Przeglądaj źródła

Merge pull request #34 from tanersener/development

merge v0.2.3 to master
Taner Sener 6 lat temu
rodzic
commit
47c7f8ea6a
100 zmienionych plików z 2019 dodań i 605 usunięć
  1. 1 0
      .gitignore
  2. 6 0
      CHANGELOG.md
  3. 13 11
      README.md
  4. 4 2
      android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  5. 4 2
      android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  6. 4 2
      android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  7. 20 17
      android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  8. 77 0
      android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  9. 9 9
      example/pubspec.lock
  10. 1 0
      flutter_ffmpeg.iml
  11. 52 22
      lib/flutter_ffmpeg.dart
  12. 2 1
      packages/flutter_ffmpeg_audio/.gitignore
  13. 4 2
      packages/flutter_ffmpeg_audio/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  14. 4 2
      packages/flutter_ffmpeg_audio/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  15. 4 2
      packages/flutter_ffmpeg_audio/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  16. 20 17
      packages/flutter_ffmpeg_audio/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  17. 77 0
      packages/flutter_ffmpeg_audio/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  18. 52 22
      packages/flutter_ffmpeg_audio/lib/flutter_ffmpeg.dart
  19. 3 3
      packages/flutter_ffmpeg_audio/pubspec.yaml
  20. 2 1
      packages/flutter_ffmpeg_audio_lts/.gitignore
  21. 4 2
      packages/flutter_ffmpeg_audio_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  22. 4 2
      packages/flutter_ffmpeg_audio_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  23. 4 2
      packages/flutter_ffmpeg_audio_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  24. 20 17
      packages/flutter_ffmpeg_audio_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  25. 77 0
      packages/flutter_ffmpeg_audio_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  26. 52 22
      packages/flutter_ffmpeg_audio_lts/lib/flutter_ffmpeg.dart
  27. 3 3
      packages/flutter_ffmpeg_audio_lts/pubspec.yaml
  28. 2 1
      packages/flutter_ffmpeg_full-gpl/.gitignore
  29. 4 2
      packages/flutter_ffmpeg_full-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  30. 4 2
      packages/flutter_ffmpeg_full-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  31. 4 2
      packages/flutter_ffmpeg_full-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  32. 20 17
      packages/flutter_ffmpeg_full-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  33. 77 0
      packages/flutter_ffmpeg_full-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  34. 52 22
      packages/flutter_ffmpeg_full-gpl/lib/flutter_ffmpeg.dart
  35. 3 3
      packages/flutter_ffmpeg_full-gpl/pubspec.yaml
  36. 2 1
      packages/flutter_ffmpeg_full-gpl_lts/.gitignore
  37. 4 2
      packages/flutter_ffmpeg_full-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  38. 4 2
      packages/flutter_ffmpeg_full-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  39. 4 2
      packages/flutter_ffmpeg_full-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  40. 20 17
      packages/flutter_ffmpeg_full-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  41. 77 0
      packages/flutter_ffmpeg_full-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  42. 52 22
      packages/flutter_ffmpeg_full-gpl_lts/lib/flutter_ffmpeg.dart
  43. 3 3
      packages/flutter_ffmpeg_full-gpl_lts/pubspec.yaml
  44. 2 1
      packages/flutter_ffmpeg_full/.gitignore
  45. 4 2
      packages/flutter_ffmpeg_full/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  46. 4 2
      packages/flutter_ffmpeg_full/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  47. 4 2
      packages/flutter_ffmpeg_full/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  48. 20 17
      packages/flutter_ffmpeg_full/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  49. 77 0
      packages/flutter_ffmpeg_full/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  50. 52 22
      packages/flutter_ffmpeg_full/lib/flutter_ffmpeg.dart
  51. 3 3
      packages/flutter_ffmpeg_full/pubspec.yaml
  52. 2 1
      packages/flutter_ffmpeg_full_lts/.gitignore
  53. 4 2
      packages/flutter_ffmpeg_full_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  54. 4 2
      packages/flutter_ffmpeg_full_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  55. 4 2
      packages/flutter_ffmpeg_full_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  56. 20 17
      packages/flutter_ffmpeg_full_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  57. 77 0
      packages/flutter_ffmpeg_full_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  58. 52 22
      packages/flutter_ffmpeg_full_lts/lib/flutter_ffmpeg.dart
  59. 3 3
      packages/flutter_ffmpeg_full_lts/pubspec.yaml
  60. 2 1
      packages/flutter_ffmpeg_https-gpl/.gitignore
  61. 4 2
      packages/flutter_ffmpeg_https-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  62. 4 2
      packages/flutter_ffmpeg_https-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  63. 4 2
      packages/flutter_ffmpeg_https-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  64. 20 17
      packages/flutter_ffmpeg_https-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  65. 77 0
      packages/flutter_ffmpeg_https-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  66. 52 22
      packages/flutter_ffmpeg_https-gpl/lib/flutter_ffmpeg.dart
  67. 3 3
      packages/flutter_ffmpeg_https-gpl/pubspec.yaml
  68. 2 1
      packages/flutter_ffmpeg_https-gpl_lts/.gitignore
  69. 4 2
      packages/flutter_ffmpeg_https-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  70. 4 2
      packages/flutter_ffmpeg_https-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  71. 4 2
      packages/flutter_ffmpeg_https-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  72. 20 17
      packages/flutter_ffmpeg_https-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  73. 77 0
      packages/flutter_ffmpeg_https-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  74. 52 22
      packages/flutter_ffmpeg_https-gpl_lts/lib/flutter_ffmpeg.dart
  75. 3 3
      packages/flutter_ffmpeg_https-gpl_lts/pubspec.yaml
  76. 2 1
      packages/flutter_ffmpeg_https/.gitignore
  77. 4 2
      packages/flutter_ffmpeg_https/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  78. 4 2
      packages/flutter_ffmpeg_https/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  79. 4 2
      packages/flutter_ffmpeg_https/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  80. 20 17
      packages/flutter_ffmpeg_https/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  81. 77 0
      packages/flutter_ffmpeg_https/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  82. 52 22
      packages/flutter_ffmpeg_https/lib/flutter_ffmpeg.dart
  83. 3 3
      packages/flutter_ffmpeg_https/pubspec.yaml
  84. 2 1
      packages/flutter_ffmpeg_https_lts/.gitignore
  85. 4 2
      packages/flutter_ffmpeg_https_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  86. 4 2
      packages/flutter_ffmpeg_https_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  87. 4 2
      packages/flutter_ffmpeg_https_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  88. 20 17
      packages/flutter_ffmpeg_https_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  89. 77 0
      packages/flutter_ffmpeg_https_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  90. 52 22
      packages/flutter_ffmpeg_https_lts/lib/flutter_ffmpeg.dart
  91. 3 3
      packages/flutter_ffmpeg_https_lts/pubspec.yaml
  92. 2 1
      packages/flutter_ffmpeg_min-gpl/.gitignore
  93. 4 2
      packages/flutter_ffmpeg_min-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java
  94. 4 2
      packages/flutter_ffmpeg_min-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java
  95. 4 2
      packages/flutter_ffmpeg_min-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java
  96. 20 17
      packages/flutter_ffmpeg_min-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java
  97. 77 0
      packages/flutter_ffmpeg_min-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java
  98. 52 22
      packages/flutter_ffmpeg_min-gpl/lib/flutter_ffmpeg.dart
  99. 3 3
      packages/flutter_ffmpeg_min-gpl/pubspec.yaml
  100. 2 1
      packages/flutter_ffmpeg_min-gpl_lts/.gitignore

