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; 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 no * longer asked for any. * * It used to be a list — eac3, ac3, dts, dca, truehd, mlp, opus, flac — each * added after its own WebCodecs failure: packet gaps it choked on, a FLAC * description it would not accept, channel layouts it dropped. What that 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. * * The split had a second cost that only showed up once audio gained a * bitrate ladder: with AAC native and opus in WASM, choosing a rung meant * choosing a decoder, so a cheaper stream could cost more CPU than the * expensive one and the ladder could not simply follow the link. One path * makes bitrate the only variable again. * * The price is real and worth saying: AAC now decodes in WASM rather than in * the browser's own code, which is more CPU per packet on exactly the phones * where audio is already tight. Multi-channel and the error path came here * anyway, so this is the same code that was already carrying the hard cases. */ private needsSoftwareDecoding; /** * 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