FlutterFfmpegPlugin.m 15 KB

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