first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
# `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 `validatePcm` on every `write` raises `INVALID_PCM` on bad input.
- Sync `start` / `write` / `flush` honour the base contract; additive
`flushAsync(): Promise<Uint8Array>` waits for ffmpeg to drain.
- `AbortSignal` and `timeoutMs` integrated — both raise canonical
`ABORTED` / `TIMEOUT` codes on the next `flushAsync`.
## Usage: Basic
```ts
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
```ts
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): FfmpegStreamingEncoder`
- `FfmpegStreamingEncoder` — extends `StreamingEncoder` with
`flushAsync(): Promise<Uint8Array>`.
- `StreamingEncoderOptions` — `binaryProvider`, `argsFor(channels)`,
optional `signal`, `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.