Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
# `streaming-decoder.ts`
## Purpose
This module implements the `StreamingDecoder` contract from
`@suaveplan/audio-codecs` over ffmpeg.wasm. The implementation is
buffered emulation — ffmpeg.wasm is whole-file in / whole-file out and
cannot produce decoded pages incrementally, so this class accumulates
chunks in memory and runs the decode pipeline once during `flush`.
## Features
- `write(chunk)` buffers without touching the worker; returns `[]`.
- `flushAsync()` runs the decode pipeline and caches the result.
- `flush()` (synchronous, per the contract) returns the cached result
populated by a prior `flushAsync()` call.
- Idempotent `flush` / `flushAsync` — subsequent calls return cached.
- `close()` clears buffered chunks; idempotent.
## Usage: Basic
```ts
import { FfmpegWasmStreamingDecoder } from "@suaveplan/audio-codec-ffmpeg-wasm/streaming/streaming-decoder";
const decoder = new FfmpegWasmStreamingDecoder({ host, format: "wav" });
for (const chunk of chunks) decoder.write(chunk);
const result = await decoder.flushAsync();
decoder.close();
```
## Usage: Advanced
```ts
const decoder = new FfmpegWasmStreamingDecoder({
host,
format: "mp3",
sampleRate: 44_100,
channels: 2,
timeoutMs: 30_000,
signal: controller.signal,
});
let consumed = 0;
for await (const chunk of source) {
decoder.write(chunk);
consumed += chunk.byteLength;
}
const decoded = await decoder.flushAsync();
// Subsequent synchronous flush() calls return the cached result so
// CodecRegistry.decodeStream can pull frames in lock-step.
const sync = decoder.flush();
```
## API Reference
- `class FfmpegWasmStreamingDecoder implements StreamingDecoder`.
- `new FfmpegWasmStreamingDecoder(opts: StreamingDecoderOptions)`.
- `decoder.write(chunk): readonly DecodeResult[]`.
- `decoder.flush(): readonly DecodeResult[]`.
- `decoder.flushAsync(): Promise<DecodeResult | null>`.
- `decoder.close(): void`.
- `interface StreamingDecoderOptions`.
## Implementation Notes
The `StreamingDecoder` interface in `@suaveplan/audio-codecs` is fully
synchronous. ffmpeg.wasm cannot satisfy that synchronously, so the
implementation exposes an additional `flushAsync()` method that callers
must await before they invoke `flush()` to retrieve the result. The
`CodecRegistry.decodeStream` consumer in `audio-codecs` calls `flush()`
synchronously, so a wrapping `ReadableStream` `pull` body must await
`flushAsync()` first. Inside an async pull body the wait is natural.
Progress events fire only during the `flushAsync()` call, never during
`write()`.