diff --git a/flutter/flutter/CHANGELOG.md b/flutter/flutter/CHANGELOG.md index 177f077..31f1289 100644 --- a/flutter/flutter/CHANGELOG.md +++ b/flutter/flutter/CHANGELOG.md @@ -1,3 +1,9 @@ +## 4.5.1 +- Feature release based on native v4.5.1 + +## 4.5.1-LTS +- Feature release based on native v4.5.1.LTS + ## 4.5.0 - Initial release diff --git a/flutter/flutter/README.md b/flutter/flutter/README.md index b521434..3492b72 100644 --- a/flutter/flutter/README.md +++ b/flutter/flutter/README.md @@ -10,15 +10,15 @@ - `Android API Level 16` or later - `armv7`, `armv7s`, `arm64`, `arm64-simulator`, `i386`, `x86_64`, `x86_64-mac-catalyst` and `arm64-mac-catalyst` architectures on iOS - - `iOS SDK 9.3` or later + - `iOS SDK 10` or later - `arm64` and `x86_64` architectures on macOS - - `macOS SDK 10.11+` or later + - `macOS SDK 10.12+` or later - Can process Storage Access Framework (SAF) Uris on Android - - 24 external libraries + - 25 external libraries `dav1d`, `fontconfig`, `freetype`, `fribidi`, `gmp`, `gnutls`, `kvazaar`, `lame`, `libass`, `libiconv`, `libilbc` , `libtheora`, `libvorbis`, `libvpx`, `libwebp`, `libxml2`, `opencore-amr`, `opus`, `shine`, `snappy`, `soxr` - , `speex`, `twolame`, `vo-amrwbenc` + , `speex`, `twolame`, `vo-amrwbenc`, `zimg` - 4 external libraries with GPL license @@ -32,7 +32,7 @@ Add `ffmpeg_kit_flutter` as a dependency in your `pubspec.yaml file`. ```yaml dependencies: - ffmpeg_kit_flutter: ^4.5.0 + ffmpeg_kit_flutter: 4.5.1 ``` #### 2.1 Packages @@ -55,7 +55,7 @@ using the following dependency format. ```yaml dependencies: - ffmpeg_kit_flutter_: ^4.5.0 + ffmpeg_kit_flutter_: 4.5.1 ``` Note that hyphens in the package name must be replaced with underscores. Additionally, do not forget to use the package @@ -67,7 +67,7 @@ In order to install the `LTS` variant, append `-LTS` to the version you have for ```yaml dependencies: - ffmpeg_kit_flutter: 4.5.0-LTS + ffmpeg_kit_flutter: 4.5.1-LTS ``` #### 2.4 LTS Releases @@ -99,7 +99,7 @@ The following table shows the Android API level and iOS deployment target requir 24 12.1 16 -9.3 +10 @@ -111,7 +111,7 @@ The following table shows the Android API level and iOS deployment target requir ```dart import 'package:ffmpeg_kit_flutter/ffmpeg_kit.dart'; - FFmpegKit.executeAsync('-i file1.mp4 -c:v mpeg4 file2.mp4', (session) async { + FFmpegKit.execute('-i file1.mp4 -c:v mpeg4 file2.mp4').then((session) async { final returnCode = await session.getReturnCode(); if (ReturnCode.isSuccess(returnCode)) { @@ -133,7 +133,7 @@ The following table shows the Android API level and iOS deployment target requir 2. Each `execute` call creates a new session. Access every detail about your execution from the session created. ```dart - FFmpegKit.executeAsync('-i file1.mp4 -c:v mpeg4 file2.mp4', (session) async { + FFmpegKit.execute('-i file1.mp4 -c:v mpeg4 file2.mp4').then((session) async { // Unique session id created for this execution final sessionId = session.getSessionId(); @@ -190,7 +190,7 @@ The following table shows the Android API level and iOS deployment target requir 4. Execute `FFprobe` commands. ```dart - FFprobeKit.executeAsync(ffprobeCommand, (session) { + FFprobeKit.execute(ffprobeCommand).then((session) async { // CALLED WHEN SESSION IS EXECUTED @@ -200,8 +200,18 @@ The following table shows the Android API level and iOS deployment target requir 5. Get media information for a file/url. ```dart - FFprobeKit.getMediaInformationAsync('', (session) async { - final information = await (session as MediaInformationSession).getMediaInformation(); + FFprobeKit.getMediaInformation('').then((session) async { + final information = await session.getMediaInformation(); + + if (information == null) { + + // CHECK THE FOLLOWING ATTRIBUTES ON ERROR + final state = FFmpegKitConfig.sessionStateToString(await session.getState()); + final returnCode = await session.getReturnCode(); + final failStackTrace = await session.getFailStackTrace(); + final duration = await session.getDuration(); + final output = await session.getOutput(); + } }); ``` @@ -237,7 +247,7 @@ The following table shows the Android API level and iOS deployment target requir }); ``` -8. Get previous `FFmpeg` and `FFprobe` sessions from the session history. +8. Get previous `FFmpeg`, `FFprobe` and `MediaInformation` sessions from the session history. ```dart FFmpegKit.listSessions().then((sessionList) { @@ -246,7 +256,13 @@ The following table shows the Android API level and iOS deployment target requir }); }); - FFprobeKit.listSessions().then((sessionList) { + FFprobeKit.listFFprobeSessions().then((sessionList) { + sessionList.forEach((session) { + final sessionId = session.getSessionId(); + }); + }); + + FFprobeKit.listMediaInformationSessions().then((sessionList) { sessionList.forEach((session) { final sessionId = session.getSessionId(); }); @@ -255,10 +271,18 @@ The following table shows the Android API level and iOS deployment target requir 9. Enable global callbacks. -- Execute Callback, called when an async execution is ended +- Session type specific Complete Callbacks, called when an async session has been completed ```dart - FFmpegKitConfig.enableExecuteCallback((session) { + FFmpegKitConfig.enableFFmpegSessionCompleteCallback((session) { + final sessionId = session.getSessionId(); + }); + + FFmpegKitConfig.enableFFprobeSessionCompleteCallback((session) { + final sessionId = session.getSessionId(); + }); + + FFmpegKitConfig.enableMediaInformationSessionCompleteCallback((session) { final sessionId = session.getSessionId(); }); ``` diff --git a/flutter/flutter/android/.gitignore b/flutter/flutter/android/.gitignore index d3320fb..b5ad5b7 100644 --- a/flutter/flutter/android/.gitignore +++ b/flutter/flutter/android/.gitignore @@ -1,6 +1,6 @@ *.iml .gradle -/local.properties +local.properties /.idea/workspace.xml /.idea/libraries .DS_Store diff --git a/flutter/flutter/android/build.gradle b/flutter/flutter/android/build.gradle index 39eeabb..0a033dc 100644 --- a/flutter/flutter/android/build.gradle +++ b/flutter/flutter/android/build.gradle @@ -17,8 +17,8 @@ android { defaultConfig { minSdkVersion 24 targetSdkVersion 30 - versionCode 450 - versionName "4.5.0" + versionCode 451 + versionName "4.5.1" } buildTypes { @@ -43,6 +43,6 @@ repositories { dependencies { implementation 'androidx.annotation:annotation:1.2.0' - implementation 'com.arthenica:ffmpeg-kit-https:4.5' + implementation 'com.arthenica:ffmpeg-kit-https:4.5.1-1' } diff --git a/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFmpegKitFlutterPlugin.java b/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFmpegKitFlutterPlugin.java index 02279c7..3d227bb 100644 --- a/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFmpegKitFlutterPlugin.java +++ b/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFmpegKitFlutterPlugin.java @@ -23,6 +23,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.util.Log; import androidx.annotation.NonNull; @@ -57,12 +58,11 @@ import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; -import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; @@ -114,7 +114,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met // EVENTS public static final String EVENT_LOG_CALLBACK_EVENT = "FFmpegKitLogCallbackEvent"; public static final String EVENT_STATISTICS_CALLBACK_EVENT = "FFmpegKitStatisticsCallbackEvent"; - public static final String EVENT_EXECUTE_CALLBACK_EVENT = "FFmpegKitExecuteCallbackEvent"; + public static final String EVENT_COMPLETE_CALLBACK_EVENT = "FFmpegKitCompleteCallbackEvent"; // REQUEST CODES public static final int READABLE_REQUEST_CODE = 10000; @@ -127,11 +127,11 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met public static final String ARGUMENT_FFPROBE_JSON_OUTPUT = "ffprobeJsonOutput"; public static final String ARGUMENT_WRITABLE = "writable"; - private static final int asyncWriteToPipeConcurrencyLimit = 10; + private static final int asyncConcurrencyLimit = 10; private final AtomicBoolean logsEnabled; private final AtomicBoolean statisticsEnabled; - private final ExecutorService asyncWriteToPipeExecutorService; + private final ExecutorService asyncExecutorService; private MethodChannel methodChannel; private EventChannel eventChannel; @@ -147,10 +147,10 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met public FFmpegKitFlutterPlugin() { this.logsEnabled = new AtomicBoolean(false); this.statisticsEnabled = new AtomicBoolean(false); - this.asyncWriteToPipeExecutorService = Executors.newFixedThreadPool(asyncWriteToPipeConcurrencyLimit); + this.asyncExecutorService = Executors.newFixedThreadPool(asyncConcurrencyLimit); this.resultHandler = new FFmpegKitFlutterMethodResultHandler(); - registerGlobalCallbacks(); + Log.d(LIBRARY_NAME, String.format("FFmpegKitFlutterPlugin created %s.", this)); } @SuppressWarnings("deprecation") @@ -165,7 +165,9 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met } protected void registerGlobalCallbacks() { - FFmpegKitConfig.enableExecuteCallback(this::emitSession); + FFmpegKitConfig.enableFFmpegSessionCompleteCallback(this::emitSession); + FFmpegKitConfig.enableFFprobeSessionCompleteCallback(this::emitSession); + FFmpegKitConfig.enableMediaInformationSessionCompleteCallback(this::emitSession); FFmpegKitConfig.enableLogCallback(log -> { if (logsEnabled.get()) { @@ -192,7 +194,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) { - Log.d(LIBRARY_NAME, String.format("FFmpegKitFlutterPlugin attached to activity %s.", activityPluginBinding.getActivity())); + Log.d(LIBRARY_NAME, String.format("FFmpegKitFlutterPlugin %s attached to activity %s.", this, activityPluginBinding.getActivity())); init(flutterPluginBinding.getBinaryMessenger(), flutterPluginBinding.getApplicationContext(), activityPluginBinding.getActivity(), null, activityPluginBinding); } @@ -215,7 +217,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met @Override public void onListen(final Object o, final EventChannel.EventSink eventSink) { this.eventSink = eventSink; - Log.d(LIBRARY_NAME, String.format("FFmpegKitFlutterPlugin started listening to events on %s.", eventSink)); + Log.d(LIBRARY_NAME, String.format("FFmpegKitFlutterPlugin %s started listening to events on %s.", this, eventSink)); } @Override @@ -249,72 +251,72 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met @Override public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { - final Optional sessionIdOptional = Optional.ofNullable(call.argument(ARGUMENT_SESSION_ID)); + final Integer sessionId = call.argument(ARGUMENT_SESSION_ID); final Integer waitTimeout = call.argument(ARGUMENT_WAIT_TIMEOUT); - final Optional> argumentsOptional = Optional.ofNullable(call.argument(ARGUMENT_ARGUMENTS)); - final Optional ffprobeJsonOutputOptional = Optional.ofNullable(call.argument(ARGUMENT_FFPROBE_JSON_OUTPUT)); - final Optional writableOptional = Optional.ofNullable(call.argument(ARGUMENT_WRITABLE)); + final List arguments = call.argument(ARGUMENT_ARGUMENTS); + final String ffprobeJsonOutput = call.argument(ARGUMENT_FFPROBE_JSON_OUTPUT); + final Boolean writable = call.argument(ARGUMENT_WRITABLE); switch (call.method) { case "abstractSessionGetEndTime": - if (sessionIdOptional.isPresent()) { - abstractSessionGetEndTime(sessionIdOptional.get(), result); + if (sessionId != null) { + abstractSessionGetEndTime(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "abstractSessionGetDuration": - if (sessionIdOptional.isPresent()) { - abstractSessionGetDuration(sessionIdOptional.get(), result); + if (sessionId != null) { + abstractSessionGetDuration(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "abstractSessionGetAllLogs": - if (sessionIdOptional.isPresent()) { - abstractSessionGetAllLogs(sessionIdOptional.get(), waitTimeout, result); + if (sessionId != null) { + abstractSessionGetAllLogs(sessionId, waitTimeout, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "abstractSessionGetLogs": - if (sessionIdOptional.isPresent()) { - abstractSessionGetLogs(sessionIdOptional.get(), result); + if (sessionId != null) { + abstractSessionGetLogs(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "abstractSessionGetAllLogsAsString": - if (sessionIdOptional.isPresent()) { - abstractSessionGetAllLogsAsString(sessionIdOptional.get(), waitTimeout, result); + if (sessionId != null) { + abstractSessionGetAllLogsAsString(sessionId, waitTimeout, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "abstractSessionGetState": - if (sessionIdOptional.isPresent()) { - abstractSessionGetState(sessionIdOptional.get(), result); + if (sessionId != null) { + abstractSessionGetState(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "abstractSessionGetReturnCode": - if (sessionIdOptional.isPresent()) { - abstractSessionGetReturnCode(sessionIdOptional.get(), result); + if (sessionId != null) { + abstractSessionGetReturnCode(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "abstractSessionGetFailStackTrace": - if (sessionIdOptional.isPresent()) { - abstractSessionGetFailStackTrace(sessionIdOptional.get(), result); + if (sessionId != null) { + abstractSessionGetFailStackTrace(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "thereAreAsynchronousMessagesInTransmit": - if (sessionIdOptional.isPresent()) { - abstractSessionThereAreAsynchronousMessagesInTransmit(sessionIdOptional.get(), result); + if (sessionId != null) { + abstractSessionThereAreAsynchronousMessagesInTransmit(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } @@ -323,50 +325,57 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met getArch(result); break; case "ffmpegSession": - if (argumentsOptional.isPresent()) { - ffmpegSession(argumentsOptional.get(), result); + if (arguments != null) { + ffmpegSession(arguments, result); } else { resultHandler.errorAsync(result, "INVALID_ARGUMENTS", "Invalid arguments array."); } break; case "ffmpegSessionGetAllStatistics": - if (sessionIdOptional.isPresent()) { - ffmpegSessionGetAllStatistics(sessionIdOptional.get(), waitTimeout, result); + if (sessionId != null) { + ffmpegSessionGetAllStatistics(sessionId, waitTimeout, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "ffmpegSessionGetStatistics": - if (sessionIdOptional.isPresent()) { - ffmpegSessionGetStatistics(sessionIdOptional.get(), result); + if (sessionId != null) { + ffmpegSessionGetStatistics(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "ffprobeSession": - if (argumentsOptional.isPresent()) { - ffprobeSession(argumentsOptional.get(), result); + if (arguments != null) { + ffprobeSession(arguments, result); } else { resultHandler.errorAsync(result, "INVALID_ARGUMENTS", "Invalid arguments array."); } break; case "mediaInformationSession": - if (argumentsOptional.isPresent()) { - mediaInformationSession(argumentsOptional.get(), result); + if (arguments != null) { + mediaInformationSession(arguments, result); } else { resultHandler.errorAsync(result, "INVALID_ARGUMENTS", "Invalid arguments array."); } break; + case "getMediaInformation": + if (sessionId != null) { + getMediaInformation(sessionId, result); + } else { + resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); + } + break; case "mediaInformationJsonParserFrom": - if (ffprobeJsonOutputOptional.isPresent()) { - mediaInformationJsonParserFrom(ffprobeJsonOutputOptional.get(), result); + if (ffprobeJsonOutput != null) { + mediaInformationJsonParserFrom(ffprobeJsonOutput, result); } else { resultHandler.errorAsync(result, "INVALID_FFPROBE_JSON_OUTPUT", "Invalid ffprobe json output."); } break; case "mediaInformationJsonParserFromWithError": - if (ffprobeJsonOutputOptional.isPresent()) { - mediaInformationJsonParserFromWithError(ffprobeJsonOutputOptional.get(), result); + if (ffprobeJsonOutput != null) { + mediaInformationJsonParserFromWithError(ffprobeJsonOutput, result); } else { resultHandler.errorAsync(result, "INVALID_FFPROBE_JSON_OUTPUT", "Invalid ffprobe json output."); } @@ -390,28 +399,28 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met disableStatistics(result); break; case "setFontconfigConfigurationPath": - final Optional pathOptional = Optional.ofNullable(call.argument("path")); - if (pathOptional.isPresent()) { - setFontconfigConfigurationPath(pathOptional.get(), result); + final String path = call.argument("path"); + if (path != null) { + setFontconfigConfigurationPath(path, result); } else { resultHandler.errorAsync(result, "INVALID_PATH", "Invalid path."); } break; case "setFontDirectory": { - final Optional fontDirectoryOptional = Optional.ofNullable(call.argument("fontDirectory")); + final String fontDirectory = call.argument("fontDirectory"); final Map fontNameMap = call.argument("fontNameMap"); - if (fontDirectoryOptional.isPresent()) { - setFontDirectory(fontDirectoryOptional.get(), fontNameMap, result); + if (fontDirectory != null) { + setFontDirectory(fontDirectory, fontNameMap, result); } else { resultHandler.errorAsync(result, "INVALID_FONT_DIRECTORY", "Invalid font directory."); } break; } case "setFontDirectoryList": { - final Optional> fontDirectoryListOptional = Optional.ofNullable(call.argument("fontDirectoryList")); + final List fontDirectoryList = call.argument("fontDirectoryList"); final Map fontNameMap = call.argument("fontNameMap"); - if (fontDirectoryListOptional.isPresent()) { - setFontDirectoryList(fontDirectoryListOptional.get(), fontNameMap, result); + if (fontDirectoryList != null) { + setFontDirectoryList(fontDirectoryList, fontNameMap, result); } else { resultHandler.errorAsync(result, "INVALID_FONT_DIRECTORY_LIST", "Invalid font directory list."); } @@ -421,9 +430,9 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met registerNewFFmpegPipe(result); break; case "closeFFmpegPipe": - final Optional ffmpegPipePathOptional = Optional.ofNullable(call.argument("ffmpegPipePath")); - if (ffmpegPipePathOptional.isPresent()) { - closeFFmpegPipe(ffmpegPipePathOptional.get(), result); + final String ffmpegPipePath = call.argument("ffmpegPipePath"); + if (ffmpegPipePath != null) { + closeFFmpegPipe(ffmpegPipePath, result); } else { resultHandler.errorAsync(result, "INVALID_PIPE_PATH", "Invalid ffmpeg pipe path."); } @@ -438,42 +447,63 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met getBuildDate(result); break; case "setEnvironmentVariable": - final Optional variableNameOptional = Optional.ofNullable(call.argument("variableName")); - final Optional variableValueOptional = Optional.ofNullable(call.argument("variableValue")); + final String variableName = call.argument("variableName"); + final String variableValue = call.argument("variableValue"); - if (variableNameOptional.isPresent() && variableValueOptional.isPresent()) { - setEnvironmentVariable(variableNameOptional.get(), variableValueOptional.get(), result); - } else if (variableValueOptional.isPresent()) { + if (variableName != null && variableValue != null) { + setEnvironmentVariable(variableName, variableValue, result); + } else if (variableValue != null) { resultHandler.errorAsync(result, "INVALID_NAME", "Invalid environment variable name."); } else { resultHandler.errorAsync(result, "INVALID_VALUE", "Invalid environment variable value."); } break; case "ignoreSignal": - final Optional signalOptional = Optional.ofNullable(call.argument("signal")); - if (signalOptional.isPresent()) { - ignoreSignal(signalOptional.get(), result); + final Integer signal = call.argument("signal"); + if (signal != null) { + ignoreSignal(signal, result); } else { resultHandler.errorAsync(result, "INVALID_SIGNAL", "Invalid signal value."); } break; + case "ffmpegSessionExecute": + if (sessionId != null) { + ffmpegSessionExecute(sessionId, result); + } else { + resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); + } + break; + case "ffprobeSessionExecute": + if (sessionId != null) { + ffprobeSessionExecute(sessionId, result); + } else { + resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); + } + break; + case "mediaInformationSessionExecute": + if (sessionId != null) { + mediaInformationSessionExecute(sessionId, waitTimeout, result); + } else { + resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); + } + break; case "asyncFFmpegSessionExecute": - if (sessionIdOptional.isPresent()) { - asyncFFmpegSessionExecute(sessionIdOptional.get(), result); + if (sessionId != null) { + asyncFFmpegSessionExecute(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "asyncFFprobeSessionExecute": - if (sessionIdOptional.isPresent()) { - asyncFFprobeSessionExecute(sessionIdOptional.get(), result); + if (sessionId != null) { + asyncFFprobeSessionExecute(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } break; case "asyncMediaInformationSessionExecute": - if (sessionIdOptional.isPresent()) { - asyncMediaInformationSessionExecute(sessionIdOptional.get(), waitTimeout, result); + if (sessionId != null) { + asyncMediaInformationSessionExecute(sessionId, waitTimeout, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } @@ -482,9 +512,9 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met getLogLevel(result); break; case "setLogLevel": - final Optional levelOptional = Optional.ofNullable(call.argument("level")); - if (levelOptional.isPresent()) { - setLogLevel(levelOptional.get(), result); + final Integer level = call.argument("level"); + if (level != null) { + setLogLevel(level, result); } else { resultHandler.errorAsync(result, "INVALID_LEVEL", "Invalid level value."); } @@ -493,16 +523,16 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met getSessionHistorySize(result); break; case "setSessionHistorySize": - final Optional sessionHistorySizeOptional = Optional.ofNullable(call.argument("sessionHistorySize")); - if (sessionHistorySizeOptional.isPresent()) { - setSessionHistorySize(sessionHistorySizeOptional.get(), result); + final Integer sessionHistorySize = call.argument("sessionHistorySize"); + if (sessionHistorySize != null) { + setSessionHistorySize(sessionHistorySize, result); } else { resultHandler.errorAsync(result, "INVALID_SIZE", "Invalid session history size value."); } break; case "getSession": - if (sessionIdOptional.isPresent()) { - getSession(sessionIdOptional.get(), result); + if (sessionId != null) { + getSession(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } @@ -520,9 +550,9 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met clearSessions(result); break; case "getSessionsByState": - final Optional stateOptional = Optional.ofNullable(call.argument("state")); - if (stateOptional.isPresent()) { - getSessionsByState(stateOptional.get(), result); + final Integer state = call.argument("state"); + if (state != null) { + getSessionsByState(state, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION_STATE", "Invalid session state value."); } @@ -531,16 +561,16 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met getLogRedirectionStrategy(result); break; case "setLogRedirectionStrategy": - final Optional strategyOptional = Optional.ofNullable(call.argument("strategy")); - if (strategyOptional.isPresent()) { - setLogRedirectionStrategy(strategyOptional.get(), result); + final Integer strategy = call.argument("strategy"); + if (strategy != null) { + setLogRedirectionStrategy(strategy, result); } else { resultHandler.errorAsync(result, "INVALID_LOG_REDIRECTION_STRATEGY", "Invalid log redirection strategy value."); } break; case "messagesInTransmit": - if (sessionIdOptional.isPresent()) { - messagesInTransmit(sessionIdOptional.get(), result); + if (sessionId != null) { + messagesInTransmit(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } @@ -549,11 +579,11 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met getPlatform(result); break; case "writeToPipe": - final Optional inputOptional = Optional.ofNullable(call.argument("input")); - final Optional pipeOptional = Optional.ofNullable(call.argument("pipe")); - if (inputOptional.isPresent() && pipeOptional.isPresent()) { - writeToPipe(inputOptional.get(), pipeOptional.get(), result); - } else if (pipeOptional.isPresent()) { + final String input = call.argument("input"); + final String pipe = call.argument("pipe"); + if (input != null && pipe != null) { + writeToPipe(input, pipe, result); + } else if (pipe != null) { resultHandler.errorAsync(result, "INVALID_INPUT", "Invalid input value."); } else { resultHandler.errorAsync(result, "INVALID_PIPE", "Invalid pipe value."); @@ -562,20 +592,26 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met case "selectDocument": final String title = call.argument("title"); final String type = call.argument("type"); - final Optional> extraTypesOptional = Optional.ofNullable(call.argument("extraTypes")); - final String[] extraTypesArray = extraTypesOptional.map(strings -> strings.toArray(new String[0])).orElse(null); - if (writableOptional.isPresent()) { - selectDocument(writableOptional.get(), title, type, extraTypesArray, result); + final List extraTypes = call.argument("extraTypes"); + final String[] extraTypesArray; + if (extraTypes != null) { + extraTypesArray = extraTypes.toArray(new String[0]); + } else { + extraTypesArray = null; + } + if (writable != null) { + selectDocument(writable, title, type, extraTypesArray, result); } else { resultHandler.errorAsync(result, "INVALID_WRITABLE", "Invalid writable value."); } break; case "getSafParameter": - final Optional uriOptional = Optional.ofNullable(call.argument("uri")); - if (writableOptional.isPresent() && uriOptional.isPresent()) { - getSafParameter(writableOptional.get(), uriOptional.get(), result); - } else if (uriOptional.isPresent()) { - resultHandler.errorAsync(result, "INVALID_WRITABLE", "Invalid writable value."); + final String uri = call.argument("uri"); + final String openMode = call.argument("openMode"); + if (uri != null && openMode != null) { + getSafParameter(uri, openMode, result); + } else if (uri != null) { + resultHandler.errorAsync(result, "INVALID_OPEN_MODE", "Invalid openMode value."); } else { resultHandler.errorAsync(result, "INVALID_URI", "Invalid uri value."); } @@ -584,8 +620,8 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met cancel(result); break; case "cancelSession": - if (sessionIdOptional.isPresent()) { - cancelSession(sessionIdOptional.get(), result); + if (sessionId != null) { + cancelSession(sessionId, result); } else { resultHandler.errorAsync(result, "INVALID_SESSION", "Invalid session id."); } @@ -596,6 +632,9 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met case "getFFprobeSessions": getFFprobeSessions(result); break; + case "getMediaInformationSessions": + getMediaInformationSessions(result); + break; case "getPackageName": getPackageName(result); break; @@ -610,6 +649,8 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met @SuppressWarnings("deprecation") protected void init(final BinaryMessenger messenger, final Context context, final Activity activity, final io.flutter.plugin.common.PluginRegistry.Registrar registrar, final ActivityPluginBinding activityBinding) { + registerGlobalCallbacks(); + if (methodChannel == null) { methodChannel = new MethodChannel(messenger, METHOD_CHANNEL); methodChannel.setMethodCallHandler(this); @@ -635,7 +676,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met activityBinding.addActivityResultListener(this); } - Log.d(LIBRARY_NAME, String.format("FFmpegKitFlutterPlugin initialized with context %s and activity %s.", context, activity)); + Log.d(LIBRARY_NAME, String.format("FFmpegKitFlutterPlugin %s initialised with context %s and activity %s.", this, context, activity)); } protected void uninit() { @@ -648,7 +689,6 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met this.context = null; this.activity = null; - this.flutterPluginBinding = null; this.activityPluginBinding = null; Log.d(LIBRARY_NAME, "FFmpegKitFlutterPlugin uninitialized."); @@ -711,7 +751,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met timeout = AbstractSession.DEFAULT_TIMEOUT_FOR_ASYNCHRONOUS_MESSAGES_IN_TRANSMIT; } final List allLogs = session.getAllLogs(timeout); - resultHandler.successAsync(result, allLogs.stream().map(FFmpegKitFlutterPlugin::toMap).collect(Collectors.toList())); + resultHandler.successAsync(result, toLogMapList(allLogs)); } } @@ -721,7 +761,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met resultHandler.errorAsync(result, "SESSION_NOT_FOUND", "Session not found."); } else { final List allLogs = session.getLogs(); - resultHandler.successAsync(result, allLogs.stream().map(FFmpegKitFlutterPlugin::toMap).collect(Collectors.toList())); + resultHandler.successAsync(result, toLogMapList(allLogs)); } } @@ -800,7 +840,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met if (session == null) { resultHandler.errorAsync(result, "SESSION_NOT_FOUND", "Session not found."); } else { - if (session instanceof FFmpegSession) { + if (session.isFFmpeg()) { final int timeout; if (isValidPositiveNumber(waitTimeout)) { timeout = waitTimeout; @@ -808,7 +848,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met timeout = AbstractSession.DEFAULT_TIMEOUT_FOR_ASYNCHRONOUS_MESSAGES_IN_TRANSMIT; } final List allStatistics = ((FFmpegSession) session).getAllStatistics(timeout); - resultHandler.successAsync(result, allStatistics.stream().map(FFmpegKitFlutterPlugin::toMap).collect(Collectors.toList())); + resultHandler.successAsync(result, toStatisticsMapList(allStatistics)); } else { resultHandler.errorAsync(result, "NOT_FFMPEG_SESSION", "A session is found but it does not have the correct type."); } @@ -820,9 +860,9 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met if (session == null) { resultHandler.errorAsync(result, "SESSION_NOT_FOUND", "Session not found."); } else { - if (session instanceof FFmpegSession) { + if (session.isFFmpeg()) { final List statistics = ((FFmpegSession) session).getStatistics(); - resultHandler.successAsync(result, statistics.stream().map(FFmpegKitFlutterPlugin::toMap).collect(Collectors.toList())); + resultHandler.successAsync(result, toStatisticsMapList(statistics)); } else { resultHandler.errorAsync(result, "NOT_FFMPEG_SESSION", "A session is found but it does not have the correct type."); } @@ -843,6 +883,21 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met resultHandler.successAsync(result, toMap(session)); } + protected void getMediaInformation(@NonNull final Integer sessionId, @NonNull final Result result) { + final Session session = FFmpegKitConfig.getSession(sessionId.longValue()); + if (session == null) { + resultHandler.errorAsync(result, "SESSION_NOT_FOUND", "Session not found."); + } else { + if (session.isMediaInformation()) { + final MediaInformationSession mediaInformationSession = (MediaInformationSession) session; + final MediaInformation mediaInformation = mediaInformationSession.getMediaInformation(); + resultHandler.successAsync(result, toMap(mediaInformation)); + } else { + resultHandler.errorAsync(result, "NOT_MEDIA_INFORMATION_SESSION", "A session is found but it does not have the correct type."); + } + } + } + // MediaInformationJsonParser protected void mediaInformationJsonParserFrom(@NonNull final String ffprobeJsonOutput, @NonNull final Result result) { @@ -988,12 +1043,60 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met } } + protected void ffmpegSessionExecute(@NonNull final Integer sessionId, @NonNull final Result result) { + final Session session = FFmpegKitConfig.getSession(sessionId.longValue()); + if (session == null) { + resultHandler.errorAsync(result, "SESSION_NOT_FOUND", "Session not found."); + } else { + if (session.isFFmpeg()) { + final FFmpegSessionExecuteTask ffmpegSessionExecuteTask = new FFmpegSessionExecuteTask((FFmpegSession) session, resultHandler, result); + asyncExecutorService.submit(ffmpegSessionExecuteTask); + } else { + resultHandler.errorAsync(result, "NOT_FFMPEG_SESSION", "A session is found but it does not have the correct type."); + } + } + } + + protected void ffprobeSessionExecute(@NonNull final Integer sessionId, @NonNull final Result result) { + final Session session = FFmpegKitConfig.getSession(sessionId.longValue()); + if (session == null) { + resultHandler.errorAsync(result, "SESSION_NOT_FOUND", "Session not found."); + } else { + if (session.isFFprobe()) { + final FFprobeSessionExecuteTask ffprobeSessionExecuteTask = new FFprobeSessionExecuteTask((FFprobeSession) session, resultHandler, result); + asyncExecutorService.submit(ffprobeSessionExecuteTask); + } else { + resultHandler.errorAsync(result, "NOT_FFPROBE_SESSION", "A session is found but it does not have the correct type."); + } + } + } + + protected void mediaInformationSessionExecute(@NonNull final Integer sessionId, @Nullable final Integer waitTimeout, @NonNull final Result result) { + final Session session = FFmpegKitConfig.getSession(sessionId.longValue()); + if (session == null) { + resultHandler.errorAsync(result, "SESSION_NOT_FOUND", "Session not found."); + } else { + if (session.isMediaInformation()) { + final int timeout; + if (isValidPositiveNumber(waitTimeout)) { + timeout = waitTimeout; + } else { + timeout = AbstractSession.DEFAULT_TIMEOUT_FOR_ASYNCHRONOUS_MESSAGES_IN_TRANSMIT; + } + final MediaInformationSessionExecuteTask mediaInformationSessionExecuteTask = new MediaInformationSessionExecuteTask((MediaInformationSession) session, timeout, resultHandler, result); + asyncExecutorService.submit(mediaInformationSessionExecuteTask); + } else { + resultHandler.errorAsync(result, "NOT_MEDIA_INFORMATION_SESSION", "A session is found but it does not have the correct type."); + } + } + } + protected void asyncFFmpegSessionExecute(@NonNull final Integer sessionId, @NonNull final Result result) { final Session session = FFmpegKitConfig.getSession(sessionId.longValue()); if (session == null) { resultHandler.errorAsync(result, "SESSION_NOT_FOUND", "Session not found."); } else { - if (session instanceof FFmpegSession) { + if (session.isFFmpeg()) { FFmpegKitConfig.asyncFFmpegExecute((FFmpegSession) session); resultHandler.successAsync(result, null); } else { @@ -1007,7 +1110,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met if (session == null) { resultHandler.errorAsync(result, "SESSION_NOT_FOUND", "Session not found."); } else { - if (session instanceof FFprobeSession) { + if (session.isFFprobe()) { FFmpegKitConfig.asyncFFprobeExecute((FFprobeSession) session); resultHandler.successAsync(result, null); } else { @@ -1021,7 +1124,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met if (session == null) { resultHandler.errorAsync(result, "SESSION_NOT_FOUND", "Session not found."); } else { - if (session instanceof MediaInformationSession) { + if (session.isMediaInformation()) { final int timeout; if (isValidPositiveNumber(waitTimeout)) { timeout = waitTimeout; @@ -1074,7 +1177,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met } protected void getSessions(@NonNull final Result result) { - resultHandler.successAsync(result, toSessionArray(FFmpegKitConfig.getSessions())); + resultHandler.successAsync(result, toSessionMapList(FFmpegKitConfig.getSessions())); } protected void clearSessions(@NonNull final Result result) { @@ -1083,7 +1186,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met } protected void getSessionsByState(@NonNull final Integer sessionState, @NonNull final Result result) { - resultHandler.successAsync(result, toSessionArray(FFmpegKitConfig.getSessionsByState(toSessionState(sessionState)))); + resultHandler.successAsync(result, toSessionMapList(FFmpegKitConfig.getSessionsByState(toSessionState(sessionState)))); } protected void getLogRedirectionStrategy(@NonNull final Result result) { @@ -1104,11 +1207,17 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met } protected void writeToPipe(@NonNull final String inputPath, @NonNull final String namedPipePath, @NonNull final Result result) { - final AsyncWriteToPipeTask asyncTask = new AsyncWriteToPipeTask(inputPath, namedPipePath, resultHandler, result); - asyncWriteToPipeExecutorService.submit(asyncTask); + final WriteToPipeTask asyncTask = new WriteToPipeTask(inputPath, namedPipePath, resultHandler, result); + asyncExecutorService.submit(asyncTask); } protected void selectDocument(@NonNull final Boolean writable, @Nullable final String title, @Nullable final String type, @Nullable final String[] extraTypes, @NonNull final Result result) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + android.util.Log.i(LIBRARY_NAME, String.format(Locale.getDefault(), "selectDocument is not supported on API Level %d", Build.VERSION.SDK_INT)); + resultHandler.errorAsync(result, "SELECT_FAILED", String.format(Locale.getDefault(), "selectDocument is not supported on API Level %d", Build.VERSION.SDK_INT)); + return; + } + final Intent intent; if (writable) { intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); @@ -1152,26 +1261,27 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met } } - protected void getSafParameter(@NonNull final Boolean writable, @NonNull final String uriString, @NonNull final Result result) { + protected void getSafParameter(@NonNull final String uriString, @NonNull final String openMode, @NonNull final Result result) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + android.util.Log.i(LIBRARY_NAME, String.format(Locale.getDefault(), "getSafParameter is not supported on API Level %d", Build.VERSION.SDK_INT)); + resultHandler.errorAsync(result, "GET_SAF_PARAMETER_FAILED", String.format(Locale.getDefault(), "getSafParameter is not supported on API Level %d", Build.VERSION.SDK_INT)); + return; + } + if (context != null) { final Uri uri = Uri.parse(uriString); if (uri == null) { - Log.w(LIBRARY_NAME, String.format("Cannot getSafParameter using parameters writable: %s, uriString: %s. Uri string cannot be parsed.", writable, uriString)); + Log.w(LIBRARY_NAME, String.format("Cannot getSafParameter using parameters uriString: %s, openMode: %s. Uri string cannot be parsed.", uriString, openMode)); resultHandler.errorAsync(result, "GET_SAF_PARAMETER_FAILED", "Uri string cannot be parsed."); } else { - final String safParameter; - if (writable) { - safParameter = FFmpegKitConfig.getSafParameterForWrite(context, uri); - } else { - safParameter = FFmpegKitConfig.getSafParameterForRead(context, uri); - } + final String safParameter = FFmpegKitConfig.getSafParameter(context, uri, openMode); - Log.d(LIBRARY_NAME, String.format("getSafParameter using parameters writable: %s, uriString: %s completed with saf parameter: %s.", writable, uriString, safParameter)); + Log.d(LIBRARY_NAME, String.format("getSafParameter using parameters uriString: %s, openMode: %s completed with saf parameter: %s.", uriString, openMode, safParameter)); resultHandler.successAsync(result, safParameter); } } else { - Log.w(LIBRARY_NAME, String.format("Cannot getSafParameter using parameters writable: %s, uriString: %s. Context is null.", writable, uriString)); + Log.w(LIBRARY_NAME, String.format("Cannot getSafParameter using parameters uriString: %s, openMode: %s. Context is null.", uriString, openMode)); resultHandler.errorAsync(result, "INVALID_CONTEXT", "Context is null."); } } @@ -1189,13 +1299,17 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met } protected void getFFmpegSessions(@NonNull final Result result) { - resultHandler.successAsync(result, toSessionArray(FFmpegKit.listSessions())); + resultHandler.successAsync(result, toSessionMapList(FFmpegKit.listSessions())); } // FFprobeKit protected void getFFprobeSessions(@NonNull final Result result) { - resultHandler.successAsync(result, toSessionArray(FFprobeKit.listSessions())); + resultHandler.successAsync(result, toSessionMapList(FFprobeKit.listFFprobeSessions())); + } + + protected void getMediaInformationSessions(@NonNull final Result result) { + resultHandler.successAsync(result, toSessionMapList(FFprobeKit.listMediaInformationSessions())); } // Packages @@ -1240,19 +1354,17 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met sessionMap.put(KEY_SESSION_START_TIME, toLong(session.getStartTime())); sessionMap.put(KEY_SESSION_COMMAND, session.getCommand()); - if (session.isFFprobe()) { - if (session instanceof MediaInformationSession) { - final MediaInformationSession mediaInformationSession = (MediaInformationSession) session; - final MediaInformation mediaInformation = mediaInformationSession.getMediaInformation(); - if (mediaInformation != null) { - sessionMap.put(KEY_SESSION_MEDIA_INFORMATION, toMap(mediaInformation)); - } - sessionMap.put(KEY_SESSION_TYPE, SESSION_TYPE_MEDIA_INFORMATION); - } else { - sessionMap.put(KEY_SESSION_TYPE, SESSION_TYPE_FFPROBE); - } - } else { + if (session.isFFmpeg()) { sessionMap.put(KEY_SESSION_TYPE, SESSION_TYPE_FFMPEG); + } else if (session.isFFprobe()) { + sessionMap.put(KEY_SESSION_TYPE, SESSION_TYPE_FFPROBE); + } else if (session.isMediaInformation()) { + final MediaInformationSession mediaInformationSession = (MediaInformationSession) session; + final MediaInformation mediaInformation = mediaInformationSession.getMediaInformation(); + if (mediaInformation != null) { + sessionMap.put(KEY_SESSION_MEDIA_INFORMATION, toMap(mediaInformation)); + } + sessionMap.put(KEY_SESSION_TYPE, SESSION_TYPE_MEDIA_INFORMATION); } return sessionMap; @@ -1339,29 +1451,21 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met return statisticsMap; } - protected static List> toSessionArray(final List sessions) { - final List> sessionArray = new ArrayList<>(); - - for (int i = 0; i < sessions.size(); i++) { - sessionArray.add(toMap(sessions.get(i))); - } - - return sessionArray; - } - protected static Map toMap(final MediaInformation mediaInformation) { - Map map = new HashMap<>(); - if (mediaInformation != null) { + Map map = new HashMap<>(); + if (mediaInformation.getAllProperties() != null) { JSONObject allProperties = mediaInformation.getAllProperties(); if (allProperties != null) { map = toMap(allProperties); } } - } - return map; + return map; + } else { + return null; + } } protected static Map toMap(final JSONObject jsonObject) { @@ -1404,6 +1508,36 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met return list; } + protected static List> toSessionMapList(final List sessionList) { + final List> list = new ArrayList<>(); + + for (int i = 0; i < sessionList.size(); i++) { + list.add(toMap(sessionList.get(i))); + } + + return list; + } + + protected static List> toLogMapList(final List logList) { + final List> list = new ArrayList<>(); + + for (int i = 0; i < logList.size(); i++) { + list.add(toMap(logList.get(i))); + } + + return list; + } + + protected static List> toStatisticsMapList(final List statisticsList) { + final List> list = new ArrayList<>(); + + for (int i = 0; i < statisticsList.size(); i++) { + list.add(toMap(statisticsList.get(i))); + } + + return list; + } + protected static boolean isValidPositiveNumber(final Integer value) { return (value != null) && (value >= 0); } @@ -1422,7 +1556,7 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met protected void emitSession(final Session session) { final HashMap sessionMap = new HashMap<>(); - sessionMap.put(EVENT_EXECUTE_CALLBACK_EVENT, toMap(session)); + sessionMap.put(EVENT_COMPLETE_CALLBACK_EVENT, toMap(session)); resultHandler.successAsync(eventSink, sessionMap); } diff --git a/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFmpegSessionExecuteTask.java b/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFmpegSessionExecuteTask.java new file mode 100644 index 0000000..a099cf4 --- /dev/null +++ b/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFmpegSessionExecuteTask.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Taner Sener + * + * This file is part of FFmpegKit. + * + * FFmpegKit 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. + * + * FFmpegKit 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 FFmpegKit. If not, see . + */ + +package com.arthenica.ffmpegkit.flutter; + +import androidx.annotation.NonNull; + +import com.arthenica.ffmpegkit.FFmpegKitConfig; +import com.arthenica.ffmpegkit.FFmpegSession; + +import io.flutter.plugin.common.MethodChannel; + +public class FFmpegSessionExecuteTask implements Runnable { + private final FFmpegSession ffmpegSession; + private final FFmpegKitFlutterMethodResultHandler resultHandler; + private final MethodChannel.Result result; + + public FFmpegSessionExecuteTask(@NonNull final FFmpegSession ffmpegSession, @NonNull final FFmpegKitFlutterMethodResultHandler resultHandler, @NonNull final MethodChannel.Result result) { + this.ffmpegSession = ffmpegSession; + this.resultHandler = resultHandler; + this.result = result; + } + + @Override + public void run() { + FFmpegKitConfig.ffmpegExecute(ffmpegSession); + resultHandler.successAsync(result, null); + } +} diff --git a/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFprobeSessionExecuteTask.java b/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFprobeSessionExecuteTask.java new file mode 100644 index 0000000..dc748eb --- /dev/null +++ b/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/FFprobeSessionExecuteTask.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 Taner Sener + * + * This file is part of FFmpegKit. + * + * FFmpegKit 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. + * + * FFmpegKit 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 FFmpegKit. If not, see . + */ + +package com.arthenica.ffmpegkit.flutter; + +import androidx.annotation.NonNull; + +import com.arthenica.ffmpegkit.FFmpegKitConfig; +import com.arthenica.ffmpegkit.FFprobeSession; + +import io.flutter.plugin.common.MethodChannel; + +public class FFprobeSessionExecuteTask implements Runnable { + private final FFprobeSession ffprobeSession; + private final FFmpegKitFlutterMethodResultHandler resultHandler; + private final MethodChannel.Result result; + + public FFprobeSessionExecuteTask(@NonNull final FFprobeSession ffprobeSession, @NonNull final FFmpegKitFlutterMethodResultHandler resultHandler, @NonNull final MethodChannel.Result result) { + this.ffprobeSession = ffprobeSession; + this.resultHandler = resultHandler; + this.result = result; + } + + @Override + public void run() { + FFmpegKitConfig.ffprobeExecute(ffprobeSession); + resultHandler.successAsync(result, null); + } +} diff --git a/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/MediaInformationSessionExecuteTask.java b/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/MediaInformationSessionExecuteTask.java new file mode 100644 index 0000000..f942cd2 --- /dev/null +++ b/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/MediaInformationSessionExecuteTask.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 Taner Sener + * + * This file is part of FFmpegKit. + * + * FFmpegKit 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. + * + * FFmpegKit 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 FFmpegKit. If not, see . + */ + +package com.arthenica.ffmpegkit.flutter; + +import androidx.annotation.NonNull; + +import com.arthenica.ffmpegkit.FFmpegKitConfig; +import com.arthenica.ffmpegkit.MediaInformationSession; + +import io.flutter.plugin.common.MethodChannel; + +public class MediaInformationSessionExecuteTask implements Runnable { + private final MediaInformationSession mediaInformationSession; + private final int timeout; + private final FFmpegKitFlutterMethodResultHandler resultHandler; + private final MethodChannel.Result result; + + public MediaInformationSessionExecuteTask(@NonNull final MediaInformationSession mediaInformationSession, final int timeout, @NonNull final FFmpegKitFlutterMethodResultHandler resultHandler, @NonNull final MethodChannel.Result result) { + this.mediaInformationSession = mediaInformationSession; + this.timeout = timeout; + this.resultHandler = resultHandler; + this.result = result; + } + + @Override + public void run() { + FFmpegKitConfig.getMediaInformationExecute(mediaInformationSession, timeout); + resultHandler.successAsync(result, null); + } +} diff --git a/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/AsyncWriteToPipeTask.java b/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/WriteToPipeTask.java similarity index 89% rename from flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/AsyncWriteToPipeTask.java rename to flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/WriteToPipeTask.java index d052738..e157437 100644 --- a/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/AsyncWriteToPipeTask.java +++ b/flutter/flutter/android/src/main/java/com/arthenica/ffmpegkit/flutter/WriteToPipeTask.java @@ -29,13 +29,13 @@ import java.io.IOException; import io.flutter.plugin.common.MethodChannel; -public class AsyncWriteToPipeTask implements Runnable { +public class WriteToPipeTask implements Runnable { private final String inputPath; private final String namedPipePath; private final FFmpegKitFlutterMethodResultHandler resultHandler; private final MethodChannel.Result result; - public AsyncWriteToPipeTask(@NonNull final String inputPath, @NonNull final String namedPipePath, @NonNull final FFmpegKitFlutterMethodResultHandler resultHandler, @NonNull final MethodChannel.Result result) { + public WriteToPipeTask(@NonNull final String inputPath, @NonNull final String namedPipePath, @NonNull final FFmpegKitFlutterMethodResultHandler resultHandler, @NonNull final MethodChannel.Result result) { this.inputPath = inputPath; this.namedPipePath = namedPipePath; this.resultHandler = resultHandler; diff --git a/flutter/flutter/ios/Classes/FFmpegKitFlutterPlugin.m b/flutter/flutter/ios/Classes/FFmpegKitFlutterPlugin.m index 649b4dd..c7b5f8a 100644 --- a/flutter/flutter/ios/Classes/FFmpegKitFlutterPlugin.m +++ b/flutter/flutter/ios/Classes/FFmpegKitFlutterPlugin.m @@ -57,7 +57,7 @@ static int const SESSION_TYPE_MEDIA_INFORMATION = 3; // EVENTS static NSString *const EVENT_LOG_CALLBACK_EVENT = @"FFmpegKitLogCallbackEvent"; static NSString *const EVENT_STATISTICS_CALLBACK_EVENT = @"FFmpegKitStatisticsCallbackEvent"; -static NSString *const EVENT_EXECUTE_CALLBACK_EVENT = @"FFmpegKitExecuteCallbackEvent"; +static NSString *const EVENT_COMPLETE_CALLBACK_EVENT = @"FFmpegKitCompleteCallbackEvent"; // ARGUMENT NAMES static NSString *const ARGUMENT_SESSION_ID = @"sessionId"; @@ -71,7 +71,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; FlutterEventSink _eventSink; BOOL logsEnabled; BOOL statisticsEnabled; - dispatch_queue_t asyncWriteToPipeDispatchQueue; + dispatch_queue_t asyncDispatchQueue; } - (instancetype)init { @@ -79,9 +79,9 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; if (self) { logsEnabled = false; statisticsEnabled = false; - asyncWriteToPipeDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + asyncDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - [self registerGlobalCallbacks]; + NSLog(@"FFmpegKitFlutterPlugin %p created.\n", self); } return self; @@ -89,6 +89,8 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; - (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { _eventSink = eventSink; + NSLog(@"FFmpegKitFlutterPlugin %p started listening to events on %p.\n", self, eventSink); + [self registerGlobalCallbacks]; return nil; } @@ -108,9 +110,19 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; } - (void)registerGlobalCallbacks { - [FFmpegKitConfig enableExecuteCallback:^(id session){ + [FFmpegKitConfig enableFFmpegSessionCompleteCallback:^(FFmpegSession* session){ NSDictionary *dictionary = [FFmpegKitFlutterPlugin toSessionDictionary:session]; - self->_eventSink([FFmpegKitFlutterPlugin toStringDictionary:EVENT_EXECUTE_CALLBACK_EVENT withDictionary:dictionary]); + self->_eventSink([FFmpegKitFlutterPlugin toStringDictionary:EVENT_COMPLETE_CALLBACK_EVENT withDictionary:dictionary]); + }]; + + [FFmpegKitConfig enableFFprobeSessionCompleteCallback:^(FFprobeSession* session){ + NSDictionary *dictionary = [FFmpegKitFlutterPlugin toSessionDictionary:session]; + self->_eventSink([FFmpegKitFlutterPlugin toStringDictionary:EVENT_COMPLETE_CALLBACK_EVENT withDictionary:dictionary]); + }]; + + [FFmpegKitConfig enableMediaInformationSessionCompleteCallback:^(MediaInformationSession* session){ + NSDictionary *dictionary = [FFmpegKitFlutterPlugin toSessionDictionary:session]; + self->_eventSink([FFmpegKitFlutterPlugin toStringDictionary:EVENT_COMPLETE_CALLBACK_EVENT withDictionary:dictionary]); }]; [FFmpegKitConfig enableLogCallback: ^(Log* log){ @@ -220,6 +232,12 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; } else { result([FlutterError errorWithCode:@"INVALID_ARGUMENTS" message:@"Invalid arguments array." details:nil]); } + } else if ([@"getMediaInformation" isEqualToString:call.method]) { + if (sessionId != nil) { + [self getMediaInformation:sessionId result:result]; + } else { + result([FlutterError errorWithCode:@"INVALID_SESSION" message:@"Invalid session id." details:nil]); + } } else if ([@"mediaInformationJsonParserFrom" isEqualToString:call.method]) { if (ffprobeJsonOutput != nil) { [self mediaInformationJsonParserFrom:ffprobeJsonOutput result:result]; @@ -299,6 +317,24 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; } else { result([FlutterError errorWithCode:@"INVALID_SIGNAL" message:@"Invalid signal value." details:nil]); } + } else if ([@"ffmpegSessionExecute" isEqualToString:call.method]) { + if (sessionId != nil) { + [self ffmpegSessionExecute:sessionId result:result]; + } else { + result([FlutterError errorWithCode:@"INVALID_SESSION" message:@"Invalid session id." details:nil]); + } + } else if ([@"ffprobeSessionExecute" isEqualToString:call.method]) { + if (sessionId != nil) { + [self ffprobeSessionExecute:sessionId result:result]; + } else { + result([FlutterError errorWithCode:@"INVALID_SESSION" message:@"Invalid session id." details:nil]); + } + } else if ([@"mediaInformationSessionExecute" isEqualToString:call.method]) { + if (sessionId != nil) { + [self mediaInformationSessionExecute:sessionId timeout:waitTimeout result:result]; + } else { + result([FlutterError errorWithCode:@"INVALID_SESSION" message:@"Invalid session id." details:nil]); + } } else if ([@"asyncFFmpegSessionExecute" isEqualToString:call.method]) { if (sessionId != nil) { [self asyncFFmpegSessionExecute:sessionId result:result]; @@ -399,6 +435,8 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; [self getFFmpegSessions:result]; } else if ([@"getFFprobeSessions" isEqualToString:call.method]) { [self getFFprobeSessions:result]; + } else if ([@"getMediaInformationSessions" isEqualToString:call.method]) { + [self getMediaInformationSessions:result]; } else if ([@"getPackageName" isEqualToString:call.method]) { [self getPackageName:result]; } else if ([@"getExternalLibraries" isEqualToString:call.method]) { @@ -525,7 +563,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; // FFmpegSession - (void)ffmpegSession:(NSArray*)arguments result:(FlutterResult)result { - FFmpegSession* session = [[FFmpegSession alloc] init:arguments withExecuteCallback:nil withLogCallback:nil withStatisticsCallback:nil withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs]; + FFmpegSession* session = [[FFmpegSession alloc] init:arguments withCompleteCallback:nil withLogCallback:nil withStatisticsCallback:nil withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs]; result([FFmpegKitFlutterPlugin toSessionDictionary:session]); } @@ -534,7 +572,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; if (session == nil) { result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); } else { - if ([session isMemberOfClass:[FFmpegSession class]]) { + if ([session isFFmpeg]) { int timeout; if ([FFmpegKitFlutterPlugin isValidPositiveNumber:waitTimeout]) { timeout = [waitTimeout intValue]; @@ -554,7 +592,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; if (session == nil) { result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); } else { - if ([session isMemberOfClass:[FFmpegSession class]]) { + if ([session isFFmpeg]) { NSArray* statistics = [(FFmpegSession*)session getStatistics]; result([FFmpegKitFlutterPlugin toStatisticsArray:statistics]); } else { @@ -566,17 +604,31 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; // FFprobeSession - (void)ffprobeSession:(NSArray*)arguments result:(FlutterResult)result { - FFprobeSession* session = [[FFprobeSession alloc] init:arguments withExecuteCallback:nil withLogCallback:nil withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs]; + FFprobeSession* session = [[FFprobeSession alloc] init:arguments withCompleteCallback:nil withLogCallback:nil withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs]; result([FFmpegKitFlutterPlugin toSessionDictionary:session]); } // MediaInformationSession - (void)mediaInformationSession:(NSArray*)arguments result:(FlutterResult)result { - MediaInformationSession* session = [[MediaInformationSession alloc] init:arguments withExecuteCallback:nil withLogCallback:nil]; + MediaInformationSession* session = [[MediaInformationSession alloc] init:arguments withCompleteCallback:nil withLogCallback:nil]; result([FFmpegKitFlutterPlugin toSessionDictionary:session]); } +- (void)getMediaInformation:(NSNumber*)sessionId result:(FlutterResult)result { + AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:[sessionId longValue]]; + if (session == nil) { + result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); + } else { + if ([session isMediaInformation]) { + MediaInformationSession *mediaInformationSession = (MediaInformationSession*)session; + result([FFmpegKitFlutterPlugin toMediaInformationDictionary:[mediaInformationSession getMediaInformation]]); + } else { + result([FlutterError errorWithCode:@"NOT_MEDIA_INFORMATION_SESSION" message:@"A session is found but it does not have the correct type." details:nil]); + } + } +} + // MediaInformationJsonParser - (void)mediaInformationJsonParserFrom:(NSString*)ffprobeJsonOutput result:(FlutterResult)result { @@ -708,12 +760,66 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; } } +- (void)ffmpegSessionExecute:(NSNumber*)sessionId result:(FlutterResult)result { + AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:[sessionId longValue]]; + if (session == nil) { + result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); + } else { + if ([session isFFmpeg]) { + dispatch_async(asyncDispatchQueue, ^{ + [FFmpegKitConfig ffmpegExecute:(FFmpegSession*)session]; + result(nil); + }); + } else { + result([FlutterError errorWithCode:@"NOT_FFMPEG_SESSION" message:@"A session is found but it does not have the correct type." details:nil]); + } + } +} + +- (void)ffprobeSessionExecute:(NSNumber*)sessionId result:(FlutterResult)result { + AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:[sessionId longValue]]; + if (session == nil) { + result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); + } else { + if ([session isFFprobe]) { + dispatch_async(asyncDispatchQueue, ^{ + [FFmpegKitConfig ffprobeExecute:(FFprobeSession*)session]; + result(nil); + }); + } else { + result([FlutterError errorWithCode:@"NOT_FFPROBE_SESSION" message:@"A session is found but it does not have the correct type." details:nil]); + } + } +} + +- (void)mediaInformationSessionExecute:(NSNumber*)sessionId timeout:(NSNumber*)waitTimeout result:(FlutterResult)result { + AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:[sessionId longValue]]; + if (session == nil) { + result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); + } else { + if ([session isMediaInformation]) { + int timeout; + if ([FFmpegKitFlutterPlugin isValidPositiveNumber:waitTimeout]) { + timeout = [waitTimeout intValue]; + } else { + timeout = AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; + } + dispatch_async(asyncDispatchQueue, ^{ + [FFmpegKitConfig getMediaInformationExecute:(MediaInformationSession*)session withTimeout:timeout]; + result(nil); + }); + } else { + result([FlutterError errorWithCode:@"NOT_MEDIA_INFORMATION_SESSION" message:@"A session is found but it does not have the correct type." details:nil]); + } + } +} + - (void)asyncFFmpegSessionExecute:(NSNumber*)sessionId result:(FlutterResult)result { AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:[sessionId longValue]]; if (session == nil) { result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); } else { - if ([session isMemberOfClass:[FFmpegSession class]]) { + if ([session isFFmpeg]) { [FFmpegKitConfig asyncFFmpegExecute:(FFmpegSession*)session]; result(nil); } else { @@ -727,7 +833,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; if (session == nil) { result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); } else { - if ([session isMemberOfClass:[FFprobeSession class]]) { + if ([session isFFprobe]) { [FFmpegKitConfig asyncFFprobeExecute:(FFprobeSession*)session]; result(nil); } else { @@ -741,7 +847,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; if (session == nil) { result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); } else { - if ([session isMemberOfClass:[MediaInformationSession class]]) { + if ([session isMediaInformation]) { int timeout; if ([FFmpegKitFlutterPlugin isValidPositiveNumber:waitTimeout]) { timeout = [waitTimeout intValue]; @@ -852,7 +958,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; } - (void)writeToPipe:(NSString*)inputPath pipe:(NSString*)namedPipePath result:(FlutterResult)result { - dispatch_async(asyncWriteToPipeDispatchQueue, ^{ + dispatch_async(asyncDispatchQueue, ^{ NSLog(@"Starting copy %@ to pipe %@ operation.\n", inputPath, namedPipePath); @@ -933,7 +1039,11 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; // FFprobeKit - (void)getFFprobeSessions:(FlutterResult)result { - result([FFmpegKitFlutterPlugin toSessionArray:[FFprobeKit listSessions]]); + result([FFmpegKitFlutterPlugin toSessionArray:[FFprobeKit listFFprobeSessions]]); +} + +- (void)getMediaInformationSessions:(FlutterResult)result { + result([FFmpegKitFlutterPlugin toSessionArray:[FFprobeKit listMediaInformationSessions]]); } // Packages @@ -971,16 +1081,14 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; dictionary[KEY_SESSION_START_TIME] = [NSNumber numberWithLong:[[session getStartTime] timeIntervalSince1970]*1000]; dictionary[KEY_SESSION_COMMAND] = [session getCommand]; - if ([session isFFprobe]) { - if ([(AbstractSession*)session isMemberOfClass:[MediaInformationSession class]]) { - MediaInformationSession *mediaInformationSession = (MediaInformationSession*)session; - dictionary[KEY_SESSION_MEDIA_INFORMATION] = [FFmpegKitFlutterPlugin toMediaInformationDictionary:[mediaInformationSession getMediaInformation]]; - dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_MEDIA_INFORMATION]; - } else { - dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_FFPROBE]; - } - } else { + if ([session isFFmpeg]) { dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_FFMPEG]; + } else if ([session isFFprobe]) { + dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_FFPROBE]; + } else if ([session isMediaInformation]) { + MediaInformationSession *mediaInformationSession = (MediaInformationSession*)session; + dictionary[KEY_SESSION_MEDIA_INFORMATION] = [FFmpegKitFlutterPlugin toMediaInformationDictionary:[mediaInformationSession getMediaInformation]]; + dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_MEDIA_INFORMATION]; } return dictionary; diff --git a/flutter/flutter/ios/ffmpeg_kit_flutter.podspec b/flutter/flutter/ios/ffmpeg_kit_flutter.podspec index ffc3996..9cbeaee 100644 --- a/flutter/flutter/ios/ffmpeg_kit_flutter.podspec +++ b/flutter/flutter/ios/ffmpeg_kit_flutter.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'ffmpeg_kit_flutter' - s.version = '4.5.0' + s.version = '4.5.1' s.summary = 'FFmpeg Kit for Flutter' s.description = 'A Flutter plugin for running FFmpeg and FFprobe commands.' s.homepage = 'https://github.com/tanersener/ffmpeg-kit' @@ -23,113 +23,113 @@ Pod::Spec.new do |s| s.subspec 'min' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-min', "4.5" + ss.dependency 'ffmpeg-kit-ios-min', "4.5.1" ss.ios.deployment_target = '12.1' end s.subspec 'min-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-min', "4.5.LTS" - ss.ios.deployment_target = '9.3' + ss.dependency 'ffmpeg-kit-ios-min', "4.5.1.LTS" + ss.ios.deployment_target = '10' end s.subspec 'min-gpl' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-min-gpl', "4.5" + ss.dependency 'ffmpeg-kit-ios-min-gpl', "4.5.1" ss.ios.deployment_target = '12.1' end s.subspec 'min-gpl-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-min-gpl', "4.5.LTS" - ss.ios.deployment_target = '9.3' + ss.dependency 'ffmpeg-kit-ios-min-gpl', "4.5.1.LTS" + ss.ios.deployment_target = '10' end s.subspec 'https' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-https', "4.5" + ss.dependency 'ffmpeg-kit-ios-https', "4.5.1" ss.ios.deployment_target = '12.1' end s.subspec 'https-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-https', "4.5.LTS" - ss.ios.deployment_target = '9.3' + ss.dependency 'ffmpeg-kit-ios-https', "4.5.1.LTS" + ss.ios.deployment_target = '10' end s.subspec 'https-gpl' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-https-gpl', "4.5" + ss.dependency 'ffmpeg-kit-ios-https-gpl', "4.5.1" ss.ios.deployment_target = '12.1' end s.subspec 'https-gpl-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-https-gpl', "4.5.LTS" - ss.ios.deployment_target = '9.3' + ss.dependency 'ffmpeg-kit-ios-https-gpl', "4.5.1.LTS" + ss.ios.deployment_target = '10' end s.subspec 'audio' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-audio', "4.5" + ss.dependency 'ffmpeg-kit-ios-audio', "4.5.1" ss.ios.deployment_target = '12.1' end s.subspec 'audio-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-audio', "4.5.LTS" - ss.ios.deployment_target = '9.3' + ss.dependency 'ffmpeg-kit-ios-audio', "4.5.1.LTS" + ss.ios.deployment_target = '10' end s.subspec 'video' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-video', "4.5" + ss.dependency 'ffmpeg-kit-ios-video', "4.5.1" ss.ios.deployment_target = '12.1' end s.subspec 'video-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-video', "4.5.LTS" - ss.ios.deployment_target = '9.3' + ss.dependency 'ffmpeg-kit-ios-video', "4.5.1.LTS" + ss.ios.deployment_target = '10' end s.subspec 'full' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-full', "4.5" + ss.dependency 'ffmpeg-kit-ios-full', "4.5.1" ss.ios.deployment_target = '12.1' end s.subspec 'full-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-full', "4.5.LTS" - ss.ios.deployment_target = '9.3' + ss.dependency 'ffmpeg-kit-ios-full', "4.5.1.LTS" + ss.ios.deployment_target = '10' end s.subspec 'full-gpl' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-full-gpl', "4.5" + ss.dependency 'ffmpeg-kit-ios-full-gpl', "4.5.1" ss.ios.deployment_target = '12.1' end s.subspec 'full-gpl-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-ios-full-gpl', "4.5.LTS" - ss.ios.deployment_target = '9.3' + ss.dependency 'ffmpeg-kit-ios-full-gpl', "4.5.1.LTS" + ss.ios.deployment_target = '10' end end diff --git a/flutter/flutter/lib/abstract_session.dart b/flutter/flutter/lib/abstract_session.dart index 6529192..4667569 100644 --- a/flutter/flutter/lib/abstract_session.dart +++ b/flutter/flutter/lib/abstract_session.dart @@ -20,7 +20,6 @@ import 'package:ffmpeg_kit_flutter_platform_interface/ffmpeg_kit_flutter_platform_interface.dart'; import 'package:flutter/services.dart'; -import 'execute_callback.dart'; import 'ffmpeg_kit_config.dart'; import 'ffmpeg_session.dart'; import 'ffprobe_session.dart'; @@ -35,7 +34,7 @@ import 'session_state.dart'; import 'src/ffmpeg_kit_factory.dart'; /// Abstract session implementation which includes common features shared by -/// "FFmpeg" and "FFprobe" sessions. +/// "FFmpeg", "FFprobe" and "MediaInformation" sessions. class AbstractSession extends Session { static FFmpegKitPlatform _platform = FFmpegKitPlatform.instance; @@ -221,11 +220,7 @@ class AbstractSession extends Session { return session; } - /// Returns the session specific execute callback function. - ExecuteCallback? getExecuteCallback() => - FFmpegKitFactory.getExecuteCallback(this.getSessionId()); - - /// Returns the session specific log callback function. + /// Returns the session specific log callback. LogCallback? getLogCallback() => FFmpegKitFactory.getLogCallback(this.getSessionId()); @@ -429,6 +424,9 @@ class AbstractSession extends Session { /// Returns whether it is an "FFprobe" session or not. bool isFFprobe() => false; + /// Returns whether it is an "MediaInformation" session or not. + bool isMediaInformation() => false; + /// Cancels running the session. void cancel() {} } diff --git a/flutter/flutter/lib/chapter.dart b/flutter/flutter/lib/chapter.dart new file mode 100644 index 0000000..cbf7bfc --- /dev/null +++ b/flutter/flutter/lib/chapter.dart @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 Taner Sener + * + * This file is part of FFmpegKit. + * + * FFmpegKit 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. + * + * FFmpegKit 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 FFmpegKit. If not, see . + */ + +/// Chapter class. +class Chapter { + static const keyId = "id"; + static const keyTimeBase = "time_base"; + static const keyStart = "start"; + static const keyStartTime = "start_time"; + static const keyEnd = "end"; + static const keyEndTime = "end_time"; + static const keyTags = "tags"; + + Map? _allProperties; + + /// Creates a new [Chapter] instance + Chapter(this._allProperties); + + /// Returns id. + int? getId() => this.getNumberProperty(Chapter.keyId)?.toInt(); + + /// Returns time base. + String? getTimeBase() => this.getStringProperty(Chapter.keyTimeBase); + + /// Returns start. + int? getStart() => this.getNumberProperty(Chapter.keyStart)?.toInt(); + + /// Returns start time. + String? getStartTime() => this.getStringProperty(Chapter.keyStartTime); + + /// Returns end. + int? getEnd() => this.getNumberProperty(Chapter.keyEnd)?.toInt(); + + /// Returns end time. + String? getEndTime() => this.getStringProperty(Chapter.keyEndTime); + + /// Returns all tags. + Map? getTags() => this.getProperties(Chapter.keyTags); + + /// Returns the chapter property associated with the key. + String? getStringProperty(String key) => this._allProperties?[key]; + + /// Returns the chapter property associated with the key. + num? getNumberProperty(String key) => this._allProperties?[key]; + + /// Returns the chapter properties associated with the key. + dynamic getProperties(String key) => this._allProperties?[key]; + + /// Returns all properties found. + Map? getAllProperties() => this._allProperties; +} diff --git a/flutter/flutter/lib/ffmpeg_kit.dart b/flutter/flutter/lib/ffmpeg_kit.dart index c19efe2..88d3d29 100644 --- a/flutter/flutter/lib/ffmpeg_kit.dart +++ b/flutter/flutter/lib/ffmpeg_kit.dart @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021 Taner Sener + * Copyright (c) 2019-2022 Taner Sener * * This file is part of FFmpegKit. * @@ -20,9 +20,9 @@ import 'package:ffmpeg_kit_flutter_platform_interface/ffmpeg_kit_flutter_platform_interface.dart'; import 'package:flutter/services.dart'; -import 'execute_callback.dart'; import 'ffmpeg_kit_config.dart'; import 'ffmpeg_session.dart'; +import 'ffmpeg_session_complete_callback.dart'; import 'log_callback.dart'; import 'src/ffmpeg_kit_factory.dart'; import 'statistics_callback.dart'; @@ -31,32 +31,49 @@ import 'statistics_callback.dart'; class FFmpegKit { static FFmpegKitPlatform _platform = FFmpegKitPlatform.instance; + /// Synchronously executes FFmpeg command provided. Space character is used + /// to split command into arguments. You can use single or double quote + /// characters to specify arguments inside your command. + static Future execute(String command) async => + FFmpegKit.executeWithArguments(FFmpegKitConfig.parseArguments(command)); + + /// Synchronously executes FFmpeg with arguments provided. + static Future executeWithArguments( + List commandArguments) async { + final session = + await FFmpegSession.create(commandArguments, null, null, null, null); + + await FFmpegKitConfig.ffmpegExecute(session); + + return session; + } + /// Starts an asynchronous FFmpeg execution for the given command. Space character is used to split the command /// into arguments. You can use single or double quote characters to specify arguments inside your command. /// /// Note that this method returns immediately and does not wait the execution to complete. You must use an - /// [ExecuteCallback] if you want to be notified about the result. + /// [FFmpegSessionCompleteCallback] if you want to be notified about the result. static Future executeAsync(String command, - [ExecuteCallback? executeCallback = null, + [FFmpegSessionCompleteCallback? completeCallback = null, LogCallback? logCallback = null, StatisticsCallback? statisticsCallback = null]) async => FFmpegKit.executeWithArgumentsAsync( FFmpegKitConfig.parseArguments(command), - executeCallback, + completeCallback, logCallback, statisticsCallback); /// Starts an asynchronous FFmpeg execution with arguments provided. /// /// Note that this method returns immediately and does not wait the execution to complete. You must use an - /// [ExecuteCallback] if you want to be notified about the result. + /// [FFmpegSessionCompleteCallback] if you want to be notified about the result. static Future executeWithArgumentsAsync( List commandArguments, - [ExecuteCallback? executeCallback = null, + [FFmpegSessionCompleteCallback? completeCallback = null, LogCallback? logCallback = null, StatisticsCallback? statisticsCallback = null]) async { final session = await FFmpegSession.create(commandArguments, - executeCallback, logCallback, statisticsCallback, null); + completeCallback, logCallback, statisticsCallback, null); await FFmpegKitConfig.asyncFFmpegExecute(session); diff --git a/flutter/flutter/lib/ffmpeg_kit_config.dart b/flutter/flutter/lib/ffmpeg_kit_config.dart index b2c037e..bf57480 100644 --- a/flutter/flutter/lib/ffmpeg_kit_config.dart +++ b/flutter/flutter/lib/ffmpeg_kit_config.dart @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021 Taner Sener + * Copyright (c) 2019-2022 Taner Sener * * This file is part of FFmpegKit. * @@ -20,13 +20,15 @@ import 'package:ffmpeg_kit_flutter_platform_interface/ffmpeg_kit_flutter_platform_interface.dart'; import 'package:flutter/services.dart'; -import 'execute_callback.dart'; import 'ffmpeg_session.dart'; +import 'ffmpeg_session_complete_callback.dart'; import 'ffprobe_session.dart'; +import 'ffprobe_session_complete_callback.dart'; import 'level.dart'; import 'log_callback.dart'; import 'log_redirection_strategy.dart'; import 'media_information_session.dart'; +import 'media_information_session_complete_callback.dart'; import 'session.dart'; import 'session_state.dart'; import 'signal.dart'; @@ -215,10 +217,48 @@ class FFmpegKitConfig { } } + /// Synchronously executes the FFmpeg session provided. + static Future ffmpegExecute(FFmpegSession ffmpegSession) async { + try { + await init(); + return _platform + .ffmpegKitConfigFFmpegExecute(ffmpegSession.getSessionId()); + } on PlatformException catch (e, stack) { + print("Plugin ffmpegExecute error: ${e.message}"); + return Future.error("ffmpegExecute failed.", stack); + } + } + + /// Synchronously executes the FFprobe session provided. + static Future ffprobeExecute(FFprobeSession ffprobeSession) async { + try { + await init(); + return _platform + .ffmpegKitConfigFFprobeExecute(ffprobeSession.getSessionId()); + } on PlatformException catch (e, stack) { + print("Plugin ffprobeExecute error: ${e.message}"); + return Future.error("ffprobeExecute failed.", stack); + } + } + + /// Synchronously executes the media information session provided. + static Future getMediaInformationExecute( + MediaInformationSession mediaInformationSession, + [int? waitTimeout = null]) async { + try { + await init(); + return _platform.ffmpegKitConfigGetMediaInformationExecute( + mediaInformationSession.getSessionId(), waitTimeout); + } on PlatformException catch (e, stack) { + print("Plugin getMediaInformationExecute error: ${e.message}"); + return Future.error("getMediaInformationExecute failed.", stack); + } + } + /// Starts an asynchronous FFmpeg execution for the given session. /// /// Note that this method returns immediately and does not wait the execution to complete. You must use an - /// [ExecuteCallback] if you want to be notified about the result. + /// [FFmpegSessionCompleteCallback] if you want to be notified about the result. static Future asyncFFmpegExecute(FFmpegSession ffmpegSession) async { try { await init(); @@ -233,7 +273,7 @@ class FFmpegKitConfig { /// Starts an asynchronous FFprobe execution for the given session. /// /// Note that this method returns immediately and does not wait the execution to complete. You must use an - /// [ExecuteCallback] if you want to be notified about the result. + /// [FFprobeSessionCompleteCallback] if you want to be notified about the result. static Future asyncFFprobeExecute(FFprobeSession ffprobeSession) async { try { await init(); @@ -248,7 +288,7 @@ class FFmpegKitConfig { /// Starts an asynchronous FFprobe execution for the given media information session. /// /// Note that this method returns immediately and does not wait the execution to complete. You must use an - /// [ExecuteCallback] if you want to be notified about the result. + /// [MediaInformationSessionCompleteCallback] if you want to be notified about the result. static Future asyncGetMediaInformationExecute( MediaInformationSession mediaInformationSession, [int? waitTimeout = null]) async { @@ -262,22 +302,55 @@ class FFmpegKitConfig { } } - /// Sets a global callback function to redirect FFmpeg/FFprobe logs. + /// Sets a global callback to redirect FFmpeg/FFprobe logs. static void enableLogCallback([LogCallback? logCallback = null]) { FFmpegKitFactory.setGlobalLogCallback(logCallback); } - /// Sets a global callback function to redirect FFmpeg statistics. + /// Sets a global callback to redirect FFmpeg statistics. static void enableStatisticsCallback( [StatisticsCallback? statisticsCallback = null]) { FFmpegKitFactory.setGlobalStatisticsCallback(statisticsCallback); } - /// Sets a global callback function to receive execution results. - static void enableExecuteCallback([ExecuteCallback? executeCallback = null]) { - FFmpegKitFactory.setGlobalExecuteCallback(executeCallback); + /// Sets a global FFmpegSessionCompleteCallback to receive execution results + /// for FFmpeg sessions. + static void enableFFmpegSessionCompleteCallback( + [FFmpegSessionCompleteCallback? ffmpegSessionCompleteCallback = null]) { + FFmpegKitFactory.setGlobalFFmpegSessionCompleteCallback( + ffmpegSessionCompleteCallback); } + /// Returns the global FFmpegSessionCompleteCallback set. + static FFmpegSessionCompleteCallback? getFFmpegSessionCompleteCallback() => + FFmpegKitFactory.getGlobalFFmpegSessionCompleteCallback(); + + /// Sets a global FFprobeSessionCompleteCallback to receive execution results + /// for FFprobe sessions. + static void enableFFprobeSessionCompleteCallback( + [FFprobeSessionCompleteCallback? ffprobeSessionCompleteCallback = null]) { + FFmpegKitFactory.setGlobalFFprobeSessionCompleteCallback( + ffprobeSessionCompleteCallback); + } + + /// Returns the global FFprobeSessionCompleteCallback set. + static FFprobeSessionCompleteCallback? getFFprobeSessionCompleteCallback() => + FFmpegKitFactory.getGlobalFFprobeSessionCompleteCallback(); + + /// Sets a global MediaInformationSessionCompleteCallback to receive + /// execution results for MediaInformation sessions. + static void enableMediaInformationSessionCompleteCallback( + [MediaInformationSessionCompleteCallback? + mediaInformationSessionCompleteCallback = null]) { + FFmpegKitFactory.setGlobalMediaInformationSessionCompleteCallback( + mediaInformationSessionCompleteCallback); + } + + /// Returns the global MediaInformationSessionCompleteCallback set. + static MediaInformationSessionCompleteCallback? + getMediaInformationSessionCompleteCallback() => + FFmpegKitFactory.getGlobalMediaInformationSessionCompleteCallback(); + /// Returns the current log level. static int getLogLevel() => _activeLogLevel; @@ -293,6 +366,55 @@ class FFmpegKitConfig { } } + /// Converts the given Structured Access Framework Uri ("content:…") into + /// an input url that can be used in FFmpeg and FFprobe commands. + /// + /// Note that this method is Android only. It will fail if called on other + /// platforms. It also requires API Level ≥ 19. On older API levels it + /// returns an empty url. + static Future getSafParameterForRead(String uriString) async { + try { + await init(); + return _platform.ffmpegKitConfigGetSafParameter(uriString, "r"); + } on PlatformException catch (e, stack) { + print("Plugin getSafParameterForRead error: ${e.message}"); + return Future.error("getSafParameterForRead failed.", stack); + } + } + + /// Converts the given Structured Access Framework Uri ("content:…") into + /// an output url that can be used in FFmpeg and FFprobe commands. + /// + /// Note that this method is Android only. It will fail if called on other + /// platforms. It also requires API Level ≥ 19. On older API levels it + /// returns an empty url. + static Future getSafParameterForWrite(String uriString) async { + try { + await init(); + return _platform.ffmpegKitConfigGetSafParameter(uriString, "w"); + } on PlatformException catch (e, stack) { + print("Plugin getSafParameterForWrite error: ${e.message}"); + return Future.error("getSafParameterForWrite failed.", stack); + } + } + + /// Converts the given Structured Access Framework Uri into an saf protocol + /// url opened with the given open mode. + /// + /// Note that this method is Android only. It will fail if called on other + /// platforms. It also requires API Level ≥ 19. On older API levels it + /// returns an empty url. + static Future getSafParameter( + String uriString, String openMode) async { + try { + await init(); + return _platform.ffmpegKitConfigGetSafParameter(uriString, openMode); + } on PlatformException catch (e, stack) { + print("Plugin getSafParameter error: ${e.message}"); + return Future.error("getSafParameter failed.", stack); + } + } + /// Returns the session history size. static Future getSessionHistorySize() async { try { @@ -386,6 +508,72 @@ class FFmpegKitConfig { } } + /// Returns all FFmpeg sessions in the session history. + static Future> getFFmpegSessions() async { + try { + await FFmpegKitConfig.init(); + return _platform.ffmpegKitListSessions().then((sessions) { + if (sessions == null) { + return List.empty(); + } else { + return sessions + .map((dynamic sessionObject) => FFmpegKitFactory.mapToSession( + sessionObject as Map)) + .map((session) => session as FFmpegSession) + .toList(); + } + }); + } on PlatformException catch (e, stack) { + print("Plugin getFFmpegSessions error: ${e.message}"); + return Future.error("getFFmpegSessions failed.", stack); + } + } + + /// Returns all FFprobe sessions in the session history. + static Future> getFFprobeSessions() async { + try { + await FFmpegKitConfig.init(); + return _platform.ffprobeKitListFFprobeSessions().then((sessions) { + if (sessions == null) { + return List.empty(); + } else { + return sessions + .map((dynamic sessionObject) => FFmpegKitFactory.mapToSession( + sessionObject as Map)) + .map((session) => session as FFprobeSession) + .toList(); + } + }); + } on PlatformException catch (e, stack) { + print("Plugin getFFprobeSessions error: ${e.message}"); + return Future.error("getFFprobeSessions failed.", stack); + } + } + + /// Returns all MediaInformation sessions in the session history. + static Future> + getMediaInformationSessions() async { + try { + await FFmpegKitConfig.init(); + return _platform + .ffprobeKitListMediaInformationSessions() + .then((sessions) { + if (sessions == null) { + return List.empty(); + } else { + return sessions + .map((dynamic sessionObject) => FFmpegKitFactory.mapToSession( + sessionObject as Map)) + .map((session) => session as MediaInformationSession) + .toList(); + } + }); + } on PlatformException catch (e, stack) { + print("Plugin getMediaInformationSessions error: ${e.message}"); + return Future.error("getMediaInformationSessions failed.", stack); + } + } + /// Returns sessions that have [sessionState]. static Future> getSessionsByState( SessionState sessionState) async { @@ -624,36 +812,4 @@ class FFmpegKitConfig { return Future.error("selectDocumentForWrite failed.", stack); } } - - /// Converts the given Structured Access Framework Uri ("content:…") into - /// an input url that can be used in FFmpeg and FFprobe commands. - /// - /// Note that this method is Android only. It will fail if called on other - /// platforms. It also requires API Level ≥ 19. On older API levels it - /// returns an empty url. - static Future getSafParameterForRead(String uriString) async { - try { - await init(); - return _platform.ffmpegKitConfigGetSafParameterForRead(uriString); - } on PlatformException catch (e, stack) { - print("Plugin getSafParameterForRead error: ${e.message}"); - return Future.error("getSafParameterForRead failed.", stack); - } - } - - /// Converts the given Structured Access Framework Uri ("content:…") into - /// an output url that can be used in FFmpeg and FFprobe commands. - /// - /// Note that this method is Android only. It will fail if called on other - /// platforms. It also requires API Level ≥ 19. On older API levels it - /// returns an empty url. - static Future getSafParameterForWrite(String uriString) async { - try { - await init(); - return _platform.ffmpegKitConfigGetSafParameterForWrite(uriString); - } on PlatformException catch (e, stack) { - print("Plugin getSafParameterForWrite error: ${e.message}"); - return Future.error("getSafParameterForWrite failed.", stack); - } - } } diff --git a/flutter/flutter/lib/ffmpeg_session.dart b/flutter/flutter/lib/ffmpeg_session.dart index 736b21d..2cbc777 100644 --- a/flutter/flutter/lib/ffmpeg_session.dart +++ b/flutter/flutter/lib/ffmpeg_session.dart @@ -21,8 +21,8 @@ import 'package:ffmpeg_kit_flutter_platform_interface/ffmpeg_kit_flutter_platfor import 'package:flutter/services.dart'; import 'abstract_session.dart'; -import 'execute_callback.dart'; import 'ffmpeg_kit_config.dart'; +import 'ffmpeg_session_complete_callback.dart'; import 'log_callback.dart'; import 'log_redirection_strategy.dart'; import 'src/ffmpeg_kit_factory.dart'; @@ -33,7 +33,7 @@ import 'statistics_callback.dart'; class FFmpegSession extends AbstractSession { /// Creates a new FFmpeg session with [argumentsArray]. static Future create(List argumentsArray, - [ExecuteCallback? executeCallback = null, + [FFmpegSessionCompleteCallback? completeCallback = null, LogCallback? logCallback = null, StatisticsCallback? statisticsCallback = null, LogRedirectionStrategy? logRedirectionStrategy = null]) async { @@ -41,7 +41,8 @@ class FFmpegSession extends AbstractSession { argumentsArray, logRedirectionStrategy); final sessionId = session.getSessionId(); - FFmpegKitFactory.setExecuteCallback(sessionId, executeCallback); + FFmpegKitFactory.setFFmpegSessionCompleteCallback( + sessionId, completeCallback); FFmpegKitFactory.setLogCallback(sessionId, logCallback); FFmpegKitFactory.setStatisticsCallback(sessionId, statisticsCallback); @@ -53,10 +54,14 @@ class FFmpegSession extends AbstractSession { static FFmpegSession fromMap(Map sessionMap) => AbstractSession.createFFmpegSessionFromMap(sessionMap); - /// Returns the session specific statistics callback function. + /// Returns the session specific statistics callback. StatisticsCallback? getStatisticsCallback() => FFmpegKitFactory.getStatisticsCallback(this.getSessionId()); + /// Returns the session specific complete callback. + FFmpegSessionCompleteCallback? getCompleteCallback() => + FFmpegKitFactory.getFFmpegSessionCompleteCallback(this.getSessionId()); + /// Returns all statistics entries generated for this session. If there are /// asynchronous statistics that are not delivered yet, this method waits for /// them until [waitTimeout]. @@ -120,4 +125,6 @@ class FFmpegSession extends AbstractSession { bool isFFmpeg() => true; bool isFFprobe() => false; + + bool isMediaInformation() => false; } diff --git a/flutter/flutter/lib/execute_callback.dart b/flutter/flutter/lib/ffmpeg_session_complete_callback.dart similarity index 73% rename from flutter/flutter/lib/execute_callback.dart rename to flutter/flutter/lib/ffmpeg_session_complete_callback.dart index 063bff7..4e8a40c 100644 --- a/flutter/flutter/lib/execute_callback.dart +++ b/flutter/flutter/lib/ffmpeg_session_complete_callback.dart @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021 Taner Sener + * Copyright (c) 2021 Taner Sener * * This file is part of FFmpegKit. * @@ -17,13 +17,13 @@ * along with FFmpegKit. If not, see . */ -import 'session.dart'; +import 'ffmpeg_session.dart'; -/// Callback function invoked when a session ends running. -/// Session has either SessionState.completed or SessionState.failed state when -/// the callback is invoked. +/// Callback function that is invoked when an asynchronous FFmpeg session has +/// ended. Session has either SessionState.completed or SessionState.failed +/// state when the callback is invoked. /// If it has SessionState.completed state, "ReturnCode" should be checked to /// see the execution result. /// If "getState" returns SessionState.failed then "getFailStackTrace" should /// be used to get the failure reason. -typedef ExecuteCallback = void Function(Session session); +typedef FFmpegSessionCompleteCallback = void Function(FFmpegSession session); diff --git a/flutter/flutter/lib/ffprobe_kit.dart b/flutter/flutter/lib/ffprobe_kit.dart index 87e2aff..49165a2 100644 --- a/flutter/flutter/lib/ffprobe_kit.dart +++ b/flutter/flutter/lib/ffprobe_kit.dart @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021 Taner Sener + * Copyright (c) 2019-2022 Taner Sener * * This file is part of FFmpegKit. * @@ -20,52 +20,119 @@ import 'package:ffmpeg_kit_flutter_platform_interface/ffmpeg_kit_flutter_platform_interface.dart'; import 'package:flutter/services.dart'; -import 'execute_callback.dart'; import 'ffmpeg_kit_config.dart'; import 'ffprobe_session.dart'; +import 'ffprobe_session_complete_callback.dart'; import 'log_callback.dart'; import 'media_information_session.dart'; +import 'media_information_session_complete_callback.dart'; import 'src/ffmpeg_kit_factory.dart'; /// Main class to run "FFprobe" commands. class FFprobeKit { static FFmpegKitPlatform _platform = FFmpegKitPlatform.instance; + /// Synchronously executes FFprobe command provided. Space character is used + /// to split command into arguments. You can use single or double quote + /// characters to specify arguments inside your command. + static Future execute(String command) async => + FFprobeKit.executeWithArguments(FFmpegKitConfig.parseArguments(command)); + + /// Synchronously executes FFprobe with arguments provided. + static Future executeWithArguments( + List commandArguments) async { + final session = + await FFprobeSession.create(commandArguments, null, null, null); + + await FFmpegKitConfig.ffprobeExecute(session); + + return session; + } + /// Starts an asynchronous FFprobe execution for the given command. Space character is used to split the command /// into arguments. You can use single or double quote characters to specify arguments inside your command. /// /// Note that this method returns immediately and does not wait the execution to complete. You must use an - /// [ExecuteCallback] if you want to be notified about the result. + /// [FFprobeSessionCompleteCallback] if you want to be notified about the result. static Future executeAsync(String command, - [ExecuteCallback? executeCallback = null, + [FFprobeSessionCompleteCallback? completeCallback = null, LogCallback? logCallback = null]) async => FFprobeKit.executeWithArgumentsAsync( FFmpegKitConfig.parseArguments(command), - executeCallback, + completeCallback, logCallback); /// Starts an asynchronous FFprobe execution with arguments provided. /// /// Note that this method returns immediately and does not wait the execution to complete. You must use an - /// [ExecuteCallback] if you want to be notified about the result. + /// [FFprobeSessionCompleteCallback] if you want to be notified about the result. static Future executeWithArgumentsAsync( List commandArguments, - [ExecuteCallback? executeCallback = null, + [FFprobeSessionCompleteCallback? completeCallback = null, LogCallback? logCallback = null]) async { final session = await FFprobeSession.create( - commandArguments, executeCallback, logCallback, null); + commandArguments, completeCallback, logCallback, null); await FFmpegKitConfig.asyncFFprobeExecute(session); return session; } + /// Extracts media information for the file specified with path. + static Future getMediaInformation(String path, + [int? waitTimeout = null]) async { + final commandArguments = [ + "-v", + "error", + "-hide_banner", + "-print_format", + "json", + "-show_format", + "-show_streams", + "-show_chapters", + "-i", + path + ]; + return FFprobeKit.getMediaInformationFromCommandArguments( + commandArguments, waitTimeout); + } + + /// Extracts media information using the command provided. The command + /// passed to this method must generate the output in JSON format in order to + /// successfully extract media information from it. + static Future getMediaInformationFromCommand( + String command, + [int? waitTimeout = null]) async => + FFprobeKit.getMediaInformationFromCommandArguments( + FFmpegKitConfig.parseArguments(command), waitTimeout); + + /// Extracts media information using the command arguments provided. The + /// command passed to this method must generate the output in JSON format in + /// order to successfully extract media information from it. + static Future + getMediaInformationFromCommandArguments(List commandArguments, + [int? waitTimeout = null]) async { + final session = + await MediaInformationSession.create(commandArguments, null, null); + + await FFmpegKitConfig.getMediaInformationExecute(session, waitTimeout); + + final mediaInformation = await _platform + .mediaInformationSessionGetMediaInformation(session.getSessionId()) + .then(FFmpegKitFactory.mapToNullableMediaInformation); + if (mediaInformation != null) { + session.setMediaInformation(mediaInformation); + } + + return session; + } + /// Starts an asynchronous FFprobe execution to extract the media information for the specified file. /// /// Note that this method returns immediately and does not wait the execution to complete. You must use an - /// [ExecuteCallback] if you want to be notified about the result. + /// [MediaInformationSessionCompleteCallback] if you want to be notified about the result. static Future getMediaInformationAsync(String path, - [ExecuteCallback? executeCallback = null, + [MediaInformationSessionCompleteCallback? completeCallback = null, LogCallback? logCallback = null, int? waitTimeout = null]) async { final commandArguments = [ @@ -76,54 +143,64 @@ class FFprobeKit { "json", "-show_format", "-show_streams", + "-show_chapters", "-i", path ]; return FFprobeKit.getMediaInformationFromCommandArgumentsAsync( - commandArguments, executeCallback, logCallback, waitTimeout); + commandArguments, completeCallback, logCallback, waitTimeout); } /// Starts an asynchronous FFprobe execution to extract media information using a command. The command passed to /// this method must generate the output in JSON format in order to successfully extract media information from it. /// /// Note that this method returns immediately and does not wait the execution to complete. You must use an - /// [ExecuteCallback] if you want to be notified about the result. + /// [MediaInformationSessionCompleteCallback] if you want to be notified about the result. static Future getMediaInformationFromCommandAsync( String command, - [ExecuteCallback? executeCallback = null, + [MediaInformationSessionCompleteCallback? completeCallback = null, LogCallback? logCallback = null, int? waitTimeout = null]) async => FFprobeKit.getMediaInformationFromCommandArgumentsAsync( FFmpegKitConfig.parseArguments(command), - executeCallback, + completeCallback, logCallback, waitTimeout); - /// Starts an asynchronous FFprobe execution to extract media information using command arguments. The command - /// passed to this method must generate the output in JSON format in order to successfully extract media information - /// from it. + /// Starts an asynchronous FFprobe execution to extract media information + /// using command arguments. The command passed to this method must generate + /// the output in JSON format in order to successfully extract media + /// information from it. /// - /// Note that this method returns immediately and does not wait the execution to complete. You must use an - /// [ExecuteCallback] if you want to be notified about the result. + /// Note that this method returns immediately and does not wait the execution + /// to complete. You must use an [MediaInformationSessionCompleteCallback] if you want to be + /// notified about the result. static Future getMediaInformationFromCommandArgumentsAsync( List commandArguments, - [ExecuteCallback? executeCallback = null, + [MediaInformationSessionCompleteCallback? completeCallback = null, LogCallback? logCallback = null, int? waitTimeout = null]) async { final session = await MediaInformationSession.create( - commandArguments, executeCallback, logCallback); + commandArguments, completeCallback, logCallback); await FFmpegKitConfig.asyncGetMediaInformationExecute(session, waitTimeout); + final mediaInformation = await _platform + .mediaInformationSessionGetMediaInformation(session.getSessionId()) + .then(FFmpegKitFactory.mapToNullableMediaInformation); + if (mediaInformation != null) { + session.setMediaInformation(mediaInformation); + } + return session; } /// Lists all FFprobe sessions in the session history. - static Future> listSessions() async { + static Future> listFFprobeSessions() async { try { await FFmpegKitConfig.init(); - return _platform.ffprobeKitListSessions().then((sessions) { + return _platform.ffprobeKitListFFprobeSessions().then((sessions) { if (sessions == null) { return List.empty(); } else { @@ -135,8 +212,32 @@ class FFprobeKit { } }); } on PlatformException catch (e, stack) { - print("Plugin listSessions error: ${e.message}"); - return Future.error("listSessions failed.", stack); + print("Plugin listFFprobeSessions error: ${e.message}"); + return Future.error("listFFprobeSessions failed.", stack); + } + } + + /// Lists all MediaInformation sessions in the session history. + static Future> + listMediaInformationSessions() async { + try { + await FFmpegKitConfig.init(); + return _platform + .ffprobeKitListMediaInformationSessions() + .then((sessions) { + if (sessions == null) { + return List.empty(); + } else { + return sessions + .map((dynamic sessionObject) => FFmpegKitFactory.mapToSession( + sessionObject as Map)) + .map((session) => session as MediaInformationSession) + .toList(); + } + }); + } on PlatformException catch (e, stack) { + print("Plugin listMediaInformationSessions error: ${e.message}"); + return Future.error("listMediaInformationSessions failed.", stack); } } } diff --git a/flutter/flutter/lib/ffprobe_session.dart b/flutter/flutter/lib/ffprobe_session.dart index 74769ce..af35a38 100644 --- a/flutter/flutter/lib/ffprobe_session.dart +++ b/flutter/flutter/lib/ffprobe_session.dart @@ -18,7 +18,7 @@ */ import 'abstract_session.dart'; -import 'execute_callback.dart'; +import 'ffprobe_session_complete_callback.dart'; import 'log_callback.dart'; import 'log_redirection_strategy.dart'; import 'src/ffmpeg_kit_factory.dart'; @@ -27,14 +27,15 @@ import 'src/ffmpeg_kit_factory.dart'; class FFprobeSession extends AbstractSession { /// Creates a new FFprobe session with [argumentsArray]. static Future create(List argumentsArray, - [ExecuteCallback? executeCallback = null, + [FFprobeSessionCompleteCallback? completeCallback = null, LogCallback? logCallback = null, LogRedirectionStrategy? logRedirectionStrategy = null]) async { final session = await AbstractSession.createFFprobeSession( argumentsArray, logRedirectionStrategy); final sessionId = session.getSessionId(); - FFmpegKitFactory.setExecuteCallback(sessionId, executeCallback); + FFmpegKitFactory.setFFprobeSessionCompleteCallback( + sessionId, completeCallback); FFmpegKitFactory.setLogCallback(sessionId, logCallback); return session; @@ -45,7 +46,13 @@ class FFprobeSession extends AbstractSession { static FFprobeSession fromMap(Map sessionMap) => AbstractSession.createFFprobeSessionFromMap(sessionMap); + /// Returns the session specific complete callback. + FFprobeSessionCompleteCallback? getCompleteCallback() => + FFmpegKitFactory.getFFprobeSessionCompleteCallback(this.getSessionId()); + bool isFFmpeg() => false; bool isFFprobe() => true; + + bool isMediaInformation() => false; } diff --git a/flutter/flutter/lib/ffprobe_session_complete_callback.dart b/flutter/flutter/lib/ffprobe_session_complete_callback.dart new file mode 100644 index 0000000..cfdca17 --- /dev/null +++ b/flutter/flutter/lib/ffprobe_session_complete_callback.dart @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021 Taner Sener + * + * This file is part of FFmpegKit. + * + * FFmpegKit 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. + * + * FFmpegKit 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 FFmpegKit. If not, see . + */ + +import 'ffprobe_session.dart'; + +/// Callback function that is invoked when an asynchronous FFprobe session has +/// ended. Session has either SessionState.completed or SessionState.failed +/// state when the callback is invoked. +/// If it has SessionState.completed state, "ReturnCode" should be checked to +/// see the execution result. +/// If "getState" returns SessionState.failed then "getFailStackTrace" should +/// be used to get the failure reason. +typedef FFprobeSessionCompleteCallback = void Function(FFprobeSession session); diff --git a/flutter/flutter/lib/media_information.dart b/flutter/flutter/lib/media_information.dart index 44dba7e..83560f1 100644 --- a/flutter/flutter/lib/media_information.dart +++ b/flutter/flutter/lib/media_information.dart @@ -17,6 +17,7 @@ * along with FFmpegKit. If not, see . */ +import 'chapter.dart'; import 'stream_information.dart'; /// Media information class. @@ -87,6 +88,20 @@ class MediaInformation { return list; } + /// Returns all chapters found as a list. + List getChapters() { + final List list = List.empty(growable: true); + + dynamic createChapter(Map chapterProperties) => + list.add(new Chapter(chapterProperties)); + + this._allProperties?["chapters"]?.forEach((Object? chapter) { + createChapter(chapter as Map); + }); + + return list; + } + /// Returns all media properties. Map? getMediaProperties() => this._allProperties?[keyMediaProperties]; diff --git a/flutter/flutter/lib/media_information_session.dart b/flutter/flutter/lib/media_information_session.dart index 8d9b7bf..c550ca8 100644 --- a/flutter/flutter/lib/media_information_session.dart +++ b/flutter/flutter/lib/media_information_session.dart @@ -18,26 +18,26 @@ */ import 'abstract_session.dart'; -import 'execute_callback.dart'; -import 'ffprobe_session.dart'; import 'log_callback.dart'; import 'media_information.dart'; +import 'media_information_session_complete_callback.dart'; import 'src/ffmpeg_kit_factory.dart'; /// A custom FFprobe session, which produces a "MediaInformation" object /// using the FFprobe output. -class MediaInformationSession extends FFprobeSession { +class MediaInformationSession extends AbstractSession { MediaInformation? _mediaInformation; /// Creates a new MediaInformation session with [argumentsArray]. static Future create(List argumentsArray, - [ExecuteCallback? executeCallback = null, + [MediaInformationSessionCompleteCallback? completeCallback = null, LogCallback? logCallback = null]) async { final session = await AbstractSession.createMediaInformationSession(argumentsArray); final sessionId = session.getSessionId(); - FFmpegKitFactory.setExecuteCallback(sessionId, executeCallback); + FFmpegKitFactory.setMediaInformationSessionCompleteCallback( + sessionId, completeCallback); FFmpegKitFactory.setLogCallback(sessionId, logCallback); return session; @@ -55,4 +55,15 @@ class MediaInformationSession extends FFprobeSession { void setMediaInformation(MediaInformation? mediaInformation) { this._mediaInformation = mediaInformation; } + + /// Returns the session specific complete callback. + MediaInformationSessionCompleteCallback? getCompleteCallback() => + FFmpegKitFactory.getMediaInformationSessionCompleteCallback( + this.getSessionId()); + + bool isFFmpeg() => false; + + bool isFFprobe() => false; + + bool isMediaInformation() => true; } diff --git a/flutter/flutter/lib/media_information_session_complete_callback.dart b/flutter/flutter/lib/media_information_session_complete_callback.dart new file mode 100644 index 0000000..213900f --- /dev/null +++ b/flutter/flutter/lib/media_information_session_complete_callback.dart @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Taner Sener + * + * This file is part of FFmpegKit. + * + * FFmpegKit 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. + * + * FFmpegKit 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 FFmpegKit. If not, see . + */ + +import 'media_information_session.dart'; + +/// Callback function that is invoked when an asynchronous MediaInformation +/// session has ended. Session has either SessionState.completed or +/// SessionState.failed state when the callback is invoked. +/// If it has SessionState.completed state, "ReturnCode" should be checked to +/// see the execution result. +/// If "getState" returns SessionState.failed then "getFailStackTrace" should +/// be used to get the failure reason. +typedef MediaInformationSessionCompleteCallback = void Function( + MediaInformationSession session); diff --git a/flutter/flutter/lib/session.dart b/flutter/flutter/lib/session.dart index fe095e4..7fe16ae 100644 --- a/flutter/flutter/lib/session.dart +++ b/flutter/flutter/lib/session.dart @@ -17,7 +17,6 @@ * along with FFmpegKit. If not, see . */ -import 'execute_callback.dart'; import 'log.dart'; import 'log_callback.dart'; import 'log_redirection_strategy.dart'; @@ -26,10 +25,7 @@ import 'session_state.dart'; /// Common interface for all "FFmpegKit" sessions. abstract class Session { - /// Returns the session specific execute callback function. - ExecuteCallback? getExecuteCallback(); - - /// Returns the session specific log callback function. + /// Returns the session specific log callback. LogCallback? getLogCallback(); /// Returns the session identifier. @@ -106,6 +102,9 @@ abstract class Session { /// Returns whether it is an "FFprobe" session or not. bool isFFprobe(); + /// Returns whether it is an "MediaInformation" session or not. + bool isMediaInformation(); + /// Cancels running the session. void cancel(); } diff --git a/flutter/flutter/lib/src/ffmpeg_kit_factory.dart b/flutter/flutter/lib/src/ffmpeg_kit_factory.dart index 0a6aa1a..bb4529d 100644 --- a/flutter/flutter/lib/src/ffmpeg_kit_factory.dart +++ b/flutter/flutter/lib/src/ffmpeg_kit_factory.dart @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021 Taner Sener + * Copyright (c) 2019-2022 Taner Sener * * This file is part of FFmpegKit. * @@ -17,18 +17,26 @@ * along with FFmpegKit. If not, see . */ -import '../execute_callback.dart'; import '../ffmpeg_session.dart'; +import '../ffmpeg_session_complete_callback.dart'; import '../ffprobe_session.dart'; +import '../ffprobe_session_complete_callback.dart'; import '../log.dart'; import '../log_callback.dart'; import '../log_redirection_strategy.dart'; +import '../media_information.dart'; import '../media_information_session.dart'; +import '../media_information_session_complete_callback.dart'; import '../session.dart'; import '../statistics.dart'; import '../statistics_callback.dart'; -final executeCallbackMap = new Map(); +final ffmpegSessionCompleteCallbackMap = + new Map(); +final ffprobeSessionCompleteCallbackMap = + new Map(); +final mediaInformationSessionCompleteCallbackMap = + new Map(); final logCallbackMap = new Map(); final statisticsCallbackMap = new Map(); final logRedirectionStrategyMap = new Map(); @@ -36,7 +44,10 @@ final logRedirectionStrategyMap = new Map(); class FFmpegKitFactory { static LogCallback? _logCallback; static StatisticsCallback? _statisticsCallback; - static ExecuteCallback? _executeCallback; + static FFmpegSessionCompleteCallback? _ffmpegSessionCompleteCallback; + static FFprobeSessionCompleteCallback? _ffprobeSessionCompleteCallback; + static MediaInformationSessionCompleteCallback? + _mediaInformationSessionCompleteCallback; static Statistics mapToStatistics(Map statisticsMap) => new Statistics( @@ -80,7 +91,16 @@ class FFmpegKitFactory { } } - static String getVersion() => "4.5.0"; + static MediaInformation? mapToNullableMediaInformation( + Map? mediaInformationMap) { + if (mediaInformationMap != null) { + return new MediaInformation(mediaInformationMap); + } else { + return null; + } + } + + static String getVersion() => "4.5.1"; static LogRedirectionStrategy? getLogRedirectionStrategy(int? sessionId) => logRedirectionStrategyMap[sessionId]; @@ -125,20 +145,64 @@ class FFmpegKitFactory { _statisticsCallback = statisticsCallback; } - static ExecuteCallback? getExecuteCallback(int? sessionId) => - executeCallbackMap[sessionId]; + static FFmpegSessionCompleteCallback? getFFmpegSessionCompleteCallback( + int? sessionId) => + ffmpegSessionCompleteCallbackMap[sessionId]; - static void setExecuteCallback( - int? sessionId, ExecuteCallback? executeCallback) { - if (sessionId != null && executeCallback != null) { - executeCallbackMap[sessionId] = executeCallback; + static void setFFmpegSessionCompleteCallback( + int? sessionId, FFmpegSessionCompleteCallback? completeCallback) { + if (sessionId != null && completeCallback != null) { + ffmpegSessionCompleteCallbackMap[sessionId] = completeCallback; } } - static ExecuteCallback? getGlobalExecuteCallback() => _executeCallback; + static FFmpegSessionCompleteCallback? + getGlobalFFmpegSessionCompleteCallback() => + _ffmpegSessionCompleteCallback; - static void setGlobalExecuteCallback(ExecuteCallback? executeCallback) { - _executeCallback = executeCallback; + static void setGlobalFFmpegSessionCompleteCallback( + FFmpegSessionCompleteCallback? completeCallback) { + _ffmpegSessionCompleteCallback = completeCallback; + } + + static FFprobeSessionCompleteCallback? getFFprobeSessionCompleteCallback( + int? sessionId) => + ffprobeSessionCompleteCallbackMap[sessionId]; + + static void setFFprobeSessionCompleteCallback( + int? sessionId, FFprobeSessionCompleteCallback? completeCallback) { + if (sessionId != null && completeCallback != null) { + ffprobeSessionCompleteCallbackMap[sessionId] = completeCallback; + } + } + + static FFprobeSessionCompleteCallback? + getGlobalFFprobeSessionCompleteCallback() => + _ffprobeSessionCompleteCallback; + + static void setGlobalFFprobeSessionCompleteCallback( + FFprobeSessionCompleteCallback? completeCallback) { + _ffprobeSessionCompleteCallback = completeCallback; + } + + static MediaInformationSessionCompleteCallback? + getMediaInformationSessionCompleteCallback(int? sessionId) => + mediaInformationSessionCompleteCallbackMap[sessionId]; + + static void setMediaInformationSessionCompleteCallback(int? sessionId, + MediaInformationSessionCompleteCallback? completeCallback) { + if (sessionId != null && completeCallback != null) { + mediaInformationSessionCompleteCallbackMap[sessionId] = completeCallback; + } + } + + static MediaInformationSessionCompleteCallback? + getGlobalMediaInformationSessionCompleteCallback() => + _mediaInformationSessionCompleteCallback; + + static void setGlobalMediaInformationSessionCompleteCallback( + MediaInformationSessionCompleteCallback? completeCallback) { + _mediaInformationSessionCompleteCallback = completeCallback; } static DateTime? validDate(int? time) { diff --git a/flutter/flutter/lib/src/ffmpeg_kit_flutter_initializer.dart b/flutter/flutter/lib/src/ffmpeg_kit_flutter_initializer.dart index 0c9c455..6c54e21 100644 --- a/flutter/flutter/lib/src/ffmpeg_kit_flutter_initializer.dart +++ b/flutter/flutter/lib/src/ffmpeg_kit_flutter_initializer.dart @@ -23,12 +23,16 @@ import 'package:ffmpeg_kit_flutter_platform_interface/ffmpeg_kit_flutter_platfor import 'package:flutter/services.dart'; import '../arch_detect.dart'; -import '../execute_callback.dart'; import '../ffmpeg_kit_config.dart'; import '../ffmpeg_session.dart'; +import '../ffmpeg_session_complete_callback.dart'; +import '../ffprobe_session.dart'; +import '../ffprobe_session_complete_callback.dart'; import '../level.dart'; import '../log_callback.dart'; import '../log_redirection_strategy.dart'; +import '../media_information_session.dart'; +import '../media_information_session_complete_callback.dart'; import '../packages.dart'; import '../session.dart'; import '../statistics.dart'; @@ -59,8 +63,8 @@ class FFmpegKitInitializer { eventMap['FFmpegKitLogCallbackEvent']; final Map? statisticsEvent = eventMap['FFmpegKitStatisticsCallbackEvent']; - final Map? executeEvent = - eventMap['FFmpegKitExecuteCallbackEvent']; + final Map? completeEvent = + eventMap['FFmpegKitCompleteCallbackEvent']; if (logEvent != null) { _processLogCallbackEvent(logEvent); @@ -70,8 +74,8 @@ class FFmpegKitInitializer { _processStatisticsCallbackEvent(statisticsEvent); } - if (executeEvent != null) { - _processExecuteCallbackEvent(executeEvent); + if (completeEvent != null) { + _processCompleteCallbackEvent(completeEvent); } } } @@ -98,144 +102,195 @@ class FFmpegKitInitializer { return; } - FFmpegKitConfig.getSession(sessionId).then((Session? session) { - activeLogRedirectionStrategy = - session?.getLogRedirectionStrategy() ?? activeLogRedirectionStrategy; - final LogCallback? logCallback = session?.getLogCallback(); + activeLogRedirectionStrategy = + FFmpegKitFactory.getLogRedirectionStrategy(sessionId) ?? + activeLogRedirectionStrategy; + final LogCallback? logCallback = FFmpegKitFactory.getLogCallback(sessionId); - if (logCallback != null) { - sessionCallbackDefined = true; + if (logCallback != null) { + sessionCallbackDefined = true; - try { - // NOTIFY SESSION CALLBACK DEFINED - logCallback(log); - } on Exception catch (e, stack) { - print("Exception thrown inside session LogCallback block. $e"); - print(stack); - } + try { + // NOTIFY SESSION CALLBACK DEFINED + logCallback(log); + } on Exception catch (e, stack) { + print("Exception thrown inside session log callback. $e"); + print(stack); } + } - final globalLogCallbackFunction = FFmpegKitFactory.getGlobalLogCallback(); - if (globalLogCallbackFunction != null) { - globalCallbackDefined = true; + final globalLogCallbackFunction = FFmpegKitFactory.getGlobalLogCallback(); + if (globalLogCallbackFunction != null) { + globalCallbackDefined = true; - try { - // NOTIFY GLOBAL CALLBACK DEFINED - globalLogCallbackFunction(log); - } on Exception catch (e, stack) { - print("Exception thrown inside global LogCallback block. $e"); - print(stack); - } + try { + // NOTIFY GLOBAL CALLBACK DEFINED + globalLogCallbackFunction(log); + } on Exception catch (e, stack) { + print("Exception thrown inside global log callback. $e"); + print(stack); } + } - // EXECUTE THE LOG STRATEGY - switch (activeLogRedirectionStrategy) { - case LogRedirectionStrategy.neverPrintLogs: - { + // EXECUTE THE LOG STRATEGY + switch (activeLogRedirectionStrategy) { + case LogRedirectionStrategy.neverPrintLogs: + { + return; + } + case LogRedirectionStrategy.printLogsWhenGlobalCallbackNotDefined: + { + if (globalCallbackDefined) { return; } - case LogRedirectionStrategy.printLogsWhenGlobalCallbackNotDefined: - { - if (globalCallbackDefined) { - return; - } + } + break; + case LogRedirectionStrategy.printLogsWhenSessionCallbackNotDefined: + { + if (sessionCallbackDefined) { + return; } - break; - case LogRedirectionStrategy.printLogsWhenSessionCallbackNotDefined: - { - if (sessionCallbackDefined) { - return; - } + } + break; + case LogRedirectionStrategy.printLogsWhenNoCallbacksDefined: + { + if (globalCallbackDefined || sessionCallbackDefined) { + return; } - break; - case LogRedirectionStrategy.printLogsWhenNoCallbacksDefined: - { - if (globalCallbackDefined || sessionCallbackDefined) { - return; - } - } - break; - case LogRedirectionStrategy.alwaysPrintLogs: - {} - break; - } + } + break; + case LogRedirectionStrategy.alwaysPrintLogs: + {} + break; + } - // PRINT LOGS - switch (level) { - case Level.avLogQuiet: - { - // PRINT NO OUTPUT - } - break; - default: - { - stdout.write(text); - } - } - }); + // PRINT LOGS + switch (level) { + case Level.avLogQuiet: + { + // PRINT NO OUTPUT + } + break; + default: + { + stdout.write(text); + } + } } void _processStatisticsCallbackEvent(Map event) { final Statistics statistics = FFmpegKitFactory.mapToStatistics(event); final int sessionId = event["sessionId"]; - FFmpegKitConfig.getSession(sessionId).then((Session? session) { - if (session != null && session.isFFmpeg()) { - final FFmpegSession ffmpegSession = session as FFmpegSession; - final StatisticsCallback? statisticsCallback = - ffmpegSession.getStatisticsCallback(); - - if (statisticsCallback != null) { - try { - // NOTIFY SESSION CALLBACK DEFINED - statisticsCallback(statistics); - } on Exception catch (e, stack) { - print( - "Exception thrown inside session StatisticsCallback block. $e"); - print(stack); - } - } + final StatisticsCallback? statisticsCallback = + FFmpegKitFactory.getStatisticsCallback(sessionId); + if (statisticsCallback != null) { + try { + // NOTIFY SESSION CALLBACK DEFINED + statisticsCallback(statistics); + } on Exception catch (e, stack) { + print("Exception thrown inside session statistics callback. $e"); + print(stack); } + } - final globalStatisticsCallbackFunction = - FFmpegKitFactory.getGlobalStatisticsCallback(); - if (globalStatisticsCallbackFunction != null) { - try { - // NOTIFY GLOBAL CALLBACK DEFINED - globalStatisticsCallbackFunction(statistics); - } on Exception catch (e, stack) { - print("Exception thrown inside global StatisticsCallback block. $e"); - print(stack); - } + final globalStatisticsCallbackFunction = + FFmpegKitFactory.getGlobalStatisticsCallback(); + if (globalStatisticsCallbackFunction != null) { + try { + // NOTIFY GLOBAL CALLBACK DEFINED + globalStatisticsCallbackFunction(statistics); + } on Exception catch (e, stack) { + print("Exception thrown inside global statistics callback. $e"); + print(stack); } - }); + } } - void _processExecuteCallbackEvent(Map event) { + void _processCompleteCallbackEvent(Map event) { final int sessionId = event["sessionId"]; FFmpegKitConfig.getSession(sessionId).then((Session? session) { - final ExecuteCallback? executeCallback = session?.getExecuteCallback(); + if (session != null) { + if (session.isFFmpeg()) { + final ffmpegSession = session as FFmpegSession; + final FFmpegSessionCompleteCallback? completeCallback = + ffmpegSession.getCompleteCallback(); - if (executeCallback != null && session != null) { - try { - // NOTIFY SESSION CALLBACK DEFINED - executeCallback(session); - } on Exception catch (e, stack) { - print("Exception thrown inside session ExecuteCallback block. $e"); - print(stack); - } - } + if (completeCallback != null) { + try { + // NOTIFY SESSION CALLBACK DEFINED + completeCallback(ffmpegSession); + } on Exception catch (e, stack) { + print("Exception thrown inside session complete callback. $e"); + print(stack); + } + } - final globalExecuteCallbackFunction = - FFmpegKitFactory.getGlobalExecuteCallback(); - if (globalExecuteCallbackFunction != null && session != null) { - try { - // NOTIFY GLOBAL CALLBACK DEFINED - globalExecuteCallbackFunction(session); - } on Exception catch (e, stack) { - print("Exception thrown inside global ExecuteCallback block. $e"); - print(stack); + final globalFFmpegSessionCompleteCallback = + FFmpegKitFactory.getGlobalFFmpegSessionCompleteCallback(); + if (globalFFmpegSessionCompleteCallback != null) { + try { + // NOTIFY GLOBAL CALLBACK DEFINED + globalFFmpegSessionCompleteCallback(ffmpegSession); + } on Exception catch (e, stack) { + print("Exception thrown inside global complete callback. $e"); + print(stack); + } + } + } else if (session.isFFprobe()) { + final ffprobeSession = session as FFprobeSession; + final FFprobeSessionCompleteCallback? completeCallback = + ffprobeSession.getCompleteCallback(); + + if (completeCallback != null) { + try { + // NOTIFY SESSION CALLBACK DEFINED + completeCallback(ffprobeSession); + } on Exception catch (e, stack) { + print("Exception thrown inside session complete callback. $e"); + print(stack); + } + } + + final globalFFprobeSessionCompleteCallback = + FFmpegKitFactory.getGlobalFFprobeSessionCompleteCallback(); + if (globalFFprobeSessionCompleteCallback != null) { + try { + // NOTIFY GLOBAL CALLBACK DEFINED + globalFFprobeSessionCompleteCallback(ffprobeSession); + } on Exception catch (e, stack) { + print("Exception thrown inside global complete callback. $e"); + print(stack); + } + } + } else if (session.isMediaInformation()) { + final mediaInformationSession = session as MediaInformationSession; + final MediaInformationSessionCompleteCallback? completeCallback = + mediaInformationSession.getCompleteCallback(); + + if (completeCallback != null) { + try { + // NOTIFY SESSION CALLBACK DEFINED + completeCallback(mediaInformationSession); + } on Exception catch (e, stack) { + print("Exception thrown inside session complete callback. $e"); + print(stack); + } + } + + final globalMediaInformationSessionCompleteCallback = FFmpegKitFactory + .getGlobalMediaInformationSessionCompleteCallback(); + if (globalMediaInformationSessionCompleteCallback != null) { + try { + // NOTIFY GLOBAL CALLBACK DEFINED + globalMediaInformationSessionCompleteCallback( + mediaInformationSession); + } on Exception catch (e, stack) { + print("Exception thrown inside global complete callback. $e"); + print(stack); + } + } } } }); diff --git a/flutter/flutter/lib/statistics_callback.dart b/flutter/flutter/lib/statistics_callback.dart index 9b0a01c..de988a6 100644 --- a/flutter/flutter/lib/statistics_callback.dart +++ b/flutter/flutter/lib/statistics_callback.dart @@ -19,5 +19,5 @@ import 'statistics.dart'; -/// Callback function that receives statistics generated for "FFmpeg" sessions.dc +/// Callback function that receives statistics generated for "FFmpeg" sessions. typedef StatisticsCallback = void Function(Statistics statistics); diff --git a/flutter/flutter/lib/stream_information.dart b/flutter/flutter/lib/stream_information.dart index 3785a17..eb546b6 100644 --- a/flutter/flutter/lib/stream_information.dart +++ b/flutter/flutter/lib/stream_information.dart @@ -119,6 +119,6 @@ class StreamInformation { /// Returns the stream properties associated with the key. dynamic getProperties(String key) => this._allProperties?[key]; - /// Returns all properties found.d + /// Returns all properties found. Map? getAllProperties() => this._allProperties; } diff --git a/flutter/flutter/macos/Classes/FFmpegKitFlutterPlugin.m b/flutter/flutter/macos/Classes/FFmpegKitFlutterPlugin.m index c0a4b7a..f9f557e 100644 --- a/flutter/flutter/macos/Classes/FFmpegKitFlutterPlugin.m +++ b/flutter/flutter/macos/Classes/FFmpegKitFlutterPlugin.m @@ -57,7 +57,7 @@ static int const SESSION_TYPE_MEDIA_INFORMATION = 3; // EVENTS static NSString *const EVENT_LOG_CALLBACK_EVENT = @"FFmpegKitLogCallbackEvent"; static NSString *const EVENT_STATISTICS_CALLBACK_EVENT = @"FFmpegKitStatisticsCallbackEvent"; -static NSString *const EVENT_EXECUTE_CALLBACK_EVENT = @"FFmpegKitExecuteCallbackEvent"; +static NSString *const EVENT_COMPLETE_CALLBACK_EVENT = @"FFmpegKitCompleteCallbackEvent"; // ARGUMENT NAMES static NSString *const ARGUMENT_SESSION_ID = @"sessionId"; @@ -71,7 +71,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; FlutterEventSink _eventSink; BOOL logsEnabled; BOOL statisticsEnabled; - dispatch_queue_t asyncWriteToPipeDispatchQueue; + dispatch_queue_t asyncDispatchQueue; } - (instancetype)init { @@ -79,9 +79,9 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; if (self) { logsEnabled = false; statisticsEnabled = false; - asyncWriteToPipeDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + asyncDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - [self registerGlobalCallbacks]; + NSLog(@"FFmpegKitFlutterPlugin %p created.\n", self); } return self; @@ -89,6 +89,8 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; - (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { _eventSink = eventSink; + NSLog(@"FFmpegKitFlutterPlugin %p started listening to events on %p.\n", self, eventSink); + [self registerGlobalCallbacks]; return nil; } @@ -108,9 +110,19 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; } - (void)registerGlobalCallbacks { - [FFmpegKitConfig enableExecuteCallback:^(id session){ + [FFmpegKitConfig enableFFmpegSessionCompleteCallback:^(FFmpegSession* session){ NSDictionary *dictionary = [FFmpegKitFlutterPlugin toSessionDictionary:session]; - self->_eventSink([FFmpegKitFlutterPlugin toStringDictionary:EVENT_EXECUTE_CALLBACK_EVENT withDictionary:dictionary]); + self->_eventSink([FFmpegKitFlutterPlugin toStringDictionary:EVENT_COMPLETE_CALLBACK_EVENT withDictionary:dictionary]); + }]; + + [FFmpegKitConfig enableFFprobeSessionCompleteCallback:^(FFprobeSession* session){ + NSDictionary *dictionary = [FFmpegKitFlutterPlugin toSessionDictionary:session]; + self->_eventSink([FFmpegKitFlutterPlugin toStringDictionary:EVENT_COMPLETE_CALLBACK_EVENT withDictionary:dictionary]); + }]; + + [FFmpegKitConfig enableMediaInformationSessionCompleteCallback:^(MediaInformationSession* session){ + NSDictionary *dictionary = [FFmpegKitFlutterPlugin toSessionDictionary:session]; + self->_eventSink([FFmpegKitFlutterPlugin toStringDictionary:EVENT_COMPLETE_CALLBACK_EVENT withDictionary:dictionary]); }]; [FFmpegKitConfig enableLogCallback: ^(Log* log){ @@ -220,6 +232,12 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; } else { result([FlutterError errorWithCode:@"INVALID_ARGUMENTS" message:@"Invalid arguments array." details:nil]); } + } else if ([@"getMediaInformation" isEqualToString:call.method]) { + if (sessionId != nil) { + [self getMediaInformation:sessionId result:result]; + } else { + result([FlutterError errorWithCode:@"INVALID_SESSION" message:@"Invalid session id." details:nil]); + } } else if ([@"mediaInformationJsonParserFrom" isEqualToString:call.method]) { if (ffprobeJsonOutput != nil) { [self mediaInformationJsonParserFrom:ffprobeJsonOutput result:result]; @@ -299,6 +317,24 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; } else { result([FlutterError errorWithCode:@"INVALID_SIGNAL" message:@"Invalid signal value." details:nil]); } + } else if ([@"ffmpegSessionExecute" isEqualToString:call.method]) { + if (sessionId != nil) { + [self ffmpegSessionExecute:sessionId result:result]; + } else { + result([FlutterError errorWithCode:@"INVALID_SESSION" message:@"Invalid session id." details:nil]); + } + } else if ([@"ffprobeSessionExecute" isEqualToString:call.method]) { + if (sessionId != nil) { + [self ffprobeSessionExecute:sessionId result:result]; + } else { + result([FlutterError errorWithCode:@"INVALID_SESSION" message:@"Invalid session id." details:nil]); + } + } else if ([@"mediaInformationSessionExecute" isEqualToString:call.method]) { + if (sessionId != nil) { + [self mediaInformationSessionExecute:sessionId timeout:waitTimeout result:result]; + } else { + result([FlutterError errorWithCode:@"INVALID_SESSION" message:@"Invalid session id." details:nil]); + } } else if ([@"asyncFFmpegSessionExecute" isEqualToString:call.method]) { if (sessionId != nil) { [self asyncFFmpegSessionExecute:sessionId result:result]; @@ -399,6 +435,8 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; [self getFFmpegSessions:result]; } else if ([@"getFFprobeSessions" isEqualToString:call.method]) { [self getFFprobeSessions:result]; + } else if ([@"getMediaInformationSessions" isEqualToString:call.method]) { + [self getMediaInformationSessions:result]; } else if ([@"getPackageName" isEqualToString:call.method]) { [self getPackageName:result]; } else if ([@"getExternalLibraries" isEqualToString:call.method]) { @@ -525,7 +563,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; // FFmpegSession - (void)ffmpegSession:(NSArray*)arguments result:(FlutterResult)result { - FFmpegSession* session = [[FFmpegSession alloc] init:arguments withExecuteCallback:nil withLogCallback:nil withStatisticsCallback:nil withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs]; + FFmpegSession* session = [[FFmpegSession alloc] init:arguments withCompleteCallback:nil withLogCallback:nil withStatisticsCallback:nil withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs]; result([FFmpegKitFlutterPlugin toSessionDictionary:session]); } @@ -534,7 +572,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; if (session == nil) { result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); } else { - if ([session isMemberOfClass:[FFmpegSession class]]) { + if ([session isFFmpeg]) { int timeout; if ([FFmpegKitFlutterPlugin isValidPositiveNumber:waitTimeout]) { timeout = [waitTimeout intValue]; @@ -554,7 +592,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; if (session == nil) { result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); } else { - if ([session isMemberOfClass:[FFmpegSession class]]) { + if ([session isFFmpeg]) { NSArray* statistics = [(FFmpegSession*)session getStatistics]; result([FFmpegKitFlutterPlugin toStatisticsArray:statistics]); } else { @@ -566,17 +604,31 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; // FFprobeSession - (void)ffprobeSession:(NSArray*)arguments result:(FlutterResult)result { - FFprobeSession* session = [[FFprobeSession alloc] init:arguments withExecuteCallback:nil withLogCallback:nil withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs]; + FFprobeSession* session = [[FFprobeSession alloc] init:arguments withCompleteCallback:nil withLogCallback:nil withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs]; result([FFmpegKitFlutterPlugin toSessionDictionary:session]); } // MediaInformationSession - (void)mediaInformationSession:(NSArray*)arguments result:(FlutterResult)result { - MediaInformationSession* session = [[MediaInformationSession alloc] init:arguments withExecuteCallback:nil withLogCallback:nil]; + MediaInformationSession* session = [[MediaInformationSession alloc] init:arguments withCompleteCallback:nil withLogCallback:nil]; result([FFmpegKitFlutterPlugin toSessionDictionary:session]); } +- (void)getMediaInformation:(NSNumber*)sessionId result:(FlutterResult)result { + AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:[sessionId longValue]]; + if (session == nil) { + result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); + } else { + if ([session isMediaInformation]) { + MediaInformationSession *mediaInformationSession = (MediaInformationSession*)session; + result([FFmpegKitFlutterPlugin toMediaInformationDictionary:[mediaInformationSession getMediaInformation]]); + } else { + result([FlutterError errorWithCode:@"NOT_MEDIA_INFORMATION_SESSION" message:@"A session is found but it does not have the correct type." details:nil]); + } + } +} + // MediaInformationJsonParser - (void)mediaInformationJsonParserFrom:(NSString*)ffprobeJsonOutput result:(FlutterResult)result { @@ -708,12 +760,66 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; } } +- (void)ffmpegSessionExecute:(NSNumber*)sessionId result:(FlutterResult)result { + AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:[sessionId longValue]]; + if (session == nil) { + result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); + } else { + if ([session isFFmpeg]) { + dispatch_async(asyncDispatchQueue, ^{ + [FFmpegKitConfig ffmpegExecute:(FFmpegSession*)session]; + result(nil); + }); + } else { + result([FlutterError errorWithCode:@"NOT_FFMPEG_SESSION" message:@"A session is found but it does not have the correct type." details:nil]); + } + } +} + +- (void)ffprobeSessionExecute:(NSNumber*)sessionId result:(FlutterResult)result { + AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:[sessionId longValue]]; + if (session == nil) { + result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); + } else { + if ([session isFFprobe]) { + dispatch_async(asyncDispatchQueue, ^{ + [FFmpegKitConfig ffprobeExecute:(FFprobeSession*)session]; + result(nil); + }); + } else { + result([FlutterError errorWithCode:@"NOT_FFPROBE_SESSION" message:@"A session is found but it does not have the correct type." details:nil]); + } + } +} + +- (void)mediaInformationSessionExecute:(NSNumber*)sessionId timeout:(NSNumber*)waitTimeout result:(FlutterResult)result { + AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:[sessionId longValue]]; + if (session == nil) { + result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); + } else { + if ([session isMediaInformation]) { + int timeout; + if ([FFmpegKitFlutterPlugin isValidPositiveNumber:waitTimeout]) { + timeout = [waitTimeout intValue]; + } else { + timeout = AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; + } + dispatch_async(asyncDispatchQueue, ^{ + [FFmpegKitConfig getMediaInformationExecute:(MediaInformationSession*)session withTimeout:timeout]; + result(nil); + }); + } else { + result([FlutterError errorWithCode:@"NOT_MEDIA_INFORMATION_SESSION" message:@"A session is found but it does not have the correct type." details:nil]); + } + } +} + - (void)asyncFFmpegSessionExecute:(NSNumber*)sessionId result:(FlutterResult)result { AbstractSession* session = (AbstractSession*)[FFmpegKitConfig getSession:[sessionId longValue]]; if (session == nil) { result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); } else { - if ([session isMemberOfClass:[FFmpegSession class]]) { + if ([session isFFmpeg]) { [FFmpegKitConfig asyncFFmpegExecute:(FFmpegSession*)session]; result(nil); } else { @@ -727,7 +833,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; if (session == nil) { result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); } else { - if ([session isMemberOfClass:[FFprobeSession class]]) { + if ([session isFFprobe]) { [FFmpegKitConfig asyncFFprobeExecute:(FFprobeSession*)session]; result(nil); } else { @@ -741,7 +847,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; if (session == nil) { result([FlutterError errorWithCode:@"SESSION_NOT_FOUND" message:@"Session not found." details:nil]); } else { - if ([session isMemberOfClass:[MediaInformationSession class]]) { + if ([session isMediaInformation]) { int timeout; if ([FFmpegKitFlutterPlugin isValidPositiveNumber:waitTimeout]) { timeout = [waitTimeout intValue]; @@ -852,7 +958,7 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; } - (void)writeToPipe:(NSString*)inputPath pipe:(NSString*)namedPipePath result:(FlutterResult)result { - dispatch_async(asyncWriteToPipeDispatchQueue, ^{ + dispatch_async(asyncDispatchQueue, ^{ NSLog(@"Starting copy %@ to pipe %@ operation.\n", inputPath, namedPipePath); @@ -933,7 +1039,11 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; // FFprobeKit - (void)getFFprobeSessions:(FlutterResult)result { - result([FFmpegKitFlutterPlugin toSessionArray:[FFprobeKit listSessions]]); + result([FFmpegKitFlutterPlugin toSessionArray:[FFprobeKit listFFprobeSessions]]); +} + +- (void)getMediaInformationSessions:(FlutterResult)result { + result([FFmpegKitFlutterPlugin toSessionArray:[FFprobeKit listMediaInformationSessions]]); } // Packages @@ -971,16 +1081,14 @@ extern int const AbstractSessionDefaultTimeoutForAsynchronousMessagesInTransmit; dictionary[KEY_SESSION_START_TIME] = [NSNumber numberWithLong:[[session getStartTime] timeIntervalSince1970]*1000]; dictionary[KEY_SESSION_COMMAND] = [session getCommand]; - if ([session isFFprobe]) { - if ([(AbstractSession*)session isMemberOfClass:[MediaInformationSession class]]) { - MediaInformationSession *mediaInformationSession = (MediaInformationSession*)session; - dictionary[KEY_SESSION_MEDIA_INFORMATION] = [FFmpegKitFlutterPlugin toMediaInformationDictionary:[mediaInformationSession getMediaInformation]]; - dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_MEDIA_INFORMATION]; - } else { - dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_FFPROBE]; - } - } else { + if ([session isFFmpeg]) { dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_FFMPEG]; + } else if ([session isFFprobe]) { + dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_FFPROBE]; + } else if ([session isMediaInformation]) { + MediaInformationSession *mediaInformationSession = (MediaInformationSession*)session; + dictionary[KEY_SESSION_MEDIA_INFORMATION] = [FFmpegKitFlutterPlugin toMediaInformationDictionary:[mediaInformationSession getMediaInformation]]; + dictionary[KEY_SESSION_TYPE] = [NSNumber numberWithInt:SESSION_TYPE_MEDIA_INFORMATION]; } return dictionary; diff --git a/flutter/flutter/macos/ffmpeg_kit_flutter.podspec b/flutter/flutter/macos/ffmpeg_kit_flutter.podspec index 8ab6806..2b970c0 100644 --- a/flutter/flutter/macos/ffmpeg_kit_flutter.podspec +++ b/flutter/flutter/macos/ffmpeg_kit_flutter.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'ffmpeg_kit_flutter' - s.version = '4.5.0' + s.version = '4.5.1' s.summary = 'FFmpeg Kit for Flutter' s.description = 'A Flutter plugin for running FFmpeg and FFprobe commands.' s.homepage = 'https://github.com/tanersener/ffmpeg-kit' @@ -23,113 +23,113 @@ Pod::Spec.new do |s| s.subspec 'min' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-min', "4.5" + ss.dependency 'ffmpeg-kit-macos-min', "4.5.1" ss.osx.deployment_target = '10.15' end s.subspec 'min-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-min', "4.5.LTS" - ss.osx.deployment_target = '10.11' + ss.dependency 'ffmpeg-kit-macos-min', "4.5.1.LTS" + ss.osx.deployment_target = '10.12' end s.subspec 'min-gpl' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-min-gpl', "4.5" + ss.dependency 'ffmpeg-kit-macos-min-gpl', "4.5.1" ss.osx.deployment_target = '10.15' end s.subspec 'min-gpl-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-min-gpl', "4.5.LTS" - ss.osx.deployment_target = '10.11' + ss.dependency 'ffmpeg-kit-macos-min-gpl', "4.5.1.LTS" + ss.osx.deployment_target = '10.12' end s.subspec 'https' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-https', "4.5" + ss.dependency 'ffmpeg-kit-macos-https', "4.5.1" ss.osx.deployment_target = '10.15' end s.subspec 'https-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-https', "4.5.LTS" - ss.osx.deployment_target = '10.11' + ss.dependency 'ffmpeg-kit-macos-https', "4.5.1.LTS" + ss.osx.deployment_target = '10.12' end s.subspec 'https-gpl' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-https-gpl', "4.5" + ss.dependency 'ffmpeg-kit-macos-https-gpl', "4.5.1" ss.osx.deployment_target = '10.15' end s.subspec 'https-gpl-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-https-gpl', "4.5.LTS" - ss.osx.deployment_target = '10.11' + ss.dependency 'ffmpeg-kit-macos-https-gpl', "4.5.1.LTS" + ss.osx.deployment_target = '10.12' end s.subspec 'audio' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-audio', "4.5" + ss.dependency 'ffmpeg-kit-macos-audio', "4.5.1" ss.osx.deployment_target = '10.15' end s.subspec 'audio-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-audio', "4.5.LTS" - ss.osx.deployment_target = '10.11' + ss.dependency 'ffmpeg-kit-macos-audio', "4.5.1.LTS" + ss.osx.deployment_target = '10.12' end s.subspec 'video' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-video', "4.5" + ss.dependency 'ffmpeg-kit-macos-video', "4.5.1" ss.osx.deployment_target = '10.15' end s.subspec 'video-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-video', "4.5.LTS" - ss.osx.deployment_target = '10.11' + ss.dependency 'ffmpeg-kit-macos-video', "4.5.1.LTS" + ss.osx.deployment_target = '10.12' end s.subspec 'full' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-full', "4.5" + ss.dependency 'ffmpeg-kit-macos-full', "4.5.1" ss.osx.deployment_target = '10.15' end s.subspec 'full-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-full', "4.5.LTS" - ss.osx.deployment_target = '10.11' + ss.dependency 'ffmpeg-kit-macos-full', "4.5.1.LTS" + ss.osx.deployment_target = '10.12' end s.subspec 'full-gpl' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-full-gpl', "4.5" + ss.dependency 'ffmpeg-kit-macos-full-gpl', "4.5.1" ss.osx.deployment_target = '10.15' end s.subspec 'full-gpl-lts' do |ss| ss.source_files = 'Classes/**/*' ss.public_header_files = 'Classes/**/*.h' - ss.dependency 'ffmpeg-kit-macos-full-gpl', "4.5.LTS" - ss.osx.deployment_target = '10.11' + ss.dependency 'ffmpeg-kit-macos-full-gpl', "4.5.1.LTS" + ss.osx.deployment_target = '10.12' end end diff --git a/flutter/flutter/pubspec.yaml b/flutter/flutter/pubspec.yaml index 99a5b42..203d365 100644 --- a/flutter/flutter/pubspec.yaml +++ b/flutter/flutter/pubspec.yaml @@ -3,7 +3,7 @@ description: FFmpeg Kit for Flutter. Supports Android, iOS and macOS platforms. repository: https://github.com/tanersener/ffmpeg-kit issue_tracker: https://github.com/tanersener/ffmpeg-kit/issues homepage: https://github.com/tanersener/ffmpeg-kit -version: 4.5.0 +version: 4.5.1 environment: sdk: ">=2.12.0 <3.0.0" @@ -21,7 +21,7 @@ flutter: pluginClass: FFmpegKitFlutterPlugin dependencies: - ffmpeg_kit_flutter_platform_interface: ^0.1.0 + ffmpeg_kit_flutter_platform_interface: ^0.2.1 flutter: sdk: flutter diff --git a/flutter/flutter_platform_interface/CHANGELOG.md b/flutter/flutter_platform_interface/CHANGELOG.md index 83b74a2..d88998e 100644 --- a/flutter/flutter_platform_interface/CHANGELOG.md +++ b/flutter/flutter_platform_interface/CHANGELOG.md @@ -1,2 +1,11 @@ +## 0.2.1 +- Fixes the signature of getMediaInformation method + +## 0.2.0 +- Implements execute methods +- Merges existing getSafParameter methods into a single method with a new openMode parameter +- Adds list media information sessions methods to FFprobeKit +- Adds getMediaInformation method + ## 0.1.0 - Initial release diff --git a/flutter/flutter_platform_interface/lib/ffmpeg_kit_flutter_platform_interface.dart b/flutter/flutter_platform_interface/lib/ffmpeg_kit_flutter_platform_interface.dart index 82f50ea..779dcff 100644 --- a/flutter/flutter_platform_interface/lib/ffmpeg_kit_flutter_platform_interface.dart +++ b/flutter/flutter_platform_interface/lib/ffmpeg_kit_flutter_platform_interface.dart @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Taner Sener + * Copyright (c) 2021-2022 Taner Sener * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -218,6 +218,22 @@ abstract class FFmpegKitPlatform extends PlatformInterface { ' has not been implemented!'); } + Future ffmpegKitConfigFFmpegExecute(int? sessionId) async { + throw UnimplementedError( + 'ffmpegKitConfigFFmpegExecute() has not been implemented!'); + } + + Future ffmpegKitConfigFFprobeExecute(int? sessionId) async { + throw UnimplementedError( + 'ffmpegKitConfigFFprobeExecute() has not been implemented!'); + } + + Future ffmpegKitConfigGetMediaInformationExecute( + int? sessionId, int? waitTimeout) async { + throw UnimplementedError('ffmpegKitConfigGetMediaInformationExecute()' + ' has not been implemented!'); + } + Future ffmpegKitConfigSetLogLevel(int logLevel) async { throw UnimplementedError( 'ffmpegKitConfigSetLogLevel() has not been implemented!'); @@ -314,16 +330,10 @@ abstract class FFmpegKitPlatform extends PlatformInterface { 'ffmpegKitConfigSelectDocumentForWrite() has not been implemented!'); } - Future ffmpegKitConfigGetSafParameterForRead( - String uriString) async { + Future ffmpegKitConfigGetSafParameter( + String uriString, String openMode) async { throw UnimplementedError( - 'ffmpegKitConfigGetSafParameterForRead() has not been implemented!'); - } - - Future ffmpegKitConfigGetSafParameterForWrite( - String uriString) async { - throw UnimplementedError( - 'ffmpegKitConfigGetSafParameterForWrite() has not been implemented!'); + 'ffmpegKitConfigGetSafParameter() has not been implemented!'); } // FFmpegKitFlutterInitializer @@ -348,9 +358,14 @@ abstract class FFmpegKitPlatform extends PlatformInterface { // FFprobeKit - Future?> ffprobeKitListSessions() async { + Future?> ffprobeKitListFFprobeSessions() async { throw UnimplementedError( - 'ffprobeKitListSessions() has not been implemented!'); + 'ffprobeKitListFFprobeSessions() has not been implemented!'); + } + + Future?> ffprobeKitListMediaInformationSessions() async { + throw UnimplementedError( + 'ffprobeKitListMediaInformationSessions() has not been implemented!'); } // MediaInformationJsonParser @@ -367,6 +382,12 @@ abstract class FFmpegKitPlatform extends PlatformInterface { 'mediaInformationJsonParserFromWithError() has not been implemented!'); } + Future?> mediaInformationSessionGetMediaInformation( + int? sessionId) async { + throw UnimplementedError('mediaInformationSessionGetMediaInformation() ' + 'has not been implemented!'); + } + Future getPackageName() async { throw UnimplementedError('getPackageName() has not been implemented!'); } diff --git a/flutter/flutter_platform_interface/lib/method_channel_ffmpeg_kit_flutter.dart b/flutter/flutter_platform_interface/lib/method_channel_ffmpeg_kit_flutter.dart index 1076405..9de767f 100644 --- a/flutter/flutter_platform_interface/lib/method_channel_ffmpeg_kit_flutter.dart +++ b/flutter/flutter_platform_interface/lib/method_channel_ffmpeg_kit_flutter.dart @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Taner Sener + * Copyright (c) 2021-2022 Taner Sener * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -99,13 +99,14 @@ class MethodChannelFFmpegKit extends FFmpegKitPlatform { int? sessionId) async => _channel.invokeMethod( 'abstractSessionThereAreAsynchronousMessagesInTransmit', - {'sessionId': sessionId}).then((value) => value ?? false); + {'sessionId': sessionId}).then((bool? value) => value ?? false); // ArchDetect @override - Future archDetectGetArch() async => - _channel.invokeMethod('getArch').then((value) => value ?? ""); + Future archDetectGetArch() async => _channel + .invokeMethod('getArch') + .then((String? value) => value ?? ""); // FFmpegKit @@ -179,6 +180,20 @@ class MethodChannelFFmpegKit extends FFmpegKitPlatform { Future ffmpegKitConfigIgnoreSignal(int signal) async => _channel.invokeMethod('ignoreSignal', {'signal': signal}); + @override + Future ffmpegKitConfigFFmpegExecute(int? sessionId) async => _channel + .invokeMethod('ffmpegSessionExecute', {'sessionId': sessionId}); + + @override + Future ffmpegKitConfigFFprobeExecute(int? sessionId) async => _channel + .invokeMethod('ffprobeSessionExecute', {'sessionId': sessionId}); + + @override + Future ffmpegKitConfigGetMediaInformationExecute( + int? sessionId, int? waitTimeout) async => + _channel.invokeMethod('mediaInformationSessionExecute', + {'sessionId': sessionId, 'waitTimeout': waitTimeout}); + @override Future ffmpegKitConfigAsyncFFmpegExecute(int? sessionId) async => _channel.invokeMethod( @@ -286,16 +301,10 @@ class MethodChannelFFmpegKit extends FFmpegKitPlatform { }); @override - Future ffmpegKitConfigGetSafParameterForRead( - String uriString) async => + Future ffmpegKitConfigGetSafParameter( + String uriString, String openMode) async => _channel.invokeMethod( - 'getSafParameter', {'writable': false, 'uri': uriString}); - - @override - Future ffmpegKitConfigGetSafParameterForWrite( - String uriString) async => - _channel.invokeMethod( - 'getSafParameter', {'writable': true, 'uri': uriString}); + 'getSafParameter', {'uri': uriString, 'openMode': openMode}); // FFmpegKitFlutterInitializer @@ -318,9 +327,13 @@ class MethodChannelFFmpegKit extends FFmpegKitPlatform { // FFprobeKit @override - Future?> ffprobeKitListSessions() async => + Future?> ffprobeKitListFFprobeSessions() async => _channel.invokeMethod>('getFFprobeSessions'); + @override + Future?> ffprobeKitListMediaInformationSessions() async => + _channel.invokeMethod>('getMediaInformationSessions'); + // MediaInformationJsonParser @override @@ -337,6 +350,14 @@ class MethodChannelFFmpegKit extends FFmpegKitPlatform { 'mediaInformationJsonParserFromWithError', {'ffprobeJsonOutput': ffprobeJsonOutput}); + // MediaInformationSession + + @override + Future?> mediaInformationSessionGetMediaInformation( + int? sessionId) async => + _channel.invokeMethod>( + 'getMediaInformation', {'sessionId': sessionId}); + @override Future getPackageName() async => _channel.invokeMethod('getPackageName'); diff --git a/flutter/flutter_platform_interface/pubspec.yaml b/flutter/flutter_platform_interface/pubspec.yaml index aadfaf1..44ef877 100644 --- a/flutter/flutter_platform_interface/pubspec.yaml +++ b/flutter/flutter_platform_interface/pubspec.yaml @@ -5,7 +5,7 @@ issue_tracker: https://github.com/tanersener/ffmpeg-kit/issues homepage: https://github.com/tanersener/ffmpeg-kit # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 0.1.0 +version: 0.2.1 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/tools/release/flutter/create_packages.sh b/tools/release/flutter/create_packages.sh index 5867cef..e61a6db 100755 --- a/tools/release/flutter/create_packages.sh +++ b/tools/release/flutter/create_packages.sh @@ -2,8 +2,8 @@ CURRENT_DIR=$(pwd) BASEDIR="${CURRENT_DIR}/../../.." -TMPDIR="${BASEDIR}/.tmp" -PACKAGES_DIR="${TMPDIR}/flutter_packages" +PACKAGES_DIR_MAIN="${BASEDIR}/prebuilt/bundle-flutter-main" +PACKAGES_DIR_LTS="${BASEDIR}/prebuilt/bundle-flutter-lts" SOURCE_DIR="${BASEDIR}/flutter/flutter" PACKAGES=(min min-gpl https https-gpl audio video full full-gpl) @@ -18,7 +18,7 @@ prepare_inline_sed() { create_main_releases() { for CURRENT_PACKAGE in "${PACKAGES[@]}"; do local FLUTTER_PACKAGE_NAME="$(echo "${CURRENT_PACKAGE}" | sed "s/\-/\_/g")" - local PACKAGE_PATH="${PACKAGES_DIR}/${CURRENT_PACKAGE}" + local PACKAGE_PATH="${PACKAGES_DIR_MAIN}/${CURRENT_PACKAGE}" cp -R ${SOURCE_DIR} ${PACKAGE_PATH} # 1. pubspec @@ -29,31 +29,34 @@ create_main_releases() { # 2. android # UPDATE MIN SDK VERSION $SED_INLINE "s|com.arthenica:.*|com.arthenica:ffmpeg-kit-$CURRENT_PACKAGE:$NATIVE_VERSION'|g" ${PACKAGE_PATH}/android/build.gradle - rm -f ${PACKAGE_PATH}/android/build.gradle.tmp + rm -f ${PACKAGE_PATH}/android/build.gradle.tmp # 3. ios $SED_INLINE "s|ffmpeg_kit_flutter|ffmpeg_kit_flutter_$FLUTTER_PACKAGE_NAME|g" ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter.podspec # UPDATE VERSION $SED_INLINE "s|s.default_subspec.*|s.default_subspec = '$CURRENT_PACKAGE'|g" ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter.podspec - rm -f ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter.podspec.tmp + rm -f ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter.podspec.tmp mv ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter.podspec ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter_$FLUTTER_PACKAGE_NAME.podspec # 4. macos $SED_INLINE "s|ffmpeg_kit_flutter|ffmpeg_kit_flutter_$FLUTTER_PACKAGE_NAME|g" ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter.podspec # UPDATE VERSION $SED_INLINE "s|s.default_subspec.*|s.default_subspec = '$CURRENT_PACKAGE'|g" ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter.podspec - rm -f ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter.podspec.tmp + rm -f ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter.podspec.tmp mv ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter.podspec ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter_$FLUTTER_PACKAGE_NAME.podspec done; + # CREATE DEFAULT PACKAGE + cp -R "${SOURCE_DIR}" "${PACKAGES_DIR_MAIN}/default" + echo "main releases created!" } create_lts_releases() { for CURRENT_PACKAGE in "${PACKAGES[@]}"; do local FLUTTER_PACKAGE_NAME="$(echo "${CURRENT_PACKAGE}" | sed "s/\-/\_/g")" - local PACKAGE_PATH="${PACKAGES_DIR}/${CURRENT_PACKAGE}-lts" + local PACKAGE_PATH="${PACKAGES_DIR_LTS}/${CURRENT_PACKAGE}" cp -R ${SOURCE_DIR} ${PACKAGE_PATH} # 1. pubspec @@ -64,24 +67,35 @@ create_lts_releases() { # 2. android $SED_INLINE "s|minSdkVersion.*|minSdkVersion 16|g" ${PACKAGE_PATH}/android/build.gradle $SED_INLINE "s|com.arthenica:.*|com.arthenica:ffmpeg-kit-$CURRENT_PACKAGE:$NATIVE_VERSION.LTS'|g" ${PACKAGE_PATH}/android/build.gradle - rm -f ${PACKAGE_PATH}/android/build.gradle.tmp + rm -f ${PACKAGE_PATH}/android/build.gradle.tmp # 3. ios $SED_INLINE "s|ffmpeg_kit_flutter|ffmpeg_kit_flutter_$FLUTTER_PACKAGE_NAME|g" ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter.podspec $SED_INLINE "s|s.version.*|s.version = '$VERSION.LTS'|g" ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter.podspec $SED_INLINE "s|s.default_subspec.*|s.default_subspec = '$CURRENT_PACKAGE-lts'|g" ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter.podspec - rm -f ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter.podspec.tmp + rm -f ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter.podspec.tmp mv ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter.podspec ${PACKAGE_PATH}/ios/ffmpeg_kit_flutter_$FLUTTER_PACKAGE_NAME.podspec # 4. macos $SED_INLINE "s|ffmpeg_kit_flutter|ffmpeg_kit_flutter_$FLUTTER_PACKAGE_NAME|g" ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter.podspec $SED_INLINE "s|s.version.*|s.version = '$VERSION.LTS'|g" ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter.podspec $SED_INLINE "s|s.default_subspec.*|s.default_subspec = '$CURRENT_PACKAGE-lts'|g" ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter.podspec - rm -f ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter.podspec.tmp + rm -f ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter.podspec.tmp mv ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter.podspec ${PACKAGE_PATH}/macos/ffmpeg_kit_flutter_$FLUTTER_PACKAGE_NAME.podspec done; + # CREATE DEFAULT PACKAGE + cp -R "${PACKAGES_DIR_LTS}/https" "${PACKAGES_DIR_LTS}/default" + $SED_INLINE "s|name: ffmpeg_kit_flutter_https|name: ffmpeg_kit_flutter|g" ${PACKAGES_DIR_LTS}/default/pubspec.yaml + rm -f ${PACKAGES_DIR_LTS}/default/pubspec.yaml.tmp + $SED_INLINE "s|ffmpeg_kit_flutter_https|ffmpeg_kit_flutter|g" ${PACKAGES_DIR_LTS}/default/ios/ffmpeg_kit_flutter_https.podspec + rm -f ${PACKAGES_DIR_LTS}/default/ios/ffmpeg_kit_flutter_https.podspec.tmp + mv ${PACKAGES_DIR_LTS}/default/ios/ffmpeg_kit_flutter_https.podspec ${PACKAGES_DIR_LTS}/default/ios/ffmpeg_kit_flutter.podspec + $SED_INLINE "s|ffmpeg_kit_flutter_https|ffmpeg_kit_flutter|g" ${PACKAGES_DIR_LTS}/default/macos/ffmpeg_kit_flutter_https.podspec + rm -f ${PACKAGES_DIR_LTS}/default/macos/ffmpeg_kit_flutter_https.podspec.tmp + mv ${PACKAGES_DIR_LTS}/default/macos/ffmpeg_kit_flutter_https.podspec ${PACKAGES_DIR_LTS}/default/macos/ffmpeg_kit_flutter.podspec + echo "lts releases created!" } @@ -94,13 +108,13 @@ fi VERSION="$1" NATIVE_VERSION="$2" -rm -rf "${PACKAGES_DIR}" -mkdir -p "${PACKAGES_DIR}" +rm -rf "${PACKAGES_DIR_MAIN}" +mkdir -p "${PACKAGES_DIR_MAIN}" +rm -rf "${PACKAGES_DIR_LTS}" +mkdir -p "${PACKAGES_DIR_LTS}" prepare_inline_sed create_main_releases; create_lts_releases; - -cp -R "${BASEDIR}/flutter/flutter_platform_interface" "$PACKAGES_DIR" \ No newline at end of file