-
Notifications
You must be signed in to change notification settings - Fork 25
FFmpegKit Stream Protocol
ffkitstream: lets FFmpegKit exchange bytes with a command while it is running.
Use it when the complete input or output is not available up front. Stream
inputs can be read by FFmpeg or FFprobe; stream outputs are produced by FFmpeg.
Streams are not seekable. They work best with formats that can be read or written sequentially.
Use streaming input when your app produces data that FFmpeg or FFprobe should consume.
val stream = FFmpegKitStreamInput.create("mp3")
FFmpegKit.executeAsync(
"-i ${stream.getUrl()} -c:a aac ${context.cacheDir}/out.m4a"
) {
stream.close()
}
try {
while (hasMoreAudio()) {
val chunk = nextAudioChunk()
stream.write(chunk)
}
stream.closeInput()
} catch (t: Throwable) {
stream.close()
throw t
}closeInput() tells the command that no more input bytes are coming. close()
releases the native stream.
Writes can block when the stream buffer is full. To use a timeout:
val accepted = stream.write(chunk, timeoutMs = 1000)The returned value is the number of bytes accepted. It can be smaller than the chunk size when a timeout happens.
Use streaming output when FFmpeg produces data that your app wants to consume while the command is running.
val stream = FFmpegKitStreamOutput.create("mp3")
FFmpegKit.executeAsync(
"-i ${context.cacheDir}/input.wav -f mp3 ${stream.getUrl()}"
) {
stream.close()
}
try {
while (true) {
val chunk = stream.read(maxBytes = 32 * 1024, timeoutMs = 1000)
if (chunk == null) {
// No data before timeout. Keep waiting or check session state.
continue
}
if (chunk.isEmpty()) {
// FFmpeg closed the output stream.
break
}
consumeOutput(chunk)
}
} finally {
stream.close()
}Native Android reads return null on timeout and an empty ByteArray at end of
output.
Apple uses Objective-C wrappers:
FFmpegKitStreamInput *stream = [FFmpegKitStreamInput create:@"mp3"];
[FFmpegKit executeAsync:
[NSString stringWithFormat:@"-i %@ -c:a aac out.m4a", [stream getUrl]]
withCompleteCallback:^(FFmpegSession *session) {
[stream close];
}];
[stream write:data timeout:1000];
[stream closeInput];For streaming output:
FFmpegKitStreamOutput *stream = [FFmpegKitStreamOutput create:@"mp3"];
[FFmpegKit executeAsync:
[NSString stringWithFormat:@"-i input.wav -f mp3 %@", [stream getUrl]]
withCompleteCallback:^(FFmpegSession *session) {
[stream close];
}];
NSData *chunk = [stream read:32768 timeout:1000];Apple reads return nil on timeout and empty NSData at end of output.
Linux uses C++ wrappers:
auto stream = ffmpegkit::FFmpegKitStreamInput::create("mp3");
ffmpegkit::FFmpegKit::executeAsync(
"-i " + stream->getUrl() + " -c:a aac out.m4a",
[stream](auto session) {
stream->close();
});
stream->write(data, 1000);
stream->closeInput();For streaming output:
auto stream = ffmpegkit::FFmpegKitStreamOutput::create("mp3");
ffmpegkit::FFmpegKit::executeAsync(
"-i input.wav -f mp3 " + stream->getUrl(),
[stream](auto session) {
stream->close();
});
auto chunk = stream->read(32768, 1000);Linux reads return nullptr on timeout and an empty vector at end of output.
Flutter uses asynchronous stream wrappers:
final stream = await FFmpegKitStreamInput.create(extension: 'mp3');
await FFmpegKit.executeAsync(
'-i ${stream.getUrl()} -c:a aac out.m4a',
(session) async {
await stream.close();
},
);
await stream.write(chunk, timeoutMs: 1000);
await stream.closeInput();For streaming output:
final stream = await FFmpegKitStreamOutput.create(extension: 'mp3');
await FFmpegKit.executeAsync(
'-i input.wav -f mp3 ${stream.getUrl()}',
(session) async {
await stream.close();
},
);
final chunk = await stream.read(32 * 1024, timeoutMs: 1000);Flutter returns an empty Uint8List when no bytes are returned by the native
layer, including end-of-output.
React Native exchanges stream data as base64 strings.
const stream = await FFmpegKitStreamInput.create('mp3');
await FFmpegKit.executeAsync(
`-i ${stream.getUrl()} -c:a aac out.m4a`,
async () => {
await stream.close();
}
);
await stream.write(base64Chunk, 1000);
await stream.closeInput();For streaming output:
const stream = await FFmpegKitStreamOutput.create('mp3');
await FFmpegKit.executeAsync(
`-i input.wav -f mp3 ${stream.getUrl()}`,
async () => {
await stream.close();
}
);
const chunkBase64 = await stream.read(32 * 1024, 1000);React Native returns an empty string when no base64 data is returned by the native layer, including end-of-output.
- Default stream capacity is 1 MiB unless you pass a custom capacity.
- Use
timeoutMs = -1or omit the timeout to wait indefinitely. - Use
timeoutMs = 0for an immediate timeout check. - Always call
close()when done, even if the command fails.
Copyright (c) 2026 FFmpegKitNext
- Status
- Versions
- Changelog
- Project Layout
- Using
- Building
- External Libraries
- Patents
- License