FlutterFFmpegPlugin.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. /*
  2. * Copyright (c) 2019 Taner Sener
  3. *
  4. * This file is part of FlutterFFmpeg.
  5. *
  6. * FlutterFFmpeg is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Lesser General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * FlutterFFmpeg is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with FlutterFFmpeg. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.arthenica.flutter.ffmpeg;
  20. import android.content.Context;
  21. import com.arthenica.mobileffmpeg.AbiDetect;
  22. import com.arthenica.mobileffmpeg.Config;
  23. import com.arthenica.mobileffmpeg.FFmpeg;
  24. import com.arthenica.mobileffmpeg.Level;
  25. import com.arthenica.mobileffmpeg.LogCallback;
  26. import com.arthenica.mobileffmpeg.LogMessage;
  27. import com.arthenica.mobileffmpeg.MediaInformation;
  28. import com.arthenica.mobileffmpeg.Statistics;
  29. import com.arthenica.mobileffmpeg.StatisticsCallback;
  30. import com.arthenica.mobileffmpeg.StreamInformation;
  31. import java.util.ArrayList;
  32. import java.util.HashMap;
  33. import java.util.List;
  34. import java.util.Map;
  35. import java.util.Set;
  36. import io.flutter.plugin.common.EventChannel;
  37. import io.flutter.plugin.common.MethodCall;
  38. import io.flutter.plugin.common.MethodChannel;
  39. import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
  40. import io.flutter.plugin.common.MethodChannel.Result;
  41. import io.flutter.plugin.common.PluginRegistry.Registrar;
  42. /**
  43. * <h3>Flutter FFmpeg Plugin</h3>
  44. *
  45. * @author Taner Sener
  46. * @since 0.1.0
  47. */
  48. public class FlutterFFmpegPlugin implements MethodCallHandler, EventChannel.StreamHandler {
  49. public static final String LIBRARY_NAME = "flutter-ffmpeg";
  50. public static final String PLATFORM_NAME = "android";
  51. public static final String KEY_VERSION = "version";
  52. public static final String KEY_RC = "rc";
  53. public static final String KEY_PLATFORM = "platform";
  54. public static final String KEY_PACKAGE_NAME = "packageName";
  55. public static final String KEY_LAST_RC = "lastRc";
  56. public static final String KEY_PIPE = "pipe";
  57. public static final String KEY_LAST_COMMAND_OUTPUT = "lastCommandOutput";
  58. public static final String KEY_LOG_TEXT = "log";
  59. public static final String KEY_LOG_LEVEL = "level";
  60. public static final String KEY_STAT_TIME = "time";
  61. public static final String KEY_STAT_SIZE = "size";
  62. public static final String KEY_STAT_BITRATE = "bitrate";
  63. public static final String KEY_STAT_SPEED = "speed";
  64. public static final String KEY_STAT_VIDEO_FRAME_NUMBER = "videoFrameNumber";
  65. public static final String KEY_STAT_VIDEO_QUALITY = "videoQuality";
  66. public static final String KEY_STAT_VIDEO_FPS = "videoFps";
  67. public static final String EVENT_LOG = "FlutterFFmpegLogCallback";
  68. public static final String EVENT_STAT = "FlutterFFmpegStatisticsCallback";
  69. private EventChannel.EventSink eventSink;
  70. private final Registrar registrar;
  71. private final FlutterFFmpegResultHandler flutterFFmpegResultHandler;
  72. /**
  73. * Registers plugin to registry.
  74. *
  75. * @param registrar receiver of plugin registration
  76. */
  77. public static void registerWith(final Registrar registrar) {
  78. FlutterFFmpegPlugin flutterFFmpegPlugin = new FlutterFFmpegPlugin(registrar);
  79. final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter_ffmpeg");
  80. channel.setMethodCallHandler(flutterFFmpegPlugin);
  81. final EventChannel eventChannel = new EventChannel(registrar.messenger(), "flutter_ffmpeg_event");
  82. eventChannel.setStreamHandler(flutterFFmpegPlugin);
  83. }
  84. private FlutterFFmpegPlugin(Registrar registrar) {
  85. this.registrar = registrar;
  86. this.flutterFFmpegResultHandler = new FlutterFFmpegResultHandler();
  87. }
  88. private Context getActiveContext() {
  89. return (registrar.activity() != null) ? registrar.activity() : registrar.context();
  90. }
  91. /**
  92. * Handles method calls.
  93. *
  94. * @param call method call
  95. * @param result result callback
  96. */
  97. @Override
  98. public void onMethodCall(final MethodCall call, final Result result) {
  99. if (call.method.equals("getPlatform")) {
  100. final String abi = AbiDetect.getAbi();
  101. flutterFFmpegResultHandler.success(result, toStringMap(KEY_PLATFORM, PLATFORM_NAME + "-" + abi));
  102. } else if (call.method.equals("getFFmpegVersion")) {
  103. final String version = Config.getFFmpegVersion();
  104. flutterFFmpegResultHandler.success(result, toStringMap(KEY_VERSION, version));
  105. } else if (call.method.equals("executeFFmpegWithArguments")) {
  106. List<String> arguments = call.argument("arguments");
  107. final FlutterFFmpegExecuteFFmpegAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteFFmpegAsyncArgumentsTask(arguments, flutterFFmpegResultHandler, result);
  108. asyncTask.execute("dummy-trigger");
  109. } else if (call.method.equals("executeFFprobeWithArguments")) {
  110. List<String> arguments = call.argument("arguments");
  111. final FlutterFFmpegExecuteFFprobeAsyncArgumentsTask asyncTask = new FlutterFFmpegExecuteFFprobeAsyncArgumentsTask(arguments, flutterFFmpegResultHandler, result);
  112. asyncTask.execute("dummy-trigger");
  113. } else if (call.method.equals("cancel")) {
  114. FFmpeg.cancel();
  115. } else if (call.method.equals("enableRedirection")) {
  116. Config.enableRedirection();
  117. } else if (call.method.equals("disableRedirection")) {
  118. Config.disableRedirection();
  119. } else if (call.method.equals("getLogLevel")) {
  120. final Level level = Config.getLogLevel();
  121. flutterFFmpegResultHandler.success(result, toIntMap(KEY_LOG_LEVEL, levelToInt(level)));
  122. } else if (call.method.equals("setLogLevel")) {
  123. Integer level = call.argument("level");
  124. if (level == null) {
  125. level = Level.AV_LOG_TRACE.getValue();
  126. }
  127. Config.setLogLevel(Level.from(level));
  128. } else if (call.method.equals("enableLogs")) {
  129. Config.enableLogCallback(new LogCallback() {
  130. @Override
  131. public void apply(final LogMessage logMessage) {
  132. emitLogMessage(logMessage);
  133. }
  134. });
  135. } else if (call.method.equals("disableLogs")) {
  136. Config.enableLogCallback(null);
  137. } else if (call.method.equals("enableStatistics")) {
  138. Config.enableStatisticsCallback(new StatisticsCallback() {
  139. @Override
  140. public void apply(final Statistics statistics) {
  141. emitStatistics(statistics);
  142. }
  143. });
  144. } else if (call.method.equals("disableStatistics")) {
  145. Config.enableStatisticsCallback(null);
  146. } else if (call.method.equals("getLastReceivedStatistics")) {
  147. flutterFFmpegResultHandler.success(result, toMap(Config.getLastReceivedStatistics()));
  148. } else if (call.method.equals("resetStatistics")) {
  149. Config.resetStatistics();
  150. } else if (call.method.equals("setFontconfigConfigurationPath")) {
  151. String path = call.argument("path");
  152. Config.setFontconfigConfigurationPath(path);
  153. } else if (call.method.equals("setFontDirectory")) {
  154. String path = call.argument("fontDirectory");
  155. Map<String, String> map = call.argument("fontNameMap");
  156. Config.setFontDirectory(getActiveContext(), path, map);
  157. } else if (call.method.equals("getPackageName")) {
  158. final String packageName = Config.getPackageName();
  159. flutterFFmpegResultHandler.success(result, toStringMap(KEY_PACKAGE_NAME, packageName));
  160. } else if (call.method.equals("getExternalLibraries")) {
  161. final List<String> externalLibraries = Config.getExternalLibraries();
  162. flutterFFmpegResultHandler.success(result, externalLibraries);
  163. } else if (call.method.equals("getLastReturnCode")) {
  164. int lastReturnCode = Config.getLastReturnCode();
  165. flutterFFmpegResultHandler.success(result, toIntMap(KEY_LAST_RC, lastReturnCode));
  166. } else if (call.method.equals("getLastCommandOutput")) {
  167. final String lastCommandOutput = Config.getLastCommandOutput();
  168. flutterFFmpegResultHandler.success(result, toStringMap(KEY_LAST_COMMAND_OUTPUT, lastCommandOutput));
  169. } else if (call.method.equals("getMediaInformation")) {
  170. final String path = call.argument("path");
  171. Integer timeout = call.argument("timeout");
  172. if (timeout == null) {
  173. timeout = 10000;
  174. }
  175. final FlutterFFmpegGetMediaInformationAsyncTask asyncTask = new FlutterFFmpegGetMediaInformationAsyncTask(path, flutterFFmpegResultHandler, result);
  176. asyncTask.execute();
  177. } else if (call.method.equals("registerNewFFmpegPipe")) {
  178. final String pipe = Config.registerNewFFmpegPipe(getActiveContext());
  179. flutterFFmpegResultHandler.success(result, toStringMap(KEY_PIPE, pipe));
  180. } else {
  181. flutterFFmpegResultHandler.notImplemented(result);
  182. }
  183. }
  184. @Override
  185. public void onListen(Object o, EventChannel.EventSink eventSink) {
  186. this.eventSink = eventSink;
  187. }
  188. @Override
  189. public void onCancel(Object o) {
  190. this.eventSink = null;
  191. }
  192. protected void emitLogMessage(final LogMessage logMessage) {
  193. final HashMap<String, Object> logWrapperMap = new HashMap<>();
  194. final HashMap<String, Object> logMap = new HashMap<>();
  195. logMap.put(KEY_LOG_LEVEL, levelToInt(logMessage.getLevel()));
  196. logMap.put(KEY_LOG_TEXT, logMessage.getText());
  197. logWrapperMap.put(EVENT_LOG, logMap);
  198. flutterFFmpegResultHandler.success(eventSink, logWrapperMap);
  199. }
  200. protected void emitStatistics(final Statistics statistics) {
  201. final HashMap<String, Object> statisticsMap = new HashMap<>();
  202. statisticsMap.put(EVENT_STAT, toMap(statistics));
  203. flutterFFmpegResultHandler.success(eventSink, statisticsMap);
  204. }
  205. public static int levelToInt(final Level level) {
  206. return (level == null) ? Level.AV_LOG_TRACE.getValue() : level.getValue();
  207. }
  208. public static HashMap<String, String> toStringMap(final String key, final String value) {
  209. final HashMap<String, String> map = new HashMap<>();
  210. map.put(key, value);
  211. return map;
  212. }
  213. public static HashMap<String, Integer> toIntMap(final String key, final int value) {
  214. final HashMap<String, Integer> map = new HashMap<>();
  215. map.put(key, value);
  216. return map;
  217. }
  218. public static Map<String, Object> toMap(final Statistics statistics) {
  219. final HashMap<String, Object> statisticsMap = new HashMap<>();
  220. if (statistics != null) {
  221. statisticsMap.put(KEY_STAT_TIME, statistics.getTime());
  222. statisticsMap.put(KEY_STAT_SIZE, (statistics.getSize() < Integer.MAX_VALUE) ? (int) statistics.getSize() : (int) (statistics.getSize() % Integer.MAX_VALUE));
  223. statisticsMap.put(KEY_STAT_BITRATE, statistics.getBitrate());
  224. statisticsMap.put(KEY_STAT_SPEED, statistics.getSpeed());
  225. statisticsMap.put(KEY_STAT_VIDEO_FRAME_NUMBER, statistics.getVideoFrameNumber());
  226. statisticsMap.put(KEY_STAT_VIDEO_QUALITY, statistics.getVideoQuality());
  227. statisticsMap.put(KEY_STAT_VIDEO_FPS, statistics.getVideoFps());
  228. }
  229. return statisticsMap;
  230. }
  231. public static HashMap<String, Object> toMediaInformationMap(final MediaInformation mediaInformation) {
  232. final HashMap<String, Object> map = new HashMap<>();
  233. if (mediaInformation != null) {
  234. if (mediaInformation.getFormat() != null) {
  235. map.put("format", mediaInformation.getFormat());
  236. }
  237. if (mediaInformation.getPath() != null) {
  238. map.put("path", mediaInformation.getPath());
  239. }
  240. if (mediaInformation.getStartTime() != null) {
  241. map.put("startTime", mediaInformation.getStartTime().intValue());
  242. }
  243. if (mediaInformation.getDuration() != null) {
  244. map.put("duration", mediaInformation.getDuration().intValue());
  245. }
  246. if (mediaInformation.getBitrate() != null) {
  247. map.put("bitrate", mediaInformation.getBitrate().intValue());
  248. }
  249. if (mediaInformation.getRawInformation() != null) {
  250. map.put("rawInformation", mediaInformation.getRawInformation());
  251. }
  252. final Set<Map.Entry<String, String>> metadata = mediaInformation.getMetadataEntries();
  253. if ((metadata != null) && (metadata.size() > 0)) {
  254. final HashMap<String, String> metadataMap = new HashMap<>();
  255. for (Map.Entry<String, String> entry : metadata) {
  256. metadataMap.put(entry.getKey(), entry.getValue());
  257. }
  258. map.put("metadata", metadataMap);
  259. }
  260. final List<StreamInformation> streams = mediaInformation.getStreams();
  261. if ((streams != null) && (streams.size() > 0)) {
  262. final ArrayList<Map<String, Object>> array = new ArrayList<>();
  263. for (StreamInformation streamInformation : streams) {
  264. array.add(toStreamInformationMap(streamInformation));
  265. }
  266. map.put("streams", array);
  267. }
  268. }
  269. return map;
  270. }
  271. public static Map<String, Object> toStreamInformationMap(final StreamInformation streamInformation) {
  272. final HashMap<String, Object> map = new HashMap<>();
  273. if (streamInformation != null) {
  274. if (streamInformation.getIndex() != null) {
  275. map.put("index", streamInformation.getIndex().intValue());
  276. }
  277. if (streamInformation.getType() != null) {
  278. map.put("type", streamInformation.getType());
  279. }
  280. if (streamInformation.getCodec() != null) {
  281. map.put("codec", streamInformation.getCodec());
  282. }
  283. if (streamInformation.getFullCodec() != null) {
  284. map.put("fullCodec", streamInformation.getFullCodec());
  285. }
  286. if (streamInformation.getFormat() != null) {
  287. map.put("format", streamInformation.getFormat());
  288. }
  289. if (streamInformation.getFullFormat() != null) {
  290. map.put("fullFormat", streamInformation.getFullFormat());
  291. }
  292. if (streamInformation.getWidth() != null) {
  293. map.put("width", streamInformation.getWidth().intValue());
  294. }
  295. if (streamInformation.getHeight() != null) {
  296. map.put("height", streamInformation.getHeight().intValue());
  297. }
  298. if (streamInformation.getBitrate() != null) {
  299. map.put("bitrate", streamInformation.getBitrate().intValue());
  300. }
  301. if (streamInformation.getSampleRate() != null) {
  302. map.put("sampleRate", streamInformation.getSampleRate().intValue());
  303. }
  304. if (streamInformation.getSampleFormat() != null) {
  305. map.put("sampleFormat", streamInformation.getSampleFormat());
  306. }
  307. if (streamInformation.getChannelLayout() != null) {
  308. map.put("channelLayout", streamInformation.getChannelLayout());
  309. }
  310. if (streamInformation.getSampleAspectRatio() != null) {
  311. map.put("sampleAspectRatio", streamInformation.getSampleAspectRatio());
  312. }
  313. if (streamInformation.getDisplayAspectRatio() != null) {
  314. map.put("displayAspectRatio", streamInformation.getDisplayAspectRatio());
  315. }
  316. if (streamInformation.getAverageFrameRate() != null) {
  317. map.put("averageFrameRate", streamInformation.getAverageFrameRate());
  318. }
  319. if (streamInformation.getRealFrameRate() != null) {
  320. map.put("realFrameRate", streamInformation.getRealFrameRate());
  321. }
  322. if (streamInformation.getTimeBase() != null) {
  323. map.put("timeBase", streamInformation.getTimeBase());
  324. }
  325. if (streamInformation.getCodecTimeBase() != null) {
  326. map.put("codecTimeBase", streamInformation.getCodecTimeBase());
  327. }
  328. final Set<Map.Entry<String, String>> metadata = streamInformation.getMetadataEntries();
  329. if ((metadata != null) && (metadata.size() > 0)) {
  330. final HashMap<String, String> metadataMap = new HashMap<>();
  331. for (Map.Entry<String, String> entry : metadata) {
  332. metadataMap.put(entry.getKey(), entry.getValue());
  333. }
  334. map.put("metadata", metadataMap);
  335. }
  336. final Set<Map.Entry<String, String>> sidedata = streamInformation.getSidedataEntries();
  337. if ((sidedata != null) && (sidedata.size() > 0)) {
  338. final HashMap<String, String> sidedataMap = new HashMap<>();
  339. for (Map.Entry<String, String> entry : sidedata) {
  340. sidedataMap.put(entry.getKey(), entry.getValue());
  341. }
  342. map.put("sidedata", sidedataMap);
  343. }
  344. }
  345. return map;
  346. }
  347. }