23 #import "libavutil/ffversion.h"
76 static NSRecursiveLock *
lock;
99 typedef NS_ENUM(NSUInteger, CallbackType) {
127 - (instancetype)init:(
long)sessionId logLevel:(
int)logLevel data:(NSString*)logData {
139 - (instancetype)init:(
long)sessionId
140 videoFrameNumber:(
int)videoFrameNumber
142 quality:(
float)videoQuality
145 bitrate:(
double)bitrate
146 speed:(
double)speed {
149 _type = StatisticsType;
163 - (CallbackType)getType {
167 - (long)getSessionId {
175 - (NSString*)getLogData {
179 - (
int)getStatisticsFrameNumber {
183 - (float)getStatisticsFps {
187 - (float)getStatisticsQuality {
191 - (int64_t)getStatisticsSize {
195 - (
int)getStatisticsTime {
199 - (double)getStatisticsBitrate {
203 - (double)getStatisticsSpeed {
215 dispatch_semaphore_wait(
semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(milliSeconds * NSEC_PER_MSEC)));
235 [callbackDataArray addObject:callbackData];
247 CallbackData *callbackData = [[
CallbackData alloc] init:_sessionId videoFrameNumber:frameNumber fps:fps quality:quality size:size time:time bitrate:bitrate speed:speed];
250 [callbackDataArray addObject:callbackData];
268 newData = [callbackDataArray objectAtIndex:0];
269 [callbackDataArray removeObjectAtIndex:0];
271 }
@catch(NSException *exception) {
344 int activeLogLevel = av_log_get_level();
347 if ((activeLogLevel == LevelAVLogQuiet && level != LevelAVLogStdErr) || (level > activeLogLevel)) {
351 NSString *logData = [[NSString alloc] initWithFormat:[NSString stringWithCString:format encoding:NSUTF8StringEncoding] arguments:vargs];
353 if (logData.length > 0) {
373 void process_log(
long sessionId,
int levelValue, NSString* logMessage) {
374 int activeLogLevel = av_log_get_level();
375 Log* log = [[
Log alloc] init:sessionId:levelValue:logMessage];
376 BOOL globalCallbackDefined =
false;
377 BOOL sessionCallbackDefined =
false;
381 if ((activeLogLevel == LevelAVLogQuiet && levelValue != LevelAVLogStdErr) || (levelValue > activeLogLevel)) {
387 if (session != nil) {
388 activeLogRedirectionStrategy = [session getLogRedirectionStrategy];
389 [session addLog:log];
391 LogCallback sessionLogCallback = [session getLogCallback];
392 if (sessionLogCallback != nil) {
393 sessionCallbackDefined = TRUE;
397 sessionLogCallback(log);
399 @catch(NSException* exception) {
400 NSLog(
@"Exception thrown inside session LogCallback block. %@", [exception callStackSymbols]);
406 if (globalLogCallback != nil) {
407 globalCallbackDefined = TRUE;
411 globalLogCallback(log);
413 @catch(NSException* exception) {
414 NSLog(
@"Exception thrown inside global LogCallback block. %@", [exception callStackSymbols]);
419 switch (activeLogRedirectionStrategy) {
420 case LogRedirectionStrategyNeverPrintLogs: {
423 case LogRedirectionStrategyPrintLogsWhenGlobalCallbackNotDefined: {
424 if (globalCallbackDefined) {
429 case LogRedirectionStrategyPrintLogsWhenSessionCallbackNotDefined: {
430 if (sessionCallbackDefined) {
434 case LogRedirectionStrategyPrintLogsWhenNoCallbacksDefined: {
435 if (globalCallbackDefined || sessionCallbackDefined) {
439 case LogRedirectionStrategyAlwaysPrintLogs: {
444 switch (levelValue) {
445 case LevelAVLogQuiet:
450 NSLog(
@"%@: %@", [
FFmpegKitConfig logLevelToString:levelValue], logMessage);
455 void process_statistics(
long sessionId,
int videoFrameNumber,
float videoFps,
float videoQuality,
long size,
int time,
double bitrate,
double speed) {
457 Statistics *statistics = [[
Statistics alloc] init:sessionId videoFrameNumber:videoFrameNumber videoFps:videoFps videoQuality:videoQuality size:size time:time bitrate:bitrate speed:speed];
460 if (session != nil && [session isFFmpeg]) {
465 if (sessionStatisticsCallback != nil) {
467 sessionStatisticsCallback(statistics);
469 @catch(NSException* exception) {
470 NSLog(
@"Exception thrown inside session StatisticsCallback block. %@", [exception callStackSymbols]);
476 if (globalStatisticsCallback != nil) {
478 globalStatisticsCallback(statistics);
480 @catch(NSException* exception) {
481 NSLog(
@"Exception thrown inside global StatisticsCallback block. %@", [exception callStackSymbols]);
490 int activeLogLevel = av_log_get_level();
491 if ((activeLogLevel != LevelAVLogQuiet) && (LevelAVLogDebug <= activeLogLevel)) {
492 NSLog(
@"Async callback block started.\n");
500 if (callbackData != nil) {
502 if ([callbackData getType] == LogType) {
503 process_log([callbackData getSessionId], [callbackData getLogLevel], [callbackData getLogData]);
506 [callbackData getStatisticsFrameNumber],
507 [callbackData getStatisticsFps],
508 [callbackData getStatisticsQuality],
509 [callbackData getStatisticsSize],
510 [callbackData getStatisticsTime],
511 [callbackData getStatisticsBitrate],
512 [callbackData getStatisticsSpeed]);
521 }
@catch(NSException *exception) {
522 activeLogLevel = av_log_get_level();
523 if ((activeLogLevel != LevelAVLogQuiet) && (LevelAVLogWarning <= activeLogLevel)) {
524 NSLog(
@"Async callback block received error: %@n\n", exception);
525 NSLog(
@"%@", [exception callStackSymbols]);
531 activeLogLevel = av_log_get_level();
532 if ((activeLogLevel != LevelAVLogQuiet) && (LevelAVLogDebug <= activeLogLevel)) {
533 NSLog(
@"Async callback block stopped.\n");
538 NSString*
const LIB_NAME =
@"ffmpeg";
543 char **commandCharPArray = (
char **)av_malloc(
sizeof(
char*) * ([arguments count] + 1));
549 commandCharPArray[0] = (
char *)av_malloc(
sizeof(
char) * ([LIB_NAME length] + 1));
550 strcpy(commandCharPArray[0], [LIB_NAME UTF8String]);
553 for (
int i=0; i < [arguments count]; i++) {
554 NSString *argument = [arguments objectAtIndex:i];
555 commandCharPArray[i + 1] = (
char *) [argument UTF8String];
565 int returnCode =
ffmpeg_execute(([arguments count] + 1), commandCharPArray);
571 av_free(commandCharPArray[0]);
572 av_free(commandCharPArray);
578 NSString*
const LIB_NAME =
@"ffprobe";
583 char **commandCharPArray = (
char **)av_malloc(
sizeof(
char*) * ([arguments count] + 1));
589 commandCharPArray[0] = (
char *)av_malloc(
sizeof(
char) * ([LIB_NAME length] + 1));
590 strcpy(commandCharPArray[0], [LIB_NAME UTF8String]);
593 for (
int i=0; i < [arguments count]; i++) {
594 NSString *argument = [arguments objectAtIndex:i];
595 commandCharPArray[i + 1] = (
char *) [argument UTF8String];
605 int returnCode =
ffprobe_execute(([arguments count] + 1), commandCharPArray);
611 av_free(commandCharPArray[0]);
612 av_free(commandCharPArray);
645 lock = [[NSRecursiveLock alloc] init];
646 semaphore = dispatch_semaphore_create(0);
663 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
682 av_log_set_callback(av_log_default_callback);
688 + (
int)setFontconfigConfigurationPath:(NSString*)path {
692 + (void)setFontDirectory:(NSString*)fontDirectoryPath with:(NSDictionary*)fontNameMapping {
696 + (void)setFontDirectoryList:(NSArray*)fontDirectoryArray with:(NSDictionary*)fontNameMapping {
697 NSError *error = nil;
698 BOOL isDirectory = YES;
700 int validFontNameMappingCount = 0;
701 NSString *tempConfigurationDirectory = [NSTemporaryDirectory() stringByAppendingPathComponent:@"fontconfig"];
702 NSString *fontConfigurationFile = [tempConfigurationDirectory stringByAppendingPathComponent:@"fonts.conf"];
704 if (![[NSFileManager defaultManager] fileExistsAtPath:tempConfigurationDirectory isDirectory:&isDirectory]) {
705 if (![[NSFileManager defaultManager] createDirectoryAtPath:tempConfigurationDirectory withIntermediateDirectories:YES attributes:nil error:&error]) {
706 NSLog(
@"Failed to set font directory. Error received while creating temp conf directory: %@.", error);
709 NSLog(
@"Created temporary font conf directory: TRUE.");
712 if ([[NSFileManager defaultManager] fileExistsAtPath:fontConfigurationFile isDirectory:&isFile]) {
713 BOOL fontConfigurationDeleted = [[NSFileManager defaultManager] removeItemAtPath:fontConfigurationFile error:nil];
714 NSLog(
@"Deleted old temporary font configuration: %s.", fontConfigurationDeleted?
"TRUE":
"FALSE");
718 NSString *fontNameMappingBlock =
@"";
719 for (NSString *fontName in [fontNameMapping allKeys]) {
720 NSString *mappedFontName = [fontNameMapping objectForKey:fontName];
722 if ((fontName != nil) && (mappedFontName != nil) && ([fontName length] > 0) && ([mappedFontName length] > 0)) {
724 fontNameMappingBlock = [NSString stringWithFormat:@"%@\n%@\n%@%@%@\n%@\n%@\n%@%@%@\n%@\n%@\n",
725 @" <match target=\"pattern\">",
726 @" <test qual=\"any\" name=\"family\">",
727 @" <string>", fontName, @"</string>",
729 @" <edit name=\"family\" mode=\"assign\" binding=\"same\">",
730 @" <string>", mappedFontName, @"</string>",
734 validFontNameMappingCount++;
738 NSMutableString *fontConfiguration = [NSMutableString stringWithFormat:@"%@\n%@\n%@\n%@\n",
739 @"<?xml version=\"1.0\"?>",
740 @"<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">",
742 @" <dir prefix=\"cwd\">.</dir>"];
743 for (
int i=0; i < [fontDirectoryArray count]; i++) {
744 NSString *fontDirectoryPath = [fontDirectoryArray objectAtIndex:i];
745 [fontConfiguration appendString: @" <dir>"];
746 [fontConfiguration appendString: fontDirectoryPath];
747 [fontConfiguration appendString: @"</dir>"];
749 [fontConfiguration appendString:fontNameMappingBlock];
750 [fontConfiguration appendString:@"</fontconfig>"];
752 if (![fontConfiguration writeToFile:fontConfigurationFile atomically:YES encoding:NSUTF8StringEncoding error:&error]) {
753 NSLog(
@"Failed to set font directory. Error received while saving font configuration: %@.", error);
757 NSLog(
@"Saved new temporary font configuration with %d font name mappings.", validFontNameMappingCount);
761 for (
int i=0; i < [fontDirectoryArray count]; i++) {
762 NSString *fontDirectoryPath = [fontDirectoryArray objectAtIndex:i];
763 NSLog(
@"Font directory %@ registered successfully.", fontDirectoryPath);
768 NSError *error = nil;
772 NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
773 NSString *pipesDir = [cacheDir stringByAppendingPathComponent:@"pipes"];
775 if (![[NSFileManager defaultManager] fileExistsAtPath:pipesDir isDirectory:&isDirectory]) {
776 if (![[NSFileManager defaultManager] createDirectoryAtPath:pipesDir withIntermediateDirectories:YES attributes:nil error:&error]) {
777 NSLog(
@"Failed to create pipes directory: %@. Operation failed with %@.", pipesDir, error);
782 NSString *newFFmpegPipePath = [NSString stringWithFormat:@"%@/%@%ld", pipesDir, FFmpegKitNamedPipePrefix, [pipeIndexGenerator
getAndIncrement]];
787 int rc = mkfifo([newFFmpegPipePath UTF8String], S_IRWXU | S_IRWXG | S_IROTH);
789 return newFFmpegPipePath;
791 NSLog(
@"Failed to register new FFmpeg pipe %@. Operation failed with rc=%d.", newFFmpegPipePath, rc);
796 + (void)closeFFmpegPipe:(NSString*)ffmpegPipePath {
797 NSFileManager *fileManager = [NSFileManager defaultManager];
799 if ([fileManager fileExistsAtPath:ffmpegPipePath]){
800 [fileManager removeItemAtPath:ffmpegPipePath error:nil];
805 return [NSString stringWithUTF8String:FFMPEG_VERSION];
810 return [NSString stringWithFormat:@"%@-lts", FFmpegKitVersion];
817 #if defined(FFMPEG_KIT_LTS)
826 sprintf(buildDate,
"%d", FFMPEG_KIT_BUILD_DATE);
827 return [NSString stringWithUTF8String:buildDate];
830 + (
int)setEnvironmentVariable:(NSString*)variableName value:(NSString*)variableValue {
831 return setenv([variableName UTF8String], [variableValue UTF8String],
true);
834 + (void)ignoreSignal:(Signal)signal {
835 if (signal == SignalQuit) {
837 }
else if (signal == SignalInt) {
839 }
else if (signal == SignalTerm) {
841 }
else if (signal == SignalXcpu) {
843 }
else if (signal == SignalPipe) {
853 int returnCode =
executeFFmpeg([ffmpegSession getSessionId], [ffmpegSession getArguments]);
855 }
@catch (NSException *exception) {
856 [ffmpegSession
fail:exception];
857 NSLog(
@"FFmpeg execute failed: %@.%@", [
FFmpegKit argumentsToString:[ffmpegSession getArguments]], [NSString stringWithFormat:
@"%@", [exception callStackSymbols]]);
866 int returnCode =
executeFFprobe([ffprobeSession getSessionId], [ffprobeSession getArguments]);
868 }
@catch (NSException *exception) {
869 [ffprobeSession
fail:exception];
870 NSLog(
@"FFprobe execute failed: %@.%@", [
FFmpegKit argumentsToString:[ffprobeSession getArguments]], [NSString stringWithFormat:
@"%@", [exception callStackSymbols]]);
874 + (void)getMediaInformationExecute:(
MediaInformationSession*)mediaInformationSession withTimeout:(
int)waitTimeout {
879 int returnCodeValue =
executeFFprobe([mediaInformationSession getSessionId], [mediaInformationSession getArguments]);
881 [mediaInformationSession
complete:returnCode];
882 if ([returnCode isSuccess]) {
886 }
@catch (NSException *exception) {
887 [mediaInformationSession
fail:exception];
888 NSLog(
@"Get media information execute failed: %@.%@", [
FFmpegKit argumentsToString:[mediaInformationSession getArguments]], [NSString stringWithFormat:
@"%@", [exception callStackSymbols]]);
896 + (void)asyncFFmpegExecute:(
FFmpegSession*)ffmpegSession onDispatchQueue:(dispatch_queue_t)queue {
899 dispatch_async(queue, ^{
902 if (globalExecuteCallback != nil) {
903 globalExecuteCallback(ffmpegSession);
907 if (sessionExecuteCallback != nil) {
908 sessionExecuteCallback(ffmpegSession);
917 + (void)asyncFFprobeExecute:(
FFprobeSession*)ffprobeSession onDispatchQueue:(dispatch_queue_t)queue {
920 dispatch_async(queue, ^{
923 if (globalExecuteCallback != nil) {
924 globalExecuteCallback(ffprobeSession);
928 if (sessionExecuteCallback != nil) {
929 sessionExecuteCallback(ffprobeSession);
934 + (void)asyncGetMediaInformationExecute:(
MediaInformationSession*)mediaInformationSession withTimeout:(
int)waitTimeout {
938 + (void)asyncGetMediaInformationExecute:(
MediaInformationSession*)mediaInformationSession onDispatchQueue:(dispatch_queue_t)queue withTimeout:(
int)waitTimeout {
941 dispatch_async(queue, ^{
944 if (globalExecuteCallback != nil) {
945 globalExecuteCallback(mediaInformationSession);
949 if (sessionExecuteCallback != nil) {
950 sessionExecuteCallback(mediaInformationSession);
975 + (void)setLogLevel:(
int)level {
979 + (NSString*)logLevelToString:(
int)level {
981 case LevelAVLogStdErr:
return @"STDERR";
982 case LevelAVLogTrace:
return @"TRACE";
983 case LevelAVLogDebug:
return @"DEBUG";
984 case LevelAVLogVerbose:
return @"VERBOSE";
985 case LevelAVLogInfo:
return @"INFO";
986 case LevelAVLogWarning:
return @"WARNING";
987 case LevelAVLogError:
return @"ERROR";
988 case LevelAVLogFatal:
return @"FATAL";
989 case LevelAVLogPanic:
return @"PANIC";
990 case LevelAVLogQuiet:
return @"QUIET";
999 + (void)setSessionHistorySize:(
int)pSessionHistorySize {
1005 @throw([NSException exceptionWithName:NSInvalidArgumentException reason:@"Session history size must not exceed the hard limit!" userInfo:nil]);
1006 }
else if (pSessionHistorySize > 0) {
1012 NSNumber* sessionIdNumber = [NSNumber numberWithLong:[session
getSessionId]];
1014 [sessionHistoryLock lock];
1021 [sessionHistoryMap setObject:session forKey:sessionIdNumber];
1022 [sessionHistoryList addObject:session];
1024 id<Session> first = [sessionHistoryList firstObject];
1026 NSNumber* key = [NSNumber numberWithLong:[first getSessionId]];
1027 [sessionHistoryList removeObject:key];
1028 [sessionHistoryMap removeObjectForKey:key];
1033 [sessionHistoryLock unlock];
1036 + (id<
Session>)getSession:(
long)sessionId {
1037 [sessionHistoryLock lock];
1039 id<Session> session = [sessionHistoryMap objectForKey:[NSNumber numberWithLong:sessionId]];
1041 [sessionHistoryLock unlock];
1047 [sessionHistoryLock lock];
1049 id<Session> lastSession = [sessionHistoryList lastObject];
1051 [sessionHistoryLock unlock];
1057 id<Session> lastCompletedSession = nil;
1059 [sessionHistoryLock lock];
1062 id<Session> session = [sessionHistoryList objectAtIndex:i];
1063 if ([session getState] == SessionStateCompleted) {
1064 lastCompletedSession = session;
1069 [sessionHistoryLock unlock];
1071 return lastCompletedSession;
1075 [sessionHistoryLock lock];
1077 NSArray* sessionsCopy = [sessionHistoryList copy];
1079 [sessionHistoryLock unlock];
1081 return sessionsCopy;
1085 NSMutableArray* ffmpegSessions = [[NSMutableArray alloc] init];
1087 [sessionHistoryLock lock];
1089 for(
int i = 0; i < [sessionHistoryList count]; i++) {
1090 id<Session> session = [sessionHistoryList objectAtIndex:i];
1091 if ([session isFFmpeg]) {
1092 [ffmpegSessions addObject:session];
1096 [sessionHistoryLock unlock];
1098 return ffmpegSessions;
1102 NSMutableArray* ffprobeSessions = [[NSMutableArray alloc] init];
1104 [sessionHistoryLock lock];
1106 for(
int i = 0; i < [sessionHistoryList count]; i++) {
1107 id<Session> session = [sessionHistoryList objectAtIndex:i];
1108 if ([session isFFprobe]) {
1109 [ffprobeSessions addObject:session];
1113 [sessionHistoryLock unlock];
1115 return ffprobeSessions;
1118 + (NSArray*)getSessionsByState:(SessionState)state {
1119 NSMutableArray* sessions = [[NSMutableArray alloc] init];
1121 [sessionHistoryLock lock];
1123 for(
int i = 0; i < [sessionHistoryList count]; i++) {
1124 id<Session> session = [sessionHistoryList objectAtIndex:i];
1125 if ([session getState] == state) {
1126 [sessions addObject:session];
1130 [sessionHistoryLock unlock];
1139 + (void)setLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
1143 + (
int)messagesInTransmit:(
long)sessionId {
1147 + (NSString*)sessionStateToString:(SessionState)state {
1149 case SessionStateCreated:
return @"CREATED";
1150 case SessionStateRunning:
return @"RUNNING";
1151 case SessionStateFailed:
return @"FAILED";
1152 case SessionStateCompleted:
return @"COMPLETED";
1153 default:
return @"";
void(^ ExecuteCallback)(id< Session > session)
int executeFFprobe(long sessionId, NSArray *arguments)
void callbackWait(int milliSeconds)
void ffmpegkit_log_callback_function(void *ptr, int level, const char *format, va_list vargs)
static atomic_short sessionMap[SESSION_MAP_SIZE]
void cancelSession(long sessionId)
static volatile NSMutableDictionary * sessionHistoryMap
static int sessionHistorySize
static NSRecursiveLock * lock
volatile int handleSIGINT
int ffprobe_execute(int argc, char **argv)
volatile int handleSIGTERM
void process_log(long sessionId, int levelValue, NSString *logMessage)
void ffmpegkit_statistics_callback_function(int frameNumber, float fps, float quality, int64_t size, int time, double bitrate, double speed)
static dispatch_queue_t asyncDispatchQueue
void removeSession(long sessionId)
void statisticsCallbackDataAdd(int frameNumber, float fps, float quality, int64_t size, int time, double bitrate, double speed)
static LogCallback logCallback
void addSession(long sessionId)
CallbackData * callbackDataRemove()
__thread volatile long _sessionId
void callbackBlockFunction()
double _statisticsBitrate
static NSRecursiveLock * sessionHistoryLock
static ExecuteCallback executeCallback
static dispatch_semaphore_t semaphore
static int redirectionEnabled
void resetMessagesInTransmit(long sessionId)
static NSMutableArray * callbackDataArray
int _statisticsFrameNumber
void process_statistics(long sessionId, int videoFrameNumber, float videoFps, float videoQuality, long size, int time, double bitrate, double speed)
int cancelRequested(long sessionId)
volatile int handleSIGPIPE
const int SESSION_MAP_SIZE
NSString *const FFmpegKitVersion
void logCallbackDataAdd(int level, NSString *logData)
volatile int handleSIGXCPU
static LogRedirectionStrategy globalLogRedirectionStrategy
static StatisticsCallback statisticsCallback
static atomic_int sessionInTransitMessageCountMap[SESSION_MAP_SIZE]
static AtomicLong * pipeIndexGenerator
static NSMutableArray * sessionHistoryList
typedef NS_ENUM(NSUInteger, CallbackType)
NSString *const FFmpegKitNamedPipePrefix
int ffmpeg_execute(int argc, char **argv)
volatile int handleSIGQUIT
int executeFFmpeg(long sessionId, NSArray *arguments)
void(^ LogCallback)(Log *log)
void(^ StatisticsCallback)(Statistics *statistics)
void fail:(NSException *exception)
void complete:(ReturnCode *returnCode)
ExecuteCallback getExecuteCallback()
NSString * getAllLogsAsStringWithTimeout:(int waitTimeout)
void addSession:(id< Session > session)
void asyncFFmpegExecute:onDispatchQueue:(FFmpegSession *ffmpegSession,[onDispatchQueue] dispatch_queue_t queue)
NSArray * getFFmpegSessions()
LogRedirectionStrategy getLogRedirectionStrategy()
void getMediaInformationExecute:withTimeout:(MediaInformationSession *mediaInformationSession,[withTimeout] int waitTimeout)
void disableRedirection()
NSString * registerNewFFmpegPipe()
int setFontconfigConfigurationPath:(NSString *path)
ExecuteCallback getExecuteCallback()
NSString * getBuildDate()
void ffprobeExecute:(FFprobeSession *ffprobeSession)
NSArray * getFFprobeSessions()
void ffmpegExecute:(FFmpegSession *ffmpegSession)
int getSessionHistorySize()
id< Session > getSession:(long sessionId)
int setEnvironmentVariable:value:(NSString *variableName,[value] NSString *variableValue)
id< Session > getLastSession()
void asyncFFprobeExecute:onDispatchQueue:(FFprobeSession *ffprobeSession,[onDispatchQueue] dispatch_queue_t queue)
NSString * getFFmpegVersion()
void asyncGetMediaInformationExecute:onDispatchQueue:withTimeout:(MediaInformationSession *mediaInformationSession,[onDispatchQueue] dispatch_queue_t queue,[withTimeout] int waitTimeout)
void setFontDirectoryList:with:(NSArray *fontDirectoryList,[with] NSDictionary *fontNameMapping)
void closeFFmpegPipe:(NSString *ffmpegPipePath)
id< Session > getLastCompletedSession()
StatisticsCallback getStatisticsCallback()
void addStatistics:(Statistics *statistics)