# flutter_ffmpeg
![GitHub release](https://img.shields.io/badge/release-v0.2.10-blue.svg)
![](https://img.shields.io/pub/v/flutter_ffmpeg.svg)
FFmpeg plugin for Flutter. Supports iOS and Android.
### 1. Features
- Based on `MobileFFmpeg`
- Includes both `FFmpeg` and `FFprobe`
- Supports
- Both `Android` and `iOS`
- Both Android (API Level 16+) and iOS (SDK 9.3+)
- FFmpeg `v4.1`, `v4.2` and `v4.3-dev` releases
- `arm-v7a`, `arm-v7a-neon`, `arm64-v8a`, `x86` and `x86_64` architectures on Android
- `armv7`, `armv7s`, `arm64`, `arm64e`, `i386` and `x86_64` architectures on iOS
- 24 external libraries
`fontconfig`, `freetype`, `fribidi`, `gmp`, `gnutls`, `kvazaar`, `lame`, `libaom`, `libass`, `libiconv`, `libilbc`, `libtheora`, `libvorbis`, `libvpx`, `libwebp`, `libxml2`, `opencore-amr`, `opus`, `shine`, `snappy`, `soxr`, `speex`, `twolame`, `wavpack`
- 4 external libraries with GPL license
`vid.stab`, `x264`, `x265`, `xvidcore`
- Concurrent execution
- `zlib` and `MediaCodec` Android system libraries
- `bzip2`, `zlib`, `iconv` iOS system libraries and `AudioToolbox`, `CoreImage`, `VideoToolbox`, `AVFoundation` iOS system frameworks
- Licensed under LGPL 3.0, can be customized to support GPL v3.0
- Includes eight different packages with different external libraries enabled in FFmpeg
|
min |
min-gpl |
https |
https-gpl |
audio |
video |
full |
full-gpl |
external libraries |
- |
vid.stab x264 x265 xvidcore |
gmp gnutls |
gmp gnutls vid.stab x264 x265 xvidcore |
lame libilbc libvorbis opencore-amr opus shine soxr speex twolame wavpack |
fontconfig freetype fribidi kvazaar libaom libass libiconv libtheora libvpx libwebp snappy |
fontconfig freetype fribidi gmp gnutls kvazaar lame libaom libass libiconv libilbc libtheora libvorbis libvpx libwebp libxml2 opencore-amr opus shine snappy soxr speex twolame wavpack |
fontconfig freetype fribidi gmp gnutls kvazaar lame libaom libass libiconv libilbc libtheora libvorbis libvpx libwebp libxml2 opencore-amr opus shine snappy soxr speex twolame vid.stab wavpack x264 x265 xvidcore |
android system libraries |
zlib MediaCodec |
ios system libraries |
zlib AudioToolbox AVFoundation CoreImage iconv VideoToolbox bzip2 |
### 2. Installation
Add `flutter_ffmpeg` as a dependency in your `pubspec.yaml file`.
```
dependencies:
flutter_ffmpeg: ^0.2.10
```
#### 2.1 Packages
Installation of `FlutterFFmpeg` using `pub` enables the default package, which is based on `https` package. It is possible to enable other packages using the following steps.
##### 2.1.1 Android
- Edit `android/build.gradle` file and define package name in `ext.flutterFFmpegPackage` variable.
```
ext {
flutterFFmpegPackage = ""
}
```
##### 2.1.2 iOS (Flutter >= 1.20.x)
- Edit `ios/Podfile` and add the following block **before** `target 'Runner do` section :
```
# "fork" of method flutter_install_ios_plugin_pods (in fluttertools podhelpers.rb) to get lts version of ffmpeg
def flutter_install_ios_plugin_pods(ios_application_path = nil)
# defined_in_file is set by CocoaPods and is a Pathname to the Podfile.
ios_application_path ||= File.dirname(defined_in_file.realpath) if self.respond_to?(:defined_in_file)
raise 'Could not find iOS application path' unless ios_application_path
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
symlink_dir = File.expand_path('.symlinks', ios_application_path)
system('rm', '-rf', symlink_dir) # Avoid the complication of dependencies like FileUtils.
symlink_plugins_dir = File.expand_path('plugins', symlink_dir)
system('mkdir', '-p', symlink_plugins_dir)
plugins_file = File.join(ios_application_path, '..', '.flutter-plugins-dependencies')
plugin_pods = flutter_parse_plugins_file(plugins_file)
plugin_pods.each do |plugin_hash|
plugin_name = plugin_hash['name']
plugin_path = plugin_hash['path']
if (plugin_name && plugin_path)
symlink = File.join(symlink_plugins_dir, plugin_name)
File.symlink(plugin_path, symlink)
if plugin_name == 'flutter_ffmpeg'
pod plugin_name+'/', :path => File.join('.symlinks', 'plugins', plugin_name, 'ios')
else
pod plugin_name, :path => File.join('.symlinks', 'plugins', plugin_name, 'ios')
end
end
end
```
- Ensure that `flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))` function is called
within `target 'Runner' do` block. In that case, it is mandatory that the added function is
named `flutter_install_ios_plugin_pods` and that you **do not** make an explicit call within that block.
##### 2.1.3 iOS (Flutter < 1.20.x)
- Edit `ios/Podfile` file and modify the default `# Plugin Pods` block as follows.
Do not forget to specify package name in `` section.
```
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
system('mkdir -p .symlinks/plugins')
plugin_pods = parse_KV_file('../.flutter-plugins')
plugin_pods.each do |name, path|
symlink = File.join('.symlinks', 'plugins', name)
File.symlink(path, symlink)
if name == 'flutter_ffmpeg'
pod name+'/', :path => File.join(symlink, 'ios')
else
pod name, :path => File.join(symlink, 'ios')
end
end
```
##### 2.1.4 Package Names
The following table shows all package names defined for `flutter_ffmpeg`.
| Package | Main Release | LTS Release |
| :----: | :----: | :----: |
| min | min | min-lts |
| min-gpl | min-gpl | min-gpl-lts |
| https | https | https-lts |
| https-gpl | https-gpl | https-gpl-lts |
| audio | audio | audio-lts |
| video | video | video-lts |
| full | full | full-lts |
| full-gpl | full-gpl | full-gpl-lts |
#### 2.2 Existing Applications
It is possible to add `flutter_ffmpeg` to existing applications using [Add-to-App](https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps) guide.
Please execute the following additional steps if you are integrating into an iOS application.
1. Go to `Build Phases` of `Pods` -> `FlutterPluginRegistrant` target and add all frameworks under the `Pods/mobile-ffmpeg-` directory to the `Link Binary With Libraries` section
2. Go to `Build Phases` of `Pods` -> `FlutterPluginRegistrant` target and add all system libraries/frameworks listed in Step 4 of [Importing-Frameworks](https://github.com/tanersener/mobile-ffmpeg/wiki/Importing-Frameworks) guide to the `Link Binary With Libraries` section
3. Go to `Build Phases` of `Pods` -> `FlutterPluginRegistrant` target and add `AVFoundation` system framework to the `Link Binary With Libraries` section
#### 2.3 LTS Releases
`flutter_ffmpeg` is published in two different variants: `Main Release` and `LTS Release`. Both releases share the same source code but is built with different settings. Below you can see the changes between the two.
In order to install the `LTS` variant, install the `https-lts` package using instructions in `2.1` or append `-lts` to the package name you are using.
|
Main Release |
LTS Release |
Android API Level |
24 |
16 |
Android Camera Access |
Yes |
- |
Android Architectures |
arm-v7a-neon arm64-v8a x86 x86-64 |
arm-v7a arm-v7a-neon arm64-v8a x86 x86-64 |
Xcode Support |
10.1 |
7.3.1 |
iOS SDK |
12.1 |
9.3 |
iOS Architectures |
arm64 arm64e x86-64 |
armv7 arm64 i386 x86-64 |
### 3. Using
1. Execute FFmpeg commands.
- Use execute() method with a single command line
```
import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg();
_flutterFFmpeg.execute("-i file1.mp4 -c:v mpeg4 file2.mp4").then((rc) => print("FFmpeg process exited with rc $rc"));
```
- Use executeWithArguments() method with an array of arguments
```
import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg();
var arguments = ["-i", "file1.mp4", "-c:v", "mpeg4", "file2.mp4"];
_flutterFFmpeg.executeWithArguments(arguments).then((rc) => print("FFmpeg process exited with rc $rc"));
```
2. Execute FFprobe commands.
- Use execute() method with a single command line
```
import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
_flutterFFprobe.execute("-i file1.mp4").then((rc) => print("FFprobe process exited with rc $rc"));
```
- Use executeWithArguments() method with an array of arguments
```
import 'package:flutter_ffmpeg/flutter_ffmpeg.dart';
final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
var arguments = ["-i", "file1.mp4"];
_flutterFFprobe.executeWithArguments(arguments).then((rc) => print("FFprobe process exited with rc $rc"));
```
3. Check execution output. Zero represents successful execution, non-zero values represent failure.
```
final FlutterFFmpegConfig _flutterFFmpegConfig = new FlutterFFmpegConfig();
_flutterFFmpegConfig.getLastReturnCode().then((rc) => print("Last rc: $rc"));
_flutterFFmpegConfig.getLastCommandOutput().then((output) => print("Last command output: $output"));
```
4. Stop an ongoing operation. Note that this function does not wait for termination to complete and returns immediately.
```
_flutterFFmpeg.cancel();
```
5. Get media information for a file.
- Print all fields
```
final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
_flutterFFprobe.getMediaInformation("").then((info) => print(info));
```
- Print selected fields
```
final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
_flutterFFprobe.getMediaInformation("").then((info) {
print("Media Information");
print("Path: ${info['path']}");
print("Format: ${info['format']}");
print("Duration: ${info['duration']}");
print("Start time: ${info['startTime']}");
print("Bitrate: ${info['bitrate']}");
if (info['streams'] != null) {
final streamsInfoArray = info['streams'];
if (streamsInfoArray.length > 0) {
for (var streamsInfo in streamsInfoArray) {
print("Stream id: ${streamsInfo['index']}");
print("Stream type: ${streamsInfo['type']}");
print("Stream codec: ${streamsInfo['codec']}");
print("Stream full codec: ${streamsInfo['fullCodec']}");
print("Stream format: ${streamsInfo['format']}");
print("Stream full format: ${streamsInfo['fullFormat']}");
print("Stream width: ${streamsInfo['width']}");
print("Stream height: ${streamsInfo['height']}");
print("Stream bitrate: ${streamsInfo['bitrate']}");
print("Stream sample rate: ${streamsInfo['sampleRate']}");
print("Stream sample format: ${streamsInfo['sampleFormat']}");
print("Stream channel layout: ${streamsInfo['channelLayout']}");
print("Stream sar: ${streamsInfo['sampleAspectRatio']}");
print("Stream dar: ${streamsInfo['displayAspectRatio']}");
print("Stream average frame rate: ${streamsInfo['averageFrameRate']}");
print("Stream real frame rate: ${streamsInfo['realFrameRate']}");
print("Stream time base: ${streamsInfo['timeBase']}");
print("Stream codec time base: ${streamsInfo['codecTimeBase']}");
final metadataMap = streamsInfo['metadata'];
if (metadataMap != null) {
print('Stream metadata encoder: ${metadataMap['encoder']}');
print('Stream metadata rotate: ${metadataMap['rotate']}');
print('Stream metadata creation time: ${metadataMap['creation_time']}');
print('Stream metadata handler name: ${metadataMap['handler_name']}');
}
final sideDataMap = streamsInfo['sidedata'];
if (sideDataMap != null) {
print('Stream side data displaymatrix: ${sideDataMap['displaymatrix']}');
}
}
}
}
```
6. Enable log callback and redirect all `FFmpeg`/`FFprobe` logs to a console/file/widget.
```
void logCallback(int level, String message) {
print(message);
}
...
_flutterFFmpegConfig.enableLogCallback(this.logCallback);
```
7. Enable statistics callback and follow the progress of an ongoing `FFmpeg` operation.
```
void statisticsCallback(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) {
print("Statistics: time: $time, size: $size, bitrate: $bitrate, speed: $speed, videoFrameNumber: $videoFrameNumber, videoQuality: $videoQuality, videoFps: $videoFps");
}
...
_flutterFFmpegConfig.enableStatisticsCallback(this.statisticsCallback);
```
8. Poll statistics without implementing statistics callback.
```
_flutterFFmpegConfig.getLastReceivedStatistics().then((stats) => print(stats));
```
9. Reset statistics before starting a new operation.
```
_flutterFFmpegConfig.resetStatistics();
```
10. Set log level.
```
_flutterFFmpegConfig.setLogLevel(LogLevel.AV_LOG_WARNING);
```
11. Register your own fonts by specifying a custom fonts directory, so they are available to use in `FFmpeg` filters. Please note that this function can not work on relative paths, you need to provide full file system path.
```
_flutterFFmpegConfig.setFontDirectory("");
```
12. Use your own `fontconfig` configuration.
```
_flutterFFmpegConfig.setFontconfigConfigurationPath("");
```
13. Disable log functionality of the library. Logs will not be printed to console and log callback will be disabled.
```
_flutterFFmpegConfig.disableLogs();
```
14. Disable statistics functionality of the library. Statistics callback will be disabled but the last received statistics data will be still available.
```
_flutterFFmpegConfig.disableStatistics();
```
15. List enabled external libraries.
```
_flutterFFmpegConfig.getExternalLibraries().then((packageList) {
packageList.forEach((value) => print("External library: $value"));
});
```
16. Create new `FFmpeg` pipe.
```
_flutterFFmpegConfig.registerNewFFmpegPipe().then((path) {
then((stats) => print("New ffmpeg pipe at $path"));
});
```
### 4. Tips
- `flutter_ffmpeg` uses file system paths, it does not know what an `assets` folder or a `project` folder is. So you can't use resources on those folders directly, you need to provide full paths of those resources.
- `flutter_ffmpeg` requires ios deployment target to be at least `9.3`.
If you don't specify a deployment target or set a value smaller than `9.3` then your build will fail with the following error.
```
Resolving dependencies of `Podfile`
[!] CocoaPods could not find compatible versions for pod "flutter_ffmpeg":
In Podfile:
flutter_ffmpeg (from `.symlinks/plugins/flutter_ffmpeg/ios`)
Specs satisfying the `flutter_ffmpeg (from `.symlinks/plugins/flutter_ffmpeg/ios`)` dependency were found, but they required a higher minimum
deployment target.
```
You can fix this issue by adding `platform :ios, '9.3'` definition to your `ios/Podfile` file.
```
platform :ios, '9.3'
```
- `flutter_ffmpeg` includes native libraries that require ios deployment target to be at least `9.3`.
If a deployment target is not set or a value smaller than `9.3` is used then your build will fail with the following error.
```
ld: targeted OS version does not support use of thread local variables in __gnutls_rnd_deinit for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
```
Unfortunately the latest versions of `Flutter` and `Cocoapods` have some issues about setting ios deployment target from `Podfile`.
Having `platform :ios, '9.3'` in your `Podfile` is not enough. `Runner` project still uses the default value `8.0`.
You need to open `Runner.xcworkspace` in `Xcode` and set `iOS Deployment Target` of `Runner` project to `9.3` manually.
- Enabling `ProGuard` on releases older than `v0.2.4` causes linking errors. Please add the following rule inside your `proguard-rules.pro` file to preserve necessary method names and prevent linking errors.
```
-keep class com.arthenica.mobileffmpeg.Config {
native ;
void log(int, byte[]);
void statistics(int, float, float, long , int, double, double);
}
```
- `ffmpeg` requires a valid `fontconfig` configuration to render subtitles. Unfortunately, Android does not include a default `fontconfig` configuration.
So if you do not register a font or specify a `fontconfig` configuration under Android, then the burning process will not produce any errors but subtitles won't be burned in your file.
You can overcome this situation by registering a font using `setFontDirectory` method or specifying your own `fontconfig` configuration using `setFontconfigConfigurationPath` method.
- By default, Xcode compresses `PNG` files during packaging. If you use `.png` files in your commands make sure you set the following two settings to `NO`. If one of them is set to `YES`, your operations may fail with `Error while decoding stream #0:0: Generic error in an external library` error.
- Some `flutter_ffmpeg` packages include `libc++_shared.so` native library. If a second library which also includes `libc++_shared.so` is added as a dependency, `gradle` fails with `More than one file was found with OS independent path 'lib/x86/libc++_shared.so'` error message.
You can fix this error by adding the following block into your `build.gradle`.
```
android {
packagingOptions {
pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
}
}
```
### 5. Updates
Refer to [Changelog](CHANGELOG.md) for updates.
### 6. License
This project is licensed under the LGPL v3.0. However, if installation is customized to use a package with `-gpl` postfix (min-gpl, https-gpl, full-gpl) then `FlutterFFmpeg` is subject to the GPL v3.0 license.
Digital assets used in test applications are published in the public domain.
### 7. Contributing
Feel free to submit issues or pull requests.
### 8. See Also
- [FFmpeg](https://www.ffmpeg.org)
- [Mobile FFmpeg Wiki](https://github.com/tanersener/mobile-ffmpeg/wiki)
- [FFmpeg License and Legal Considerations](https://ffmpeg.org/legal.html)