-
Notifications
You must be signed in to change notification settings - Fork 25
Using FFmpegKitNext on iOS
This page shows how to use FFmpegKitNext in an iOS application after the iOS framework, xcframework or local Swift package has already been added to your Xcode project.
It applies to both iOS and iPadOS. iPad applications use the same iOS frameworks and xcframeworks.
Objective-C projects import headers from the ffmpegkit module:
#include <ffmpegkit/FFmpegKit.h>
#include <ffmpegkit/FFmpegKitConfig.h>
#include <ffmpegkit/FFprobeKit.h>Swift projects import the same module:
import ffmpegkit#include <ffmpegkit/FFmpegKit.h>
#include <ffmpegkit/FFmpegKitConfig.h>
FFmpegSession *session = [FFmpegKit execute:@"-i file1.mp4 -c:v mpeg4 file2.mp4"];
ReturnCode *returnCode = [session getReturnCode];
if ([ReturnCode isSuccess:returnCode]) {
// Success.
} else if ([ReturnCode isCancel:returnCode]) {
// Cancelled.
} else {
NSLog(@"Command failed with state %@ and rc %@.%@",
[FFmpegKitConfig sessionStateToString:[session getState]],
returnCode,
[session getFailStackTrace]);
}String commands are split by spaces. Use executeWithArguments: when paths or filter arguments may contain spaces or quotes.
NSArray *arguments = @[
@"-i", inputPath,
@"-vf", @"scale=1280:-2",
@"-c:v", @"mpeg4",
outputPath
];
FFmpegSession *session = [FFmpegKit executeWithArguments:arguments];FFmpegSession *session =
[FFmpegKit executeAsync:@"-i file1.mp4 -c:v mpeg4 file2.mp4"
withCompleteCallback:^(FFmpegSession *session) {
SessionState state = [session getState];
ReturnCode *returnCode = [session getReturnCode];
NSLog(@"FFmpeg exited with state %@ and rc %@.%@",
[FFmpegKitConfig sessionStateToString:state],
returnCode,
[session getFailStackTrace]);
}
withLogCallback:^(Log *log) {
NSLog(@"%@", [log getMessage]);
}
withStatisticsCallback:^(Statistics *statistics) {
NSLog(@"frame=%d time=%f speed=%f",
[statistics getVideoFrameNumber],
[statistics getTime],
[statistics getSpeed]);
}];dispatch_queue_t queue =
dispatch_queue_create("com.example.ffmpeg", DISPATCH_QUEUE_SERIAL);
[FFmpegKit executeWithArgumentsAsync:arguments
withCompleteCallback:^(FFmpegSession *session) {
NSLog(@"Completed session %ld", [session getSessionId]);
}
withLogCallback:nil
withStatisticsCallback:nil
onDispatchQueue:queue];Each execute call creates a session.
FFmpegSession *session = [FFmpegKit execute:@"-i file1.mp4 -c:v mpeg4 file2.mp4"];
long sessionId = [session getSessionId];
NSString *command = [session getCommand];
NSArray *arguments = [session getArguments];
SessionState state = [session getState];
ReturnCode *returnCode = [session getReturnCode];
NSDate *startTime = [session getStartTime];
NSDate *endTime = [session getEndTime];
long duration = [session getDuration];
NSString *output = [session getOutput];
NSString *failStackTrace = [session getFailStackTrace];
NSArray *logs = [session getLogs];
NSArray *allLogs = [session getAllLogs];
NSArray *statistics = [session getStatistics];
Statistics *lastStatistics = [session getLastReceivedStatistics];Session history can be queried and bounded:
[FFmpegKitConfig setSessionHistorySize:100];
NSArray *sessions = [FFmpegKitConfig getSessions];
NSArray *ffmpegSessions = [FFmpegKitConfig getFFmpegSessions];
NSArray *runningSessions = [FFmpegKitConfig getSessionsByState:SessionStateRunning];
id<Session> lastSession = [FFmpegKitConfig getLastSession];
id<Session> lastCompletedSession = [FFmpegKitConfig getLastCompletedSession];
[FFmpegKitConfig deleteSession:sessionId];#include <ffmpegkit/FFprobeKit.h>
NSString *ffprobeCommand =
[NSString stringWithFormat:@"-hide_banner -v error -show_format -show_streams %@", inputPath];
FFprobeSession *session = [FFprobeKit execute:ffprobeCommand];
if (![ReturnCode isSuccess:[session getReturnCode]]) {
NSLog(@"FFprobe failed. Output: %@", [session getOutput]);
}Asynchronous execution:
[FFprobeKit executeAsync:ffprobeCommand
withCompleteCallback:^(FFprobeSession *session) {
NSLog(@"FFprobe output: %@", [session getOutput]);
}];For paths that may contain spaces, use arguments:
NSArray *arguments = @[
@"-hide_banner",
@"-v", @"error",
@"-show_format",
@"-show_streams",
inputPath
];
FFprobeSession *session = [FFprobeKit executeWithArguments:arguments];MediaInformationSession *session = [FFprobeKit getMediaInformation:inputPath];
if ([ReturnCode isSuccess:[session getReturnCode]]) {
MediaInformation *mediaInformation = [session getMediaInformation];
NSLog(@"format=%@ duration=%@ size=%@",
[mediaInformation getFormat],
[mediaInformation getDuration],
[mediaInformation getSize]);
for (StreamInformation *stream in [mediaInformation getStreams]) {
NSLog(@"stream=%@ codec=%@ width=%@ height=%@",
[stream getType],
[stream getCodec],
[stream getWidth],
[stream getHeight]);
}
} else {
NSLog(@"Media information failed. Output: %@", [session getOutput]);
}Asynchronous media information:
[FFprobeKit getMediaInformationAsync:inputPath
withCompleteCallback:^(MediaInformationSession *session) {
MediaInformation *mediaInformation = [session getMediaInformation];
NSLog(@"duration=%@", [mediaInformation getDuration]);
}];Use the command variants when you need a custom FFprobe command. The command must print JSON media information.
NSArray *arguments = @[
@"-v", @"error",
@"-print_format", @"json",
@"-show_format",
@"-show_streams",
inputPath
];
[FFprobeKit getMediaInformationFromCommandArgumentsAsync:arguments
withCompleteCallback:^(MediaInformationSession *session) {
MediaInformation *info = [session getMediaInformation];
NSLog(@"format=%@", [info getFormat]);
}
withLogCallback:nil
onDispatchQueue:dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)
withTimeout:10000];// Cancel all running FFmpeg sessions.
[FFmpegKit cancel];
// Cancel one session.
[FFmpegKit cancel:sessionId];[FFmpegKitConfig enableFFmpegSessionCompleteCallback:^(FFmpegSession *session) {
NSLog(@"Global FFmpeg callback: %ld", [session getSessionId]);
}];
[FFmpegKitConfig enableFFprobeSessionCompleteCallback:^(FFprobeSession *session) {
NSLog(@"Global FFprobe callback: %ld", [session getSessionId]);
}];
[FFmpegKitConfig enableMediaInformationSessionCompleteCallback:^(MediaInformationSession *session) {
NSLog(@"Global media information callback: %ld", [session getSessionId]);
}];
[FFmpegKitConfig enableLogCallback:^(Log *log) {
NSLog(@"%@", [log getMessage]);
}];
[FFmpegKitConfig enableStatisticsCallback:^(Statistics *statistics) {
NSLog(@"time=%f bitrate=%f", [statistics getTime], [statistics getBitrate]);
}];
[FFmpegKitConfig setLogLevel:LevelAVLogInfo];Redirection is enabled by default. If you call [FFmpegKitConfig disableRedirection], logs go to stderr, callbacks are disabled and FFprobeKit media information helpers do not work.
For advanced cases, create a session yourself and choose a log redirection strategy:
FFmpegSession *session =
[FFmpegSession create:arguments
withCompleteCallback:^(FFmpegSession *session) {
NSLog(@"Completed with rc %@", [session getReturnCode]);
}
withLogCallback:nil
withStatisticsCallback:nil
withLogRedirectionStrategy:LogRedirectionStrategyNeverPrintLogs];
[FFmpegKitConfig asyncFFmpegExecute:session];drawtext requires an iOS artifact that includes freetype and harfbuzz. If you want font family lookup through registered font directories, the artifact must also include fontconfig.
Register application fonts before running commands that need them:
NSString *resourceFolder = [[NSBundle mainBundle] resourcePath];
NSDictionary *fontNameMapping = @{@"MyFontName": @"Doppio One"};
[FFmpegKitConfig setFontDirectoryList:@[
resourceFolder,
@"/System/Library/Fonts"
] with:fontNameMapping];You can then use the mapped name in filters that use fontconfig:
NSString *command =
@"-i input.mp4 -vf drawtext=font='MyFontName':text='Hello':x=20:y=20 output.mp4";Default FFmpegKitNext iOS artifacts support the custom ffkitmem: and ffkitstream: protocols. If your artifact was built without FFmpegKit protocol support, these examples will not work.
Use FFmpegKitInputBuffer and FFmpegKitOutputBuffer for finite, seekable memory-backed input and output. This avoids temporary files for data that already lives in memory.
#include <ffmpegkit/FFmpegKitInputBuffer.h>
#include <ffmpegkit/FFmpegKitOutputBuffer.h>
NSData *inputData = [NSData dataWithContentsOfFile:inputImagePath];
FFmpegKitInputBuffer *input = [FFmpegKitInputBuffer fromData:inputData extension:@"jpg"];
FFmpegKitOutputBuffer *output = [FFmpegKitOutputBuffer create:@"jpg"];
NSArray *arguments = @[
@"-hide_banner",
@"-y",
@"-i", [input getUrl],
@"-vf", @"scale=640:-2",
[output getUrl]
];
[FFmpegKit executeWithArgumentsAsync:arguments
withCompleteCallback:^(FFmpegSession *session) {
if ([ReturnCode isSuccess:[session getReturnCode]]) {
NSData *result = [output toData];
NSLog(@"Created %ld bytes", (long)[result length]);
} else {
NSLog(@"FFmpeg failed: %@", [session getAllLogsAsString]);
}
[input close];
[output close];
}];asDataNoCopy returns an NSData view backed by native memory. That view is valid only until the output buffer is closed or FFmpeg writes more data to the same resource.
Use FFmpegKitStreamInput when data arrives over time. Choose an FFmpeg demuxer that can read non-seekable input.
#include <ffmpegkit/FFmpegKitStreamInput.h>
FFmpegKitStreamInput *streamInput = [FFmpegKitStreamInput create:@"raw"];
NSArray *arguments = @[
@"-f", @"s16le",
@"-ar", @"44100",
@"-ac", @"1",
@"-i", [streamInput getUrl],
@"-f", @"wav",
outputPath
];
[FFmpegKit executeWithArgumentsAsync:arguments
withCompleteCallback:^(FFmpegSession *session) {
NSLog(@"stream input rc=%@", [session getReturnCode]);
[streamInput close];
}];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
for (NSData *pcmChunk in pcmChunks) {
[streamInput write:pcmChunk timeout:5000];
}
[streamInput closeInput];
});Use FFmpegKitStreamOutput when the application wants to consume output incrementally. Choose a muxer that can write to non-seekable output, such as mpegts.
#include <ffmpegkit/FFmpegKitStreamOutput.h>
FFmpegKitStreamOutput *streamOutput = [FFmpegKitStreamOutput create:@"ts"];
NSArray *arguments = @[
@"-i", inputPath,
@"-f", @"mpegts",
[streamOutput getUrl]
];
[FFmpegKit executeWithArgumentsAsync:arguments
withCompleteCallback:^(FFmpegSession *session) {
NSLog(@"stream output rc=%@", [session getReturnCode]);
[streamOutput close];
}];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
NSData *chunk = [streamOutput read:32768 timeout:1000];
if ([chunk length] > 0) {
// Process the first output chunk.
}
});Named pipes are available for workflows that need a filesystem path.
NSString *pipePath = [FFmpegKitConfig registerNewFFmpegPipe];
NSArray *arguments = @[
@"-y",
@"-f", @"image2pipe",
@"-i", pipePath,
@"-c:v", @"mpeg4",
outputPath
];
[FFmpegKit executeWithArgumentsAsync:arguments
withCompleteCallback:^(FFmpegSession *session) {
[FFmpegKitConfig closeFFmpegPipe:pipePath];
NSLog(@"pipe rc=%@", [session getReturnCode]);
}];
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
NSFileHandle *writer = [NSFileHandle fileHandleForWritingAtPath:pipePath];
[writer writeData:imageData];
[writer closeFile];
});Applications that embed Mono-based runtimes, such as Unity or Xamarin applications, should ignore SIGXCPU before running FFmpeg commands.
[FFmpegKitConfig ignoreSignal:SignalXcpu];See the iOS and iPadOS test applications in the ffmpeg-kit-next-test project for full Objective-C and Swift examples, including command execution, HTTPS media information, pipes, concurrent execution and FFmpegKit protocols.
Copyright (c) 2026 FFmpegKitNext
- Status
- Versions
- Changelog
- Project Layout
- Using
- Building
- External Libraries
- Patents
- License