diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 0f78688e..9fd99b53 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -18,8 +18,9 @@ PODS: - libwebp/mux (1.2.3): - libwebp/demux - libwebp/webp (1.2.3) - - path_provider_ios (0.0.1): + - path_provider_foundation (0.0.1): - Flutter + - FlutterMacOS - video_player_avfoundation (0.0.1): - Flutter - video_thumbnail (0.0.1): @@ -30,7 +31,7 @@ DEPENDENCIES: - ffmpeg_kit_flutter_min_gpl (from `.symlinks/plugins/ffmpeg_kit_flutter_min_gpl/ios`) - Flutter (from `Flutter`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) - video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`) @@ -46,8 +47,8 @@ EXTERNAL SOURCES: :path: Flutter image_picker_ios: :path: ".symlinks/plugins/image_picker_ios/ios" - path_provider_ios: - :path: ".symlinks/plugins/path_provider_ios/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/ios" video_player_avfoundation: :path: ".symlinks/plugins/video_player_avfoundation/ios" video_thumbnail: @@ -59,7 +60,7 @@ SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c - path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 + path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1 diff --git a/example/lib/main.dart b/example/lib/main.dart index ec59f42f..bc52f1fd 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -124,6 +124,7 @@ class _VideoEditorState extends State { _isExporting.value = true; // NOTE: To use `-crf 1` and [VideoExportPreset] you need `ffmpeg_kit_flutter_min_gpl` package (with `ffmpeg_kit` only it won't work) await _controller.exportVideo( + // format: 'gif', // preset: VideoExportPreset.medium, // customInstruction: "-crf 17", onProgress: (stats, value) => _exportingProgress.value = value, diff --git a/example/lib/widgets/export_result.dart b/example/lib/widgets/export_result.dart index 504cb389..ca0e38b4 100644 --- a/example/lib/widgets/export_result.dart +++ b/example/lib/widgets/export_result.dart @@ -3,8 +3,18 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:fraction/fraction.dart'; +import 'package:path/path.dart' as path; import 'package:video_player/video_player.dart'; +Future _getImageDimension(File file, + {required Function(Size) onResult}) async { + var decodedImage = await decodeImageFromList(file.readAsBytesSync()); + onResult(Size(decodedImage.width.toDouble(), decodedImage.height.toDouble())); +} + +String _fileMBSize(File file) => + ' ${(file.lengthSync() / (1024 * 1024)).toStringAsFixed(1)} MB'; + class VideoResultPopup extends StatefulWidget { const VideoResultPopup({super.key, required this.video}); @@ -15,23 +25,41 @@ class VideoResultPopup extends StatefulWidget { } class _VideoResultPopupState extends State { - late VideoPlayerController _controller; + VideoPlayerController? _controller; + FileImage? _fileImage; + Size _fileDimension = Size.zero; + late final bool _isGif = + path.extension(widget.video.path).toLowerCase() == ".gif"; + late String _fileMbSize; @override void initState() { super.initState(); - _controller = VideoPlayerController.file(widget.video); - _controller.initialize().then((_) { - setState(() {}); - _controller.play(); - _controller.setLooping(true); - }); + if (_isGif) { + _getImageDimension( + widget.video, + onResult: (d) => setState(() => _fileDimension = d), + ); + } else { + _controller = VideoPlayerController.file(widget.video); + _controller?.initialize().then((_) { + _fileDimension = _controller?.value.size ?? Size.zero; + setState(() {}); + _controller?.play(); + _controller?.setLooping(true); + }); + } + _fileMbSize = _fileMBSize(widget.video); } @override void dispose() { - _controller.pause(); - _controller.dispose(); + if (_isGif) { + _fileImage?.evict(); + } else { + _controller?.pause(); + _controller?.dispose(); + } super.dispose(); } @@ -44,21 +72,25 @@ class _VideoResultPopupState extends State { alignment: Alignment.bottomLeft, children: [ AspectRatio( - aspectRatio: _controller.value.aspectRatio, - child: VideoPlayer(_controller), + aspectRatio: _fileDimension.aspectRatio == 0 + ? 1 + : _fileDimension.aspectRatio, + child: + _isGif ? Image.file(widget.video) : VideoPlayer(_controller!), ), Positioned( bottom: 0, child: FileDescription( description: { 'Video path': widget.video.path, - 'Video duration': - '${(_controller.value.duration.inMilliseconds / 1000).toStringAsFixed(2)}s', - 'Video ratio': - Fraction.fromDouble(_controller.value.aspectRatio) - .reduce() - .toString(), - 'Video size': _controller.value.size.toString(), + if (!_isGif) + 'Video duration': + '${((_controller?.value.duration.inMilliseconds ?? 0) / 1000).toStringAsFixed(2)}s', + 'Video ratio': Fraction.fromDouble(_fileDimension.aspectRatio) + .reduce() + .toString(), + 'Video dimension': _fileDimension.toString(), + 'Video size': _fileMbSize, }, ), ), @@ -80,20 +112,17 @@ class CoverResultPopup extends StatefulWidget { class _CoverResultPopupState extends State { late final Uint8List _imagebytes = widget.cover.readAsBytesSync(); - Size? _fileSize; + Size? _fileDimension; + late String _fileMbSize; @override void initState() { super.initState(); - _readFileData(); - } - - Future _readFileData() async { - var decodedImage = await decodeImageFromList(_imagebytes); - setState(() { - _fileSize = - Size(decodedImage.width.toDouble(), decodedImage.height.toDouble()); - }); + _getImageDimension( + widget.cover, + onResult: (d) => setState(() => _fileDimension = d), + ); + _fileMbSize = _fileMBSize(widget.cover); } @override @@ -110,10 +139,11 @@ class _CoverResultPopupState extends State { description: { 'Cover path': widget.cover.path, 'Cover ratio': - Fraction.fromDouble(_fileSize?.aspectRatio ?? 0) + Fraction.fromDouble(_fileDimension?.aspectRatio ?? 0) .reduce() .toString(), - 'Cover size': _fileSize.toString(), + 'Cover dimension': _fileDimension.toString(), + 'Cover size': _fileMbSize, }, ), ), diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 730b1a5b..84785add 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -15,10 +15,11 @@ dependencies: video_editor: path: ../ - helpers: ^1.1.4 - image_picker: ^0.8.6 - video_player: ^2.4.10 fraction: ^4.1.4 + helpers: ^1.1.4 + image_picker: ^0.8.6+1 + path: ^1.8.2 + video_player: ^2.5.1 flutter: uses-material-design: true diff --git a/lib/domain/bloc/controller.dart b/lib/domain/bloc/controller.dart index e2f50e19..c36a706e 100644 --- a/lib/domain/bloc/controller.dart +++ b/lib/domain/bloc/controller.dart @@ -508,7 +508,7 @@ class VideoEditorController extends ChangeNotifier { if (!isFiltersEnabled) return ""; // CALCULATE FILTERS - final String gif = videoFormat != "gif" ? "" : "fps=10 -loop 0"; + final bool isGif = videoFormat?.toLowerCase() == "gif"; final String scaleInstruction = scale == 1.0 ? "" : "scale=iw*$scale:ih*$scale"; @@ -517,10 +517,12 @@ class VideoEditorController extends ChangeNotifier { _getCrop(), scaleInstruction, _getRotation(), - gif + isGif ? "fps=10" : "", ]; filters.removeWhere((item) => item.isEmpty); - return filters.isNotEmpty ? "-vf '${filters.join(",")}'" : ""; + return filters.isNotEmpty + ? "-vf '${filters.join(",")}'${isGif ? " -loop 0" : ""}" + : ""; } /// Export the video using this edition parameters and return a `File`. diff --git a/pubspec.yaml b/pubspec.yaml index abaccea1..d78c0a94 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,11 +15,11 @@ dependencies: sdk: flutter ffmpeg_kit_flutter_min_gpl: ^5.1.0 - path_provider: ^2.0.11 - video_player: ^2.4.10 - video_thumbnail: ^0.5.3 path: ^1.8.0 # update to `1.8.1` causes #79 + path_provider: ^2.0.12 transparent_image: ^2.0.0 # show fade-in placeholder in thumbnails generation + video_player: ^2.5.1 + video_thumbnail: ^0.5.3 dev_dependencies: flutter_test: