diff --git a/src/media-sink.ts b/src/media-sink.ts index 01d82cf..6fd28b8 100644 --- a/src/media-sink.ts +++ b/src/media-sink.ts @@ -38,6 +38,7 @@ import { isWebKit, last, mapAsyncGenerator, + missingWebCodecsClassMessage, promiseWithResolvers, removeItem, Rotation, @@ -1691,8 +1692,12 @@ export class VideoSampleSink extends BaseMediaSampleSink { onError: (error: unknown) => unknown, ) { if (!(await this._track.canDecode())) { + if (typeof VideoDecoder === 'undefined') { + throw new Error(missingWebCodecsClassMessage('VideoDecoder')); + } + throw new Error( - 'This video track cannot be decoded by this browser. Make sure to check decodability before using' + 'This video track cannot be decoded in this environment. Make sure to check decodability before using' + ' a track.', ); } @@ -2426,8 +2431,12 @@ export class AudioSampleSink extends BaseMediaSampleSink { onError: (error: unknown) => unknown, ) { if (!(await this._track.canDecode())) { + if (typeof AudioDecoder === 'undefined') { + throw new Error(missingWebCodecsClassMessage('AudioDecoder')); + } + throw new Error( - 'This audio track cannot be decoded by this browser. Make sure to check decodability before using' + 'This audio track cannot be decoded in this environment. Make sure to check decodability before using' + ' a track.', ); } diff --git a/src/media-source.ts b/src/media-source.ts index 74bf8a7..c724b96 100644 --- a/src/media-source.ts +++ b/src/media-source.ts @@ -29,6 +29,7 @@ import { clearIntervalUnthrottled, floorToDivisor, last, + missingWebCodecsClassMessage, promiseWithResolvers, roundToDivisor, setInt24, @@ -718,7 +719,7 @@ class VideoEncoderWrapper { if (!selected) { if (typeof VideoEncoder === 'undefined') { - throw new Error('VideoEncoder is not supported by this browser.'); + throw new Error(missingWebCodecsClassMessage('VideoEncoder')); } // The candidates only differ in their rate control, so we describe them as one config with a @@ -731,7 +732,7 @@ class VideoEncoderWrapper { throw new Error( `This specific encoder configuration (${firstConfig.codec}, ${rateControls.join(' / ')},` + ` ${firstConfig.width}x${firstConfig.height}, hardware acceleration:` - + ` ${firstConfig.hardwareAcceleration ?? 'no-preference'}) is not supported by this browser.` + + ` ${firstConfig.hardwareAcceleration ?? 'no-preference'}) is not supported in this environment.` + ` Consider using another codec or changing your video parameters.`, ); } @@ -1694,8 +1695,8 @@ export class MediaStreamVideoTrackSource extends VideoSource { }); } else { throw new Error( - 'When no explicit frame rate is set, MediaStreamTrackProcessor is required; but it\'s not supported' - + ' by this browser.', + 'When no explicit frame rate is set, MediaStreamTrackProcessor is required; but it\'s not available' + + ' in this environment.', ); } } @@ -2210,7 +2211,7 @@ class AudioEncoderWrapper { this.initPcmEncoder(); } else { if (typeof AudioEncoder === 'undefined') { - throw new Error('AudioEncoder is not supported by this browser.'); + throw new Error(missingWebCodecsClassMessage('AudioEncoder')); } let supported: boolean; @@ -2226,7 +2227,8 @@ class AudioEncoderWrapper { throw new Error( `This specific encoder configuration (${encoderConfig.codec}, ${encoderConfig.bitrate} bps,` + ` ${encoderConfig.numberOfChannels} channels, ${encoderConfig.sampleRate} Hz) is not` - + ` supported by this browser. Consider using another codec or changing your audio parameters.`, + + ` supported in this environment. Consider using another codec or changing your` + + ` audio parameters.`, ); } diff --git a/src/misc.ts b/src/misc.ts index 60fea06..3b0d98a 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -721,6 +721,16 @@ export const getChromiumVersion = () => { return chromiumVersionCache = Number(match[1]!); }; +export const missingWebCodecsClassMessage = (className: string) => { + if (typeof globalThis.isSecureContext !== 'undefined' && !globalThis.isSecureContext) { + // WebCodecs is not exposed in insecure contexts + return `${className} is not available in this environment; this may be because this page is running in an` + + ` insecure context. Try serving your page over HTTPS or use localhost.`; + } + + return `${className} is not available in this environment.`; +}; + /** * T or a promise that resolves to T. * @group Miscellaneous diff --git a/src/packet.ts b/src/packet.ts index 36901f4..f345b5d 100644 --- a/src/packet.ts +++ b/src/packet.ts @@ -155,7 +155,7 @@ export class EncodedPacket { throw new TypeError('Metadata-only packets cannot be converted to a video chunk.'); } if (typeof EncodedVideoChunk === 'undefined') { - throw new Error('Your browser does not support EncodedVideoChunk.'); + throw new Error('EncodedVideoChunk is not available in this environment.'); } return new EncodedVideoChunk({ @@ -179,7 +179,7 @@ export class EncodedPacket { throw new TypeError('Metadata-only packets cannot be converted to a video chunk.'); } if (typeof EncodedVideoChunk === 'undefined') { - throw new Error('Your browser does not support EncodedVideoChunk.'); + throw new Error('EncodedVideoChunk is not available in this environment.'); } return new EncodedVideoChunk({ @@ -198,7 +198,7 @@ export class EncodedPacket { throw new TypeError('Metadata-only packets cannot be converted to an audio chunk.'); } if (typeof EncodedAudioChunk === 'undefined') { - throw new Error('Your browser does not support EncodedAudioChunk.'); + throw new Error('EncodedAudioChunk is not available in this environment.'); } return new EncodedAudioChunk({