/*
* Copyright (c) 2019 Taner Sener
*
* This file is part of FlutterFFmpeg.
*
* FlutterFFmpeg 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.
*
* FlutterFFmpeg 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 FlutterFFmpeg. If not, see .
*/
#import "FlutterFFmpegPlugin.h"
#import
#import
#import
#import
static NSString *const PLATFORM_NAME = @"ios";
static NSString *const KEY_VERSION = @"version";
static NSString *const KEY_RC = @"rc";
static NSString *const KEY_PLATFORM = @"platform";
static NSString *const KEY_PACKAGE_NAME = @"packageName";
static NSString *const KEY_LAST_RC = @"lastRc";
static NSString *const KEY_LAST_COMMAND_OUTPUT = @"lastCommandOutput";
static NSString *const KEY_PIPE = @"pipe";
static NSString *const KEY_LOG_TEXT = @"log";
static NSString *const KEY_LOG_LEVEL = @"level";
static NSString *const KEY_STAT_TIME = @"time";
static NSString *const KEY_STAT_SIZE = @"size";
static NSString *const KEY_STAT_BITRATE = @"bitrate";
static NSString *const KEY_STAT_SPEED = @"speed";
static NSString *const KEY_STAT_VIDEO_FRAME_NUMBER = @"videoFrameNumber";
static NSString *const KEY_STAT_VIDEO_QUALITY = @"videoQuality";
static NSString *const KEY_STAT_VIDEO_FPS = @"videoFps";
static NSString *const EVENT_LOG = @"FlutterFFmpegLogCallback";
static NSString *const EVENT_STAT = @"FlutterFFmpegStatisticsCallback";
/**
* Flutter FFmpeg Plugin
*/
@implementation FlutterFFmpegPlugin {
FlutterEventSink _eventSink;
}
- (FlutterError *)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink {
_eventSink = eventSink;
return nil;
}
- (FlutterError *)onCancelWithArguments:(id)arguments {
_eventSink = nil;
return nil;
}
+ (void)registerWithRegistrar:(NSObject*)registrar {
FlutterFFmpegPlugin* instance = [[FlutterFFmpegPlugin alloc] init];
FlutterMethodChannel* methodChannel = [FlutterMethodChannel methodChannelWithName:@"flutter_ffmpeg" binaryMessenger:[registrar messenger]];
[registrar addMethodCallDelegate:instance channel:methodChannel];
FlutterEventChannel* eventChannel = [FlutterEventChannel eventChannelWithName:@"flutter_ffmpeg_event" binaryMessenger:[registrar messenger]];
[eventChannel setStreamHandler:instance];
}
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
// ARGUMENTS
NSArray* arguments = call.arguments[@"arguments"];
NSString* command = call.arguments[@"command"];
NSString* delimiter = call.arguments[@"delimiter"];
if ([@"getPlatform" isEqualToString:call.method]) {
NSString *architecture = [ArchDetect getArch];
result([FlutterFFmpegPlugin toStringDictionary:KEY_PLATFORM :[NSString stringWithFormat:@"%@-%@", PLATFORM_NAME, architecture]]);
} else if ([@"getFFmpegVersion" isEqualToString:call.method]) {
result([FlutterFFmpegPlugin toStringDictionary:KEY_VERSION :[MobileFFmpegConfig getFFmpegVersion]]);
} else if ([@"executeFFmpegWithArguments" isEqualToString:call.method]) {
NSLog(@"Running FFmpeg with arguments: %@.\n", arguments);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
int rc = [MobileFFmpeg executeWithArguments:arguments];
NSLog(@"FFmpeg exited with rc: %d\n", rc);
result([FlutterFFmpegPlugin toIntDictionary:KEY_RC :[NSNumber numberWithInt:rc]]);
});
} else if ([@"executeFFprobeWithArguments" isEqualToString:call.method]) {
NSLog(@"Running FFprobe with arguments: %@.\n", arguments);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
int rc = [MobileFFprobe executeWithArguments:arguments];
NSLog(@"FFprobe exited with rc: %d\n", rc);
result([FlutterFFmpegPlugin toIntDictionary:KEY_RC :[NSNumber numberWithInt:rc]]);
});
} else if ([@"cancel" isEqualToString:call.method]) {
[MobileFFmpeg cancel];
} else if ([@"enableRedirection" isEqualToString:call.method]) {
[MobileFFmpegConfig enableRedirection];
} else if ([@"disableRedirection" isEqualToString:call.method]) {
[MobileFFmpegConfig disableRedirection];
} else if ([@"getLogLevel" isEqualToString:call.method]) {
int logLevel = [MobileFFmpegConfig getLogLevel];
result([FlutterFFmpegPlugin toIntDictionary:KEY_LOG_LEVEL :[NSNumber numberWithInt:logLevel]]);
} else if ([@"setLogLevel" isEqualToString:call.method]) {
NSNumber* logLevel = call.arguments[@"level"];
[MobileFFmpegConfig setLogLevel:[logLevel intValue]];
} else if ([@"enableLogs" isEqualToString:call.method]) {
[MobileFFmpegConfig setLogDelegate:self];
} else if ([@"disableLogs" isEqualToString:call.method]) {
[MobileFFmpegConfig setLogDelegate:nil];
} else if ([@"enableStatistics" isEqualToString:call.method]) {
[MobileFFmpegConfig setStatisticsDelegate:self];
} else if ([@"disableStatistics" isEqualToString:call.method]) {
[MobileFFmpegConfig setStatisticsDelegate:nil];
} else if ([@"getLastReceivedStatistics" isEqualToString:call.method]) {
Statistics *statistics = [MobileFFmpegConfig getLastReceivedStatistics];
result([FlutterFFmpegPlugin toStatisticsDictionary:statistics]);
} else if ([@"resetStatistics" isEqualToString:call.method]) {
[MobileFFmpegConfig resetStatistics];
} else if ([@"setFontconfigConfigurationPath" isEqualToString:call.method]) {
NSString* path = call.arguments[@"path"];
[MobileFFmpegConfig setFontconfigConfigurationPath:path];
} else if ([@"setFontDirectory" isEqualToString:call.method]) {
NSString* fontDirectoryPath = call.arguments[@"fontDirectory"];
NSDictionary* fontNameMapping = call.arguments[@"fontNameMap"];
[MobileFFmpegConfig setFontDirectory:fontDirectoryPath with:fontNameMapping];
} else if ([@"getPackageName" isEqualToString:call.method]) {
NSString *packageName = [MobileFFmpegConfig getPackageName];
result([FlutterFFmpegPlugin toStringDictionary:KEY_PACKAGE_NAME :packageName]);
} else if ([@"getExternalLibraries" isEqualToString:call.method]) {
NSArray *externalLibraries = [MobileFFmpegConfig getExternalLibraries];
result(externalLibraries);
} else if ([@"getLastReturnCode" isEqualToString:call.method]) {
int lastReturnCode = [MobileFFmpegConfig getLastReturnCode];
result([FlutterFFmpegPlugin toIntDictionary:KEY_LAST_RC :[NSNumber numberWithInt:lastReturnCode]]);
} else if ([@"getLastCommandOutput" isEqualToString:call.method]) {
NSString *lastCommandOutput = [MobileFFmpegConfig getLastCommandOutput];
result([FlutterFFmpegPlugin toStringDictionary:KEY_LAST_COMMAND_OUTPUT :lastCommandOutput]);
} else if ([@"getMediaInformation" isEqualToString:call.method]) {
NSString* path = call.arguments[@"path"];
NSLog(@"Getting media information for %@.\n", path);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
MediaInformation *mediaInformation = [MobileFFprobe getMediaInformation:path];
result([FlutterFFmpegPlugin toMediaInformationDictionary:mediaInformation]);
});
} else if ([@"registerNewFFmpegPipe" isEqualToString:call.method]) {
NSString *pipe = [MobileFFmpegConfig registerNewFFmpegPipe];
result([FlutterFFmpegPlugin toStringDictionary:KEY_PIPE :pipe]);
} else {
result(FlutterMethodNotImplemented);
}
}
- (void)logCallback: (int)level :(NSString*)message {
dispatch_async(dispatch_get_main_queue(), ^{
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
dictionary[KEY_LOG_LEVEL] = [NSNumber numberWithInt:level];
dictionary[KEY_LOG_TEXT] = message;
[self emitLogMessage: dictionary];
});
}
- (void)statisticsCallback:(Statistics *)statistics {
dispatch_async(dispatch_get_main_queue(), ^{
[self emitStatistics: statistics];
});
}
- (void)emitLogMessage:(NSDictionary*)logMessage{
_eventSink([FlutterFFmpegPlugin toStringDictionary:EVENT_LOG withDictionary:logMessage]);
}
- (void)emitStatistics:(Statistics*)statistics{
NSDictionary *dictionary = [FlutterFFmpegPlugin toStatisticsDictionary:statistics];
_eventSink([FlutterFFmpegPlugin toStringDictionary:EVENT_STAT withDictionary:dictionary]);
}
+ (NSDictionary *)toStringDictionary:(NSString*)key :(NSString*)value {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
dictionary[key] = value;
return dictionary;
}
+ (NSDictionary *)toStringDictionary:(NSString*)key withDictionary:(NSDictionary*)value {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
dictionary[key] = value;
return dictionary;
}
+ (NSDictionary *)toIntDictionary:(NSString*)key :(NSNumber*)value {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
dictionary[key] = value;
return dictionary;
}
+ (NSDictionary *)toStatisticsDictionary:(Statistics*)statistics {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
if (statistics != nil) {
dictionary[KEY_STAT_TIME] = [NSNumber numberWithInt: [statistics getTime]];
dictionary[KEY_STAT_SIZE] = [NSNumber numberWithLong: [statistics getSize]];
dictionary[KEY_STAT_BITRATE] = [NSNumber numberWithDouble: [statistics getBitrate]];
dictionary[KEY_STAT_SPEED] = [NSNumber numberWithDouble: [statistics getSpeed]];
dictionary[KEY_STAT_VIDEO_FRAME_NUMBER] = [NSNumber numberWithInt: [statistics getVideoFrameNumber]];
dictionary[KEY_STAT_VIDEO_QUALITY] = [NSNumber numberWithFloat: [statistics getVideoQuality]];
dictionary[KEY_STAT_VIDEO_FPS] = [NSNumber numberWithFloat: [statistics getVideoFps]];
}
return dictionary;
}
+ (NSDictionary *)toMediaInformationDictionary:(MediaInformation*)mediaInformation {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
if (mediaInformation != nil) {
if ([mediaInformation getFormat] != nil) {
dictionary[@"format"] = [mediaInformation getFormat];
}
if ([mediaInformation getPath] != nil) {
dictionary[@"path"] = [mediaInformation getPath];
}
if ([mediaInformation getStartTime] != nil) {
dictionary[@"startTime"] = [mediaInformation getStartTime];
}
if ([mediaInformation getDuration] != nil) {
dictionary[@"duration"] = [mediaInformation getDuration];
}
if ([mediaInformation getBitrate] != nil) {
dictionary[@"bitrate"] = [mediaInformation getBitrate];
}
if ([mediaInformation getRawInformation] != nil) {
dictionary[@"rawInformation"] = [mediaInformation getRawInformation];
}
NSDictionary *metadataDictionary = [mediaInformation getMetadataEntries];
if (metadataDictionary != nil && ([metadataDictionary count] > 0)) {
dictionary[@"metadata"] = metadataDictionary;
}
NSArray *streams = [mediaInformation getStreams];
if (streams != nil && ([streams count] > 0)) {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i=0; i < [streams count]; i++) {
StreamInformation *streamInformation= [streams objectAtIndex:i];
[array addObject: [FlutterFFmpegPlugin toStreamInformationDictionary:streamInformation]];
}
dictionary[@"streams"] = array;
}
}
return dictionary;
}
+ (NSDictionary *)toStreamInformationDictionary:(StreamInformation*)streamInformation {
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
if (streamInformation != nil) {
if ([streamInformation getIndex] != nil) {
dictionary[@"index"] = [streamInformation getIndex];
}
if ([streamInformation getType] != nil) {
dictionary[@"type"] = [streamInformation getType];
}
if ([streamInformation getCodec] != nil) {
dictionary[@"codec"] = [streamInformation getCodec];
}
if ([streamInformation getFullCodec] != nil) {
dictionary[@"fullCodec"] = [streamInformation getFullCodec];
}
if ([streamInformation getFormat] != nil) {
dictionary[@"format"] = [streamInformation getFormat];
}
if ([streamInformation getFullFormat] != nil) {
dictionary[@"fullFormat"] = [streamInformation getFullFormat];
}
if ([streamInformation getWidth] != nil) {
dictionary[@"width"] = [streamInformation getWidth];
}
if ([streamInformation getHeight] != nil) {
dictionary[@"height"] = [streamInformation getHeight];
}
if ([streamInformation getBitrate] != nil) {
dictionary[@"bitrate"] = [streamInformation getBitrate];
}
if ([streamInformation getSampleRate] != nil) {
dictionary[@"sampleRate"] = [streamInformation getSampleRate];
}
if ([streamInformation getSampleFormat] != nil) {
dictionary[@"sampleFormat"] = [streamInformation getSampleFormat];
}
if ([streamInformation getChannelLayout] != nil) {
dictionary[@"channelLayout"] = [streamInformation getChannelLayout];
}
if ([streamInformation getSampleAspectRatio] != nil) {
dictionary[@"sampleAspectRatio"] = [streamInformation getSampleAspectRatio];
}
if ([streamInformation getDisplayAspectRatio] != nil) {
dictionary[@"displayAspectRatio"] = [streamInformation getDisplayAspectRatio];
}
if ([streamInformation getAverageFrameRate] != nil) {
dictionary[@"averageFrameRate"] = [streamInformation getAverageFrameRate];
}
if ([streamInformation getRealFrameRate] != nil) {
dictionary[@"realFrameRate"] = [streamInformation getRealFrameRate];
}
if ([streamInformation getTimeBase] != nil) {
dictionary[@"timeBase"] = [streamInformation getTimeBase];
}
if ([streamInformation getCodecTimeBase] != nil) {
dictionary[@"codecTimeBase"] = [streamInformation getCodecTimeBase];
}
NSDictionary *metadataDictionary = [streamInformation getMetadataEntries];
if (metadataDictionary != nil && ([metadataDictionary count] > 0)) {
dictionary[@"metadata"] = metadataDictionary;
}
NSDictionary *sidedataDictionary = [streamInformation getSidedataEntries];
if (sidedataDictionary != nil && ([sidedataDictionary count] > 0)) {
dictionary[@"sidedata"] = sidedataDictionary;
}
}
return dictionary;
}
@end