diff --git a/CHANGELOG.md b/CHANGELOG.md index c1fa361..91b1523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ - Adds setEnvironmentVariable API method - Depends on mobile-ffmpeg v4.4 - Allows modifying mobile-ffmpeg version for android -- Fixes issue #115, #120 and #178 +- Includes an updated example application +- Fixes issue #115, #120, #157, #159, #170, #178 and #202 ## 0.2.10 - Fixes issue #94 diff --git a/README.md b/README.md index 13ec062..d367aca 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # flutter_ffmpeg -![GitHub release](https://img.shields.io/badge/release-v0.2.10-blue.svg) +![GitHub release](https://img.shields.io/badge/release-v0.3.0-blue.svg) ![](https://img.shields.io/pub/v/flutter_ffmpeg.svg) FFmpeg plugin for Flutter. Supports iOS and Android. @@ -13,12 +13,12 @@ FFmpeg plugin for Flutter. Supports iOS and Android. - 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 + - FFmpeg `v4.1`, `v4.2` , `v4.3` and `v4.4-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 + - 25 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` + `fontconfig`, `freetype`, `fribidi`, `gmp`, `gnutls`, `kvazaar`, `lame`, `libaom`, `libass`, `libiconv`, `libilbc`, `libtheora`, `libvorbis`, `libvpx`, `libwebp`, `libxml2`, `opencore-amr`, `opus`, `shine`, `snappy`, `soxr`, `speex`, `twolame`, `vo-amrwbenc`, `wavpack` - 4 external libraries with GPL license @@ -26,23 +26,41 @@ FFmpeg plugin for Flutter. Supports iOS and Android. - Concurrent execution - `zlib` and `MediaCodec` Android system libraries - - `bzip2`, `zlib`, `iconv` iOS system libraries and `AudioToolbox`, `CoreImage`, `VideoToolbox`, `AVFoundation` iOS system frameworks + - `bzip2`, `iconv`, `libuuid`, `zlib` system libraries and `AudioToolbox`, `VideoToolbox`, `AVFoundation` 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 + +### 2. Installation + +Add `flutter_ffmpeg` as a dependency in your `pubspec.yaml file`. + ``` +dependencies: + flutter_ffmpeg: ^0.3.0 + ``` + +#### 2.1 Packages + +`ffmpeg` includes built-in encoders for some popular formats bu not for all of them. External libraries are necessary +to encode some specific codecs/formats. For example, to encode an `mp3` file you need `lame` or `shine` library +enabled. You have to install `flutter_ffmpeg` a package that has them inside. To encode an `h264` video you need to +install a package with `x264` inside. To encode `vp8` or `vp9` videos you need a package with `libvpx` inside. + +`flutter_ffmpeg` provides eight different packages that include different sets of external libraries. Packages are +named according to the external libraries included in them. Below you can see which libraries are enabled in each one +of them. - - - - - - - - + + + + + + + + @@ -52,10 +70,10 @@ FFmpeg plugin for Flutter. Supports iOS and Android. - + - - + + @@ -63,22 +81,13 @@ FFmpeg plugin for Flutter. Supports iOS and Android. - +
minmin-gplhttpshttps-gplaudiovideofullfull-gplminmin-gplhttpshttps-gplaudiovideofullfull-gpl
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
lame
libilbc
libvorbis
opencore-amr
opus
shine
soxr
speex
twolame
vo-amrwbenc
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
fontconfig
freetype
fribidi
gmp
gnutls
kvazaar
lame
libaom
libass
libiconv
libilbc
libtheora
libvorbis
libvpx
libwebp
libxml2
opencore-amr
opus
shine
snappy
soxr
speex
twolame
vo-amrwbenc
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
vo-amrwbenc
wavpack
x264
x265
xvidcore
android system libraries
ios system librarieszlib
AudioToolbox
AVFoundation
CoreImage
iconv
VideoToolbox
bzip2
zlib
AudioToolbox
AVFoundation
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. +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 @@ -90,8 +99,49 @@ Installation of `FlutterFFmpeg` using `pub` enables the default package, which i } ``` +##### 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.2 iOS +##### 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. @@ -113,7 +163,7 @@ Installation of `FlutterFFmpeg` using `pub` enables the default package, which i end ``` -##### 2.1.3 Package Names +##### 2.1.4 Package Names The following table shows all package names defined for `flutter_ffmpeg`. @@ -143,55 +193,23 @@ Please execute the following additional steps if you are integrating into an iOS #### 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. +`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 differences 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 ReleaseLTS Release
Android API Level2416
Android Camera AccessYes-
Android Architecturesarm-v7a-neon
arm64-v8a
x86
x86-64
arm-v7a
arm-v7a-neon
arm64-v8a
x86
x86-64
Xcode Support10.17.3.1
iOS SDK12.19.3
iOS Architecturesarm64
arm64e
x86-64
armv7
arm64
i386
x86-64
+| | 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 | 11.0 | 9.3 | +| iOS AVFoundation | Yes | - | +| iOS Architectures | arm64
x86-64
x86-64-mac-catalyst | armv7
arm64
i386
x86-64 | ### 3. Using -1. Execute FFmpeg commands. +1. Execute synchronous FFmpeg commands. - Use execute() method with a single command line ``` @@ -212,8 +230,15 @@ In order to install the `LTS` variant, install the `https-lts` package using ins var arguments = ["-i", "file1.mp4", "-c:v", "mpeg4", "file2.mp4"]; _flutterFFmpeg.executeWithArguments(arguments).then((rc) => print("FFmpeg process exited with rc $rc")); ``` +2. Execute asynchronous FFmpeg commands. -2. Execute FFprobe commands. + ``` + _flutterFFmpeg.executeAsync(ffmpegCommand, (int executionId, int returnCode) { + print("FFmpeg process for executionId $executionId exited with rc $returnCode"); + }).then((executionId) => print("Async FFmpeg process started with executionId $executionId.")); + ``` + +3. Execute FFprobe commands. - Use execute() method with a single command line ``` @@ -235,7 +260,7 @@ In order to install the `LTS` variant, install the `https-lts` package using ins _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. +4. Check execution output. Zero represents successful execution, 255 means user cancel and non-zero values represent failure. ``` final FlutterFFmpegConfig _flutterFFmpegConfig = new FlutterFFmpegConfig(); @@ -245,12 +270,17 @@ In order to install the `LTS` variant, install the `https-lts` package using ins _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. Stop ongoing FFmpeg operations. Note that these two functions do not wait for termination to complete and return immediately. + - Stop all executions + ``` + _flutterFFmpeg.cancel(); + ``` + - Stop a specific execution + ``` + _flutterFFmpeg.cancelExecution(executionId); + ``` -5. Get media information for a file. +6. Get media information for a file. - Print all fields ``` final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe(); @@ -264,37 +294,43 @@ In order to install the `LTS` variant, install the `https-lts` package using ins _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']; + print("Path: ${info.getMediaProperties()['path']}"); + print("Format: ${info.getMediaProperties()['format']}"); + print("Duration: ${info.getMediaProperties()['duration']}"); + print("Start time: ${info.getMediaProperties()['startTime']}"); + print("Bitrate: ${info.getMediaProperties()['bitrate']}"); + Map tags = info.getMediaProperties()['tags']; + if (tags != null) { + tags.forEach((key, value) { + print("Tag: " + key + ":" + value + "\n"); + }); + } + + if (info.getStreams() != null) { + List streams = info.getStreams(); + + if (streams.length > 0) { + for (var stream in streams) { + print("Stream id: ${stream.getAllProperties()['index']}"); + print("Stream type: ${stream.getAllProperties()['type']}"); + print("Stream codec: ${stream.getAllProperties()['codec']}"); + print("Stream full codec: ${stream.getAllProperties()['fullCodec']}"); + print("Stream format: ${stream.getAllProperties()['format']}"); + print("Stream full format: ${stream.getAllProperties()['fullFormat']}"); + print("Stream width: ${stream.getAllProperties()['width']}"); + print("Stream height: ${stream.getAllProperties()['height']}"); + print("Stream bitrate: ${stream.getAllProperties()['bitrate']}"); + print("Stream sample rate: ${stream.getAllProperties()['sampleRate']}"); + print("Stream sample format: ${stream.getAllProperties()['sampleFormat']}"); + print("Stream channel layout: ${stream.getAllProperties()['channelLayout']}"); + print("Stream sar: ${stream.getAllProperties()['sampleAspectRatio']}"); + print("Stream dar: ${stream.getAllProperties()['displayAspectRatio']}"); + print("Stream average frame rate: ${stream.getAllProperties()['averageFrameRate']}"); + print("Stream real frame rate: ${stream.getAllProperties()['realFrameRate']}"); + print("Stream time base: ${stream.getAllProperties()['timeBase']}"); + print("Stream codec time base: ${stream.getAllProperties()['codecTimeBase']}"); + + final metadataMap = stream.getAllProperties()['metadata']; if (metadataMap != null) { print('Stream metadata encoder: ${metadataMap['encoder']}'); print('Stream metadata rotate: ${metadataMap['rotate']}'); @@ -302,7 +338,7 @@ In order to install the `LTS` variant, install the `https-lts` package using ins print('Stream metadata handler name: ${metadataMap['handler_name']}'); } - final sideDataMap = streamsInfo['sidedata']; + final sideDataMap = stream.getAllProperties()['sidedata']; if (sideDataMap != null) { print('Stream side data displaymatrix: ${sideDataMap['displaymatrix']}'); } @@ -312,78 +348,85 @@ In order to install the `LTS` variant, install the `https-lts` package using ins ``` -6. Enable log callback and redirect all `FFmpeg`/`FFprobe` logs to a console/file/widget. +7. Enable log callback and redirect all `FFmpeg`/`FFprobe` logs to a console/file/widget. ``` - void logCallback(int level, String message) { - print(message); + void logCallback(Log log) { + print("${log.executionId}:${log.message}"); } ... _flutterFFmpegConfig.enableLogCallback(this.logCallback); ``` -7. Enable statistics callback and follow the progress of an ongoing `FFmpeg` operation. +8. 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"); + void statisticsCallback(Statistics statistics) { + print("Statistics: executionId: ${statistics.executionId}, time: ${statistics.time}, size: ${statistics.size}, bitrate: ${statistics.bitrate}, speed: ${statistics.speed}, videoFrameNumber: ${statistics.videoFrameNumber}, videoQuality: ${statistics.videoQuality}, videoFps: ${statistics.videoFps}"); } ... _flutterFFmpegConfig.enableStatisticsCallback(this.statisticsCallback); ``` -8. Poll statistics without implementing statistics callback. +9. Poll statistics without implementing statistics callback. ``` _flutterFFmpegConfig.getLastReceivedStatistics().then((stats) => print(stats)); ``` -9. Reset statistics before starting a new operation. +10. List ongoing executions. ``` - _flutterFFmpegConfig.resetStatistics(); + _flutterFFmpeg.listExecutions().then((ffmpegExecutions) { + ffmpegExecutions.forEach((execution) { + ffprint( + "Execution id:${execution.executionId}, startTime:${execution.command}, command:${execution.startTime}."); + }); + }); ``` -10. Set log level. +11. 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. +12. 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. +13. 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. +14. 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. +15. 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. Example Application +You can see how FlutterFFmpeg is used inside an application by running the example application provided under the +`example` folder. It supports command execution, video encoding, accessing https, encoding audio, +burning subtitles, video stabilisation, pipe operations and concurrent command execution. + + -### 4. Tips +### 5. 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. +- `flutter_ffmpeg` requires ios deployment target to be at least `11.0` for Main releases and `9.3` for LTS releases. + If you don't specify a deployment target or set a value smaller than the required one then your build will fail with + the following error. ``` Resolving dependencies of `Podfile` @@ -395,8 +438,13 @@ In order to install the `LTS` variant, install the `https-lts` package using ins deployment target. ``` - You can fix this issue by adding `platform :ios, '9.3'` definition to your `ios/Podfile` file. + You can fix this issue by adding the definition to your `ios/Podfile` file. + - `Main` releases + ``` + platform :ios, '11.0' + ``` + - `LTS` releases ``` platform :ios, '9.3' ``` @@ -447,21 +495,35 @@ You can overcome this situation by registering a font using `setFontDirectory` m } ``` -### 5. Updates +### 6. Updates Refer to [Changelog](CHANGELOG.md) for updates. -### 6. License +### 7. 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. +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. +In test application; embedded fonts are licensed under the +[SIL Open Font License](https://opensource.org/licenses/OFL-1.1), other digital assets are published in the public +domain. -### 7. Contributing +### 8. Patents + +It is not clearly explained in their documentation but it is believed that `FFmpeg`, `kvazaar`, `x264` and `x265` +include algorithms which are subject to software patents. If you live in a country where software algorithms are +patentable then you'll probably need to pay royalty fees to patent holders. We are not lawyers though, so we recommend +that you seek legal advice first. See [FFmpeg Patent Mini-FAQ](https://ffmpeg.org/legal.html). + +### 9. Contributing Feel free to submit issues or pull requests. -### 8. See Also +Please note that `master` branch includes only the latest released source code. Changes planned for the next release +are implemented under the `development` branch. Therefore, if you want to create a pull request, please open it against +the `development`. + +### 10. See Also - [FFmpeg](https://www.ffmpeg.org) - [Mobile FFmpeg Wiki](https://github.com/tanersener/mobile-ffmpeg/wiki) diff --git a/doc/assets/flutter_test_app.gif b/doc/assets/flutter_test_app.gif new file mode 100644 index 0000000..23779bb Binary files /dev/null and b/doc/assets/flutter_test_app.gif differ diff --git a/example/README.md b/example/README.md index c980e11..1a72536 100644 --- a/example/README.md +++ b/example/README.md @@ -4,7 +4,7 @@ Demonstrates how to use the flutter_ffmpeg plugin. ## Getting Started -1. Execute FFmpeg commands. +1. Execute synchronous FFmpeg commands. - Use execute() method with a single command line ``` @@ -25,8 +25,15 @@ Demonstrates how to use the flutter_ffmpeg plugin. var arguments = ["-i", "file1.mp4", "-c:v", "mpeg4", "file2.mp4"]; _flutterFFmpeg.executeWithArguments(arguments).then((rc) => print("FFmpeg process exited with rc $rc")); ``` +2. Execute asynchronous FFmpeg commands. -2. Execute FFprobe commands. + ``` + _flutterFFmpeg.executeAsync(ffmpegCommand, (int executionId, int returnCode) { + print("FFmpeg process for executionId $executionId exited with rc $returnCode"); + }).then((executionId) => print("Async FFmpeg process started with executionId $executionId.")); + ``` + +3. Execute FFprobe commands. - Use execute() method with a single command line ``` @@ -48,7 +55,7 @@ Demonstrates how to use the flutter_ffmpeg plugin. _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. +4. Check execution output. Zero represents successful execution, 255 means user cancel and non-zero values represent failure. ``` final FlutterFFmpegConfig _flutterFFmpegConfig = new FlutterFFmpegConfig(); @@ -58,12 +65,17 @@ Demonstrates how to use the flutter_ffmpeg plugin. _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. Stop ongoing FFmpeg operations. Note that these two functions do not wait for termination to complete and return immediately. + - Stop all executions + ``` + _flutterFFmpeg.cancel(); + ``` + - Stop a specific execution + ``` + _flutterFFmpeg.cancelExecution(executionId); + ``` -5. Get media information for a file. +6. Get media information for a file. - Print all fields ``` final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe(); @@ -77,37 +89,43 @@ Demonstrates how to use the flutter_ffmpeg plugin. _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']; + print("Path: ${info.getMediaProperties()['path']}"); + print("Format: ${info.getMediaProperties()['format']}"); + print("Duration: ${info.getMediaProperties()['duration']}"); + print("Start time: ${info.getMediaProperties()['startTime']}"); + print("Bitrate: ${info.getMediaProperties()['bitrate']}"); + Map tags = info.getMediaProperties()['tags']; + if (tags != null) { + tags.forEach((key, value) { + print("Tag: " + key + ":" + value + "\n"); + }); + } + + if (info.getStreams() != null) { + List streams = info.getStreams(); + + if (streams.length > 0) { + for (var stream in streams) { + print("Stream id: ${stream.getAllProperties()['index']}"); + print("Stream type: ${stream.getAllProperties()['type']}"); + print("Stream codec: ${stream.getAllProperties()['codec']}"); + print("Stream full codec: ${stream.getAllProperties()['fullCodec']}"); + print("Stream format: ${stream.getAllProperties()['format']}"); + print("Stream full format: ${stream.getAllProperties()['fullFormat']}"); + print("Stream width: ${stream.getAllProperties()['width']}"); + print("Stream height: ${stream.getAllProperties()['height']}"); + print("Stream bitrate: ${stream.getAllProperties()['bitrate']}"); + print("Stream sample rate: ${stream.getAllProperties()['sampleRate']}"); + print("Stream sample format: ${stream.getAllProperties()['sampleFormat']}"); + print("Stream channel layout: ${stream.getAllProperties()['channelLayout']}"); + print("Stream sar: ${stream.getAllProperties()['sampleAspectRatio']}"); + print("Stream dar: ${stream.getAllProperties()['displayAspectRatio']}"); + print("Stream average frame rate: ${stream.getAllProperties()['averageFrameRate']}"); + print("Stream real frame rate: ${stream.getAllProperties()['realFrameRate']}"); + print("Stream time base: ${stream.getAllProperties()['timeBase']}"); + print("Stream codec time base: ${stream.getAllProperties()['codecTimeBase']}"); + + final metadataMap = stream.getAllProperties()['metadata']; if (metadataMap != null) { print('Stream metadata encoder: ${metadataMap['encoder']}'); print('Stream metadata rotate: ${metadataMap['rotate']}'); @@ -115,7 +133,7 @@ Demonstrates how to use the flutter_ffmpeg plugin. print('Stream metadata handler name: ${metadataMap['handler_name']}'); } - final sideDataMap = streamsInfo['sidedata']; + final sideDataMap = stream.getAllProperties()['sidedata']; if (sideDataMap != null) { print('Stream side data displaymatrix: ${sideDataMap['displaymatrix']}'); } @@ -125,69 +143,67 @@ Demonstrates how to use the flutter_ffmpeg plugin. ``` -6. Enable log callback and redirect all `FFmpeg`/`FFprobe` logs to a console/file/widget. +7. Enable log callback and redirect all `FFmpeg`/`FFprobe` logs to a console/file/widget. ``` - void logCallback(int level, String message) { - print(message); + void logCallback(Log log) { + print("${log.executionId}:${log.message}"); } ... _flutterFFmpegConfig.enableLogCallback(this.logCallback); ``` -7. Enable statistics callback and follow the progress of an ongoing `FFmpeg` operation. +8. 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"); + void statisticsCallback(Statistics statistics) { + print("Statistics: executionId: ${statistics.executionId}, time: ${statistics.time}, size: ${statistics.size}, bitrate: ${statistics.bitrate}, speed: ${statistics.speed}, videoFrameNumber: ${statistics.videoFrameNumber}, videoQuality: ${statistics.videoQuality}, videoFps: ${statistics.videoFps}"); } ... _flutterFFmpegConfig.enableStatisticsCallback(this.statisticsCallback); ``` -8. Poll statistics without implementing statistics callback. +9. Poll statistics without implementing statistics callback. ``` _flutterFFmpegConfig.getLastReceivedStatistics().then((stats) => print(stats)); ``` -9. Reset statistics before starting a new operation. +10. List ongoing executions. ``` - _flutterFFmpegConfig.resetStatistics(); + _flutterFFmpeg.listExecutions().then((ffmpegExecutions) { + ffmpegExecutions.forEach((execution) { + ffprint( + "Execution id:${execution.executionId}, startTime:${execution.command}, command:${execution.startTime}."); + }); + }); ``` -10. Set log level. +11. 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. +12. 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. +13. 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. +14. 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. +15. 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")); }); - ``` - + ``` \ No newline at end of file diff --git a/example/android/build.gradle b/example/android/build.gradle index 03fb4f1..d9d6580 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -24,7 +24,7 @@ subprojects { project.evaluationDependsOn(':app') } -ext.flutterFFmpegPackage = 'full-gpl' +ext.flutterFFmpegPackage = 'full' task clean(type: Delete) { delete rootProject.buildDir diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 2b1af55..91b377a 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -5,9 +5,11 @@ PODS: - mobile-ffmpeg-audio (= 4.4.LTS) - fluttertoast (0.0.2): - Flutter + - Toast - mobile-ffmpeg-audio (4.4.LTS) - path_provider (0.0.1): - Flutter + - Toast (4.0.0) - video_player (0.0.1): - Flutter @@ -21,6 +23,7 @@ DEPENDENCIES: SPEC REPOS: trunk: - mobile-ffmpeg-audio + - Toast EXTERNAL SOURCES: Flutter: @@ -37,9 +40,10 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: 0e3d915762c693b495b44d77113d4970485de6ec flutter_ffmpeg: 657ce8a1502c3d25bf34efc58b380b248836f772 - fluttertoast: b644586ef3b16f67fae9a1f8754cef6b2d6b634b + fluttertoast: 6122fa75143e992b1d3470f61000f591a798cc58 mobile-ffmpeg-audio: 1e0a053f8a6de57114e50ff48b3a85ff1c60f902 path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c + Toast: 91b396c56ee72a5790816f40d3a94dd357abc196 video_player: 9cc823b1d9da7e8427ee591e8438bfbcde500e6e PODFILE CHECKSUM: 45c71b51796b78cb6a09bb97eb9786d4c7aa085f diff --git a/example/lib/concurrent_execution_tab.dart b/example/lib/concurrent_execution_tab.dart index 7f015f4..81f20d0 100644 --- a/example/lib/concurrent_execution_tab.dart +++ b/example/lib/concurrent_execution_tab.dart @@ -112,7 +112,7 @@ class ConcurrentExecutionTab { } } - listFFmpegExecutions(); + runListFFmpegExecutions(); }); }); }); @@ -126,7 +126,7 @@ class ConcurrentExecutionTab { return new File("${documentsDirectory.path}/$video"); } - void listFFmpegExecutio() { + void runListFFmpegExecutions() { listFFmpegExecutions().then((ffmpegExecutions) { ffprint("Listing ongoing FFmpeg executions."); int i = 0; diff --git a/example/pubspec.lock b/example/pubspec.lock index f5ecf07..b09d61a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -49,7 +49,7 @@ packages: name: cupertino_icons url: "https://pub.dartlang.org" source: hosted - version: "0.1.3" + version: "1.0.0" fake_async: dependency: transitive description: @@ -57,6 +57,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" file: dependency: transitive description: @@ -92,7 +99,7 @@ packages: name: fluttertoast url: "https://pub.dartlang.org" source: hosted - version: "7.0.2" + version: "7.1.1" intl: dependency: transitive description: @@ -127,7 +134,7 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "1.6.14" + version: "1.6.16" path_provider_linux: dependency: transitive description: @@ -141,7 +148,7 @@ packages: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "0.0.4+3" + version: "0.0.4+4" path_provider_platform_interface: dependency: transitive description: @@ -149,6 +156,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.3" platform: dependency: transitive description: @@ -237,7 +251,7 @@ packages: name: video_player url: "https://pub.dartlang.org" source: hosted - version: "0.10.12+2" + version: "0.10.12+3" video_player_platform_interface: dependency: transitive description: @@ -252,6 +266,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.3+2" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.2+1" xdg_directories: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 26a3ca4..9e22206 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -4,16 +4,17 @@ version: 0.3.0 publish_to: 'none' environment: - sdk: ">=2.0.0-dev.68.0 <3.0.0" + sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.10.0 <2.0.0" dependencies: flutter: sdk: flutter - cupertino_icons: ^0.1.3 - path_provider: ^1.6.14 + cupertino_icons: ^1.0.0 + path_provider: ^1.6.16 path: ^1.7.0 - fluttertoast: ^7.0.2 - video_player: ^0.10.12+2 + fluttertoast: ^7.1.1 + video_player: ^0.10.12+3 flutter_ffmpeg: path: .. diff --git a/lib/flutter_ffmpeg.dart b/lib/flutter_ffmpeg.dart index 90144ca..c1f2955 100644 --- a/lib/flutter_ffmpeg.dart +++ b/lib/flutter_ffmpeg.dart @@ -450,7 +450,8 @@ class FlutterFFmpeg { List arguments, ExecuteCallback executeCallback) async { try { return await _methodChannel.invokeMethod( - 'executeFFmpegAsyncWithArguments', {'arguments': arguments}).then((map) { + 'executeFFmpegAsyncWithArguments', + {'arguments': arguments}).then((map) { var executionId = map["executionId"]; FlutterFFmpegConfig.addExecuteCallback(executionId, executeCallback); return executionId; diff --git a/lib/stream_information.dart b/lib/stream_information.dart index e711906..5ed94eb 100644 --- a/lib/stream_information.dart +++ b/lib/stream_information.dart @@ -18,7 +18,6 @@ */ class StreamInformation { - Map _allProperties; /// Creates a new [StreamInformation] instance @@ -28,5 +27,4 @@ class StreamInformation { Map getAllProperties() { return _allProperties; } - } diff --git a/pubspec.yaml b/pubspec.yaml index 228fe74..b0e6574 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,13 +5,14 @@ homepage: https://github.com/tanersener/flutter-ffmpeg environment: sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.10.0 <2.0.0" dependencies: flutter: sdk: flutter dev_dependencies: - path_provider: ^1.6.14 + path_provider: ^1.6.16 path: ^1.7.0 flutter: