Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Remove ffmpeg_kit_flutter from the package #156

Merged
merged 5 commits into from
Jun 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 80 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
# Flutter video editor

[![Pub](https://img.shields.io/pub/v/video_editor.svg)](https://pub.dev/packages/video_editor)
[![ffmpeg_kit_flutter](https://img.shields.io/badge/ffmpeg_kit_flutter-v5.1.0-green)](https://pub.dev/packages/ffmpeg_kit_flutter)
[![GitHub stars](https://img.shields.io/github/stars/LeGoffMael/video_editor?style=social)](https://github.com/LeGoffMael/video_editor/stargazers)

A video editor that allows to edit (trim, crop, rotate and scale) and choose a cover with a very flexible UI design.

The exportation is made using [ffmpeg_kit_flutter](https://pub.dev/packages/ffmpeg_kit_flutter) library.
The library provides some tools to execute the exportation but does not handle it.

This library is written in Dart only but uses external packages such as [ffmpeg_kit_flutter](https://pub.dev/packages/ffmpeg_kit_flutter) and [video_thumbnail](https://pub.dev/packages/video_thumbnail), which makes it available only on iOS and Android plaforms for now.
This library is written in Dart only but uses external packages such as [video_thumbnail](https://pub.dev/packages/video_thumbnail), which makes it available only on iOS and Android plaforms for now ((web support is currently in progress)[https://github.com/LeGoffMael/video_editor/pull/147]).

## 📖 Installation

Expand All @@ -27,25 +26,6 @@ dependencies:
import 'package:video_editor/video_editor.dart';
```

Since [v1.3.0](https://github.com/LeGoffMael/video_editor/releases/tag/v1.3.0), video_editor uses ffmpeg_kit_flutter main release which supports the latest features. (More info on [flutter FFmepeg kit](https://github.com/arthenica/ffmpeg-kit/tree/main/flutter/flutter))

Those Android API level and iOS deployment target are required to uses this package. If you're planing to target older devices, check about the [LTS release](#1-how-to-use-ffmpeg-lts-release).

<table>
<thead>
<tr>
<th align="center">Android<br>API Level</th>
<th align="center">iOS Minimum<br>Deployment Target</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">24</td>
<td align="center">12.1</td>
</tr>
</tbody>
</table>

## 📸 Screenshots

| Example app running on an Iphone 11 pro | Customization example, light mode |
Expand Down Expand Up @@ -73,18 +53,44 @@ void dispose() {
super.dispose();
}

// Basic export video function
Future<void> exportVideo() => _controller.exportVideo(
onCompleted: (file) {}, // show the exported video
);
/// Basic export video function
Future<void> exportVideo() async {
final config = VideoFFmpegVideoEditorConfig(_controller);
// Returns the generated command and the output path
final FFmpegVideoEditorExecute execute = await config.getExecuteConfig();

// Export a GIF image, with some handlers
Future<void> exporGif() => _controller.exportVideo(
format: VideoExportFormat.gif, // or GifExportFormat(fps: 20), to customize the fps
onProgress: (stats, value) {}, // show exportation progress
onError: (e, s) {}, // handle the error
onCompleted: (file) {}, // show the exported GIF
);
// ... handle the video exportation yourself, using ffmpeg_kit_flutter, your own video server, ...
}

/// Export the video as a GIF image
Future<void> exportGif() async {
final gifConfig = VideoFFmpegVideoEditorConfig(
_controller,
format: VideoExportFormat.gif,
);
// Returns the generated command and the output path
final FFmpegVideoEditorExecute gifExecute = await gifConfig.getExecuteConfig();

// ...
}

/// Export a video, with custom command (ultrafast preset + horizontal flip)
Future<void> exportMirroredVideo() async {
final mirrorConfig = VideoFFmpegVideoEditorConfig(
_controller,
name: 'mirror-video'
commandBuilder: (VideoFFmpegVideoEditorConfig config, String videoPath, String outputPath) {
final List<String> filters = config.getExportFilters();
filters.add('hflip'); // add horizontal flip

return '-i $videoPath ${config.filtersCmd(filters)} -preset ultrafast $outputPath';
},
);
// Returns the generated command and the output path
final FFmpegVideoEditorExecute mirrorExecute = await mirrorConfig.getExecuteConfig();

// ...
}
```

For more details check out the [example](https://github.com/LeGoffMael/video_editor/tree/master/example).
Expand All @@ -93,16 +99,13 @@ For more details check out the [example](https://github.com/LeGoffMael/video_edi

| Function | Description |
| -------------------------------- | --------------------------------- |
| initialize(aspectRatio) | Init the `controller` parameters, the video, the trim and the cover, call `cropAspectRatio` |
| initialize(double? aspectRatio) | Init the `controller` parameters, the video, the trim and the cover, call `cropAspectRatio` |
| rotate90Degrees(RotateDirection) | Rotate the video by 90 degrees in the direction provided |
| preferredCropAspectRatio | Update the aspect ratio of the crop area |
| setPreferredRatioFromCrop | Update the aspect ratio to the current crop area ratio |
| cropAspectRatio | Update the aspect ratio + update the crop area to the center of the video size |
| updateCrop | Update the controller crop min and max values |
| updateTrim | Update the controller trim min and max values |
| getMetaData(onCompleted) | Return the metadata of the video file in `onCompleted` function |
| exportVideo(onCompleted) | Return the generated video with the controller parameters in `onCompleted` function |
| extractCover(onCompleted) | Return the selected cover with the controller parameters in `onCompleted` function |
| cropAspectRatio(double?) | Update the aspect ratio + update the crop area to the center of the video size |
| updateCrop(Offset, Offset) | Update the controller crop min and max values |
| applyCacheCrop | Update the controller crop min and max values with cache values |
| updateTrim(double, double) | Update the controller trim min and max values |

| Getter | Description |
| -------------------------------- | --------------------------------- |
Expand Down Expand Up @@ -148,7 +151,7 @@ Display the trimmer containing video thumbnails with rotation and crop parameter
| -------------------------------- | --------------------------------- |
| required VideoEditorController controller | The `controller` param is mandatory so every change in the controller settings will propagate in the trim slider view |
| double height = 0.0 | The `height` param specifies the height of the generated thumbnails |
| double quality = 10 | The `quality` param specifies the quality of the generated thumbnails, from 0 to 100 ([more info](https://pub.dev/packages/video_thumbnail)) |
| int quality = 10 | The `quality` param specifies the quality of the generated thumbnails, from 0 to 100 ([more info](https://pub.dev/packages/video_thumbnail)) |
| double horizontalMargin = 0.0 | The `horizontalMargin` param specifies the horizontal space to set around the slider. It is important when the trim can be dragged (`controller.maxDuration` < `controller.videoDuration`) |
| Widget? child | The `child` param can be specify to display a widget below this one (e.g: TrimTimeline) |
| bool hasHaptic = true | The `hasHaptic` param specifies if haptic feed back can be triggered when the trim touch an edge (left or right) |
Expand Down Expand Up @@ -176,7 +179,7 @@ Display a couple of generated covers with rotation and crop parameters to update
| -------------------------------- | --------------------------------- |
| required VideoEditorController controller | The `controller` param is mandatory so every change in the controller settings will propagate in the cover selection view |
| double size = 0.0 | The `size` param specifies the max size of the generated thumbnails |
| double quality = 10 | The `quality` param specifies the quality of the generated thumbnails, from 0 to 100 ([more info](https://pub.dev/packages/video_thumbnail)) |
| int quality = 10 | The `quality` param specifies the quality of the generated thumbnails, from 0 to 100 ([more info](https://pub.dev/packages/video_thumbnail)) |
| double horizontalMargin = 0.0 | The `horizontalMargin` param need to be specify when there is a margin outside the crop view, so in case of a change the new layout can be computed properly. |
| int quantity = 5 | The `quantity` param specifies the quantity of thumbnails to generate |
| Wrap? wrap | The `wrap` widget to use to customize the thumbnails wrapper |
Expand Down Expand Up @@ -235,7 +238,7 @@ You can create your own TrimStyle class to customize the TrimSlider appareance.
| IconData? leftIcon = Icons.arrow_left | The `leftIcon` param specifies the icon to show on the left edge of the trimmed area |
| IconData? rightIcon = Icons.arrow_right | The `rightIcon` param specifies the icon to show on the right edge of the trimmed area |

#### 3. CoverStyle
### 3. CoverStyle

You can create your own CoverStyle class to customize the CoverSelection appareance.

Expand All @@ -247,26 +250,45 @@ You can create your own CoverStyle class to customize the CoverSelection apparea

</details>

## 💭 FAQ
#### Export

### 1. How to use FFmpeg LTS release
#### 1. FFmpegVideoEditorConfig

Since [v1.3.0](https://github.com/LeGoffMael/video_editor/releases/tag/v1.3.0), video_editor uses ffmpeg_kit_flutter main release which supports the latest features. If you want to support a wider range of devices you should use the LTS release. [more info](https://github.com/arthenica/ffmpeg-kit/tree/main/flutter/flutter#24-lts-releases)
| Param | Description |
| -------------------------------- | --------------------------------- |
| String? name | The `name` param specifies the filename of the generated file |
| String? outputDirectory | The `outputDirectory` param specifies where the file should be generated, default to temporary directory |
| double scale = 1 | The `scale` param is used to increase or decrease the generated file dimensions |
| bool isFiltersEnabled = true | The `isFiltersEnabled` param specifies if the editor parameters should be applied |

#### 2. VideoFFmpegVideoEditorConfig

To do this, add this to your `pubspec.yaml`:
```yaml
dependency_overrides:
ffmpeg_kit_flutter_min_gpl: ^5.1.0-LTS
```
Contains all FFmpegVideoEditorConfig parameters.

On Android, if it gives a `minSdkVersion` error, try adding the following in `/android/app/src/main/AndroidManifest.xml`.
| Param | Description |
| -------------------------------- | --------------------------------- |
| VideoExportFormat format = VideoExportFormat.mp4 | The `format` param specifies the extension of the generated video |
| String Function? commandBuilder | The `commandBuilder` param can be used to generate a command with custom options |

```xml
<manifest xmlns:android=... package=... xmlns:tools="http://schemas.android.com/tools" >
<uses-sdk tools:overrideLibrary="com.arthenica.ffmpegkit.flutter"/>
</manifest>
```
#### 3. CoverFFmpegVideoEditorConfig

Contains all FFmpegVideoEditorConfig parameters.

| Param | Description |
| -------------------------------- | --------------------------------- |
| CoverExportFormat format = CoverExportFormat.jpg | The `format` param specifies the extension of the generated cover |
| int quality = 100 | The `quality` param specifies the quality of the generated thumbnails, from 0 to 100 ([more info](https://pub.dev/packages/video_thumbnail)) |
| String Function? commandBuilder | The `commandBuilder` param can be used to generate a command with custom options |

## 💭 FAQ

### 1. Why was FFmpeg removed from this package ?

Starting from version 3.0.0, the video_editor package no longer includes [ffmpeg_kit_flutter](https://pub.dev/packages/ffmpeg_kit_flutter).

- The inclusion of ffmpeg_kit_flutter binary in this package caused numerous issues for users who intended to utilize a different instance of FFmpeg within the same project (#37, #129, #153).
- Additionally, it came to my attention that the video_editor package may have been mis-licensed and subject to the GPL v3.0 license since version 1.2.3, when it began utilizing the ffmpeg_kit_flutter_min_kit binary.
- Lastly, the FFmpeg package is quite large and significantly increases the app size, which is not ideal for developers seeking to handle exportation in a different way.

## ✨ Credit

Expand Down
28 changes: 14 additions & 14 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
PODS:
- ffmpeg-kit-ios-min-gpl (5.1)
- ffmpeg_kit_flutter_min_gpl (5.1.0):
- ffmpeg_kit_flutter_min_gpl/min-gpl (= 5.1.0)
- ffmpeg-kit-ios-min (5.1)
- ffmpeg_kit_flutter_min (5.1.0):
- ffmpeg_kit_flutter_min/min (= 5.1.0)
- Flutter
- ffmpeg_kit_flutter_min_gpl/min-gpl (5.1.0):
- ffmpeg-kit-ios-min-gpl (= 5.1)
- ffmpeg_kit_flutter_min/min (5.1.0):
- ffmpeg-kit-ios-min (= 5.1)
- Flutter
- Flutter (1.0.0)
- image_picker_ios (0.0.1):
Expand All @@ -28,7 +28,7 @@ PODS:
- libwebp

DEPENDENCIES:
- ffmpeg_kit_flutter_min_gpl (from `.symlinks/plugins/ffmpeg_kit_flutter_min_gpl/ios`)
- ffmpeg_kit_flutter_min (from `.symlinks/plugins/ffmpeg_kit_flutter_min/ios`)
- Flutter (from `Flutter`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
Expand All @@ -37,12 +37,12 @@ DEPENDENCIES:

SPEC REPOS:
trunk:
- ffmpeg-kit-ios-min-gpl
- ffmpeg-kit-ios-min
- libwebp

EXTERNAL SOURCES:
ffmpeg_kit_flutter_min_gpl:
:path: ".symlinks/plugins/ffmpeg_kit_flutter_min_gpl/ios"
ffmpeg_kit_flutter_min:
:path: ".symlinks/plugins/ffmpeg_kit_flutter_min/ios"
Flutter:
:path: Flutter
image_picker_ios:
Expand All @@ -55,13 +55,13 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/video_thumbnail/ios"

SPEC CHECKSUMS:
ffmpeg-kit-ios-min-gpl: ac5eb47f98b27e26d14c009de3ce9181007ce688
ffmpeg_kit_flutter_min_gpl: f657651b493b7608fad9dda3ad606eb0946c9faa
ffmpeg-kit-ios-min: 3748f7726cd539e308090371a9d572d1a9dfaf7e
ffmpeg_kit_flutter_min: 03876cb5ff5f7cf493cbcef07fa738ae423e009c
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5
libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
video_player_avfoundation: 81e49bb3d9fb63dccf9fa0f6d877dc3ddbeac126
video_thumbnail: c4e2a3c539e247d4de13cd545344fd2d26ffafd1

PODFILE CHECKSUM: ccd901b1465efa56fa30557fdc5ae4eddcc3f504
Expand Down
4 changes: 2 additions & 2 deletions example/lib/crop.dart → example/lib/crop_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
import 'package:fraction/fraction.dart';
import 'package:video_editor/video_editor.dart';

class CropScreen extends StatelessWidget {
const CropScreen({super.key, required this.controller});
class CropPage extends StatelessWidget {
const CropPage({super.key, required this.controller});

final VideoEditorController controller;

Expand Down
48 changes: 48 additions & 0 deletions example/lib/export_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'dart:developer';
import 'dart:io';

import 'package:ffmpeg_kit_flutter_min/ffmpeg_kit.dart';
import 'package:ffmpeg_kit_flutter_min/ffmpeg_kit_config.dart';
import 'package:ffmpeg_kit_flutter_min/ffmpeg_session.dart';
import 'package:ffmpeg_kit_flutter_min/return_code.dart';
import 'package:ffmpeg_kit_flutter_min/statistics.dart';
import 'package:video_editor/video_editor.dart';

class ExportService {
static Future<void> dispose() async {
final executions = await FFmpegKit.listSessions();
if (executions.isNotEmpty) await FFmpegKit.cancel();
}

static Future<FFmpegSession> runFFmpegCommand(
FFmpegVideoEditorExecute execute, {
required void Function(File file) onCompleted,
void Function(Object, StackTrace)? onError,
void Function(Statistics)? onProgress,
}) {
log('FFmpeg start process with command = ${execute.command}');
return FFmpegKit.executeAsync(
execute.command,
(session) async {
final state =
FFmpegKitConfig.sessionStateToString(await session.getState());
final code = await session.getReturnCode();

if (ReturnCode.isSuccess(code)) {
onCompleted(File(execute.outputPath));
} else {
if (onError != null) {
onError(
Exception(
'FFmpeg process exited with state $state and return code $code.\n${await session.getOutput()}'),
StackTrace.current,
);
}
return;
}
},
null,
onProgress,
);
}
}
Loading