# `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`. - `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()`.