FFmpegKit iOS / macOS / tvOS API 6.0
Loading...
Searching...
No Matches
FFmpegKitConfig.m
Go to the documentation of this file.
1/*
2 * Copyright (c) 2018-2022 Taner Sener
3 *
4 * This file is part of FFmpegKit.
5 *
6 * FFmpegKit 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 * FFmpegKit 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 FFmpegKit. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#import <stdatomic.h>
21#import <sys/types.h>
22#import <sys/stat.h>
23#import "libavutil/ffversion.h"
24#import "libavutil/bprint.h"
25#import "fftools_ffmpeg.h"
26#import "ArchDetect.h"
27#import "AtomicLong.h"
28#import "FFmpegKit.h"
29#import "FFmpegKitConfig.h"
30#import "FFmpegSession.h"
31#import "FFprobeKit.h"
32#import "FFprobeSession.h"
33#import "Level.h"
36#import "SessionState.h"
37
39NSString* const FFmpegKitVersion = @"6.0";
40
44NSString* const FFmpegKitNamedPipePrefix = @"fk_pipe_";
45
50
51/* Session history variables */
53static volatile NSMutableDictionary* sessionHistoryMap;
54static NSMutableArray* sessionHistoryList;
55static NSRecursiveLock* sessionHistoryLock;
56
58#define SESSION_MAP_SIZE 1000
59static atomic_short sessionMap[SESSION_MAP_SIZE];
60static atomic_int sessionInTransitMessageCountMap[SESSION_MAP_SIZE];
61
62static dispatch_queue_t asyncDispatchQueue;
63
66
69
74
75static LogRedirectionStrategy globalLogRedirectionStrategy;
76
79static NSRecursiveLock *lock;
80static dispatch_semaphore_t semaphore;
81static NSMutableArray *callbackDataArray;
82
84volatile int handleSIGQUIT = 1;
85volatile int handleSIGINT = 1;
86volatile int handleSIGTERM = 1;
87volatile int handleSIGXCPU = 1;
88volatile int handleSIGPIPE = 1;
89
91__thread long globalSessionId = 0;
92
94int configuredLogLevel = LevelAVLogInfo;
95
97int ffmpeg_execute(int argc, char **argv);
98
100int ffprobe_execute(int argc, char **argv);
101
102typedef NS_ENUM(NSUInteger, CallbackType) {
103 LogType,
104 StatisticsType
105};
106
108 while ([sessionHistoryList count] > sessionHistorySize) {
109 id<Session> first = [sessionHistoryList firstObject];
110 if (first != nil) {
111 [sessionHistoryList removeObjectAtIndex:0];
112 [sessionHistoryMap removeObjectForKey:[NSNumber numberWithLong:[first getSessionId]]];
113 }
114 }
115}
116
117void addSessionToSessionHistory(id<Session> session) {
118 NSNumber* sessionIdNumber = [NSNumber numberWithLong:[session getSessionId]];
119
120 [sessionHistoryLock lock];
121
122 /*
123 * ASYNC SESSIONS CALL THIS METHOD TWICE
124 * THIS CHECK PREVENTS ADDING THE SAME SESSION AGAIN
125 */
126 if ([sessionHistoryMap objectForKey:sessionIdNumber] == nil) {
127 [sessionHistoryMap setObject:session forKey:sessionIdNumber];
128 [sessionHistoryList addObject:session];
130 }
131
132 [sessionHistoryLock unlock];
133}
134
138@interface CallbackData : NSObject
139
140@end
141
142@implementation CallbackData {
143 CallbackType _type;
144 long _sessionId; // session id
145
146 int _logLevel; // log level
147 AVBPrint _logData; // log data
148
149 int _statisticsFrameNumber; // statistics frame number
150 float _statisticsFps; // statistics fps
151 float _statisticsQuality; // statistics quality
152 int64_t _statisticsSize; // statistics size
153 double _statisticsTime; // statistics time
154 double _statisticsBitrate; // statistics bitrate
155 double _statisticsSpeed; // statistics speed
156}
157
158 - (instancetype)init:(long)sessionId logLevel:(int)logLevel data:(AVBPrint*)data {
159 self = [super init];
160 if (self) {
161 _type = LogType;
162 _sessionId = sessionId;
163 _logLevel = logLevel;
164 av_bprint_init(&_logData, 0, AV_BPRINT_SIZE_UNLIMITED);
165 av_bprintf(&_logData, "%s", data->str);
166 }
167
168 return self;
169}
170
171 - (instancetype)init:(long)sessionId
172 videoFrameNumber:(int)videoFrameNumber
173 fps:(float)videoFps
174 quality:(float)videoQuality
175 size:(int64_t)size
176 time:(double)time
177 bitrate:(double)bitrate
178 speed:(double)speed {
179 self = [super init];
180 if (self) {
181 _type = StatisticsType;
182 _sessionId = sessionId;
183 _statisticsFrameNumber = videoFrameNumber;
184 _statisticsFps = videoFps;
185 _statisticsQuality = videoQuality;
186 _statisticsSize = size;
187 _statisticsTime = time;
188 _statisticsBitrate = bitrate;
189 _statisticsSpeed = speed;
190 }
191
192 return self;
193}
194
195- (CallbackType)getType {
196 return _type;
197}
198
199- (long)getSessionId {
200 return _sessionId;
201}
202
203- (int)getLogLevel {
204 return _logLevel;
205}
206
207- (AVBPrint*)getLogData {
208 return &_logData;
209}
210
211- (int)getStatisticsFrameNumber {
213}
214
215- (float)getStatisticsFps {
216 return _statisticsFps;
217}
218
219- (float)getStatisticsQuality {
220 return _statisticsQuality;
221}
222
223- (int64_t)getStatisticsSize {
224 return _statisticsSize;
225}
226
227- (double)getStatisticsTime {
228 return _statisticsTime;
229}
230
231- (double)getStatisticsBitrate {
232 return _statisticsBitrate;
233}
234
235- (double)getStatisticsSpeed {
236 return _statisticsSpeed;
237}
238
239@end
240
246void callbackWait(int milliSeconds) {
247 dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(milliSeconds * NSEC_PER_MSEC)));
248}
249
254 dispatch_semaphore_signal(semaphore);
255}
256
257static const char *avutil_log_get_level_str(int level) {
258 switch (level) {
259 case AV_LOG_STDERR:
260 return "stderr";
261 case AV_LOG_QUIET:
262 return "quiet";
263 case AV_LOG_DEBUG:
264 return "debug";
265 case AV_LOG_VERBOSE:
266 return "verbose";
267 case AV_LOG_INFO:
268 return "info";
269 case AV_LOG_WARNING:
270 return "warning";
271 case AV_LOG_ERROR:
272 return "error";
273 case AV_LOG_FATAL:
274 return "fatal";
275 case AV_LOG_PANIC:
276 return "panic";
277 default:
278 return "";
279 }
280}
281
282static void avutil_log_format_line(void *avcl, int level, const char *fmt, va_list vl, AVBPrint part[4], int *print_prefix) {
283 int flags = av_log_get_flags();
284 AVClass* avc = avcl ? *(AVClass **) avcl : NULL;
285 av_bprint_init(part+0, 0, 1);
286 av_bprint_init(part+1, 0, 1);
287 av_bprint_init(part+2, 0, 1);
288 av_bprint_init(part+3, 0, 65536);
289
290 if (*print_prefix && avc) {
291 if (avc->parent_log_context_offset) {
292 AVClass** parent = *(AVClass ***) (((uint8_t *) avcl) +
293 avc->parent_log_context_offset);
294 if (parent && *parent) {
295 av_bprintf(part+0, "[%s @ %p] ",
296 (*parent)->item_name(parent), parent);
297 }
298 }
299 av_bprintf(part+1, "[%s @ %p] ",
300 avc->item_name(avcl), avcl);
301 }
302
303 if (*print_prefix && (level > AV_LOG_QUIET) && (flags & AV_LOG_PRINT_LEVEL))
304 av_bprintf(part+2, "[%s] ", avutil_log_get_level_str(level));
305
306 av_vbprintf(part+3, fmt, vl);
307
308 if(*part[0].str || *part[1].str || *part[2].str || *part[3].str) {
309 char lastc = part[3].len && part[3].len <= part[3].size ? part[3].str[part[3].len - 1] : 0;
310 *print_prefix = lastc == '\n' || lastc == '\r';
311 }
312}
313
314static void avutil_log_sanitize(uint8_t *line) {
315 while(*line){
316 if(*line < 0x08 || (*line > 0x0D && *line < 0x20))
317 *line='?';
318 line++;
319 }
320}
321
328void logCallbackDataAdd(int level, AVBPrint *data) {
329 CallbackData* callbackData = [[CallbackData alloc] init:globalSessionId logLevel:level data:data];
330
331 [lock lock];
332 [callbackDataArray addObject:callbackData];
333 [lock unlock];
334
336
338}
339
343void statisticsCallbackDataAdd(int frameNumber, float fps, float quality, int64_t size, double time, double bitrate, double speed) {
344 CallbackData *callbackData = [[CallbackData alloc] init:globalSessionId videoFrameNumber:frameNumber fps:fps quality:quality size:size time:time bitrate:bitrate speed:speed];
345
346 [lock lock];
347 [callbackDataArray addObject:callbackData];
348 [lock unlock];
349
351
353}
354
359 CallbackData *newData = nil;
360
361 [lock lock];
362
363 @try {
364 if ([callbackDataArray count] > 0) {
365 newData = [callbackDataArray objectAtIndex:0];
366 [callbackDataArray removeObjectAtIndex:0];
367 }
368 } @catch(NSException *exception) {
369 // DO NOTHING
370 } @finally {
371 [lock unlock];
372 }
373
374 return newData;
375}
376
382void registerSessionId(long sessionId) {
383 atomic_store(&sessionMap[sessionId % SESSION_MAP_SIZE], 1);
384}
385
391void removeSession(long sessionId) {
392 atomic_store(&sessionMap[sessionId % SESSION_MAP_SIZE], 0);
393}
394
400void cancelSession(long sessionId) {
401 atomic_store(&sessionMap[sessionId % SESSION_MAP_SIZE], 2);
402}
403
410int cancelRequested(long sessionId) {
411 if (atomic_load(&sessionMap[sessionId % SESSION_MAP_SIZE]) == 2) {
412 return 1;
413 } else {
414 return 0;
415 }
416}
417
423void resetMessagesInTransmit(long sessionId) {
424 atomic_store(&sessionInTransitMessageCountMap[sessionId % SESSION_MAP_SIZE], 0);
425}
426
435void ffmpegkit_log_callback_function(void *ptr, int level, const char* format, va_list vargs) {
436 AVBPrint fullLine;
437 AVBPrint part[4];
438 int print_prefix = 1;
439
440 // DO NOT PROCESS UNWANTED LOGS
441 if (level >= 0) {
442 level &= 0xff;
443 }
444 int activeLogLevel = av_log_get_level();
445
446 // LevelAVLogStdErr logs are always redirected
447 if ((activeLogLevel == LevelAVLogQuiet && level != LevelAVLogStdErr) || (level > activeLogLevel)) {
448 return;
449 }
450
451 av_bprint_init(&fullLine, 0, AV_BPRINT_SIZE_UNLIMITED);
452
453 avutil_log_format_line(ptr, level, format, vargs, part, &print_prefix);
454 avutil_log_sanitize(part[0].str);
455 avutil_log_sanitize(part[1].str);
456 avutil_log_sanitize(part[2].str);
457 avutil_log_sanitize(part[3].str);
458
459 // COMBINE ALL 4 LOG PARTS
460 av_bprintf(&fullLine, "%s%s%s%s", part[0].str, part[1].str, part[2].str, part[3].str);
461
462 if (fullLine.len > 0) {
463 logCallbackDataAdd(level, &fullLine);
464 }
465
466 av_bprint_finalize(part, NULL);
467 av_bprint_finalize(part+1, NULL);
468 av_bprint_finalize(part+2, NULL);
469 av_bprint_finalize(part+3, NULL);
470 av_bprint_finalize(&fullLine, NULL);
471}
472
484void ffmpegkit_statistics_callback_function(int frameNumber, float fps, float quality, int64_t size, double time, double bitrate, double speed) {
485 statisticsCallbackDataAdd(frameNumber, fps, quality, size, time, bitrate, speed);
486}
487
488void process_log(long sessionId, int levelValue, AVBPrint* logMessage) {
489 int activeLogLevel = av_log_get_level();
490 NSString* message = [NSString stringWithCString:logMessage->str encoding:NSUTF8StringEncoding];
491 if (message == nil) {
492 // WE DROP LOGS THAT WE CANNOT DISPLAY
493 return;
494 }
495 Log* log = [[Log alloc] init:sessionId:levelValue:message];
496 BOOL globalCallbackDefined = false;
497 BOOL sessionCallbackDefined = false;
498 LogRedirectionStrategy activeLogRedirectionStrategy = globalLogRedirectionStrategy;
499
500 // LevelAVLogStdErr logs are always redirected
501 if ((activeLogLevel == LevelAVLogQuiet && levelValue != LevelAVLogStdErr) || (levelValue > activeLogLevel)) {
502 // LOG NEITHER PRINTED NOR FORWARDED
503 return;
504 }
505
506 id<Session> session = [FFmpegKitConfig getSession:sessionId];
507 if (session != nil) {
508 activeLogRedirectionStrategy = [session getLogRedirectionStrategy];
509 [session addLog:log];
510
511 LogCallback sessionLogCallback = [session getLogCallback];
512 if (sessionLogCallback != nil) {
513 sessionCallbackDefined = TRUE;
514
515 @try {
516 // NOTIFY SESSION CALLBACK DEFINED
517 sessionLogCallback(log);
518 }
519 @catch(NSException* exception) {
520 NSLog(@"Exception thrown inside session log callback. %@", [exception callStackSymbols]);
521 }
522 }
523 }
524
525 LogCallback globalLogCallback = logCallback;
526 if (globalLogCallback != nil) {
527 globalCallbackDefined = TRUE;
528
529 @try {
530 // NOTIFY GLOBAL CALLBACK DEFINED
531 globalLogCallback(log);
532 }
533 @catch(NSException* exception) {
534 NSLog(@"Exception thrown inside global log callback. %@", [exception callStackSymbols]);
535 }
536 }
537
538 // EXECUTE THE LOG STRATEGY
539 switch (activeLogRedirectionStrategy) {
540 case LogRedirectionStrategyNeverPrintLogs: {
541 return;
542 }
543 case LogRedirectionStrategyPrintLogsWhenGlobalCallbackNotDefined: {
544 if (globalCallbackDefined) {
545 return;
546 }
547 }
548 break;
549 case LogRedirectionStrategyPrintLogsWhenSessionCallbackNotDefined: {
550 if (sessionCallbackDefined) {
551 return;
552 }
553 }
554 break;
555 case LogRedirectionStrategyPrintLogsWhenNoCallbacksDefined: {
556 if (globalCallbackDefined || sessionCallbackDefined) {
557 return;
558 }
559 }
560 break;
561 case LogRedirectionStrategyAlwaysPrintLogs: {
562 }
563 break;
564 }
565
566 // PRINT LOGS
567 switch (levelValue) {
568 case LevelAVLogQuiet:
569 // PRINT NO OUTPUT
570 break;
571 default:
572 // WRITE TO NSLOG
573 NSLog(@"%@: %@", [FFmpegKitConfig logLevelToString:levelValue], [NSString stringWithCString:logMessage->str encoding:NSUTF8StringEncoding]);
574 break;
575 }
576}
577
578void process_statistics(long sessionId, int videoFrameNumber, float videoFps, float videoQuality, long size, double time, double bitrate, double speed) {
579
580 Statistics *statistics = [[Statistics alloc] init:sessionId videoFrameNumber:videoFrameNumber videoFps:videoFps videoQuality:videoQuality size:size time:time bitrate:bitrate speed:speed];
581
582 id<Session> session = [FFmpegKitConfig getSession:sessionId];
583 if (session != nil && [session isFFmpeg]) {
584 FFmpegSession *ffmpegSession = (FFmpegSession*)session;
585 [ffmpegSession addStatistics:statistics];
586
587 StatisticsCallback sessionStatisticsCallback = [ffmpegSession getStatisticsCallback];
588 if (sessionStatisticsCallback != nil) {
589 @try {
590 sessionStatisticsCallback(statistics);
591 }
592 @catch(NSException* exception) {
593 NSLog(@"Exception thrown inside session statistics callback. %@", [exception callStackSymbols]);
594 }
595 }
596 }
597
598 StatisticsCallback globalStatisticsCallback = statisticsCallback;
599 if (globalStatisticsCallback != nil) {
600 @try {
601 globalStatisticsCallback(statistics);
602 }
603 @catch(NSException* exception) {
604 NSLog(@"Exception thrown inside global statistics callback. %@", [exception callStackSymbols]);
605 }
606 }
607}
608
613 int activeLogLevel = av_log_get_level();
614 if ((activeLogLevel != LevelAVLogQuiet) && (LevelAVLogDebug <= activeLogLevel)) {
615 NSLog(@"Async callback block started.\n");
616 }
617
618 while(redirectionEnabled) {
619 @autoreleasepool {
620 @try {
621
622 CallbackData *callbackData = callbackDataRemove();
623 if (callbackData != nil) {
624
625 if ([callbackData getType] == LogType) {
626 process_log([callbackData getSessionId], [callbackData getLogLevel], [callbackData getLogData]);
627 av_bprint_finalize([callbackData getLogData], NULL);
628 } else {
629 process_statistics([callbackData getSessionId],
630 [callbackData getStatisticsFrameNumber],
631 [callbackData getStatisticsFps],
632 [callbackData getStatisticsQuality],
633 [callbackData getStatisticsSize],
634 [callbackData getStatisticsTime],
635 [callbackData getStatisticsBitrate],
636 [callbackData getStatisticsSpeed]);
637 }
638
639 atomic_fetch_sub(&sessionInTransitMessageCountMap[[callbackData getSessionId] % SESSION_MAP_SIZE], 1);
640
641 } else {
642 callbackWait(100);
643 }
644
645 } @catch(NSException *exception) {
646 activeLogLevel = av_log_get_level();
647 if ((activeLogLevel != LevelAVLogQuiet) && (LevelAVLogWarning <= activeLogLevel)) {
648 NSLog(@"Async callback block received error: %@n\n", exception);
649 NSLog(@"%@", [exception callStackSymbols]);
650 }
651 }
652 }
653 }
654
655 activeLogLevel = av_log_get_level();
656 if ((activeLogLevel != LevelAVLogQuiet) && (LevelAVLogDebug <= activeLogLevel)) {
657 NSLog(@"Async callback block stopped.\n");
658 }
659}
660
661int executeFFmpeg(long sessionId, NSArray* arguments) {
662 NSString* const LIB_NAME = @"ffmpeg";
663
664 // SETS DEFAULT LOG LEVEL BEFORE STARTING A NEW RUN
665 av_log_set_level(configuredLogLevel);
666
667 char **commandCharPArray = (char **)av_malloc(sizeof(char*) * ([arguments count] + 1));
668
669 /* PRESERVE USAGE FORMAT
670 *
671 * ffmpeg <arguments>
672 */
673 commandCharPArray[0] = (char *)av_malloc(sizeof(char) * ([LIB_NAME length] + 1));
674 strcpy(commandCharPArray[0], [LIB_NAME UTF8String]);
675
676 // PREPARE ARRAY ELEMENTS
677 for (int i=0; i < [arguments count]; i++) {
678 NSString *argument = [arguments objectAtIndex:i];
679 commandCharPArray[i + 1] = (char *) [argument UTF8String];
680 }
681
682 // REGISTER THE ID BEFORE STARTING THE SESSION
683 globalSessionId = sessionId;
684 registerSessionId(sessionId);
685
686 resetMessagesInTransmit(sessionId);
687
688 // RUN
689 int returnCode = ffmpeg_execute(([arguments count] + 1), commandCharPArray);
690
691 // ALWAYS REMOVE THE ID FROM THE MAP
692 removeSession(sessionId);
693
694 // CLEANUP
695 av_free(commandCharPArray[0]);
696 av_free(commandCharPArray);
697
698 return returnCode;
699}
700
701int executeFFprobe(long sessionId, NSArray* arguments) {
702 NSString* const LIB_NAME = @"ffprobe";
703
704 // SETS DEFAULT LOG LEVEL BEFORE STARTING A NEW RUN
705 av_log_set_level(configuredLogLevel);
706
707 char **commandCharPArray = (char **)av_malloc(sizeof(char*) * ([arguments count] + 1));
708
709 /* PRESERVE USAGE FORMAT
710 *
711 * ffprobe <arguments>
712 */
713 commandCharPArray[0] = (char *)av_malloc(sizeof(char) * ([LIB_NAME length] + 1));
714 strcpy(commandCharPArray[0], [LIB_NAME UTF8String]);
715
716 // PREPARE ARRAY ELEMENTS
717 for (int i=0; i < [arguments count]; i++) {
718 NSString *argument = [arguments objectAtIndex:i];
719 commandCharPArray[i + 1] = (char *) [argument UTF8String];
720 }
721
722 // REGISTER THE ID BEFORE STARTING THE SESSION
723 globalSessionId = sessionId;
724 registerSessionId(sessionId);
725
726 resetMessagesInTransmit(sessionId);
727
728 // RUN
729 int returnCode = ffprobe_execute(([arguments count] + 1), commandCharPArray);
730
731 // ALWAYS REMOVE THE ID FROM THE MAP
732 removeSession(sessionId);
733
734 // CLEANUP
735 av_free(commandCharPArray[0]);
736 av_free(commandCharPArray);
737
738 return returnCode;
739}
740
741@implementation FFmpegKitConfig
742
743+ (void)initialize {
744 [ArchDetect class];
745 [FFmpegKit class];
746 [FFprobeKit class];
747
748 pipeIndexGenerator = [[AtomicLong alloc] initWithValue:1];
749
751 sessionHistoryMap = [[NSMutableDictionary alloc] init];
752 sessionHistoryList = [[NSMutableArray alloc] init];
753 sessionHistoryLock = [[NSRecursiveLock alloc] init];
754
755 for(int i = 0; i<SESSION_MAP_SIZE; i++) {
756 atomic_init(&sessionMap[i], 0);
757 atomic_init(&sessionInTransitMessageCountMap[i], 0);
758 }
759
760 asyncDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
761
762 logCallback = nil;
763 statisticsCallback = nil;
767
768 globalLogRedirectionStrategy = LogRedirectionStrategyPrintLogsWhenNoCallbacksDefined;
769
771 lock = [[NSRecursiveLock alloc] init];
772 semaphore = dispatch_semaphore_create(0);
773 callbackDataArray = [[NSMutableArray alloc] init];
774
776}
777
778+ (void)enableRedirection {
779 [lock lock];
780
781 if (redirectionEnabled != 0) {
782 [lock unlock];
783 return;
784 }
786
787 [lock unlock];
788
789 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
791 });
792
793 av_log_set_callback(ffmpegkit_log_callback_function);
795}
796
797+ (void)disableRedirection {
798 [lock lock];
799
800 if (redirectionEnabled != 1) {
801 [lock unlock];
802 return;
803 }
805
806 [lock unlock];
807
808 av_log_set_callback(av_log_default_callback);
810
812}
813
814+ (int)setFontconfigConfigurationPath:(NSString*)path {
815 return [FFmpegKitConfig setEnvironmentVariable:@"FONTCONFIG_PATH" value:path];
816}
817
818+ (void)setFontDirectory:(NSString*)fontDirectoryPath with:(NSDictionary*)fontNameMapping {
819 [FFmpegKitConfig setFontDirectoryList:[NSArray arrayWithObject:fontDirectoryPath] with:fontNameMapping];
820}
821
822+ (void)setFontDirectoryList:(NSArray*)fontDirectoryArray with:(NSDictionary*)fontNameMapping {
823 NSError *error = nil;
824 BOOL isDirectory = YES;
825 BOOL isFile = NO;
826 int validFontNameMappingCount = 0;
827 NSString *tempConfigurationDirectory = [NSTemporaryDirectory() stringByAppendingPathComponent:@"fontconfig"];
828 NSString *fontConfigurationFile = [tempConfigurationDirectory stringByAppendingPathComponent:@"fonts.conf"];
829
830 if (![[NSFileManager defaultManager] fileExistsAtPath:tempConfigurationDirectory isDirectory:&isDirectory]) {
831 if (![[NSFileManager defaultManager] createDirectoryAtPath:tempConfigurationDirectory withIntermediateDirectories:YES attributes:nil error:&error]) {
832 NSLog(@"Failed to set font directory. Error received while creating temp conf directory: %@.", error);
833 return;
834 }
835 NSLog(@"Created temporary font conf directory: TRUE.");
836 }
837
838 if ([[NSFileManager defaultManager] fileExistsAtPath:fontConfigurationFile isDirectory:&isFile]) {
839 BOOL fontConfigurationDeleted = [[NSFileManager defaultManager] removeItemAtPath:fontConfigurationFile error:nil];
840 NSLog(@"Deleted old temporary font configuration: %s.", fontConfigurationDeleted?"TRUE":"FALSE");
841 }
842
843 /* PROCESS MAPPINGS FIRST */
844 NSString *fontNameMappingBlock = @"";
845 for (NSString *fontName in [fontNameMapping allKeys]) {
846 NSString *mappedFontName = [fontNameMapping objectForKey:fontName];
847
848 if ((fontName != nil) && (mappedFontName != nil) && ([fontName length] > 0) && ([mappedFontName length] > 0)) {
849
850 fontNameMappingBlock = [NSString stringWithFormat:@"%@\n%@\n%@%@%@\n%@\n%@\n%@%@%@\n%@\n%@\n",
851 @" <match target=\"pattern\">",
852 @" <test qual=\"any\" name=\"family\">",
853 @" <string>", fontName, @"</string>",
854 @" </test>",
855 @" <edit name=\"family\" mode=\"assign\" binding=\"same\">",
856 @" <string>", mappedFontName, @"</string>",
857 @" </edit>",
858 @" </match>"];
859
860 validFontNameMappingCount++;
861 }
862 }
863
864 NSMutableString *fontConfiguration = [NSMutableString stringWithFormat:@"%@\n%@\n%@\n%@\n",
865 @"<?xml version=\"1.0\"?>",
866 @"<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">",
867 @"<fontconfig>",
868 @" <dir prefix=\"cwd\">.</dir>"];
869 for (int i=0; i < [fontDirectoryArray count]; i++) {
870 NSString *fontDirectoryPath = [fontDirectoryArray objectAtIndex:i];
871 [fontConfiguration appendString: @" <dir>"];
872 [fontConfiguration appendString: fontDirectoryPath];
873 [fontConfiguration appendString: @"</dir>\n"];
874 }
875 [fontConfiguration appendString:fontNameMappingBlock];
876 [fontConfiguration appendString:@"</fontconfig>\n"];
877
878 if (![fontConfiguration writeToFile:fontConfigurationFile atomically:YES encoding:NSUTF8StringEncoding error:&error]) {
879 NSLog(@"Failed to set font directory. Error received while saving font configuration: %@.", error);
880 return;
881 }
882
883 NSLog(@"Saved new temporary font configuration with %d font name mappings.", validFontNameMappingCount);
884
885 [FFmpegKitConfig setFontconfigConfigurationPath:tempConfigurationDirectory];
886
887 for (int i=0; i < [fontDirectoryArray count]; i++) {
888 NSString *fontDirectoryPath = [fontDirectoryArray objectAtIndex:i];
889 NSLog(@"Font directory %@ registered successfully.", fontDirectoryPath);
890 }
891}
892
893+ (NSString*)registerNewFFmpegPipe {
894 NSError *error = nil;
895 BOOL isDirectory;
896
897 // PIPES ARE CREATED UNDER THE PIPES DIRECTORY
898 NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
899 NSString *pipesDir = [cacheDir stringByAppendingPathComponent:@"pipes"];
900
901 if (![[NSFileManager defaultManager] fileExistsAtPath:pipesDir isDirectory:&isDirectory]) {
902 if (![[NSFileManager defaultManager] createDirectoryAtPath:pipesDir withIntermediateDirectories:YES attributes:nil error:&error]) {
903 NSLog(@"Failed to create pipes directory: %@. Operation failed with %@.", pipesDir, error);
904 return nil;
905 }
906 }
907
908 NSString *newFFmpegPipePath = [NSString stringWithFormat:@"%@/%@%ld", pipesDir, FFmpegKitNamedPipePrefix, [pipeIndexGenerator getAndIncrement]];
909
910 // FIRST CLOSE OLD PIPES WITH THE SAME NAME
911 [FFmpegKitConfig closeFFmpegPipe:newFFmpegPipePath];
912
913 int rc = mkfifo([newFFmpegPipePath UTF8String], S_IRWXU | S_IRWXG | S_IROTH);
914 if (rc == 0) {
915 return newFFmpegPipePath;
916 } else {
917 NSLog(@"Failed to register new FFmpeg pipe %@. Operation failed with rc=%d.", newFFmpegPipePath, rc);
918 return nil;
919 }
920}
921
922+ (void)closeFFmpegPipe:(NSString*)ffmpegPipePath {
923 NSFileManager *fileManager = [NSFileManager defaultManager];
924
925 if ([fileManager fileExistsAtPath:ffmpegPipePath]){
926 [fileManager removeItemAtPath:ffmpegPipePath error:nil];
927 }
928}
929
930+ (NSString*)getFFmpegVersion {
931 return [NSString stringWithUTF8String:FFMPEG_VERSION];
932}
933
934+ (NSString*)getVersion {
935 if ([FFmpegKitConfig isLTSBuild] == 1) {
936 return [NSString stringWithFormat:@"%@-lts", FFmpegKitVersion];
937 } else {
938 return FFmpegKitVersion;
939 }
940}
941
942+ (int)isLTSBuild {
943 #if defined(FFMPEG_KIT_LTS)
944 return 1;
945 #else
946 return 0;
947 #endif
948}
949
950+ (NSString*)getBuildDate {
951 char buildDate[10];
952 sprintf(buildDate, "%d", FFMPEG_KIT_BUILD_DATE);
953 return [NSString stringWithUTF8String:buildDate];
954}
955
956+ (int)setEnvironmentVariable:(NSString*)variableName value:(NSString*)variableValue {
957 return setenv([variableName UTF8String], [variableValue UTF8String], true);
958}
959
960+ (void)ignoreSignal:(Signal)signal {
961 if (signal == SignalQuit) {
962 handleSIGQUIT = 0;
963 } else if (signal == SignalInt) {
964 handleSIGINT = 0;
965 } else if (signal == SignalTerm) {
966 handleSIGTERM = 0;
967 } else if (signal == SignalXcpu) {
968 handleSIGXCPU = 0;
969 } else if (signal == SignalPipe) {
970 handleSIGPIPE = 0;
971 }
972}
973
974+ (void)ffmpegExecute:(FFmpegSession*)ffmpegSession {
975 [ffmpegSession startRunning];
976
977 @try {
978 int returnCode = executeFFmpeg([ffmpegSession getSessionId], [ffmpegSession getArguments]);
979 [ffmpegSession complete:[[ReturnCode alloc] init:returnCode]];
980 } @catch (NSException *exception) {
981 [ffmpegSession fail:exception];
982 NSLog(@"FFmpeg execute failed: %@.%@", [FFmpegKitConfig argumentsToString:[ffmpegSession getArguments]], [NSString stringWithFormat:@"%@", [exception callStackSymbols]]);
983 }
984}
985
986+ (void)ffprobeExecute:(FFprobeSession*)ffprobeSession {
987 [ffprobeSession startRunning];
988
989 @try {
990 int returnCode = executeFFprobe([ffprobeSession getSessionId], [ffprobeSession getArguments]);
991 [ffprobeSession complete:[[ReturnCode alloc] init:returnCode]];
992 } @catch (NSException *exception) {
993 [ffprobeSession fail:exception];
994 NSLog(@"FFprobe execute failed: %@.%@", [FFmpegKitConfig argumentsToString:[ffprobeSession getArguments]], [NSString stringWithFormat:@"%@", [exception callStackSymbols]]);
995 }
996}
997
998+ (void)getMediaInformationExecute:(MediaInformationSession*)mediaInformationSession withTimeout:(int)waitTimeout {
999 [mediaInformationSession startRunning];
1000
1001 @try {
1002 int returnCodeValue = executeFFprobe([mediaInformationSession getSessionId], [mediaInformationSession getArguments]);
1003 ReturnCode* returnCode = [[ReturnCode alloc] init:returnCodeValue];
1004 [mediaInformationSession complete:returnCode];
1005 if ([returnCode isValueSuccess]) {
1006 NSArray* allLogs = [mediaInformationSession getAllLogsWithTimeout:waitTimeout];
1007 NSMutableString* ffprobeJsonOutput = [[NSMutableString alloc] init];
1008 for (int i=0; i < [allLogs count]; i++) {
1009 Log* log = [allLogs objectAtIndex:i];
1010 if ([log getLevel] == LevelAVLogStdErr) {
1011 [ffprobeJsonOutput appendString:[log getMessage]];
1012 }
1013 }
1014 MediaInformation* mediaInformation = [MediaInformationJsonParser fromWithError:ffprobeJsonOutput];
1015 [mediaInformationSession setMediaInformation:mediaInformation];
1016 }
1017 } @catch (NSException *exception) {
1018 [mediaInformationSession fail:exception];
1019 NSLog(@"Get media information execute failed: %@.%@", [FFmpegKitConfig argumentsToString:[mediaInformationSession getArguments]], [NSString stringWithFormat:@"\n%@\n%@", [exception userInfo], [exception callStackSymbols]]);
1020 }
1021}
1022
1023+ (void)asyncFFmpegExecute:(FFmpegSession*)ffmpegSession {
1024 [FFmpegKitConfig asyncFFmpegExecute:ffmpegSession onDispatchQueue:asyncDispatchQueue];
1025}
1026
1027+ (void)asyncFFmpegExecute:(FFmpegSession*)ffmpegSession onDispatchQueue:(dispatch_queue_t)queue {
1028 dispatch_async(queue, ^{
1029 [FFmpegKitConfig ffmpegExecute:ffmpegSession];
1030
1031 FFmpegSessionCompleteCallback completeCallback = [ffmpegSession getCompleteCallback];
1032 if (completeCallback != nil) {
1033 @try {
1034 // NOTIFY SESSION CALLBACK DEFINED
1035 completeCallback(ffmpegSession);
1036 }
1037 @catch(NSException* exception) {
1038 NSLog(@"Exception thrown inside session complete callback. %@", [exception callStackSymbols]);
1039 }
1040 }
1041
1043 if (globalFFmpegSessionCompleteCallback != nil) {
1044 @try {
1045 // NOTIFY SESSION CALLBACK DEFINED
1046 globalFFmpegSessionCompleteCallback(ffmpegSession);
1047 }
1048 @catch(NSException* exception) {
1049 NSLog(@"Exception thrown inside global complete callback. %@", [exception callStackSymbols]);
1050 }
1051 }
1052 });
1053}
1054
1055+ (void)asyncFFprobeExecute:(FFprobeSession*)ffprobeSession {
1056 [FFmpegKitConfig asyncFFprobeExecute:ffprobeSession onDispatchQueue:asyncDispatchQueue];
1057}
1058
1059+ (void)asyncFFprobeExecute:(FFprobeSession*)ffprobeSession onDispatchQueue:(dispatch_queue_t)queue {
1060 dispatch_async(queue, ^{
1061 [FFmpegKitConfig ffprobeExecute:ffprobeSession];
1062
1063 FFprobeSessionCompleteCallback completeCallback = [ffprobeSession getCompleteCallback];
1064 if (completeCallback != nil) {
1065 @try {
1066 // NOTIFY SESSION CALLBACK DEFINED
1067 completeCallback(ffprobeSession);
1068 }
1069 @catch(NSException* exception) {
1070 NSLog(@"Exception thrown inside session complete callback. %@", [exception callStackSymbols]);
1071 }
1072 }
1073
1075 if (globalFFprobeSessionCompleteCallback != nil) {
1076 @try {
1077 // NOTIFY SESSION CALLBACK DEFINED
1078 globalFFprobeSessionCompleteCallback(ffprobeSession);
1079 }
1080 @catch(NSException* exception) {
1081 NSLog(@"Exception thrown inside global complete callback. %@", [exception callStackSymbols]);
1082 }
1083 }
1084 });
1085}
1086
1087+ (void)asyncGetMediaInformationExecute:(MediaInformationSession*)mediaInformationSession withTimeout:(int)waitTimeout {
1088 [FFmpegKitConfig asyncGetMediaInformationExecute:mediaInformationSession onDispatchQueue:asyncDispatchQueue withTimeout:waitTimeout];
1089}
1090
1091+ (void)asyncGetMediaInformationExecute:(MediaInformationSession*)mediaInformationSession onDispatchQueue:(dispatch_queue_t)queue withTimeout:(int)waitTimeout {
1092 dispatch_async(queue, ^{
1093 [FFmpegKitConfig getMediaInformationExecute:mediaInformationSession withTimeout:waitTimeout];
1094
1095 MediaInformationSessionCompleteCallback completeCallback = [mediaInformationSession getCompleteCallback];
1096 if (completeCallback != nil) {
1097 @try {
1098 // NOTIFY SESSION CALLBACK DEFINED
1099 completeCallback(mediaInformationSession);
1100 }
1101 @catch(NSException* exception) {
1102 NSLog(@"Exception thrown inside session complete callback. %@", [exception callStackSymbols]);
1103 }
1104 }
1105
1107 if (globalMediaInformationSessionCompleteCallback != nil) {
1108 @try {
1109 // NOTIFY SESSION CALLBACK DEFINED
1110 globalMediaInformationSessionCompleteCallback(mediaInformationSession);
1111 }
1112 @catch(NSException* exception) {
1113 NSLog(@"Exception thrown inside global complete callback. %@", [exception callStackSymbols]);
1114 }
1115 }
1116 });
1117}
1118
1119+ (void)enableLogCallback:(LogCallback)callback {
1120 logCallback = callback;
1121}
1122
1123+ (void)enableStatisticsCallback:(StatisticsCallback)callback {
1124 statisticsCallback = callback;
1125}
1126
1127+ (void)enableFFmpegSessionCompleteCallback:(FFmpegSessionCompleteCallback)completeCallback {
1128 ffmpegSessionCompleteCallback = completeCallback;
1129}
1130
1133}
1134
1135+ (void)enableFFprobeSessionCompleteCallback:(FFprobeSessionCompleteCallback)completeCallback {
1136 ffprobeSessionCompleteCallback = completeCallback;
1137}
1138
1141}
1142
1143+ (void)enableMediaInformationSessionCompleteCallback:(MediaInformationSessionCompleteCallback)completeCallback {
1144 mediaInformationSessionCompleteCallback = completeCallback;
1145}
1146
1149}
1150
1151+ (int)getLogLevel {
1152 return configuredLogLevel;
1153}
1154
1155+ (void)setLogLevel:(int)level {
1156 configuredLogLevel = level;
1157}
1158
1159+ (NSString*)logLevelToString:(int)level {
1160 switch (level) {
1161 case LevelAVLogStdErr: return @"STDERR";
1162 case LevelAVLogTrace: return @"TRACE";
1163 case LevelAVLogDebug: return @"DEBUG";
1164 case LevelAVLogVerbose: return @"VERBOSE";
1165 case LevelAVLogInfo: return @"INFO";
1166 case LevelAVLogWarning: return @"WARNING";
1167 case LevelAVLogError: return @"ERROR";
1168 case LevelAVLogFatal: return @"FATAL";
1169 case LevelAVLogPanic: return @"PANIC";
1170 case LevelAVLogQuiet: return @"QUIET";
1171 default: return @"";
1172 }
1173}
1174
1176 return sessionHistorySize;
1177}
1178
1179+ (void)setSessionHistorySize:(int)pSessionHistorySize {
1180 if (pSessionHistorySize >= SESSION_MAP_SIZE) {
1181
1182 /*
1183 * THERE IS A HARD LIMIT ON THE NATIVE SIDE. HISTORY SIZE MUST BE SMALLER THAN SESSION_MAP_SIZE
1184 */
1185 @throw([NSException exceptionWithName:NSInvalidArgumentException reason:@"Session history size must not exceed the hard limit!" userInfo:nil]);
1186 } else if (pSessionHistorySize > 0) {
1187 sessionHistorySize = pSessionHistorySize;
1189 }
1190}
1191
1192+ (id<Session>)getSession:(long)sessionId {
1193 [sessionHistoryLock lock];
1194
1195 id<Session> session = [sessionHistoryMap objectForKey:[NSNumber numberWithLong:sessionId]];
1196
1197 [sessionHistoryLock unlock];
1198
1199 return session;
1200}
1201
1202+ (id<Session>)getLastSession {
1203 [sessionHistoryLock lock];
1204
1205 id<Session> lastSession = [sessionHistoryList lastObject];
1206
1207 [sessionHistoryLock unlock];
1208
1209 return lastSession;
1210}
1211
1212+ (id<Session>)getLastCompletedSession {
1213 id<Session> lastCompletedSession = nil;
1214
1215 [sessionHistoryLock lock];
1216
1217 for(int i = [sessionHistoryList count] - 1; i >= 0; i--) {
1218 id<Session> session = [sessionHistoryList objectAtIndex:i];
1219 if ([session getState] == SessionStateCompleted) {
1220 lastCompletedSession = session;
1221 break;
1222 }
1223 }
1224
1225 [sessionHistoryLock unlock];
1226
1227 return lastCompletedSession;
1228}
1229
1230+ (NSArray*)getSessions {
1231 [sessionHistoryLock lock];
1232
1233 NSArray* sessionsCopy = [sessionHistoryList copy];
1234
1235 [sessionHistoryLock unlock];
1236
1237 return sessionsCopy;
1238}
1239
1240+ (void)clearSessions {
1241 [sessionHistoryLock lock];
1242
1243 [sessionHistoryList removeAllObjects];
1244 [sessionHistoryMap removeAllObjects];
1245
1246 [sessionHistoryLock unlock];
1247}
1248
1249+ (NSArray*)getFFmpegSessions {
1250 NSMutableArray* ffmpegSessions = [[NSMutableArray alloc] init];
1251
1252 [sessionHistoryLock lock];
1253
1254 for(int i = 0; i < [sessionHistoryList count]; i++) {
1255 id<Session> session = [sessionHistoryList objectAtIndex:i];
1256 if ([session isFFmpeg]) {
1257 [ffmpegSessions addObject:session];
1258 }
1259 }
1260
1261 [sessionHistoryLock unlock];
1262
1263 return ffmpegSessions;
1264}
1265
1266+ (NSArray*)getFFprobeSessions {
1267 NSMutableArray* ffprobeSessions = [[NSMutableArray alloc] init];
1268
1269 [sessionHistoryLock lock];
1270
1271 for(int i = 0; i < [sessionHistoryList count]; i++) {
1272 id<Session> session = [sessionHistoryList objectAtIndex:i];
1273 if ([session isFFprobe]) {
1274 [ffprobeSessions addObject:session];
1275 }
1276 }
1277
1278 [sessionHistoryLock unlock];
1279
1280 return ffprobeSessions;
1281}
1282
1283+ (NSArray*)getMediaInformationSessions {
1284 NSMutableArray* mediaInformationSessions = [[NSMutableArray alloc] init];
1285
1286 [sessionHistoryLock lock];
1287
1288 for(int i = 0; i < [sessionHistoryList count]; i++) {
1289 id<Session> session = [sessionHistoryList objectAtIndex:i];
1290 if ([session isMediaInformation]) {
1291 [mediaInformationSessions addObject:session];
1292 }
1293 }
1294
1295 [sessionHistoryLock unlock];
1296
1297 return mediaInformationSessions;
1298}
1299
1300+ (NSArray*)getSessionsByState:(SessionState)state {
1301 NSMutableArray* sessions = [[NSMutableArray alloc] init];
1302
1303 [sessionHistoryLock lock];
1304
1305 for(int i = 0; i < [sessionHistoryList count]; i++) {
1306 id<Session> session = [sessionHistoryList objectAtIndex:i];
1307 if ([session getState] == state) {
1308 [sessions addObject:session];
1309 }
1310 }
1311
1312 [sessionHistoryLock unlock];
1313
1314 return sessions;
1315}
1316
1317+ (LogRedirectionStrategy)getLogRedirectionStrategy {
1319}
1320
1321+ (void)setLogRedirectionStrategy:(LogRedirectionStrategy)logRedirectionStrategy {
1322 globalLogRedirectionStrategy = logRedirectionStrategy;
1323}
1324
1325+ (int)messagesInTransmit:(long)sessionId {
1326 return atomic_load(&sessionInTransitMessageCountMap[sessionId % SESSION_MAP_SIZE]);
1327}
1328
1329+ (NSString*)sessionStateToString:(SessionState)state {
1330 switch (state) {
1331 case SessionStateCreated: return @"CREATED";
1332 case SessionStateRunning: return @"RUNNING";
1333 case SessionStateFailed: return @"FAILED";
1334 case SessionStateCompleted: return @"COMPLETED";
1335 default: return @"";
1336 }
1337}
1338
1339+ (NSArray*)parseArguments:(NSString*)command {
1340 NSMutableArray *argumentArray = [[NSMutableArray alloc] init];
1341 NSMutableString *currentArgument = [[NSMutableString alloc] init];
1342
1343 bool singleQuoteStarted = false;
1344 bool doubleQuoteStarted = false;
1345
1346 for (int i = 0; i < command.length; i++) {
1347 unichar previousChar;
1348 if (i > 0) {
1349 previousChar = [command characterAtIndex:(i - 1)];
1350 } else {
1351 previousChar = 0;
1352 }
1353 unichar currentChar = [command characterAtIndex:i];
1354
1355 if (currentChar == ' ') {
1356 if (singleQuoteStarted || doubleQuoteStarted) {
1357 [currentArgument appendFormat: @"%C", currentChar];
1358 } else if ([currentArgument length] > 0) {
1359 [argumentArray addObject: currentArgument];
1360 currentArgument = [[NSMutableString alloc] init];
1361 }
1362 } else if (currentChar == '\'' && (previousChar == 0 || previousChar != '\\')) {
1363 if (singleQuoteStarted) {
1364 singleQuoteStarted = false;
1365 } else if (doubleQuoteStarted) {
1366 [currentArgument appendFormat: @"%C", currentChar];
1367 } else {
1368 singleQuoteStarted = true;
1369 }
1370 } else if (currentChar == '\"' && (previousChar == 0 || previousChar != '\\')) {
1371 if (doubleQuoteStarted) {
1372 doubleQuoteStarted = false;
1373 } else if (singleQuoteStarted) {
1374 [currentArgument appendFormat: @"%C", currentChar];
1375 } else {
1376 doubleQuoteStarted = true;
1377 }
1378 } else {
1379 [currentArgument appendFormat: @"%C", currentChar];
1380 }
1381 }
1382
1383 if ([currentArgument length] > 0) {
1384 [argumentArray addObject: currentArgument];
1385 }
1386
1387 return argumentArray;
1388}
1389
1390+ (NSString*)argumentsToString:(NSArray*)arguments {
1391 if (arguments == nil) {
1392 return @"nil";
1393 }
1394
1395 NSMutableString *string = [NSMutableString stringWithString:@""];
1396 for (int i=0; i < [arguments count]; i++) {
1397 NSString *argument = [arguments objectAtIndex:i];
1398 if (i > 0) {
1399 [string appendString:@" "];
1400 }
1401 [string appendString:argument];
1402 }
1403
1404 return string;
1405}
1406
1407@end
NSString *const FFmpegKitVersion
int executeFFprobe(long sessionId, NSArray *arguments)
void statisticsCallbackDataAdd(int frameNumber, float fps, float quality, int64_t size, double time, double bitrate, double speed)
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]
float _statisticsQuality
void cancelSession(long sessionId)
static volatile NSMutableDictionary * sessionHistoryMap
#define SESSION_MAP_SIZE
int _logLevel
static FFmpegSessionCompleteCallback ffmpegSessionCompleteCallback
static int sessionHistorySize
static void avutil_log_sanitize(uint8_t *line)
static NSRecursiveLock * lock
volatile int handleSIGINT
int ffprobe_execute(int argc, char **argv)
volatile int handleSIGTERM
void ffmpegkit_statistics_callback_function(int frameNumber, float fps, float quality, int64_t size, double time, double bitrate, double speed)
static const char * avutil_log_get_level_str(int level)
static void avutil_log_format_line(void *avcl, int level, const char *fmt, va_list vl, AVBPrint part[4], int *print_prefix)
static dispatch_queue_t asyncDispatchQueue
void removeSession(long sessionId)
static LogCallback logCallback
void callbackNotify()
float _statisticsFps
long _sessionId
void callbackBlockFunction()
double _statisticsBitrate
static MediaInformationSessionCompleteCallback mediaInformationSessionCompleteCallback
void registerSessionId(long sessionId)
double _statisticsSpeed
static NSRecursiveLock * sessionHistoryLock
void process_log(long sessionId, int levelValue, AVBPrint *logMessage)
double _statisticsTime
static dispatch_semaphore_t semaphore
void deleteExpiredSessions()
__thread long globalSessionId
static int redirectionEnabled
void resetMessagesInTransmit(long sessionId)
static NSMutableArray * callbackDataArray
int64_t _statisticsSize
AVBPrint _logData
CallbackData * callbackDataRemove()
int _statisticsFrameNumber
int cancelRequested(long sessionId)
volatile int handleSIGPIPE
void process_statistics(long sessionId, int videoFrameNumber, float videoFps, float videoQuality, long size, double time, double bitrate, double speed)
NSString *const FFmpegKitVersion
volatile int handleSIGXCPU
static LogRedirectionStrategy globalLogRedirectionStrategy
static StatisticsCallback statisticsCallback
static atomic_int sessionInTransitMessageCountMap[SESSION_MAP_SIZE]
static FFprobeSessionCompleteCallback ffprobeSessionCompleteCallback
static AtomicLong * pipeIndexGenerator
static NSMutableArray * sessionHistoryList
typedef NS_ENUM(NSUInteger, CallbackType)
NSString *const FFmpegKitNamedPipePrefix
int configuredLogLevel
void addSessionToSessionHistory(id< Session > session)
int ffmpeg_execute(int argc, char **argv)
volatile int handleSIGQUIT
void logCallbackDataAdd(int level, AVBPrint *data)
int executeFFmpeg(long sessionId, NSArray *arguments)
void(^ FFmpegSessionCompleteCallback)(FFmpegSession *session)
void(^ FFprobeSessionCompleteCallback)(FFprobeSession *session)
void(^ LogCallback)(Log *log)
Definition LogCallback.h:31
void(^ MediaInformationSessionCompleteCallback)(MediaInformationSession *session)
void(^ StatisticsCallback)(Statistics *statistics)
#define AV_LOG_STDERR
void set_report_callback(void(*callback)(int, float, float, int64_t, double, double, double))
void fail:(NSException *exception)
NSArray * getAllLogsWithTimeout:(int waitTimeout)
void complete:(ReturnCode *returnCode)
long getAndIncrement()
Definition AtomicLong.m:40
void asyncFFmpegExecute:onDispatchQueue:(FFmpegSession *ffmpegSession,[onDispatchQueue] dispatch_queue_t queue)
NSArray * getFFmpegSessions()
FFmpegSessionCompleteCallback getFFmpegSessionCompleteCallback()
LogRedirectionStrategy getLogRedirectionStrategy()
void getMediaInformationExecute:withTimeout:(MediaInformationSession *mediaInformationSession,[withTimeout] int waitTimeout)
NSString * registerNewFFmpegPipe()
FFprobeSessionCompleteCallback getFFprobeSessionCompleteCallback()
NSString * getVersion()
MediaInformationSessionCompleteCallback getMediaInformationSessionCompleteCallback()
int setFontconfigConfigurationPath:(NSString *path)
NSString * getBuildDate()
void ffprobeExecute:(FFprobeSession *ffprobeSession)
NSArray * getFFprobeSessions()
NSArray * getMediaInformationSessions()
void ffmpegExecute:(FFmpegSession *ffmpegSession)
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)
FFmpegSessionCompleteCallback getCompleteCallback()
FFprobeSessionCompleteCallback getCompleteCallback()
Definition Log.h:29
NSString * getMessage()
Definition Log.m:47
MediaInformation * fromWithError:(NSString *ffprobeJsonOutput)
void setMediaInformation:(MediaInformation *mediaInformation)
MediaInformationSessionCompleteCallback getCompleteCallback()