47 lines
2.1 KiB
TypeScript
47 lines
2.1 KiB
TypeScript
/**
|
|
* OpusCapability - runtime detection of WebCodecs Ogg-Opus decode support.
|
|
*
|
|
* The Opus decode path is a WebCodecs `AudioDecoder` streaming pipeline (OpusStreamDecoder), NOT
|
|
* `decodeAudioData`. So the capability gate must test the path actually used: whether the browser has
|
|
* `AudioDecoder` AND supports the `codec: 'opus'` config. `AudioDecoder` is available on Chrome/Edge,
|
|
* Firefox 130+, and Safari 16.4+; older Safari and older Firefox lack it, and those listeners fall back
|
|
* to the universal lossless WAV path (§3.4 / OQ2 / AC7 — no listener ever gets silence over a codec gap).
|
|
*
|
|
* This module is the detection *seam* only — it answers "can this browser stream-decode Opus via
|
|
* WebCodecs?". The player (StreamingAudioPlayerService.ResolveStreamFormatAsync) consumes the answer to
|
|
* choose the delivery format; this module never touches the player or the stream request. The result is
|
|
* cached after the first probe (capability does not change within a session).
|
|
*/
|
|
|
|
let cachedSupport: Promise<boolean> | null = null;
|
|
|
|
/**
|
|
* Resolve whether this browser can stream-decode Ogg Opus via WebCodecs. Cached after the first call.
|
|
* Never rejects — any failure (no AudioDecoder, unsupported config, thrown probe) resolves to `false`
|
|
* (treat as unsupported, fall back to lossless) so an interop error can never silence playback.
|
|
*/
|
|
export function canDecodeOggOpus(): Promise<boolean> {
|
|
if (cachedSupport === null) {
|
|
cachedSupport = probe();
|
|
}
|
|
return cachedSupport;
|
|
}
|
|
|
|
async function probe(): Promise<boolean> {
|
|
try {
|
|
if (typeof AudioDecoder === 'undefined' || typeof AudioDecoder.isConfigSupported !== 'function') {
|
|
return false;
|
|
}
|
|
// 48 kHz stereo is the canonical fullband Opus shape this site produces. isConfigSupported does
|
|
// not need the OpusHead `description` to report codec support, so we probe without it.
|
|
const result = await AudioDecoder.isConfigSupported({
|
|
codec: 'opus',
|
|
sampleRate: 48000,
|
|
numberOfChannels: 2
|
|
});
|
|
return result.supported === true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|