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
JavaScript
/*!
* 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