UNPKG

mediabunny

Version:

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

1,485 lines (1,479 loc) 1.42 MB
/*! * Copyright (c) 2026-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 __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name); var __typeError = (msg) => { throw TypeError(msg); }; var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc2) => { 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: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __using = (stack, value, async) => { if (value != null) { if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected"); var dispose, inner; if (async) dispose = value[__knownSymbol("asyncDispose")]; if (dispose === void 0) { dispose = value[__knownSymbol("dispose")]; if (async) inner = dispose; } if (typeof dispose !== "function") __typeError("Object not disposable"); if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } }; stack.push([async, dispose, value]); } else if (async) { stack.push([async]); } return value; }; var __callDispose = (stack, error, hasError) => { var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) { return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _; }; var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e); var next = (it) => { while (it = stack.pop()) { try { var result = it[1] && it[1].call(it[2]); if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next())); } catch (e) { fail(e); } } if (hasError) throw error; }; return next(); }; // src/index.ts var index_exports = {}; __export(index_exports, { ADTS: () => ADTS, ALL_FORMATS: () => ALL_FORMATS, ALL_TRACK_TYPES: () => ALL_TRACK_TYPES, AUDIO_CODECS: () => AUDIO_CODECS, AdtsInputFormat: () => AdtsInputFormat, AdtsOutputFormat: () => AdtsOutputFormat, AppendOnlyStreamTarget: () => AppendOnlyStreamTarget, AttachedFile: () => AttachedFile, AudioBufferSink: () => AudioBufferSink, AudioBufferSource: () => AudioBufferSource, AudioSample: () => AudioSample, AudioSampleResource: () => AudioSampleResource, AudioSampleSink: () => AudioSampleSink, AudioSampleSource: () => AudioSampleSource, AudioSource: () => AudioSource, BaseMediaSampleSink: () => BaseMediaSampleSink, BlobSource: () => BlobSource, BufferSource: () => BufferSource, BufferTarget: () => BufferTarget, CanvasSink: () => CanvasSink, CanvasSource: () => CanvasSource, CmafOutputFormat: () => CmafOutputFormat, ConcurrentRunner: () => ConcurrentRunner, Conversion: () => Conversion, ConversionCanceledError: () => ConversionCanceledError, CustomAudioDecoder: () => CustomAudioDecoder, CustomAudioEncoder: () => CustomAudioEncoder, CustomPathedSource: () => CustomPathedSource, CustomSource: () => CustomSource, CustomVideoDecoder: () => CustomVideoDecoder, CustomVideoEncoder: () => CustomVideoEncoder, EncodedAudioPacketSource: () => EncodedAudioPacketSource, EncodedPacket: () => EncodedPacket, EncodedPacketSink: () => EncodedPacketSink, EncodedVideoPacketSource: () => EncodedVideoPacketSource, EventEmitter: () => EventEmitter, FLAC: () => FLAC, FilePathSource: () => FilePathSource, FilePathTarget: () => FilePathTarget, FlacInputFormat: () => FlacInputFormat, FlacOutputFormat: () => FlacOutputFormat, HLS: () => HLS, HLS_FORMATS: () => HLS_FORMATS, HlsInputFormat: () => HlsInputFormat, HlsOutputFormat: () => HlsOutputFormat, Input: () => Input, InputAudioTrack: () => InputAudioTrack, InputDisposedError: () => InputDisposedError, InputFormat: () => InputFormat, InputTrack: () => InputTrack, InputVideoTrack: () => InputVideoTrack, IsobmffInputFormat: () => IsobmffInputFormat, IsobmffOutputFormat: () => IsobmffOutputFormat2, MATROSKA: () => MATROSKA, MP3: () => MP3, MP4: () => MP4, MPEG_TS: () => MPEG_TS, MatroskaInputFormat: () => MatroskaInputFormat, MediaSource: () => MediaSource, MediaStreamAudioTrackSource: () => MediaStreamAudioTrackSource, MediaStreamVideoTrackSource: () => MediaStreamVideoTrackSource, MkvOutputFormat: () => MkvOutputFormat2, MovOutputFormat: () => MovOutputFormat, Mp3InputFormat: () => Mp3InputFormat, Mp3OutputFormat: () => Mp3OutputFormat, Mp4InputFormat: () => Mp4InputFormat, Mp4OutputFormat: () => Mp4OutputFormat, MpegTsInputFormat: () => MpegTsInputFormat, MpegTsOutputFormat: () => MpegTsOutputFormat, NON_PCM_AUDIO_CODECS: () => NON_PCM_AUDIO_CODECS, NullTarget: () => NullTarget, OGG: () => OGG, OggInputFormat: () => OggInputFormat, OggOutputFormat: () => OggOutputFormat, Output: () => Output, OutputAudioTrack: () => OutputAudioTrack2, OutputFormat: () => OutputFormat, OutputSubtitleTrack: () => OutputSubtitleTrack2, OutputTrack: () => OutputTrack2, OutputTrackGroup: () => OutputTrackGroup, OutputVideoTrack: () => OutputVideoTrack2, PCM_AUDIO_CODECS: () => PCM_AUDIO_CODECS, PathedSource: () => PathedSource, PathedTarget: () => PathedTarget, 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, RangedSource: () => RangedSource, RangedTarget: () => RangedTarget, ReadableStreamSource: () => ReadableStreamSource, RichImageData: () => RichImageData, SUBTITLE_CODECS: () => SUBTITLE_CODECS, Source: () => Source, SourceRef: () => SourceRef, StreamSource: () => StreamSource, StreamTarget: () => StreamTarget, SubtitleSource: () => SubtitleSource, Target: () => Target, TextSubtitleSource: () => TextSubtitleSource, UnsupportedInputFormatError: () => UnsupportedInputFormatError, UrlSource: () => UrlSource, VIDEO_CODECS: () => VIDEO_CODECS, VIDEO_SAMPLE_PIXEL_FORMATS: () => VIDEO_SAMPLE_PIXEL_FORMATS, VideoSample: () => VideoSample, VideoSampleColorSpace: () => VideoSampleColorSpace, VideoSampleResource: () => VideoSampleResource, VideoSampleSink: () => VideoSampleSink, VideoSampleSource: () => VideoSampleSource, VideoSource: () => VideoSource, WAVE: () => WAVE, WEBM: () => WEBM, WavOutputFormat: () => WavOutputFormat, WaveInputFormat: () => WaveInputFormat, WebMInputFormat: () => WebMInputFormat, WebMOutputFormat: () => WebMOutputFormat, asc: () => asc, canDecode: () => canDecode, canDecodeAudio: () => canDecodeAudio, canDecodeVideo: () => canDecodeVideo, canEncode: () => canEncode, canEncodeAudio: () => canEncodeAudio, canEncodeSubtitles: () => canEncodeSubtitles, canEncodeVideo: () => canEncodeVideo, desc: () => desc, getDecodableAudioCodecs: () => getDecodableAudioCodecs, getDecodableCodecs: () => getDecodableCodecs, getDecodableVideoCodecs: () => getDecodableVideoCodecs, getEncodableAudioCodecs: () => getEncodableAudioCodecs, getEncodableCodecs: () => getEncodableCodecs, getEncodableSubtitleCodecs: () => getEncodableSubtitleCodecs, getEncodableVideoCodecs: () => getEncodableVideoCodecs, getFirstEncodableAudioCodec: () => getFirstEncodableAudioCodec, getFirstEncodableSubtitleCodec: () => getFirstEncodableSubtitleCodec, getFirstEncodableVideoCodec: () => getFirstEncodableVideoCodec, prefer: () => prefer, registerDecoder: () => registerDecoder, registerEncoder: () => registerEncoder, registerVideoSampleTransformer: () => registerVideoSampleTransformer }); // 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 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.constructor === Uint8Array) { return source; } else if (ArrayBuffer.isView(source)) { return new Uint8Array(source.buffer, source.byteOffset, source.byteLength); } else { return new Uint8Array(source); } }; var toDataView = (source) => { if (source.constructor === DataView) { return source; } else if (ArrayBuffer.isView(source)) { return new DataView(source.buffer, source.byteOffset, source.byteLength); } else { return new DataView(source); } }; var textDecoder = /* @__PURE__ */ new TextDecoder(); var textEncoder = /* @__PURE__ */ new TextEncoder(); var isIso88591Compatible = (text) => { for (let i = 0; i < text.length; i++) { const code = text.charCodeAt(i); if (code > 255) { return false; } } return true; }; 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 = /* @__PURE__ */ 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 "pq": 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 = /* @__PURE__ */ 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 = /* @__PURE__ */ 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(); this.pending = 0; } async acquire() { let resolver; const nextPromise = new Promise((resolve) => { let resolved = false; resolver = () => { if (resolved) { return; } resolve(); this.pending--; resolved = true; }; }); const currentPromiseAlias = this.currentPromise; this.currentPromise = nextPromise; this.pending++; await currentPromiseAlias; return resolver; } }; var HEX_STRING_REGEX = /^[0-9a-fA-F]+$/; var bytesToHexString = (bytes2) => { return [...bytes2].map((x) => x.toString(16).padStart(2, "0")).join(""); }; var hexStringToBytes = (hexString) => { assert(hexString.length % 2 === 0); const bytes2 = new Uint8Array(hexString.length / 2); for (let i = 0; i < hexString.length; i += 2) { bytes2[i / 2] = parseInt(hexString.slice(i, i + 2), 16); } return bytes2; }; 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 roundIfAlmostInteger = (value) => { const rounded = Math.round(value); if (Math.abs(value / rounded - 1) < 10 * Number.EPSILON) { return rounded; } else { return value; } }; var roundToMultiple = (value, multiple) => { return Math.round(value / multiple) * multiple; }; var roundToDivisor = (value, multiple) => { return Math.round(value * multiple) / multiple; }; var floorToMultiple = (value, multiple) => { return Math.floor(value / multiple) * multiple; }; var floorToDivisor = (value, multiple) => { return Math.floor(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 mergeRequestInit = (init1, init2) => { const merged = { ...init1, ...init2 }; if (init1.headers || init2.headers) { const headers1 = init1.headers ? normalizeHeaders(init1.headers) : {}; const headers2 = init2.headers ? normalizeHeaders(init2.headers) : {}; const mergedHeaders = { ...headers1 }; Object.entries(headers2).forEach(([key2, value2]) => { const existingKey = Object.keys(mergedHeaders).find( (key1) => key1.toLowerCase() === key2.toLowerCase() ); if (existingKey) { delete mergedHeaders[existingKey]; } mergedHeaders[key2] = value2; }); merged.headers = mergedHeaders; } return merged; }; var normalizeHeaders = (headers) => { if (headers instanceof Headers) { const result = {}; headers.forEach((value, key) => { result[key] = value; }); return result; } if (Array.isArray(headers)) { const result = {}; headers.forEach(([key, value]) => { result[key] = value; }); return result; } return headers; }; var retriedFetch = async (fetchFn, url2, requestInit, getRetryDelay, shouldStop) => { let attempts = 0; while (true) { try { return await fetchFn(url2, requestInit); } catch (error) { if (shouldStop()) { throw error; } attempts++; const retryDelayInSeconds = getRetryDelay(attempts, error, url2); 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 wait(1e3 * retryDelayInSeconds); } if (shouldStop()) { throw error; } } } }; 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 { num: sign * currNumerator, den: currDenominator }; } prevNumerator = currNumerator; prevDenominator = currDenominator; currNumerator = nextNumerator; currDenominator = nextDenominator; remainder = 1 / (remainder - integer); if (!isFinite(remainder)) { break; } } return { num: sign * currNumerator, den: currDenominator }; }; var CallSerializer = class { constructor() { this.currentPromise = Promise.resolve(); } call(fn) { return this.currentPromise = this.currentPromise.then(fn); } }; var isWebKitCache = null; var isWebKit = () => { if (isWebKitCache !== null) { return isWebKitCache; } return isWebKitCache = !!(typeof navigator !== "undefined" && // eslint-disable-next-line @typescript-eslint/no-deprecated (navigator.vendor?.match(/apple/i) || /AppleWebKit/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) || /\b(iPad|iPhone|iPod)\b/.test(navigator.userAgent))); }; var isFirefoxCache = null; var isFirefox = () => { if (isFirefoxCache !== null) { return isFirefoxCache; } return isFirefoxCache = typeof navigator !== "undefined" && navigator.userAgent?.includes("Firefox"); }; var isChromiumCache = null; var isChromium = () => { if (isChromiumCache !== null) { return isChromiumCache; } return isChromiumCache = !!(typeof navigator !== "undefined" && (navigator.vendor?.includes("Google Inc") || /Chrome/.test(navigator.userAgent))); }; var chromiumVersionCache = null; var getChromiumVersion = () => { if (chromiumVersionCache !== null) { return chromiumVersionCache; } if (typeof navigator === "undefined") { return null; } const match = /\bChrome\/(\d+)/.exec(navigator.userAgent); if (!match) { return null; } return chromiumVersionCache = Number(match[1]); }; var coalesceIndex = (a, b) => { return a !== -1 ? a : b; }; var closedIntervalsOverlap = (startA, endA, startB, endB) => { return startA <= endB && startB <= endA; }; var keyValueIterator = function* (object) { for (const key in object) { const value = object[key]; if (value === void 0) { continue; } yield { key, value }; } }; var imageMimeTypeToExtension = (mimeType) => { switch (mimeType.toLowerCase()) { case "image/jpeg": case "image/jpg": return ".jpg"; case "image/png": return ".png"; case "image/gif": return ".gif"; case "image/webp": return ".webp"; case "image/bmp": return ".bmp"; case "image/svg+xml": return ".svg"; case "image/tiff": return ".tiff"; case "image/avif": return ".avif"; case "image/x-icon": case "image/vnd.microsoft.icon": return ".ico"; default: return null; } }; var base64ToBytes = (base64) => { const decoded = atob(base64); const bytes2 = new Uint8Array(decoded.length); for (let i = 0; i < decoded.length; i++) { bytes2[i] = decoded.charCodeAt(i); } return bytes2; }; var bytesToBase64 = (bytes2) => { let string = ""; for (let i = 0; i < bytes2.length; i++) { string += String.fromCharCode(bytes2[i]); } return btoa(string); }; var uint8ArraysAreEqual = (a, b) => { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) { return false; } } return true; }; var polyfillSymbolDispose = () => { Symbol.dispose ??= Symbol("Symbol.dispose"); }; var isNumber = (x) => { return typeof x === "number" && !Number.isNaN(x); }; var joinPaths = (basePath, relativePath) => { if (relativePath.includes("://")) { return relativePath; } if (basePath.includes("://")) { const queryIndex = basePath.indexOf("?"); if (queryIndex !== -1) { basePath = basePath.slice(0, queryIndex); } } let result; if (relativePath.startsWith("/")) { const protocolIndex2 = basePath.indexOf("://"); if (protocolIndex2 === -1) { result = relativePath; } else { const pathStart = basePath.indexOf("/", protocolIndex2 + 3); if (pathStart === -1) { result = basePath + relativePath; } else { result = basePath.slice(0, pathStart) + relativePath; } } } else { const lastSlash = basePath.lastIndexOf("/"); if (lastSlash === -1) { result = relativePath; } else { result = basePath.slice(0, lastSlash + 1) + relativePath; } } let prefix = ""; const protocolIndex = result.indexOf("://"); if (protocolIndex !== -1) { const pathStart = result.indexOf("/", protocolIndex + 3); if (pathStart !== -1) { prefix = result.slice(0, pathStart); result = result.slice(pathStart); } } const segments = result.split("/"); const normalized = []; for (const segment of segments) { if (segment === "..") { normalized.pop(); } else if (segment !== ".") { normalized.push(segment); } } return prefix + normalized.join("/"); }; var arrayCount = (array, predicate) => { let count = 0; for (let i = 0; i < array.length; i++) { if (predicate(array[i])) { count++; } } return count; }; var arrayArgmin = (array, getValue) => { let minIndex = -1; let minValue = Infinity; for (let i = 0; i < array.length; i++) { const value = getValue(array[i]); if (value < minValue) { minValue = value; minIndex = i; } } return minIndex; }; var arrayArgmax = (array, getValue) => { let maxIndex = -1; let maxValue = -Infinity; for (let i = 0; i < array.length; i++) { const value = getValue(array[i]); if (value > maxValue) { maxValue = value; maxIndex = i; } } return maxIndex; }; var simplifyRational = (rational) => { assert(Number.isInteger(rational.num)); assert(Number.isInteger(rational.den)); assert(rational.den !== 0); let a = Math.abs(rational.num); let b = Math.abs(rational.den); while (b !== 0) { const t = a % b; a = b; b = t; } const gcd = a || 1; return { num: rational.num / gcd, den: rational.den / gcd }; }; var validateRectangle = (rect, propertyPath) => { if (typeof rect !== "object" || !rect) { throw new TypeError(`${propertyPath} must be an object.`); } if (!Number.isInteger(rect.left) || rect.left < 0) { throw new TypeError(`${propertyPath}.left must be a non-negative integer.`); } if (!Number.isInteger(rect.top) || rect.top < 0) { throw new TypeError(`${propertyPath}.top must be a non-negative integer.`); } if (!Number.isInteger(rect.width) || rect.width < 0) { throw new TypeError(`${propertyPath}.width must be a non-negative integer.`); } if (!Number.isInteger(rect.height) || rect.height < 0) { throw new TypeError(`${propertyPath}.height must be a non-negative integer.`); } }; var unthrottledTimerWorker; var nextUnthrottledTimerId = 1; var unthrottledTimeoutCallbacks = /* @__PURE__ */ new Map(); var unthrottledIntervalCallbacks = /* @__PURE__ */ new Map(); var shouldUseNativeTimers = () => { return typeof window === "undefined"; }; var unthrottledTimerWorkerMain = () => { const timeoutHandles = /* @__PURE__ */ new Map(); const intervalHandles = /* @__PURE__ */ new Map(); self.onmessage = (event) => { const message = event.data; switch (message.type) { case "set-timeout": { const handle = setTimeout(() => { timeoutHandles.delete(message.timerId); self.postMessage({ type: "fire", timerId: message.timerId }); }, message.delay); timeoutHandles.set(message.timerId, handle); } ; break; case "set-interval": { const handle = setInterval(() => { self.postMessage({ type: "fire", timerId: message.timerId }); }, message.delay); intervalHandles.set(message.timerId, handle); } ; break; case "clear-timeout": { const handle = timeoutHandles.get(message.timerId); if (handle !== void 0) { clearTimeout(handle); timeoutHandles.delete(message.timerId); } } ; break; case "clear-interval": { const handle = intervalHandles.get(message.timerId); if (handle !== void 0) { clearInterval(handle); intervalHandles.delete(message.timerId); } } ; break; } }; }; var getUnthrottledTimerWorker = () => { if (unthrottledTimerWorker) { return unthrottledTimerWorker; } const workerSource = `(${unthrottledTimerWorkerMain.toString()})();`; const workerURL = URL.createObjectURL(new Blob([workerSource], { type: "text/javascript" })); unthrottledTimerWorker = new Worker(workerURL); URL.revokeObjectURL(workerURL); unthrottledTimerWorker.onmessage = (event) => { const message = event.data; const timeoutCallback = unthrottledTimeoutCallbacks.get(message.timerId); if (timeoutCallback) { unthrottledTimeoutCallbacks.delete(message.timerId); timeoutCallback(); return; } const intervalCallback = unthrottledIntervalCallbacks.get(message.timerId); if (intervalCallback) { intervalCallback(); } }; return unthrottledTimerWorker; }; var setIntervalUnthrottled = (callback, delay) => { if (shouldUseNativeTimers()) { return { id: setInterval(callback, delay) }; } const timerId = nextUnthrottledTimerId++; unthrottledIntervalCallbacks.set(timerId, () => { callback(); }); getUnthrottledTimerWorker().postMessage({ type: "set-interval", timerId, delay }); return { id: timerId }; }; var clearIntervalUnthrottled = (timer) => { if (shouldUseNativeTimers()) { clearInterval(timer.id); return; } assert(typeof timer.id === "number"); unthrottledIntervalCallbacks.delete(timer.id); getUnthrottledTimerWorker().postMessage({ type: "clear-interval", timerId: timer.id }); }; var wait = (ms) => { return new Promise((resolve) => setTimeout(resolve, ms)); }; var toArray = (x) => { if (Array.isArray(x)) { return x; } else { return [x]; } }; var EventEmitter = class { constructor() { /** @internal */ this._listeners = /* @__PURE__ */ new Map(); } /** Registers a listener for the given event. */ on(event, listener, options) { if (!this._listeners.has(event)) { this._listeners.set(event, /* @__PURE__ */ new Set()); } const entry = { fn: listener, once: options?.once ?? false }; this._listeners.get(event).add(entry); return () => { this._listeners.get(event)?.delete(entry); }; } /** @internal */ _emit(...args) { const [event, data] = args; const listeners = this._listeners.get(event); if (!listeners) { return; } for (const entry of listeners) { try { entry.fn(data); } catch (error) { console.error(error); } if (entry.once) { listeners.delete(entry); } } } }; var ceilToMultipleOfTwo = (value) => Math.ceil(value / 2) * 2; var ConcurrentRunner = class { constructor(parallelism) { /** @internal */ this._queue = []; /** @internal */ this._errored = false; this.parallelism = parallelism; } /** Whether any function has errored. The runner is effectively bricked if this is `true`, by design. */ get errored() { return this._errored; } /** The number of tasks currently running. */ get inFlightCount() { return this._queue.length; } /** * Schedules an async function to be run. If the maximum allowed level of parallelism has not yet been reached, * the function will be executed immediately and `run()` will resolve immediately. Otherwise, the function will be * called as soon as any currently-running function finishes, and `run()` will only resolve then. * * Throws if the runner is errored. */ async run(fn) { if (this._errored) { await Promise.race(this._queue); } while (this._queue.length >= this.parallelism) { await Promise.race(this._queue); } const promise = fn(); this._queue.push(promise); void promise.then(() => removeItem(this._queue, promise)).catch(() => this._errored = true); } /** Waits for all currently running functions to finish. Throws if the runner is errored. */ async flush() { await Promise.all(this._queue); } }; var isRecordStringString = (value) => { return value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every((x) => typeof x === "string"); }; // src/metadata.ts var RichImageData = class { /** Creates a new {@link RichImageData}. */ constructor(data, mimeType) { this.data = data; this.mimeType = mimeType; if (!(data instanceof Uint8Array)) { throw new TypeError("data must be a Uint8Array."); } if (typeof mimeType !== "string") { throw new TypeError("mimeType must be a string."); } } }; var AttachedFile = class { /** Creates a new {@link AttachedFile}. */ constructor(data, mimeType, name, description) { this.data = data; this.mimeType = mimeType; this.name = name; this.description = description; if (!(data instanceof Uint8Array)) { throw new TypeError("data must be a Uint8Array."); } if (mimeType !== void 0 && typeof mimeType !== "string") { throw new TypeError("mimeType, when provided, must be a string."); } if (name !== void 0 && typeof name !== "string") { throw new TypeError("name, when provided, must be a string."); } if (description !== void 0 && typeof description !== "string") { throw new TypeError("description, when provided, must be a string."); } } }; var validateMetadataTags = (tags) => { if (!tags || typeof tags !== "object") { throw new TypeError("tags must be an object."); } if (tags.title !== void 0 && typeof tags.title !== "string") { throw new TypeError("tags.title, when provided, must be a string."); } if (tags.description !== void 0 && typeof tags.description !== "string") { throw new TypeError("tags.description, when provided, must be a string."); } if (tags.artist !== void 0 && typeof tags.artist !== "string") { throw new TypeError("tags.artist, when provided, must be a string."); } if (tags.album !== void 0 && typeof tags.album !== "string") { throw new TypeError("tags.album, when provided, must be a string."); } if (tags.albumArtist !== void 0 && typeof tags.albumArtist !== "string") { throw new TypeError("tags.albumArtist, when provided, must be a string."); } if (tags.trackNumber !== void 0 && (!Number.isInteger(tags.trackNumber) || tags.trackNumber <= 0)) { throw new TypeError("tags.trackNumber, when provided, must be a positive integer."); } if (tags.tracksTotal !== void 0 && (!Number.isInteger(tags.tracksTotal) || tags.tracksTotal <= 0)) { throw new TypeError("tags.tracksTotal, when provided, must be a positive integer."); } if (tags.discNumber !== void 0 && (!Number.isInteger(tags.discNumber) || tags.discNumber <= 0)) { throw new TypeError("tags.discNumber, when provided, must be a positive integer."); } if (tags.discsTotal !== void 0 && (!Number.isInteger(tags.discsTotal) || tags.discsTotal <= 0)) { throw new TypeError("tags.discsTotal, when provided, must be a positive integer."); } if (tags.genre !== void 0 && typeof tags.genre !== "string") { throw new TypeError("tags.genre, when provided, must be a string."); } if (tags.date !== void 0 && (!(tags.date instanceof Date) || Number.isNaN(tags.date.getTime()))) { throw new TypeError("tags.date, when provided, must be a valid Date."); } if (tags.lyrics !== void 0 && typeof tags.lyrics !== "string") { throw new TypeError("tags.lyrics, when provided, must be a string."); } if (tags.images !== void 0) { if (!Array.isArray(tags.images)) { throw new TypeError("tags.images, when provided, must be an array."); } for (const image of tags.images) { if (!image || typeof image !== "object") { throw new TypeError("Each image in tags.images must be an object."); } if (!(image.data instanceof Uint8Array)) { throw new TypeError("Each image.data must be a Uint8Array."); } if (typeof image.mimeType !== "string") { throw new TypeError("Each image.mimeType must be a string."); } if (!["coverFront", "coverBack", "unknown"].includes(image.kind)) { throw new TypeError("Each image.kind must be 'coverFront', 'coverBack', or 'unknown'."); } } } if (tags.comment !== void 0 && typeof tags.comment !== "string") { throw new TypeError("tags.comment, when provided, must be a string."); } if (tags.raw !== void 0) { if (!tags.raw || typeof tags.raw !== "object") { throw new TypeError("tags.raw, when provided, must be an object."); } for (const value of Object.values(tags.raw)) { if (value !== null && typeof value !== "string" && !(value instanceof Uint8Array) && !(value instanceof RichImageData) && !(value instanceof AttachedFile) && !isRecordStringString(value)) { throw new TypeError( "Each value in tags.raw must be a string, Uint8Array, RichImageData, AttachedFile, Record<string, string>, or null." ); } } } }; var metadataTagsAreEmpty = (tags) => { return tags.title === void 0 && tags.description === void 0 && tags.artist === void 0 && tags.album === void 0 && tags.albumArtist === void 0 && tags.trackNumber === void 0 && tags.tracksTotal === void 0 && tags.discNumber === void 0 && tags.discsTotal === void 0 && tags.genre === void 0 && tags.date === void 0 && tags.lyrics === void 0 && (!tags.images || tags.images.length === 0) && tags.comment === void 0 && (tags.raw === void 0 || Object.keys(tags.raw).length === 0); }; var DEFAULT_TRACK_DISPOSITION = { default: true, primary: true, forced: false, original: false, commentary: false, hearingImpaired: false, visuallyImpaired: false }; var validateTrackDisposition = (disposition) => { if (!disposition || typeof disposition !== "object") { throw new TypeError("disposition must be an object."); } if (disposition.default !== void 0 && typeof disposition.default !== "boolean") { throw new TypeError("disposition.default must be a boolean."); } if (disposition.primary !== void 0 && typeof disposition.primary !== "boolean") { throw new TypeError("disposition.primary must be a boolean."); } if (disposition.forced !== void 0 && typeof disposition.forced !== "boolean") { throw new TypeError("disposition.forced must be a boolean."); } if (disposition.original !== void 0 && typeof disposition.original !== "boolean") { throw new TypeError("disposition.original must be a boolean."); } if (disposition.commentary !== void 0 && typeof disposition.commentary !== "boolean") { throw new TypeError("disposition.commentary must be a boolean."); } if (disposition.hearingImpaired !== void 0 && typeof disposition.hearingImpaired !== "boolean") { throw new TypeError("disposition.hearingImpaired must be a boolean."); } if (disposition.visuallyImpaired !== void 0 && typeof disposition.visuallyImpaired !== "boolean") { throw new TypeError("disposition.visuallyImpaired must be a boolean."); } }; // shared/bitstream.ts 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; } writeBits(n, value) { const end = this.pos + n; for (let i = this.pos; i < end; i++) { const byteIndex = Math.floor(i / 8); let byte = this.bytes[byteIndex]; const bitIndex = 7 - (i & 7); byte &= ~(1 << bitIndex); byte |= (value & 1 << end - i - 1) >> end - i - 1 << bitIndex; this.bytes[byteIndex] = byte; } this.pos = end; } 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; } }; // shared/aac-misc.ts var aacFrequencyTable = [ 96e3, 88200, 64e3, 48e3, 44100, 32e3, 24e3, 22050, 16e3, 12e3, 11025, 8e3, 7350 ]; var aacChannelMap = [-1, 1, 2, 3, 4, 5, 6, 8]; 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 { if (frequencyIndex < aacFrequencyTable.length) { sampleRate = aacFrequencyTable[frequencyIndex]; } } const channelConfiguration = bitstream.readBits(4); let numberOfChannels = null; if (channelConfiguration >= 1 && channelConfiguration <= 7) { numberOfChannels = aacChannelMap[channelConfiguration]; } return { objectType, frequencyIndex, sampleRate, channelConfiguration, numberOfChannels }; }; var buildAacAudioSpecificConfig = (config) => { let frequencyIndex = aacFrequencyTable.indexOf(config.sampleRate); let customSampleRate = null; if (frequencyIndex === -1) { frequencyIndex = 15; customSampleRate = config.sampleRate; } const channelConfiguration = aacChannelMap.indexOf(config.numberOfChannels); if (channelConfiguration === -1) { throw new TypeError(`Unsupported number of channels: ${config.numberOfChannels}`); } let bitCount = 5 + 4 + 4; if (config.objectType >= 32) { bitCount += 6; } if (frequencyIndex === 15) { bitCount += 24; } const byteCount = Math.ceil(bitCount / 8); const bytes2 = new Uint8Array(byteCount); const bitstream = new Bitstream(bytes2); if (config.objectType < 32) { bitstream.writeBits(5, config.objectType); } else { bitstream.writeBits(5, 31); bitstream.writeBits(6, config.objectType - 32); } bitstream.writeBits(4, frequencyIndex); if (frequencyIndex === 15) { bitstream.writeBits(24, customSampleRate); } bitstream.writeBits(4, channelConfiguration); return bytes2; }; var buildAdtsHeaderTemplate = (config) => { const header = new Uint8Array(7); const bitstream = new Bitstream(header); const { objectType, frequencyIndex, channelConfiguration } = config; const profile = objectType - 1; bitstream.writeBits(12, 4095); bitstream.writeBits(1, 0); bitstream.writeBits(2, 0); bitstream.writeBits(1, 1); bitstream.writeBits(2, profile); bitstream.writeBits(4, frequencyIndex); bitstream.writeBits(1, 0); bitstream.writeBits(3, channelConfiguration); bitstream.writeBits(1, 0); bitstream.writeBits(1, 0); bitstream.writeBits(1, 0); bitstream.writeBits(1, 0); bitstream.skipBits(13); bitstream.writeBits(11, 2047); bitstream.writeBits(2, 0); return { header, bitstream }; }; var writeAdtsFrameLength = (bitstream, frameLength) => { bitstream.pos = 30; bitstream.writeBits(13, frameLength); }; // 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", "ac3", "eac3" ]; var AUDIO_CODECS = [ ...NON_PCM_AUDIO_CODECS, ...PCM_AUDIO_CODECS ]; var SUBTITLE_CODECS = [ "webvtt" ]; var AVC_LEVEL_TABLE = [ { maxMacroblocks: 99, maxBitrate: 64e3, maxDpbMbs: 396, level: 10 }, // Level 1 { maxMacroblocks: 396, maxBitrate: 192e3, maxDpbMbs: 900, level: 11 }, // Level 1.1 { maxMacroblocks: 396, maxBitrate: 384e3, maxDpbMbs: 2376, level: 12 }, // Level 1.2 { maxMacroblocks: 396, maxBitrate: 768e3, maxDpbMbs: 2376, level: 13 }, // Level 1.3 { maxMacroblocks: 396, maxBitrate: 2e6, maxDpbMbs: 2376, level: 20 }, // Level 2 { maxMacroblocks: 792, maxBitrate: 4e6, maxDpbMbs: 475