FlutterFfmpegPlugin.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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. #import "FlutterFFmpegPlugin.h"
  20. #import <mobileffmpeg/ArchDetect.h>
  21. #import <mobileffmpeg/MobileFFmpeg.h>
  22. #import <mobileffmpeg/MobileFFmpegConfig.h>
  23. static NSString *const PLATFORM_NAME = @"ios";
  24. static NSString *const KEY_VERSION = @"version";
  25. static NSString *const KEY_RC = @"rc";
  26. static NSString *const KEY_PLATFORM = @"platform";
  27. static NSString *const KEY_PACKAGE_NAME = @"packageName";
  28. static NSString *const KEY_LAST_RC = @"lastRc";
  29. static NSString *const KEY_LAST_COMMAND_OUTPUT = @"lastCommandOutput";
  30. static NSString *const KEY_LOG_TEXT = @"log";
  31. static NSString *const KEY_LOG_LEVEL = @"level";
  32. static NSString *const KEY_STAT_TIME = @"time";
  33. static NSString *const KEY_STAT_SIZE = @"size";
  34. static NSString *const KEY_STAT_BITRATE = @"bitrate";
  35. static NSString *const KEY_STAT_SPEED = @"speed";
  36. static NSString *const KEY_STAT_VIDEO_FRAME_NUMBER = @"videoFrameNumber";
  37. static NSString *const KEY_STAT_VIDEO_QUALITY = @"videoQuality";
  38. static NSString *const KEY_STAT_VIDEO_FPS = @"videoFps";
  39. static NSString *const EVENT_LOG = @"FlutterFFmpegLogCallback";
  40. static NSString *const EVENT_STAT = @"FlutterFFmpegStatisticsCallback";
  41. /**
  42. * Flutter FFmpeg Plugin
  43. */
  44. @implementation FlutterFFmpegPlugin {
  45. FlutterEventSink _eventSink;
  46. }
  47. - (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink {
  48. _eventSink = eventSink;
  49. return nil;
  50. }
  51. - (FlutterError *)onCancelWithArguments:(id)arguments {
  52. _eventSink = nil;
  53. return nil;
  54. }
  55. + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  56. FlutterFFmpegPlugin* instance = [[FlutterFFmpegPlugin alloc] init];
  57. FlutterMethodChannel* methodChannel = [FlutterMethodChannel methodChannelWithName:@"flutter_ffmpeg" binaryMessenger:[registrar messenger]];
  58. [registrar addMethodCallDelegate:instance channel:methodChannel];
  59. FlutterEventChannel* eventChannel = [FlutterEventChannel eventChannelWithName:@"flutter_ffmpeg_event" binaryMessenger:[registrar messenger]];
  60. [eventChannel setStreamHandler:instance];
  61. }
  62. - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
  63. // ARGUMENTS
  64. NSArray* arguments = call.arguments[@"arguments"];
  65. NSString* command = call.arguments[@"command"];
  66. NSString* delimiter = call.arguments[@"delimiter"];
  67. if ([@"getPlatform" isEqualToString:call.method]) {
  68. NSString *architecture = [ArchDetect getArch];
  69. result([FlutterFFmpegPlugin toStringDictionary:KEY_PLATFORM :[NSString stringWithFormat:@"%@-%@", PLATFORM_NAME, architecture]]);
  70. } else if ([@"getFFmpegVersion" isEqualToString:call.method]) {
  71. result([FlutterFFmpegPlugin toStringDictionary:KEY_VERSION :[MobileFFmpeg getFFmpegVersion]]);
  72. } else if ([@"executeWithArguments" isEqualToString:call.method]) {
  73. NSLog(@"Running FFmpeg with arguments: %@.\n", arguments);
  74. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  75. int rc = [MobileFFmpeg executeWithArguments:arguments];
  76. NSLog(@"FFmpeg exited with rc: %d\n", rc);
  77. result([FlutterFFmpegPlugin toIntDictionary:KEY_RC :[NSNumber numberWithInt:rc]]);
  78. });
  79. } else if ([@"execute" isEqualToString:call.method]) {
  80. if (delimiter == nil) {
  81. delimiter = @" ";
  82. }
  83. NSLog(@"Running FFmpeg command: %@ with delimiter %@.\n", command, delimiter);
  84. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  85. int rc = [MobileFFmpeg execute:command delimiter:delimiter];
  86. NSLog(@"FFmpeg exited with rc: %d\n", rc);
  87. result([FlutterFFmpegPlugin toIntDictionary:KEY_RC :[NSNumber numberWithInt:rc]]);
  88. });
  89. } else if ([@"cancel" isEqualToString:call.method]) {
  90. [MobileFFmpeg cancel];
  91. } else if ([@"enableRedirection" isEqualToString:call.method]) {
  92. [MobileFFmpegConfig enableRedirection];
  93. } else if ([@"disableRedirection" isEqualToString:call.method]) {
  94. [MobileFFmpegConfig disableRedirection];
  95. } else if ([@"getLogLevel" isEqualToString:call.method]) {
  96. int logLevel = [MobileFFmpegConfig getLogLevel];
  97. result([FlutterFFmpegPlugin toIntDictionary:KEY_LOG_LEVEL :[NSNumber numberWithInt:logLevel]]);
  98. } else if ([@"setLogLevel" isEqualToString:call.method]) {
  99. NSNumber* logLevel = call.arguments[@"level"];
  100. [MobileFFmpegConfig setLogLevel:[logLevel intValue]];
  101. } else if ([@"enableLogs" isEqualToString:call.method]) {
  102. [MobileFFmpegConfig setLogDelegate:self];
  103. } else if ([@"disableLogs" isEqualToString:call.method]) {
  104. [MobileFFmpegConfig setLogDelegate:nil];
  105. } else if ([@"enableStatistics" isEqualToString:call.method]) {
  106. [MobileFFmpegConfig setStatisticsDelegate:self];
  107. } else if ([@"disableStatistics" isEqualToString:call.method]) {
  108. [MobileFFmpegConfig setStatisticsDelegate:nil];
  109. } else if ([@"getLastReceivedStatistics" isEqualToString:call.method]) {
  110. Statistics *statistics = [MobileFFmpegConfig getLastReceivedStatistics];
  111. result([FlutterFFmpegPlugin toStatisticsDictionary:statistics]);
  112. } else if ([@"resetStatistics" isEqualToString:call.method]) {
  113. [MobileFFmpegConfig resetStatistics];
  114. } else if ([@"setFontconfigConfigurationPath" isEqualToString:call.method]) {
  115. NSString* path = call.arguments[@"path"];
  116. [MobileFFmpegConfig setFontconfigConfigurationPath:path];
  117. } else if ([@"setFontDirectory" isEqualToString:call.method]) {
  118. NSString* fontDirectoryPath = call.arguments[@"fontDirectory"];
  119. NSDictionary* fontNameMapping = call.arguments[@"fontNameMap"];
  120. [MobileFFmpegConfig setFontDirectory:fontDirectoryPath with:fontNameMapping];
  121. } else if ([@"getPackageName" isEqualToString:call.method]) {
  122. NSString *packageName = [MobileFFmpegConfig getPackageName];
  123. result([FlutterFFmpegPlugin toStringDictionary:KEY_PACKAGE_NAME :packageName]);
  124. } else if ([@"getExternalLibraries" isEqualToString:call.method]) {
  125. NSArray *externalLibraries = [MobileFFmpegConfig getExternalLibraries];
  126. result(externalLibraries);
  127. } else if ([@"getLastReturnCode" isEqualToString:call.method]) {
  128. int lastReturnCode = [MobileFFmpeg getLastReturnCode];
  129. result([FlutterFFmpegPlugin toIntDictionary:KEY_LAST_RC :[NSNumber numberWithInt:lastReturnCode]]);
  130. } else if ([@"getLastCommandOutput" isEqualToString:call.method]) {
  131. NSString *lastCommandOutput = [MobileFFmpeg getLastCommandOutput];
  132. result([FlutterFFmpegPlugin toStringDictionary:KEY_LAST_COMMAND_OUTPUT :lastCommandOutput]);
  133. } else if ([@"getMediaInformation" isEqualToString:call.method]) {
  134. NSString* path = call.arguments[@"path"];
  135. NSNumber* timeout = call.arguments[@"timeout"];
  136. NSLog(@"Getting media information for %@ with timeout %d.\n", path, [timeout intValue]);
  137. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  138. MediaInformation *mediaInformation = [MobileFFmpeg getMediaInformation:path timeout:[timeout intValue]];
  139. result([FlutterFFmpegPlugin toMediaInformationDictionary:mediaInformation]);
  140. });
  141. } else {
  142. result(FlutterMethodNotImplemented);
  143. }
  144. }
  145. - (void)logCallback: (int)level :(NSString*)message {
  146. dispatch_async(dispatch_get_main_queue(), ^{
  147. NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
  148. dictionary[KEY_LOG_LEVEL] = [NSNumber numberWithInt:level];
  149. dictionary[KEY_LOG_TEXT] = message;
  150. [self emitLogMessage: dictionary];
  151. });
  152. }
  153. - (void)statisticsCallback:(Statistics *)statistics {
  154. dispatch_async(dispatch_get_main_queue(), ^{
  155. [self emitStatistics: statistics];
  156. });
  157. }
  158. - (void)emitLogMessage:(NSDictionary*)logMessage{
  159. _eventSink([FlutterFFmpegPlugin toStringDictionary:EVENT_LOG :logMessage]);
  160. }
  161. - (void)emitStatistics:(Statistics*)statistics{
  162. NSDictionary *dictionary = [FlutterFFmpegPlugin toStatisticsDictionary:statistics];
  163. _eventSink([FlutterFFmpegPlugin toStringDictionary:EVENT_STAT :dictionary]);
  164. }
  165. + (NSDictionary *)toStringDictionary:(NSString*)key :(NSString*)value {
  166. NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
  167. dictionary[key] = value;
  168. return dictionary;
  169. }
  170. + (NSDictionary *)toIntDictionary:(NSString*)key :(NSNumber*)value {
  171. NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
  172. dictionary[key] = value;
  173. return dictionary;
  174. }
  175. + (NSDictionary *)toStatisticsDictionary:(Statistics*)statistics {
  176. NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
  177. if (statistics != nil) {
  178. dictionary[KEY_STAT_TIME] = [NSNumber numberWithInt: [statistics getTime]];
  179. dictionary[KEY_STAT_SIZE] = [NSNumber numberWithLong: [statistics getSize]];
  180. dictionary[KEY_STAT_BITRATE] = [NSNumber numberWithDouble: [statistics getBitrate]];
  181. dictionary[KEY_STAT_SPEED] = [NSNumber numberWithDouble: [statistics getSpeed]];
  182. dictionary[KEY_STAT_VIDEO_FRAME_NUMBER] = [NSNumber numberWithInt: [statistics getVideoFrameNumber]];
  183. dictionary[KEY_STAT_VIDEO_QUALITY] = [NSNumber numberWithFloat: [statistics getVideoQuality]];
  184. dictionary[KEY_STAT_VIDEO_FPS] = [NSNumber numberWithFloat: [statistics getVideoFps]];
  185. }
  186. return dictionary;
  187. }
  188. + (NSDictionary *)toMediaInformationDictionary:(MediaInformation*)mediaInformation {
  189. NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
  190. if (mediaInformation != nil) {
  191. if ([mediaInformation getFormat] != nil) {
  192. dictionary[@"format"] = [mediaInformation getFormat];
  193. }
  194. if ([mediaInformation getPath] != nil) {
  195. dictionary[@"path"] = [mediaInformation getPath];
  196. }
  197. if ([mediaInformation getStartTime] != nil) {
  198. dictionary[@"startTime"] = [mediaInformation getStartTime];
  199. }
  200. if ([mediaInformation getDuration] != nil) {
  201. dictionary[@"duration"] = [mediaInformation getDuration];
  202. }
  203. if ([mediaInformation getBitrate] != nil) {
  204. dictionary[@"bitrate"] = [mediaInformation getBitrate];
  205. }
  206. if ([mediaInformation getRawInformation] != nil) {
  207. dictionary[@"rawInformation"] = [mediaInformation getRawInformation];
  208. }
  209. NSDictionary *metadataDictionary = [mediaInformation getMetadataEntries];
  210. if (metadataDictionary != nil && ([metadataDictionary count] > 0)) {
  211. dictionary[@"metadata"] = metadataDictionary;
  212. }
  213. NSArray *streams = [mediaInformation getStreams];
  214. if (streams != nil && ([streams count] > 0)) {
  215. NSMutableArray *array = [[NSMutableArray alloc] init];
  216. for (int i=0; i < [streams count]; i++) {
  217. StreamInformation *streamInformation= [streams objectAtIndex:i];
  218. [array addObject: [FlutterFFmpegPlugin toStreamInformationDictionary:streamInformation]];
  219. }
  220. dictionary[@"streams"] = array;
  221. }
  222. }
  223. return dictionary;
  224. }
  225. + (NSDictionary *)toStreamInformationDictionary:(StreamInformation*)streamInformation {
  226. NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
  227. if (streamInformation != nil) {
  228. if ([streamInformation getIndex] != nil) {
  229. dictionary[@"index"] = [streamInformation getIndex];
  230. }
  231. if ([streamInformation getType] != nil) {
  232. dictionary[@"type"] = [streamInformation getType];
  233. }
  234. if ([streamInformation getCodec] != nil) {
  235. dictionary[@"codec"] = [streamInformation getCodec];
  236. }
  237. if ([streamInformation getFullCodec] != nil) {
  238. dictionary[@"fullCodec"] = [streamInformation getFullCodec];
  239. }
  240. if ([streamInformation getFormat] != nil) {
  241. dictionary[@"format"] = [streamInformation getFormat];
  242. }
  243. if ([streamInformation getFullFormat] != nil) {
  244. dictionary[@"fullFormat"] = [streamInformation getFullFormat];
  245. }
  246. if ([streamInformation getWidth] != nil) {
  247. dictionary[@"width"] = [streamInformation getWidth];
  248. }
  249. if ([streamInformation getHeight] != nil) {
  250. dictionary[@"height"] = [streamInformation getHeight];
  251. }
  252. if ([streamInformation getBitrate] != nil) {
  253. dictionary[@"bitrate"] = [streamInformation getBitrate];
  254. }
  255. if ([streamInformation getSampleRate] != nil) {
  256. dictionary[@"sampleRate"] = [streamInformation getSampleRate];
  257. }
  258. if ([streamInformation getSampleFormat] != nil) {
  259. dictionary[@"sampleFormat"] = [streamInformation getSampleFormat];
  260. }
  261. if ([streamInformation getChannelLayout] != nil) {
  262. dictionary[@"channelLayout"] = [streamInformation getChannelLayout];
  263. }
  264. if ([streamInformation getSampleAspectRatio] != nil) {
  265. dictionary[@"sampleAspectRatio"] = [streamInformation getSampleAspectRatio];
  266. }
  267. if ([streamInformation getDisplayAspectRatio] != nil) {
  268. dictionary[@"displayAspectRatio"] = [streamInformation getDisplayAspectRatio];
  269. }
  270. if ([streamInformation getAverageFrameRate] != nil) {
  271. dictionary[@"averageFrameRate"] = [streamInformation getAverageFrameRate];
  272. }
  273. if ([streamInformation getRealFrameRate] != nil) {
  274. dictionary[@"realFrameRate"] = [streamInformation getRealFrameRate];
  275. }
  276. if ([streamInformation getTimeBase] != nil) {
  277. dictionary[@"timeBase"] = [streamInformation getTimeBase];
  278. }
  279. if ([streamInformation getCodecTimeBase] != nil) {
  280. dictionary[@"codecTimeBase"] = [streamInformation getCodecTimeBase];
  281. }
  282. NSDictionary *metadataDictionary = [streamInformation getMetadataEntries];
  283. if (metadataDictionary != nil && ([metadataDictionary count] > 0)) {
  284. dictionary[@"metadata"] = metadataDictionary;
  285. }
  286. }
  287. return dictionary;
  288. }
  289. @end