Taner Sener 5 gadi atpakaļ
vecāks
revīzija
eb002e7ec5

+ 6 - 0
CHANGELOG.md

@@ -1,3 +1,9 @@
+## 0.2.9
+- Implements FFprobe
+- Add concurrent execution support
+- Re-organises plugin classes
+- iOS releases depend on iconv system library instead of iconv external library
+
 ## 0.2.8
 - Uses ffmpeg v4.3
 - Implements registerNewFFmpegPipe API method

+ 68 - 36
README.md

@@ -1,6 +1,6 @@
 # flutter_ffmpeg 
 
-![GitHub release](https://img.shields.io/badge/release-v0.2.8-blue.svg)
+![GitHub release](https://img.shields.io/badge/release-v0.2.9-blue.svg)
 ![](https://img.shields.io/pub/v/flutter_ffmpeg.svg)
 
 FFmpeg plugin for Flutter. Supports iOS and Android.
@@ -8,10 +8,12 @@ FFmpeg plugin for Flutter. Supports iOS and Android.
 <img src="https://github.com/tanersener/flutter-ffmpeg/raw/development/doc/assets/flutter-ffmpeg-logo-v2-cropped.png" width="240">
 
 ### 1. Features
-- Based on MobileFFmpeg
+- 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.2-dev-x` and `v4.3-dev-x` (master) releases
+    - 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
@@ -22,8 +24,9 @@ FFmpeg plugin for Flutter. Supports iOS and Android.
 
         `vid.stab`, `x264`, `x265`, `xvidcore`
 
+    - Concurrent execution
     - `zlib` and `MediaCodec` Android system libraries
-    - `bzip2`, `zlib` iOS system libraries and `AudioToolbox`, `CoreImage`, `VideoToolbox`, `AVFoundation` iOS system frameworks
+    - `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
@@ -60,7 +63,7 @@ FFmpeg plugin for Flutter. Supports iOS and Android.
 </tr>
 <tr>
 <td align="center"><sup>ios system libraries</sup></td>
-<td align="center" colspan=8><sup>zlib</sup><br><sup>AudioToolbox</sup><br><sup>AVFoundation</sup><br><sup>CoreImage</sup><br><sup>VideoToolbox</sup><br><sup>bzip2</sup></td>
+<td align="center" colspan=8><sup>zlib</sup><br><sup>AudioToolbox</sup><br><sup>AVFoundation</sup><br><sup>CoreImage</sup><br><sup>iconv</sup><br><sup>VideoToolbox</sup><br><sup>bzip2</sup></td>
 </tr>
 </tbody>
 </table>
@@ -70,7 +73,7 @@ FFmpeg plugin for Flutter. Supports iOS and Android.
 Add `flutter_ffmpeg` as a dependency in your `pubspec.yaml file`.
   ```
 dependencies:
-    flutter_ffmpeg: ^0.2.8
+    flutter_ffmpeg: ^0.2.9
   ```
 
 #### 2.1 Packages
@@ -188,7 +191,7 @@ In order to install the `LTS` variant, install the `https-lts` package using ins
 
 ### 3. Using
 
-1. Execute commands.
+1. Execute FFmpeg commands.
 
     - Use execute() method with a single command line  
     ```
@@ -210,26 +213,55 @@ In order to install the `LTS` variant, install the `https-lts` package using ins
     _flutterFFmpeg.executeWithArguments(arguments).then((rc) => print("FFmpeg process exited with rc $rc"));
     ```
 
-2. Check execution output. Zero represents successful execution, non-zero values represent failure.
+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  
+
     ```
-    _flutterFFmpeg.getLastReturnCode().then((rc) => print("Last rc: $rc"));
+    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"));
 
-    _flutterFFmpeg.getLastCommandOutput().then((output) => print("Last command output: $output"));
+    _flutterFFmpegConfig.getLastCommandOutput().then((output) => print("Last command output: $output"));
     ```
 
-3. Stop an ongoing operation. Note that this function does not wait for termination to complete and returns immediately.
+4. Stop an ongoing operation. Note that this function does not wait for termination to complete and returns immediately.
     ```
     _flutterFFmpeg.cancel();
     ```
 
-4. Get media information for a file.
+5. Get media information for a file.
     - Print all fields
     ```
-    _flutterFFmpeg.getMediaInformation("<file path or uri>").then((info) => print(info));
+   final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
+
+    _flutterFFprobe.getMediaInformation("<file path or uri>").then((info) => print(info));
     ```
     - Print selected fields
     ```
-    _flutterFFmpeg.getMediaInformation("<file path or uri>").then((info) {
+   final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
+
+    _flutterFFprobe.getMediaInformation("<file path or uri>").then((info) {
         print("Media Information");
 
         print("Path: ${info['path']}");
@@ -280,68 +312,68 @@ In order to install the `LTS` variant, install the `https-lts` package using ins
 
     ```
 
-5. Enable log callback and redirect all `FFmpeg` logs to a console/file/widget.
+6. Enable log callback and redirect all `FFmpeg`/`FFprobe` logs to a console/file/widget.
     ```
     void logCallback(int level, String message) {
         print(message);
     }
     ...
-    _flutterFFmpeg.enableLogCallback(this.logCallback);
+    _flutterFFmpegConfig.enableLogCallback(this.logCallback);
     ```
 
-6. Enable statistics callback and follow the progress of an ongoing operation.
+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");
     }
     ...
-    _flutterFFmpeg.enableStatisticsCallback(this.statisticsCallback);
+    _flutterFFmpegConfig.enableStatisticsCallback(this.statisticsCallback);
     ```
 
-7. Poll statistics without implementing statistics callback.
+8. Poll statistics without implementing statistics callback.
     ```
-    _flutterFFmpeg.getLastReceivedStatistics().then((stats) => print(stats));
+    _flutterFFmpegConfig.getLastReceivedStatistics().then((stats) => print(stats));
     ```
 
-8. Reset statistics before starting a new operation.
+9. Reset statistics before starting a new operation.
     ```
-    _flutterFFmpeg.resetStatistics();
+    _flutterFFmpegConfig.resetStatistics();
     ```
 
-9. Set log level.
+10. Set log level.
     ```
-    _flutterFFmpeg.setLogLevel(LogLevel.AV_LOG_WARNING);
+    _flutterFFmpegConfig.setLogLevel(LogLevel.AV_LOG_WARNING);
     ```
 
-10. Register your own fonts by specifying a custom fonts directory, so they are available to use in `FFmpeg` filters.
+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.
     ```
-    _flutterFFmpeg.setFontDirectory("<folder with fonts>");
+    _flutterFFmpegConfig.setFontDirectory("<folder with fonts>");
     ```
 
-11. Use your own `fontconfig` configuration.
+12. Use your own `fontconfig` configuration.
     ```
-    _flutterFFmpeg.setFontconfigConfigurationPath("<fontconfig configuration directory>");
+    _flutterFFmpegConfig.setFontconfigConfigurationPath("<fontconfig configuration directory>");
     ```
 
-12. Disable log functionality of the library. Logs will not be printed to console and log callback will be disabled.
+13. Disable log functionality of the library. Logs will not be printed to console and log callback will be disabled.
     ```
-    _flutterFFmpeg.disableLogs();
+    _flutterFFmpegConfig.disableLogs();
     ```
 
-13. Disable statistics functionality of the library. Statistics callback will be disabled but the last received statistics data will be still available.
+14. Disable statistics functionality of the library. Statistics callback will be disabled but the last received statistics data will be still available.
     ```
-    _flutterFFmpeg.disableStatistics();
+    _flutterFFmpegConfig.disableStatistics();
     ```
 
-14. List enabled external libraries.
+15. List enabled external libraries.
     ```
-    _flutterFFmpeg.getExternalLibraries().then((packageList) {
+    _flutterFFmpegConfig.getExternalLibraries().then((packageList) {
          packageList.forEach((value) => print("External library: $value"));
     });
     ```
-15. Create new `FFmpeg` pipe. 
+16. Create new `FFmpeg` pipe. 
     ```
-    _flutterFFmpeg.registerNewFFmpegPipe().then((path) {
+    _flutterFFmpegConfig.registerNewFFmpegPipe().then((path) {
          then((stats) => print("New ffmpeg pipe at $path"));
     });
     ```

+ 4 - 4
android/build.gradle

@@ -11,7 +11,7 @@ String safeExtGet(String prop, String fallback) {
 }
 
 group 'com.arthenica.flutter.ffmpeg'
-version '0.2.8'
+version '0.2.9'
 
 buildscript {
     repositories {
@@ -20,7 +20,7 @@ buildscript {
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.3.2'
+        classpath 'com.android.tools.build:gradle:3.5.3'
     }
 }
 
@@ -34,7 +34,7 @@ rootProject.allprojects {
 apply plugin: 'com.android.library'
 
 android {
-    compileSdkVersion 28
+    compileSdkVersion 29
 
     defaultConfig {
         minSdkVersion safeExtGet('flutterFFmpegPackage', 'https').contains("-lts") ? 16 : 24
@@ -45,5 +45,5 @@ android {
 }
 
 dependencies {
-    implementation 'com.arthenica:mobile-ffmpeg-' + safePackageName(safeExtGet('flutterFFmpegPackage', 'https')) + ':' + safePackageVersion(safeExtGet('flutterFFmpegPackage', 'https'), '4.3', '4.2.2')
+    implementation 'com.arthenica:mobile-ffmpeg-' + safePackageName(safeExtGet('flutterFFmpegPackage', 'https')) + ':' + safePackageVersion(safeExtGet('flutterFFmpegPackage', 'https'), '4.3.1', '4.3.1')
 }

+ 3 - 3
android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java → android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteFFmpegAsyncArgumentsTask.java

@@ -35,20 +35,20 @@ import io.flutter.plugin.common.MethodChannel;
  * @author Taner Sener
  * @since 0.1.0
  */
-public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, Integer, Integer> {
+public class FlutterFFmpegExecuteFFmpegAsyncArgumentsTask extends AsyncTask<String, Integer, Integer> {
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
     private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteFFmpegAsyncArgumentsTask(final List<String> arguments, final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
         this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
-    protected Integer doInBackground(final String... dummyString) {
+    protected Integer doInBackground(final String... unusedArgs) {
         final String[] argumentsArray = arguments.toArray(new String[0]);
 
         Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("Running FFmpeg with arguments: %s.", Arrays.toString(argumentsArray)));

+ 15 - 21
android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java → android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteFFprobeAsyncArgumentsTask.java

@@ -22,46 +22,40 @@ package com.arthenica.flutter.ffmpeg;
 import android.os.AsyncTask;
 import android.util.Log;
 
-import com.arthenica.mobileffmpeg.FFmpeg;
+import com.arthenica.mobileffmpeg.FFprobe;
+
+import java.util.Arrays;
+import java.util.List;
 
 import io.flutter.plugin.common.MethodChannel;
 
 /**
- * Asynchronous task which performs {@link FFmpeg#execute(String, String)} method invocations.
+ * Asynchronous task which performs {@link FFprobe#execute(String[])} method invocations.
  *
  * @author Taner Sener
- * @since 0.1.0
+ * @since 0.2.9
  */
-public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Integer, Integer> {
+public class FlutterFFmpegExecuteFFprobeAsyncArgumentsTask extends AsyncTask<String, Integer, Integer> {
 
-    private String delimiter;
     private final MethodChannel.Result result;
+    private final List<String> arguments;
     private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
-        if (delimiter == null) {
-            this.delimiter = " ";
-        } else {
-            this.delimiter = delimiter;
-        }
-
+    FlutterFFmpegExecuteFFprobeAsyncArgumentsTask(final List<String> arguments, final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final MethodChannel.Result result) {
+        this.arguments = arguments;
         this.result = result;
         this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
-    protected Integer doInBackground(final String... strings) {
-        int rc = -1;
-
-        if ((strings != null) && (strings.length > 0)) {
-            final String command = strings[0];
+    protected Integer doInBackground(final String... unusedArgs) {
+        final String[] argumentsArray = arguments.toArray(new String[0]);
 
-            Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("Running FFmpeg command: %s with delimiter %s.", command, delimiter));
+        Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("Running FFprobe with arguments: %s.", Arrays.toString(argumentsArray)));
 
-            rc = FFmpeg.execute(command);
+        int rc = FFprobe.execute(argumentsArray);
 
-            Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("FFmpeg exited with rc: %d", rc));
-        }
+        Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("FFprobe exited with rc: %d", rc));
 
         return rc;
     }

+ 8 - 21
android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -22,46 +22,33 @@ package com.arthenica.flutter.ffmpeg;
 import android.os.AsyncTask;
 import android.util.Log;
 
-import com.arthenica.mobileffmpeg.FFmpeg;
+import com.arthenica.mobileffmpeg.FFprobe;
 import com.arthenica.mobileffmpeg.MediaInformation;
 
 import io.flutter.plugin.common.MethodChannel;
 
 /**
- * Asynchronous task which performs {@link FFmpeg#getMediaInformation(String, Long)} method invocations.
+ * Asynchronous task which performs {@link FFprobe#getMediaInformation(String, Long)} method invocations.
  *
  * @author Taner Sener
  * @since 0.1.0
  */
 public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String, Integer, MediaInformation> {
 
-    private Integer timeout;
+    private final String path;
     private final MethodChannel.Result result;
     private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
-        this.timeout = timeout;
+    FlutterFFmpegGetMediaInformationAsyncTask(final String path, final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final MethodChannel.Result result) {
+        this.path = path;
         this.result = result;
         this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
-    protected MediaInformation doInBackground(final String... strings) {
-        MediaInformation mediaInformation = null;
-
-        if ((strings != null) && (strings.length > 0)) {
-            final String path = strings[0];
-
-            if (timeout == null) {
-                Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("Getting media information for %s", path));
-                mediaInformation = FFmpeg.getMediaInformation(path);
-            } else {
-                Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("Getting media information for %s with timeout %d.", path, timeout.longValue()));
-                mediaInformation = FFmpeg.getMediaInformation(path, timeout.longValue());
-            }
-        }
-
-        return mediaInformation;
+    protected MediaInformation doInBackground(final String... unusedArgs) {
+        Log.d(FlutterFFmpegPlugin.LIBRARY_NAME, String.format("Getting media information for %s.", path));
+        return FFprobe.getMediaInformation(path);
     }
 
     @Override

+ 11 - 12
android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -121,23 +121,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
-            final String version = FFmpeg.getFFmpegVersion();
+            final String version = Config.getFFmpegVersion();
             flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
-        } else if (call.method.equals("executeWithArguments")) {
+        } else if (call.method.equals("executeFFmpegWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
+            final FlutterFFmpegExecuteFFmpegAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteFFmpegAsyncArgumentsTask(arguments, flutterFFmpegResultHandler, result);
             asyncTask.execute("dummy-trigger");
 
-        } else if (call.method.equals("execute")) {
+        } else if (call.method.equals("executeFFprobeWithArguments")) {
 
-            String command = call.argument("command");
-            String delimiter = call.argument("delimiter");
+            List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
-            asyncTask.execute(command);
+            final FlutterFFmpegExecuteFFprobeAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteFFprobeAsyncArgumentsTask(arguments, flutterFFmpegResultHandler, result);
+            asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("cancel")) {
 
@@ -224,12 +223,12 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReturnCode")) {
 
-            int lastReturnCode = FFmpeg.getLastReturnCode();
+            int lastReturnCode = Config.getLastReturnCode();
             flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
-            final String lastCommandOutput = FFmpeg.getLastCommandOutput();
+            final String lastCommandOutput = Config.getLastCommandOutput();
             flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
@@ -239,8 +238,8 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
-            asyncTask.execute(path);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(path, flutterFFmpegResultHandler, result);
+            asyncTask.execute();
 
         } else if (call.method.equals("registerNewFFmpegPipe")) {
 

+ 1 - 0
example/.gitignore

@@ -74,3 +74,4 @@ unlinked_spec.ds
 !**/ios/**/default.pbxuser
 !**/ios/**/default.perspectivev3
 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
+/.flutter-plugins-dependencies

+ 72 - 30
example/README.md

@@ -4,7 +4,7 @@ Demonstrates how to use the flutter_ffmpeg plugin.
 
 ## Getting Started
 
-1. Execute commands.
+1. Execute FFmpeg commands.
 
     - Use execute() method with a single command line  
     ```
@@ -26,26 +26,55 @@ Demonstrates how to use the flutter_ffmpeg plugin.
     _flutterFFmpeg.executeWithArguments(arguments).then((rc) => print("FFmpeg process exited with rc $rc"));
     ```
 
-2. Check execution output. Zero represents successful execution, non-zero values represent failure.
+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  
+
     ```
-    _flutterFFmpeg.getLastReturnCode().then((rc) => print("Last rc: $rc"));
+    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"));
 
-    _flutterFFmpeg.getLastCommandOutput().then((output) => print("Last command output: $output"));
+    _flutterFFmpegConfig.getLastCommandOutput().then((output) => print("Last command output: $output"));
     ```
 
-3. Stop an ongoing operation. Note that this function does not wait for termination to complete and returns immediately.
+4. Stop an ongoing operation. Note that this function does not wait for termination to complete and returns immediately.
     ```
     _flutterFFmpeg.cancel();
     ```
 
-4. Get media information for a file.
+5. Get media information for a file.
     - Print all fields
     ```
-    _flutterFFmpeg.getMediaInformation("<file path or uri>").then((info) => print(info));
+   final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
+
+    _flutterFFprobe.getMediaInformation("<file path or uri>").then((info) => print(info));
     ```
     - Print selected fields
     ```
-    _flutterFFmpeg.getMediaInformation("<file path or uri>").then((info) {
+   final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
+
+    _flutterFFprobe.getMediaInformation("<file path or uri>").then((info) {
         print("Media Information");
 
         print("Path: ${info['path']}");
@@ -77,74 +106,87 @@ Demonstrates how to use the flutter_ffmpeg plugin.
                     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']}');
+                    }
                 }
             }
         }
 
     ```
 
-5. Enable log callback and redirect all `FFmpeg` logs to a console/file/widget.
+6. Enable log callback and redirect all `FFmpeg`/`FFprobe` logs to a console/file/widget.
     ```
     void logCallback(int level, String message) {
         print(message);
     }
     ...
-    _flutterFFmpeg.enableLogCallback(this.logCallback);
+    _flutterFFmpegConfig.enableLogCallback(this.logCallback);
     ```
 
-6. Enable statistics callback and follow the progress of an ongoing operation.
+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");
     }
     ...
-    _flutterFFmpeg.enableStatisticsCallback(this.statisticsCallback);
+    _flutterFFmpegConfig.enableStatisticsCallback(this.statisticsCallback);
     ```
 
-7. Poll statistics without implementing statistics callback.
+8. Poll statistics without implementing statistics callback.
     ```
-    _flutterFFmpeg.getLastReceivedStatistics().then((stats) => print(stats));
+    _flutterFFmpegConfig.getLastReceivedStatistics().then((stats) => print(stats));
     ```
 
-8. Reset statistics before starting a new operation.
+9. Reset statistics before starting a new operation.
     ```
-    _flutterFFmpeg.resetStatistics();
+    _flutterFFmpegConfig.resetStatistics();
     ```
 
-9. Set log level.
+10. Set log level.
     ```
-    _flutterFFmpeg.setLogLevel(LogLevel.AV_LOG_WARNING);
+    _flutterFFmpegConfig.setLogLevel(LogLevel.AV_LOG_WARNING);
     ```
 
-10. Register your own fonts by specifying a custom fonts directory, so they are available to use in `FFmpeg` filters.
+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.
     ```
-    _flutterFFmpeg.setFontDirectory("<folder with fonts>");
+    _flutterFFmpegConfig.setFontDirectory("<folder with fonts>");
     ```
 
-11. Use your own `fontconfig` configuration.
+12. Use your own `fontconfig` configuration.
     ```
-    _flutterFFmpeg.setFontconfigConfigurationPath("<fontconfig configuration directory>");
+    _flutterFFmpegConfig.setFontconfigConfigurationPath("<fontconfig configuration directory>");
     ```
 
-12. Disable log functionality of the library. Logs will not be printed to console and log callback will be disabled.
+13. Disable log functionality of the library. Logs will not be printed to console and log callback will be disabled.
     ```
-    _flutterFFmpeg.disableLogs();
+    _flutterFFmpegConfig.disableLogs();
     ```
 
-13. Disable statistics functionality of the library. Statistics callback will be disabled but the last received statistics data will be still available.
+14. Disable statistics functionality of the library. Statistics callback will be disabled but the last received statistics data will be still available.
     ```
-    _flutterFFmpeg.disableStatistics();
+    _flutterFFmpegConfig.disableStatistics();
     ```
 
-14. List enabled external libraries.
+15. List enabled external libraries.
     ```
-    _flutterFFmpeg.getExternalLibraries().then((packageList) {
+    _flutterFFmpegConfig.getExternalLibraries().then((packageList) {
          packageList.forEach((value) => print("External library: $value"));
     });
     ```
-15. Create new `FFmpeg` pipe. 
+16. Create new `FFmpeg` pipe. 
     ```
-    _flutterFFmpeg.registerNewFFmpegPipe().then((path) {
+    _flutterFFmpegConfig.registerNewFFmpegPipe().then((path) {
          then((stats) => print("New ffmpeg pipe at $path"));
     });
     ```

+ 1 - 1
example/android/build.gradle

@@ -5,7 +5,7 @@ buildscript {
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.3.2'
+        classpath 'com.android.tools.build:gradle:3.5.3'
     }
 }
 

+ 2 - 2
example/android/gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,6 @@
-#Fri Jun 23 08:50:38 CEST 2017
+#Sat Jan 18 23:10:37 GMT 2020
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

+ 103 - 43
example/lib/flutter_ffmpeg_example.dart

@@ -113,7 +113,9 @@ class FlutterFFmpegTestAppState extends State<MainPage>
   static const String ASSET_2 = "2.jpg";
   static const String ASSET_3 = "3.jpg";
 
+  final FlutterFFmpegConfig _flutterFFmpegConfig = new FlutterFFmpegConfig();
   final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg();
+  final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();
 
   TextEditingController _commandController;
   TabController _controller;
@@ -169,7 +171,7 @@ class FlutterFFmpegTestAppState extends State<MainPage>
     testParseDoubleQuotesAndEscapesInCommand();
   }
 
-  void testRunCommand() {
+  void testRunFFmpegCommand() {
     getLastReturnCode().then((rc) => print("Last rc: $rc"));
     getLastCommandOutput().then((output) =>
         debugPrint("Last command output: \"$output\"", wrapWidth: 1024));
@@ -179,11 +181,11 @@ class FlutterFFmpegTestAppState extends State<MainPage>
     testParseArguments();
     registerNewFFmpegPipe().then((path) => print("New FFmpeg pipe: $path"));
 
-    print("Testing COMMAND.");
+    print("Testing FFmpeg COMMAND.");
 
     // ENABLE LOG CALLBACK ON EACH CALL
-    _flutterFFmpeg.enableLogCallback(commandOutputLogCallback);
-    _flutterFFmpeg.enableStatisticsCallback(statisticsCallback);
+    _flutterFFmpegConfig.enableLogCallback(commandOutputLogCallback);
+    _flutterFFmpegConfig.enableStatisticsCallback(statisticsCallback);
 
     // CLEAR OUTPUT ON EACH EXECUTION
     _commandOutput = "";
@@ -204,19 +206,48 @@ class FlutterFFmpegTestAppState extends State<MainPage>
     // disableLogs();
     // enableLogs();
 
-    execute(_commandController.text)
+    executeFFmpeg(_commandController.text)
         .then((rc) => print("FFmpeg process exited with rc $rc"));
     // executeWithArguments(_commandController.text.split(" ")).then((rc) => print("FFmpeg process exited with rc $rc"));
 
     setState(() {});
   }
 
+  void testRunFFprobeCommand() {
+    getLastReturnCode().then((rc) => print("Last rc: $rc"));
+    getLastCommandOutput().then((output) =>
+        debugPrint("Last command output: \"$output\"", wrapWidth: 1024));
+
+    print("Testing ParseArguments.");
+
+    testParseArguments();
+    registerNewFFmpegPipe().then((path) => print("New FFmpeg pipe: $path"));
+
+    print("Testing FFprobe COMMAND.");
+
+    // ENABLE LOG CALLBACK ON EACH CALL
+    _flutterFFmpegConfig.enableLogCallback(commandOutputLogCallback);
+    _flutterFFmpegConfig.enableStatisticsCallback(statisticsCallback);
+
+    // CLEAR OUTPUT ON EACH EXECUTION
+    _commandOutput = "";
+
+    VideoUtil.tempDirectory.then((tempDirectory) {
+      setFontconfigConfigurationPath(tempDirectory.path);
+    });
+
+    executeFFprobe(_commandController.text)
+        .then((rc) => print("FFprobe process exited with rc $rc"));
+
+    setState(() {});
+  }
+
   void testGetMediaInformation(String mediaPath) {
     print("Testing Get Media Information.");
 
     // ENABLE LOG CALLBACK ON EACH CALL
-    _flutterFFmpeg.enableLogCallback(commandOutputLogCallback);
-    _flutterFFmpeg.enableStatisticsCallback(null);
+    _flutterFFmpegConfig.enableLogCallback(commandOutputLogCallback);
+    _flutterFFmpegConfig.enableStatisticsCallback(null);
 
     // CLEAR OUTPUT ON EACH EXECUTION
     _commandOutput = "";
@@ -284,8 +315,8 @@ class FlutterFFmpegTestAppState extends State<MainPage>
     print("Testing VIDEO.");
 
     // ENABLE LOG CALLBACK ON EACH CALL
-    _flutterFFmpeg.enableLogCallback(encodeOutputLogCallback);
-    _flutterFFmpeg.enableStatisticsCallback(statisticsCallback);
+    _flutterFFmpegConfig.enableLogCallback(encodeOutputLogCallback);
+    _flutterFFmpegConfig.enableStatisticsCallback(statisticsCallback);
 
     // CLEAR OUTPUT ON EACH EXECUTION
     _encodeOutput = "";
@@ -301,13 +332,11 @@ class FlutterFFmpegTestAppState extends State<MainPage>
           final String ffmpegCodec = getFFmpegCodecName();
 
           VideoUtil.assetPath(videoPath).then((fullVideoPath) {
-            execute(VideoUtil.generateEncodeVideoScript(image1Path, image2Path,
+            executeFFmpeg(VideoUtil.generateEncodeVideoScript(image1Path, image2Path,
                     image3Path, fullVideoPath, ffmpegCodec, customOptions))
                 .then((rc) {
               if (rc == 0) {
-                getLastCommandOutput().then((output) => debugPrint(
-                    "Last command output: \"$output\"",
-                    wrapWidth: 1024));
+                executeFFprobe("-i " + fullVideoPath);
               }
             });
 
@@ -359,92 +388,100 @@ class FlutterFFmpegTestAppState extends State<MainPage>
   }
 
   Future<String> getFFmpegVersion() async {
-    return await _flutterFFmpeg.getFFmpegVersion();
+    return await _flutterFFmpegConfig.getFFmpegVersion();
   }
 
   Future<String> getPlatform() async {
-    return await _flutterFFmpeg.getPlatform();
+    return await _flutterFFmpegConfig.getPlatform();
   }
 
-  Future<int> executeWithArguments(List arguments) async {
+  Future<int> executeFFmpegWithArguments(List arguments) async {
     return await _flutterFFmpeg.executeWithArguments(arguments);
   }
 
-  Future<int> execute(String command) async {
+  Future<int> executeFFmpeg(String command) async {
     return await _flutterFFmpeg.execute(command);
   }
 
+  Future<int> executeFFprobeWithArguments(List arguments) async {
+    return await _flutterFFprobe.executeWithArguments(arguments);
+  }
+
+  Future<int> executeFFprobe(String command) async {
+    return await _flutterFFprobe.execute(command);
+  }
+
   Future<void> cancel() async {
     return await _flutterFFmpeg.cancel();
   }
 
   Future<void> disableRedirection() async {
-    return await _flutterFFmpeg.disableRedirection();
+    return await _flutterFFmpegConfig.disableRedirection();
   }
 
   Future<int> getLogLevel() async {
-    return await _flutterFFmpeg.getLogLevel();
+    return await _flutterFFmpegConfig.getLogLevel();
   }
 
   Future<void> setLogLevel(int logLevel) async {
-    return await _flutterFFmpeg.setLogLevel(logLevel);
+    return await _flutterFFmpegConfig.setLogLevel(logLevel);
   }
 
   Future<void> enableLogs() async {
-    return await _flutterFFmpeg.enableLogs();
+    return await _flutterFFmpegConfig.enableLogs();
   }
 
   Future<void> disableLogs() async {
-    return await _flutterFFmpeg.disableLogs();
+    return await _flutterFFmpegConfig.disableLogs();
   }
 
   Future<void> enableStatistics() async {
-    return await _flutterFFmpeg.enableStatistics();
+    return await _flutterFFmpegConfig.enableStatistics();
   }
 
   Future<void> disableStatistics() async {
-    return await _flutterFFmpeg.disableStatistics();
+    return await _flutterFFmpegConfig.disableStatistics();
   }
 
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
-    return await _flutterFFmpeg.getLastReceivedStatistics();
+    return await _flutterFFmpegConfig.getLastReceivedStatistics();
   }
 
   Future<void> resetStatistics() async {
-    return await _flutterFFmpeg.resetStatistics();
+    return await _flutterFFmpegConfig.resetStatistics();
   }
 
   Future<void> setFontconfigConfigurationPath(String path) async {
-    return await _flutterFFmpeg.setFontconfigConfigurationPath(path);
+    return await _flutterFFmpegConfig.setFontconfigConfigurationPath(path);
   }
 
   Future<void> setFontDirectory(
       String fontDirectory, Map<String, String> fontNameMap) async {
-    return await _flutterFFmpeg.setFontDirectory(fontDirectory, fontNameMap);
+    return await _flutterFFmpegConfig.setFontDirectory(fontDirectory, fontNameMap);
   }
 
   Future<String> getPackageName() async {
-    return await _flutterFFmpeg.getPackageName();
+    return await _flutterFFmpegConfig.getPackageName();
   }
 
   Future<List<dynamic>> getExternalLibraries() async {
-    return await _flutterFFmpeg.getExternalLibraries();
+    return await _flutterFFmpegConfig.getExternalLibraries();
   }
 
   Future<int> getLastReturnCode() async {
-    return await _flutterFFmpeg.getLastReturnCode();
+    return await _flutterFFmpegConfig.getLastReturnCode();
   }
 
   Future<String> getLastCommandOutput() async {
-    return await _flutterFFmpeg.getLastCommandOutput();
+    return await _flutterFFmpegConfig.getLastCommandOutput();
   }
 
   Future<Map<dynamic, dynamic>> getMediaInformation(String path) async {
-    return await _flutterFFmpeg.getMediaInformation(path);
+    return await _flutterFFprobe.getMediaInformation(path);
   }
 
   Future<String> registerNewFFmpegPipe() async {
-    return await _flutterFFmpeg.registerNewFFmpegPipe();
+    return await _flutterFFmpegConfig.registerNewFFmpegPipe();
   }
 
   void _changedCodec(String selectedCodec) {
@@ -595,9 +632,32 @@ class FlutterFFmpegTestAppState extends State<MainPage>
                 Container(
                   padding: const EdgeInsets.only(bottom: 20),
                   child: new InkWell(
-                    onTap: () => testRunCommand(),
+                    onTap: () => testRunFFmpegCommand(),
                     child: new Container(
-                      width: 100,
+                      width: 130,
+                      height: 38,
+                      decoration: new BoxDecoration(
+                        color: Color(0xFF2ECC71),
+                        borderRadius: new BorderRadius.circular(5),
+                      ),
+                      child: new Center(
+                        child: new Text(
+                          'RUN FFMPEG',
+                          style: new TextStyle(
+                              fontSize: 14.0,
+                              fontWeight: FontWeight.bold,
+                              color: Colors.white),
+                        ),
+                      ),
+                    ),
+                  ),
+                ),
+                Container(
+                  padding: const EdgeInsets.only(bottom: 20),
+                  child: new InkWell(
+                    onTap: () => testRunFFprobeCommand(),
+                    child: new Container(
+                      width: 130,
                       height: 38,
                       decoration: new BoxDecoration(
                         color: Color(0xFF2ECC71),
@@ -605,7 +665,7 @@ class FlutterFFmpegTestAppState extends State<MainPage>
                       ),
                       child: new Center(
                         child: new Text(
-                          'RUN',
+                          'RUN FFPROBE',
                           style: new TextStyle(
                               fontSize: 14.0,
                               fontWeight: FontWeight.bold,
@@ -702,7 +762,7 @@ class FlutterFFmpegTestAppState extends State<MainPage>
   }
 
   void testParseSimpleCommand() {
-    var argumentArray = _flutterFFmpeg.parseArguments(
+    var argumentArray = FlutterFFmpeg.parseArguments(
         "-hide_banner   -loop 1  -i file.jpg  -filter_complex  [0:v]setpts=PTS-STARTPTS[video] -map [video] -vsync 2 -async 1  video.mp4");
 
     assert(argumentArray != null);
@@ -725,7 +785,7 @@ class FlutterFFmpegTestAppState extends State<MainPage>
   }
 
   void testParseSingleQuotesInCommand() {
-    var argumentArray = _flutterFFmpeg.parseArguments(
+    var argumentArray = FlutterFFmpeg.parseArguments(
         "-loop 1 'file one.jpg'  -filter_complex  '[0:v]setpts=PTS-STARTPTS[video]'  -map  [video]  video.mp4 ");
 
     assert(argumentArray != null);
@@ -742,7 +802,7 @@ class FlutterFFmpegTestAppState extends State<MainPage>
   }
 
   void testParseDoubleQuotesInCommand() {
-    var argumentArray = _flutterFFmpeg.parseArguments(
+    var argumentArray = FlutterFFmpeg.parseArguments(
         "-loop  1 \"file one.jpg\"   -filter_complex \"[0:v]setpts=PTS-STARTPTS[video]\"  -map  [video]  video.mp4 ");
 
     assert(argumentArray != null);
@@ -757,7 +817,7 @@ class FlutterFFmpegTestAppState extends State<MainPage>
     assert("[video]" == argumentArray[6]);
     assert("video.mp4" == argumentArray[7]);
 
-    argumentArray = _flutterFFmpeg.parseArguments(
+    argumentArray = FlutterFFmpeg.parseArguments(
         " -i   file:///tmp/input.mp4 -vcodec libx264 -vf \"scale=1024:1024,pad=width=1024:height=1024:x=0:y=0:color=black\"  -acodec copy  -q:v 0  -q:a   0 video.mp4");
 
     assert(argumentArray != null);
@@ -780,7 +840,7 @@ class FlutterFFmpegTestAppState extends State<MainPage>
   }
 
   void testParseDoubleQuotesAndEscapesInCommand() {
-    var argumentArray = _flutterFFmpeg.parseArguments(
+    var argumentArray = FlutterFFmpeg.parseArguments(
         "  -i   file:///tmp/input.mp4 -vf \"subtitles=file:///tmp/subtitles.srt:force_style=\'FontSize=16,PrimaryColour=&HFFFFFF&\'\" -vcodec libx264   -acodec copy  -q:v 0 -q:a  0  video.mp4");
 
     assert(argumentArray != null);
@@ -802,7 +862,7 @@ class FlutterFFmpegTestAppState extends State<MainPage>
     assert("0" == argumentArray[11]);
     assert("video.mp4" == argumentArray[12]);
 
-    argumentArray = _flutterFFmpeg.parseArguments(
+    argumentArray = FlutterFFmpeg.parseArguments(
         "  -i   file:///tmp/input.mp4 -vf \"subtitles=file:///tmp/subtitles.srt:force_style=\\\"FontSize=16,PrimaryColour=&HFFFFFF&\\\"\" -vcodec libx264   -acodec copy  -q:v 0 -q:a  0  video.mp4");
 
     assert(argumentArray != null);

+ 9 - 9
example/pubspec.lock

@@ -7,7 +7,7 @@ packages:
       name: archive
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.0.10"
+    version: "2.0.11"
   args:
     dependency: transitive
     description:
@@ -21,7 +21,7 @@ packages:
       name: async
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.3.0"
+    version: "2.4.0"
   boolean_selector:
     dependency: transitive
     description:
@@ -72,10 +72,10 @@ packages:
   flutter_ffmpeg:
     dependency: "direct main"
     description:
-      name: flutter_ffmpeg
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.2.8"
+      path: ".."
+      relative: true
+    source: path
+    version: "0.2.9"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -94,14 +94,14 @@ packages:
       name: matcher
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.12.5"
+    version: "0.12.6"
   meta:
     dependency: transitive
     description:
       name: meta
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.7"
+    version: "1.1.8"
   path:
     dependency: "direct main"
     description:
@@ -190,7 +190,7 @@ packages:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.5"
+    version: "0.2.11"
   typed_data:
     dependency: transitive
     description:

+ 2 - 1
example/pubspec.yaml

@@ -12,7 +12,8 @@ dependencies:
   cupertino_icons: ^0.1.2
   path: ^1.6.4
   path_provider: ^1.4.0
-  flutter_ffmpeg: ^0.2.8
+  flutter_ffmpeg:
+    path: ..
 
 dev_dependencies:
   flutter_test:

+ 13 - 16
ios/Classes/FlutterFfmpegPlugin.m

@@ -20,8 +20,9 @@
 #import "FlutterFFmpegPlugin.h"
 
 #import <mobileffmpeg/ArchDetect.h>
-#import <mobileffmpeg/MobileFFmpeg.h>
 #import <mobileffmpeg/MobileFFmpegConfig.h>
+#import <mobileffmpeg/MobileFFmpeg.h>
+#import <mobileffmpeg/MobileFFprobe.h>
 
 static NSString *const PLATFORM_NAME = @"ios";
 
@@ -88,9 +89,9 @@ static NSString *const EVENT_STAT = @"FlutterFFmpegStatisticsCallback";
 
     } else if ([@"getFFmpegVersion" isEqualToString:call.method]) {
 
-        result([FlutterFFmpegPlugin toStringDictionary:KEY_VERSION :[MobileFFmpeg getFFmpegVersion]]);
+        result([FlutterFFmpegPlugin toStringDictionary:KEY_VERSION :[MobileFFmpegConfig getFFmpegVersion]]);
 
-    } else if ([@"executeWithArguments" isEqualToString:call.method]) {
+    } else if ([@"executeFFmpegWithArguments" isEqualToString:call.method]) {
 
         NSLog(@"Running FFmpeg with arguments: %@.\n", arguments);
 
@@ -103,18 +104,15 @@ static NSString *const EVENT_STAT = @"FlutterFFmpegStatisticsCallback";
             result([FlutterFFmpegPlugin toIntDictionary:KEY_RC :[NSNumber numberWithInt:rc]]);
         });
 
-    } else if ([@"execute" isEqualToString:call.method]) {
-
-        if (delimiter == nil) {
-            delimiter = @" ";
-        }
+    } else if ([@"executeFFprobeWithArguments" isEqualToString:call.method]) {
 
-        NSLog(@"Running FFmpeg command: %@ with delimiter %@.\n", command, delimiter);
+        NSLog(@"Running FFprobe with arguments: %@.\n", arguments);
 
         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-            int rc = [MobileFFmpeg execute:command delimiter:delimiter];
 
-            NSLog(@"FFmpeg exited with rc: %d\n", rc);
+            int rc = [MobileFFprobe executeWithArguments:arguments];
+
+            NSLog(@"FFprobe exited with rc: %d\n", rc);
 
             result([FlutterFFmpegPlugin toIntDictionary:KEY_RC :[NSNumber numberWithInt:rc]]);
         });
@@ -189,23 +187,22 @@ static NSString *const EVENT_STAT = @"FlutterFFmpegStatisticsCallback";
 
     } else if ([@"getLastReturnCode" isEqualToString:call.method]) {
 
-        int lastReturnCode = [MobileFFmpeg getLastReturnCode];
+        int lastReturnCode = [MobileFFmpegConfig getLastReturnCode];
         result([FlutterFFmpegPlugin toIntDictionary:KEY_LAST_RC :[NSNumber numberWithInt:lastReturnCode]]);
 
     } else if ([@"getLastCommandOutput" isEqualToString:call.method]) {
 
-        NSString *lastCommandOutput = [MobileFFmpeg getLastCommandOutput];
+        NSString *lastCommandOutput = [MobileFFmpegConfig getLastCommandOutput];
         result([FlutterFFmpegPlugin toStringDictionary:KEY_LAST_COMMAND_OUTPUT :lastCommandOutput]);
 
     } else if ([@"getMediaInformation" isEqualToString:call.method]) {
 
         NSString* path = call.arguments[@"path"];
-        NSNumber* timeout = call.arguments[@"timeout"];
 
-        NSLog(@"Getting media information for %@ with timeout %d.\n", path, [timeout intValue]);
+        NSLog(@"Getting media information for %@.\n", path);
 
         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-            MediaInformation *mediaInformation = [MobileFFmpeg getMediaInformation:path timeout:[timeout intValue]];
+            MediaInformation *mediaInformation = [MobileFFprobe getMediaInformation:path];
             result([FlutterFFmpegPlugin toMediaInformationDictionary:mediaInformation]);
         });
 

+ 17 - 17
ios/flutter_ffmpeg.podspec

@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = 'flutter_ffmpeg'
-  s.version          = '0.2.8'
+  s.version          = '0.2.9'
   s.summary          = 'FFmpeg plugin for Flutter.'
   s.description      = 'FFmpeg plugin based on mobile-ffmpeg for Flutter.'
   s.homepage         = 'https://github.com/tanersener/flutter-ffmpeg'
@@ -24,112 +24,112 @@ Pod::Spec.new do |s|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-min', '4.3'
+    ss.dependency 'mobile-ffmpeg-min', '4.3.1'
   end
 
   s.subspec 'min-lts' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-min', '4.2.2.LTS'
+    ss.dependency 'mobile-ffmpeg-min', '4.3.1.LTS'
   end
 
   s.subspec 'min-gpl' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-min-gpl', '4.3'
+    ss.dependency 'mobile-ffmpeg-min-gpl', '4.3.1'
   end
 
   s.subspec 'min-gpl-lts' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-min-gpl', '4.2.2.LTS'
+    ss.dependency 'mobile-ffmpeg-min-gpl', '4.3.1.LTS'
   end
 
   s.subspec 'https' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-https', '4.3'
+    ss.dependency 'mobile-ffmpeg-https', '4.3.1'
   end
 
   s.subspec 'https-lts' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-https', '4.2.2.LTS'
+    ss.dependency 'mobile-ffmpeg-https', '4.3.1.LTS'
   end
 
   s.subspec 'https-gpl' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-https-gpl', '4.3'
+    ss.dependency 'mobile-ffmpeg-https-gpl', '4.3.1'
   end
 
   s.subspec 'https-gpl-lts' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-https-gpl', '4.2.2.LTS'
+    ss.dependency 'mobile-ffmpeg-https-gpl', '4.3.1.LTS'
   end
 
   s.subspec 'audio' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-audio', '4.3'
+    ss.dependency 'mobile-ffmpeg-audio', '4.3.1'
   end
 
   s.subspec 'audio-lts' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-audio', '4.2.2.LTS'
+    ss.dependency 'mobile-ffmpeg-audio', '4.3.1.LTS'
   end
 
   s.subspec 'video' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-video', '4.3'
+    ss.dependency 'mobile-ffmpeg-video', '4.3.1'
   end
 
   s.subspec 'video-lts' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-video', '4.2.2.LTS'
+    ss.dependency 'mobile-ffmpeg-video', '4.3.1.LTS'
   end
 
   s.subspec 'full' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-full', '4.3'
+    ss.dependency 'mobile-ffmpeg-full', '4.3.1'
   end
 
   s.subspec 'full-lts' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-full', '4.2.2.LTS'
+    ss.dependency 'mobile-ffmpeg-full', '4.3.1.LTS'
   end
 
   s.subspec 'full-gpl' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-full-gpl', '4.3'
+    ss.dependency 'mobile-ffmpeg-full-gpl', '4.3.1'
   end
 
   s.subspec 'full-gpl-lts' do |ss|
     ss.source_files        = 'Classes/**/*'
     ss.public_header_files = 'Classes/**/*.h'
 
-    ss.dependency 'mobile-ffmpeg-full-gpl', '4.2.2.LTS'
+    ss.dependency 'mobile-ffmpeg-full-gpl', '4.3.1.LTS'
   end
 
 end

+ 82 - 47
lib/flutter_ffmpeg.dart

@@ -21,7 +21,7 @@ import 'dart:async';
 
 import 'package:flutter/services.dart';
 
-class FlutterFFmpeg {
+class FlutterFFmpegConfig {
   static const MethodChannel _methodChannel =
       const MethodChannel('flutter_ffmpeg');
   static const EventChannel _eventChannel =
@@ -37,7 +37,7 @@ class FlutterFFmpeg {
       double videoQuality,
       double videoFps) statisticsCallback;
 
-  FlutterFFmpeg() {
+  FlutterFFmpegConfig() {
     logCallback = null;
     statisticsCallback = null;
 
@@ -132,39 +132,6 @@ class FlutterFFmpeg {
     }
   }
 
-  /// Executes FFmpeg with [commandArguments] provided.
-  Future<int> executeWithArguments(List<String> arguments) async {
-    try {
-      final Map<dynamic, dynamic> result = await _methodChannel
-          .invokeMethod('executeWithArguments', {'arguments': arguments});
-      return result['rc'];
-    } on PlatformException catch (e) {
-      print("Plugin error: ${e.message}");
-      return -1;
-    }
-  }
-
-  /// Executes FFmpeg [command] provided.
-  Future<int> execute(String command) async {
-    try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
-          'executeWithArguments', {'arguments': parseArguments(command)});
-      return result['rc'];
-    } on PlatformException catch (e) {
-      print("Plugin error: ${e.message}");
-      return -1;
-    }
-  }
-
-  /// Cancels an ongoing operation.
-  Future<void> cancel() async {
-    try {
-      await _methodChannel.invokeMethod('cancel');
-    } on PlatformException catch (e) {
-      print("Plugin error: ${e.message}");
-    }
-  }
-
   /// Enables redirection
   Future<void> enableRedirection() async {
     try {
@@ -356,6 +323,7 @@ class FlutterFFmpeg {
   }
 
   /// Returns log output of last executed command. Please note that disabling redirection using
+  /// This method does not support executing multiple concurrent commands. If you execute multiple commands at the same time, this method will return output from all executions.
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
@@ -368,32 +336,58 @@ class FlutterFFmpeg {
     }
   }
 
-  /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
-      [int timeout = 10000]) async {
+  /// Creates a new FFmpeg pipe and returns its path.
+  Future<String> registerNewFFmpegPipe() async {
     try {
-      return await _methodChannel.invokeMethod(
-          'getMediaInformation', {'path': path, 'timeout': timeout});
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('registerNewFFmpegPipe');
+      return result['pipe'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;
     }
   }
+}
 
-  /// Creates a new FFmpeg pipe and returns its path.
-  Future<String> registerNewFFmpegPipe() async {
+class FlutterFFmpeg {
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+
+  /// Executes FFmpeg with [commandArguments] provided.
+  Future<int> executeWithArguments(List<String> arguments) async {
     try {
-      final Map<dynamic, dynamic> result =
-          await _methodChannel.invokeMethod('registerNewFFmpegPipe');
-      return result['pipe'];
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeFFmpegWithArguments', {'arguments': arguments});
+      return result['rc'];
+    } on PlatformException catch (e) {
+      print("Plugin error: ${e.message}");
+      return -1;
+    }
+  }
+
+  /// Executes FFmpeg [command] provided.
+  Future<int> execute(String command) async {
+    try {
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'executeFFmpegWithArguments', {'arguments': FlutterFFmpeg.parseArguments(command)});
+      return result['rc'];
+    } on PlatformException catch (e) {
+      print("Plugin error: ${e.message}");
+      return -1;
+    }
+  }
+
+  /// Cancels an ongoing operation.
+  Future<void> cancel() async {
+    try {
+      await _methodChannel.invokeMethod('cancel');
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
-      return null;
     }
   }
 
   /// Parses the given [command] into arguments.
-  List<String> parseArguments(String command) {
+  static List<String> parseArguments(String command) {
     List<String> argumentList = new List();
     StringBuffer currentArgument = new StringBuffer();
 
@@ -446,3 +440,44 @@ class FlutterFFmpeg {
     return argumentList;
   }
 }
+
+class FlutterFFprobe {
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+
+  /// Executes FFprobe with [commandArguments] provided.
+  Future<int> executeWithArguments(List<String> arguments) async {
+    try {
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeFFprobeWithArguments', {'arguments': arguments});
+      return result['rc'];
+    } on PlatformException catch (e) {
+      print("Plugin error: ${e.message}");
+      return -1;
+    }
+  }
+
+  /// Executes FFprobe [command] provided.
+  Future<int> execute(String command) async {
+    try {
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'executeFFprobeWithArguments', {'arguments': FlutterFFmpeg.parseArguments(command)});
+      return result['rc'];
+    } on PlatformException catch (e) {
+      print("Plugin error: ${e.message}");
+      return -1;
+    }
+  }
+
+  /// Returns media information for given [path]
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path) async {
+    try {
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path});
+    } on PlatformException catch (e) {
+      print("Plugin error: ${e.message}");
+      return null;
+    }
+  }
+
+}

+ 7 - 0
lib/log_level.dart

@@ -18,6 +18,11 @@
  */
 
 class LogLevel {
+  ///
+  /// This log level is used to specify logs printed to stderr by ffmpeg.
+  /// Logs that has this level are not filtered and always redirected.
+  static const int AV_LOG_STDERR = -16;
+
   /// Print no output.
   static const int AV_LOG_QUIET = -8;
 
@@ -68,6 +73,8 @@ class LogLevel {
         return "FATAL";
       case LogLevel.AV_LOG_PANIC:
         return "PANIC";
+      case LogLevel.AV_LOG_STDERR:
+        return "STDERR";
       case LogLevel.AV_LOG_QUIET:
       default:
         return "";

+ 2 - 2
pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
 description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
-version: 0.2.8
+version: 0.2.9
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^1.4.0
+  path_provider: ^1.5.1
   path: ^1.6.4
 
 flutter: