2.9 KiB
stream-encoder
Purpose
createStreamingEncoder returns a FfmpegStreamingEncoder — the
@suaveplan/audio-codecs StreamingEncoder plus an additive Promise-returning
flushAsync() companion. It backs the encoder with a single long-running
ffmpeg subprocess that consumes interleaved f32le PCM on stdin and emits the
selected container format on stdout, lazily spawned on the first start() so
that an encoder constructed but never used pays no process cost.
Features
- Single persistent ffmpeg subprocess per encoder.
- Lazy spawn on first
start(); channel count chosen by the caller. - Strict
validatePcmon everywriteraisesINVALID_PCMon bad input. - Sync
start/write/flushhonour the base contract; additiveflushAsync(): Promise<Uint8Array>waits for ffmpeg to drain. AbortSignalandtimeoutMsintegrated — both raise canonicalABORTED/TIMEOUTcodes on the nextflushAsync.
Usage: Basic
import { FfmpegNativeCodec } from "@suaveplan/audio-codec-ffmpeg-native";
const encoder = new FfmpegNativeCodec().createEncoder({
sampleRate: 48_000,
format: "flac",
});
encoder.start(48_000, channelData.length);
encoder.write(channelData);
const tail = await encoder.flushAsync();
process.stdout.write(tail);
encoder.close();
Usage: Advanced
import { FfmpegNativeCodec } from "@suaveplan/audio-codec-ffmpeg-native";
const codec = new FfmpegNativeCodec({ timeoutMs: 10_000 });
const encoder = codec.createEncoder({ format: "opus" });
encoder.start(48_000, 2);
for (const block of pcmBlocks()) encoder.write(block);
const finalBytes = await encoder.flushAsync();
encoder.close();
API Reference
createStreamingEncoder(opts: StreamingEncoderOptions): FfmpegStreamingEncoderFfmpegStreamingEncoder— extendsStreamingEncoderwithflushAsync(): Promise<Uint8Array>.StreamingEncoderOptions—binaryProvider,argsFor(channels), optionalsignal,timeoutMs,spawn.
Implementation Notes
The base StreamingEncoder.start() is documented as returning header bytes;
ffmpeg emits headers on stdout as part of the normal encode flow, so we
return an empty Uint8Array from start() and let the headers fall out of
the first write / flushAsync call. This is consistent with how all the
real-world container formats (FLAC, Opus, MP3) actually framing their headers
on the same byte stream as their data.
write returns synchronously with whatever bytes ffmpeg has already pushed
out of stdout. For most encoders the first synchronous return after a single
write is empty because ffmpeg buffers frames; callers MUST await
flushAsync to retrieve trailing bytes.
chosenChannels is captured in start() (or, defensively, the length of
the first write call's channelData) because the encode argv embeds the
-ac <n> flag — once ffmpeg is running, the channel count cannot change.