+ 1 - 0
.gitignore

@@ -5,3 +5,4 @@
 build/
 /pubspec.lock
 /.packages
+/.gradle/

+ 6 - 0
CHANGELOG.md

@@ -1,3 +1,9 @@
+## 0.2.3
+- Fixed flutter v1.6 compatibility errors on packages
+
+## 0.2.2
+- Fixed flutter v1.6 compatibility errors
+
 ## 0.2.1
 - Fixed documentation errors
 - Updated package description

+ 13 - 11
README.md

@@ -1,6 +1,6 @@
 # flutter_ffmpeg 
 
-![GitHub release](https://img.shields.io/badge/release-v0.2.1-blue.svg) 
+![GitHub release](https://img.shields.io/badge/release-v0.2.3-blue.svg) 
 ![](https://img.shields.io/pub/v/flutter_ffmpeg.svg)
 
 FFmpeg plugin for Flutter. Supports iOS and Android.
@@ -10,10 +10,10 @@ FFmpeg plugin for Flutter. Supports iOS and Android.
 ### 1. Features
 - Based on MobileFFmpeg
 - Supports
-    - Both Android and IOS
+    - Both Android (API 21+) and iOS (SDK 9.3+)
     - FFmpeg `v4.2-dev-x` (master) 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
+    - `armv7`, `armv7s`, `arm64`, `arm64e`, `i386` and `x86_64` architectures on iOS
     - 24 external libraries
 
         `fontconfig`, `freetype`, `fribidi`, `gmp`, `gnutls`, `kvazaar`, `lame`, `libaom`, `libass`, `libiconv`, `libilbc`, `libtheora`, `libvorbis`, `libvpx`, `libwebp`, `libxml2`, `opencore-amr`, `opus`, `shine`, `snappy`, `soxr`, `speex`, `twolame`, `wavpack`
@@ -23,7 +23,7 @@ FFmpeg plugin for Flutter. Supports iOS and Android.
         `vid.stab`, `x264`, `x265`, `xvidcore`
 
     - `zlib` and `MediaCodec` Android system libraries
-    - `bzip2`, `zlib` IOS system libraries and `AudioToolbox`, `CoreImage`, `VideoToolbox`, `AVFoundation` IOS system frameworks
+    - `bzip2`, `zlib` 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
@@ -79,7 +79,7 @@ Installation of `FlutterFFmpeg` using `pub` enables the default package, which i
       flutter_ffmpeg:
         git:
           url: git://github.com/tanersener/flutter-ffmpeg.git
-          ref: v0.2.1
+          ref: v0.2.3
           path: packages/flutter_ffmpeg_<package_name>
 
     ```
@@ -117,18 +117,18 @@ In order to install the `LTS` variant, install the `flutter_ffmpeg_https_lts` pa
         <td align="center">arm-v7a-neon<br>arm64-v8a<br>x86<br>x86-64</td>
         <td align="center">arm-v7a<br>arm-v7a-neon<br>arm64-v8a<br>x86<br>x86-64</td>
     </tr>
-    <tr>
-        <td align="center">IOS SDK</td>
-        <td align="center">12.1</td>
-        <td align="center">9.3</td>
-    </tr>
     <tr>
         <td align="center">Xcode Support</td>
         <td align="center">10.1</td>
         <td align="center">7.3.1</td>
     </tr>
     <tr>
-        <td align="center">IOS Architectures</td>
+        <td align="center">iOS SDK</td>
+        <td align="center">12.1</td>
+        <td align="center">9.3</td>
+    </tr>
+    <tr>
+        <td align="center">iOS Architectures</td>
         <td align="center">arm64<br>arm64e<br>x86-64</td>
         <td align="center">armv7<br>arm64<br>i386<br>x86-64</td>
     </tr>
@@ -285,6 +285,8 @@ In order to install the `LTS` variant, install the `flutter_ffmpeg_https_lts` pa
 
 - If your commands include unnecessary quotes or space characters, your command will fail with `No such filter: ' '` errors. Please check your command and remove them.
 
+- `flutter_ffmpeg` uses file system paths, it does not know what an assets folder or project folder is. So you can't use resources on those folders directly, you need to provide full paths of those resources.
+
 - `execute` method is overloaded and has an optional delimiter parameter. Delimiter defines how the command string will be split into arguments. 
 When delimiter is not specified the space character is used as the default delimiter. 
 Based on this, if one or more of your command arguments include a space character, in filename path or in `-filter_complex` block, then your command string will be split into invalid arguments and execution will fail.  

+ 4 - 2
android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 9 - 9
example/pubspec.lock

@@ -7,7 +7,7 @@ packages:
       name: async
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.0.8"
+    version: "2.2.0"
   boolean_selector:
     dependency: transitive
     description:
@@ -47,7 +47,7 @@ packages:
       path: ".."
       relative: true
     source: path
-    version: "0.2.1"
+    version: "0.2.3"
   flutter_test:
     dependency: "direct dev"
     description: flutter
@@ -59,7 +59,7 @@ packages:
       name: matcher
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.12.3+1"
+    version: "0.12.5"
   meta:
     dependency: transitive
     description:
@@ -87,14 +87,14 @@ packages:
       name: pedantic
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.4.0"
+    version: "1.5.0"
   quiver:
     dependency: transitive
     description:
       name: quiver
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.0.1"
+    version: "2.0.3"
   sky_engine:
     dependency: transitive
     description: flutter
@@ -106,7 +106,7 @@ packages:
       name: source_span
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.5.4"
+    version: "1.5.5"
   stack_trace:
     dependency: transitive
     description:
@@ -120,7 +120,7 @@ packages:
       name: stream_channel
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.6.8"
+    version: "2.0.0"
   string_scanner:
     dependency: transitive
     description:
@@ -141,7 +141,7 @@ packages:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.2"
+    version: "0.2.5"
   typed_data:
     dependency: transitive
     description:
@@ -157,5 +157,5 @@ packages:
     source: hosted
     version: "2.0.8"
 sdks:
-  dart: ">=2.1.0 <3.0.0"
+  dart: ">=2.2.0 <3.0.0"
   flutter: ">=0.1.4 <2.0.0"

+ 1 - 0
flutter_ffmpeg.iml

@@ -25,6 +25,7 @@
       <excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/flutter_assets/packages" />
       <excludeFolder url="file://$MODULE_DIR$/packages" />
     </content>
+    <orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="library" name="Dart SDK" level="project" />
     <orderEntry type="library" name="Flutter Plugins" level="project" />

+ 52 - 22
lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 2 - 1
packages/flutter_ffmpeg_audio/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

+ 4 - 2
packages/flutter_ffmpeg_audio/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_audio/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_audio/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
packages/flutter_ffmpeg_audio/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
packages/flutter_ffmpeg_audio/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 52 - 22
packages/flutter_ffmpeg_audio/lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 3 - 3
packages/flutter_ffmpeg_audio/pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
-description: Flutter plugin to run FFmpeg in the mobile platform. Supports iOS and Android.
-version: 0.2.1
+description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
+version: 0.2.3
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^0.5.0+1
+  path_provider: ^1.1.0
   path: ^1.6.2
 
 flutter:

+ 2 - 1
packages/flutter_ffmpeg_audio_lts/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

+ 4 - 2
packages/flutter_ffmpeg_audio_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_audio_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_audio_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
packages/flutter_ffmpeg_audio_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
packages/flutter_ffmpeg_audio_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 52 - 22
packages/flutter_ffmpeg_audio_lts/lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 3 - 3
packages/flutter_ffmpeg_audio_lts/pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
-description: Flutter plugin to run FFmpeg in the mobile platform. Supports iOS and Android.
-version: 0.2.1
+description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
+version: 0.2.3
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^0.5.0+1
+  path_provider: ^1.1.0
   path: ^1.6.2
 
 flutter:

+ 2 - 1
packages/flutter_ffmpeg_full-gpl/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

+ 4 - 2
packages/flutter_ffmpeg_full-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_full-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_full-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
packages/flutter_ffmpeg_full-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
packages/flutter_ffmpeg_full-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 52 - 22
packages/flutter_ffmpeg_full-gpl/lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 3 - 3
packages/flutter_ffmpeg_full-gpl/pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
-description: Flutter plugin to run FFmpeg in the mobile platform. Supports iOS and Android.
-version: 0.2.1
+description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
+version: 0.2.3
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^0.5.0+1
+  path_provider: ^1.1.0
   path: ^1.6.2
 
 flutter:

+ 2 - 1
packages/flutter_ffmpeg_full-gpl_lts/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

+ 4 - 2
packages/flutter_ffmpeg_full-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_full-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_full-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
packages/flutter_ffmpeg_full-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
packages/flutter_ffmpeg_full-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 52 - 22
packages/flutter_ffmpeg_full-gpl_lts/lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 3 - 3
packages/flutter_ffmpeg_full-gpl_lts/pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
-description: Flutter plugin to run FFmpeg in the mobile platform. Supports iOS and Android.
-version: 0.2.1
+description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
+version: 0.2.3
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^0.5.0+1
+  path_provider: ^1.1.0
   path: ^1.6.2
 
 flutter:

+ 2 - 1
packages/flutter_ffmpeg_full/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

+ 4 - 2
packages/flutter_ffmpeg_full/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_full/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_full/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
packages/flutter_ffmpeg_full/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
packages/flutter_ffmpeg_full/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 52 - 22
packages/flutter_ffmpeg_full/lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 3 - 3
packages/flutter_ffmpeg_full/pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
-description: Flutter plugin to run FFmpeg in the mobile platform. Supports iOS and Android.
-version: 0.2.1
+description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
+version: 0.2.3
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^0.5.0+1
+  path_provider: ^1.1.0
   path: ^1.6.2
 
 flutter:

+ 2 - 1
packages/flutter_ffmpeg_full_lts/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

+ 4 - 2
packages/flutter_ffmpeg_full_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_full_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_full_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
packages/flutter_ffmpeg_full_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
packages/flutter_ffmpeg_full_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 52 - 22
packages/flutter_ffmpeg_full_lts/lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 3 - 3
packages/flutter_ffmpeg_full_lts/pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
-description: Flutter plugin to run FFmpeg in the mobile platform. Supports iOS and Android.
-version: 0.2.1
+description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
+version: 0.2.3
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^0.5.0+1
+  path_provider: ^1.1.0
   path: ^1.6.2
 
 flutter:

+ 2 - 1
packages/flutter_ffmpeg_https-gpl/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

+ 4 - 2
packages/flutter_ffmpeg_https-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_https-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_https-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
packages/flutter_ffmpeg_https-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
packages/flutter_ffmpeg_https-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 52 - 22
packages/flutter_ffmpeg_https-gpl/lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 3 - 3
packages/flutter_ffmpeg_https-gpl/pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
-description: Flutter plugin to run FFmpeg in the mobile platform. Supports iOS and Android.
-version: 0.2.1
+description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
+version: 0.2.3
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^0.5.0+1
+  path_provider: ^1.1.0
   path: ^1.6.2
 
 flutter:

+ 2 - 1
packages/flutter_ffmpeg_https-gpl_lts/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

+ 4 - 2
packages/flutter_ffmpeg_https-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_https-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_https-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
packages/flutter_ffmpeg_https-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
packages/flutter_ffmpeg_https-gpl_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 52 - 22
packages/flutter_ffmpeg_https-gpl_lts/lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 3 - 3
packages/flutter_ffmpeg_https-gpl_lts/pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
-description: Flutter plugin to run FFmpeg in the mobile platform. Supports iOS and Android.
-version: 0.2.1
+description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
+version: 0.2.3
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^0.5.0+1
+  path_provider: ^1.1.0
   path: ^1.6.2
 
 flutter:

+ 2 - 1
packages/flutter_ffmpeg_https/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

+ 4 - 2
packages/flutter_ffmpeg_https/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_https/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_https/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
packages/flutter_ffmpeg_https/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
packages/flutter_ffmpeg_https/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 52 - 22
packages/flutter_ffmpeg_https/lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 3 - 3
packages/flutter_ffmpeg_https/pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
-description: Flutter plugin to run FFmpeg in the mobile platform. Supports iOS and Android.
-version: 0.2.1
+description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
+version: 0.2.3
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^0.5.0+1
+  path_provider: ^1.1.0
   path: ^1.6.2
 
 flutter:

+ 2 - 1
packages/flutter_ffmpeg_https_lts/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

+ 4 - 2
packages/flutter_ffmpeg_https_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_https_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_https_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
packages/flutter_ffmpeg_https_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
packages/flutter_ffmpeg_https_lts/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 52 - 22
packages/flutter_ffmpeg_https_lts/lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 3 - 3
packages/flutter_ffmpeg_https_lts/pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
-description: Flutter plugin to run FFmpeg in the mobile platform. Supports iOS and Android.
-version: 0.2.1
+description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
+version: 0.2.3
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^0.5.0+1
+  path_provider: ^1.1.0
   path: ^1.6.2
 
 flutter:

+ 2 - 1
packages/flutter_ffmpeg_min-gpl/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

+ 4 - 2
packages/flutter_ffmpeg_min-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncArgumentsTask.java

@@ -39,10 +39,12 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     private final MethodChannel.Result result;
     private final List<String> arguments;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncArgumentsTask(final List<String> arguments, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncArgumentsTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final List<String> arguments, final MethodChannel.Result result) {
         this.arguments = arguments;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -60,7 +62,7 @@ public class FlutterFFmpegExecuteAsyncArgumentsTask extends AsyncTask<String, In
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_min-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegExecuteAsyncCommandTask.java

@@ -36,8 +36,9 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     private String delimiter;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegExecuteAsyncCommandTask(final String delimiter, final MethodChannel.Result result) {
+    FlutterFFmpegExecuteAsyncCommandTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final String delimiter, final MethodChannel.Result result) {
         if (delimiter == null) {
             this.delimiter = " ";
         } else {
@@ -45,6 +46,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
         }
 
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -66,7 +68,7 @@ public class FlutterFFmpegExecuteAsyncCommandTask extends AsyncTask<String, Inte
 
     @Override
     protected void onPostExecute(final Integer rc) {
-        result.success(FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toIntMap(FlutterFFmpegPlugin.KEY_RC, rc));
     }
 
 }

+ 4 - 2
packages/flutter_ffmpeg_min-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegGetMediaInformationAsyncTask.java

@@ -37,10 +37,12 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     private Integer timeout;
     private final MethodChannel.Result result;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
-    FlutterFFmpegGetMediaInformationAsyncTask(final Integer timeout, final MethodChannel.Result result) {
+    FlutterFFmpegGetMediaInformationAsyncTask(final FlutterFFmpegResultHandler flutterFFmpegResultHandler, final Integer timeout, final MethodChannel.Result result) {
         this.timeout = timeout;
         this.result = result;
+        this.flutterFFmpegResultHandler = flutterFFmpegResultHandler;
     }
 
     @Override
@@ -64,7 +66,7 @@ public class FlutterFFmpegGetMediaInformationAsyncTask extends AsyncTask<String,
 
     @Override
     protected void onPostExecute(final MediaInformation mediaInformation) {
-        result.success(FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
+        flutterFFmpegResultHandler.success(result, FlutterFFmpegPlugin.toMediaInformationMap(mediaInformation));
     }
 
 }

+ 20 - 17
packages/flutter_ffmpeg_min-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegPlugin.java

@@ -80,6 +80,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
     private EventChannel.EventSink eventSink;
     private final Registrar registrar;
+    private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
 
     /**
      * Registers plugin to registry.
@@ -87,17 +88,19 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
      * @param registrar receiver of plugin registration
      */
     public static void registerWith(final Registrar registrar) {
-        FlutterFFmpegPlugin handler = new FlutterFFmpegPlugin(registrar);
+        FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
 
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
-        channel.setMethodCallHandler(handler);
+        channel.setMethodCallHandler(flutterFFmpegPlugin);
 
         final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
-        eventChannel.setStreamHandler(handler);
+        eventChannel.setStreamHandler(flutterFFmpegPlugin);
     }
 
     private FlutterFFmpegPlugin(Registrar registrar) {
         this.registrar = registrar;
+
+        this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
     }
 
     private Context getActiveContext() {
@@ -115,18 +118,18 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         if (call.method.equals("getPlatform")) {
 
             final String abi = AbiDetect.getAbi();
-            result.success(toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
 
         } else if (call.method.equals("getFFmpegVersion")) {
 
             final String version = FFmpeg.getFFmpegVersion();
-            result.success(toStringMap(KEY_VERSION, version));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
 
         } else if (call.method.equals("executeWithArguments")) {
 
             List<String> arguments = call.argument("arguments");
 
-            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(arguments, result);
+            final FlutterFFmpegExecuteAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteAsyncArgumentsTask(flutterFFmpegResultHandler, arguments, result);
             asyncTask.execute("dummy-trigger");
 
         } else if (call.method.equals("execute")) {
@@ -134,7 +137,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
             String command = call.argument("command");
             String delimiter = call.argument("delimiter");
 
-            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(delimiter, result);
+            final FlutterFFmpegExecuteAsyncCommandTask asyncTask = new FlutterFFmpegExecuteAsyncCommandTask(flutterFFmpegResultHandler, delimiter, result);
             asyncTask.execute(command);
 
         } else if (call.method.equals("cancel")) {
@@ -152,7 +155,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getLogLevel")) {
 
             final Level level = Config.getLogLevel();
-            result.success(toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
 
         } else if (call.method.equals("setLogLevel")) {
 
@@ -192,7 +195,7 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         } else if (call.method.equals("getLastReceivedStatistics")) {
 
-            result.success(toMap(Config.getLastReceivedStatistics()));
+            flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
 
         } else if (call.method.equals("resetStatistics")) {
 
@@ -217,22 +220,22 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
         } else if (call.method.equals("getPackageName")) {
 
             final String packageName = Config.getPackageName();
-            result.success(toStringMap(KEY_PACKAGE_NAME, packageName));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
 
         } else if (call.method.equals("getExternalLibraries")) {
 
             final List<String> externalLibraries = Config.getExternalLibraries();
-            result.success(externalLibraries);
+            flutterFFmpegResultHandler.success(result, externalLibraries);
 
         } else if (call.method.equals("getLastReturnCode")) {
 
             int lastReturnCode = FFmpeg.getLastReturnCode();
-            result.success(toIntMap(KEY_LAST_RC, lastReturnCode));
+            flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
 
         } else if (call.method.equals("getLastCommandOutput")) {
 
             final String lastCommandOutput = FFmpeg.getLastCommandOutput();
-            result.success(toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
+            flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
 
         } else if (call.method.equals("getMediaInformation")) {
             final String path = call.argument("path");
@@ -241,11 +244,11 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
                 timeout = 10000;
             }
 
-            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(timeout, result);
+            final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(flutterFFmpegResultHandler, timeout, result);
             asyncTask.execute(path);
 
         } else {
-            result.notImplemented();
+            flutterFFmpegResultHandler.notImplemented(result);
         }
     }
 
@@ -268,13 +271,13 @@ public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.Stre
 
         logWrapperMap.put(EVENT_LOG, logMap);
 
-        eventSink.success(logWrapperMap);
+        flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
     }
 
     protected void emitStatistics(final Statistics statistics) {
         final HashMap<String, Object> statisticsMap = new HashMap<>();
         statisticsMap.put(EVENT_STAT, toMap(statistics));
-        eventSink.success(statisticsMap);
+        flutterFFmpegResultHandler.success(eventSink, statisticsMap);
     }
 
     public static int levelToInt(final Level level) {

+ 77 - 0
packages/flutter_ffmpeg_min-gpl/android/src/main/java/com/arthenica/flutter/ffmpeg/FlutterFFmpegResultHandler.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2019 Taner Sener
+ *
+ * This file is part of FlutterFFmpeg.
+ *
+ * FlutterFFmpeg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * FlutterFFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with FlutterFFmpeg.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.arthenica.flutter.ffmpeg;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import io.flutter.plugin.common.EventChannel;
+import io.flutter.plugin.common.MethodChannel;
+
+/**
+ * <h3>Flutter FFmpeg Result Handler</h3>
+ *
+ * @author Taner Sener
+ * @since 0.2.2
+ */
+class FlutterFFmpegResultHandler {
+    private final Handler handler;
+
+    FlutterFFmpegResultHandler() {
+        handler = new Handler(Looper.getMainLooper());
+    }
+
+    void notImplemented(final MethodChannel.Result result) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.notImplemented();
+                }
+            }
+        });
+    }
+
+    void success(final MethodChannel.Result result, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (result != null) {
+                    result.success(object);
+                }
+            }
+        });
+    }
+
+    void success(final EventChannel.EventSink eventSink, final Object object) {
+        handler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                if (eventSink != null) {
+                    eventSink.success(object);
+                }
+            }
+        });
+    }
+
+}

+ 52 - 22
packages/flutter_ffmpeg_min-gpl/lib/flutter_ffmpeg.dart

@@ -22,11 +22,20 @@ import 'dart:async';
 import 'package:flutter/services.dart';
 
 class FlutterFFmpeg {
-  static const MethodChannel _methodChannel = const MethodChannel('flutter_ffmpeg');
-  static const EventChannel _eventChannel = const EventChannel('flutter_ffmpeg_event');
+  static const MethodChannel _methodChannel =
+      const MethodChannel('flutter_ffmpeg');
+  static const EventChannel _eventChannel =
+      const EventChannel('flutter_ffmpeg_event');
 
   Function(int level, String message) logCallback;
-  Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) statisticsCallback;
+  Function(
+      int time,
+      int size,
+      double bitrate,
+      double speed,
+      int videoFrameNumber,
+      double videoQuality,
+      double videoFps) statisticsCallback;
 
   FlutterFFmpeg() {
     logCallback = null;
@@ -46,8 +55,10 @@ class FlutterFFmpeg {
   void _onEvent(Object event) {
     if (event is Map<dynamic, dynamic>) {
       final Map<String, dynamic> eventMap = event.cast();
-      final Map<dynamic, dynamic> logEvent = eventMap['FlutterFFmpegLogCallback'];
-      final Map<dynamic, dynamic> statisticsEvent = eventMap['FlutterFFmpegStatisticsCallback'];
+      final Map<dynamic, dynamic> logEvent =
+          eventMap['FlutterFFmpegLogCallback'];
+      final Map<dynamic, dynamic> statisticsEvent =
+          eventMap['FlutterFFmpegStatisticsCallback'];
 
       if (logEvent != null) {
         int level = logEvent['level'];
@@ -74,10 +85,12 @@ class FlutterFFmpeg {
           double bitrate = _doublePrecision(statisticsEvent['bitrate'], 2);
           double speed = _doublePrecision(statisticsEvent['speed'], 2);
           int videoFrameNumber = statisticsEvent['videoFrameNumber'];
-          double videoQuality = _doublePrecision(statisticsEvent['videoQuality'], 2);
+          double videoQuality =
+              _doublePrecision(statisticsEvent['videoQuality'], 2);
           double videoFps = _doublePrecision(statisticsEvent['videoFps'], 2);
 
-          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber, videoQuality, videoFps);
+          this.statisticsCallback(time, size, bitrate, speed, videoFrameNumber,
+              videoQuality, videoFps);
         }
       }
     }
@@ -98,7 +111,8 @@ class FlutterFFmpeg {
   /// Returns FFmpeg version bundled within the library.
   Future<String> getFFmpegVersion() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getFFmpegVersion');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getFFmpegVersion');
       return result['version'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -109,7 +123,8 @@ class FlutterFFmpeg {
   /// Returns platform name where library is loaded.
   Future<String> getPlatform() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPlatform');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPlatform');
       return result['platform'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -120,7 +135,8 @@ 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});
+      final Map<dynamic, dynamic> result = await _methodChannel
+          .invokeMethod('executeWithArguments', {'arguments': arguments});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -131,7 +147,8 @@ class FlutterFFmpeg {
   /// Executes FFmpeg [command] provided. Command is split into arguments using provided [delimiter].
   Future<int> execute(String command, [String delimiter = ' ']) async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('execute', {'command': command, 'delimiter': delimiter});
+      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod(
+          'execute', {'command': command, 'delimiter': delimiter});
       return result['rc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -175,7 +192,8 @@ class FlutterFFmpeg {
   /// Returns log level.
   Future<int> getLogLevel() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLogLevel');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLogLevel');
       return result['level'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -241,7 +259,10 @@ class FlutterFFmpeg {
   }
 
   /// Sets a callback to redirect FFmpeg statistics. [newCallback] is a new statistics callback function, use null to disable a previously defined callback
-  void enableStatisticsCallback(Function(int time, int size, double bitrate, double speed, int videoFrameNumber, double videoQuality, double videoFps) newCallback) {
+  void enableStatisticsCallback(
+      Function(int time, int size, double bitrate, double speed,
+              int videoFrameNumber, double videoQuality, double videoFps)
+          newCallback) {
     try {
       this.statisticsCallback = newCallback;
     } on PlatformException catch (e) {
@@ -253,7 +274,8 @@ class FlutterFFmpeg {
   /// videoQuality fields
   Future<Map<dynamic, dynamic>> getLastReceivedStatistics() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReceivedStatistics');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReceivedStatistics');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -273,14 +295,16 @@ class FlutterFFmpeg {
   /// Sets and overrides fontconfig configuration directory.
   Future<void> setFontconfigConfigurationPath(String path) async {
     try {
-      await _methodChannel.invokeMethod('setFontconfigConfigurationPath', {'path': path});
+      await _methodChannel
+          .invokeMethod('setFontconfigConfigurationPath', {'path': path});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
     }
   }
 
   /// Registers fonts inside the given [fontDirectory], so they are available to use in FFmpeg filters.
-  Future<void> setFontDirectory(String fontDirectory, Map<String, String> fontNameMap) async {
+  Future<void> setFontDirectory(
+      String fontDirectory, Map<String, String> fontNameMap) async {
     var parameters;
     if (fontNameMap == null) {
       parameters = {'fontDirectory': fontDirectory};
@@ -298,7 +322,8 @@ class FlutterFFmpeg {
   /// Returns FlutterFFmpeg package name.
   Future<String> getPackageName() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getPackageName');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getPackageName');
       return result['packageName'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -309,7 +334,8 @@ class FlutterFFmpeg {
   /// Returns supported external libraries.
   Future<List<dynamic>> getExternalLibraries() async {
     try {
-      final List<dynamic> result = await _methodChannel.invokeMethod('getExternalLibraries');
+      final List<dynamic> result =
+          await _methodChannel.invokeMethod('getExternalLibraries');
       return result;
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -320,7 +346,8 @@ class FlutterFFmpeg {
   /// Returns return code of last executed command.
   Future<int> getLastReturnCode() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastReturnCode');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastReturnCode');
       return result['lastRc'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -332,7 +359,8 @@ class FlutterFFmpeg {
   /// [disableRedirection()] method also disables this functionality.
   Future<String> getLastCommandOutput() async {
     try {
-      final Map<dynamic, dynamic> result = await _methodChannel.invokeMethod('getLastCommandOutput');
+      final Map<dynamic, dynamic> result =
+          await _methodChannel.invokeMethod('getLastCommandOutput');
       return result['lastCommandOutput'];
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
@@ -341,9 +369,11 @@ class FlutterFFmpeg {
   }
 
   /// Returns media information for given [path] using optional [timeout]
-  Future<Map<dynamic, dynamic>> getMediaInformation(String path, [int timeout = 10000]) async {
+  Future<Map<dynamic, dynamic>> getMediaInformation(String path,
+      [int timeout = 10000]) async {
     try {
-      return await _methodChannel.invokeMethod('getMediaInformation', {'path': path, 'timeout': timeout});
+      return await _methodChannel.invokeMethod(
+          'getMediaInformation', {'path': path, 'timeout': timeout});
     } on PlatformException catch (e) {
       print("Plugin error: ${e.message}");
       return null;

+ 3 - 3
packages/flutter_ffmpeg_min-gpl/pubspec.yaml

@@ -1,6 +1,6 @@
 name: flutter_ffmpeg
-description: Flutter plugin to run FFmpeg in the mobile platform. Supports iOS and Android.
-version: 0.2.1
+description: Flutter plugin to run FFmpeg on mobile platforms. Supports iOS and Android.
+version: 0.2.3
 author: Taner Sener <tanersener@gmail.com>
 homepage: https://github.com/tanersener/flutter-ffmpeg
 
@@ -12,7 +12,7 @@ dependencies:
     sdk: flutter
 
 dev_dependencies:
-  path_provider: ^0.5.0+1
+  path_provider: ^1.1.0
   path: ^1.6.2
 
 flutter:

+ 2 - 1
packages/flutter_ffmpeg_min-gpl_lts/.gitignore

@@ -3,5 +3,6 @@
 .dart_tool/
 .pub/
 build/
-pubspec.lock
+/pubspec.lock
 /.packages
+/.gradle/

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików