import type { AudioTrack } from "../types"; import { type PCMFrame } from "./SoftwareAudioDecoder"; import { WasmBindings } from "../wasm/bindings"; export declare class MoviAudioDecoder { private decoder; private swDecoder; private bindings; private useSoftware; private pendingData; private pendingPCM; private pendingChunks; private isConfigured; private onData; private onPCM; private onError; private onBroken; private currentExtradata; private swFramesProduced; private swRevertAttempts; private static readonly MAX_SW_REVERTS; private currentTrack; private hasTriedSoftwareFallback; private hasDescription; private _downmix; constructor(); setDownmix(downmix: boolean): void; /** * Declare the audio broken on its FIRST failed packet instead of riding out a * run of them — for a caller that already knows recovery is impossible. See * SoftwareAudioDecoder.maxConsecutiveFailures for why waiting can never * finish in that case. Remembered so it survives a decoder rebuild. */ private _failFast; setFailFast(on: boolean): void; setBindings(bindings: WasmBindings): void; /** True when audio runs through the WASM software decoder (TrueHD/DTS/AC-3/ * Opus/FLAC/…) rather than WebCodecs. The software path decodes sub-realtime * on a cold start, so callers can gate cold-start mitigations on this. */ get usesSoftware(): boolean; /** * Configure the decoder for a specific track */ configure(track: AudioTrack, extradata?: Uint8Array): Promise; /** * Every codec, in software. FFmpeg WASM decodes all of them; WebCodecs is not * asked for any. * * This is where it started, and it is where it has come back to. The reasons * on both legs of that trip are worth keeping, because the trip will look * tempting again. * * The software side used to be a list — eac3, ac3, dts, dca, truehd, mlp, * opus, flac — and each entry was added after its own WebCodecs failure: * packet gaps it choked on, a FLAC description it would not accept, channel * layouts it dropped. What the list really recorded is that the browser's * audio decoders disagree with each other and with the demuxer feeding them, * and that every disagreement was found by a user HEARING it rather than by * a test. It only ever grew, so eventually it swallowed everything. * * It was then opened back up, offering each codec to WebCodecs and letting it * fall through to WASM only on failure. The motivation was real and still is: * WASM audio decode runs on the same thread as demux and render, and on a * heavy source that thread is the scarce one — measured on 4K60 HEVC at 74 * Mbps, software AAC left the audio buffer oscillating against its starve * line and each dip cost the picture a couple of seconds of dropped video. * * What the reopening got wrong is the shape of the evidence. FLAC failed * immediately and audibly, in all three browsers, so it went back at once. * Opus passed everything put to it — three real music-video MKVs with seeks, * across Chrome, Firefox and Safari, thousands of decodes, not one error — * and broke in the owner's hands within hours. The tests used well-formed * local files; the failure the original note described was packet gaps in * this player's TYPICAL inputs, which is the split-audio path those tests * never touched. Clean files passing said nothing about the files that * matter. * * So: one path again, for every codec. If audio CPU becomes the problem, the * lever is not putting WebCodecs back — it is fewer live source nodes, or * moving software decode off the main thread. Anyone reopening this owes a * reproduction of the failure case, not another sweep of the files to hand. */ static needsSoftwareDecoding(_codec: string): boolean; /** * The software fallback gave up: 50 packets in a row rejected. * * If it never decoded a single frame, the fallback itself is the problem — * the hardware decoder had been running fine until one EncodingError, and * swapping it for a decoder that rejects EVERYTHING trades a hiccup for * permanent silence (seen in the wild: `sendPacket failed: -1094995529` * repeating for the rest of the video, with a manual seek only resetting the * counter so it could fail another 50 times). So put the hardware decoder * back and let the owner re-align the source. * * If it HAD been producing audio, the stream is just out of step — leave it * in place and let the owner's re-align handle it. */ private handleSoftwareBroken; private initSoftwareDecoder; /** * Map FFmpeg codec names to WebCodecs codec strings */ private mapCodecToWebCodecs; /** * Decode an encoded audio chunk */ /** * True while packets are going through the WASM software decoder, i.e. when * decodeBatch() is worth using. Codecs that land here (TrueHD/MLP/DTS) emit * very small access units, so the per-packet round-trip dominates. */ canBatch(): boolean; /** * Decode a run of packets in as few WASM round-trips as possible. Only the * software path actually batches; WebCodecs has no such cost (and its own * queue), so it just replays them one by one. Returns packets consumed — * always all of them unless the software batch stopped early (format change * or pts discontinuity), in which case the caller re-submits the rest. */ decodeBatch(packets: { data: Uint8Array; pts: number; }[]): number; decode(data: Uint8Array, timestamp: number, keyframe: boolean): void; /** * Set data output callback */ setOnData(callback: (data: AudioData) => void): void; /** * Set PCM frame output callback (used by the software decoder path). */ setOnPCM(callback: (frame: PCMFrame) => void): void; /** * Set error callback */ setOnError(callback: (error: Error) => void): void; /** * Fires when the software decoder's failure circuit-breaker trips — every * packet is being rejected and audio has gone silent. The owner is expected * to re-align the audio source (seek it back to the playhead) and flush, * which clears the breaker; a manual seek did exactly that by accident. */ setOnBroken(callback: () => void): void; /** * Flush the decoder */ flush(): Promise; /** * Reset the decoder */ reset(): void; /** * Close the decoder */ close(): void; /** * Check if decoder is configured */ get configured(): boolean; /** * Get queue size */ get queueSize(): number; /** * Get decoder stats for nerd stats overlay */ getStats(): { decoderType: string; queueSize: number; }; /** * Strip ADTS header from AAC packet data if present. * ADTS header is 7 bytes (without CRC) or 9 bytes (with CRC). * Sync word: 0xFFF (12 bits). */ private static stripAdtsHeader; } //# sourceMappingURL=AudioDecoder.d.ts.map