Initial Commit
This commit is contained in:
@@ -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()`.
|
||||
Reference in New Issue
Block a user