UNPKG

mediabunny

Version:

Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.

1,391 lines (1,387 loc) 679 kB
/*! * Copyright (c) 2025-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ "use strict"; var Mediabunny = (() => { var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { ALL_FORMATS: () => ALL_FORMATS, ALL_TRACK_TYPES: () => ALL_TRACK_TYPES, AUDIO_CODECS: () => AUDIO_CODECS, AudioBufferSink: () => AudioBufferSink, AudioBufferSource: () => AudioBufferSource, AudioSample: () => AudioSample, AudioSampleSink: () => AudioSampleSink, AudioSampleSource: () => AudioSampleSource, AudioSource: () => AudioSource, BaseMediaSampleSink: () => BaseMediaSampleSink, BlobSource: () => BlobSource, BufferSource: () => BufferSource, BufferTarget: () => BufferTarget, CanvasSink: () => CanvasSink, CanvasSource: () => CanvasSource, Conversion: () => Conversion, CustomAudioDecoder: () => CustomAudioDecoder, CustomAudioEncoder: () => CustomAudioEncoder, CustomVideoDecoder: () => CustomVideoDecoder, CustomVideoEncoder: () => CustomVideoEncoder, EncodedAudioPacketSource: () => EncodedAudioPacketSource, EncodedPacket: () => EncodedPacket, EncodedPacketSink: () => EncodedPacketSink, EncodedVideoPacketSource: () => EncodedVideoPacketSource, Input: () => Input, InputAudioTrack: () => InputAudioTrack, InputFormat: () => InputFormat, InputTrack: () => InputTrack, InputVideoTrack: () => InputVideoTrack, IsobmffInputFormat: () => IsobmffInputFormat, IsobmffOutputFormat: () => IsobmffOutputFormat2, MATROSKA: () => MATROSKA, MP3: () => MP3, MP4: () => MP4, MatroskaInputFormat: () => MatroskaInputFormat, MediaSource: () => MediaSource, MediaStreamAudioTrackSource: () => MediaStreamAudioTrackSource, MediaStreamVideoTrackSource: () => MediaStreamVideoTrackSource, MkvOutputFormat: () => MkvOutputFormat2, MovOutputFormat: () => MovOutputFormat, Mp3InputFormat: () => Mp3InputFormat, Mp3OutputFormat: () => Mp3OutputFormat, Mp4InputFormat: () => Mp4InputFormat, Mp4OutputFormat: () => Mp4OutputFormat, NON_PCM_AUDIO_CODECS: () => NON_PCM_AUDIO_CODECS, OGG: () => OGG, OggInputFormat: () => OggInputFormat, OggOutputFormat: () => OggOutputFormat, Output: () => Output, OutputFormat: () => OutputFormat, PCM_AUDIO_CODECS: () => PCM_AUDIO_CODECS, QTFF: () => QTFF, QUALITY_HIGH: () => QUALITY_HIGH, QUALITY_LOW: () => QUALITY_LOW, QUALITY_MEDIUM: () => QUALITY_MEDIUM, QUALITY_VERY_HIGH: () => QUALITY_VERY_HIGH, QUALITY_VERY_LOW: () => QUALITY_VERY_LOW, Quality: () => Quality, QuickTimeInputFormat: () => QuickTimeInputFormat, SUBTITLE_CODECS: () => SUBTITLE_CODECS, Source: () => Source, StreamSource: () => StreamSource, StreamTarget: () => StreamTarget, SubtitleSource: () => SubtitleSource, Target: () => Target, TextSubtitleSource: () => TextSubtitleSource, UrlSource: () => UrlSource, VIDEO_CODECS: () => VIDEO_CODECS, VideoSample: () => VideoSample, VideoSampleSink: () => VideoSampleSink, VideoSampleSource: () => VideoSampleSource, VideoSource: () => VideoSource, WAVE: () => WAVE, WEBM: () => WEBM, WavOutputFormat: () => WavOutputFormat, WaveInputFormat: () => WaveInputFormat, WebMInputFormat: () => WebMInputFormat, WebMOutputFormat: () => WebMOutputFormat, canEncode: () => canEncode, canEncodeAudio: () => canEncodeAudio, canEncodeSubtitles: () => canEncodeSubtitles, canEncodeVideo: () => canEncodeVideo, getEncodableAudioCodecs: () => getEncodableAudioCodecs, getEncodableCodecs: () => getEncodableCodecs, getEncodableSubtitleCodecs: () => getEncodableSubtitleCodecs, getEncodableVideoCodecs: () => getEncodableVideoCodecs, getFirstEncodableAudioCodec: () => getFirstEncodableAudioCodec, getFirstEncodableSubtitleCodec: () => getFirstEncodableSubtitleCodec, getFirstEncodableVideoCodec: () => getFirstEncodableVideoCodec, registerDecoder: () => registerDecoder, registerEncoder: () => registerEncoder }); // src/misc.ts function assert(x) { if (!x) { throw new Error("Assertion failed."); } } var normalizeRotation = (rotation) => { const mappedRotation = (rotation % 360 + 360) % 360; if (mappedRotation === 0 || mappedRotation === 90 || mappedRotation === 180 || mappedRotation === 270) { return mappedRotation; } else { throw new Error(`Invalid rotation ${rotation}.`); } }; var last = (arr) => { return arr && arr[arr.length - 1]; }; var isU32 = (value) => { return value >= 0 && value < 2 ** 32; }; var Bitstream = class _Bitstream { constructor(bytes2) { this.bytes = bytes2; /** Current offset in bits. */ this.pos = 0; } seekToByte(byteOffset) { this.pos = 8 * byteOffset; } readBit() { const byteIndex = Math.floor(this.pos / 8); const byte = this.bytes[byteIndex] ?? 0; const bitIndex = 7 - (this.pos & 7); const bit = (byte & 1 << bitIndex) >> bitIndex; this.pos++; return bit; } readBits(n) { if (n === 1) { return this.readBit(); } let result = 0; for (let i = 0; i < n; i++) { result <<= 1; result |= this.readBit(); } return result; } readAlignedByte() { if (this.pos % 8 !== 0) { throw new Error("Bitstream is not byte-aligned."); } const byteIndex = this.pos / 8; const byte = this.bytes[byteIndex] ?? 0; this.pos += 8; return byte; } skipBits(n) { this.pos += n; } getBitsLeft() { return this.bytes.length * 8 - this.pos; } clone() { const clone = new _Bitstream(this.bytes); clone.pos = this.pos; return clone; } }; var readExpGolomb = (bitstream) => { let leadingZeroBits = 0; while (bitstream.readBits(1) === 0 && leadingZeroBits < 32) { leadingZeroBits++; } if (leadingZeroBits >= 32) { throw new Error("Invalid exponential-Golomb code."); } const result = (1 << leadingZeroBits) - 1 + bitstream.readBits(leadingZeroBits); return result; }; var readSignedExpGolomb = (bitstream) => { const codeNum = readExpGolomb(bitstream); return (codeNum & 1) === 0 ? -(codeNum >> 1) : codeNum + 1 >> 1; }; var writeBits = (bytes2, start, end, value) => { for (let i = start; i < end; i++) { const byteIndex = Math.floor(i / 8); let byte = bytes2[byteIndex]; const bitIndex = 7 - (i & 7); byte &= ~(1 << bitIndex); byte |= (value & 1 << end - i - 1) >> end - i - 1 << bitIndex; bytes2[byteIndex] = byte; } }; var toUint8Array = (source) => { if (source instanceof Uint8Array) { return source; } else if (source instanceof ArrayBuffer) { return new Uint8Array(source); } else { return new Uint8Array(source.buffer, source.byteOffset, source.byteLength); } }; var toDataView = (source) => { if (source instanceof DataView) { return source; } else if (source instanceof ArrayBuffer) { return new DataView(source); } else { return new DataView(source.buffer, source.byteOffset, source.byteLength); } }; var textEncoder = new TextEncoder(); var invertObject = (object) => { return Object.fromEntries(Object.entries(object).map(([key, value]) => [value, key])); }; var COLOR_PRIMARIES_MAP = { bt709: 1, // ITU-R BT.709 bt470bg: 5, // ITU-R BT.470BG smpte170m: 6, // ITU-R BT.601 525 - SMPTE 170M bt2020: 9, // ITU-R BT.202 smpte432: 12 // SMPTE EG 432-1 }; var COLOR_PRIMARIES_MAP_INVERSE = invertObject(COLOR_PRIMARIES_MAP); var TRANSFER_CHARACTERISTICS_MAP = { "bt709": 1, // ITU-R BT.709 "smpte170m": 6, // SMPTE 170M "linear": 8, // Linear transfer characteristics "iec61966-2-1": 13, // IEC 61966-2-1 "pg": 16, // Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system "hlg": 18 // Rec. ITU-R BT.2100-2 hybrid loggamma (HLG) system }; var TRANSFER_CHARACTERISTICS_MAP_INVERSE = invertObject(TRANSFER_CHARACTERISTICS_MAP); var MATRIX_COEFFICIENTS_MAP = { "rgb": 0, // Identity "bt709": 1, // ITU-R BT.709 "bt470bg": 5, // ITU-R BT.470BG "smpte170m": 6, // SMPTE 170M "bt2020-ncl": 9 // ITU-R BT.2020-2 (non-constant luminance) }; var MATRIX_COEFFICIENTS_MAP_INVERSE = invertObject(MATRIX_COEFFICIENTS_MAP); var colorSpaceIsComplete = (colorSpace) => { return !!colorSpace && !!colorSpace.primaries && !!colorSpace.transfer && !!colorSpace.matrix && colorSpace.fullRange !== void 0; }; var isAllowSharedBufferSource = (x) => { return x instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && x instanceof SharedArrayBuffer || ArrayBuffer.isView(x); }; var AsyncMutex = class { constructor() { this.currentPromise = Promise.resolve(); } async acquire() { let resolver; const nextPromise = new Promise((resolve) => { resolver = resolve; }); const currentPromiseAlias = this.currentPromise; this.currentPromise = nextPromise; await currentPromiseAlias; return resolver; } }; var bytesToHexString = (bytes2) => { return [...bytes2].map((x) => x.toString(16).padStart(2, "0")).join(""); }; var reverseBitsU32 = (x) => { x = x >> 1 & 1431655765 | (x & 1431655765) << 1; x = x >> 2 & 858993459 | (x & 858993459) << 2; x = x >> 4 & 252645135 | (x & 252645135) << 4; x = x >> 8 & 16711935 | (x & 16711935) << 8; x = x >> 16 & 65535 | (x & 65535) << 16; return x >>> 0; }; var binarySearchExact = (arr, key, valueGetter) => { let low = 0; let high = arr.length - 1; let ans = -1; while (low <= high) { const mid = low + high >> 1; const midVal = valueGetter(arr[mid]); if (midVal === key) { ans = mid; high = mid - 1; } else if (midVal < key) { low = mid + 1; } else { high = mid - 1; } } return ans; }; var binarySearchLessOrEqual = (arr, key, valueGetter) => { let low = 0; let high = arr.length - 1; let ans = -1; while (low <= high) { const mid = low + (high - low + 1) / 2 | 0; const midVal = valueGetter(arr[mid]); if (midVal <= key) { ans = mid; low = mid + 1; } else { high = mid - 1; } } return ans; }; var insertSorted = (arr, item, valueGetter) => { const insertionIndex = binarySearchLessOrEqual(arr, valueGetter(item), valueGetter); arr.splice(insertionIndex + 1, 0, item); }; var promiseWithResolvers = () => { let resolve; let reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; }; var removeItem = (arr, item) => { const index = arr.indexOf(item); if (index !== -1) { arr.splice(index, 1); } }; var findLast = (arr, predicate) => { for (let i = arr.length - 1; i >= 0; i--) { if (predicate(arr[i])) { return arr[i]; } } return void 0; }; var findLastIndex = (arr, predicate) => { for (let i = arr.length - 1; i >= 0; i--) { if (predicate(arr[i])) { return i; } } return -1; }; var toAsyncIterator = async function* (source) { if (Symbol.iterator in source) { yield* source[Symbol.iterator](); } else { yield* source[Symbol.asyncIterator](); } }; var validateAnyIterable = (iterable) => { if (!(Symbol.iterator in iterable) && !(Symbol.asyncIterator in iterable)) { throw new TypeError("Argument must be an iterable or async iterable."); } }; var assertNever = (x) => { throw new Error(`Unexpected value: ${x}`); }; var getUint24 = (view2, byteOffset, littleEndian) => { const byte1 = view2.getUint8(byteOffset); const byte2 = view2.getUint8(byteOffset + 1); const byte3 = view2.getUint8(byteOffset + 2); if (littleEndian) { return byte1 | byte2 << 8 | byte3 << 16; } else { return byte1 << 16 | byte2 << 8 | byte3; } }; var getInt24 = (view2, byteOffset, littleEndian) => { return getUint24(view2, byteOffset, littleEndian) << 8 >> 8; }; var setUint24 = (view2, byteOffset, value, littleEndian) => { value = value >>> 0; value = value & 16777215; if (littleEndian) { view2.setUint8(byteOffset, value & 255); view2.setUint8(byteOffset + 1, value >>> 8 & 255); view2.setUint8(byteOffset + 2, value >>> 16 & 255); } else { view2.setUint8(byteOffset, value >>> 16 & 255); view2.setUint8(byteOffset + 1, value >>> 8 & 255); view2.setUint8(byteOffset + 2, value & 255); } }; var setInt24 = (view2, byteOffset, value, littleEndian) => { value = clamp(value, -8388608, 8388607); if (value < 0) { value = value + 16777216 & 16777215; } setUint24(view2, byteOffset, value, littleEndian); }; var setInt64 = (view2, byteOffset, value, littleEndian) => { if (littleEndian) { view2.setUint32(byteOffset + 0, value, true); view2.setInt32(byteOffset + 4, Math.floor(value / 2 ** 32), true); } else { view2.setInt32(byteOffset + 0, Math.floor(value / 2 ** 32), true); view2.setUint32(byteOffset + 4, value, true); } }; var mapAsyncGenerator = (generator, map) => { return { async next() { const result = await generator.next(); if (result.done) { return { value: void 0, done: true }; } else { return { value: map(result.value), done: false }; } }, return() { return generator.return(); }, throw(error) { return generator.throw(error); }, [Symbol.asyncIterator]() { return this; } }; }; var clamp = (value, min, max) => { return Math.max(min, Math.min(max, value)); }; var UNDETERMINED_LANGUAGE = "und"; var roundToPrecision = (value, digits) => { const factor = 10 ** digits; return Math.round(value * factor) / factor; }; var roundToMultiple = (value, multiple) => { return Math.round(value / multiple) * multiple; }; var ilog = (x) => { let ret = 0; while (x) { ret++; x >>= 1; } return ret; }; var ISO_639_2_REGEX = /^[a-z]{3}$/; var isIso639Dash2LanguageCode = (x) => { return ISO_639_2_REGEX.test(x); }; var SECOND_TO_MICROSECOND_FACTOR = 1e6 * (1 + Number.EPSILON); var mergeObjectsDeeply = (a, b) => { const result = { ...a }; for (const key in b) { if (typeof a[key] === "object" && a[key] !== null && typeof b[key] === "object" && b[key] !== null) { result[key] = mergeObjectsDeeply( a[key], b[key] ); } else { result[key] = b[key]; } } return result; }; var retriedFetch = async (url2, requestInit, getRetryDelay) => { let attempts = 0; while (true) { try { return await fetch(url2, requestInit); } catch (error) { attempts++; const retryDelayInSeconds = getRetryDelay(attempts); if (retryDelayInSeconds === null) { throw error; } console.error("Retrying failed fetch. Error:", error); if (!Number.isFinite(retryDelayInSeconds) || retryDelayInSeconds < 0) { throw new TypeError("Retry delay must be a non-negative finite number."); } if (retryDelayInSeconds > 0) { await new Promise((resolve) => setTimeout(resolve, 1e3 * retryDelayInSeconds)); } } } }; var computeRationalApproximation = (x, maxDenominator) => { const sign = x < 0 ? -1 : 1; x = Math.abs(x); let prevNumerator = 0, prevDenominator = 1; let currNumerator = 1, currDenominator = 0; let remainder = x; while (true) { const integer = Math.floor(remainder); const nextNumerator = integer * currNumerator + prevNumerator; const nextDenominator = integer * currDenominator + prevDenominator; if (nextDenominator > maxDenominator) { return { numerator: sign * currNumerator, denominator: currDenominator }; } prevNumerator = currNumerator; prevDenominator = currDenominator; currNumerator = nextNumerator; currDenominator = nextDenominator; remainder = 1 / (remainder - integer); if (!isFinite(remainder)) { break; } } return { numerator: sign * currNumerator, denominator: currDenominator }; }; var CallSerializer = class { constructor() { this.currentPromise = Promise.resolve(); } call(fn) { return this.currentPromise = this.currentPromise.then(fn); } }; var isSafariCache = null; var isSafari = () => { if (isSafariCache !== null) { return isSafariCache; } const result = !!(typeof navigator !== "undefined" && navigator.vendor?.match(/apple/i) && !navigator.userAgent?.match(/crios/i) && !navigator.userAgent?.match(/fxios/i) && !navigator.userAgent?.match(/Opera|OPT\//)); isSafariCache = result; return result; }; // src/custom-coder.ts var CustomVideoDecoder = class { /** Returns true iff the decoder can decode the given codec configuration. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars static supports(codec, config) { return false; } }; var CustomAudioDecoder = class { /** Returns true iff the decoder can decode the given codec configuration. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars static supports(codec, config) { return false; } }; var CustomVideoEncoder = class { /** Returns true iff the encoder can encode the given codec configuration. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars static supports(codec, config) { return false; } }; var CustomAudioEncoder = class { /** Returns true iff the encoder can encode the given codec configuration. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars static supports(codec, config) { return false; } }; var customVideoDecoders = []; var customAudioDecoders = []; var customVideoEncoders = []; var customAudioEncoders = []; var registerDecoder = (decoder) => { if (decoder.prototype instanceof CustomVideoDecoder) { const casted = decoder; if (customVideoDecoders.includes(casted)) { console.warn("Video decoder already registered."); return; } customVideoDecoders.push(casted); } else if (decoder.prototype instanceof CustomAudioDecoder) { const casted = decoder; if (customAudioDecoders.includes(casted)) { console.warn("Audio decoder already registered."); return; } customAudioDecoders.push(casted); } else { throw new TypeError("Decoder must be a CustomVideoDecoder or CustomAudioDecoder."); } }; var registerEncoder = (encoder) => { if (encoder.prototype instanceof CustomVideoEncoder) { const casted = encoder; if (customVideoEncoders.includes(casted)) { console.warn("Video encoder already registered."); return; } customVideoEncoders.push(casted); } else if (encoder.prototype instanceof CustomAudioEncoder) { const casted = encoder; if (customAudioEncoders.includes(casted)) { console.warn("Audio encoder already registered."); return; } customAudioEncoders.push(casted); } else { throw new TypeError("Encoder must be a CustomVideoEncoder or CustomAudioEncoder."); } }; // src/codec.ts var VIDEO_CODECS = [ "avc", "hevc", "vp9", "av1", "vp8" ]; var PCM_AUDIO_CODECS = [ "pcm-s16", // We don't prefix 'le' so we're compatible with the WebCodecs-registered PCM codec strings "pcm-s16be", "pcm-s24", "pcm-s24be", "pcm-s32", "pcm-s32be", "pcm-f32", "pcm-f32be", "pcm-f64", "pcm-f64be", "pcm-u8", "pcm-s8", "ulaw", "alaw" ]; var NON_PCM_AUDIO_CODECS = [ "aac", "opus", "mp3", "vorbis", "flac" ]; var AUDIO_CODECS = [ ...NON_PCM_AUDIO_CODECS, ...PCM_AUDIO_CODECS ]; var SUBTITLE_CODECS = [ "webvtt" ]; var AVC_LEVEL_TABLE = [ { maxMacroblocks: 99, maxBitrate: 64e3, level: 10 }, // Level 1 { maxMacroblocks: 396, maxBitrate: 192e3, level: 11 }, // Level 1.1 { maxMacroblocks: 396, maxBitrate: 384e3, level: 12 }, // Level 1.2 { maxMacroblocks: 396, maxBitrate: 768e3, level: 13 }, // Level 1.3 { maxMacroblocks: 396, maxBitrate: 2e6, level: 20 }, // Level 2 { maxMacroblocks: 792, maxBitrate: 4e6, level: 21 }, // Level 2.1 { maxMacroblocks: 1620, maxBitrate: 4e6, level: 22 }, // Level 2.2 { maxMacroblocks: 1620, maxBitrate: 1e7, level: 30 }, // Level 3 { maxMacroblocks: 3600, maxBitrate: 14e6, level: 31 }, // Level 3.1 { maxMacroblocks: 5120, maxBitrate: 2e7, level: 32 }, // Level 3.2 { maxMacroblocks: 8192, maxBitrate: 2e7, level: 40 }, // Level 4 { maxMacroblocks: 8192, maxBitrate: 5e7, level: 41 }, // Level 4.1 { maxMacroblocks: 8704, maxBitrate: 5e7, level: 42 }, // Level 4.2 { maxMacroblocks: 22080, maxBitrate: 135e6, level: 50 }, // Level 5 { maxMacroblocks: 36864, maxBitrate: 24e7, level: 51 }, // Level 5.1 { maxMacroblocks: 36864, maxBitrate: 24e7, level: 52 }, // Level 5.2 { maxMacroblocks: 139264, maxBitrate: 24e7, level: 60 }, // Level 6 { maxMacroblocks: 139264, maxBitrate: 48e7, level: 61 }, // Level 6.1 { maxMacroblocks: 139264, maxBitrate: 8e8, level: 62 } // Level 6.2 ]; var HEVC_LEVEL_TABLE = [ { maxPictureSize: 36864, maxBitrate: 128e3, tier: "L", level: 30 }, // Level 1 (Low Tier) { maxPictureSize: 122880, maxBitrate: 15e5, tier: "L", level: 60 }, // Level 2 (Low Tier) { maxPictureSize: 245760, maxBitrate: 3e6, tier: "L", level: 63 }, // Level 2.1 (Low Tier) { maxPictureSize: 552960, maxBitrate: 6e6, tier: "L", level: 90 }, // Level 3 (Low Tier) { maxPictureSize: 983040, maxBitrate: 1e7, tier: "L", level: 93 }, // Level 3.1 (Low Tier) { maxPictureSize: 2228224, maxBitrate: 12e6, tier: "L", level: 120 }, // Level 4 (Low Tier) { maxPictureSize: 2228224, maxBitrate: 3e7, tier: "H", level: 120 }, // Level 4 (High Tier) { maxPictureSize: 2228224, maxBitrate: 2e7, tier: "L", level: 123 }, // Level 4.1 (Low Tier) { maxPictureSize: 2228224, maxBitrate: 5e7, tier: "H", level: 123 }, // Level 4.1 (High Tier) { maxPictureSize: 8912896, maxBitrate: 25e6, tier: "L", level: 150 }, // Level 5 (Low Tier) { maxPictureSize: 8912896, maxBitrate: 1e8, tier: "H", level: 150 }, // Level 5 (High Tier) { maxPictureSize: 8912896, maxBitrate: 4e7, tier: "L", level: 153 }, // Level 5.1 (Low Tier) { maxPictureSize: 8912896, maxBitrate: 16e7, tier: "H", level: 153 }, // Level 5.1 (High Tier) { maxPictureSize: 8912896, maxBitrate: 6e7, tier: "L", level: 156 }, // Level 5.2 (Low Tier) { maxPictureSize: 8912896, maxBitrate: 24e7, tier: "H", level: 156 }, // Level 5.2 (High Tier) { maxPictureSize: 35651584, maxBitrate: 6e7, tier: "L", level: 180 }, // Level 6 (Low Tier) { maxPictureSize: 35651584, maxBitrate: 24e7, tier: "H", level: 180 }, // Level 6 (High Tier) { maxPictureSize: 35651584, maxBitrate: 12e7, tier: "L", level: 183 }, // Level 6.1 (Low Tier) { maxPictureSize: 35651584, maxBitrate: 48e7, tier: "H", level: 183 }, // Level 6.1 (High Tier) { maxPictureSize: 35651584, maxBitrate: 24e7, tier: "L", level: 186 }, // Level 6.2 (Low Tier) { maxPictureSize: 35651584, maxBitrate: 8e8, tier: "H", level: 186 } // Level 6.2 (High Tier) ]; var VP9_LEVEL_TABLE = [ { maxPictureSize: 36864, maxBitrate: 2e5, level: 10 }, // Level 1 { maxPictureSize: 73728, maxBitrate: 8e5, level: 11 }, // Level 1.1 { maxPictureSize: 122880, maxBitrate: 18e5, level: 20 }, // Level 2 { maxPictureSize: 245760, maxBitrate: 36e5, level: 21 }, // Level 2.1 { maxPictureSize: 552960, maxBitrate: 72e5, level: 30 }, // Level 3 { maxPictureSize: 983040, maxBitrate: 12e6, level: 31 }, // Level 3.1 { maxPictureSize: 2228224, maxBitrate: 18e6, level: 40 }, // Level 4 { maxPictureSize: 2228224, maxBitrate: 3e7, level: 41 }, // Level 4.1 { maxPictureSize: 8912896, maxBitrate: 6e7, level: 50 }, // Level 5 { maxPictureSize: 8912896, maxBitrate: 12e7, level: 51 }, // Level 5.1 { maxPictureSize: 8912896, maxBitrate: 18e7, level: 52 }, // Level 5.2 { maxPictureSize: 35651584, maxBitrate: 18e7, level: 60 }, // Level 6 { maxPictureSize: 35651584, maxBitrate: 24e7, level: 61 }, // Level 6.1 { maxPictureSize: 35651584, maxBitrate: 48e7, level: 62 } // Level 6.2 ]; var AV1_LEVEL_TABLE = [ { maxPictureSize: 147456, maxBitrate: 15e5, tier: "M", level: 0 }, // Level 2.0 (Main Tier) { maxPictureSize: 278784, maxBitrate: 3e6, tier: "M", level: 1 }, // Level 2.1 (Main Tier) { maxPictureSize: 665856, maxBitrate: 6e6, tier: "M", level: 4 }, // Level 3.0 (Main Tier) { maxPictureSize: 1065024, maxBitrate: 1e7, tier: "M", level: 5 }, // Level 3.1 (Main Tier) { maxPictureSize: 2359296, maxBitrate: 12e6, tier: "M", level: 8 }, // Level 4.0 (Main Tier) { maxPictureSize: 2359296, maxBitrate: 3e7, tier: "H", level: 8 }, // Level 4.0 (High Tier) { maxPictureSize: 2359296, maxBitrate: 2e7, tier: "M", level: 9 }, // Level 4.1 (Main Tier) { maxPictureSize: 2359296, maxBitrate: 5e7, tier: "H", level: 9 }, // Level 4.1 (High Tier) { maxPictureSize: 8912896, maxBitrate: 3e7, tier: "M", level: 12 }, // Level 5.0 (Main Tier) { maxPictureSize: 8912896, maxBitrate: 1e8, tier: "H", level: 12 }, // Level 5.0 (High Tier) { maxPictureSize: 8912896, maxBitrate: 4e7, tier: "M", level: 13 }, // Level 5.1 (Main Tier) { maxPictureSize: 8912896, maxBitrate: 16e7, tier: "H", level: 13 }, // Level 5.1 (High Tier) { maxPictureSize: 8912896, maxBitrate: 6e7, tier: "M", level: 14 }, // Level 5.2 (Main Tier) { maxPictureSize: 8912896, maxBitrate: 24e7, tier: "H", level: 14 }, // Level 5.2 (High Tier) { maxPictureSize: 35651584, maxBitrate: 6e7, tier: "M", level: 15 }, // Level 5.3 (Main Tier) { maxPictureSize: 35651584, maxBitrate: 24e7, tier: "H", level: 15 }, // Level 5.3 (High Tier) { maxPictureSize: 35651584, maxBitrate: 6e7, tier: "M", level: 16 }, // Level 6.0 (Main Tier) { maxPictureSize: 35651584, maxBitrate: 24e7, tier: "H", level: 16 }, // Level 6.0 (High Tier) { maxPictureSize: 35651584, maxBitrate: 1e8, tier: "M", level: 17 }, // Level 6.1 (Main Tier) { maxPictureSize: 35651584, maxBitrate: 48e7, tier: "H", level: 17 }, // Level 6.1 (High Tier) { maxPictureSize: 35651584, maxBitrate: 16e7, tier: "M", level: 18 }, // Level 6.2 (Main Tier) { maxPictureSize: 35651584, maxBitrate: 8e8, tier: "H", level: 18 }, // Level 6.2 (High Tier) { maxPictureSize: 35651584, maxBitrate: 16e7, tier: "M", level: 19 }, // Level 6.3 (Main Tier) { maxPictureSize: 35651584, maxBitrate: 8e8, tier: "H", level: 19 } // Level 6.3 (High Tier) ]; var VP9_DEFAULT_SUFFIX = ".01.01.01.01.00"; var AV1_DEFAULT_SUFFIX = ".0.110.01.01.01.0"; var buildVideoCodecString = (codec, width, height, bitrate) => { if (codec === "avc") { const profileIndication = 100; const totalMacroblocks = Math.ceil(width / 16) * Math.ceil(height / 16); const levelInfo = AVC_LEVEL_TABLE.find( (level) => totalMacroblocks <= level.maxMacroblocks && bitrate <= level.maxBitrate ) ?? last(AVC_LEVEL_TABLE); const levelIndication = levelInfo ? levelInfo.level : 0; const hexProfileIndication = profileIndication.toString(16).padStart(2, "0"); const hexProfileCompatibility = "00"; const hexLevelIndication = levelIndication.toString(16).padStart(2, "0"); return `avc1.${hexProfileIndication}${hexProfileCompatibility}${hexLevelIndication}`; } else if (codec === "hevc") { const profilePrefix = ""; const profileIdc = 1; const compatibilityFlags = "6"; const pictureSize = width * height; const levelInfo = HEVC_LEVEL_TABLE.find( (level) => pictureSize <= level.maxPictureSize && bitrate <= level.maxBitrate ) ?? last(HEVC_LEVEL_TABLE); const constraintFlags = "B0"; return `hev1.${profilePrefix}${profileIdc}.${compatibilityFlags}.${levelInfo.tier}${levelInfo.level}.${constraintFlags}`; } else if (codec === "vp8") { return "vp8"; } else if (codec === "vp9") { const profile = "00"; const pictureSize = width * height; const levelInfo = VP9_LEVEL_TABLE.find( (level) => pictureSize <= level.maxPictureSize && bitrate <= level.maxBitrate ) ?? last(VP9_LEVEL_TABLE); const bitDepth = "08"; return `vp09.${profile}.${levelInfo.level.toString().padStart(2, "0")}.${bitDepth}`; } else if (codec === "av1") { const profile = 0; const pictureSize = width * height; const levelInfo = AV1_LEVEL_TABLE.find( (level2) => pictureSize <= level2.maxPictureSize && bitrate <= level2.maxBitrate ) ?? last(AV1_LEVEL_TABLE); const level = levelInfo.level.toString().padStart(2, "0"); const bitDepth = "08"; return `av01.${profile}.${level}${levelInfo.tier}.${bitDepth}`; } throw new TypeError(`Unhandled codec '${codec}'.`); }; var generateVp9CodecConfigurationFromCodecString = (codecString) => { const parts = codecString.split("."); const profile = Number(parts[1]); const level = Number(parts[2]); const bitDepth = Number(parts[3]); const chromaSubsampling = parts[4] ? Number(parts[4]) : 1; return [ 1, 1, profile, 2, 1, level, 3, 1, bitDepth, 4, 1, chromaSubsampling ]; }; var generateAv1CodecConfigurationFromCodecString = (codecString) => { const parts = codecString.split("."); const marker = 1; const version = 1; const firstByte = (marker << 7) + version; const profile = Number(parts[1]); const levelAndTier = parts[2]; const level = Number(levelAndTier.slice(0, -1)); const secondByte = (profile << 5) + level; const tier = levelAndTier.slice(-1) === "H" ? 1 : 0; const bitDepth = Number(parts[3]); const highBitDepth = bitDepth === 8 ? 0 : 1; const twelveBit = 0; const monochrome = parts[4] ? Number(parts[4]) : 0; const chromaSubsamplingX = parts[5] ? Number(parts[5][0]) : 1; const chromaSubsamplingY = parts[5] ? Number(parts[5][1]) : 1; const chromaSamplePosition = parts[5] ? Number(parts[5][2]) : 0; const thirdByte = (tier << 7) + (highBitDepth << 6) + (twelveBit << 5) + (monochrome << 4) + (chromaSubsamplingX << 3) + (chromaSubsamplingY << 2) + chromaSamplePosition; const initialPresentationDelayPresent = 0; const fourthByte = initialPresentationDelayPresent; return [firstByte, secondByte, thirdByte, fourthByte]; }; var extractVideoCodecString = (trackInfo) => { const { codec, codecDescription, colorSpace, avcCodecInfo, hevcCodecInfo, vp9CodecInfo, av1CodecInfo } = trackInfo; if (codec === "avc") { if (avcCodecInfo) { const bytes2 = new Uint8Array([ avcCodecInfo.avcProfileIndication, avcCodecInfo.profileCompatibility, avcCodecInfo.avcLevelIndication ]); return `avc1.${bytesToHexString(bytes2)}`; } if (!codecDescription || codecDescription.byteLength < 4) { throw new TypeError("AVC decoder description is not provided or is not at least 4 bytes long."); } return `avc1.${bytesToHexString(codecDescription.subarray(1, 4))}`; } else if (codec === "hevc") { let generalProfileSpace; let generalProfileIdc; let compatibilityFlags; let generalTierFlag; let generalLevelIdc; let constraintFlags; if (hevcCodecInfo) { generalProfileSpace = hevcCodecInfo.generalProfileSpace; generalProfileIdc = hevcCodecInfo.generalProfileIdc; compatibilityFlags = reverseBitsU32(hevcCodecInfo.generalProfileCompatibilityFlags); generalTierFlag = hevcCodecInfo.generalTierFlag; generalLevelIdc = hevcCodecInfo.generalLevelIdc; constraintFlags = [...hevcCodecInfo.generalConstraintIndicatorFlags]; } else { if (!codecDescription || codecDescription.byteLength < 23) { throw new TypeError("HEVC decoder description is not provided or is not at least 23 bytes long."); } const view2 = toDataView(codecDescription); const profileByte = view2.getUint8(1); generalProfileSpace = profileByte >> 6 & 3; generalProfileIdc = profileByte & 31; compatibilityFlags = reverseBitsU32(view2.getUint32(2)); generalTierFlag = profileByte >> 5 & 1; generalLevelIdc = view2.getUint8(12); constraintFlags = []; for (let i = 0; i < 6; i++) { constraintFlags.push(view2.getUint8(6 + i)); } } let codecString = "hev1."; codecString += ["", "A", "B", "C"][generalProfileSpace] + generalProfileIdc; codecString += "."; codecString += compatibilityFlags.toString(16).toUpperCase(); codecString += "."; codecString += generalTierFlag === 0 ? "L" : "H"; codecString += generalLevelIdc; while (constraintFlags.length > 0 && constraintFlags[constraintFlags.length - 1] === 0) { constraintFlags.pop(); } if (constraintFlags.length > 0) { codecString += "."; codecString += constraintFlags.map((x) => x.toString(16).toUpperCase()).join("."); } return codecString; } else if (codec === "vp8") { return "vp8"; } else if (codec === "vp9") { if (!vp9CodecInfo) { const pictureSize = trackInfo.width * trackInfo.height; let level2 = last(VP9_LEVEL_TABLE).level; for (const entry of VP9_LEVEL_TABLE) { if (pictureSize <= entry.maxPictureSize) { level2 = entry.level; break; } } return `vp09.00.${level2.toString().padStart(2, "0")}.08`; } const profile = vp9CodecInfo.profile.toString().padStart(2, "0"); const level = vp9CodecInfo.level.toString().padStart(2, "0"); const bitDepth = vp9CodecInfo.bitDepth.toString().padStart(2, "0"); const chromaSubsampling = vp9CodecInfo.chromaSubsampling.toString().padStart(2, "0"); const colourPrimaries = vp9CodecInfo.colourPrimaries.toString().padStart(2, "0"); const transferCharacteristics = vp9CodecInfo.transferCharacteristics.toString().padStart(2, "0"); const matrixCoefficients = vp9CodecInfo.matrixCoefficients.toString().padStart(2, "0"); const videoFullRangeFlag = vp9CodecInfo.videoFullRangeFlag.toString().padStart(2, "0"); let string = `vp09.${profile}.${level}.${bitDepth}.${chromaSubsampling}`; string += `.${colourPrimaries}.${transferCharacteristics}.${matrixCoefficients}.${videoFullRangeFlag}`; if (string.endsWith(VP9_DEFAULT_SUFFIX)) { string = string.slice(0, -VP9_DEFAULT_SUFFIX.length); } return string; } else if (codec === "av1") { if (!av1CodecInfo) { const pictureSize = trackInfo.width * trackInfo.height; let level2 = last(VP9_LEVEL_TABLE).level; for (const entry of VP9_LEVEL_TABLE) { if (pictureSize <= entry.maxPictureSize) { level2 = entry.level; break; } } return `av01.0.${level2.toString().padStart(2, "0")}M.08`; } const profile = av1CodecInfo.profile; const level = av1CodecInfo.level.toString().padStart(2, "0"); const tier = av1CodecInfo.tier ? "H" : "M"; const bitDepth = av1CodecInfo.bitDepth.toString().padStart(2, "0"); const monochrome = av1CodecInfo.monochrome ? "1" : "0"; const chromaSubsampling = 100 * av1CodecInfo.chromaSubsamplingX + 10 * av1CodecInfo.chromaSubsamplingY + 1 * (av1CodecInfo.chromaSubsamplingX && av1CodecInfo.chromaSubsamplingY ? av1CodecInfo.chromaSamplePosition : 0); const colorPrimaries = colorSpace?.primaries ? COLOR_PRIMARIES_MAP[colorSpace.primaries] : 1; const transferCharacteristics = colorSpace?.transfer ? TRANSFER_CHARACTERISTICS_MAP[colorSpace.transfer] : 1; const matrixCoefficients = colorSpace?.matrix ? MATRIX_COEFFICIENTS_MAP[colorSpace.matrix] : 1; const videoFullRangeFlag = colorSpace?.fullRange ? 1 : 0; let string = `av01.${profile}.${level}${tier}.${bitDepth}`; string += `.${monochrome}.${chromaSubsampling.toString().padStart(3, "0")}`; string += `.${colorPrimaries.toString().padStart(2, "0")}`; string += `.${transferCharacteristics.toString().padStart(2, "0")}`; string += `.${matrixCoefficients.toString().padStart(2, "0")}`; string += `.${videoFullRangeFlag}`; if (string.endsWith(AV1_DEFAULT_SUFFIX)) { string = string.slice(0, -AV1_DEFAULT_SUFFIX.length); } return string; } throw new TypeError(`Unhandled codec '${codec}'.`); }; var buildAudioCodecString = (codec, numberOfChannels, sampleRate) => { if (codec === "aac") { if (numberOfChannels >= 2 && sampleRate <= 24e3) { return "mp4a.40.29"; } if (sampleRate <= 24e3) { return "mp4a.40.5"; } return "mp4a.40.2"; } else if (codec === "mp3") { return "mp3"; } else if (codec === "opus") { return "opus"; } else if (codec === "vorbis") { return "vorbis"; } else if (codec === "flac") { return "flac"; } else if (PCM_AUDIO_CODECS.includes(codec)) { return codec; } throw new TypeError(`Unhandled codec '${codec}'.`); }; var extractAudioCodecString = (trackInfo) => { const { codec, codecDescription, aacCodecInfo } = trackInfo; if (codec === "aac") { if (!aacCodecInfo) { throw new TypeError("AAC codec info must be provided."); } if (aacCodecInfo.isMpeg2) { return "mp4a.67"; } else { const audioSpecificConfig = parseAacAudioSpecificConfig(codecDescription); return `mp4a.40.${audioSpecificConfig.objectType}`; } } else if (codec === "mp3") { return "mp3"; } else if (codec === "opus") { return "opus"; } else if (codec === "vorbis") { return "vorbis"; } else if (codec === "flac") { return "flac"; } else if (codec && PCM_AUDIO_CODECS.includes(codec)) { return codec; } throw new TypeError(`Unhandled codec '${codec}'.`); }; var parseAacAudioSpecificConfig = (bytes2) => { if (!bytes2 || bytes2.byteLength < 2) { throw new TypeError("AAC description must be at least 2 bytes long."); } const bitstream = new Bitstream(bytes2); let objectType = bitstream.readBits(5); if (objectType === 31) { objectType = 32 + bitstream.readBits(6); } const frequencyIndex = bitstream.readBits(4); let sampleRate = null; if (frequencyIndex === 15) { sampleRate = bitstream.readBits(24); } else { const freqTable = [ 96e3, 88200, 64e3, 48e3, 44100, 32e3, 24e3, 22050, 16e3, 12e3, 11025, 8e3, 7350 ]; if (frequencyIndex < freqTable.length) { sampleRate = freqTable[frequencyIndex]; } } const channelConfiguration = bitstream.readBits(4); let numberOfChannels = null; if (channelConfiguration >= 1 && channelConfiguration <= 7) { const channelMap = { 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 8 }; numberOfChannels = channelMap[channelConfiguration]; } return { objectType, frequencyIndex, sampleRate, channelConfiguration, numberOfChannels }; }; var OPUS_INTERNAL_SAMPLE_RATE = 48e3; var PCM_CODEC_REGEX = /^pcm-([usf])(\d+)+(be)?$/; var parsePcmCodec = (codec) => { assert(PCM_AUDIO_CODECS.includes(codec)); if (codec === "ulaw") { return { dataType: "ulaw", sampleSize: 1, littleEndian: true, silentValue: 255 }; } else if (codec === "alaw") { return { dataType: "alaw", sampleSize: 1, littleEndian: true, silentValue: 213 }; } const match = PCM_CODEC_REGEX.exec(codec); assert(match); let dataType; if (match[1] === "u") { dataType = "unsigned"; } else if (match[1] === "s") { dataType = "signed"; } else { dataType = "float"; } const sampleSize = Number(match[2]) / 8; const littleEndian = match[3] !== "be"; const silentValue = codec === "pcm-u8" ? 2 ** 7 : 0; return { dataType, sampleSize, littleEndian, silentValue }; }; var inferCodecFromCodecString = (codecString) => { if (codecString.startsWith("avc1") || codecString.startsWith("avc3")) { return "avc"; } else if (codecString.startsWith("hev1") || codecString.startsWith("hvc1")) { return "hevc"; } else if (codecString === "vp8") { return "vp8"; } else if (codecString.startsWith("vp09")) { return "vp9"; } else if (codecString.startsWith("av01")) { return "av1"; } if (codecString.startsWith("mp4a.40") || codecString === "mp4a.67") { return "aac"; } else if (codecString === "mp3" || codecString === "mp4a.69" || codecString === "mp4a.6B" || codecString === "mp4a.6b") { return "mp3"; } else if (codecString === "opus") { return "opus"; } else if (codecString === "vorbis") { return "vorbis"; } else if (codecString === "flac") { return "flac"; } else if (codecString === "ulaw") { return "ulaw"; } else if (codecString === "alaw") { return "alaw"; } else if (PCM_CODEC_REGEX.test(codecString)) { return codecString; } if (codecString === "webvtt") { return "webvtt"; } return null; }; var getVideoEncoderConfigExtension = (codec) => { if (codec === "avc") { return { avc: { format: "avc" // Ensure the format is not Annex B } }; } else if (codec === "hevc") { return { hevc: { format: "hevc" // Ensure the format is not Annex B } }; } return {}; }; var getAudioEncoderConfigExtension = (codec) => { if (codec === "aac") { return { aac: { format: "aac" // Ensure the format is not ADTS } }; } else if (codec === "opus") { return { opus: { format: "opus" } }; } return {}; }; var Quality = class { /** @internal */ constructor(factor) { this._factor = factor; } /** @internal */ _toVideoBitrate(codec, width, height) { const pixels = width * height; const codecEfficiencyFactors = { avc: 1, // H.264/AVC (baseline) hevc: 0.6, // H.265/HEVC (~40% more efficient than AVC) vp9: 0.6, // Similar to HEVC av1: 0.4, // ~60% more efficient than AVC vp8: 1.2 // Slightly less efficient than AVC }; const referencePixels = 1920 * 1080; const referenceBitrate = 3e6; const scaleFactor = Math.pow(pixels / referencePixels, 0.95); const baseBitrate = referenceBitrate * scaleFactor; const codecAdjustedBitrate = baseBitrate * codecEfficiencyFactors[codec]; const finalBitrate = codecAdjustedBitrate * this._factor; return Math.ceil(finalBitrate / 1e3) * 1e3; } /** @internal */ _toAudioBitrate(codec) { if (PCM_AUDIO_CODECS.includes(codec) || codec === "flac") { return void 0; } const baseRates = { aac: 128e3, // 128kbps base for AAC opus: 64e3, // 64kbps base for Opus mp3: 16e4, // 160kbps base for MP3 vorbis: 64e3 // 64kbps base for Vorbis }; const baseBitrate = baseRates[codec]; if (!baseBitrate) { throw new Error(`Unhandled codec: ${codec}`); } let finalBitrate = baseBitrate * this._factor; if (codec === "aac") { const validRates = [96e3, 128e3, 16e4, 192e3]; finalBitrate = validRates.reduce( (prev, curr) => Math.abs(curr - finalBitrate) < Math.abs(prev - finalBitrate) ? curr : prev ); } else if (codec === "opus" || codec === "vorbis") { finalBitrate = Math.max(6e3, finalBitrate); } else if (codec === "mp3") { const validRates = [ 8e3, 16e3, 24e3, 32e3, 4e4, 48e3, 64e3, 8e4, 96e3, 112e3, 128e3, 16e4, 192e3, 224e3, 256e3, 32e4 ]; finalBitrate = validRates.reduce( (prev, curr) => Math.abs(curr - finalBitrate) < Math.abs(prev - finalBitrate) ? curr : prev ); } return Math.round(finalBitrate / 1e3) * 1e3; } }; var QUALITY_VERY_LOW = new Quality(0.3); var QUALITY_LOW = new Quality(0.6); var QUALITY_MEDIUM = new Quality(1); var QUALITY_HIGH = new Quality(2); var QUALITY_VERY_HIGH = new Quality(4); var VALID_VIDEO_CODEC_STRING_PREFIXES = ["avc1", "avc3", "hev1", "hvc1", "vp8", "vp09", "av01"]; var AVC_CODEC_STRING_REGEX = /^(avc1|avc3)\.[0-9a-fA-F]{6}$/; var HEVC_CODEC_STRING_REGEX = /^(hev1|hvc1)\.(?:[ABC]?\d+)\.[0-9a-fA-F]{1,8}\.[LH]\d+(?:\.[0-9a-fA-F]{1,2}){0,6}$/; var VP9_CODEC_STRING_REGEX = /^vp09(?:\.\d{2}){3}(?:(?:\.\d{2}){5})?$/; var AV1_CODEC_STRING_REGEX = /^av01\.\d\.\d{2}[MH]\.\d{2}(?:\.\d\.\d{3}\.\d{2}\.\d{2}\.\d{2}\.\d)?$/; var validateVideoChunkMetadata = (metadata) => { if (!metadata) { throw new TypeError("Video chunk metadata must be provided."); } if (typeof metadata !== "object") { throw new TypeError("Video chunk metadata must be an object."); } if (!metadata.decoderConfig) { throw new TypeError("Video chunk metadata must include a decoder configuration."); } if (typeof metadata.decoderConfig !== "object") { throw new TypeError("Video chunk metadata decoder configuration must be an object."); } if (typeof metadata.decoderConfig.codec !== "string") { throw new TypeError("Video chunk metadata decoder configuration must specify a codec string."); } if (!VALID_VIDEO_CODEC_STRING_PREFIXES.some((prefix) => metadata.decoderConfig.codec.startsWith(prefix))) { throw new TypeError( "Video chunk metadata decoder configuration codec string must be a valid video codec string as specified in the WebCodecs Codec Registry." ); } if (!Number.isInteger(metadata.decoderConfig.codedWidth) || metadata.decoderConfig.codedWidth <= 0) { throw new TypeError( "Video chunk metadata decoder configuration must specify a valid codedWidth (positive integer)." ); } if (!Number.isInteger(metadata.decoderConfig.codedHeight) || metadata.decoderConfig.codedHeight <= 0) { throw new TypeError( "Video chunk metadata decoder configuration must specify a valid codedHeight (positive integer)." ); } if (metadata.decoderConfig.description !== void 0) { if (!isAllowSharedBufferSource(metadata.decoderConfig.description)) { throw new TypeError( "Video chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view." ); } } if (metadata.decoderConfig.colorSpace !== void 0) { const { colorSpace } = metadata.decoderConfig; if (typeof colorSpace !== "object") { throw new TypeError( "Video chunk metadata decoder configuration colorSpace, when provided, must be an object." ); } const primariesValues = Object.keys(COLOR_PRIMARIES_MAP); if (colorSpace.primaries != null && !primariesValues.includes(colorSpace.primaries)) { throw new TypeError( `Video chunk metadata decoder configuration colorSpace primaries, when defined, must be one of ${primariesValues.join(", ")}.` ); } const transferValues