Skip to content

Commit

Permalink
Add support for vertical videos (and other aspect ratios)
Browse files Browse the repository at this point in the history
  • Loading branch information
KyleKun committed Jan 11, 2023
1 parent 659d3bf commit ff04d3f
Show file tree
Hide file tree
Showing 5 changed files with 458 additions and 341 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Added Subtitles feature
- Added Calendar page
- Added feature to upload videos from gallery
- Vertical videos are now supported
- Added functionality to trim videos
- Added automatic and manual geotagging in recording
- Added options to create a movie by period or only with selected videos
Expand All @@ -14,7 +15,8 @@
- Added validation checks for unsupported videos
- Added video save percentage & progress indicators
- Increased video recording quality to 1080p
- Added logs routines that are saved to txt files
- Added routine to save logs in txt files
- Improvements in recording screen

## v1.1 - 05/2021
- Added feature to receive daily notifications
Expand Down
218 changes: 105 additions & 113 deletions lib/pages/home/calendar_editor/calendar_editor_page.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:io';

import 'package:ffmpeg_kit_flutter_full_gpl/return_code.dart';
Expand Down Expand Up @@ -127,126 +126,119 @@ class _CalendarEditorPageState extends State<CalendarEditorPage> {
);

if (rawFile != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'processingVideo'.tr,
),
),
);
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(
// content: Text(
// 'processingVideo'.tr,
// ),
// ),
// );

// Video validation before navigation to the video editing page
final bool isVideoValid =
await _validateInputVideo(rawFile.path, context);
// final bool isVideoValid =
// await _validateInputVideo(rawFile.path, context);

// Go to the save video page
if (isVideoValid) {
Get.toNamed(
Routes.SAVE_VIDEO,
arguments: {
'videoPath': rawFile.path,
'currentDate': _selectedDate,
'isFromRecordingPage': false,
},
);
}
// if (isVideoValid) {
Get.toNamed(
Routes.SAVE_VIDEO,
arguments: {
'videoPath': rawFile.path,
'currentDate': _selectedDate,
'isFromRecordingPage': false,
},
);
// }
}
}

// Ensure the video passes all validations before processing
Future<bool> _validateInputVideo(
String videoPath, BuildContext context) async {
return await executeFFprobe(
'-v error -print_format json -show_format -select_streams v:0 -show_streams $videoPath')
.then((session) async {
final returnCode = await session.getReturnCode();
if (ReturnCode.isSuccess(returnCode)) {
final sessionLog = await session.getOutput();
if (sessionLog == null) return false;
final Map<String, dynamic> videoStreamDetails =
jsonDecode(sessionLog)['streams'][0];

final num videoWidth = videoStreamDetails['width'];
final num videoHeight = videoStreamDetails['height'];

// Check for video orientation before saving video.
// In some videos, the rotation property is not explicity defined which will cause ffprobe to return a null value,
// so the workaround here compares the values of video width & height to determine the orientation

// The orientation is always portrait whenever the video height is greater than the video width
if (videoHeight > videoWidth) {
await _showPortraitModeErrorDialog();
return false;
}

// Check for video aspect ratio before saving video.
// In some videos, the DAP/SAP (display/sample aspect ratio) properties are not explicity defined which will cause ffprobe to return a null value,
// so the workaround here uses the video width & height to determine the aspect ratio
final num videoAspectRatio = (videoWidth / videoHeight).toPrecision(2);

// 1.78 is the decimal equivalent of 16:9 aspect ratio videos
if (videoAspectRatio != 1.78) {
await _showAspectRatioErrorDialog();
return false;
}

return true;
}
return false;
});
}

Future<void> _showPortraitModeErrorDialog() async {
return await showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
title: Text(
'oops'.tr,
),
content: Text(
'unsupportedPortraitMode'.tr,
),
actions: [
TextButton(
onPressed: () async => Navigator.pop(context),
style: TextButton.styleFrom(
foregroundColor: AppColors.green,
),
child: Text('ok'.tr),
)
],
),
);
}

Future<void> _showAspectRatioErrorDialog() async {
return await showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
title: Text(
'oops'.tr,
),
content: Text(
'videoResolutionWarning'.tr,
),
actions: [
TextButton(
onPressed: () async => Navigator.pop(context),
style: TextButton.styleFrom(
foregroundColor: AppColors.green,
),
child: Text('ok'.tr),
)
],
),
);
}
// Future<bool> _validateInputVideo(
// String videoPath, BuildContext context) async {
// return await executeFFprobe(
// '-v error -print_format json -show_format -select_streams v:0 -show_streams $videoPath')
// .then((session) async {
// final returnCode = await session.getReturnCode();
// if (ReturnCode.isSuccess(returnCode)) {
// final sessionLog = await session.getOutput();
// if (sessionLog == null) return false;
// final Map<String, dynamic> videoStreamDetails =
// jsonDecode(sessionLog)['streams'][0];

// final num videoWidth = videoStreamDetails['width'];
// final num videoHeight = videoStreamDetails['height'];

// print('videoWidth: $videoWidth');
// print('videoHeight: $videoHeight');

// // Check for video aspect ratio before saving video.
// // In some videos, the DAP/SAP (display/sample aspect ratio) properties are not explicity defined which will cause ffprobe to return a null value,
// // so the workaround here uses the video width & height to determine the aspect ratio
// final num videoAspectRatio = (videoWidth / videoHeight).toPrecision(2);

// // 1.78 is the decimal equivalent of 16:9 aspect ratio videos
// if (videoAspectRatio != 1.78) {
// // await _showAspectRatioErrorDialog();
// // return false;
// }

// return true;
// }
// return false;
// });
// }

// Future<void> _showPortraitModeErrorDialog() async {
// return await showDialog(
// context: context,
// builder: (context) => AlertDialog(
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(10),
// ),
// title: Text(
// 'oops'.tr,
// ),
// content: Text(
// 'unsupportedPortraitMode'.tr,
// ),
// actions: [
// TextButton(
// onPressed: () async => Navigator.pop(context),
// style: TextButton.styleFrom(
// foregroundColor: AppColors.green,
// ),
// child: Text('ok'.tr),
// )
// ],
// ),
// );
// }

// Future<void> _showAspectRatioErrorDialog() async {
// return await showDialog(
// context: context,
// builder: (context) => AlertDialog(
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(10),
// ),
// title: Text(
// 'oops'.tr,
// ),
// content: Text(
// 'videoResolutionWarning'.tr,
// ),
// actions: [
// TextButton(
// onPressed: () async => Navigator.pop(context),
// style: TextButton.styleFrom(
// foregroundColor: AppColors.green,
// ),
// child: Text('ok'.tr),
// )
// ],
// ),
// );
// }

Future<void> deleteVideoDialog() async {
return await showDialog(
Expand Down
Loading

0 comments on commit ff04d3f

Please sign in to comment.