FlutterFfmpegPlugin.m 15 KB

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