/*
* Copyright (c) 2018-2020 Taner Sener
*
* This file is part of FFmpegKit.
*
* FFmpegKit 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.
*
* FFmpegKit 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 FFmpegKit. If not, see .
*/
#include
#include
#include "libavutil/ffversion.h"
#include "fftools_ffmpeg.h"
#include "ArchDetect.h"
#include "FFmpegKitConfig.h"
#include "FFmpegKit.h"
typedef enum {
LogType = 1,
StatisticsType = 2
} CallbackType;
/**
* Callback data class.
*/
@interface CallbackData : NSObject
@end
@implementation CallbackData {
CallbackType type;
long executionId; // execution id
int logLevel; // log level
NSString *logData; // log data
int statisticsFrameNumber; // statistics frame number
float statisticsFps; // statistics fps
float statisticsQuality; // statistics quality
int64_t statisticsSize; // statistics size
int statisticsTime; // statistics time
double statisticsBitrate; // statistics bitrate
double statisticsSpeed; // statistics speed
}
- (instancetype)initWithId:(long)currentExecutionId logLevel:(int)newLogLevel data:(NSString*)newData {
self = [super init];
if (self) {
type = LogType;
executionId = currentExecutionId;
logLevel = newLogLevel;
logData = newData;
}
return self;
}
- (instancetype)initWithId:(long)currentExecutionId
videoFrameNumber:(int)videoFrameNumber
fps:(float)videoFps
quality:(float)videoQuality
size:(int64_t)size
time:(int)time
bitrate:(double)bitrate
speed:(double)speed {
self = [super init];
if (self) {
type = StatisticsType;
executionId = currentExecutionId;
statisticsFrameNumber = videoFrameNumber;
statisticsFps = videoFps;
statisticsQuality = videoQuality;
statisticsSize = size;
statisticsTime = time;
statisticsBitrate = bitrate;
statisticsSpeed = speed;
}
return self;
}
- (CallbackType)getType {
return type;
}
- (long)getExecutionId {
return executionId;
}
- (int)getLogLevel {
return logLevel;
}
- (NSString*)getLogData {
return logData;
}
- (int)getStatisticsFrameNumber {
return statisticsFrameNumber;
}
- (float)getStatisticsFps {
return statisticsFps;
}
- (float)getStatisticsQuality {
return statisticsQuality;
}
- (int64_t)getStatisticsSize {
return statisticsSize;
}
- (int)getStatisticsTime {
return statisticsTime;
}
- (double)getStatisticsBitrate {
return statisticsBitrate;
}
- (double)getStatisticsSpeed {
return statisticsSpeed;
}
@end
/** Execution map variables */
const int EXECUTION_MAP_SIZE = 1000;
static volatile int executionMap[EXECUTION_MAP_SIZE];
static NSRecursiveLock *executionMapLock;
/** Redirection control variables */
static int redirectionEnabled;
static NSRecursiveLock *lock;
static dispatch_semaphore_t semaphore;
static NSMutableArray *callbackDataArray;
/** Holds delegate defined to redirect logs */
static id logDelegate = nil;
/** Holds delegate defined to redirect statistics */
static id statisticsDelegate = nil;
/** Common return code values */
int const RETURN_CODE_SUCCESS = 0;
int const RETURN_CODE_CANCEL = 255;
int lastReturnCode;
NSMutableString *lastCommandOutput;
NSString *const LIB_NAME = @"ffmpeg-kit";
NSString *const FFMPEG_KIT_PIPE_PREFIX = @"mf_pipe_";
static Statistics *lastReceivedStatistics = nil;
static NSMutableArray *supportedExternalLibraries;
static int lastCreatedPipeIndex;
/** Fields that control the handling of SIGNALs */
volatile int handleSIGQUIT = 1;
volatile int handleSIGINT = 1;
volatile int handleSIGTERM = 1;
volatile int handleSIGXCPU = 1;
volatile int handleSIGPIPE = 1;
/** Holds the id of the current execution */
__thread volatile long executionId = 0;
/** Holds the default log level */
int configuredLogLevel = AV_LOG_INFO;
void callbackWait(int milliSeconds) {
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(milliSeconds * NSEC_PER_MSEC)));
}
void callbackNotify() {
dispatch_semaphore_signal(semaphore);
}
/**
* Adds log data to the end of callback data list.
*
* @param level log level
* @param logData log data
*/
void logCallbackDataAdd(int level, NSString *logData) {
CallbackData *callbackData = [[CallbackData alloc] initWithId:executionId logLevel:level data:logData];
[lock lock];
[callbackDataArray addObject:callbackData];
[lastCommandOutput appendString:logData];
[lock unlock];
callbackNotify();
}
/**
* Adds statistics data to the end of callback data list.
*/
void statisticsCallbackDataAdd(int frameNumber, float fps, float quality, int64_t size, int time, double bitrate, double speed) {
CallbackData *callbackData = [[CallbackData alloc] initWithId:executionId videoFrameNumber:frameNumber fps:fps quality:quality size:size time:time bitrate:bitrate speed:speed];
[lock lock];
[callbackDataArray addObject:callbackData];
[lock unlock];
callbackNotify();
}
/**
* Adds an execution id to the execution map.
*
* @param id execution id
*/
void addExecution(long id) {
[executionMapLock lock];
int key = id % EXECUTION_MAP_SIZE;
executionMap[key] = 1;
[executionMapLock unlock];
}
/**
* Removes head of callback data list.
*/
CallbackData *callbackDataRemove() {
CallbackData *newData = nil;
[lock lock];
@try {
newData = [callbackDataArray objectAtIndex:0];
[callbackDataArray removeObjectAtIndex:0];
} @catch(NSException *exception) {
// DO NOTHING
} @finally {
[lock unlock];
}
return newData;
}
/**
* Removes an execution id from the execution map.
*
* @param id execution id
*/
void removeExecution(long id) {
[executionMapLock lock];
int key = id % EXECUTION_MAP_SIZE;
executionMap[key] = 0;
[executionMapLock unlock];
}
/**
* Checks whether a cancel request for the given execution id exists in the execution map.
*
* @param id execution id
* @return 1 if exists, false otherwise
*/
int cancelRequested(long id) {
int found = 0;
[executionMapLock lock];
int key = id % EXECUTION_MAP_SIZE;
if (executionMap[key] == 0) {
found = 1;
}
[executionMapLock unlock];
return found;
}
/**
* Callback function for FFmpeg logs.
*
* @param ptr pointer to AVClass struct
* @param level log level
* @param format format string
* @param vargs arguments
*/
void ffmpegkit_log_callback_function(void *ptr, int level, const char* format, va_list vargs) {
// DO NOT PROCESS UNWANTED LOGS
if (level >= 0) {
level &= 0xff;
}
int activeLogLevel = av_log_get_level();
// AV_LOG_STDERR logs are always redirected
if ((activeLogLevel == AV_LOG_QUIET && level != AV_LOG_STDERR) || (level > activeLogLevel)) {
return;
}
NSString *logData = [[NSString alloc] initWithFormat:[NSString stringWithCString:format encoding:NSUTF8StringEncoding] arguments:vargs];
if (logData.length > 0) {
logCallbackDataAdd(level, logData);
}
}
/**
* Callback function for FFmpeg statistics.
*
* @param frameNumber last processed frame number
* @param fps frames processed per second
* @param quality quality of the output stream (video only)
* @param size size in bytes
* @param time processed output duration
* @param bitrate output bit rate in kbits/s
* @param speed processing speed = processed duration / operation duration
*/
void ffmpegkit_statistics_callback_function(int frameNumber, float fps, float quality, int64_t size, int time, double bitrate, double speed) {
statisticsCallbackDataAdd(frameNumber, fps, quality, size, time, bitrate, speed);
}
/**
* Forwards callback messages to Delegates.
*/
void callbackBlockFunction() {
int activeLogLevel = av_log_get_level();
if ((activeLogLevel != AV_LOG_QUIET) && (AV_LOG_DEBUG <= activeLogLevel)) {
NSLog(@"Async callback block started.\n");
}
while(redirectionEnabled) {
@autoreleasepool {
@try {
CallbackData *callbackData = callbackDataRemove();
if (callbackData != nil) {
if ([callbackData getType] == LogType) {
// LOG CALLBACK
int activeLogLevel = av_log_get_level();
int levelValue = [callbackData getLogLevel];
if ((activeLogLevel == AV_LOG_QUIET && levelValue != AV_LOG_STDERR) || (levelValue > activeLogLevel)) {
// LOG NEITHER PRINTED NOR FORWARDED
} else {
if (logDelegate != nil) {
// FORWARD LOG TO DELEGATE
[logDelegate logCallback:[callbackData getExecutionId]:[callbackData getLogLevel]:[callbackData getLogData]];
} else {
switch (levelValue) {
case AV_LOG_QUIET:
// PRINT NO OUTPUT
break;
default:
// WRITE TO NSLOG
NSLog(@"%@: %@", [FFmpegKitConfig logLevelToString:[callbackData getLogLevel]], [callbackData getLogData]);
break;
}
}
}
} else {
// STATISTICS CALLBACK
Statistics *newStatistics = [[Statistics alloc] initWithId:[callbackData getExecutionId] videoFrameNumber:[callbackData getStatisticsFrameNumber] fps:[callbackData getStatisticsFps] quality:[callbackData getStatisticsQuality] size:[callbackData getStatisticsSize] time:[callbackData getStatisticsTime] bitrate:[callbackData getStatisticsBitrate] speed:[callbackData getStatisticsSpeed]];
[lastReceivedStatistics update:newStatistics];
if (logDelegate != nil) {
// FORWARD STATISTICS TO DELEGATE
[statisticsDelegate statisticsCallback:lastReceivedStatistics];
}
}
} else {
callbackWait(100);
}
} @catch(NSException *exception) {
activeLogLevel = av_log_get_level();
if ((activeLogLevel != AV_LOG_QUIET) && (AV_LOG_WARNING <= activeLogLevel)) {
NSLog(@"Async callback block received error: %@n\n", exception);
NSLog(@"%@", [exception callStackSymbols]);
}
}
}
}
activeLogLevel = av_log_get_level();
if ((activeLogLevel != AV_LOG_QUIET) && (AV_LOG_DEBUG <= activeLogLevel)) {
NSLog(@"Async callback block stopped.\n");
}
}
@interface FFmpegKitConfig()
/**
* Returns build configuration for FFmpeg.
*
* @return build configuration string
*/
+ (NSString*)getBuildConf;
@end
@implementation FFmpegKitConfig
+ (void)initialize {
supportedExternalLibraries = [[NSMutableArray alloc] init];
[supportedExternalLibraries addObject:@"fontconfig"];
[supportedExternalLibraries addObject:@"freetype"];
[supportedExternalLibraries addObject:@"fribidi"];
[supportedExternalLibraries addObject:@"gmp"];
[supportedExternalLibraries addObject:@"gnutls"];
[supportedExternalLibraries addObject:@"kvazaar"];
[supportedExternalLibraries addObject:@"mp3lame"];
[supportedExternalLibraries addObject:@"libaom"];
[supportedExternalLibraries addObject:@"libass"];
[supportedExternalLibraries addObject:@"iconv"];
[supportedExternalLibraries addObject:@"libilbc"];
[supportedExternalLibraries addObject:@"libtheora"];
[supportedExternalLibraries addObject:@"libvidstab"];
[supportedExternalLibraries addObject:@"libvorbis"];
[supportedExternalLibraries addObject:@"libvpx"];
[supportedExternalLibraries addObject:@"libwebp"];
[supportedExternalLibraries addObject:@"libxml2"];
[supportedExternalLibraries addObject:@"opencore-amr"];
[supportedExternalLibraries addObject:@"openh264"];
[supportedExternalLibraries addObject:@"opus"];
[supportedExternalLibraries addObject:@"rubberband"];
[supportedExternalLibraries addObject:@"sdl2"];
[supportedExternalLibraries addObject:@"shine"];
[supportedExternalLibraries addObject:@"snappy"];
[supportedExternalLibraries addObject:@"soxr"];
[supportedExternalLibraries addObject:@"speex"];
[supportedExternalLibraries addObject:@"tesseract"];
[supportedExternalLibraries addObject:@"twolame"];
[supportedExternalLibraries addObject:@"wavpack"];
[supportedExternalLibraries addObject:@"x264"];
[supportedExternalLibraries addObject:@"x265"];
[supportedExternalLibraries addObject:@"xvid"];
for(int i = 0; i)newLogDelegate {
logDelegate = newLogDelegate;
}
/**
* Sets a StatisticsDelegate.
*
* @param newStatisticsDelegate statistics delegate
*/
+ (void)setStatisticsDelegate:(id)newStatisticsDelegate {
statisticsDelegate = newStatisticsDelegate;
}
/**
* Returns the last received statistics data.
*
* @return last received statistics data
*/
+ (Statistics*)getLastReceivedStatistics {
return lastReceivedStatistics;
}
/**
* Resets last received statistics.
*/
+ (void)resetStatistics {
lastReceivedStatistics = [[Statistics alloc] init];
}
/**
* Sets and overrides fontconfig configuration directory.
*
* @param path directory which contains fontconfig configuration (fonts.conf)
*/
+ (void)setFontconfigConfigurationPath:(NSString*)path {
if (path != nil) {
setenv("FONTCONFIG_PATH", [path UTF8String], true);
}
}
/**
* Registers fonts inside the given path, so they are available in FFmpeg filters.
*
* Note that you need to build FFmpegKit with fontconfig
* enabled or use a prebuilt package with fontconfig inside to use this feature.
*
* @param fontDirectoryPath directory which contains fonts (.ttf and .otf files)
* @param fontNameMapping custom font name mappings, useful to access your fonts with more friendly names
*/
+ (void)setFontDirectory:(NSString*)fontDirectoryPath with:(NSDictionary*)fontNameMapping {
NSError *error = nil;
BOOL isDirectory = YES;
BOOL isFile = NO;
int validFontNameMappingCount = 0;
NSString *tempConfigurationDirectory = [NSTemporaryDirectory() stringByAppendingPathComponent:@".ffmpegkit"];
NSString *fontConfigurationFile = [tempConfigurationDirectory stringByAppendingPathComponent:@"fonts.conf"];
int activeLogLevel = av_log_get_level();
if (![[NSFileManager defaultManager] fileExistsAtPath:tempConfigurationDirectory isDirectory:&isDirectory]) {
if (![[NSFileManager defaultManager] createDirectoryAtPath:tempConfigurationDirectory withIntermediateDirectories:YES attributes:nil error:&error]) {
if ((activeLogLevel != AV_LOG_QUIET) && (AV_LOG_WARNING <= activeLogLevel)) {
NSLog(@"Failed to set font directory. Error received while creating temp conf directory: %@.", error);
}
return;
}
if ((activeLogLevel != AV_LOG_QUIET) && (AV_LOG_DEBUG <= activeLogLevel)) {
NSLog(@"Created temporary font conf directory: TRUE.");
}
}
if ([[NSFileManager defaultManager] fileExistsAtPath:fontConfigurationFile isDirectory:&isFile]) {
BOOL fontConfigurationDeleted = [[NSFileManager defaultManager] removeItemAtPath:fontConfigurationFile error:NULL];
if ((activeLogLevel != AV_LOG_QUIET) && (AV_LOG_DEBUG <= activeLogLevel)) {
NSLog(@"Deleted old temporary font configuration: %s.", fontConfigurationDeleted?"TRUE":"FALSE");
}
}
/* PROCESS MAPPINGS FIRST */
NSString *fontNameMappingBlock = @"";
for (NSString *fontName in [fontNameMapping allKeys]) {
NSString *mappedFontName = [fontNameMapping objectForKey:fontName];
if ((fontName != nil) && (mappedFontName != nil) && ([fontName length] > 0) && ([mappedFontName length] > 0)) {
fontNameMappingBlock = [NSString stringWithFormat:@"%@\n%@\n%@%@%@\n%@\n%@\n%@%@%@\n%@\n%@\n",
@" ",
@" ",
@" ", fontName, @"",
@" ",
@" ",
@" ", mappedFontName, @"",
@" ",
@" "];
validFontNameMappingCount++;
}
}
NSString *fontConfiguration = [NSString stringWithFormat:@"%@\n%@\n%@\n%@\n%@%@%@\n%@\n%@\n",
@"",
@"",
@"",
@" .",
@" ", fontDirectoryPath, @"",
fontNameMappingBlock,
@""];
if (![fontConfiguration writeToFile:fontConfigurationFile atomically:YES encoding:NSUTF8StringEncoding error:&error]) {
if ((activeLogLevel != AV_LOG_QUIET) && (AV_LOG_WARNING <= activeLogLevel)) {
NSLog(@"Failed to set font directory. Error received while saving font configuration: %@.", error);
}
return;
}
if ((activeLogLevel != AV_LOG_QUIET) && (AV_LOG_DEBUG <= activeLogLevel)) {
NSLog(@"Saved new temporary font configuration with %d font name mappings.", validFontNameMappingCount);
}
[FFmpegKitConfig setFontconfigConfigurationPath:tempConfigurationDirectory];
if ((activeLogLevel != AV_LOG_QUIET) && (AV_LOG_DEBUG <= activeLogLevel)) {
NSLog(@"Font directory %@ registered successfully.", fontDirectoryPath);
}
}
/**
* Returns build configuration for FFmpeg.
*
* @return build configuration string
*/
+ (NSString*)getBuildConf {
return [NSString stringWithUTF8String:FFMPEG_CONFIGURATION];
}
/**
* Returns package name.
*
* @return guessed package name according to supported external libraries
*/
+ (NSString*)getPackageName {
NSArray *enabledLibraryArray = [FFmpegKitConfig getExternalLibraries];
Boolean speex = [enabledLibraryArray containsObject:@"speex"];
Boolean fribidi = [enabledLibraryArray containsObject:@"fribidi"];
Boolean gnutls = [enabledLibraryArray containsObject:@"gnutls"];
Boolean xvid = [enabledLibraryArray containsObject:@"xvid"];
Boolean min = false;
Boolean minGpl = false;
Boolean https = false;
Boolean httpsGpl = false;
Boolean audio = false;
Boolean video = false;
Boolean full = false;
Boolean fullGpl = false;
if (speex && fribidi) {
if (xvid) {
fullGpl = true;
} else {
full = true;
}
} else if (speex) {
audio = true;
} else if (fribidi) {
video = true;
} else if (xvid) {
if (gnutls) {
httpsGpl = true;
} else {
minGpl = true;
}
} else {
if (gnutls) {
https = true;
} else {
min = true;
}
}
if (fullGpl) {
if ([enabledLibraryArray containsObject:@"fontconfig"] &&
[enabledLibraryArray containsObject:@"freetype"] &&
[enabledLibraryArray containsObject:@"fribidi"] &&
[enabledLibraryArray containsObject:@"gmp"] &&
[enabledLibraryArray containsObject:@"gnutls"] &&
[enabledLibraryArray containsObject:@"kvazaar"] &&
[enabledLibraryArray containsObject:@"mp3lame"] &&
[enabledLibraryArray containsObject:@"libaom"] &&
[enabledLibraryArray containsObject:@"libass"] &&
[enabledLibraryArray containsObject:@"iconv"] &&
[enabledLibraryArray containsObject:@"libilbc"] &&
[enabledLibraryArray containsObject:@"libtheora"] &&
[enabledLibraryArray containsObject:@"libvidstab"] &&
[enabledLibraryArray containsObject:@"libvorbis"] &&
[enabledLibraryArray containsObject:@"libvpx"] &&
[enabledLibraryArray containsObject:@"libwebp"] &&
[enabledLibraryArray containsObject:@"libxml2"] &&
[enabledLibraryArray containsObject:@"opencore-amr"] &&
[enabledLibraryArray containsObject:@"opus"] &&
[enabledLibraryArray containsObject:@"shine"] &&
[enabledLibraryArray containsObject:@"snappy"] &&
[enabledLibraryArray containsObject:@"soxr"] &&
[enabledLibraryArray containsObject:@"speex"] &&
[enabledLibraryArray containsObject:@"twolame"] &&
[enabledLibraryArray containsObject:@"wavpack"] &&
[enabledLibraryArray containsObject:@"x264"] &&
[enabledLibraryArray containsObject:@"x265"] &&
[enabledLibraryArray containsObject:@"xvid"]) {
return @"full-gpl";
} else {
return @"custom";
}
}
if (full) {
if ([enabledLibraryArray containsObject:@"fontconfig"] &&
[enabledLibraryArray containsObject:@"freetype"] &&
[enabledLibraryArray containsObject:@"fribidi"] &&
[enabledLibraryArray containsObject:@"gmp"] &&
[enabledLibraryArray containsObject:@"gnutls"] &&
[enabledLibraryArray containsObject:@"kvazaar"] &&
[enabledLibraryArray containsObject:@"mp3lame"] &&
[enabledLibraryArray containsObject:@"libaom"] &&
[enabledLibraryArray containsObject:@"libass"] &&
[enabledLibraryArray containsObject:@"iconv"] &&
[enabledLibraryArray containsObject:@"libilbc"] &&
[enabledLibraryArray containsObject:@"libtheora"] &&
[enabledLibraryArray containsObject:@"libvorbis"] &&
[enabledLibraryArray containsObject:@"libvpx"] &&
[enabledLibraryArray containsObject:@"libwebp"] &&
[enabledLibraryArray containsObject:@"libxml2"] &&
[enabledLibraryArray containsObject:@"opencore-amr"] &&
[enabledLibraryArray containsObject:@"opus"] &&
[enabledLibraryArray containsObject:@"shine"] &&
[enabledLibraryArray containsObject:@"snappy"] &&
[enabledLibraryArray containsObject:@"soxr"] &&
[enabledLibraryArray containsObject:@"speex"] &&
[enabledLibraryArray containsObject:@"twolame"] &&
[enabledLibraryArray containsObject:@"wavpack"]) {
return @"full";
} else {
return @"custom";
}
}
if (video) {
if ([enabledLibraryArray containsObject:@"fontconfig"] &&
[enabledLibraryArray containsObject:@"freetype"] &&
[enabledLibraryArray containsObject:@"fribidi"] &&
[enabledLibraryArray containsObject:@"kvazaar"] &&
[enabledLibraryArray containsObject:@"libaom"] &&
[enabledLibraryArray containsObject:@"libass"] &&
[enabledLibraryArray containsObject:@"iconv"] &&
[enabledLibraryArray containsObject:@"libtheora"] &&
[enabledLibraryArray containsObject:@"libvpx"] &&
[enabledLibraryArray containsObject:@"libwebp"] &&
[enabledLibraryArray containsObject:@"snappy"]) {
return @"video";
} else {
return @"custom";
}
}
if (audio) {
if ([enabledLibraryArray containsObject:@"mp3lame"] &&
[enabledLibraryArray containsObject:@"libilbc"] &&
[enabledLibraryArray containsObject:@"libvorbis"] &&
[enabledLibraryArray containsObject:@"opencore-amr"] &&
[enabledLibraryArray containsObject:@"opus"] &&
[enabledLibraryArray containsObject:@"shine"] &&
[enabledLibraryArray containsObject:@"soxr"] &&
[enabledLibraryArray containsObject:@"speex"] &&
[enabledLibraryArray containsObject:@"twolame"] &&
[enabledLibraryArray containsObject:@"wavpack"]) {
return @"audio";
} else {
return @"custom";
}
}
if (httpsGpl) {
if ([enabledLibraryArray containsObject:@"gmp"] &&
[enabledLibraryArray containsObject:@"gnutls"] &&
[enabledLibraryArray containsObject:@"libvidstab"] &&
[enabledLibraryArray containsObject:@"x264"] &&
[enabledLibraryArray containsObject:@"x265"] &&
[enabledLibraryArray containsObject:@"xvid"]) {
return @"https-gpl";
} else {
return @"custom";
}
}
if (https) {
if ([enabledLibraryArray containsObject:@"gmp"] &&
[enabledLibraryArray containsObject:@"gnutls"]) {
return @"https";
} else {
return @"custom";
}
}
if (minGpl) {
if ([enabledLibraryArray containsObject:@"libvidstab"] &&
[enabledLibraryArray containsObject:@"x264"] &&
[enabledLibraryArray containsObject:@"x265"] &&
[enabledLibraryArray containsObject:@"xvid"]) {
return @"min-gpl";
} else {
return @"custom";
}
}
return @"min";
}
/**
* Returns supported external libraries.
*
* @return array of supported external libraries
*/
+ (NSArray*)getExternalLibraries {
NSString *buildConfiguration = [FFmpegKitConfig getBuildConf];
NSMutableArray *enabledLibraryArray = [[NSMutableArray alloc] init];
for (int i=0; i < [supportedExternalLibraries count]; i++) {
NSString *supportedExternalLibrary = [supportedExternalLibraries objectAtIndex:i];
NSString *libraryName1 = [NSString stringWithFormat:@"enable-%@", supportedExternalLibrary];
NSString *libraryName2 = [NSString stringWithFormat:@"enable-lib%@", supportedExternalLibrary];
if ([buildConfiguration rangeOfString:libraryName1].location != NSNotFound || [buildConfiguration rangeOfString:libraryName2].location != NSNotFound) {
[enabledLibraryArray addObject:supportedExternalLibrary];
}
}
[enabledLibraryArray sortUsingSelector:@selector(compare:)];
return enabledLibraryArray;
}
/**
* Creates a new named pipe to use in FFmpeg operations.
*
* Please note that creator is responsible of closing created pipes.
*
* @return the full path of named pipe
*/
+ (NSString*)registerNewFFmpegPipe {
// PIPES ARE CREATED UNDER THE CACHE DIRECTORY
NSString *cacheDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *newFFmpegPipePath = [NSString stringWithFormat:@"%@/%@%d", cacheDir, FFMPEG_KIT_PIPE_PREFIX, (++lastCreatedPipeIndex)];
// FIRST CLOSE OLD PIPES WITH THE SAME NAME
[FFmpegKitConfig closeFFmpegPipe:newFFmpegPipePath];
int rc = mkfifo([newFFmpegPipePath UTF8String], S_IRWXU | S_IRWXG | S_IROTH);
if (rc == 0) {
return newFFmpegPipePath;
} else {
int activeLogLevel = av_log_get_level();
if ((activeLogLevel != AV_LOG_QUIET) && (AV_LOG_WARNING <= activeLogLevel)) {
NSLog(@"Failed to register new FFmpeg pipe %@. Operation failed with rc=%d.", newFFmpegPipePath, rc);
}
return nil;
}
}
/**
* Closes a previously created FFmpeg pipe.
*
* @param ffmpegPipePath full path of ffmpeg pipe
*/
+ (void)closeFFmpegPipe:(NSString*)ffmpegPipePath {
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:ffmpegPipePath]){
[fileManager removeItemAtPath:ffmpegPipePath error:NULL];
}
}
/**
* Returns FFmpeg version bundled within the library.
*
* @return FFmpeg version string
*/
+ (NSString*)getFFmpegVersion {
return [NSString stringWithUTF8String:FFMPEG_VERSION];
}
/**
* Returns FFmpegKit library version.
*
* @return FFmpegKit version string
*/
+ (NSString*)getVersion {
if ([ArchDetect isLTSBuild] == 1) {
return [NSString stringWithFormat:@"%@-lts", FFMPEG_KIT_VERSION];
} else {
return FFMPEG_KIT_VERSION;
}
}
/**
* Returns FFmpegKit library build date.
*
* @return FFmpegKit library build date
*/
+ (NSString*)getBuildDate {
char buildDate[10];
sprintf(buildDate, "%d", FFMPEG_KIT_BUILD_DATE);
return [NSString stringWithUTF8String:buildDate];
}
/**
* Returns return code of last executed command.
*
* @return return code of last executed command
*/
+ (int)getLastReturnCode {
return lastReturnCode;
}
/**
* Returns log output of last executed single FFmpeg/FFprobe command.
*
* This method does not support executing multiple concurrent commands. If you execute
* multiple commands at the same time, this method will return output from all executions.
*
* Please note that disabling redirection using FFmpegKitConfig.disableRedirection() method
* also disables this functionality.
*
* @return output of last executed command
*/
+ (NSString*)getLastCommandOutput {
return lastCommandOutput;
}
/**
* Registers a new ignored signal. Ignored signals are not handled by the library.
*
* @param signum signal number to ignore
*/
+ (void)ignoreSignal:(int)signum {
if (signum == SIGQUIT) {
handleSIGQUIT = 0;
} else if (signum == SIGINT) {
handleSIGINT = 0;
} else if (signum == SIGTERM) {
handleSIGTERM = 0;
} else if (signum == SIGXCPU) {
handleSIGXCPU = 0;
} else if (signum == SIGPIPE) {
handleSIGPIPE = 0;
}
}
@end