���ѧۧݧ�ӧ�� �ާ֧ߧ֧էا֧� - ���֧էѧܧ�ڧ��ӧѧ�� - /var/www/nitsch.de/wp-includes/js/dist/script-modules/video-conversion/worker.min.js
���ѧ٧ѧ�
var f={APPLY:"apply",CALLBACK:"callback",PING:"ping",PONG:"pong"},b={PROVIDER:"provider",INJECTOR:"injector"},E=e=>{let n=!!e?.type&&Object.values(f).includes(e.type),t=!!e?.id&&typeof e.id=="string",a=!!e?.path&&Array.isArray(e.path)&&e.path.every(p=>typeof p=="string"),i=!!e?.sender&&Object.values(b).includes(e.sender),r=e?.callbackIds===void 0||Array.isArray(e.callbackIds)&&e.callbackIds.every(p=>typeof p=="string"),m=e?.args===void 0||Array.isArray(e.args),s=e?.error===void 0||typeof e.error=="string",o=!!e?.meta&&e.meta!==null&&typeof e.meta=="object",c=!!e?.namespace&&typeof e.namespace=="string",u=!!e?.timeStamp&&typeof e.timeStamp=="number";return n&&t&&a&&i&&r&&m&&s&&o&&c&&u},N=()=>[void 0,void 0,void 0,void 0].map(()=>Math.floor(Math.random()*(2**53-1)).toString(16)).join("-"),C=N,V=(e,n,...t)=>{let a=setTimeout(()=>{clearTimeout(a),e(...t),a=setInterval(e,n,...t)},0);return()=>clearInterval(a)},W=V,H=(e,n)=>n!==void 0&&e instanceof n,d=H,z=e=>{let n=new WeakSet,t=a=>!a||typeof a!="object"||n.has(a)?[]:(n.add(a),d(a,globalThis.ArrayBuffer)||d(a,globalThis.MessagePort)||d(a,globalThis.ImageBitmap)||d(a,globalThis.OffscreenCanvas)||d(a,globalThis.AudioData)||d(a,globalThis.VideoFrame)||d(a,globalThis.RTCDataChannel)||d(a,globalThis.MediaSourceHandle)||d(a,globalThis.MIDIAccess)||d(a,globalThis.MediaStreamTrack)||d(a,globalThis.ReadableStream)||d(a,globalThis.WritableStream)||d(a,globalThis.TransformStream)?[a]:Array.isArray(a)?a.flatMap(t):Object.values(a).flatMap(t));return t(e)},w=z,A=Symbol("PROXY_MARKER"),G=async(e,n)=>{let t,a,i=new Set,r=new Promise((s,o)=>{t=W(async()=>{try{let c=C(),u=await e.onMessage(l=>{if(!E(l))return;let g=l;g.namespace===n.namespace&&g.sender===b.PROVIDER&&g.type===f.PONG&&g.id===c&&s()});u&&i.add(u);let p={type:f.PING,sender:b.INJECTOR,id:c,path:[],meta:{},namespace:n.namespace,timeStamp:Date.now()};e.sendMessage(p,n.transfer?w(p):[])}catch(c){o(c)}},n.heartbeatInterval)}),m=new Promise((s,o)=>{let c=setTimeout(()=>o(Error(`Provider unavailable: heartbeat check timeout ${n.heartbeatTimeout}ms.`)),n.heartbeatTimeout);a=()=>clearTimeout(c)});await Promise.race([r,m]).finally(()=>{t(),a(),i.forEach(s=>s()),i.clear()})},j=(e,n,t)=>(n.onMessage(async a=>{if(!E(a))return;let i=a;if(i.namespace===t.namespace&&i.sender===b.INJECTOR)switch(i.type){case f.PING:{let r={type:f.PONG,sender:b.PROVIDER,id:i.id,path:i.path,meta:i.meta,namespace:t.namespace,timeStamp:Date.now()};n.sendMessage(r,t.transfer?w(r):[]);break}case f.APPLY:{try{let m=i.args?.map(s=>i.callbackIds?.includes(s)?(...o)=>{let c={type:f.CALLBACK,sender:b.PROVIDER,id:s,path:i.path,meta:i.meta,data:o,namespace:t.namespace,timeStamp:Date.now()};n.sendMessage(c,t.transfer?w(c):[])}:s);i.data=await(i.path?.reduce((s,o)=>s[o],e)).apply(e,m||[])}catch(m){i.error=m.message}let r={type:f.APPLY,sender:b.PROVIDER,id:i.id,path:i.path,data:i.data,error:i.error,meta:i.meta,namespace:t.namespace,timeStamp:Date.now()};n.sendMessage(r,t.transfer?w(r):[]);break}}}),e),q=(e,n,t)=>{let a=(i,r)=>new Proxy(i,{get(s,o,c){if(s[A])return Reflect.get(s,o,c);let u=()=>{};return u[A]=!0,a(u,[...r,o])},apply(s,o,c){return new Promise(async(u,p)=>{try{t.heartbeatCheck&&await G(n,t);let l=[],g=c.map(y=>{if(typeof y=="function"){let h=C();return l.push(h),n.onMessage(L=>{if(!E(L))return;let k=L;k.namespace===t.namespace&&k.sender===b.PROVIDER&&k.type===f.CALLBACK&&k.id===h&&y(...k.data)}),h}else return y}),D=C(),U=await n.onMessage(y=>{if(!E(y))return;let h=y;h.namespace===t.namespace&&h.sender===b.PROVIDER&&h.type===f.APPLY&&h.id===D&&(h.error?p(Error(h.error)):u(h.data),U?.())}),M={type:f.APPLY,sender:b.INJECTOR,id:D,path:r,args:g,meta:{},callbackIds:l,timeStamp:Date.now(),namespace:t.namespace};n.sendMessage(M,t.transfer?w(M):[])}catch(l){p(l)}})}});return a(e,[])},$=(e,n)=>{let t;return(a,...i)=>t??=j(e(...i),a,n)},X=(e,n)=>{let t;return a=>t??=q(n.backup?Object.freeze(e()):{},a,n)},P=(e,n)=>{let t={namespace:n?.namespace??"__comctx__",heartbeatCheck:n?.heartbeatCheck??!0,heartbeatInterval:n?.heartbeatInterval??300,heartbeatTimeout:n?.heartbeatTimeout??1e3,transfer:n?.transfer??!1,backup:n?.backup??!1};if(t.heartbeatTimeout<=t.heartbeatInterval)throw Error(`Invalid heartbeat config: timeout (${t.heartbeatTimeout}ms) must exceed interval (${t.heartbeatInterval}ms).`);return[$(e,t),X(e,t)]};var R=Symbol("worker");var Q=class{worker;constructor(e){this.worker=e}sendMessage=(e,n)=>{this.worker.postMessage(e,n)};onMessage=e=>{let n=t=>e(t.data);return this.worker.addEventListener("message",n),()=>this.worker.removeEventListener("message",n)}},S=new WeakMap;function x(e,n){e.failure??=n;for(let t of e.pendingRejects)t(e.failure);e.pendingRejects.clear()}function _(e){let[,n]=P(()=>({}),{namespace:"__wordpress_worker__",heartbeatCheck:!1,transfer:!0}),t=n(new Q(e)),a=s=>{let o=s.message;x(r,new Error(o?`Worker error: ${o}`:"Worker error: the worker crashed or failed to load."))},i=()=>{x(r,new Error("Worker error: a message could not be deserialized."))};e.addEventListener("error",a),e.addEventListener("messageerror",i);let r={worker:e,pendingRejects:new Set,removeListeners:()=>{e.removeEventListener("error",a),e.removeEventListener("messageerror",i)}},m=new Proxy(t,{get(s,o){if(o===R)return e;let c=Reflect.get(s,o);return typeof c!="function"?c:(...u)=>r.failure?Promise.reject(r.failure):new Promise((p,l)=>{r.pendingRejects.add(l),Promise.resolve().then(()=>c(...u)).then(g=>{r.pendingRejects.delete(l),p(g)},g=>{r.pendingRejects.delete(l),l(g)})})}});return S.set(m,r),m}function I(e){let n=S.get(e);n&&(x(n,new Error("Worker was terminated.")),n.removeListeners(),S.delete(e),n.worker.terminate())}var O=`// node_modules/comctx/core/dist/index.js var e = { APPLY: \`apply\`, CALLBACK: \`callback\`, PING: \`ping\`, PONG: \`pong\` }; var t = { PROVIDER: \`provider\`, INJECTOR: \`injector\` }; var n = (n2) => { let r2 = !!n2?.type && Object.values(e).includes(n2.type), i2 = !!n2?.id && typeof n2.id == \`string\`, a2 = !!n2?.path && Array.isArray(n2.path) && n2.path.every((e2) => typeof e2 == \`string\`), o2 = !!n2?.sender && Object.values(t).includes(n2.sender), s2 = n2?.callbackIds === void 0 || Array.isArray(n2.callbackIds) && n2.callbackIds.every((e2) => typeof e2 == \`string\`), c2 = n2?.args === void 0 || Array.isArray(n2.args), l2 = n2?.error === void 0 || typeof n2.error == \`string\`, u2 = !!n2?.meta && n2.meta !== null && typeof n2.meta == \`object\`, d2 = !!n2?.namespace && typeof n2.namespace == \`string\`, f2 = !!n2?.timeStamp && typeof n2.timeStamp == \`number\`; return r2 && i2 && a2 && o2 && s2 && c2 && l2 && u2 && d2 && f2; }; var r = () => [...[, , , ,]].map(() => Math.floor(Math.random() * (2 ** 53 - 1)).toString(16)).join(\`-\`); var i = r; var a = (e2, t2, ...n2) => { let r2 = setTimeout(() => { clearTimeout(r2), e2(...n2), r2 = setInterval(e2, t2, ...n2); }, 0); return () => clearInterval(r2); }; var o = a; var s = (e2, t2) => t2 !== void 0 && e2 instanceof t2; var c = s; var l = (e2) => { let t2 = /* @__PURE__ */ new WeakSet(), n2 = (e3) => !e3 || typeof e3 != \`object\` || t2.has(e3) ? [] : (t2.add(e3), c(e3, globalThis.ArrayBuffer) || c(e3, globalThis.MessagePort) || c(e3, globalThis.ImageBitmap) || c(e3, globalThis.OffscreenCanvas) || c(e3, globalThis.AudioData) || c(e3, globalThis.VideoFrame) || c(e3, globalThis.RTCDataChannel) || c(e3, globalThis.MediaSourceHandle) || c(e3, globalThis.MIDIAccess) || c(e3, globalThis.MediaStreamTrack) || c(e3, globalThis.ReadableStream) || c(e3, globalThis.WritableStream) || c(e3, globalThis.TransformStream) ? [e3] : Array.isArray(e3) ? e3.flatMap(n2) : Object.values(e3).flatMap(n2)); return n2(e2); }; var u = l; var d = /* @__PURE__ */ Symbol(\`PROXY_MARKER\`); var f = async (r2, a2) => { let s2, c2, l2 = /* @__PURE__ */ new Set(), d2 = new Promise((c3, d3) => { s2 = o(async () => { try { let o2 = i(), s3 = await r2.onMessage((r3) => { if (!n(r3)) return; let i2 = r3; i2.namespace === a2.namespace && i2.sender === t.PROVIDER && i2.type === e.PONG && i2.id === o2 && c3(); }); s3 && l2.add(s3); let d4 = { type: e.PING, sender: t.INJECTOR, id: o2, path: [], meta: {}, namespace: a2.namespace, timeStamp: Date.now() }; r2.sendMessage(d4, a2.transfer ? u(d4) : []); } catch (e2) { d3(e2); } }, a2.heartbeatInterval); }), f2 = new Promise((e2, t2) => { let n2 = setTimeout(() => t2(Error(\`Provider unavailable: heartbeat check timeout \${a2.heartbeatTimeout}ms.\`)), a2.heartbeatTimeout); c2 = () => clearTimeout(n2); }); await Promise.race([d2, f2]).finally(() => { s2(), c2(), l2.forEach((e2) => e2()), l2.clear(); }); }; var p = (r2, i2, a2) => (i2.onMessage(async (o2) => { if (!n(o2)) return; let s2 = o2; if (s2.namespace === a2.namespace && s2.sender === t.INJECTOR) switch (s2.type) { case e.PING: { let n2 = { type: e.PONG, sender: t.PROVIDER, id: s2.id, path: s2.path, meta: s2.meta, namespace: a2.namespace, timeStamp: Date.now() }; i2.sendMessage(n2, a2.transfer ? u(n2) : []); break; } case e.APPLY: { try { let n3 = s2.args?.map((n4) => s2.callbackIds?.includes(n4) ? (...r3) => { let o3 = { type: e.CALLBACK, sender: t.PROVIDER, id: n4, path: s2.path, meta: s2.meta, data: r3, namespace: a2.namespace, timeStamp: Date.now() }; i2.sendMessage(o3, a2.transfer ? u(o3) : []); } : n4); s2.data = await (s2.path?.reduce((e2, t2) => e2[t2], r2)).apply(r2, n3 || []); } catch (e2) { s2.error = e2.message; } let n2 = { type: e.APPLY, sender: t.PROVIDER, id: s2.id, path: s2.path, data: s2.data, error: s2.error, meta: s2.meta, namespace: a2.namespace, timeStamp: Date.now() }; i2.sendMessage(n2, a2.transfer ? u(n2) : []); break; } } }), r2); var m = (r2, a2, o2) => { let s2 = (r3, c2) => { let l2 = new Proxy(r3, { get(e2, t2, n2) { if (e2[d]) return Reflect.get(e2, t2, n2); let r4 = () => { }; return r4[d] = true, s2(r4, [...c2, t2]); }, apply(r4, s3, l3) { return new Promise(async (r5, s4) => { try { o2.heartbeatCheck && await f(a2, o2); let d2 = [], p2 = l3.map((r6) => { if (typeof r6 == \`function\`) { let s5 = i(); return d2.push(s5), a2.onMessage((i2) => { if (!n(i2)) return; let a3 = i2; a3.namespace === o2.namespace && a3.sender === t.PROVIDER && a3.type === e.CALLBACK && a3.id === s5 && r6(...a3.data); }), s5; } else return r6; }), m2 = i(), h2 = await a2.onMessage((i2) => { if (!n(i2)) return; let a3 = i2; a3.namespace === o2.namespace && a3.sender === t.PROVIDER && a3.type === e.APPLY && a3.id === m2 && (a3.error ? s4(Error(a3.error)) : r5(a3.data), h2?.()); }), g2 = { type: e.APPLY, sender: t.INJECTOR, id: m2, path: c2, args: p2, meta: {}, callbackIds: d2, timeStamp: Date.now(), namespace: o2.namespace }; a2.sendMessage(g2, o2.transfer ? u(g2) : []); } catch (e2) { s4(e2); } }); } }); return l2; }; return s2(r2, []); }; var h = (e2, t2) => { let n2; return (r2, ...i2) => n2 ??= p(e2(...i2), r2, t2); }; var g = (e2, t2) => { let n2; return (r2) => n2 ??= m(t2.backup ? Object.freeze(e2()) : {}, r2, t2); }; var _ = (e2, t2) => { let n2 = { namespace: t2?.namespace ?? \`__comctx__\`, heartbeatCheck: t2?.heartbeatCheck ?? true, heartbeatInterval: t2?.heartbeatInterval ?? 300, heartbeatTimeout: t2?.heartbeatTimeout ?? 1e3, transfer: t2?.transfer ?? false, backup: t2?.backup ?? false }; if (n2.heartbeatTimeout <= n2.heartbeatInterval) throw Error(\`Invalid heartbeat config: timeout (\${n2.heartbeatTimeout}ms) must exceed interval (\${n2.heartbeatInterval}ms).\`); return [h(e2, n2), g(e2, n2)]; }; // packages/worker-threads/build-module/worker-thread.mjs var WorkerProvideAdapter = class { sendMessage = (message, transfer) => { self.postMessage(message, { transfer }); }; onMessage = (callback) => { const handler = (event) => callback(event.data); self.addEventListener("message", handler); return () => self.removeEventListener("message", handler); }; }; function normalizeThrowable(error) { if (error instanceof Error && error.message) { return error; } let message = ""; try { message = String(error); } catch { } return new Error(message || "Unknown error in worker thread"); } function withNormalizedErrors(target) { const wrapped = {}; for (const key of Object.keys(target)) { const value = target[key]; if (typeof value !== "function") { wrapped[key] = value; continue; } wrapped[key] = async (...args) => { try { return await value.apply(target, args); } catch (error) { throw normalizeThrowable(error); } }; } return wrapped; } function expose(target) { const [provide] = _(() => withNormalizedErrors(target), { namespace: "__wordpress_worker__", heartbeatCheck: false, transfer: true }); provide(new WorkerProvideAdapter()); } // node_modules/mediabunny/dist/modules/src/misc.js 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 i2 = start; i2 < end; i2++) { const byteIndex = Math.floor(i2 / 8); let byte = bytes2[byteIndex]; const bitIndex = 7 - (i2 & 7); byte &= ~(1 << bitIndex); byte |= (value & 1 << end - i2 - 1) >> end - i2 - 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 textEncoder = /* @__PURE__ */ new TextEncoder(); 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 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 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 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 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 promiseWithResolvers = () => { let resolve; let reject; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; }; 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 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 UNDETERMINED_LANGUAGE = "und"; var roundToMultiple = (value, multiple) => { return Math.round(value / multiple) * multiple; }; var roundToDivisor = (value, multiple) => { return Math.round(value * multiple) / multiple; }; var floorToDivisor = (value, multiple) => { return Math.floor(value * multiple) / multiple; }; 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 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 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 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 uint8ArraysAreEqual = (a2, b) => { if (a2.length !== b.length) { return false; } for (let i2 = 0; i2 < a2.length; i2++) { if (a2[i2] !== b[i2]) { return false; } } return true; }; var polyfillSymbolDispose = () => { Symbol.dispose ??= /* @__PURE__ */ Symbol("Symbol.dispose"); }; var arrayArgmin = (array, getValue) => { let minIndex = -1; let minValue = Infinity; for (let i2 = 0; i2 < array.length; i2++) { const value = getValue(array[i2]); if (value < minValue) { minValue = value; minIndex = i2; } } return minIndex; }; var simplifyRational = (rational) => { assert(Number.isInteger(rational.num)); assert(Number.isInteger(rational.den)); assert(rational.den !== 0); let a2 = Math.abs(rational.num); let b = Math.abs(rational.den); while (b !== 0) { const t2 = a2 % b; a2 = b; b = t2; } const gcd = a2 || 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 toArray = (x) => { if (Array.isArray(x)) { return x; } else { return [x]; } }; var EventEmitter = class { constructor() { this._listeners = /* @__PURE__ */ new Map(); } /** Registers a listener for the given event. Returns a function that, when called, removes the listener again. */ 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 isRecordStringString = (value) => { return value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every((x) => typeof x === "string"); }; // node_modules/mediabunny/dist/modules/src/logging.js var LogLevel; (function(LogLevel2) { LogLevel2[LogLevel2["Silent"] = 0] = "Silent"; LogLevel2[LogLevel2["Errors"] = 1] = "Errors"; LogLevel2[LogLevel2["Warnings"] = 2] = "Warnings"; LogLevel2[LogLevel2["Info"] = 3] = "Info"; })(LogLevel || (LogLevel = {})); var Logging = class _Logging { constructor() { } /** The current log level. Defaults to {@link LogLevel.Info}. */ static get level() { return _Logging._level; } static set level(value) { if (value !== LogLevel.Silent && value !== LogLevel.Errors && value !== LogLevel.Warnings && value !== LogLevel.Info) { throw new TypeError("Invalid log level. Use one of the values of the LogLevel enum."); } _Logging._level = value; } /** @internal */ static get _emitter() { return _Logging._emitterInstance ??= new EventEmitter(); } /** Registers a listener for a log event. Returns a function that, when called, removes the listener again. */ static on(event, listener, options) { return _Logging._emitter.on(event, listener, options); } /** @internal */ static _error(...args) { _Logging._emitter._emit("error", args); if (_Logging._level >= LogLevel.Errors) { console.error(...args); } } /** @internal */ static _warn(...args) { _Logging._emitter._emit("warn", args); if (_Logging._level >= LogLevel.Warnings) { console.warn(...args); } } /** @internal */ static _info(...args) { _Logging._emitter._emit("info", args); if (_Logging._level >= LogLevel.Info) { console.info(...args); } } }; Logging._level = LogLevel.Info; Logging._emitterInstance = null; // node_modules/mediabunny/dist/modules/src/metadata.js 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 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."); } }; // node_modules/mediabunny/dist/modules/shared/bitstream.js var Bitstream = class _Bitstream { constructor(bytes2) { this.bytes = bytes2; 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(n2) { if (n2 === 1) { return this.readBit(); } let result = 0; for (let i2 = 0; i2 < n2; i2++) { result <<= 1; result |= this.readBit(); } return result; } writeBits(n2, value) { const end = this.pos + n2; for (let i2 = this.pos; i2 < end; i2++) { const byteIndex = Math.floor(i2 / 8); let byte = this.bytes[byteIndex]; const bitIndex = 7 - (i2 & 7); byte &= ~(1 << bitIndex); byte |= (value & 1 << end - i2 - 1) >> end - i2 - 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(n2) { this.pos += n2; } getBitsLeft() { return this.bytes.length * 8 - this.pos; } clone() { const clone = new _Bitstream(this.bytes); clone.pos = this.pos; return clone; } }; // node_modules/mediabunny/dist/modules/shared/aac-misc.js 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 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; }; // node_modules/mediabunny/dist/modules/src/codec.js 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: 4752, level: 21 }, // Level 2.1 { maxMacroblocks: 1620, maxBitrate: 4e6, maxDpbMbs: 8100, level: 22 }, // Level 2.2 { maxMacroblocks: 1620, maxBitrate: 1e7, maxDpbMbs: 8100, level: 30 }, // Level 3 { maxMacroblocks: 3600, maxBitrate: 14e6, maxDpbMbs: 18e3, level: 31 }, // Level 3.1 { maxMacroblocks: 5120, maxBitrate: 2e7, maxDpbMbs: 20480, level: 32 }, // Level 3.2 { maxMacroblocks: 8192, maxBitrate: 2e7, maxDpbMbs: 32768, level: 40 }, // Level 4 { maxMacroblocks: 8192, maxBitrate: 5e7, maxDpbMbs: 32768, level: 41 }, // Level 4.1 { maxMacroblocks: 8704, maxBitrate: 5e7, maxDpbMbs: 34816, level: 42 }, // Level 4.2 { maxMacroblocks: 22080, maxBitrate: 135e6, maxDpbMbs: 110400, level: 50 }, // Level 5 { maxMacroblocks: 36864, maxBitrate: 24e7, maxDpbMbs: 184320, level: 51 }, // Level 5.1 { maxMacroblocks: 36864, maxBitrate: 24e7, maxDpbMbs: 184320, level: 52 }, // Level 5.2 { maxMacroblocks: 139264, maxBitrate: 24e7, maxDpbMbs: 696320, level: 60 }, // Level 6 { maxMacroblocks: 139264, maxBitrate: 48e7, maxDpbMbs: 696320, level: 61 }, // Level 6.1 { maxMacroblocks: 139264, maxBitrate: 8e8, maxDpbMbs: 696320, 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 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 OPUS_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 === "mp3" || codecString === "mp4a.69" || codecString === "mp4a.6B" || codecString === "mp4a.6b" || codecString === "mp4a.40.34") { return "mp3"; } else if (codecString.startsWith("mp4a.40.") || codecString === "mp4a.67") { return "aac"; } else if (codecString === "opus") { return "opus"; } else if (codecString === "vorbis") { return "vorbis"; } else if (codecString === "flac") { return "flac"; } else if (codecString === "ac-3" || codecString === "ac3") { return "ac3"; } else if (codecString === "ec-3" || codecString === "eac3") { return "eac3"; } 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 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 Mediabunny 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.displayAspectWidth !== void 0 && (!Number.isInteger(metadata.decoderConfig.displayAspectWidth) || metadata.decoderConfig.displayAspectWidth <= 0)) { throw new TypeError("Video chunk metadata decoder configuration displayAspectWidth, when defined, must be a positive integer."); } if (metadata.decoderConfig.displayAspectHeight !== void 0 && (!Number.isInteger(metadata.decoderConfig.displayAspectHeight) || metadata.decoderConfig.displayAspectHeight <= 0)) { throw new TypeError("Video chunk metadata decoder configuration displayAspectHeight, when defined, must be a positive integer."); } if (metadata.decoderConfig.displayAspectWidth !== void 0 !== (metadata.decoderConfig.displayAspectHeight !== void 0)) { throw new TypeError("Video chunk metadata decoder configuration must specify both displayAspectWidth and displayAspectHeight, or neither."); } 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 = Object.keys(TRANSFER_CHARACTERISTICS_MAP); if (colorSpace.transfer != null && !transferValues.includes(colorSpace.transfer)) { throw new TypeError(\`Video chunk metadata decoder configuration colorSpace transfer, when defined, must be one of \${transferValues.join(", ")}.\`); } const matrixValues = Object.keys(MATRIX_COEFFICIENTS_MAP); if (colorSpace.matrix != null && !matrixValues.includes(colorSpace.matrix)) { throw new TypeError(\`Video chunk metadata decoder configuration colorSpace matrix, when defined, must be one of \${matrixValues.join(", ")}.\`); } if (colorSpace.fullRange != null && typeof colorSpace.fullRange !== "boolean") { throw new TypeError("Video chunk metadata decoder configuration colorSpace fullRange, when defined, must be a boolean."); } } if (metadata.decoderConfig.codec.startsWith("avc1") || metadata.decoderConfig.codec.startsWith("avc3")) { if (!AVC_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) { throw new TypeError("Video chunk metadata decoder configuration codec string for AVC must be a valid AVC codec string as specified in Section 3.4 of RFC 6381."); } } else if (metadata.decoderConfig.codec.startsWith("hev1") || metadata.decoderConfig.codec.startsWith("hvc1")) { if (!HEVC_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) { throw new TypeError("Video chunk metadata decoder configuration codec string for HEVC must be a valid HEVC codec string as specified in Section E.3 of ISO 14496-15."); } } else if (metadata.decoderConfig.codec.startsWith("vp8")) { if (metadata.decoderConfig.codec !== "vp8") { throw new TypeError('Video chunk metadata decoder configuration codec string for VP8 must be "vp8".'); } } else if (metadata.decoderConfig.codec.startsWith("vp09")) { if (!VP9_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) { throw new TypeError('Video chunk metadata decoder configuration codec string for VP9 must be a valid VP9 codec string as specified in Section "Codecs Parameter String" of https://www.webmproject.org/vp9/mp4/.'); } } else if (metadata.decoderConfig.codec.startsWith("av01")) { if (!AV1_CODEC_STRING_REGEX.test(metadata.decoderConfig.codec)) { throw new TypeError('Video chunk metadata decoder configuration codec string for AV1 must be a valid AV1 codec string as specified in Section "Codecs Parameter String" of https://aomediacodec.github.io/av1-isobmff/.'); } } }; var VALID_AUDIO_CODEC_STRING_PREFIXES = [ "mp4a", "mp3", "opus", "vorbis", "flac", "ulaw", "alaw", "pcm", "ac-3", "ec-3" ]; var validateAudioChunkMetadata = (metadata) => { if (!metadata) { throw new TypeError("Audio chunk metadata must be provided."); } if (typeof metadata !== "object") { throw new TypeError("Audio chunk metadata must be an object."); } if (!metadata.decoderConfig) { throw new TypeError("Audio chunk metadata must include a decoder configuration."); } if (typeof metadata.decoderConfig !== "object") { throw new TypeError("Audio chunk metadata decoder configuration must be an object."); } if (typeof metadata.decoderConfig.codec !== "string") { throw new TypeError("Audio chunk metadata decoder configuration must specify a codec string."); } if (!VALID_AUDIO_CODEC_STRING_PREFIXES.some((prefix) => metadata.decoderConfig.codec.startsWith(prefix))) { throw new TypeError("Audio chunk metadata decoder configuration codec string must be a valid audio codec string as specified in the Mediabunny Codec Registry."); } if (!Number.isInteger(metadata.decoderConfig.sampleRate) || metadata.decoderConfig.sampleRate <= 0) { throw new TypeError("Audio chunk metadata decoder configuration must specify a valid sampleRate (positive integer)."); } if (!Number.isInteger(metadata.decoderConfig.numberOfChannels) || metadata.decoderConfig.numberOfChannels <= 0) { throw new TypeError("Audio chunk metadata decoder configuration must specify a valid numberOfChannels (positive integer)."); } if (metadata.decoderConfig.description !== void 0) { if (!isAllowSharedBufferSource(metadata.decoderConfig.description)) { throw new TypeError("Audio chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view."); } } if (metadata.decoderConfig.codec.startsWith("mp4a") && metadata.decoderConfig.codec !== "mp4a.69" && metadata.decoderConfig.codec !== "mp4a.6B" && metadata.decoderConfig.codec !== "mp4a.6b") { const validStrings = ["mp4a.40.2", "mp4a.40.02", "mp4a.40.5", "mp4a.40.05", "mp4a.40.29", "mp4a.67"]; if (!validStrings.includes(metadata.decoderConfig.codec)) { throw new TypeError("Audio chunk metadata decoder configuration codec string for AAC must be a valid AAC codec string as specified in https://www.w3.org/TR/webcodecs-aac-codec-registration/."); } } else if (metadata.decoderConfig.codec.startsWith("mp3") || metadata.decoderConfig.codec.startsWith("mp4a")) { if (metadata.decoderConfig.codec !== "mp3" && metadata.decoderConfig.codec !== "mp4a.69" && metadata.decoderConfig.codec !== "mp4a.6B" && metadata.decoderConfig.codec !== "mp4a.6b") { throw new TypeError('Audio chunk metadata decoder configuration codec string for MP3 must be "mp3", "mp4a.69" or "mp4a.6B".'); } } else if (metadata.decoderConfig.codec.startsWith("opus")) { if (metadata.decoderConfig.codec !== "opus") { throw new TypeError('Audio chunk metadata decoder configuration codec string for Opus must be "opus".'); } if (metadata.decoderConfig.description && metadata.decoderConfig.description.byteLength < 18) { throw new TypeError("Audio chunk metadata decoder configuration description, when specified, is expected to be an Identification Header as specified in Section 5.1 of RFC 7845."); } } else if (metadata.decoderConfig.codec.startsWith("vorbis")) { if (metadata.decoderConfig.codec !== "vorbis") { throw new TypeError('Audio chunk metadata decoder configuration codec string for Vorbis must be "vorbis".'); } if (!metadata.decoderConfig.description) { throw new TypeError("Audio chunk metadata decoder configuration for Vorbis must include a description, which is expected to adhere to the format described in https://www.w3.org/TR/webcodecs-vorbis-codec-registration/."); } } else if (metadata.decoderConfig.codec.startsWith("flac")) { if (metadata.decoderConfig.codec !== "flac") { throw new TypeError('Audio chunk metadata decoder configuration codec string for FLAC must be "flac".'); } const minDescriptionSize = 4 + 4 + 34; if (!metadata.decoderConfig.description || metadata.decoderConfig.description.byteLength < minDescriptionSize) { throw new TypeError("Audio chunk metadata decoder configuration for FLAC must include a description, which is expected to adhere to the format described in https://www.w3.org/TR/webcodecs-flac-codec-registration/."); } } else if (metadata.decoderConfig.codec.startsWith("ac-3") || metadata.decoderConfig.codec.startsWith("ac3")) { if (metadata.decoderConfig.codec !== "ac-3") { throw new TypeError('Audio chunk metadata decoder configuration codec string for AC-3 must be "ac-3".'); } } else if (metadata.decoderConfig.codec.startsWith("ec-3") || metadata.decoderConfig.codec.startsWith("eac3")) { if (metadata.decoderConfig.codec !== "ec-3") { throw new TypeError('Audio chunk metadata decoder configuration codec string for EC-3 must be "ec-3".'); } } else if (metadata.decoderConfig.codec.startsWith("pcm") || metadata.decoderConfig.codec.startsWith("ulaw") || metadata.decoderConfig.codec.startsWith("alaw")) { if (!PCM_AUDIO_CODECS.includes(metadata.decoderConfig.codec)) { throw new TypeError(\`Audio chunk metadata decoder configuration codec string for PCM must be one of the supported PCM codecs (\${PCM_AUDIO_CODECS.join(", ")}).\`); } } }; var validateSubtitleMetadata = (metadata) => { if (!metadata) { throw new TypeError("Subtitle metadata must be provided."); } if (typeof metadata !== "object") { throw new TypeError("Subtitle metadata must be an object."); } if (!metadata.config) { throw new TypeError("Subtitle metadata must include a config object."); } if (typeof metadata.config !== "object") { throw new TypeError("Subtitle metadata config must be an object."); } if (typeof metadata.config.description !== "string") { throw new TypeError("Subtitle metadata config description must be a string."); } }; // node_modules/mediabunny/dist/modules/shared/ac3-misc.js var AC3_SAMPLE_RATES = [48e3, 44100, 32e3]; var EAC3_REDUCED_SAMPLE_RATES = [24e3, 22050, 16e3]; // node_modules/mediabunny/dist/modules/src/codec-data.js var AvcNalUnitType; (function(AvcNalUnitType2) { AvcNalUnitType2[AvcNalUnitType2["NON_IDR_SLICE"] = 1] = "NON_IDR_SLICE"; AvcNalUnitType2[AvcNalUnitType2["SLICE_DPA"] = 2] = "SLICE_DPA"; AvcNalUnitType2[AvcNalUnitType2["SLICE_DPB"] = 3] = "SLICE_DPB"; AvcNalUnitType2[AvcNalUnitType2["SLICE_DPC"] = 4] = "SLICE_DPC"; AvcNalUnitType2[AvcNalUnitType2["IDR"] = 5] = "IDR"; AvcNalUnitType2[AvcNalUnitType2["SEI"] = 6] = "SEI"; AvcNalUnitType2[AvcNalUnitType2["SPS"] = 7] = "SPS"; AvcNalUnitType2[AvcNalUnitType2["PPS"] = 8] = "PPS"; AvcNalUnitType2[AvcNalUnitType2["AUD"] = 9] = "AUD"; AvcNalUnitType2[AvcNalUnitType2["SPS_EXT"] = 13] = "SPS_EXT"; })(AvcNalUnitType || (AvcNalUnitType = {})); var HevcNalUnitType; (function(HevcNalUnitType2) { HevcNalUnitType2[HevcNalUnitType2["RASL_N"] = 8] = "RASL_N"; HevcNalUnitType2[HevcNalUnitType2["RASL_R"] = 9] = "RASL_R"; HevcNalUnitType2[HevcNalUnitType2["BLA_W_LP"] = 16] = "BLA_W_LP"; HevcNalUnitType2[HevcNalUnitType2["RSV_IRAP_VCL23"] = 23] = "RSV_IRAP_VCL23"; HevcNalUnitType2[HevcNalUnitType2["VPS_NUT"] = 32] = "VPS_NUT"; HevcNalUnitType2[HevcNalUnitType2["SPS_NUT"] = 33] = "SPS_NUT"; HevcNalUnitType2[HevcNalUnitType2["PPS_NUT"] = 34] = "PPS_NUT"; HevcNalUnitType2[HevcNalUnitType2["AUD_NUT"] = 35] = "AUD_NUT"; HevcNalUnitType2[HevcNalUnitType2["PREFIX_SEI_NUT"] = 39] = "PREFIX_SEI_NUT"; HevcNalUnitType2[HevcNalUnitType2["SUFFIX_SEI_NUT"] = 40] = "SUFFIX_SEI_NUT"; })(HevcNalUnitType || (HevcNalUnitType = {})); var iterateNalUnitsInAnnexB = function* (packetData) { let i2 = 0; let nalStart = -1; while (i2 < packetData.length - 2) { const zeroIndex = packetData.indexOf(0, i2); if (zeroIndex === -1 || zeroIndex >= packetData.length - 2) { break; } i2 = zeroIndex; let startCodeLength = 0; if (i2 + 3 < packetData.length && packetData[i2 + 1] === 0 && packetData[i2 + 2] === 0 && packetData[i2 + 3] === 1) { startCodeLength = 4; } else if (packetData[i2 + 1] === 0 && packetData[i2 + 2] === 1) { startCodeLength = 3; } if (startCodeLength === 0) { i2++; continue; } if (nalStart !== -1 && i2 > nalStart) { yield { offset: nalStart, length: i2 - nalStart }; } nalStart = i2 + startCodeLength; i2 = nalStart; } if (nalStart !== -1 && nalStart < packetData.length) { yield { offset: nalStart, length: packetData.length - nalStart }; } }; var iterateNalUnitsInLengthPrefixed = function* (packetData, lengthSize) { let offset = 0; const dataView = new DataView(packetData.buffer, packetData.byteOffset, packetData.byteLength); while (offset + lengthSize <= packetData.length) { let nalUnitLength; if (lengthSize === 1) { nalUnitLength = dataView.getUint8(offset); } else if (lengthSize === 2) { nalUnitLength = dataView.getUint16(offset, false); } else if (lengthSize === 3) { nalUnitLength = getUint24(dataView, offset, false); } else { assert(lengthSize === 4); nalUnitLength = dataView.getUint32(offset, false); } offset += lengthSize; yield { offset, length: nalUnitLength }; offset += nalUnitLength; } }; var iterateAvcNalUnits = (packetData, decoderConfig) => { if (decoderConfig.description) { const bytes2 = toUint8Array(decoderConfig.description); const lengthSizeMinusOne = bytes2[4] & 3; const lengthSize = lengthSizeMinusOne + 1; return iterateNalUnitsInLengthPrefixed(packetData, lengthSize); } else { return iterateNalUnitsInAnnexB(packetData); } }; var extractNalUnitTypeForAvc = (byte) => { return byte & 31; }; var removeEmulationPreventionBytes = (data) => { const result = []; const len = data.length; for (let i2 = 0; i2 < len; i2++) { if (i2 + 2 < len && data[i2] === 0 && data[i2 + 1] === 0 && data[i2 + 2] === 3) { result.push(0, 0); i2 += 2; } else { result.push(data[i2]); } } return new Uint8Array(result); }; var ANNEX_B_START_CODE = new Uint8Array([0, 0, 0, 1]); var concatNalUnitsInLengthPrefixed = (nalUnits, lengthSize) => { const totalLength = nalUnits.reduce((a2, b) => a2 + lengthSize + b.byteLength, 0); const result = new Uint8Array(totalLength); let offset = 0; for (const nalUnit of nalUnits) { const dataView = new DataView(result.buffer, result.byteOffset, result.byteLength); switch (lengthSize) { case 1: dataView.setUint8(offset, nalUnit.byteLength); break; case 2: dataView.setUint16(offset, nalUnit.byteLength, false); break; case 3: setUint24(dataView, offset, nalUnit.byteLength, false); break; case 4: dataView.setUint32(offset, nalUnit.byteLength, false); break; } offset += lengthSize; result.set(nalUnit, offset); offset += nalUnit.byteLength; } return result; }; var extractAvcDecoderConfigurationRecord = (packetData) => { try { const spsUnits = []; const ppsUnits = []; const spsExtUnits = []; for (const loc of iterateNalUnitsInAnnexB(packetData)) { const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length); const type = extractNalUnitTypeForAvc(nalUnit[0]); if (type === AvcNalUnitType.SPS) { spsUnits.push(nalUnit); } else if (type === AvcNalUnitType.PPS) { ppsUnits.push(nalUnit); } else if (type === AvcNalUnitType.SPS_EXT) { spsExtUnits.push(nalUnit); } } if (spsUnits.length === 0) { return null; } if (ppsUnits.length === 0) { return null; } const spsData = spsUnits[0]; const spsInfo = parseAvcSps(spsData); assert(spsInfo !== null); const hasExtendedData = spsInfo.profileIdc === 100 || spsInfo.profileIdc === 110 || spsInfo.profileIdc === 122 || spsInfo.profileIdc === 144; return { configurationVersion: 1, avcProfileIndication: spsInfo.profileIdc, profileCompatibility: spsInfo.constraintFlags, avcLevelIndication: spsInfo.levelIdc, lengthSizeMinusOne: 3, // Typically 4 bytes for length field sequenceParameterSets: spsUnits, pictureParameterSets: ppsUnits, chromaFormat: hasExtendedData ? spsInfo.chromaFormatIdc : null, bitDepthLumaMinus8: hasExtendedData ? spsInfo.bitDepthLumaMinus8 : null, bitDepthChromaMinus8: hasExtendedData ? spsInfo.bitDepthChromaMinus8 : null, sequenceParameterSetExt: hasExtendedData ? spsExtUnits : null }; } catch (error) { Logging._error("Error building AVC Decoder Configuration Record:", error); return null; } }; var serializeAvcDecoderConfigurationRecord = (record) => { const bytes2 = []; bytes2.push(record.configurationVersion); bytes2.push(record.avcProfileIndication); bytes2.push(record.profileCompatibility); bytes2.push(record.avcLevelIndication); bytes2.push(252 | record.lengthSizeMinusOne & 3); bytes2.push(224 | record.sequenceParameterSets.length & 31); for (const sps of record.sequenceParameterSets) { const length = sps.byteLength; bytes2.push(length >> 8); bytes2.push(length & 255); for (let i2 = 0; i2 < length; i2++) { bytes2.push(sps[i2]); } } bytes2.push(record.pictureParameterSets.length); for (const pps of record.pictureParameterSets) { const length = pps.byteLength; bytes2.push(length >> 8); bytes2.push(length & 255); for (let i2 = 0; i2 < length; i2++) { bytes2.push(pps[i2]); } } if (record.avcProfileIndication === 100 || record.avcProfileIndication === 110 || record.avcProfileIndication === 122 || record.avcProfileIndication === 144) { assert(record.chromaFormat !== null); assert(record.bitDepthLumaMinus8 !== null); assert(record.bitDepthChromaMinus8 !== null); assert(record.sequenceParameterSetExt !== null); bytes2.push(252 | record.chromaFormat & 3); bytes2.push(248 | record.bitDepthLumaMinus8 & 7); bytes2.push(248 | record.bitDepthChromaMinus8 & 7); bytes2.push(record.sequenceParameterSetExt.length); for (const spsExt of record.sequenceParameterSetExt) { const length = spsExt.byteLength; bytes2.push(length >> 8); bytes2.push(length & 255); for (let i2 = 0; i2 < length; i2++) { bytes2.push(spsExt[i2]); } } } return new Uint8Array(bytes2); }; var AVC_HEVC_ASPECT_RATIO_IDC_TABLE = { 1: { num: 1, den: 1 }, 2: { num: 12, den: 11 }, 3: { num: 10, den: 11 }, 4: { num: 16, den: 11 }, 5: { num: 40, den: 33 }, 6: { num: 24, den: 11 }, 7: { num: 20, den: 11 }, 8: { num: 32, den: 11 }, 9: { num: 80, den: 33 }, 10: { num: 18, den: 11 }, 11: { num: 15, den: 11 }, 12: { num: 64, den: 33 }, 13: { num: 160, den: 99 }, 14: { num: 4, den: 3 }, 15: { num: 3, den: 2 }, 16: { num: 2, den: 1 } }; var parseAvcSps = (sps) => { try { const bitstream = new Bitstream(removeEmulationPreventionBytes(sps)); bitstream.skipBits(1); bitstream.skipBits(2); const nalUnitType = bitstream.readBits(5); if (nalUnitType !== 7) { return null; } const profileIdc = bitstream.readAlignedByte(); const constraintFlags = bitstream.readAlignedByte(); const levelIdc = bitstream.readAlignedByte(); readExpGolomb(bitstream); let chromaFormatIdc = 1; let bitDepthLumaMinus8 = 0; let bitDepthChromaMinus8 = 0; let separateColourPlaneFlag = 0; if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) { chromaFormatIdc = readExpGolomb(bitstream); if (chromaFormatIdc === 3) { separateColourPlaneFlag = bitstream.readBits(1); } bitDepthLumaMinus8 = readExpGolomb(bitstream); bitDepthChromaMinus8 = readExpGolomb(bitstream); bitstream.skipBits(1); const seqScalingMatrixPresentFlag = bitstream.readBits(1); if (seqScalingMatrixPresentFlag) { for (let i2 = 0; i2 < (chromaFormatIdc !== 3 ? 8 : 12); i2++) { const seqScalingListPresentFlag = bitstream.readBits(1); if (seqScalingListPresentFlag) { const sizeOfScalingList = i2 < 6 ? 16 : 64; let lastScale = 8; let nextScale = 8; for (let j = 0; j < sizeOfScalingList; j++) { if (nextScale !== 0) { const deltaScale = readSignedExpGolomb(bitstream); nextScale = (lastScale + deltaScale + 256) % 256; } lastScale = nextScale === 0 ? lastScale : nextScale; } } } } } readExpGolomb(bitstream); const picOrderCntType = readExpGolomb(bitstream); if (picOrderCntType === 0) { readExpGolomb(bitstream); } else if (picOrderCntType === 1) { bitstream.skipBits(1); readSignedExpGolomb(bitstream); readSignedExpGolomb(bitstream); const numRefFramesInPicOrderCntCycle = readExpGolomb(bitstream); for (let i2 = 0; i2 < numRefFramesInPicOrderCntCycle; i2++) { readSignedExpGolomb(bitstream); } } readExpGolomb(bitstream); bitstream.skipBits(1); const picWidthInMbsMinus1 = readExpGolomb(bitstream); const picHeightInMapUnitsMinus1 = readExpGolomb(bitstream); const codedWidth = 16 * (picWidthInMbsMinus1 + 1); const codedHeight = 16 * (picHeightInMapUnitsMinus1 + 1); let displayWidth = codedWidth; let displayHeight = codedHeight; const frameMbsOnlyFlag = bitstream.readBits(1); if (!frameMbsOnlyFlag) { bitstream.skipBits(1); } bitstream.skipBits(1); const frameCroppingFlag = bitstream.readBits(1); if (frameCroppingFlag) { const frameCropLeftOffset = readExpGolomb(bitstream); const frameCropRightOffset = readExpGolomb(bitstream); const frameCropTopOffset = readExpGolomb(bitstream); const frameCropBottomOffset = readExpGolomb(bitstream); let cropUnitX; let cropUnitY; const chromaArrayType = separateColourPlaneFlag === 0 ? chromaFormatIdc : 0; if (chromaArrayType === 0) { cropUnitX = 1; cropUnitY = 2 - frameMbsOnlyFlag; } else { const subWidthC = chromaFormatIdc === 3 ? 1 : 2; const subHeightC = chromaFormatIdc === 1 ? 2 : 1; cropUnitX = subWidthC; cropUnitY = subHeightC * (2 - frameMbsOnlyFlag); } displayWidth -= cropUnitX * (frameCropLeftOffset + frameCropRightOffset); displayHeight -= cropUnitY * (frameCropTopOffset + frameCropBottomOffset); } let colourPrimaries = 2; let transferCharacteristics = 2; let matrixCoefficients = 2; let fullRangeFlag = 0; let pixelAspectRatio = { num: 1, den: 1 }; let numReorderFrames = null; let maxDecFrameBuffering = null; const vuiParametersPresentFlag = bitstream.readBits(1); if (vuiParametersPresentFlag) { const aspectRatioInfoPresentFlag = bitstream.readBits(1); if (aspectRatioInfoPresentFlag) { const aspectRatioIdc = bitstream.readBits(8); if (aspectRatioIdc === 255) { pixelAspectRatio = { num: bitstream.readBits(16), den: bitstream.readBits(16) }; } else { const aspectRatio = AVC_HEVC_ASPECT_RATIO_IDC_TABLE[aspectRatioIdc]; if (aspectRatio) { pixelAspectRatio = aspectRatio; } } } const overscanInfoPresentFlag = bitstream.readBits(1); if (overscanInfoPresentFlag) { bitstream.skipBits(1); } const videoSignalTypePresentFlag = bitstream.readBits(1); if (videoSignalTypePresentFlag) { bitstream.skipBits(3); fullRangeFlag = bitstream.readBits(1); const colourDescriptionPresentFlag = bitstream.readBits(1); if (colourDescriptionPresentFlag) { colourPrimaries = bitstream.readBits(8); transferCharacteristics = bitstream.readBits(8); matrixCoefficients = bitstream.readBits(8); } } const chromaLocInfoPresentFlag = bitstream.readBits(1); if (chromaLocInfoPresentFlag) { readExpGolomb(bitstream); readExpGolomb(bitstream); } const timingInfoPresentFlag = bitstream.readBits(1); if (timingInfoPresentFlag) { bitstream.skipBits(32); bitstream.skipBits(32); bitstream.skipBits(1); } const nalHrdParametersPresentFlag = bitstream.readBits(1); if (nalHrdParametersPresentFlag) { skipAvcHrdParameters(bitstream); } const vclHrdParametersPresentFlag = bitstream.readBits(1); if (vclHrdParametersPresentFlag) { skipAvcHrdParameters(bitstream); } if (nalHrdParametersPresentFlag || vclHrdParametersPresentFlag) { bitstream.skipBits(1); } bitstream.skipBits(1); const bitstreamRestrictionFlag = bitstream.readBits(1); if (bitstreamRestrictionFlag) { bitstream.skipBits(1); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); numReorderFrames = readExpGolomb(bitstream); maxDecFrameBuffering = readExpGolomb(bitstream); } } if (numReorderFrames === null) { assert(maxDecFrameBuffering === null); const constraintSet3Flag = constraintFlags & 16; if ((profileIdc === 44 || profileIdc === 86 || profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244) && constraintSet3Flag) { numReorderFrames = 0; maxDecFrameBuffering = 0; } else { const picWidthInMbs = picWidthInMbsMinus1 + 1; const picHeightInMapUnits = picHeightInMapUnitsMinus1 + 1; const frameHeightInMbs = (2 - frameMbsOnlyFlag) * picHeightInMapUnits; const levelInfo = AVC_LEVEL_TABLE.find((x) => x.level >= levelIdc) ?? last(AVC_LEVEL_TABLE); const maxDpbFrames = Math.min(Math.floor(levelInfo.maxDpbMbs / (picWidthInMbs * frameHeightInMbs)), 16); numReorderFrames = maxDpbFrames; maxDecFrameBuffering = maxDpbFrames; } } assert(maxDecFrameBuffering !== null); return { profileIdc, constraintFlags, levelIdc, frameMbsOnlyFlag, chromaFormatIdc, bitDepthLumaMinus8, bitDepthChromaMinus8, codedWidth, codedHeight, displayWidth, displayHeight, pixelAspectRatio, colourPrimaries, matrixCoefficients, transferCharacteristics, fullRangeFlag, numReorderFrames, maxDecFrameBuffering }; } catch (error) { Logging._error("Error parsing AVC SPS:", error); return null; } }; var skipAvcHrdParameters = (bitstream) => { const cpb_cnt_minus1 = readExpGolomb(bitstream); bitstream.skipBits(4); bitstream.skipBits(4); for (let i2 = 0; i2 <= cpb_cnt_minus1; i2++) { readExpGolomb(bitstream); readExpGolomb(bitstream); bitstream.skipBits(1); } bitstream.skipBits(5); bitstream.skipBits(5); bitstream.skipBits(5); bitstream.skipBits(5); }; var iterateHevcNalUnits = (packetData, decoderConfig) => { if (decoderConfig.description) { const bytes2 = toUint8Array(decoderConfig.description); const lengthSizeMinusOne = bytes2[21] & 3; const lengthSize = lengthSizeMinusOne + 1; return iterateNalUnitsInLengthPrefixed(packetData, lengthSize); } else { return iterateNalUnitsInAnnexB(packetData); } }; var extractNalUnitTypeForHevc = (byte) => { return byte >> 1 & 63; }; var parseHevcSps = (sps) => { try { const bitstream = new Bitstream(removeEmulationPreventionBytes(sps)); bitstream.skipBits(16); bitstream.readBits(4); const spsMaxSubLayersMinus1 = bitstream.readBits(3); const spsTemporalIdNestingFlag = bitstream.readBits(1); const { general_profile_space, general_tier_flag, general_profile_idc, general_profile_compatibility_flags, general_constraint_indicator_flags, general_level_idc } = parseProfileTierLevel(bitstream, spsMaxSubLayersMinus1); readExpGolomb(bitstream); const chromaFormatIdc = readExpGolomb(bitstream); let separateColourPlaneFlag = 0; if (chromaFormatIdc === 3) { separateColourPlaneFlag = bitstream.readBits(1); } const picWidthInLumaSamples = readExpGolomb(bitstream); const picHeightInLumaSamples = readExpGolomb(bitstream); let displayWidth = picWidthInLumaSamples; let displayHeight = picHeightInLumaSamples; if (bitstream.readBits(1)) { const confWinLeftOffset = readExpGolomb(bitstream); const confWinRightOffset = readExpGolomb(bitstream); const confWinTopOffset = readExpGolomb(bitstream); const confWinBottomOffset = readExpGolomb(bitstream); let subWidthC = 1; let subHeightC = 1; const chromaArrayType = separateColourPlaneFlag === 0 ? chromaFormatIdc : 0; if (chromaArrayType === 1) { subWidthC = 2; subHeightC = 2; } else if (chromaArrayType === 2) { subWidthC = 2; subHeightC = 1; } displayWidth -= (confWinLeftOffset + confWinRightOffset) * subWidthC; displayHeight -= (confWinTopOffset + confWinBottomOffset) * subHeightC; } const bitDepthLumaMinus8 = readExpGolomb(bitstream); const bitDepthChromaMinus8 = readExpGolomb(bitstream); readExpGolomb(bitstream); const spsSubLayerOrderingInfoPresentFlag = bitstream.readBits(1); const startI = spsSubLayerOrderingInfoPresentFlag ? 0 : spsMaxSubLayersMinus1; let spsMaxNumReorderPics = 0; for (let i2 = startI; i2 <= spsMaxSubLayersMinus1; i2++) { readExpGolomb(bitstream); spsMaxNumReorderPics = readExpGolomb(bitstream); readExpGolomb(bitstream); } readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); if (bitstream.readBits(1)) { if (bitstream.readBits(1)) { skipScalingListData(bitstream); } } bitstream.skipBits(1); bitstream.skipBits(1); if (bitstream.readBits(1)) { bitstream.skipBits(4); bitstream.skipBits(4); readExpGolomb(bitstream); readExpGolomb(bitstream); bitstream.skipBits(1); } const numShortTermRefPicSets = readExpGolomb(bitstream); skipAllStRefPicSets(bitstream, numShortTermRefPicSets); if (bitstream.readBits(1)) { const numLongTermRefPicsSps = readExpGolomb(bitstream); for (let i2 = 0; i2 < numLongTermRefPicsSps; i2++) { readExpGolomb(bitstream); bitstream.skipBits(1); } } bitstream.skipBits(1); bitstream.skipBits(1); let colourPrimaries = 2; let transferCharacteristics = 2; let matrixCoefficients = 2; let fullRangeFlag = 0; let minSpatialSegmentationIdc = 0; let pixelAspectRatio = { num: 1, den: 1 }; if (bitstream.readBits(1)) { const vui = parseHevcVui(bitstream, spsMaxSubLayersMinus1); pixelAspectRatio = vui.pixelAspectRatio; colourPrimaries = vui.colourPrimaries; transferCharacteristics = vui.transferCharacteristics; matrixCoefficients = vui.matrixCoefficients; fullRangeFlag = vui.fullRangeFlag; minSpatialSegmentationIdc = vui.minSpatialSegmentationIdc; } return { displayWidth, displayHeight, pixelAspectRatio, colourPrimaries, transferCharacteristics, matrixCoefficients, fullRangeFlag, maxDecFrameBuffering: spsMaxNumReorderPics + 1, spsMaxSubLayersMinus1, spsTemporalIdNestingFlag, generalProfileSpace: general_profile_space, generalTierFlag: general_tier_flag, generalProfileIdc: general_profile_idc, generalProfileCompatibilityFlags: general_profile_compatibility_flags, generalConstraintIndicatorFlags: general_constraint_indicator_flags, generalLevelIdc: general_level_idc, chromaFormatIdc, bitDepthLumaMinus8, bitDepthChromaMinus8, minSpatialSegmentationIdc }; } catch (error) { Logging._error("Error parsing HEVC SPS:", error); return null; } }; var extractHevcDecoderConfigurationRecord = (packetData) => { try { const vpsUnits = []; const spsUnits = []; const ppsUnits = []; const seiUnits = []; for (const loc of iterateNalUnitsInAnnexB(packetData)) { const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length); const type = extractNalUnitTypeForHevc(nalUnit[0]); if (type === HevcNalUnitType.VPS_NUT) { vpsUnits.push(nalUnit); } else if (type === HevcNalUnitType.SPS_NUT) { spsUnits.push(nalUnit); } else if (type === HevcNalUnitType.PPS_NUT) { ppsUnits.push(nalUnit); } else if (type === HevcNalUnitType.PREFIX_SEI_NUT || type === HevcNalUnitType.SUFFIX_SEI_NUT) { seiUnits.push(nalUnit); } } if (spsUnits.length === 0 || ppsUnits.length === 0) return null; const spsInfo = parseHevcSps(spsUnits[0]); if (!spsInfo) return null; let parallelismType = 0; if (ppsUnits.length > 0) { const pps = ppsUnits[0]; const ppsBitstream = new Bitstream(removeEmulationPreventionBytes(pps)); ppsBitstream.skipBits(16); readExpGolomb(ppsBitstream); readExpGolomb(ppsBitstream); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); ppsBitstream.skipBits(3); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); readExpGolomb(ppsBitstream); readExpGolomb(ppsBitstream); readSignedExpGolomb(ppsBitstream); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); if (ppsBitstream.readBits(1)) { readExpGolomb(ppsBitstream); } readSignedExpGolomb(ppsBitstream); readSignedExpGolomb(ppsBitstream); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); ppsBitstream.skipBits(1); const tiles_enabled_flag = ppsBitstream.readBits(1); const entropy_coding_sync_enabled_flag = ppsBitstream.readBits(1); if (!tiles_enabled_flag && !entropy_coding_sync_enabled_flag) parallelismType = 0; else if (tiles_enabled_flag && !entropy_coding_sync_enabled_flag) parallelismType = 2; else if (!tiles_enabled_flag && entropy_coding_sync_enabled_flag) parallelismType = 3; else parallelismType = 0; } const arrays = [ ...vpsUnits.length ? [ { arrayCompleteness: 1, nalUnitType: HevcNalUnitType.VPS_NUT, nalUnits: vpsUnits } ] : [], ...spsUnits.length ? [ { arrayCompleteness: 1, nalUnitType: HevcNalUnitType.SPS_NUT, nalUnits: spsUnits } ] : [], ...ppsUnits.length ? [ { arrayCompleteness: 1, nalUnitType: HevcNalUnitType.PPS_NUT, nalUnits: ppsUnits } ] : [], ...seiUnits.length ? [ { arrayCompleteness: 1, nalUnitType: extractNalUnitTypeForHevc(seiUnits[0][0]), nalUnits: seiUnits } ] : [] ]; const record = { configurationVersion: 1, generalProfileSpace: spsInfo.generalProfileSpace, generalTierFlag: spsInfo.generalTierFlag, generalProfileIdc: spsInfo.generalProfileIdc, generalProfileCompatibilityFlags: spsInfo.generalProfileCompatibilityFlags, generalConstraintIndicatorFlags: spsInfo.generalConstraintIndicatorFlags, generalLevelIdc: spsInfo.generalLevelIdc, minSpatialSegmentationIdc: spsInfo.minSpatialSegmentationIdc, parallelismType, chromaFormatIdc: spsInfo.chromaFormatIdc, bitDepthLumaMinus8: spsInfo.bitDepthLumaMinus8, bitDepthChromaMinus8: spsInfo.bitDepthChromaMinus8, avgFrameRate: 0, constantFrameRate: 0, numTemporalLayers: spsInfo.spsMaxSubLayersMinus1 + 1, temporalIdNested: spsInfo.spsTemporalIdNestingFlag, lengthSizeMinusOne: 3, arrays }; return record; } catch (error) { Logging._error("Error building HEVC Decoder Configuration Record:", error); return null; } }; var parseProfileTierLevel = (bitstream, maxNumSubLayersMinus1) => { const general_profile_space = bitstream.readBits(2); const general_tier_flag = bitstream.readBits(1); const general_profile_idc = bitstream.readBits(5); let general_profile_compatibility_flags = 0; for (let i2 = 0; i2 < 32; i2++) { general_profile_compatibility_flags = general_profile_compatibility_flags << 1 | bitstream.readBits(1); } const general_constraint_indicator_flags = new Uint8Array(6); for (let i2 = 0; i2 < 6; i2++) { general_constraint_indicator_flags[i2] = bitstream.readBits(8); } const general_level_idc = bitstream.readBits(8); const sub_layer_profile_present_flag = []; const sub_layer_level_present_flag = []; for (let i2 = 0; i2 < maxNumSubLayersMinus1; i2++) { sub_layer_profile_present_flag.push(bitstream.readBits(1)); sub_layer_level_present_flag.push(bitstream.readBits(1)); } if (maxNumSubLayersMinus1 > 0) { for (let i2 = maxNumSubLayersMinus1; i2 < 8; i2++) { bitstream.skipBits(2); } } for (let i2 = 0; i2 < maxNumSubLayersMinus1; i2++) { if (sub_layer_profile_present_flag[i2]) bitstream.skipBits(88); if (sub_layer_level_present_flag[i2]) bitstream.skipBits(8); } return { general_profile_space, general_tier_flag, general_profile_idc, general_profile_compatibility_flags, general_constraint_indicator_flags, general_level_idc }; }; var skipScalingListData = (bitstream) => { for (let sizeId = 0; sizeId < 4; sizeId++) { for (let matrixId = 0; matrixId < (sizeId === 3 ? 2 : 6); matrixId++) { const scaling_list_pred_mode_flag = bitstream.readBits(1); if (!scaling_list_pred_mode_flag) { readExpGolomb(bitstream); } else { const coefNum = Math.min(64, 1 << 4 + (sizeId << 1)); if (sizeId > 1) { readSignedExpGolomb(bitstream); } for (let i2 = 0; i2 < coefNum; i2++) { readSignedExpGolomb(bitstream); } } } } }; var skipAllStRefPicSets = (bitstream, num_short_term_ref_pic_sets) => { const NumDeltaPocs = []; for (let stRpsIdx = 0; stRpsIdx < num_short_term_ref_pic_sets; stRpsIdx++) { NumDeltaPocs[stRpsIdx] = skipStRefPicSet(bitstream, stRpsIdx, num_short_term_ref_pic_sets, NumDeltaPocs); } }; var skipStRefPicSet = (bitstream, stRpsIdx, num_short_term_ref_pic_sets, NumDeltaPocs) => { let NumDeltaPocsThis = 0; let inter_ref_pic_set_prediction_flag = 0; let RefRpsIdx = 0; if (stRpsIdx !== 0) { inter_ref_pic_set_prediction_flag = bitstream.readBits(1); } if (inter_ref_pic_set_prediction_flag) { if (stRpsIdx === num_short_term_ref_pic_sets) { const delta_idx_minus1 = readExpGolomb(bitstream); RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1); } else { RefRpsIdx = stRpsIdx - 1; } bitstream.readBits(1); readExpGolomb(bitstream); const numDelta = NumDeltaPocs[RefRpsIdx] ?? 0; for (let j = 0; j <= numDelta; j++) { const used_by_curr_pic_flag = bitstream.readBits(1); if (!used_by_curr_pic_flag) { bitstream.readBits(1); } } NumDeltaPocsThis = NumDeltaPocs[RefRpsIdx]; } else { const num_negative_pics = readExpGolomb(bitstream); const num_positive_pics = readExpGolomb(bitstream); for (let i2 = 0; i2 < num_negative_pics; i2++) { readExpGolomb(bitstream); bitstream.readBits(1); } for (let i2 = 0; i2 < num_positive_pics; i2++) { readExpGolomb(bitstream); bitstream.readBits(1); } NumDeltaPocsThis = num_negative_pics + num_positive_pics; } return NumDeltaPocsThis; }; var parseHevcVui = (bitstream, sps_max_sub_layers_minus1) => { let colourPrimaries = 2; let transferCharacteristics = 2; let matrixCoefficients = 2; let fullRangeFlag = 0; let minSpatialSegmentationIdc = 0; let pixelAspectRatio = { num: 1, den: 1 }; if (bitstream.readBits(1)) { const aspect_ratio_idc = bitstream.readBits(8); if (aspect_ratio_idc === 255) { pixelAspectRatio = { num: bitstream.readBits(16), den: bitstream.readBits(16) }; } else { const aspectRatio = AVC_HEVC_ASPECT_RATIO_IDC_TABLE[aspect_ratio_idc]; if (aspectRatio) { pixelAspectRatio = aspectRatio; } } } if (bitstream.readBits(1)) { bitstream.readBits(1); } if (bitstream.readBits(1)) { bitstream.readBits(3); fullRangeFlag = bitstream.readBits(1); if (bitstream.readBits(1)) { colourPrimaries = bitstream.readBits(8); transferCharacteristics = bitstream.readBits(8); matrixCoefficients = bitstream.readBits(8); } } if (bitstream.readBits(1)) { readExpGolomb(bitstream); readExpGolomb(bitstream); } bitstream.readBits(1); bitstream.readBits(1); bitstream.readBits(1); if (bitstream.readBits(1)) { readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); } if (bitstream.readBits(1)) { bitstream.readBits(32); bitstream.readBits(32); if (bitstream.readBits(1)) { readExpGolomb(bitstream); } if (bitstream.readBits(1)) { skipHevcHrdParameters(bitstream, true, sps_max_sub_layers_minus1); } } if (bitstream.readBits(1)) { bitstream.readBits(1); bitstream.readBits(1); bitstream.readBits(1); minSpatialSegmentationIdc = readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); readExpGolomb(bitstream); } return { pixelAspectRatio, colourPrimaries, transferCharacteristics, matrixCoefficients, fullRangeFlag, minSpatialSegmentationIdc }; }; var skipHevcHrdParameters = (bitstream, commonInfPresentFlag, maxNumSubLayersMinus1) => { let nal_hrd_parameters_present_flag = false; let vcl_hrd_parameters_present_flag = false; let sub_pic_hrd_params_present_flag = false; if (commonInfPresentFlag) { nal_hrd_parameters_present_flag = bitstream.readBits(1) === 1; vcl_hrd_parameters_present_flag = bitstream.readBits(1) === 1; if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) { sub_pic_hrd_params_present_flag = bitstream.readBits(1) === 1; if (sub_pic_hrd_params_present_flag) { bitstream.readBits(8); bitstream.readBits(5); bitstream.readBits(1); bitstream.readBits(5); } bitstream.readBits(4); bitstream.readBits(4); if (sub_pic_hrd_params_present_flag) { bitstream.readBits(4); } bitstream.readBits(5); bitstream.readBits(5); bitstream.readBits(5); } } for (let i2 = 0; i2 <= maxNumSubLayersMinus1; i2++) { const fixed_pic_rate_general_flag = bitstream.readBits(1) === 1; let fixed_pic_rate_within_cvs_flag = true; if (!fixed_pic_rate_general_flag) { fixed_pic_rate_within_cvs_flag = bitstream.readBits(1) === 1; } let low_delay_hrd_flag = false; if (fixed_pic_rate_within_cvs_flag) { readExpGolomb(bitstream); } else { low_delay_hrd_flag = bitstream.readBits(1) === 1; } let CpbCnt = 1; if (!low_delay_hrd_flag) { const cpb_cnt_minus1 = readExpGolomb(bitstream); CpbCnt = cpb_cnt_minus1 + 1; } if (nal_hrd_parameters_present_flag) { skipSubLayerHrdParameters(bitstream, CpbCnt, sub_pic_hrd_params_present_flag); } if (vcl_hrd_parameters_present_flag) { skipSubLayerHrdParameters(bitstream, CpbCnt, sub_pic_hrd_params_present_flag); } } }; var skipSubLayerHrdParameters = (bitstream, CpbCnt, sub_pic_hrd_params_present_flag) => { for (let i2 = 0; i2 < CpbCnt; i2++) { readExpGolomb(bitstream); readExpGolomb(bitstream); if (sub_pic_hrd_params_present_flag) { readExpGolomb(bitstream); readExpGolomb(bitstream); } bitstream.readBits(1); } }; var serializeHevcDecoderConfigurationRecord = (record) => { const bytes2 = []; bytes2.push(record.configurationVersion); bytes2.push((record.generalProfileSpace & 3) << 6 | (record.generalTierFlag & 1) << 5 | record.generalProfileIdc & 31); bytes2.push(record.generalProfileCompatibilityFlags >>> 24 & 255); bytes2.push(record.generalProfileCompatibilityFlags >>> 16 & 255); bytes2.push(record.generalProfileCompatibilityFlags >>> 8 & 255); bytes2.push(record.generalProfileCompatibilityFlags & 255); bytes2.push(...record.generalConstraintIndicatorFlags); bytes2.push(record.generalLevelIdc & 255); bytes2.push(240 | record.minSpatialSegmentationIdc >> 8 & 15); bytes2.push(record.minSpatialSegmentationIdc & 255); bytes2.push(252 | record.parallelismType & 3); bytes2.push(252 | record.chromaFormatIdc & 3); bytes2.push(248 | record.bitDepthLumaMinus8 & 7); bytes2.push(248 | record.bitDepthChromaMinus8 & 7); bytes2.push(record.avgFrameRate >> 8 & 255); bytes2.push(record.avgFrameRate & 255); bytes2.push((record.constantFrameRate & 3) << 6 | (record.numTemporalLayers & 7) << 3 | (record.temporalIdNested & 1) << 2 | record.lengthSizeMinusOne & 3); bytes2.push(record.arrays.length & 255); for (const arr of record.arrays) { bytes2.push((arr.arrayCompleteness & 1) << 7 | 0 << 6 | arr.nalUnitType & 63); bytes2.push(arr.nalUnits.length >> 8 & 255); bytes2.push(arr.nalUnits.length & 255); for (const nal of arr.nalUnits) { bytes2.push(nal.length >> 8 & 255); bytes2.push(nal.length & 255); for (let i2 = 0; i2 < nal.length; i2++) { bytes2.push(nal[i2]); } } } return new Uint8Array(bytes2); }; var HevcNaluOrderState; (function(HevcNaluOrderState2) { HevcNaluOrderState2[HevcNaluOrderState2["audAllowed"] = 0] = "audAllowed"; HevcNaluOrderState2[HevcNaluOrderState2["beforeFirstVcl"] = 1] = "beforeFirstVcl"; HevcNaluOrderState2[HevcNaluOrderState2["afterFirstVcl"] = 2] = "afterFirstVcl"; HevcNaluOrderState2[HevcNaluOrderState2["eoBitstreamAllowed"] = 3] = "eoBitstreamAllowed"; HevcNaluOrderState2[HevcNaluOrderState2["noMoreDataAllowed"] = 4] = "noMoreDataAllowed"; })(HevcNaluOrderState || (HevcNaluOrderState = {})); var iterateAv1PacketObus = function* (packet) { const bitstream = new Bitstream(packet); const readLeb128 = () => { let value = 0; for (let i2 = 0; i2 < 8; i2++) { const byte = bitstream.readAlignedByte(); value |= (byte & 127) << i2 * 7; if (!(byte & 128)) { break; } if (i2 === 7 && byte & 128) { return null; } } if (value >= 2 ** 32 - 1) { return null; } return value; }; while (bitstream.getBitsLeft() >= 8) { bitstream.skipBits(1); const obuType = bitstream.readBits(4); const obuExtension = bitstream.readBits(1); const obuHasSizeField = bitstream.readBits(1); bitstream.skipBits(1); if (obuExtension) { bitstream.skipBits(8); } let obuSize; if (obuHasSizeField) { const obuSizeValue = readLeb128(); if (obuSizeValue === null) return; obuSize = obuSizeValue; } else { obuSize = Math.floor(bitstream.getBitsLeft() / 8); } assert(bitstream.pos % 8 === 0); yield { type: obuType, data: packet.subarray(bitstream.pos / 8, bitstream.pos / 8 + obuSize) }; bitstream.skipBits(obuSize * 8); } }; var parseOpusIdentificationHeader = (bytes2) => { const view2 = toDataView(bytes2); const outputChannelCount = view2.getUint8(9); const preSkip = view2.getUint16(10, true); const inputSampleRate = view2.getUint32(12, true); const outputGain = view2.getInt16(16, true); const channelMappingFamily = view2.getUint8(18); let channelMappingTable = null; if (channelMappingFamily) { channelMappingTable = bytes2.subarray(19, 19 + 2 + outputChannelCount); } return { outputChannelCount, preSkip, inputSampleRate, outputGain, channelMappingFamily, channelMappingTable }; }; var determineVideoPacketType = (codec, decoderConfig, packetData) => { switch (codec) { case "avc": { for (const loc of iterateAvcNalUnits(packetData, decoderConfig)) { const nalTypeByte = packetData[loc.offset]; const type = extractNalUnitTypeForAvc(nalTypeByte); if (type >= AvcNalUnitType.NON_IDR_SLICE && type <= AvcNalUnitType.SLICE_DPC) { return "delta"; } if (type === AvcNalUnitType.IDR) { return "key"; } if (type === AvcNalUnitType.SEI && (!isChromium() || getChromiumVersion() >= 144)) { const nalUnit = packetData.subarray(loc.offset, loc.offset + loc.length); const bytes2 = removeEmulationPreventionBytes(nalUnit); let pos = 1; do { let payloadType = 0; while (true) { const nextByte = bytes2[pos++]; if (nextByte === void 0) break; payloadType += nextByte; if (nextByte < 255) { break; } } let payloadSize = 0; while (true) { const nextByte = bytes2[pos++]; if (nextByte === void 0) break; payloadSize += nextByte; if (nextByte < 255) { break; } } const PAYLOAD_TYPE_RECOVERY_POINT = 6; if (payloadType === PAYLOAD_TYPE_RECOVERY_POINT) { const bitstream = new Bitstream(bytes2); bitstream.pos = 8 * pos; const recoveryFrameCount = readExpGolomb(bitstream); const exactMatchFlag = bitstream.readBits(1); if (recoveryFrameCount === 0 && exactMatchFlag === 1) { return "key"; } } pos += payloadSize; } while (pos < bytes2.length - 1); } } return "delta"; } ; case "hevc": { for (const loc of iterateHevcNalUnits(packetData, decoderConfig)) { const type = extractNalUnitTypeForHevc(packetData[loc.offset]); if (type < HevcNalUnitType.BLA_W_LP) { return "delta"; } if (type <= HevcNalUnitType.RSV_IRAP_VCL23) { return "key"; } } return "delta"; } ; case "vp8": { const frameType = packetData[0] & 1; return frameType === 0 ? "key" : "delta"; } ; case "vp9": { const bitstream = new Bitstream(packetData); if (bitstream.readBits(2) !== 2) { return null; } ; const profileLowBit = bitstream.readBits(1); const profileHighBit = bitstream.readBits(1); const profile = (profileHighBit << 1) + profileLowBit; if (profile === 3) { bitstream.skipBits(1); } const showExistingFrame = bitstream.readBits(1); if (showExistingFrame) { return null; } const frameType = bitstream.readBits(1); return frameType === 0 ? "key" : "delta"; } ; case "av1": { let reducedStillPictureHeader = false; for (const { type, data } of iterateAv1PacketObus(packetData)) { if (type === 1) { const bitstream = new Bitstream(data); bitstream.skipBits(4); reducedStillPictureHeader = !!bitstream.readBits(1); } else if (type === 3 || type === 6 || type === 7) { if (reducedStillPictureHeader) { return "key"; } const bitstream = new Bitstream(data); const showExistingFrame = bitstream.readBits(1); if (showExistingFrame) { return null; } const frameType = bitstream.readBits(2); return frameType === 0 ? "key" : "delta"; } } return null; } ; default: { assertNever(codec); assert(false); } ; } }; var FlacBlockType; (function(FlacBlockType2) { FlacBlockType2[FlacBlockType2["STREAMINFO"] = 0] = "STREAMINFO"; FlacBlockType2[FlacBlockType2["VORBIS_COMMENT"] = 4] = "VORBIS_COMMENT"; FlacBlockType2[FlacBlockType2["PICTURE"] = 6] = "PICTURE"; })(FlacBlockType || (FlacBlockType = {})); var parseAc3SyncFrame = (data) => { if (data.length < 7) { return null; } if (data[0] !== 11 || data[1] !== 119) { return null; } const bitstream = new Bitstream(data); bitstream.skipBits(16); bitstream.skipBits(16); const fscod = bitstream.readBits(2); if (fscod === 3) { return null; } const frmsizecod = bitstream.readBits(6); const bsid = bitstream.readBits(5); if (bsid > 8) { return null; } const bsmod = bitstream.readBits(3); const acmod = bitstream.readBits(3); if ((acmod & 1) !== 0 && acmod !== 1) { bitstream.skipBits(2); } if ((acmod & 4) !== 0) { bitstream.skipBits(2); } if (acmod === 2) { bitstream.skipBits(2); } const lfeon = bitstream.readBits(1); const bitRateCode = Math.floor(frmsizecod / 2); return { fscod, bsid, bsmod, acmod, lfeon, bitRateCode }; }; var AC3_FRAME_SIZES = [ // frmsizecod, [48kHz, 44.1kHz, 32kHz] in bytes 64 * 2, 69 * 2, 96 * 2, 64 * 2, 70 * 2, 96 * 2, 80 * 2, 87 * 2, 120 * 2, 80 * 2, 88 * 2, 120 * 2, 96 * 2, 104 * 2, 144 * 2, 96 * 2, 105 * 2, 144 * 2, 112 * 2, 121 * 2, 168 * 2, 112 * 2, 122 * 2, 168 * 2, 128 * 2, 139 * 2, 192 * 2, 128 * 2, 140 * 2, 192 * 2, 160 * 2, 174 * 2, 240 * 2, 160 * 2, 175 * 2, 240 * 2, 192 * 2, 208 * 2, 288 * 2, 192 * 2, 209 * 2, 288 * 2, 224 * 2, 243 * 2, 336 * 2, 224 * 2, 244 * 2, 336 * 2, 256 * 2, 278 * 2, 384 * 2, 256 * 2, 279 * 2, 384 * 2, 320 * 2, 348 * 2, 480 * 2, 320 * 2, 349 * 2, 480 * 2, 384 * 2, 417 * 2, 576 * 2, 384 * 2, 418 * 2, 576 * 2, 448 * 2, 487 * 2, 672 * 2, 448 * 2, 488 * 2, 672 * 2, 512 * 2, 557 * 2, 768 * 2, 512 * 2, 558 * 2, 768 * 2, 640 * 2, 696 * 2, 960 * 2, 640 * 2, 697 * 2, 960 * 2, 768 * 2, 835 * 2, 1152 * 2, 768 * 2, 836 * 2, 1152 * 2, 896 * 2, 975 * 2, 1344 * 2, 896 * 2, 976 * 2, 1344 * 2, 1024 * 2, 1114 * 2, 1536 * 2, 1024 * 2, 1115 * 2, 1536 * 2, 1152 * 2, 1253 * 2, 1728 * 2, 1152 * 2, 1254 * 2, 1728 * 2, 1280 * 2, 1393 * 2, 1920 * 2, 1280 * 2, 1394 * 2, 1920 * 2 ]; var AC3_REGISTRATION_DESCRIPTOR = new Uint8Array([5, 4, 65, 67, 45, 51]); var EAC3_REGISTRATION_DESCRIPTOR = new Uint8Array([5, 4, 69, 65, 67, 51]); var EAC3_NUMBLKS_TABLE = [1, 2, 3, 6]; var parseEac3SyncFrame = (data) => { if (data.length < 6) { return null; } if (data[0] !== 11 || data[1] !== 119) { return null; } const bitstream = new Bitstream(data); bitstream.skipBits(16); const strmtyp = bitstream.readBits(2); bitstream.skipBits(3); if (strmtyp !== 0 && strmtyp !== 2) { return null; } const frmsiz = bitstream.readBits(11); const fscod = bitstream.readBits(2); let fscod2 = 0; let numblkscod; if (fscod === 3) { fscod2 = bitstream.readBits(2); numblkscod = 3; } else { numblkscod = bitstream.readBits(2); } const acmod = bitstream.readBits(3); const lfeon = bitstream.readBits(1); const bsid = bitstream.readBits(5); if (bsid < 11 || bsid > 16) { return null; } const numblks = EAC3_NUMBLKS_TABLE[numblkscod]; let fs; if (fscod < 3) { fs = AC3_SAMPLE_RATES[fscod] / 1e3; } else { fs = EAC3_REDUCED_SAMPLE_RATES[fscod2] / 1e3; } const dataRate = Math.round((frmsiz + 1) * fs / (numblks * 16)); const bsmod = 0; const numDepSub = 0; const chanLoc = 0; const substream = { fscod, fscod2, bsid, bsmod, acmod, lfeon, numDepSub, chanLoc }; return { dataRate, substreams: [substream] }; }; // node_modules/mediabunny/dist/modules/src/packet.js var PLACEHOLDER_DATA = /* @__PURE__ */ new Uint8Array(0); var EncodedPacket = class _EncodedPacket { /** Creates a new {@link EncodedPacket} from raw bytes and timing information. */ constructor(data, type, timestamp, duration, sequenceNumber = -1, byteLength, sideData) { this.data = data; this.type = type; this.timestamp = timestamp; this.duration = duration; this.sequenceNumber = sequenceNumber; if (data === PLACEHOLDER_DATA && byteLength === void 0) { throw new Error("Internal error: byteLength must be explicitly provided when constructing metadata-only packets."); } if (byteLength === void 0) { byteLength = data.byteLength; } if (!(data instanceof Uint8Array)) { throw new TypeError("data must be a Uint8Array."); } if (type !== "key" && type !== "delta") { throw new TypeError('type must be either "key" or "delta".'); } if (!Number.isFinite(timestamp)) { throw new TypeError("timestamp must be a number."); } if (!Number.isFinite(duration) || duration < 0) { throw new TypeError("duration must be a non-negative number."); } if (!Number.isFinite(sequenceNumber)) { throw new TypeError("sequenceNumber must be a number."); } if (!Number.isInteger(byteLength) || byteLength < 0) { throw new TypeError("byteLength must be a non-negative integer."); } if (sideData !== void 0 && (typeof sideData !== "object" || !sideData)) { throw new TypeError("sideData, when provided, must be an object."); } if (sideData?.alpha !== void 0 && !(sideData.alpha instanceof Uint8Array)) { throw new TypeError("sideData.alpha, when provided, must be a Uint8Array."); } if (sideData?.alphaByteLength !== void 0 && (!Number.isInteger(sideData.alphaByteLength) || sideData.alphaByteLength < 0)) { throw new TypeError("sideData.alphaByteLength, when provided, must be a non-negative integer."); } this.byteLength = byteLength; this.sideData = sideData ?? {}; if (this.sideData.alpha && this.sideData.alphaByteLength === void 0) { this.sideData.alphaByteLength = this.sideData.alpha.byteLength; } } /** * If this packet is a metadata-only packet. Metadata-only packets don't contain their packet data. They are the * result of retrieving packets with {@link PacketRetrievalOptions.metadataOnly} set to \`true\`. */ get isMetadataOnly() { return this.data === PLACEHOLDER_DATA; } /** The timestamp of this packet in microseconds. */ get microsecondTimestamp() { return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp); } /** The duration of this packet in microseconds. */ get microsecondDuration() { return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration); } /** Converts this packet to an * [\`EncodedVideoChunk\`](https://developer.mozilla.org/en-US/docs/Web/API/EncodedVideoChunk) for use with the * WebCodecs API. */ toEncodedVideoChunk() { if (this.isMetadataOnly) { throw new TypeError("Metadata-only packets cannot be converted to a video chunk."); } if (typeof EncodedVideoChunk === "undefined") { throw new Error("Your browser does not support EncodedVideoChunk."); } return new EncodedVideoChunk({ data: this.data, type: this.type, timestamp: this.microsecondTimestamp, duration: this.microsecondDuration }); } /** * Converts this packet to an * [\`EncodedVideoChunk\`](https://developer.mozilla.org/en-US/docs/Web/API/EncodedVideoChunk) for use with the * WebCodecs API, using the alpha side data instead of the color data. Throws if no alpha side data is defined. */ alphaToEncodedVideoChunk(type = this.type) { if (!this.sideData.alpha) { throw new TypeError("This packet does not contain alpha side data."); } if (this.isMetadataOnly) { throw new TypeError("Metadata-only packets cannot be converted to a video chunk."); } if (typeof EncodedVideoChunk === "undefined") { throw new Error("Your browser does not support EncodedVideoChunk."); } return new EncodedVideoChunk({ data: this.sideData.alpha, type, timestamp: this.microsecondTimestamp, duration: this.microsecondDuration }); } /** Converts this packet to an * [\`EncodedAudioChunk\`](https://developer.mozilla.org/en-US/docs/Web/API/EncodedAudioChunk) for use with the * WebCodecs API. */ toEncodedAudioChunk() { if (this.isMetadataOnly) { throw new TypeError("Metadata-only packets cannot be converted to an audio chunk."); } if (typeof EncodedAudioChunk === "undefined") { throw new Error("Your browser does not support EncodedAudioChunk."); } return new EncodedAudioChunk({ data: this.data, type: this.type, timestamp: this.microsecondTimestamp, duration: this.microsecondDuration }); } /** * Creates an {@link EncodedPacket} from an * [\`EncodedVideoChunk\`](https://developer.mozilla.org/en-US/docs/Web/API/EncodedVideoChunk) or * [\`EncodedAudioChunk\`](https://developer.mozilla.org/en-US/docs/Web/API/EncodedAudioChunk). This method is useful * for converting chunks from the WebCodecs API to \`EncodedPacket\` instances. */ static fromEncodedChunk(chunk, sideData) { if (!(chunk instanceof EncodedVideoChunk || chunk instanceof EncodedAudioChunk)) { throw new TypeError("chunk must be an EncodedVideoChunk or EncodedAudioChunk."); } const data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); return new _EncodedPacket(data, chunk.type, chunk.timestamp / 1e6, (chunk.duration ?? 0) / 1e6, void 0, void 0, sideData); } /** Clones this packet while optionally modifying the new packet's data. */ clone(options) { if (options !== void 0 && (typeof options !== "object" || options === null)) { throw new TypeError("options, when provided, must be an object."); } if (options?.data !== void 0 && !(options.data instanceof Uint8Array)) { throw new TypeError("options.data, when provided, must be a Uint8Array."); } if (options?.type !== void 0 && options.type !== "key" && options.type !== "delta") { throw new TypeError('options.type, when provided, must be either "key" or "delta".'); } if (options?.timestamp !== void 0 && !Number.isFinite(options.timestamp)) { throw new TypeError("options.timestamp, when provided, must be a number."); } if (options?.duration !== void 0 && !Number.isFinite(options.duration)) { throw new TypeError("options.duration, when provided, must be a number."); } if (options?.sequenceNumber !== void 0 && !Number.isFinite(options.sequenceNumber)) { throw new TypeError("options.sequenceNumber, when provided, must be a number."); } if (options?.sideData !== void 0 && (typeof options.sideData !== "object" || options.sideData === null)) { throw new TypeError("options.sideData, when provided, must be an object."); } return new _EncodedPacket(options?.data ?? this.data, options?.type ?? this.type, options?.timestamp ?? this.timestamp, options?.duration ?? this.duration, options?.sequenceNumber ?? this.sequenceNumber, this.byteLength, options?.sideData ?? this.sideData); } }; // node_modules/mediabunny/dist/modules/src/isobmff/isobmff-misc.js var buildIsobmffMimeType = (info) => { const base = info.hasVideo ? "video/" : info.hasAudio ? "audio/" : "application/"; let string = base + (info.isQuickTime ? "quicktime" : "mp4"); if (info.codecStrings.length > 0) { const uniqueCodecMimeTypes = [...new Set(info.codecStrings)]; string += \`; codecs="\${uniqueCodecMimeTypes.join(", ")}"\`; } return string; }; // node_modules/mediabunny/dist/modules/src/isobmff/isobmff-reader.js var MIN_BOX_HEADER_SIZE = 8; var MAX_BOX_HEADER_SIZE = 16; // node_modules/mediabunny/dist/modules/src/matroska/ebml.js var EBMLFloat32 = class { constructor(value) { this.value = value; } }; var EBMLFloat64 = class { constructor(value) { this.value = value; } }; var EBMLSignedInt = class { constructor(value) { this.value = value; } }; var EBMLUnicodeString = class { constructor(value) { this.value = value; } }; var EBMLId; (function(EBMLId2) { EBMLId2[EBMLId2["EBML"] = 440786851] = "EBML"; EBMLId2[EBMLId2["EBMLVersion"] = 17030] = "EBMLVersion"; EBMLId2[EBMLId2["EBMLReadVersion"] = 17143] = "EBMLReadVersion"; EBMLId2[EBMLId2["EBMLMaxIDLength"] = 17138] = "EBMLMaxIDLength"; EBMLId2[EBMLId2["EBMLMaxSizeLength"] = 17139] = "EBMLMaxSizeLength"; EBMLId2[EBMLId2["DocType"] = 17026] = "DocType"; EBMLId2[EBMLId2["DocTypeVersion"] = 17031] = "DocTypeVersion"; EBMLId2[EBMLId2["DocTypeReadVersion"] = 17029] = "DocTypeReadVersion"; EBMLId2[EBMLId2["Void"] = 236] = "Void"; EBMLId2[EBMLId2["Segment"] = 408125543] = "Segment"; EBMLId2[EBMLId2["SeekHead"] = 290298740] = "SeekHead"; EBMLId2[EBMLId2["Seek"] = 19899] = "Seek"; EBMLId2[EBMLId2["SeekID"] = 21419] = "SeekID"; EBMLId2[EBMLId2["SeekPosition"] = 21420] = "SeekPosition"; EBMLId2[EBMLId2["Duration"] = 17545] = "Duration"; EBMLId2[EBMLId2["Info"] = 357149030] = "Info"; EBMLId2[EBMLId2["TimestampScale"] = 2807729] = "TimestampScale"; EBMLId2[EBMLId2["MuxingApp"] = 19840] = "MuxingApp"; EBMLId2[EBMLId2["WritingApp"] = 22337] = "WritingApp"; EBMLId2[EBMLId2["Tracks"] = 374648427] = "Tracks"; EBMLId2[EBMLId2["TrackEntry"] = 174] = "TrackEntry"; EBMLId2[EBMLId2["TrackNumber"] = 215] = "TrackNumber"; EBMLId2[EBMLId2["TrackUID"] = 29637] = "TrackUID"; EBMLId2[EBMLId2["TrackType"] = 131] = "TrackType"; EBMLId2[EBMLId2["FlagEnabled"] = 185] = "FlagEnabled"; EBMLId2[EBMLId2["FlagDefault"] = 136] = "FlagDefault"; EBMLId2[EBMLId2["FlagForced"] = 21930] = "FlagForced"; EBMLId2[EBMLId2["FlagOriginal"] = 21934] = "FlagOriginal"; EBMLId2[EBMLId2["FlagHearingImpaired"] = 21931] = "FlagHearingImpaired"; EBMLId2[EBMLId2["FlagVisualImpaired"] = 21932] = "FlagVisualImpaired"; EBMLId2[EBMLId2["FlagCommentary"] = 21935] = "FlagCommentary"; EBMLId2[EBMLId2["FlagLacing"] = 156] = "FlagLacing"; EBMLId2[EBMLId2["Name"] = 21358] = "Name"; EBMLId2[EBMLId2["Language"] = 2274716] = "Language"; EBMLId2[EBMLId2["LanguageBCP47"] = 2274717] = "LanguageBCP47"; EBMLId2[EBMLId2["CodecID"] = 134] = "CodecID"; EBMLId2[EBMLId2["CodecPrivate"] = 25506] = "CodecPrivate"; EBMLId2[EBMLId2["CodecDelay"] = 22186] = "CodecDelay"; EBMLId2[EBMLId2["SeekPreRoll"] = 22203] = "SeekPreRoll"; EBMLId2[EBMLId2["DefaultDuration"] = 2352003] = "DefaultDuration"; EBMLId2[EBMLId2["Video"] = 224] = "Video"; EBMLId2[EBMLId2["PixelWidth"] = 176] = "PixelWidth"; EBMLId2[EBMLId2["PixelHeight"] = 186] = "PixelHeight"; EBMLId2[EBMLId2["DisplayWidth"] = 21680] = "DisplayWidth"; EBMLId2[EBMLId2["DisplayHeight"] = 21690] = "DisplayHeight"; EBMLId2[EBMLId2["DisplayUnit"] = 21682] = "DisplayUnit"; EBMLId2[EBMLId2["AlphaMode"] = 21440] = "AlphaMode"; EBMLId2[EBMLId2["Audio"] = 225] = "Audio"; EBMLId2[EBMLId2["SamplingFrequency"] = 181] = "SamplingFrequency"; EBMLId2[EBMLId2["Channels"] = 159] = "Channels"; EBMLId2[EBMLId2["BitDepth"] = 25188] = "BitDepth"; EBMLId2[EBMLId2["SimpleBlock"] = 163] = "SimpleBlock"; EBMLId2[EBMLId2["BlockGroup"] = 160] = "BlockGroup"; EBMLId2[EBMLId2["Block"] = 161] = "Block"; EBMLId2[EBMLId2["BlockAdditions"] = 30113] = "BlockAdditions"; EBMLId2[EBMLId2["BlockMore"] = 166] = "BlockMore"; EBMLId2[EBMLId2["BlockAdditional"] = 165] = "BlockAdditional"; EBMLId2[EBMLId2["BlockAddID"] = 238] = "BlockAddID"; EBMLId2[EBMLId2["BlockDuration"] = 155] = "BlockDuration"; EBMLId2[EBMLId2["ReferenceBlock"] = 251] = "ReferenceBlock"; EBMLId2[EBMLId2["Cluster"] = 524531317] = "Cluster"; EBMLId2[EBMLId2["Timestamp"] = 231] = "Timestamp"; EBMLId2[EBMLId2["Cues"] = 475249515] = "Cues"; EBMLId2[EBMLId2["CuePoint"] = 187] = "CuePoint"; EBMLId2[EBMLId2["CueTime"] = 179] = "CueTime"; EBMLId2[EBMLId2["CueTrackPositions"] = 183] = "CueTrackPositions"; EBMLId2[EBMLId2["CueTrack"] = 247] = "CueTrack"; EBMLId2[EBMLId2["CueClusterPosition"] = 241] = "CueClusterPosition"; EBMLId2[EBMLId2["Colour"] = 21936] = "Colour"; EBMLId2[EBMLId2["MatrixCoefficients"] = 21937] = "MatrixCoefficients"; EBMLId2[EBMLId2["TransferCharacteristics"] = 21946] = "TransferCharacteristics"; EBMLId2[EBMLId2["Primaries"] = 21947] = "Primaries"; EBMLId2[EBMLId2["Range"] = 21945] = "Range"; EBMLId2[EBMLId2["Projection"] = 30320] = "Projection"; EBMLId2[EBMLId2["ProjectionType"] = 30321] = "ProjectionType"; EBMLId2[EBMLId2["ProjectionPoseRoll"] = 30325] = "ProjectionPoseRoll"; EBMLId2[EBMLId2["Attachments"] = 423732329] = "Attachments"; EBMLId2[EBMLId2["AttachedFile"] = 24999] = "AttachedFile"; EBMLId2[EBMLId2["FileDescription"] = 18046] = "FileDescription"; EBMLId2[EBMLId2["FileName"] = 18030] = "FileName"; EBMLId2[EBMLId2["FileMediaType"] = 18016] = "FileMediaType"; EBMLId2[EBMLId2["FileData"] = 18012] = "FileData"; EBMLId2[EBMLId2["FileUID"] = 18094] = "FileUID"; EBMLId2[EBMLId2["Chapters"] = 272869232] = "Chapters"; EBMLId2[EBMLId2["Tags"] = 307544935] = "Tags"; EBMLId2[EBMLId2["Tag"] = 29555] = "Tag"; EBMLId2[EBMLId2["Targets"] = 25536] = "Targets"; EBMLId2[EBMLId2["TargetTypeValue"] = 26826] = "TargetTypeValue"; EBMLId2[EBMLId2["TargetType"] = 25546] = "TargetType"; EBMLId2[EBMLId2["TagTrackUID"] = 25541] = "TagTrackUID"; EBMLId2[EBMLId2["TagEditionUID"] = 25545] = "TagEditionUID"; EBMLId2[EBMLId2["TagChapterUID"] = 25540] = "TagChapterUID"; EBMLId2[EBMLId2["TagAttachmentUID"] = 25542] = "TagAttachmentUID"; EBMLId2[EBMLId2["SimpleTag"] = 26568] = "SimpleTag"; EBMLId2[EBMLId2["TagName"] = 17827] = "TagName"; EBMLId2[EBMLId2["TagLanguage"] = 17530] = "TagLanguage"; EBMLId2[EBMLId2["TagString"] = 17543] = "TagString"; EBMLId2[EBMLId2["TagBinary"] = 17541] = "TagBinary"; EBMLId2[EBMLId2["ContentEncodings"] = 28032] = "ContentEncodings"; EBMLId2[EBMLId2["ContentEncoding"] = 25152] = "ContentEncoding"; EBMLId2[EBMLId2["ContentEncodingOrder"] = 20529] = "ContentEncodingOrder"; EBMLId2[EBMLId2["ContentEncodingScope"] = 20530] = "ContentEncodingScope"; EBMLId2[EBMLId2["ContentCompression"] = 20532] = "ContentCompression"; EBMLId2[EBMLId2["ContentCompAlgo"] = 16980] = "ContentCompAlgo"; EBMLId2[EBMLId2["ContentCompSettings"] = 16981] = "ContentCompSettings"; EBMLId2[EBMLId2["ContentEncryption"] = 20533] = "ContentEncryption"; })(EBMLId || (EBMLId = {})); var LEVEL_0_EBML_IDS = [ EBMLId.EBML, EBMLId.Segment ]; var LEVEL_1_EBML_IDS = [ EBMLId.SeekHead, EBMLId.Info, EBMLId.Cluster, EBMLId.Tracks, EBMLId.Cues, EBMLId.Attachments, EBMLId.Chapters, EBMLId.Tags ]; var LEVEL_0_AND_1_EBML_IDS = [ ...LEVEL_0_EBML_IDS, ...LEVEL_1_EBML_IDS ]; var measureUnsignedInt = (value) => { if (value < 1 << 8) { return 1; } else if (value < 1 << 16) { return 2; } else if (value < 1 << 24) { return 3; } else if (value < 2 ** 32) { return 4; } else if (value < 2 ** 40) { return 5; } else { return 6; } }; var measureUnsignedBigInt = (value) => { if (value < 1n << 8n) { return 1; } else if (value < 1n << 16n) { return 2; } else if (value < 1n << 24n) { return 3; } else if (value < 1n << 32n) { return 4; } else if (value < 1n << 40n) { return 5; } else if (value < 1n << 48n) { return 6; } else if (value < 1n << 56n) { return 7; } else { return 8; } }; var measureSignedInt = (value) => { if (value >= -(1 << 6) && value < 1 << 6) { return 1; } else if (value >= -(1 << 13) && value < 1 << 13) { return 2; } else if (value >= -(1 << 20) && value < 1 << 20) { return 3; } else if (value >= -(1 << 27) && value < 1 << 27) { return 4; } else if (value >= -(2 ** 34) && value < 2 ** 34) { return 5; } else { return 6; } }; var measureVarInt = (value) => { if (value < (1 << 7) - 1) { return 1; } else if (value < (1 << 14) - 1) { return 2; } else if (value < (1 << 21) - 1) { return 3; } else if (value < (1 << 28) - 1) { return 4; } else if (value < 2 ** 35 - 1) { return 5; } else if (value < 2 ** 42 - 1) { return 6; } else { throw new Error("EBML varint size not supported " + value); } }; var EBMLWriter = class { constructor(writer) { this.writer = writer; this.helper = new Uint8Array(8); this.helperView = new DataView(this.helper.buffer); this.offsets = /* @__PURE__ */ new WeakMap(); this.dataOffsets = /* @__PURE__ */ new WeakMap(); } writeByte(value) { this.helperView.setUint8(0, value); this.writer.write(this.helper.subarray(0, 1)); } writeFloat32(value) { this.helperView.setFloat32(0, value, false); this.writer.write(this.helper.subarray(0, 4)); } writeFloat64(value) { this.helperView.setFloat64(0, value, false); this.writer.write(this.helper); } writeUnsignedInt(value, width = measureUnsignedInt(value)) { let pos = 0; switch (width) { case 6: this.helperView.setUint8(pos++, value / 2 ** 40 | 0); // eslint-disable-next-line no-fallthrough case 5: this.helperView.setUint8(pos++, value / 2 ** 32 | 0); // eslint-disable-next-line no-fallthrough case 4: this.helperView.setUint8(pos++, value >> 24); // eslint-disable-next-line no-fallthrough case 3: this.helperView.setUint8(pos++, value >> 16); // eslint-disable-next-line no-fallthrough case 2: this.helperView.setUint8(pos++, value >> 8); // eslint-disable-next-line no-fallthrough case 1: this.helperView.setUint8(pos++, value); break; default: throw new Error("Bad unsigned int size " + width); } this.writer.write(this.helper.subarray(0, pos)); } writeUnsignedBigInt(value, width = measureUnsignedBigInt(value)) { let pos = 0; for (let i2 = width - 1; i2 >= 0; i2--) { this.helperView.setUint8(pos++, Number(value >> BigInt(i2 * 8) & 0xffn)); } this.writer.write(this.helper.subarray(0, pos)); } writeSignedInt(value, width = measureSignedInt(value)) { if (value < 0) { value += 2 ** (width * 8); } this.writeUnsignedInt(value, width); } writeVarInt(value, width = measureVarInt(value)) { let pos = 0; switch (width) { case 1: this.helperView.setUint8(pos++, 1 << 7 | value); break; case 2: this.helperView.setUint8(pos++, 1 << 6 | value >> 8); this.helperView.setUint8(pos++, value); break; case 3: this.helperView.setUint8(pos++, 1 << 5 | value >> 16); this.helperView.setUint8(pos++, value >> 8); this.helperView.setUint8(pos++, value); break; case 4: this.helperView.setUint8(pos++, 1 << 4 | value >> 24); this.helperView.setUint8(pos++, value >> 16); this.helperView.setUint8(pos++, value >> 8); this.helperView.setUint8(pos++, value); break; case 5: this.helperView.setUint8(pos++, 1 << 3 | value / 2 ** 32 & 7); this.helperView.setUint8(pos++, value >> 24); this.helperView.setUint8(pos++, value >> 16); this.helperView.setUint8(pos++, value >> 8); this.helperView.setUint8(pos++, value); break; case 6: this.helperView.setUint8(pos++, 1 << 2 | value / 2 ** 40 & 3); this.helperView.setUint8(pos++, value / 2 ** 32 | 0); this.helperView.setUint8(pos++, value >> 24); this.helperView.setUint8(pos++, value >> 16); this.helperView.setUint8(pos++, value >> 8); this.helperView.setUint8(pos++, value); break; default: throw new Error("Bad EBML varint size " + width); } this.writer.write(this.helper.subarray(0, pos)); } writeAsciiString(str) { this.writer.write(new Uint8Array(str.split("").map((x) => x.charCodeAt(0)))); } writeEBML(data) { if (data === null) return; if (data instanceof Uint8Array) { this.writer.write(data); } else if (Array.isArray(data)) { for (const elem of data) { this.writeEBML(elem); } } else { this.offsets.set(data, this.writer.getPos()); this.writeUnsignedInt(data.id); if (Array.isArray(data.data)) { const sizePos = this.writer.getPos(); const sizeSize = data.size === -1 ? 1 : data.size ?? 4; if (data.size === -1) { this.writeByte(255); } else { this.writer.seek(this.writer.getPos() + sizeSize); } const startPos = this.writer.getPos(); this.dataOffsets.set(data, startPos); this.writeEBML(data.data); if (data.size !== -1) { const size = this.writer.getPos() - startPos; const endPos = this.writer.getPos(); this.writer.seek(sizePos); this.writeVarInt(size, sizeSize); this.writer.seek(endPos); } } else if (typeof data.data === "number") { const size = data.size ?? measureUnsignedInt(data.data); this.writeVarInt(size); this.writeUnsignedInt(data.data, size); } else if (typeof data.data === "bigint") { const size = data.size ?? measureUnsignedBigInt(data.data); this.writeVarInt(size); this.writeUnsignedBigInt(data.data, size); } else if (typeof data.data === "string") { this.writeVarInt(data.data.length); this.writeAsciiString(data.data); } else if (data.data instanceof Uint8Array) { this.writeVarInt(data.data.byteLength, data.size); this.writer.write(data.data); } else if (data.data instanceof EBMLFloat32) { this.writeVarInt(4); this.writeFloat32(data.data.value); } else if (data.data instanceof EBMLFloat64) { this.writeVarInt(8); this.writeFloat64(data.data.value); } else if (data.data instanceof EBMLSignedInt) { const size = data.size ?? measureSignedInt(data.data.value); this.writeVarInt(size); this.writeSignedInt(data.data.value, size); } else if (data.data instanceof EBMLUnicodeString) { const bytes2 = textEncoder.encode(data.data.value); this.writeVarInt(bytes2.length); this.writer.write(bytes2); } else { assertNever(data.data); } } } }; var MAX_VAR_INT_SIZE = 8; var MAX_HEADER_SIZE = 2 * MAX_VAR_INT_SIZE; var CODEC_STRING_MAP = { "avc": "V_MPEG4/ISO/AVC", "hevc": "V_MPEGH/ISO/HEVC", "vp8": "V_VP8", "vp9": "V_VP9", "av1": "V_AV1", "aac": "A_AAC", "mp3": "A_MPEG/L3", "opus": "A_OPUS", "vorbis": "A_VORBIS", "flac": "A_FLAC", "ac3": "A_AC3", "eac3": "A_EAC3", "pcm-u8": "A_PCM/INT/LIT", "pcm-s16": "A_PCM/INT/LIT", "pcm-s16be": "A_PCM/INT/BIG", "pcm-s24": "A_PCM/INT/LIT", "pcm-s24be": "A_PCM/INT/BIG", "pcm-s32": "A_PCM/INT/LIT", "pcm-s32be": "A_PCM/INT/BIG", "pcm-f32": "A_PCM/FLOAT/IEEE", "pcm-f64": "A_PCM/FLOAT/IEEE", "webvtt": "S_TEXT/WEBVTT" }; // node_modules/mediabunny/dist/modules/src/matroska/matroska-misc.js var buildMatroskaMimeType = (info) => { const base = info.hasVideo ? "video/" : info.hasAudio ? "audio/" : "application/"; let string = base + (info.isWebM ? "webm" : "x-matroska"); if (info.codecStrings.length > 0) { const uniqueCodecMimeTypes = [...new Set(info.codecStrings.filter(Boolean))]; string += \`; codecs="\${uniqueCodecMimeTypes.join(", ")}"\`; } return string; }; // node_modules/mediabunny/dist/modules/src/adts/adts-reader.js var MIN_ADTS_FRAME_HEADER_SIZE = 7; var MAX_ADTS_FRAME_HEADER_SIZE = 9; var readAdtsFrameHeader = (slice) => { const startPos = slice.filePos; const bytes2 = readBytes(slice, 9); const bitstream = new Bitstream(bytes2); const syncword = bitstream.readBits(12); if (syncword !== 4095) { return null; } bitstream.skipBits(1); const layer = bitstream.readBits(2); if (layer !== 0) { return null; } const protectionAbsence = bitstream.readBits(1); const objectType = bitstream.readBits(2) + 1; const samplingFrequencyIndex = bitstream.readBits(4); if (samplingFrequencyIndex === 15) { return null; } bitstream.skipBits(1); const channelConfiguration = bitstream.readBits(3); if (channelConfiguration === 0) { throw new Error("ADTS frames with channel configuration 0 are not supported."); } bitstream.skipBits(1); bitstream.skipBits(1); bitstream.skipBits(1); bitstream.skipBits(1); const frameLength = bitstream.readBits(13); bitstream.skipBits(11); const numberOfAacFrames = bitstream.readBits(2) + 1; if (numberOfAacFrames !== 1) { throw new Error("ADTS frames with more than one AAC frame are not supported."); } let crcCheck = null; if (protectionAbsence === 1) { slice.filePos -= 2; } else { crcCheck = bitstream.readBits(16); } return { objectType, samplingFrequencyIndex, channelConfiguration, frameLength, numberOfAacFrames, crcCheck, startPos }; }; // node_modules/mediabunny/dist/modules/src/sample.js var __addDisposableResource = function(env, value, async) { if (value !== null && value !== void 0) { if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected."); var dispose, inner; if (async) { if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined."); dispose = value[Symbol.asyncDispose]; } if (dispose === void 0) { if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined."); dispose = value[Symbol.dispose]; if (async) inner = dispose; } if (typeof dispose !== "function") throw new TypeError("Object not disposable."); if (inner) dispose = function() { try { inner.call(this); } catch (e2) { return Promise.reject(e2); } }; env.stack.push({ value, dispose, async }); } else if (async) { env.stack.push({ async: true }); } return value; }; var __disposeResources = /* @__PURE__ */ (function(SuppressedError2) { return function(env) { function fail(e2) { env.error = env.hasError ? new SuppressedError2(e2, env.error, "An error was suppressed during disposal.") : e2; env.hasError = true; } var r2, s2 = 0; function next() { while (r2 = env.stack.pop()) { try { if (!r2.async && s2 === 1) return s2 = 0, env.stack.push(r2), Promise.resolve().then(next); if (r2.dispose) { var result = r2.dispose.call(r2.value); if (r2.async) return s2 |= 2, Promise.resolve(result).then(next, function(e2) { fail(e2); return next(); }); } else s2 |= 1; } catch (e2) { fail(e2); } } if (s2 === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve(); if (env.hasError) throw env.error; } return next(); }; })(typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) { var e2 = new Error(message); return e2.name = "SuppressedError", e2.error = error, e2.suppressed = suppressed, e2; }); polyfillSymbolDispose(); var lastVideoGcErrorLog = -Infinity; var lastAudioGcErrorLog = -Infinity; var finalizationRegistry = null; if (typeof FinalizationRegistry !== "undefined") { finalizationRegistry = new FinalizationRegistry((value) => { const now = performance.now(); if (value.type === "video") { if (now - lastVideoGcErrorLog >= 1e3) { Logging._error(\`A VideoSample was garbage collected without first being closed. For proper resource management, make sure to call close() on all your VideoSamples as soon as you're done using them.\`); lastVideoGcErrorLog = now; } if (typeof VideoFrame !== "undefined" && value.data instanceof VideoFrame) { value.data.close(); } } else { if (now - lastAudioGcErrorLog >= 1e3) { Logging._error(\`An AudioSample was garbage collected without first being closed. For proper resource management, make sure to call close() on all your AudioSamples as soon as you're done using them.\`); lastAudioGcErrorLog = now; } if (typeof AudioData !== "undefined" && value.data instanceof AudioData) { value.data.close(); } } }); } var VideoSampleResource = class { constructor() { this._referenceCount = 0; this._lastAllocationBuffer = null; } }; var VIDEO_SAMPLE_PIXEL_FORMATS = [ // 4:2:0 Y, U, V "I420", "I420P10", "I420P12", // 4:2:0 Y, U, V, A "I420A", "I420AP10", "I420AP12", // 4:2:2 Y, U, V "I422", "I422P10", "I422P12", // 4:2:2 Y, U, V, A "I422A", "I422AP10", "I422AP12", // 4:4:4 Y, U, V "I444", "I444P10", "I444P12", // 4:4:4 Y, U, V, A "I444A", "I444AP10", "I444AP12", // 4:2:0 Y, UV "NV12", // 4:4:4 RGBA "RGBA", // 4:4:4 RGBX (opaque) "RGBX", // 4:4:4 BGRA "BGRA", // 4:4:4 BGRX (opaque) "BGRX" ]; var VIDEO_SAMPLE_PIXEL_FORMATS_SET = new Set(VIDEO_SAMPLE_PIXEL_FORMATS); var VideoSample = class _VideoSample { /** The width of the frame in pixels. */ get codedWidth() { return this.visibleRect.width; } /** The height of the frame in pixels. */ get codedHeight() { return this.visibleRect.height; } /** The display width of the frame in pixels, after aspect ratio adjustment and rotation. */ get displayWidth() { return this.rotation % 180 === 0 ? this.squarePixelWidth : this.squarePixelHeight; } /** The display height of the frame in pixels, after aspect ratio adjustment and rotation. */ get displayHeight() { return this.rotation % 180 === 0 ? this.squarePixelHeight : this.squarePixelWidth; } /** The presentation timestamp of the frame in microseconds. */ get microsecondTimestamp() { return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.timestamp); } /** The duration of the frame in microseconds. */ get microsecondDuration() { return Math.trunc(SECOND_TO_MICROSECOND_FACTOR * this.duration); } /** * Whether this sample uses a pixel format that can hold transparency data. Note that this doesn't necessarily mean * that the sample is transparent. */ get hasAlpha() { return this.format && this.format.includes("A"); } constructor(data, init) { this._closed = false; if (data instanceof ArrayBuffer || typeof SharedArrayBuffer !== "undefined" && data instanceof SharedArrayBuffer || ArrayBuffer.isView(data)) { if (!init || typeof init !== "object") { throw new TypeError("init must be an object."); } if (init.format === void 0 || !VIDEO_SAMPLE_PIXEL_FORMATS_SET.has(init.format)) { throw new TypeError("init.format must be one of: " + VIDEO_SAMPLE_PIXEL_FORMATS.join(", ")); } if (!Number.isInteger(init.codedWidth) || init.codedWidth <= 0) { throw new TypeError("init.codedWidth must be a positive integer."); } if (!Number.isInteger(init.codedHeight) || init.codedHeight <= 0) { throw new TypeError("init.codedHeight must be a positive integer."); } if (init.rotation !== void 0 && ![0, 90, 180, 270].includes(init.rotation)) { throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270."); } if (!Number.isFinite(init.timestamp)) { throw new TypeError("init.timestamp must be a number."); } if (init.duration !== void 0 && (!Number.isFinite(init.duration) || init.duration < 0)) { throw new TypeError("init.duration, when provided, must be a non-negative number."); } if (init.layout !== void 0) { if (!Array.isArray(init.layout)) { throw new TypeError("init.layout, when provided, must be an array."); } for (const plane of init.layout) { if (!plane || typeof plane !== "object" || Array.isArray(plane)) { throw new TypeError("Each entry in init.layout must be an object."); } if (!Number.isInteger(plane.offset) || plane.offset < 0) { throw new TypeError("plane.offset must be a non-negative integer."); } if (!Number.isInteger(plane.stride) || plane.stride < 0) { throw new TypeError("plane.stride must be a non-negative integer."); } } } if (init.visibleRect !== void 0) { validateRectangle(init.visibleRect, "init.visibleRect"); } if (init.displayWidth !== void 0 && (!Number.isInteger(init.displayWidth) || init.displayWidth <= 0)) { throw new TypeError("init.displayWidth, when provided, must be a positive integer."); } if (init.displayHeight !== void 0 && (!Number.isInteger(init.displayHeight) || init.displayHeight <= 0)) { throw new TypeError("init.displayHeight, when provided, must be a positive integer."); } if (init.displayWidth !== void 0 !== (init.displayHeight !== void 0)) { throw new TypeError("init.displayWidth and init.displayHeight must be either both provided or both omitted."); } this._data = init._doNotCopy ? toUint8Array(data) : toUint8Array(data).slice(); this._layout = init.layout ?? createDefaultPlaneLayout(init.format, init.codedWidth, init.codedHeight); this.format = init.format; this.rotation = init.rotation ?? 0; this.timestamp = init.timestamp; this.duration = init.duration ?? 0; let colorSpaceInit = init.colorSpace ?? null; if (colorSpaceInit === null) { if (this.format === "RGBA" || this.format === "RGBX" || this.format === "BGRA" || this.format === "BGRX") { colorSpaceInit = { primaries: "bt709", transfer: "iec61966-2-1", matrix: "rgb", fullRange: true }; } else { colorSpaceInit = { primaries: "bt709", transfer: "bt709", matrix: "bt709", fullRange: false }; } } this.colorSpace = new VideoSampleColorSpace(colorSpaceInit); this.visibleRect = { left: init.visibleRect?.left ?? 0, top: init.visibleRect?.top ?? 0, width: init.visibleRect?.width ?? init.codedWidth, height: init.visibleRect?.height ?? init.codedHeight }; if (init.displayWidth !== void 0) { this.squarePixelWidth = this.rotation % 180 === 0 ? init.displayWidth : init.displayHeight; this.squarePixelHeight = this.rotation % 180 === 0 ? init.displayHeight : init.displayWidth; } else { this.squarePixelWidth = this.visibleRect.width; this.squarePixelHeight = this.visibleRect.height; } } else if (typeof VideoFrame !== "undefined" && data instanceof VideoFrame) { if (init?.rotation !== void 0 && ![0, 90, 180, 270].includes(init.rotation)) { throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270."); } if (init?.timestamp !== void 0 && !Number.isFinite(init?.timestamp)) { throw new TypeError("init.timestamp, when provided, must be a number."); } if (init?.duration !== void 0 && (!Number.isFinite(init.duration) || init.duration < 0)) { throw new TypeError("init.duration, when provided, must be a non-negative number."); } if (init?.visibleRect !== void 0) { validateRectangle(init.visibleRect, "init.visibleRect"); } this._data = data; this._layout = null; this.format = data.format; this.visibleRect = { left: data.visibleRect?.x ?? 0, top: data.visibleRect?.y ?? 0, width: data.visibleRect?.width ?? data.codedWidth, height: data.visibleRect?.height ?? data.codedHeight }; this.rotation = init?.rotation ?? 0; this.squarePixelWidth = data.displayWidth; this.squarePixelHeight = data.displayHeight; this.timestamp = init?.timestamp ?? data.timestamp / 1e6; this.duration = init?.duration ?? (data.duration ?? 0) / 1e6; this.colorSpace = new VideoSampleColorSpace(data.colorSpace); } else if (typeof HTMLImageElement !== "undefined" && data instanceof HTMLImageElement || typeof SVGImageElement !== "undefined" && data instanceof SVGImageElement || typeof ImageBitmap !== "undefined" && data instanceof ImageBitmap || typeof HTMLVideoElement !== "undefined" && data instanceof HTMLVideoElement || typeof HTMLCanvasElement !== "undefined" && data instanceof HTMLCanvasElement || typeof OffscreenCanvas !== "undefined" && data instanceof OffscreenCanvas) { if (!init || typeof init !== "object") { throw new TypeError("init must be an object."); } if (init.rotation !== void 0 && ![0, 90, 180, 270].includes(init.rotation)) { throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270."); } if (!Number.isFinite(init.timestamp)) { throw new TypeError("init.timestamp must be a number."); } if (init.duration !== void 0 && (!Number.isFinite(init.duration) || init.duration < 0)) { throw new TypeError("init.duration, when provided, must be a non-negative number."); } if (typeof VideoFrame !== "undefined") { return new _VideoSample(new VideoFrame(data, { timestamp: Math.trunc(init.timestamp * SECOND_TO_MICROSECOND_FACTOR), // Drag 0 to undefined duration: Math.trunc((init.duration ?? 0) * SECOND_TO_MICROSECOND_FACTOR) || void 0 }), init); } let width = 0; let height = 0; if ("naturalWidth" in data) { width = data.naturalWidth; height = data.naturalHeight; } else if ("videoWidth" in data) { width = data.videoWidth; height = data.videoHeight; } else if ("width" in data) { width = Number(data.width); height = Number(data.height); } if (!width || !height) { throw new TypeError("Could not determine dimensions."); } const canvas = new OffscreenCanvas(width, height); const context = canvas.getContext("2d", { alpha: isFirefox(), // Firefox has VideoFrame glitches with opaque canvases willReadFrequently: true }); if (!context) { throw new Error("OffscreenCanvas must have support for the '2d' context in order to create a VideoSample from this data."); } context.drawImage(data, 0, 0); this._data = canvas; this._layout = null; this.format = "RGBX"; this.visibleRect = { left: 0, top: 0, width, height }; this.squarePixelWidth = width; this.squarePixelHeight = height; this.rotation = init.rotation ?? 0; this.timestamp = init.timestamp; this.duration = init.duration ?? 0; this.colorSpace = new VideoSampleColorSpace({ matrix: "rgb", primaries: "bt709", transfer: "iec61966-2-1", fullRange: true }); } else if (data instanceof VideoSampleResource) { if (!init || typeof init !== "object") { throw new TypeError("init must be an object."); } if (init.rotation !== void 0 && ![0, 90, 180, 270].includes(init.rotation)) { throw new TypeError("init.rotation, when provided, must be 0, 90, 180, or 270."); } if (!Number.isFinite(init.timestamp)) { throw new TypeError("init.timestamp must be a number."); } if (init.duration !== void 0 && (!Number.isFinite(init.duration) || init.duration < 0)) { throw new TypeError("init.duration, when provided, must be a non-negative number."); } this._data = data; data._referenceCount++; this.format = data.getFormat(); if (this.format !== null && !VIDEO_SAMPLE_PIXEL_FORMATS.includes(this.format)) { throw new TypeError("getFormat() must return a VideoSamplePixelFormat or null."); } this.visibleRect = { left: 0, top: 0, width: data.getCodedWidth(), height: data.getCodedHeight() }; if (!Number.isInteger(this.visibleRect.width) || this.visibleRect.width <= 0) { throw new TypeError("getCodedWidth() must return a positive integer."); } if (!Number.isInteger(this.visibleRect.height) || this.visibleRect.height <= 0) { throw new TypeError("getCodedHeight() must return a positive integer."); } this.squarePixelWidth = data.getSquarePixelWidth(); if (!Number.isInteger(this.squarePixelWidth) || this.squarePixelWidth <= 0) { throw new TypeError("getSquarePixelWidth() must return a positive integer."); } this.squarePixelHeight = data.getSquarePixelHeight(); if (!Number.isInteger(this.squarePixelHeight) || this.squarePixelHeight <= 0) { throw new TypeError("getSquarePixelHeight() must return a positive integer."); } this.rotation = init.rotation ?? 0; this.timestamp = init.timestamp; this.duration = init.duration ?? 0; this.colorSpace = data.getColorSpace(); } else { throw new TypeError("Invalid data type: Must be a BufferSource, CanvasImageSource, or VideoSampleResource."); } this.encodeOptions = init?.encodeOptions ?? {}; this.pixelAspectRatio = simplifyRational({ num: this.squarePixelWidth * this.codedHeight, den: this.squarePixelHeight * this.codedWidth }); finalizationRegistry?.register(this, { type: "video", data: this._data }, this); } /** Clones this video sample. */ clone() { if (this._closed) { throw new Error("VideoSample is closed."); } assert(this._data !== null); if (this._data instanceof VideoSampleResource) { return new _VideoSample(this._data, { timestamp: this.timestamp, duration: this.duration, rotation: this.rotation, encodeOptions: this.encodeOptions }); } else if (isVideoFrame(this._data)) { return new _VideoSample(this._data.clone(), { timestamp: this.timestamp, duration: this.duration, rotation: this.rotation, encodeOptions: this.encodeOptions }); } else if (this._data instanceof Uint8Array) { assert(this._layout); return new _VideoSample(this._data, { format: this.format, layout: this._layout, codedWidth: this.codedWidth, codedHeight: this.codedHeight, timestamp: this.timestamp, duration: this.duration, colorSpace: this.colorSpace, rotation: this.rotation, visibleRect: this.visibleRect, displayWidth: this.displayWidth, displayHeight: this.displayHeight, encodeOptions: this.encodeOptions, // It's already been copied, if we copy it again we make the clone unnecessarily expensive _doNotCopy: true }); } else { return new _VideoSample(this._data, { format: this.format, codedWidth: this.codedWidth, codedHeight: this.codedHeight, timestamp: this.timestamp, duration: this.duration, colorSpace: this.colorSpace, rotation: this.rotation, visibleRect: this.visibleRect, displayWidth: this.displayWidth, displayHeight: this.displayHeight, encodeOptions: this.encodeOptions }); } } /** * Closes this video sample, releasing held resources. Video samples should be closed as soon as they are not * needed anymore. */ close() { if (this._closed) { return; } finalizationRegistry?.unregister(this); if (this._data instanceof VideoSampleResource) { this._data._referenceCount--; if (this._data._referenceCount === 0) { this._data.close(); } } else if (isVideoFrame(this._data)) { this._data.close(); } else { this._data = null; } this._closed = true; } /** * Returns the number of bytes required to hold this video sample's pixel data. */ allocationSize(options = {}) { validateVideoFrameCopyToOptions(options); if (this._closed) { throw new Error("VideoSample is closed."); } if ((options.format ?? this.format) == null) { throw new Error("Cannot get allocation size when format is null."); } if (isVideoFrame(this._data)) { return this._data.allocationSize(options); } const combinedLayout = ParseVideoFrameCopyToOptions(this, options); return combinedLayout.allocationSize; } /** * Copies this video sample's pixel data to an ArrayBuffer or ArrayBufferView. * @returns The byte layout of the planes of the copied data. */ async copyTo(destination, options = {}) { if (!isAllowSharedBufferSource(destination)) { throw new TypeError("destination must be an ArrayBuffer or an ArrayBuffer view."); } validateVideoFrameCopyToOptions(options); if (this._closed) { throw new Error("VideoSample is closed."); } if ((options.format ?? this.format) == null) { throw new Error("Cannot copy video sample data when format is null."); } assert(this._data !== null); if (isVideoFrame(this._data)) { return this._data.copyTo(destination, options); } if (options.format && !["RGBA", "RGBX", "BGRA", "BGRX"].includes(this.format) && ["RGBA", "RGBX", "BGRA", "BGRX"].includes(options.format)) { if (this._data instanceof VideoSampleResource) { const env_1 = { stack: [], error: void 0, hasError: false }; try { const rgbSample = __addDisposableResource(env_1, await this._data.toRgbSample({ timestamp: this.timestamp, duration: this.duration, rotation: this.rotation }, options.colorSpace ?? "srgb"), false); if (!(rgbSample instanceof _VideoSample)) { throw new TypeError("toRgbSample() must return a VideoSample."); } if (!["RGBA", "RGBX", "BGRA", "BGRX"].includes(rgbSample.format)) { throw new Error(\`Sample returned by toRgbSample was expected to have an RGB format, got '\${rgbSample.format}' instead.\`); } return await rgbSample.copyTo(destination, options); } catch (e_1) { env_1.error = e_1; env_1.hasError = true; } finally { __disposeResources(env_1); } } else { if (typeof VideoFrame === "undefined") { throw new Error("For this sample, converting from a non-RGB to an RGB format requires VideoFrame to be defined."); } const tempFrame = this.toVideoFrame(); const result = await tempFrame.copyTo(destination, options); tempFrame.close(); return result; } } const combinedLayout = ParseVideoFrameCopyToOptions(this, options); assert(this.format); const destBytes = toUint8Array(destination); if (destBytes.byteLength < combinedLayout.allocationSize) { throw new TypeError(\`Destination buffer too small. Required: \${combinedLayout.allocationSize}, Available: \${destBytes.byteLength}\`); } const planeConfigs = getPlaneConfigs(this.format); let dataPlanes; if (this._data instanceof VideoSampleResource) { let result = this._data.getDataPlanes(); if (result instanceof Promise) result = await result; if (!Array.isArray(result) || result.some((x) => !(x.data instanceof Uint8Array) || !Number.isInteger(x.stride) || x.stride < 0)) { throw new TypeError('getDataPlanes() must return an array of objects with a Uint8Array "data" property and a non-negative integer "stride" property.'); } dataPlanes = result; } else if (this._data instanceof Uint8Array) { assert(this._layout); assert(this._layout.length === planeConfigs.length); dataPlanes = this._layout.map((planeLayout, i2) => { const height = Math.ceil(this.codedHeight / planeConfigs[i2].heightDivisor); return { data: this._data.subarray(planeLayout.offset, planeLayout.offset + planeLayout.stride * height), stride: planeLayout.stride }; }); } else { const canvas = this._data; const context = canvas.getContext("2d"); assert(context); const imageData = context.getImageData(0, 0, this.codedWidth, this.codedHeight); dataPlanes = [{ data: toUint8Array(imageData.data), stride: 4 * this.codedWidth }]; } const planeLayouts = []; const numPlanes = planeConfigs.length; for (let planeIndex = 0; planeIndex < numPlanes; planeIndex++) { const computedLayout = combinedLayout.computedLayouts[planeIndex]; const sourceStride = dataPlanes[planeIndex].stride; const sourceData = dataPlanes[planeIndex].data; let sourceOffset = computedLayout.sourceTop * sourceStride; sourceOffset += computedLayout.sourceLeftBytes; let destinationOffset = computedLayout.destinationOffset; const rowBytes = computedLayout.sourceWidthBytes; const layout = { offset: destinationOffset, stride: computedLayout.destinationStride }; for (let row = 0; row < computedLayout.sourceHeight; row++) { if (sourceOffset + rowBytes > sourceData.byteLength) { throw new Error(\`Source buffer OOB read.\`); } if (destinationOffset + rowBytes > destBytes.byteLength) { throw new Error(\`Destination buffer OOB write.\`); } const srcSub = sourceData.subarray(sourceOffset, sourceOffset + rowBytes); destBytes.set(srcSub, destinationOffset); sourceOffset += sourceStride; destinationOffset += computedLayout.destinationStride; } planeLayouts.push(layout); } if (options.format !== void 0) { const needsRgbConversion = this.format.startsWith("RGB") !== options.format.startsWith("RGB"); const needsAlphaConversion = this.format.includes("X") && options.format.includes("A"); if (needsRgbConversion || needsAlphaConversion) { for (let i2 = 0; i2 < combinedLayout.allocationSize; i2 += 4) { if (needsRgbConversion) { const r2 = destBytes[i2]; const b = destBytes[i2 + 2]; destBytes[i2] = b; destBytes[i2 + 2] = r2; } if (needsAlphaConversion) { destBytes[i2 + 3] = 255; } } } } return planeLayouts; } /** * Converts this video sample to a VideoFrame for use with the WebCodecs API. The VideoFrame returned by this * method *must* be closed separately from this video sample. */ toVideoFrame() { if (this._closed) { throw new Error("VideoSample is closed."); } assert(this._data !== null); if (this._data instanceof VideoSampleResource) { if (this.format === null) { throw new Error("Cannot convert a VideoSampleResource-backed VideoSample to VideoFrame if format is null."); } const planes = this._data.getDataPlanes(); if (planes instanceof Promise) { throw new Error("Cannot convert a VideoSampleResource-backed VideoSample to VideoFrame if getDataPlanes() returns a promise."); } const size = planes.reduce((a2, b) => a2 + b.data.byteLength, 0); const buffer = new Uint8Array(size); let offset = 0; const offsets = []; for (const plane of planes) { buffer.set(plane.data, offset); offsets.push(offset); offset += plane.data.byteLength; } return new VideoFrame(buffer, { format: this.format, layout: planes.map((x, i2) => ({ offset: offsets[i2], stride: x.stride })), codedWidth: this.codedWidth, codedHeight: this.codedHeight, timestamp: this.microsecondTimestamp, duration: this.microsecondDuration, colorSpace: this.colorSpace, displayWidth: this.squarePixelWidth, // Not display* since we're not passing rotation displayHeight: this.squarePixelHeight }); } else if (isVideoFrame(this._data)) { return new VideoFrame(this._data, { timestamp: this.microsecondTimestamp, duration: this.microsecondDuration || void 0 // Drag 0 duration to undefined, glitches some codecs }); } else if (this._data instanceof Uint8Array) { return new VideoFrame(this._data, { format: this.format, codedWidth: this.codedWidth, codedHeight: this.codedHeight, timestamp: this.microsecondTimestamp, duration: this.microsecondDuration || void 0, colorSpace: this.colorSpace, displayWidth: this.squarePixelWidth, // Not display* since we're not passing rotation displayHeight: this.squarePixelHeight }); } else { return new VideoFrame(this._data, { timestamp: this.microsecondTimestamp, duration: this.microsecondDuration || void 0 }); } } draw(context, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) { let sx = 0; let sy = 0; let sWidth = this.displayWidth; let sHeight = this.displayHeight; let dx = 0; let dy = 0; let dWidth = this.displayWidth; let dHeight = this.displayHeight; if (arg5 !== void 0) { sx = arg1; sy = arg2; sWidth = arg3; sHeight = arg4; dx = arg5; dy = arg6; if (arg7 !== void 0) { dWidth = arg7; dHeight = arg8; } else { dWidth = sWidth; dHeight = sHeight; } } else { dx = arg1; dy = arg2; if (arg3 !== void 0) { dWidth = arg3; dHeight = arg4; } } if (!(typeof CanvasRenderingContext2D !== "undefined" && context instanceof CanvasRenderingContext2D || typeof OffscreenCanvasRenderingContext2D !== "undefined" && context instanceof OffscreenCanvasRenderingContext2D)) { throw new TypeError("context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D."); } if (!Number.isFinite(sx)) { throw new TypeError("sx must be a number."); } if (!Number.isFinite(sy)) { throw new TypeError("sy must be a number."); } if (!Number.isFinite(sWidth) || sWidth < 0) { throw new TypeError("sWidth must be a non-negative number."); } if (!Number.isFinite(sHeight) || sHeight < 0) { throw new TypeError("sHeight must be a non-negative number."); } if (!Number.isFinite(dx)) { throw new TypeError("dx must be a number."); } if (!Number.isFinite(dy)) { throw new TypeError("dy must be a number."); } if (!Number.isFinite(dWidth) || dWidth < 0) { throw new TypeError("dWidth must be a non-negative number."); } if (!Number.isFinite(dHeight) || dHeight < 0) { throw new TypeError("dHeight must be a non-negative number."); } if (this._closed) { throw new Error("VideoSample is closed."); } ({ sx, sy, sWidth, sHeight } = this._rotateSourceRegion(sx, sy, sWidth, sHeight, this.rotation)); const source = this.toCanvasImageSource(); context.save(); const centerX = dx + dWidth / 2; const centerY = dy + dHeight / 2; context.translate(centerX, centerY); context.rotate(this.rotation * Math.PI / 180); const aspectRatioChange = this.rotation % 180 === 0 ? 1 : dWidth / dHeight; context.scale(1 / aspectRatioChange, aspectRatioChange); context.drawImage(source, sx, sy, sWidth, sHeight, -dWidth / 2, -dHeight / 2, dWidth, dHeight); context.restore(); } /** * Draws the sample in the middle of the canvas corresponding to the context with the specified fit behavior. */ drawWithFit(context, options) { if (!(typeof CanvasRenderingContext2D !== "undefined" && context instanceof CanvasRenderingContext2D || typeof OffscreenCanvasRenderingContext2D !== "undefined" && context instanceof OffscreenCanvasRenderingContext2D)) { throw new TypeError("context must be a CanvasRenderingContext2D or OffscreenCanvasRenderingContext2D."); } if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } if (!["fill", "contain", "cover"].includes(options.fit)) { throw new TypeError("options.fit must be 'fill', 'contain', or 'cover'."); } if (options.rotation !== void 0 && ![0, 90, 180, 270].includes(options.rotation)) { throw new TypeError("options.rotation, when provided, must be 0, 90, 180, or 270."); } if (options.crop !== void 0) { validateCropRectangle(options.crop, "options."); } const canvasWidth = context.canvas.width; const canvasHeight = context.canvas.height; const rotation = options.rotation ?? this.rotation; const [rotatedWidth, rotatedHeight] = rotation % 180 === 0 ? [this.squarePixelWidth, this.squarePixelHeight] : [this.squarePixelHeight, this.squarePixelWidth]; let finalCrop = options.crop; if (finalCrop) { finalCrop = clampCropRectangle(finalCrop, rotatedWidth, rotatedHeight); } let dx; let dy; let newWidth; let newHeight; const { sx, sy, sWidth, sHeight } = this._rotateSourceRegion(options.crop?.left ?? 0, options.crop?.top ?? 0, options.crop?.width ?? rotatedWidth, options.crop?.height ?? rotatedHeight, rotation); if (options.fit === "fill") { dx = 0; dy = 0; newWidth = canvasWidth; newHeight = canvasHeight; } else { const [sampleWidth, sampleHeight] = options.crop ? [options.crop.width, options.crop.height] : [rotatedWidth, rotatedHeight]; const scale = options.fit === "contain" ? Math.min(canvasWidth / sampleWidth, canvasHeight / sampleHeight) : Math.max(canvasWidth / sampleWidth, canvasHeight / sampleHeight); newWidth = sampleWidth * scale; newHeight = sampleHeight * scale; dx = (canvasWidth - newWidth) / 2; dy = (canvasHeight - newHeight) / 2; } context.save(); const aspectRatioChange = rotation % 180 === 0 ? 1 : newWidth / newHeight; context.translate(canvasWidth / 2, canvasHeight / 2); context.rotate(rotation * Math.PI / 180); context.scale(1 / aspectRatioChange, aspectRatioChange); context.translate(-canvasWidth / 2, -canvasHeight / 2); context.drawImage(this.toCanvasImageSource(), sx, sy, sWidth, sHeight, dx, dy, newWidth, newHeight); context.restore(); } /** @internal */ _rotateSourceRegion(sx, sy, sWidth, sHeight, rotation) { if (rotation === 90) { [sx, sy, sWidth, sHeight] = [ sy, this.squarePixelHeight - sx - sWidth, sHeight, sWidth ]; } else if (rotation === 180) { [sx, sy] = [ this.squarePixelWidth - sx - sWidth, this.squarePixelHeight - sy - sHeight ]; } else if (rotation === 270) { [sx, sy, sWidth, sHeight] = [ this.squarePixelWidth - sy - sHeight, sx, sHeight, sWidth ]; } return { sx, sy, sWidth, sHeight }; } /** * Converts this video sample to a * [\`CanvasImageSource\`](https://udn.realityripple.com/docs/Web/API/CanvasImageSource) for drawing to a canvas. * * You must use the value returned by this method immediately, as any VideoFrame created internally may * automatically be closed in the next microtask. */ toCanvasImageSource() { if (this._closed) { throw new Error("VideoSample is closed."); } assert(this._data !== null); if (this._data instanceof VideoSampleResource || this._data instanceof Uint8Array) { const videoFrame = this.toVideoFrame(); queueMicrotask(() => videoFrame.close()); return videoFrame; } else { return this._data; } } /** * Transform this video sample to a new video sample given the options. Can be used to resize, rotate, and crop * the sample. * * In non-browser environments, this method will not work by default. To make it work, register a custom * transformer function via {@link registerVideoSampleTransformer}. */ async transform(options) { if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } if (options.width !== void 0 && (!Number.isInteger(options.width) || options.width <= 0)) { throw new TypeError("options.width, when provided, must be a positive integer."); } if (options.height !== void 0 && (!Number.isInteger(options.height) || options.height <= 0)) { throw new TypeError("options.height, when provided, must be a positive integer."); } if (options.roundDimensionsTo !== void 0 && (!Number.isInteger(options.roundDimensionsTo) || options.roundDimensionsTo <= 0)) { throw new TypeError("options.roundDimensionsTo, when provided, must be a positive integer."); } if (options.fit !== void 0 && !["fill", "contain", "cover"].includes(options.fit)) { throw new TypeError('options.fit, when provided, must be one of "fill", "contain", or "cover".'); } if (options.width !== void 0 && options.height !== void 0 && options.fit === void 0) { throw new TypeError("When both options.width and options.height are provided, options.fit must also be provided."); } if (options.rotate !== void 0 && ![0, 90, 180, 270].includes(options.rotate)) { throw new TypeError("options.rotate, when provided, must be 0, 90, 180 or 270."); } if (options.crop !== void 0) { validateCropRectangle(options.crop, "options."); } if (options.alpha !== void 0 && !["keep", "discard"].includes(options.alpha)) { throw new TypeError("options.alpha, when provided, must be 'keep' or 'discard'."); } const rotation = normalizeRotation(this.rotation + (options.rotate ?? 0)); const [rotatedWidth, rotatedHeight] = rotation % 180 === 0 ? [this.squarePixelWidth, this.squarePixelHeight] : [this.squarePixelHeight, this.squarePixelWidth]; let finalCrop = options.crop; if (finalCrop) { finalCrop = clampCropRectangle(finalCrop, rotatedWidth, rotatedHeight); } const cropWidth = finalCrop ? finalCrop.width : rotatedWidth; const cropHeight = finalCrop ? finalCrop.height : rotatedHeight; const originalAspectRatio = cropWidth / cropHeight; let targetWidth; let targetHeight; if (options.width !== void 0 && options.height === void 0) { targetWidth = options.width; targetHeight = targetWidth / originalAspectRatio; } else if (options.width === void 0 && options.height !== void 0) { targetHeight = options.height; targetWidth = targetHeight * originalAspectRatio; } else if (options.width !== void 0 && options.height !== void 0) { targetWidth = options.width; targetHeight = options.height; } else { targetWidth = cropWidth; targetHeight = cropHeight; } targetWidth = roundToMultiple(targetWidth, options.roundDimensionsTo ?? 1); targetHeight = roundToMultiple(targetHeight, options.roundDimensionsTo ?? 1); const description = { width: targetWidth, height: targetHeight, fit: options.fit ?? "fill", rotation, crop: finalCrop ?? { left: 0, top: 0, width: rotatedWidth, height: rotatedHeight }, alpha: options.alpha ?? "keep" }; for (const transformer of registeredVideoSampleTransformers) { let result = transformer(this, description); if (result instanceof Promise) result = await result; if (result !== null) { return result; } } let canvas = null; let canvasIsNew = false; for (const entry of transformationCanvasCache) { if (entry.canvas.width === description.width && entry.canvas.height === description.height) { canvas = entry.canvas; entry.age = transformationCanvasCacheNextAge++; break; } } if (canvas === null) { if (typeof OffscreenCanvas !== "undefined") { canvas = new OffscreenCanvas(description.width, description.height); } else { if (typeof window === "undefined" || typeof document === "undefined") { throw new Error("Cannot transform VideoSamples in this environment. Either run in an environment with OffscreenCanvas or HTMLCanvasElement, or supply a custom VideoSample transformer using registerVideoSampleTransformer()."); } canvas = document.createElement("canvas"); canvas.width = description.width; canvas.height = description.height; } canvasIsNew = true; if (transformationCanvasCache.length >= TRANSFORMATION_CANVAS_CACHE_MAX_SIZE) { transformationCanvasCache.splice(arrayArgmin(transformationCanvasCache, (x) => x.age), 1); } transformationCanvasCache.push({ canvas, age: transformationCanvasCacheNextAge++ }); } const context = canvas.getContext("2d", { alpha: true }); if (!context) { throw new Error("The '2d' canvas context is required to transform VideoSamples. Register a custom transformer using registerVideoSampleTransformer to work around this limitation."); } if (description.alpha === "discard") { context.fillStyle = "black"; context.fillRect(0, 0, description.width, description.height); } else if (!canvasIsNew) { context.clearRect(0, 0, description.width, description.height); } this.drawWithFit(context, { fit: description.fit, rotation: description.rotation, crop: description.crop }); return new _VideoSample(canvas, { timestamp: this.timestamp, duration: this.duration, rotation: 0 // Any previous rotation is now baked in }); } /** Sets the rotation metadata of this video sample. */ setRotation(newRotation) { if (![0, 90, 180, 270].includes(newRotation)) { throw new TypeError("newRotation must be 0, 90, 180, or 270."); } this.rotation = newRotation; } /** Sets the presentation timestamp of this video sample, in seconds. */ setTimestamp(newTimestamp) { if (!Number.isFinite(newTimestamp)) { throw new TypeError("newTimestamp must be a number."); } this.timestamp = newTimestamp; } /** Sets the duration of this video sample, in seconds. */ setDuration(newDuration) { if (!Number.isFinite(newDuration) || newDuration < 0) { throw new TypeError("newDuration must be a non-negative number."); } this.duration = newDuration; } /** Sets the encode options used when this sample is passed to an encoder. */ setEncodeOptions(newEncodeOptions) { if (!newEncodeOptions || typeof newEncodeOptions !== "object") { throw new TypeError("newEncodeOptions must be an object."); } this.encodeOptions = newEncodeOptions; } /** Calls \`.close()\`. */ [Symbol.dispose]() { this.close(); } }; var registeredVideoSampleTransformers = []; var TRANSFORMATION_CANVAS_CACHE_MAX_SIZE = 3; var transformationCanvasCache = []; var transformationCanvasCacheNextAge = 0; var VideoSampleColorSpace = class { /** Creates a new VideoSampleColorSpace. */ constructor(init) { if (init !== void 0) { if (!init || typeof init !== "object") { throw new TypeError("init.colorSpace, when provided, must be an object."); } const primariesValues = Object.keys(COLOR_PRIMARIES_MAP); if (init.primaries != null && !primariesValues.includes(init.primaries)) { throw new TypeError(\`init.colorSpace.primaries, when provided, must be one of \${primariesValues.join(", ")}.\`); } const transferValues = Object.keys(TRANSFER_CHARACTERISTICS_MAP); if (init.transfer != null && !transferValues.includes(init.transfer)) { throw new TypeError(\`init.colorSpace.transfer, when provided, must be one of \${transferValues.join(", ")}.\`); } const matrixValues = Object.keys(MATRIX_COEFFICIENTS_MAP); if (init.matrix != null && !matrixValues.includes(init.matrix)) { throw new TypeError(\`init.colorSpace.matrix, when provided, must be one of \${matrixValues.join(", ")}.\`); } if (init.fullRange != null && typeof init.fullRange !== "boolean") { throw new TypeError("init.colorSpace.fullRange, when provided, must be a boolean."); } } this.primaries = init?.primaries ?? null; this.transfer = init?.transfer ?? null; this.matrix = init?.matrix ?? null; this.fullRange = init?.fullRange ?? null; } /** Serializes the color space to a JSON object. */ toJSON() { return { primaries: this.primaries, transfer: this.transfer, matrix: this.matrix, fullRange: this.fullRange }; } }; var isVideoFrame = (x) => { return typeof VideoFrame !== "undefined" && x instanceof VideoFrame; }; var clampCropRectangle = (crop, outerWidth, outerHeight) => { const left = Math.min(crop.left, outerWidth); const top = Math.min(crop.top, outerHeight); const width = Math.min(crop.width, outerWidth - left); const height = Math.min(crop.height, outerHeight - top); assert(width >= 0); assert(height >= 0); return { left, top, width, height }; }; var validateCropRectangle = (crop, prefix) => { if (!crop || typeof crop !== "object") { throw new TypeError(prefix + "crop, when provided, must be an object."); } if (!Number.isInteger(crop.left) || crop.left < 0) { throw new TypeError(prefix + "crop.left must be a non-negative integer."); } if (!Number.isInteger(crop.top) || crop.top < 0) { throw new TypeError(prefix + "crop.top must be a non-negative integer."); } if (!Number.isInteger(crop.width) || crop.width < 0) { throw new TypeError(prefix + "crop.width must be a non-negative integer."); } if (!Number.isInteger(crop.height) || crop.height < 0) { throw new TypeError(prefix + "crop.height must be a non-negative integer."); } }; var validateVideoFrameCopyToOptions = (options) => { if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } if (options.colorSpace !== void 0 && !["display-p3", "srgb"].includes(options.colorSpace)) { throw new TypeError("options.colorSpace, when provided, must be 'display-p3' or 'srgb'."); } if (options.format !== void 0 && typeof options.format !== "string") { throw new TypeError("options.format, when provided, must be a string."); } if (options.layout !== void 0) { if (!Array.isArray(options.layout)) { throw new TypeError("options.layout, when provided, must be an array."); } for (const plane of options.layout) { if (!plane || typeof plane !== "object") { throw new TypeError("Each entry in options.layout must be an object."); } if (!Number.isInteger(plane.offset) || plane.offset < 0) { throw new TypeError("plane.offset must be a non-negative integer."); } if (!Number.isInteger(plane.stride) || plane.stride < 0) { throw new TypeError("plane.stride must be a non-negative integer."); } } } if (options.rect !== void 0) { if (!options.rect || typeof options.rect !== "object") { throw new TypeError("options.rect, when provided, must be an object."); } if (options.rect.x !== void 0 && (!Number.isInteger(options.rect.x) || options.rect.x < 0)) { throw new TypeError("options.rect.x, when provided, must be a non-negative integer."); } if (options.rect.y !== void 0 && (!Number.isInteger(options.rect.y) || options.rect.y < 0)) { throw new TypeError("options.rect.y, when provided, must be a non-negative integer."); } if (options.rect.width !== void 0 && (!Number.isInteger(options.rect.width) || options.rect.width < 0)) { throw new TypeError("options.rect.width, when provided, must be a non-negative integer."); } if (options.rect.height !== void 0 && (!Number.isInteger(options.rect.height) || options.rect.height < 0)) { throw new TypeError("options.rect.height, when provided, must be a non-negative integer."); } } }; var createDefaultPlaneLayout = (format, codedWidth, codedHeight) => { const planes = getPlaneConfigs(format); const layouts = []; let currentOffset = 0; for (const plane of planes) { const planeWidth = Math.ceil(codedWidth / plane.widthDivisor); const planeHeight = Math.ceil(codedHeight / plane.heightDivisor); const stride = planeWidth * plane.sampleBytes; const planeSize = stride * planeHeight; layouts.push({ offset: currentOffset, stride }); currentOffset += planeSize; } return layouts; }; var getPlaneConfigs = (format) => { const yuv = (yBytes, uvBytes, subX, subY, hasAlpha) => { const configs = [ { sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 }, { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY }, { sampleBytes: uvBytes, widthDivisor: subX, heightDivisor: subY } ]; if (hasAlpha) { configs.push({ sampleBytes: yBytes, widthDivisor: 1, heightDivisor: 1 }); } return configs; }; switch (format) { case "I420": return yuv(1, 1, 2, 2, false); case "I420P10": case "I420P12": return yuv(2, 2, 2, 2, false); case "I420A": return yuv(1, 1, 2, 2, true); case "I420AP10": case "I420AP12": return yuv(2, 2, 2, 2, true); case "I422": return yuv(1, 1, 2, 1, false); case "I422P10": case "I422P12": return yuv(2, 2, 2, 1, false); case "I422A": return yuv(1, 1, 2, 1, true); case "I422AP10": case "I422AP12": return yuv(2, 2, 2, 1, true); case "I444": return yuv(1, 1, 1, 1, false); case "I444P10": case "I444P12": return yuv(2, 2, 1, 1, false); case "I444A": return yuv(1, 1, 1, 1, true); case "I444AP10": case "I444AP12": return yuv(2, 2, 1, 1, true); case "NV12": return [ { sampleBytes: 1, widthDivisor: 1, heightDivisor: 1 }, { sampleBytes: 2, widthDivisor: 2, heightDivisor: 2 } // Interleaved U and V ]; case "RGBA": case "RGBX": case "BGRA": case "BGRX": return [ { sampleBytes: 4, widthDivisor: 1, heightDivisor: 1 } ]; default: assertNever(format); assert(false); } }; var ParseVideoFrameCopyToOptions = (sample, options) => { const defaultRect = { left: 0, top: 0, width: sample.codedWidth, height: sample.codedHeight }; const overrideRect = options.rect; const parsedRect = ParseVisibleRect(defaultRect, overrideRect, sample.codedWidth, sample.codedHeight, sample.format); const optLayout = options.layout; let format; if (!options.format || options.format === sample.format) { format = sample.format; } else if (["RGBA", "RGBX", "BGRA", "BGRX"].includes(options.format)) { format = options.format; } else { throw new Error("NotSupportedError: Invalid destination format."); } return ComputeLayoutAndAllocationSize(parsedRect, format, optLayout); }; var ParseVisibleRect = (defaultRect, overrideRect, codedWidth, codedHeight, format) => { const sourceRect = { ...defaultRect }; if (overrideRect !== void 0) { if (overrideRect.width === 0 || overrideRect.height === 0) { throw new TypeError("visibleRect dimensions cannot be zero."); } if ((overrideRect.x || 0) + (overrideRect.width || 0) > codedWidth) { throw new TypeError("visibleRect exceeds codedWidth."); } if ((overrideRect.y || 0) + (overrideRect.height || 0) > codedHeight) { throw new TypeError("visibleRect exceeds codedHeight."); } sourceRect.x = overrideRect.x || 0; sourceRect.y = overrideRect.y || 0; sourceRect.width = overrideRect.width || 0; sourceRect.height = overrideRect.height || 0; } const validAlignment = VerifyRectOffsetAlignment(format, sourceRect); if (!validAlignment) { throw new TypeError("visibleRect alignment is invalid for the format."); } return sourceRect; }; var VerifyRectOffsetAlignment = (format, rect) => { if (format === null) return true; const planes = getPlaneConfigs(format); for (let planeIndex = 0; planeIndex < planes.length; planeIndex++) { const plane = planes[planeIndex]; const sampleWidth = plane.widthDivisor; const sampleHeight = plane.heightDivisor; if ((rect.x || 0) % sampleWidth !== 0) return false; if ((rect.y || 0) % sampleHeight !== 0) return false; } return true; }; var ComputeLayoutAndAllocationSize = (parsedRect, format, layout) => { const planes = getPlaneConfigs(format); const numPlanes = planes.length; if (layout !== void 0 && layout.length !== numPlanes) { throw new TypeError(\`Layout must have \${numPlanes} planes.\`); } let minAllocationSize = 0; const computedLayouts = []; const endOffsets = []; for (let planeIndex = 0; planeIndex < numPlanes; planeIndex++) { const plane = planes[planeIndex]; const sampleBytes = plane.sampleBytes; const sampleWidth = plane.widthDivisor; const sampleHeight = plane.heightDivisor; const computedLayout = { destinationOffset: 0, destinationStride: 0, sourceTop: 0, sourceHeight: 0, sourceLeftBytes: 0, sourceWidthBytes: 0 }; computedLayout.sourceTop = Math.ceil(Math.trunc(parsedRect.y || 0) / sampleHeight); computedLayout.sourceHeight = Math.ceil(Math.trunc(parsedRect.height || 0) / sampleHeight); computedLayout.sourceLeftBytes = Math.floor(Math.trunc(parsedRect.x || 0) / sampleWidth) * sampleBytes; computedLayout.sourceWidthBytes = Math.floor(Math.trunc(parsedRect.width || 0) / sampleWidth) * sampleBytes; if (layout !== void 0) { const planeLayout = layout[planeIndex]; if (planeLayout.stride < computedLayout.sourceWidthBytes) { throw new TypeError(\`Stride for plane \${planeIndex} is too small.\`); } computedLayout.destinationOffset = planeLayout.offset; computedLayout.destinationStride = planeLayout.stride; } else { computedLayout.destinationOffset = minAllocationSize; computedLayout.destinationStride = computedLayout.sourceWidthBytes; } const planeSize = computedLayout.destinationStride * computedLayout.sourceHeight; const planeEnd = planeSize + computedLayout.destinationOffset; if (planeEnd > 4294967295) { throw new TypeError("Allocation size exceeds limit."); } endOffsets.push(planeEnd); minAllocationSize = Math.max(minAllocationSize, planeEnd); for (let earlierPlaneIndex = 0; earlierPlaneIndex < planeIndex; earlierPlaneIndex++) { const earlierLayout = computedLayouts[earlierPlaneIndex]; if (endOffsets[planeIndex] <= earlierLayout.destinationOffset || endOffsets[earlierPlaneIndex] <= computedLayout.destinationOffset) { continue; } throw new TypeError("Planes overlap."); } computedLayouts.push(computedLayout); } return { allocationSize: minAllocationSize, computedLayouts }; }; // node_modules/mediabunny/dist/modules/src/encode.js var canEncodeVideoMemo = /* @__PURE__ */ new Map(); var validateVideoEncodingConfig = (config) => { if (!config || typeof config !== "object") { throw new TypeError("Encoding config must be an object."); } if (!VIDEO_CODECS.includes(config.codec)) { throw new TypeError(\`Invalid video codec '\${config.codec}'. Must be one of: \${VIDEO_CODECS.join(", ")}.\`); } if (!(config.bitrate instanceof Quality) && (!Number.isInteger(config.bitrate) || config.bitrate <= 0)) { throw new TypeError("config.bitrate must be a positive integer or a quality."); } if (config.keyFrameInterval !== void 0 && (!Number.isFinite(config.keyFrameInterval) || config.keyFrameInterval < 0)) { throw new TypeError("config.keyFrameInterval, when provided, must be a non-negative number."); } if (config.sizeChangeBehavior !== void 0 && !["deny", "passThrough", "fill", "contain", "cover"].includes(config.sizeChangeBehavior)) { throw new TypeError("config.sizeChangeBehavior, when provided, must be 'deny', 'passThrough', 'fill', 'contain' or 'cover'."); } if (config.transform !== void 0) { if (typeof config.transform !== "object" || !config.transform) { throw new TypeError("config.transform, when provided, must be an object."); } if (config.transform.width !== void 0 && (!Number.isInteger(config.transform.width) || config.transform.width <= 0)) { throw new TypeError("config.transform.width, when provided, must be a positive integer."); } if (config.transform.height !== void 0 && (!Number.isInteger(config.transform.height) || config.transform.height <= 0)) { throw new TypeError("config.transform.height, when provided, must be a positive integer."); } if (config.transform.fit !== void 0 && !["fill", "contain", "cover"].includes(config.transform.fit)) { throw new TypeError('config.transform.fit, when provided, must be one of "fill", "contain", or "cover".'); } if (config.transform.width !== void 0 && config.transform.height !== void 0 && config.transform.fit === void 0 && !["fill", "contain", "cover"].includes(config.sizeChangeBehavior)) { throw new TypeError("When both config.transform.width and config.transform.height are provided, config.transform.fit must also be provided."); } if (config.transform.fit !== void 0 && ["fill", "contain", "cover"].includes(config.sizeChangeBehavior) && config.transform.fit !== config.sizeChangeBehavior) { throw new TypeError("config.transform.fit, when provided, cannot differ from config.sizeChangeBehavior when config.sizeChangeBehavior is 'fill', 'contain' or 'cover', as sizeChangeBehavior already determines the fitting algorithm."); } if (config.transform.rotate !== void 0 && ![0, 90, 180, 270].includes(config.transform.rotate)) { throw new TypeError("config.transform.rotate, when provided, must be 0, 90, 180 or 270."); } if (config.transform.crop !== void 0) { validateCropRectangle(config.transform.crop, "config.transform."); } if (config.transform.process !== void 0 && typeof config.transform.process !== "function") { throw new TypeError("config.transform.process, when provided, must be a function."); } if (config.transform.frameRate !== void 0 && (!Number.isFinite(config.transform.frameRate) || config.transform.frameRate <= 0)) { throw new TypeError("config.transform.frameRate, when provided, must be a finite positive number."); } if (config.transform.force !== void 0 && typeof config.transform.force !== "boolean") { throw new TypeError("config.transform.force, when provided, must be a boolean."); } } if (config.onEncodedPacket !== void 0 && typeof config.onEncodedPacket !== "function") { throw new TypeError("config.onEncodedPacket, when provided, must be a function."); } if (config.onEncoderConfig !== void 0 && typeof config.onEncoderConfig !== "function") { throw new TypeError("config.onEncoderConfig, when provided, must be a function."); } if (config.onEncodedSample !== void 0 && typeof config.onEncodedSample !== "function") { throw new TypeError("config.onEncodedSample, when provided, must be a function."); } validateVideoEncodingAdditionalOptions(config.codec, config); }; var validateVideoEncodingAdditionalOptions = (codec, options) => { if (!options || typeof options !== "object") { throw new TypeError("Encoding options must be an object."); } if (options.alpha !== void 0 && !["discard", "keep"].includes(options.alpha)) { throw new TypeError("options.alpha, when provided, must be 'discard' or 'keep'."); } if (options.bitrateMode !== void 0 && !["constant", "variable"].includes(options.bitrateMode)) { throw new TypeError("bitrateMode, when provided, must be 'constant' or 'variable'."); } if (options.latencyMode !== void 0 && !["quality", "realtime"].includes(options.latencyMode)) { throw new TypeError("latencyMode, when provided, must be 'quality' or 'realtime'."); } if (options.fullCodecString !== void 0 && typeof options.fullCodecString !== "string") { throw new TypeError("fullCodecString, when provided, must be a string."); } if (options.fullCodecString !== void 0 && inferCodecFromCodecString(options.fullCodecString) !== codec) { throw new TypeError(\`fullCodecString, when provided, must be a string that matches the specified codec (\${codec}).\`); } if (options.hardwareAcceleration !== void 0 && !["no-preference", "prefer-hardware", "prefer-software"].includes(options.hardwareAcceleration)) { throw new TypeError("hardwareAcceleration, when provided, must be 'no-preference', 'prefer-hardware' or 'prefer-software'."); } if (options.scalabilityMode !== void 0 && typeof options.scalabilityMode !== "string") { throw new TypeError("scalabilityMode, when provided, must be a string."); } if (options.contentHint !== void 0 && typeof options.contentHint !== "string") { throw new TypeError("contentHint, when provided, must be a string."); } }; var buildVideoEncoderConfig = (options) => { const resolvedBitrate = options.bitrate instanceof Quality ? options.bitrate._toVideoBitrate(options.codec, options.width, options.height) : options.bitrate; return { codec: options.fullCodecString ?? buildVideoCodecString(options.codec, options.width, options.height, resolvedBitrate), width: options.width, height: options.height, displayWidth: options.squarePixelWidth, displayHeight: options.squarePixelHeight, bitrate: resolvedBitrate, bitrateMode: options.bitrateMode, alpha: options.alpha ?? "discard", framerate: options.framerate, latencyMode: options.latencyMode, hardwareAcceleration: options.hardwareAcceleration, scalabilityMode: options.scalabilityMode, contentHint: options.contentHint, ...getVideoEncoderConfigExtension(options.codec) }; }; 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 ac3: 384e3, // 384kbps base for AC-3 eac3: 192e3 // 192kbps base for E-AC-3 }; 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_HIGH = /* @__PURE__ */ new Quality(2); var canEncodeVideo = async (codec, options = {}) => { const { width = 1280, height = 720, bitrate = 1e6, ...restOptions } = options; if (!VIDEO_CODECS.includes(codec)) { return false; } if (!Number.isInteger(width) || width <= 0) { throw new TypeError("width must be a positive integer."); } if (!Number.isInteger(height) || height <= 0) { throw new TypeError("height must be a positive integer."); } if (!(bitrate instanceof Quality) && (!Number.isInteger(bitrate) || bitrate <= 0)) { throw new TypeError("bitrate must be a positive integer or a quality."); } validateVideoEncodingAdditionalOptions(codec, restOptions); const encoderConfig = buildVideoEncoderConfig({ codec, width, height, bitrate, framerate: void 0, ...restOptions, alpha: "discard" // Since we handle alpha ourselves }); const key = JSON.stringify(encoderConfig); const memoized = canEncodeVideoMemo.get(key); if (memoized) { return memoized; } const promise = (async () => { if (customVideoEncoders.some((x) => x.supports(codec, encoderConfig))) { return true; } if (typeof VideoEncoder === "undefined") { return false; } const hasOddDimension = width % 2 === 1 || height % 2 === 1; if (hasOddDimension && (codec === "avc" || codec === "hevc")) { return false; } const support = await VideoEncoder.isConfigSupported(encoderConfig); if (!support.supported) { return false; } if (isFirefox()) { return new Promise(async (resolve) => { try { const encoder = new VideoEncoder({ output: () => { }, error: () => resolve(false) }); encoder.configure(encoderConfig); const frameData = new Uint8Array(width * height * 4); const frame = new VideoFrame(frameData, { format: "RGBA", codedWidth: width, codedHeight: height, timestamp: 0 }); encoder.encode(frame); frame.close(); await encoder.flush(); resolve(true); } catch { resolve(false); } }); } return true; })(); canEncodeVideoMemo.set(key, promise); return promise; }; // node_modules/mediabunny/dist/modules/src/custom-coder.js var customVideoEncoders = []; // node_modules/mediabunny/dist/modules/src/reader.js var FileSlice = class _FileSlice { constructor(bytes2, view2, offset, start, end) { this.bytes = bytes2; this.view = view2; this.offset = offset; this.start = start; this.end = end; this.bufferPos = start - offset; } static tempFromBytes(bytes2) { return new _FileSlice(bytes2, toDataView(bytes2), 0, 0, bytes2.length); } get length() { return this.end - this.start; } get filePos() { return this.offset + this.bufferPos; } set filePos(value) { this.bufferPos = value - this.offset; } /** The number of bytes left from the current pos to the end of the slice. */ get remainingLength() { return Math.max(this.end - this.filePos, 0); } skip(byteCount) { this.bufferPos += byteCount; } /** Creates a new subslice of this slice whose byte range must be contained within this slice. */ slice(filePos, length = this.end - filePos) { if (filePos < this.start || filePos + length > this.end) { throw new RangeError("Slicing outside of original slice."); } return new _FileSlice(this.bytes, this.view, this.offset, filePos, filePos + length); } }; var checkIsInRange = (slice, bytesToRead) => { if (slice.filePos < slice.start || slice.filePos + bytesToRead > slice.end) { throw new RangeError(\`Tried reading [\${slice.filePos}, \${slice.filePos + bytesToRead}), but slice is [\${slice.start}, \${slice.end}). This is likely an internal error, please report it alongside the file that caused it.\`); } }; var readBytes = (slice, length) => { checkIsInRange(slice, length); const bytes2 = slice.bytes.subarray(slice.bufferPos, slice.bufferPos + length); slice.bufferPos += length; return bytes2; }; // node_modules/mediabunny/dist/modules/src/muxer.js var Muxer = class { constructor(output) { this.mutex = new AsyncMutex(); this.trackTimestampInfo = /* @__PURE__ */ new WeakMap(); this.output = output; } // eslint-disable-next-line @typescript-eslint/no-unused-vars onTrackClose(track) { } validateTimestamp(track, timestampInSeconds, isKeyPacket) { if (timestampInSeconds < 0) { throw new Error(\`Timestamps must be non-negative (got \${timestampInSeconds}s).\`); } let timestampInfo = this.trackTimestampInfo.get(track); if (!timestampInfo) { if (!isKeyPacket) { throw new Error("First packet must be a key packet."); } timestampInfo = { maxTimestamp: timestampInSeconds, maxTimestampBeforeLastKeyPacket: null }; this.trackTimestampInfo.set(track, timestampInfo); } else { if (isKeyPacket) { timestampInfo.maxTimestampBeforeLastKeyPacket = timestampInfo.maxTimestamp; } if (timestampInfo.maxTimestampBeforeLastKeyPacket !== null && timestampInSeconds < timestampInfo.maxTimestampBeforeLastKeyPacket) { throw new Error(\`Timestamps cannot be smaller than the largest timestamp of the previous GOP (a GOP begins with a key packet and ends right before the next key packet). Got \${timestampInSeconds}s, but largest timestamp is \${timestampInfo.maxTimestampBeforeLastKeyPacket}s.\`); } timestampInfo.maxTimestamp = Math.max(timestampInfo.maxTimestamp, timestampInSeconds); } } }; // node_modules/mediabunny/dist/modules/src/subtitles.js var inlineTimestampRegex = /<(?:(\\d{2}):)?(\\d{2}):(\\d{2}).(\\d{3})>/g; var timestampRegex = /(?:(\\d{2}):)?(\\d{2}):(\\d{2}).(\\d{3})/; var parseSubtitleTimestamp = (string) => { const match = timestampRegex.exec(string); if (!match) throw new Error("Expected match."); return 60 * 60 * 1e3 * Number(match[1] || "0") + 60 * 1e3 * Number(match[2]) + 1e3 * Number(match[3]) + Number(match[4]); }; var formatSubtitleTimestamp = (timestamp) => { const hours = Math.floor(timestamp / (60 * 60 * 1e3)); const minutes = Math.floor(timestamp % (60 * 60 * 1e3) / (60 * 1e3)); const seconds = Math.floor(timestamp % (60 * 1e3) / 1e3); const milliseconds = timestamp % 1e3; return hours.toString().padStart(2, "0") + ":" + minutes.toString().padStart(2, "0") + ":" + seconds.toString().padStart(2, "0") + "." + milliseconds.toString().padStart(3, "0"); }; // node_modules/mediabunny/dist/modules/src/isobmff/isobmff-boxes.js var IsobmffBoxWriter = class { constructor(writer) { this.writer = writer; this.helper = new Uint8Array(8); this.helperView = new DataView(this.helper.buffer); this.offsets = /* @__PURE__ */ new WeakMap(); } writeU32(value) { this.helperView.setUint32(0, value, false); this.writer.write(this.helper.subarray(0, 4)); } writeU64(value) { this.helperView.setUint32(0, Math.floor(value / 2 ** 32), false); this.helperView.setUint32(4, value, false); this.writer.write(this.helper.subarray(0, 8)); } writeAscii(text) { for (let i2 = 0; i2 < text.length; i2++) { this.helperView.setUint8(i2 % 8, text.charCodeAt(i2)); if (i2 % 8 === 7) this.writer.write(this.helper); } if (text.length % 8 !== 0) { this.writer.write(this.helper.subarray(0, text.length % 8)); } } writeBox(box2) { this.offsets.set(box2, this.writer.getPos()); if (box2.contents && !box2.children) { this.writeBoxHeader(box2, box2.size ?? box2.contents.byteLength + 8); this.writer.write(box2.contents); } else { const startPos = this.writer.getPos(); this.writeBoxHeader(box2, 0); if (box2.contents) this.writer.write(box2.contents); if (box2.children) { for (const child of box2.children) if (child) this.writeBox(child); } const endPos = this.writer.getPos(); const size = box2.size ?? endPos - startPos; this.writer.seek(startPos); this.writeBoxHeader(box2, size); this.writer.seek(endPos); } } writeBoxHeader(box2, size) { this.writeU32(box2.largeSize ? 1 : size); this.writeAscii(box2.type); if (box2.largeSize) this.writeU64(size); } measureBoxHeader(box2) { return 8 + (box2.largeSize ? 8 : 0); } patchBox(box2) { const boxOffset = this.offsets.get(box2); assert(boxOffset !== void 0); const endPos = this.writer.getPos(); this.writer.seek(boxOffset); this.writeBox(box2); this.writer.seek(endPos); } measureBox(box2) { if (box2.contents && !box2.children) { const headerSize = this.measureBoxHeader(box2); return headerSize + box2.contents.byteLength; } else { let result = this.measureBoxHeader(box2); if (box2.contents) result += box2.contents.byteLength; if (box2.children) { for (const child of box2.children) if (child) result += this.measureBox(child); } return result; } } }; var bytes = /* @__PURE__ */ new Uint8Array(8); var view = /* @__PURE__ */ new DataView(bytes.buffer); var u8 = (value) => { return [(value % 256 + 256) % 256]; }; var u16 = (value) => { view.setUint16(0, value, false); return [bytes[0], bytes[1]]; }; var i16 = (value) => { view.setInt16(0, value, false); return [bytes[0], bytes[1]]; }; var u24 = (value) => { view.setUint32(0, value, false); return [bytes[1], bytes[2], bytes[3]]; }; var u32 = (value) => { view.setUint32(0, value, false); return [bytes[0], bytes[1], bytes[2], bytes[3]]; }; var i32 = (value) => { view.setInt32(0, value, false); return [bytes[0], bytes[1], bytes[2], bytes[3]]; }; var u64 = (value) => { view.setUint32(0, Math.floor(value / 2 ** 32), false); view.setUint32(4, value, false); return [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]; }; var i64 = (value) => { view.setInt32(0, Math.floor(value / 2 ** 32), false); view.setUint32(4, value, false); return [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]]; }; var fixed_8_8 = (value) => { view.setInt16(0, 2 ** 8 * value, false); return [bytes[0], bytes[1]]; }; var fixed_16_16 = (value) => { view.setInt32(0, 2 ** 16 * value, false); return [bytes[0], bytes[1], bytes[2], bytes[3]]; }; var fixed_2_30 = (value) => { view.setInt32(0, 2 ** 30 * value, false); return [bytes[0], bytes[1], bytes[2], bytes[3]]; }; var variableUnsignedInt = (value, byteLength) => { const bytes2 = []; let remaining = value; do { let byte = remaining & 127; remaining >>= 7; if (bytes2.length > 0) { byte |= 128; } bytes2.push(byte); if (byteLength !== void 0) { byteLength--; } } while (remaining > 0 || byteLength); return bytes2.reverse(); }; var ascii = (text, nullTerminated = false) => { const bytes2 = Array(text.length).fill(null).map((_2, i2) => text.charCodeAt(i2)); if (nullTerminated) bytes2.push(0); return bytes2; }; var rotationMatrix = (rotationInDegrees) => { const theta = rotationInDegrees * (Math.PI / 180); const cosTheta = Math.round(Math.cos(theta)); const sinTheta = Math.round(Math.sin(theta)); return [ cosTheta, sinTheta, 0, -sinTheta, cosTheta, 0, 0, 0, 1 ]; }; var IDENTITY_MATRIX = /* @__PURE__ */ rotationMatrix(0); var matrixToBytes = (matrix) => { return [ fixed_16_16(matrix[0]), fixed_16_16(matrix[1]), fixed_2_30(matrix[2]), fixed_16_16(matrix[3]), fixed_16_16(matrix[4]), fixed_2_30(matrix[5]), fixed_16_16(matrix[6]), fixed_16_16(matrix[7]), fixed_2_30(matrix[8]) ]; }; var box = (type, contents, children) => ({ type, contents: contents && new Uint8Array(contents.flat(10)), children }); var fullBox = (type, version, flags, contents, children) => box(type, [u8(version), u24(flags), contents ?? []], children); var ftyp = (details) => { const minorVersion = 512; if (details.isQuickTime) { return box("ftyp", [ ascii("qt "), // Major brand u32(minorVersion), // Minor version // Compatible brands ascii("qt ") ]); } if (details.fragmented) { if (details.cmaf) { return box("ftyp", [ ascii("iso5"), // Major brand u32(minorVersion), // Minor version // Compatible brands ascii("iso5"), ascii("iso6"), ascii("mp41"), ascii("cmfc"), ascii("dash") ]); } else { return box("ftyp", [ ascii("iso5"), // Major brand u32(minorVersion), // Minor version // Compatible brands ascii("iso5"), ascii("iso6"), ascii("mp41") ]); } } return box("ftyp", [ ascii("isom"), // Major brand u32(minorVersion), // Minor version // Compatible brands ascii("isom"), details.holdsAvc ? ascii("avc1") : [], ascii("mp41") ]); }; var styp = () => box("styp", [ ascii("iso5"), // Major brand u32(0), // Minor version // Compatible brands ascii("iso5"), ascii("iso6"), ascii("mp41"), ascii("cmfc"), ascii("dash") ]); var sidx = (muxer, referencedSize) => { let duration = muxer.maxWrittenEndTimestamp - muxer.minWrittenTimestamp; if (!Number.isFinite(duration)) { duration = 0; } return fullBox("sidx", 1, 0, [ u32(1), // Reference ID u32(GLOBAL_TIMESCALE), // Timescale u64(intoTimescale(muxer.minWrittenTimestamp, GLOBAL_TIMESCALE)), // Earliest presentation time u64(0), // First offset u16(0), // Reserved u16(1), // Reference count u32(referencedSize & 2147483647), // Reference type (0) + referenced size u32(intoTimescale(duration, GLOBAL_TIMESCALE)), // Subsegment duration u32(0) // Starts with SAP + SAP type + SAP delta time (no information provided) ]); }; var mdat = (reserveLargeSize) => ({ type: "mdat", largeSize: reserveLargeSize }); var free = (size) => ({ type: "free", size }); var moov = (muxer) => { return box("moov", void 0, [ mvhd(muxer.creationTime, muxer.trackDatas), ...muxer.trackDatas.map((x) => trak(x, muxer.creationTime)), muxer.isFragmented ? mvex(muxer.trackDatas) : null, udta(muxer) ]); }; var mvhd = (creationTime, trackDatas) => { const duration = Math.max(0, ...trackDatas.map((trackData) => intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE) + intoTimescale(trackData.startTimestampOffset ?? 0, GLOBAL_TIMESCALE))); const nextTrackId = Math.max(0, ...trackDatas.map((x) => x.track.id)) + 1; const needsU64 = !isU32(creationTime) || !isU32(duration); const u32OrU64 = needsU64 ? u64 : u32; return fullBox("mvhd", +needsU64, 0, [ u32OrU64(creationTime), // Creation time u32OrU64(creationTime), // Modification time u32(GLOBAL_TIMESCALE), // Timescale u32OrU64(duration), // Duration fixed_16_16(1), // Preferred rate fixed_8_8(1), // Preferred volume Array(10).fill(0), // Reserved matrixToBytes(IDENTITY_MATRIX), // Matrix Array(24).fill(0), // Pre-defined u32(nextTrackId) // Next track ID ]); }; var presentationSpan = (trackData) => { if (trackData.samples.length === 0) { return 0; } let minTimestamp = Infinity; let maxEndTimestamp = -Infinity; for (let i2 = 0; i2 < trackData.samples.length; i2++) { const sample = trackData.samples[i2]; if (sample.timestamp < minTimestamp) { minTimestamp = sample.timestamp; } if (sample.timestamp + sample.duration > maxEndTimestamp) { maxEndTimestamp = sample.timestamp + sample.duration; } } if (minTimestamp === Infinity) { return 0; } return maxEndTimestamp - minTimestamp; }; var trak = (trackData, creationTime) => { const trackMetadata = getTrackMetadata(trackData); const needsEditList = trackData.startTimestampOffset !== null && trackData.startTimestampOffset > 0; return box("trak", void 0, [ tkhd(trackData, creationTime), needsEditList ? edts(trackData, trackData.startTimestampOffset) : null, mdia(trackData, creationTime), trackMetadata.name !== void 0 ? box("udta", void 0, [ box("name", [ ...textEncoder.encode(trackMetadata.name) ]) ]) : null ]); }; var tkhd = (trackData, creationTime) => { const durationInGlobalTimescale = intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE) + intoTimescale(trackData.startTimestampOffset ?? 0, GLOBAL_TIMESCALE); const needsU64 = !isU32(creationTime) || !isU32(durationInGlobalTimescale); const u32OrU64 = needsU64 ? u64 : u32; let matrix; if (trackData.type === "video") { const rotation = trackData.track.metadata.rotation; matrix = rotationMatrix(rotation ?? 0); } else { matrix = IDENTITY_MATRIX; } let flags = 2; if (trackData.track.metadata.disposition?.default !== false) { flags |= 1; } return fullBox("tkhd", +needsU64, flags, [ u32OrU64(creationTime), // Creation time u32OrU64(creationTime), // Modification time u32(trackData.track.id), // Track ID u32(0), // Reserved u32OrU64(durationInGlobalTimescale), // Duration Array(8).fill(0), // Reserved u16(0), // Layer u16(trackData.track.id), // Alternate group fixed_8_8(trackData.type === "audio" ? 1 : 0), // Volume u16(0), // Reserved matrixToBytes(matrix), // Matrix fixed_16_16(trackData.type === "video" ? trackData.info.width : 0), // Track width fixed_16_16(trackData.type === "video" ? trackData.info.height : 0) // Track height ]); }; var edts = (trackData, offset) => { const startOffset = intoTimescale(offset, GLOBAL_TIMESCALE); const mediaDuration = intoTimescale(presentationSpan(trackData), GLOBAL_TIMESCALE); const needs64Bits = !isU32(startOffset) || !isU32(mediaDuration); const u32OrU64 = needs64Bits ? u64 : u32; const i32OrI64 = needs64Bits ? i64 : i32; return box("edts", void 0, [ fullBox("elst", needs64Bits ? 1 : 0, 0, [ u32(2), // Entry count // #1 u32OrU64(startOffset), // Segment duration i32OrI64(-1), // Media time fixed_16_16(1), // Media rate // #2 u32OrU64(mediaDuration), // Segment duration i32OrI64(0), // Media time fixed_16_16(1) // Media rate ]) ]); }; var mdia = (trackData, creationTime) => box("mdia", void 0, [ mdhd(trackData, creationTime), hdlr(true, TRACK_TYPE_TO_COMPONENT_SUBTYPE[trackData.type], TRACK_TYPE_TO_HANDLER_NAME[trackData.type]), minf(trackData) ]); var mdhd = (trackData, creationTime) => { const localDuration = intoTimescale(presentationSpan(trackData), trackData.timescale); const needsU64 = !isU32(creationTime) || !isU32(localDuration); const u32OrU64 = needsU64 ? u64 : u32; return fullBox("mdhd", +needsU64, 0, [ u32OrU64(creationTime), // Creation time u32OrU64(creationTime), // Modification time u32(trackData.timescale), // Timescale u32OrU64(localDuration), // Duration u16(getLanguageCodeInt(trackData.track.metadata.languageCode ?? UNDETERMINED_LANGUAGE)), // Language u16(0) // Quality ]); }; var TRACK_TYPE_TO_COMPONENT_SUBTYPE = { video: "vide", audio: "soun", subtitle: "text" }; var TRACK_TYPE_TO_HANDLER_NAME = { video: "MediabunnyVideoHandler", audio: "MediabunnySoundHandler", subtitle: "MediabunnyTextHandler" }; var hdlr = (hasComponentType, handlerType, name, manufacturer = "\\0\\0\\0\\0") => fullBox("hdlr", 0, 0, [ hasComponentType ? ascii("mhlr") : u32(0), // Component type ascii(handlerType), // Component subtype ascii(manufacturer), // Component manufacturer u32(0), // Component flags u32(0), // Component flags mask ascii(name, true) // Component name ]); var minf = (trackData) => box("minf", void 0, [ TRACK_TYPE_TO_HEADER_BOX[trackData.type](), dinf(), stbl(trackData) ]); var vmhd = () => fullBox("vmhd", 0, 1, [ u16(0), // Graphics mode u16(0), // Opcolor R u16(0), // Opcolor G u16(0) // Opcolor B ]); var smhd = () => fullBox("smhd", 0, 0, [ u16(0), // Balance u16(0) // Reserved ]); var nmhd = () => fullBox("nmhd", 0, 0); var TRACK_TYPE_TO_HEADER_BOX = { video: vmhd, audio: smhd, subtitle: nmhd }; var dinf = () => box("dinf", void 0, [ dref() ]); var dref = () => fullBox("dref", 0, 0, [ u32(1) // Entry count ], [ url() ]); var url = () => fullBox("url ", 0, 1); var stbl = (trackData) => { const needsCtts = trackData.compositionTimeOffsetTable.length > 1 || trackData.compositionTimeOffsetTable.some((x) => x.sampleCompositionTimeOffset !== 0); return box("stbl", void 0, [ stsd(trackData), stts(trackData), needsCtts ? ctts(trackData) : null, needsCtts ? cslg(trackData) : null, stsc(trackData), stsz(trackData), stco(trackData), stss(trackData) ]); }; var stsd = (trackData) => { let sampleDescription; if (trackData.type === "video") { sampleDescription = videoSampleDescription(videoCodecToBoxName(trackData.track.source._codec, trackData.info.decoderConfig.codec), trackData); } else if (trackData.type === "audio") { const boxName = audioCodecToBoxName(trackData.track.source._codec, trackData.muxer.isQuickTime); assert(boxName); sampleDescription = soundSampleDescription(boxName, trackData); } else if (trackData.type === "subtitle") { sampleDescription = subtitleSampleDescription(SUBTITLE_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData); } assert(sampleDescription); return fullBox("stsd", 0, 0, [ u32(1) // Entry count ], [ sampleDescription ]); }; var videoSampleDescription = (compressionType, trackData) => box(compressionType, [ Array(6).fill(0), // Reserved u16(1), // Data reference index u16(0), // Pre-defined u16(0), // Reserved Array(12).fill(0), // Pre-defined u16(trackData.info.width), // Width u16(trackData.info.height), // Height u32(4718592), // Horizontal resolution u32(4718592), // Vertical resolution u32(0), // Reserved u16(1), // Frame count Array(32).fill(0), // Compressor name u16(24), // Depth i16(65535) // Pre-defined ], [ VIDEO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData), pasp(trackData), colorSpaceIsComplete(trackData.info.decoderConfig.colorSpace) ? colr(trackData) : null ]); var pasp = (trackData) => { if (trackData.info.pixelAspectRatio.num === trackData.info.pixelAspectRatio.den) { return null; } return box("pasp", [ u32(trackData.info.pixelAspectRatio.num), u32(trackData.info.pixelAspectRatio.den) ]); }; var colr = (trackData) => box("colr", [ ascii(trackData.muxer.isQuickTime ? "nclc" : "nclx"), // Colour type u16(COLOR_PRIMARIES_MAP[trackData.info.decoderConfig.colorSpace.primaries]), // Colour primaries u16(TRANSFER_CHARACTERISTICS_MAP[trackData.info.decoderConfig.colorSpace.transfer]), // Transfer characteristics u16(MATRIX_COEFFICIENTS_MAP[trackData.info.decoderConfig.colorSpace.matrix]), // Matrix coefficients trackData.muxer.isQuickTime ? [] : u8((trackData.info.decoderConfig.colorSpace.fullRange ? 1 : 0) << 7) // Full range flag ]); var avcC = (trackData) => trackData.info.decoderConfig && box("avcC", [ // For AVC, description is an AVCDecoderConfigurationRecord, so nothing else to do here ...toUint8Array(trackData.info.decoderConfig.description) ]); var hvcC = (trackData) => trackData.info.decoderConfig && box("hvcC", [ // For HEVC, description is an HEVCDecoderConfigurationRecord, so nothing else to do here ...toUint8Array(trackData.info.decoderConfig.description) ]); var vpcC = (trackData) => { if (!trackData.info.decoderConfig) { return null; } const decoderConfig = trackData.info.decoderConfig; const parts = decoderConfig.codec.split("."); const profile = Number(parts[1]); const level = Number(parts[2]); const bitDepth = Number(parts[3]); const chromaSubsampling = parts[4] ? Number(parts[4]) : 1; const videoFullRangeFlag = parts[8] ? Number(parts[8]) : Number(decoderConfig.colorSpace?.fullRange ?? 0); const thirdByte = (bitDepth << 4) + (chromaSubsampling << 1) + videoFullRangeFlag; const colourPrimaries = parts[5] ? Number(parts[5]) : decoderConfig.colorSpace?.primaries ? COLOR_PRIMARIES_MAP[decoderConfig.colorSpace.primaries] : 2; const transferCharacteristics = parts[6] ? Number(parts[6]) : decoderConfig.colorSpace?.transfer ? TRANSFER_CHARACTERISTICS_MAP[decoderConfig.colorSpace.transfer] : 2; const matrixCoefficients = parts[7] ? Number(parts[7]) : decoderConfig.colorSpace?.matrix ? MATRIX_COEFFICIENTS_MAP[decoderConfig.colorSpace.matrix] : 2; return fullBox("vpcC", 1, 0, [ u8(profile), // Profile u8(level), // Level u8(thirdByte), // Bit depth, chroma subsampling, full range u8(colourPrimaries), // Colour primaries u8(transferCharacteristics), // Transfer characteristics u8(matrixCoefficients), // Matrix coefficients u16(0) // Codec initialization data size ]); }; var av1C = (trackData) => { return box("av1C", generateAv1CodecConfigurationFromCodecString(trackData.info.decoderConfig.codec)); }; var soundSampleDescription = (compressionType, trackData) => { let version = 0; let contents; let sampleSizeInBits = 16; const isPcmCodec = PCM_AUDIO_CODECS.includes(trackData.track.source._codec); if (isPcmCodec) { const codec = trackData.track.source._codec; const { sampleSize } = parsePcmCodec(codec); sampleSizeInBits = 8 * sampleSize; if (sampleSizeInBits > 16) { version = 1; } } if (trackData.muxer.isQuickTime) { version = 1; } if (version === 0) { contents = [ Array(6).fill(0), // Reserved u16(1), // Data reference index u16(version), // Version u16(0), // Revision level u32(0), // Vendor u16(trackData.info.numberOfChannels), // Number of channels u16(sampleSizeInBits), // Sample size (bits) u16(0), // Compression ID u16(0), // Packet size u16(trackData.info.sampleRate < 2 ** 16 ? trackData.info.sampleRate : 0), // Sample rate (upper) u16(0) // Sample rate (lower) ]; } else { const compressionId = isPcmCodec ? 0 : -2; contents = [ Array(6).fill(0), // Reserved u16(1), // Data reference index u16(version), // Version u16(0), // Revision level u32(0), // Vendor u16(trackData.info.numberOfChannels), // Number of channels u16(Math.min(sampleSizeInBits, 16)), // Sample size (bits) i16(compressionId), // Compression ID u16(0), // Packet size u16(trackData.info.sampleRate < 2 ** 16 ? trackData.info.sampleRate : 0), // Sample rate (upper) u16(0), // Sample rate (lower) isPcmCodec ? [ u32(1), // Samples per packet (must be 1 for uncompressed formats) u32(sampleSizeInBits / 8), // Bytes per packet u32(trackData.info.numberOfChannels * sampleSizeInBits / 8) // Bytes per frame ] : [ u32(0), // Samples per packet (don't bother, still works with 0) u32(0), // Bytes per packet (variable) u32(0) // Bytes per frame (variable) ], u32(2) // Bytes per sample (constant in FFmpeg) ]; } return box(compressionType, contents, [ audioCodecToConfigurationBox(trackData.track.source._codec, trackData.muxer.isQuickTime)?.(trackData) ?? null ]); }; var esds = (trackData) => { let objectTypeIndication; switch (trackData.track.source._codec) { case "aac": { objectTypeIndication = 64; } ; break; case "mp3": { objectTypeIndication = 107; } ; break; case "vorbis": { objectTypeIndication = 221; } ; break; default: throw new Error(\`Unhandled audio codec: \${trackData.track.source._codec}\`); } let bytes2 = [ ...u8(objectTypeIndication), // Object type indication ...u8(21), // stream type(6bits)=5 audio, flags(2bits)=1 ...u24(0), // 24bit buffer size ...u32(0), // max bitrate ...u32(0) // avg bitrate ]; if (trackData.info.decoderConfig.description) { const description = toUint8Array(trackData.info.decoderConfig.description); bytes2 = [ ...bytes2, ...u8(5), // TAG(5) = DecoderSpecificInfo ...variableUnsignedInt(description.byteLength), ...description ]; } bytes2 = [ ...u16(1), // ES_ID = 1 ...u8(0), // flags etc = 0 ...u8(4), // TAG(4) = ES Descriptor ...variableUnsignedInt(bytes2.length), ...bytes2, ...u8(6), // TAG(6) ...u8(1), // length ...u8(2) // data ]; bytes2 = [ ...u8(3), // TAG(3) = Object Descriptor ...variableUnsignedInt(bytes2.length), ...bytes2 ]; return fullBox("esds", 0, 0, bytes2); }; var wave = (trackData) => { return box("wave", void 0, [ frma(trackData), enda(trackData), box("\\0\\0\\0\\0") // NULL tag at the end ]); }; var frma = (trackData) => { return box("frma", [ ascii(audioCodecToBoxName(trackData.track.source._codec, trackData.muxer.isQuickTime)) ]); }; var enda = (trackData) => { const { littleEndian } = parsePcmCodec(trackData.track.source._codec); return box("enda", [ u16(+littleEndian) ]); }; var dOps = (trackData) => { let outputChannelCount = trackData.info.numberOfChannels; let preSkip = 3840; let inputSampleRate = trackData.info.sampleRate; let outputGain = 0; let channelMappingFamily = 0; let channelMappingTable = new Uint8Array(0); const description = trackData.info.decoderConfig?.description; if (description) { assert(description.byteLength >= 18); const bytes2 = toUint8Array(description); const header = parseOpusIdentificationHeader(bytes2); outputChannelCount = header.outputChannelCount; preSkip = header.preSkip; inputSampleRate = header.inputSampleRate; outputGain = header.outputGain; channelMappingFamily = header.channelMappingFamily; if (header.channelMappingTable) { channelMappingTable = header.channelMappingTable; } } return box("dOps", [ u8(0), // Version u8(outputChannelCount), // OutputChannelCount u16(preSkip), // PreSkip u32(inputSampleRate), // InputSampleRate i16(outputGain), // OutputGain u8(channelMappingFamily), // ChannelMappingFamily ...channelMappingTable ]); }; var dfLa = (trackData) => { const description = trackData.info.decoderConfig?.description; assert(description); const bytes2 = toUint8Array(description); return fullBox("dfLa", 0, 0, [ ...bytes2.subarray(4) ]); }; var pcmC = (trackData) => { const { littleEndian, sampleSize } = parsePcmCodec(trackData.track.source._codec); const formatFlags = +littleEndian; return fullBox("pcmC", 0, 0, [ u8(formatFlags), u8(8 * sampleSize) ]); }; var dac3 = (trackData) => { const frameInfo = parseAc3SyncFrame(trackData.info.firstPacket.data); if (!frameInfo) { throw new Error("Couldn't extract AC-3 frame info from the audio packet. Ensure the packets contain valid AC-3 sync frames (as specified in ETSI TS 102 366)."); } const bytes2 = new Uint8Array(3); const bitstream = new Bitstream(bytes2); bitstream.writeBits(2, frameInfo.fscod); bitstream.writeBits(5, frameInfo.bsid); bitstream.writeBits(3, frameInfo.bsmod); bitstream.writeBits(3, frameInfo.acmod); bitstream.writeBits(1, frameInfo.lfeon); bitstream.writeBits(5, frameInfo.bitRateCode); bitstream.writeBits(5, 0); return box("dac3", [...bytes2]); }; var dec3 = (trackData) => { const frameInfo = parseEac3SyncFrame(trackData.info.firstPacket.data); if (!frameInfo) { throw new Error("Couldn't extract E-AC-3 frame info from the audio packet. Ensure the packets contain valid E-AC-3 sync frames (as specified in ETSI TS 102 366)."); } let totalBits = 16; for (const sub of frameInfo.substreams) { totalBits += 23; if (sub.numDepSub > 0) { totalBits += 9; } else { totalBits += 1; } } const size = Math.ceil(totalBits / 8); const bytes2 = new Uint8Array(size); const bitstream = new Bitstream(bytes2); bitstream.writeBits(13, frameInfo.dataRate); bitstream.writeBits(3, frameInfo.substreams.length - 1); for (const sub of frameInfo.substreams) { bitstream.writeBits(2, sub.fscod); bitstream.writeBits(5, sub.bsid); bitstream.writeBits(1, 0); bitstream.writeBits(1, 0); bitstream.writeBits(3, sub.bsmod); bitstream.writeBits(3, sub.acmod); bitstream.writeBits(1, sub.lfeon); bitstream.writeBits(3, 0); bitstream.writeBits(4, sub.numDepSub); if (sub.numDepSub > 0) { bitstream.writeBits(9, sub.chanLoc); } else { bitstream.writeBits(1, 0); } } return box("dec3", [...bytes2]); }; var subtitleSampleDescription = (compressionType, trackData) => box(compressionType, [ Array(6).fill(0), // Reserved u16(1) // Data reference index ], [ SUBTITLE_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData) ]); var vttC = (trackData) => box("vttC", [ ...textEncoder.encode(trackData.info.config.description) ]); var stts = (trackData) => { return fullBox("stts", 0, 0, [ u32(trackData.timeToSampleTable.length), // Number of entries trackData.timeToSampleTable.map((x) => [ u32(x.sampleCount), // Sample count u32(x.sampleDelta) // Sample duration ]) ]); }; var stss = (trackData) => { if (trackData.samples.every((x) => x.type === "key")) return null; const keySamples = [...trackData.samples.entries()].filter(([, sample]) => sample.type === "key"); return fullBox("stss", 0, 0, [ u32(keySamples.length), // Number of entries keySamples.map(([index]) => u32(index + 1)) // Sync sample table ]); }; var stsc = (trackData) => { return fullBox("stsc", 0, 0, [ u32(trackData.compactlyCodedChunkTable.length), // Number of entries trackData.compactlyCodedChunkTable.map((x) => [ u32(x.firstChunk), // First chunk u32(x.samplesPerChunk), // Samples per chunk u32(1) // Sample description index ]) ]); }; var stsz = (trackData) => { if (trackData.type === "audio" && trackData.info.requiresPcmTransformation) { const { sampleSize } = parsePcmCodec(trackData.track.source._codec); return fullBox("stsz", 0, 0, [ u32(sampleSize * trackData.info.numberOfChannels), // Sample size u32(trackData.samples.reduce((acc, x) => acc + intoTimescale(x.duration, trackData.timescale), 0)) ]); } return fullBox("stsz", 0, 0, [ u32(0), // Sample size (0 means non-constant size) u32(trackData.samples.length), // Number of entries trackData.samples.map((x) => u32(x.size)) // Sample size table ]); }; var stco = (trackData) => { if (trackData.finalizedChunks.length > 0 && last(trackData.finalizedChunks).offset >= 2 ** 32) { return fullBox("co64", 0, 0, [ u32(trackData.finalizedChunks.length), // Number of entries trackData.finalizedChunks.map((x) => u64(x.offset)) // Chunk offset table ]); } return fullBox("stco", 0, 0, [ u32(trackData.finalizedChunks.length), // Number of entries trackData.finalizedChunks.map((x) => u32(x.offset)) // Chunk offset table ]); }; var ctts = (trackData) => { return fullBox("ctts", 1, 0, [ u32(trackData.compositionTimeOffsetTable.length), // Number of entries trackData.compositionTimeOffsetTable.map((x) => [ u32(x.sampleCount), // Sample count i32(x.sampleCompositionTimeOffset) // Sample offset ]) ]); }; var cslg = (trackData) => { let leastDecodeToDisplayDelta = Infinity; let greatestDecodeToDisplayDelta = -Infinity; let compositionStartTime = Infinity; let compositionEndTime = -Infinity; assert(trackData.compositionTimeOffsetTable.length > 0); assert(trackData.samples.length > 0); for (let i2 = 0; i2 < trackData.compositionTimeOffsetTable.length; i2++) { const entry = trackData.compositionTimeOffsetTable[i2]; leastDecodeToDisplayDelta = Math.min(leastDecodeToDisplayDelta, entry.sampleCompositionTimeOffset); greatestDecodeToDisplayDelta = Math.max(greatestDecodeToDisplayDelta, entry.sampleCompositionTimeOffset); } for (let i2 = 0; i2 < trackData.samples.length; i2++) { const sample = trackData.samples[i2]; compositionStartTime = Math.min(compositionStartTime, intoTimescale(sample.timestamp, trackData.timescale)); compositionEndTime = Math.max(compositionEndTime, intoTimescale(sample.timestamp + sample.duration, trackData.timescale)); } const compositionToDtsShift = Math.max(-leastDecodeToDisplayDelta, 0); if (compositionEndTime >= 2 ** 31) { return null; } return fullBox("cslg", 0, 0, [ i32(compositionToDtsShift), // Composition to DTS shift i32(leastDecodeToDisplayDelta), // Least decode to display delta i32(greatestDecodeToDisplayDelta), // Greatest decode to display delta i32(compositionStartTime), // Composition start time i32(compositionEndTime) // Composition end time ]); }; var mvex = (trackDatas) => { return box("mvex", void 0, trackDatas.map(trex)); }; var trex = (trackData) => { return fullBox("trex", 0, 0, [ u32(trackData.track.id), // Track ID u32(1), // Default sample description index u32(0), // Default sample duration u32(0), // Default sample size u32(0) // Default sample flags ]); }; var moof = (sequenceNumber, trackDatas) => { return box("moof", void 0, [ mfhd(sequenceNumber), ...trackDatas.map(traf) ]); }; var mfhd = (sequenceNumber) => { return fullBox("mfhd", 0, 0, [ u32(sequenceNumber) // Sequence number ]); }; var fragmentSampleFlags = (sample) => { let byte1 = 0; let byte2 = 0; const byte3 = 0; const byte4 = 0; const sampleIsDifferenceSample = sample.type === "delta"; byte2 |= +sampleIsDifferenceSample; if (sampleIsDifferenceSample) { byte1 |= 1; } else { byte1 |= 2; } return byte1 << 24 | byte2 << 16 | byte3 << 8 | byte4; }; var traf = (trackData) => { return box("traf", void 0, [ tfhd(trackData), tfdt(trackData), trun(trackData) ]); }; var tfhd = (trackData) => { assert(trackData.currentChunk); let tfFlags = 0; tfFlags |= 8; tfFlags |= 16; tfFlags |= 32; tfFlags |= 131072; const referenceSample = trackData.currentChunk.samples[1] ?? trackData.currentChunk.samples[0]; const referenceSampleInfo = { duration: referenceSample.timescaleUnitsToNextSample, size: referenceSample.size, flags: fragmentSampleFlags(referenceSample) }; return fullBox("tfhd", 0, tfFlags, [ u32(trackData.track.id), // Track ID u32(referenceSampleInfo.duration), // Default sample duration u32(referenceSampleInfo.size), // Default sample size u32(referenceSampleInfo.flags) // Default sample flags ]); }; var tfdt = (trackData) => { assert(trackData.currentChunk); return fullBox("tfdt", 1, 0, [ u64(intoTimescale(trackData.currentChunk.startTimestamp, trackData.timescale)) // Base Media Decode Time ]); }; var trun = (trackData) => { assert(trackData.currentChunk); const allSampleDurations = trackData.currentChunk.samples.map((x) => x.timescaleUnitsToNextSample); const allSampleSizes = trackData.currentChunk.samples.map((x) => x.size); const allSampleFlags = trackData.currentChunk.samples.map(fragmentSampleFlags); const allSampleCompositionTimeOffsets = trackData.currentChunk.samples.map((x) => intoTimescale(x.timestamp - x.decodeTimestamp, trackData.timescale)); const uniqueSampleDurations = new Set(allSampleDurations); const uniqueSampleSizes = new Set(allSampleSizes); const uniqueSampleFlags = new Set(allSampleFlags); const uniqueSampleCompositionTimeOffsets = new Set(allSampleCompositionTimeOffsets); const firstSampleFlagsPresent = uniqueSampleFlags.size === 2 && allSampleFlags[0] !== allSampleFlags[1]; const sampleDurationPresent = uniqueSampleDurations.size > 1; const sampleSizePresent = uniqueSampleSizes.size > 1; const sampleFlagsPresent = !firstSampleFlagsPresent && uniqueSampleFlags.size > 1; const sampleCompositionTimeOffsetsPresent = uniqueSampleCompositionTimeOffsets.size > 1 || [...uniqueSampleCompositionTimeOffsets].some((x) => x !== 0); let flags = 0; flags |= 1; flags |= 4 * +firstSampleFlagsPresent; flags |= 256 * +sampleDurationPresent; flags |= 512 * +sampleSizePresent; flags |= 1024 * +sampleFlagsPresent; flags |= 2048 * +sampleCompositionTimeOffsetsPresent; return fullBox("trun", 1, flags, [ u32(trackData.currentChunk.samples.length), // Sample count u32(trackData.currentChunk.offset - trackData.currentChunk.moofOffset || 0), // Data offset firstSampleFlagsPresent ? u32(allSampleFlags[0]) : [], trackData.currentChunk.samples.map((_2, i2) => [ sampleDurationPresent ? u32(allSampleDurations[i2]) : [], // Sample duration sampleSizePresent ? u32(allSampleSizes[i2]) : [], // Sample size sampleFlagsPresent ? u32(allSampleFlags[i2]) : [], // Sample flags // Sample composition time offsets sampleCompositionTimeOffsetsPresent ? i32(allSampleCompositionTimeOffsets[i2]) : [] ]) ]); }; var mfra = (trackDatas) => { return box("mfra", void 0, [ ...trackDatas.map(tfra), mfro() ]); }; var tfra = (trackData, trackIndex) => { const version = 1; return fullBox("tfra", version, 0, [ u32(trackData.track.id), // Track ID u32(63), // This specifies that traf number, trun number and sample number are 32-bit ints u32(trackData.finalizedChunks.length), // Number of entries trackData.finalizedChunks.map((chunk) => [ u64(intoTimescale(chunk.samples[0].timestamp, trackData.timescale)), // Time (in presentation time) u64(chunk.moofOffset), // moof offset u32(trackIndex + 1), // traf number u32(1), // trun number u32(1) // Sample number ]) ]); }; var mfro = () => { return fullBox("mfro", 0, 0, [ // This value needs to be overwritten manually from the outside, where the actual size of the enclosing mfra box // is known u32(0) // Size ]); }; var vtte = () => box("vtte"); var vttc = (payload, timestamp, identifier, settings, sourceId) => box("vttc", void 0, [ sourceId !== null ? box("vsid", [i32(sourceId)]) : null, identifier !== null ? box("iden", [...textEncoder.encode(identifier)]) : null, timestamp !== null ? box("ctim", [...textEncoder.encode(formatSubtitleTimestamp(timestamp))]) : null, settings !== null ? box("sttg", [...textEncoder.encode(settings)]) : null, box("payl", [...textEncoder.encode(payload)]) ]); var vtta = (notes) => box("vtta", [...textEncoder.encode(notes)]); var udta = (muxer) => { const boxes = []; const metadataFormat = muxer.format._options.metadataFormat ?? "auto"; const metadataTags = muxer.output._metadataTags; if (metadataFormat === "mdir" || metadataFormat === "auto" && !muxer.isQuickTime) { const metaBox = metaMdir(metadataTags); if (metaBox) boxes.push(metaBox); } else if (metadataFormat === "mdta") { const metaBox = metaMdta(metadataTags); if (metaBox) boxes.push(metaBox); } else if (metadataFormat === "udta" || metadataFormat === "auto" && muxer.isQuickTime) { addQuickTimeMetadataTagBoxes(boxes, muxer.output._metadataTags); } if (boxes.length === 0) { return null; } return box("udta", void 0, boxes); }; var addQuickTimeMetadataTagBoxes = (boxes, tags) => { for (const { key, value } of keyValueIterator(tags)) { switch (key) { case "title": { boxes.push(metadataTagStringBoxShort("©nam", value)); } ; break; case "description": { boxes.push(metadataTagStringBoxShort("©des", value)); } ; break; case "artist": { boxes.push(metadataTagStringBoxShort("©ART", value)); } ; break; case "album": { boxes.push(metadataTagStringBoxShort("©alb", value)); } ; break; case "albumArtist": { boxes.push(metadataTagStringBoxShort("albr", value)); } ; break; case "genre": { boxes.push(metadataTagStringBoxShort("©gen", value)); } ; break; case "date": { boxes.push(metadataTagStringBoxShort("©day", value.toISOString().slice(0, 10))); } ; break; case "comment": { boxes.push(metadataTagStringBoxShort("©cmt", value)); } ; break; case "lyrics": { boxes.push(metadataTagStringBoxShort("©lyr", value)); } ; break; case "raw": { } ; break; case "discNumber": case "discsTotal": case "trackNumber": case "tracksTotal": case "images": { } ; break; default: assertNever(key); } } if (tags.raw) { for (const key in tags.raw) { const value = tags.raw[key]; if (value == null || key.length !== 4 || boxes.some((x) => x.type === key)) { continue; } if (typeof value === "string") { boxes.push(metadataTagStringBoxShort(key, value)); } else if (value instanceof Uint8Array) { boxes.push(box(key, Array.from(value))); } } } }; var metadataTagStringBoxShort = (name, value) => { const encoded = textEncoder.encode(value); return box(name, [ u16(encoded.length), u16(getLanguageCodeInt("und")), Array.from(encoded) ]); }; var DATA_BOX_MIME_TYPE_MAP = { "image/jpeg": 13, "image/png": 14, "image/bmp": 27 }; var generateMetadataPairs = (tags, isMdta) => { const pairs = []; for (const { key, value } of keyValueIterator(tags)) { switch (key) { case "title": { pairs.push({ key: isMdta ? "title" : "©nam", value: dataStringBoxLong(value) }); } ; break; case "description": { pairs.push({ key: isMdta ? "description" : "©des", value: dataStringBoxLong(value) }); } ; break; case "artist": { pairs.push({ key: isMdta ? "artist" : "©ART", value: dataStringBoxLong(value) }); } ; break; case "album": { pairs.push({ key: isMdta ? "album" : "©alb", value: dataStringBoxLong(value) }); } ; break; case "albumArtist": { pairs.push({ key: isMdta ? "album_artist" : "aART", value: dataStringBoxLong(value) }); } ; break; case "comment": { pairs.push({ key: isMdta ? "comment" : "©cmt", value: dataStringBoxLong(value) }); } ; break; case "genre": { pairs.push({ key: isMdta ? "genre" : "©gen", value: dataStringBoxLong(value) }); } ; break; case "lyrics": { pairs.push({ key: isMdta ? "lyrics" : "©lyr", value: dataStringBoxLong(value) }); } ; break; case "date": { pairs.push({ key: isMdta ? "date" : "©day", value: dataStringBoxLong(value.toISOString().slice(0, 10)) }); } ; break; case "images": { for (const image of value) { if (image.kind !== "coverFront") { continue; } pairs.push({ key: "covr", value: box("data", [ u32(DATA_BOX_MIME_TYPE_MAP[image.mimeType] ?? 0), // Type indicator u32(0), // Locale indicator Array.from(image.data) // Kinda slow, hopefully temp ]) }); } } ; break; case "trackNumber": { if (isMdta) { const string = tags.tracksTotal !== void 0 ? \`\${value}/\${tags.tracksTotal}\` : value.toString(); pairs.push({ key: "track", value: dataStringBoxLong(string) }); } else { pairs.push({ key: "trkn", value: box("data", [ u32(0), // 8 bytes empty u32(0), u16(0), // Empty u16(value), u16(tags.tracksTotal ?? 0), u16(0) // Empty ]) }); } } ; break; case "discNumber": { if (!isMdta) { pairs.push({ key: "disc", value: box("data", [ u32(0), // 8 bytes empty u32(0), u16(0), // Empty u16(value), u16(tags.discsTotal ?? 0), u16(0) // Empty ]) }); } } ; break; case "tracksTotal": case "discsTotal": { } ; break; case "raw": { } ; break; default: assertNever(key); } } if (tags.raw) { for (const key in tags.raw) { const value = tags.raw[key]; if (value == null || !isMdta && key.length !== 4 || pairs.some((x) => x.key === key)) { continue; } if (typeof value === "string") { pairs.push({ key, value: dataStringBoxLong(value) }); } else if (value instanceof Uint8Array) { pairs.push({ key, value: box("data", [ u32(0), // Type indicator u32(0), // Locale indicator Array.from(value) ]) }); } else if (value instanceof RichImageData) { pairs.push({ key, value: box("data", [ u32(DATA_BOX_MIME_TYPE_MAP[value.mimeType] ?? 0), // Type indicator u32(0), // Locale indicator Array.from(value.data) // Kinda slow, hopefully temp ]) }); } } } return pairs; }; var metaMdir = (tags) => { const pairs = generateMetadataPairs(tags, false); if (pairs.length === 0) { return null; } return fullBox("meta", 0, 0, void 0, [ hdlr(false, "mdir", "", "appl"), // mdir handler box("ilst", void 0, pairs.map((pair) => box(pair.key, void 0, [pair.value]))) // Item list without keys box ]); }; var metaMdta = (tags) => { const pairs = generateMetadataPairs(tags, true); if (pairs.length === 0) { return null; } return box("meta", void 0, [ hdlr(false, "mdta", ""), // mdta handler fullBox("keys", 0, 0, [ u32(pairs.length) ], pairs.map((pair) => box("mdta", [ ...textEncoder.encode(pair.key) ]))), box("ilst", void 0, pairs.map((pair, i2) => { const boxName = String.fromCharCode(...u32(i2 + 1)); return box(boxName, void 0, [pair.value]); })) ]); }; var dataStringBoxLong = (value) => { return box("data", [ u32(1), // Type indicator (UTF-8) u32(0), // Locale indicator ...textEncoder.encode(value) ]); }; var videoCodecToBoxName = (codec, fullCodecString) => { switch (codec) { case "avc": return fullCodecString.startsWith("avc3") ? "avc3" : "avc1"; case "hevc": return "hvc1"; case "vp8": return "vp08"; case "vp9": return "vp09"; case "av1": return "av01"; } }; var VIDEO_CODEC_TO_CONFIGURATION_BOX = { avc: avcC, hevc: hvcC, vp8: vpcC, vp9: vpcC, av1: av1C }; var audioCodecToBoxName = (codec, isQuickTime) => { switch (codec) { case "aac": return "mp4a"; case "mp3": return "mp4a"; case "opus": return "Opus"; case "vorbis": return "mp4a"; case "flac": return "fLaC"; case "ulaw": return "ulaw"; case "alaw": return "alaw"; case "pcm-u8": return "raw "; case "pcm-s8": return "sowt"; case "ac3": return "ac-3"; case "eac3": return "ec-3"; } if (isQuickTime) { switch (codec) { case "pcm-s16": return "sowt"; case "pcm-s16be": return "twos"; case "pcm-s24": return "in24"; case "pcm-s24be": return "in24"; case "pcm-s32": return "in32"; case "pcm-s32be": return "in32"; case "pcm-f32": return "fl32"; case "pcm-f32be": return "fl32"; case "pcm-f64": return "fl64"; case "pcm-f64be": return "fl64"; } } else { switch (codec) { case "pcm-s16": return "ipcm"; case "pcm-s16be": return "ipcm"; case "pcm-s24": return "ipcm"; case "pcm-s24be": return "ipcm"; case "pcm-s32": return "ipcm"; case "pcm-s32be": return "ipcm"; case "pcm-f32": return "fpcm"; case "pcm-f32be": return "fpcm"; case "pcm-f64": return "fpcm"; case "pcm-f64be": return "fpcm"; } } }; var audioCodecToConfigurationBox = (codec, isQuickTime) => { switch (codec) { case "aac": return esds; case "mp3": return esds; case "opus": return dOps; case "vorbis": return esds; case "flac": return dfLa; case "ac3": return dac3; case "eac3": return dec3; } if (isQuickTime) { switch (codec) { case "pcm-s24": return wave; case "pcm-s24be": return wave; case "pcm-s32": return wave; case "pcm-s32be": return wave; case "pcm-f32": return wave; case "pcm-f32be": return wave; case "pcm-f64": return wave; case "pcm-f64be": return wave; } } else { switch (codec) { case "pcm-s16": return pcmC; case "pcm-s16be": return pcmC; case "pcm-s24": return pcmC; case "pcm-s24be": return pcmC; case "pcm-s32": return pcmC; case "pcm-s32be": return pcmC; case "pcm-f32": return pcmC; case "pcm-f32be": return pcmC; case "pcm-f64": return pcmC; case "pcm-f64be": return pcmC; } } return null; }; var SUBTITLE_CODEC_TO_BOX_NAME = { webvtt: "wvtt" }; var SUBTITLE_CODEC_TO_CONFIGURATION_BOX = { webvtt: vttC }; var getLanguageCodeInt = (code) => { assert(code.length === 3); ; let language = 0; for (let i2 = 0; i2 < 3; i2++) { language <<= 5; language += code.charCodeAt(i2) - 96; } return language; }; // node_modules/mediabunny/dist/modules/src/writer.js var Writer = class { constructor(target, isMonotonic) { this.finalized = false; this.started = false; this.pos = 0; this.trackedWrites = null; this.trackedStart = -1; this.trackedEnd = -1; if (target._writerAcquired) { throw new Error("Can't have multiple Writers for the same Target."); } this.target = target; target._setMonotonicity(isMonotonic); target._writerAcquired = true; } start() { assert(!this.started); this.target._start(); this.started = true; } /** Writes the given data to the target, at the current position. */ write(data) { assert(this.started && !this.finalized); this.maybeTrackWrites(data); this.target._write(data, this.pos); this.pos += data.byteLength; } /** Sets the current position for future writes to a new one. */ seek(newPos) { this.pos = newPos; } /** Returns the current position. */ getPos() { return this.pos; } /** Signals to the writer that it may be time to flush. */ async flush() { assert(this.started && !this.finalized); return this.target._flush(); } /** Called after muxing has finished. */ async finalize() { assert(this.started && !this.finalized); await this.target._finalize(); this.finalized = true; } maybeTrackWrites(data) { if (!this.trackedWrites) { return; } let pos = this.getPos(); if (pos < this.trackedStart) { if (pos + data.byteLength <= this.trackedStart) { return; } data = data.subarray(this.trackedStart - pos); pos = 0; } const neededSize = pos + data.byteLength - this.trackedStart; let newLength = this.trackedWrites.byteLength; while (newLength < neededSize) { newLength *= 2; } if (newLength !== this.trackedWrites.byteLength) { const copy = new Uint8Array(newLength); copy.set(this.trackedWrites, 0); this.trackedWrites = copy; } this.trackedWrites.set(data, pos - this.trackedStart); this.trackedEnd = Math.max(this.trackedEnd, pos + data.byteLength); } startTrackingWrites() { this.trackedWrites = new Uint8Array(2 ** 10); this.trackedStart = this.getPos(); this.trackedEnd = this.trackedStart; } stopTrackingWrites() { if (!this.trackedWrites) { throw new Error("Internal error: Can't get tracked writes since nothing was tracked."); } const slice = this.trackedWrites.subarray(0, this.trackedEnd - this.trackedStart); const result = { data: slice, start: this.trackedStart, end: this.trackedEnd }; this.trackedWrites = null; return result; } }; // node_modules/mediabunny/dist/modules/src/target.js var Target = class extends EventEmitter { constructor() { super(...arguments); this._writerAcquired = false; this._monotonicity = null; this.onwrite = null; } /** @internal */ _setMonotonicity(monotonicity) { if (this._monotonicity !== false) { this._monotonicity = monotonicity; } else { } } /** @internal */ _dispatchWrite(start, end) { this.onwrite?.(start, end); this._emit("write", { start, end }); } /** * Returns a new {@link RangedTarget} that writes data to this target using the given offset. * * Useful for writing a file into a section of a larger file. */ slice(offset) { if (!Number.isInteger(offset) || offset < 0) { throw new TypeError("offset must be a non-negative integer."); } return new RangedTarget(this, offset); } }; var ARRAY_BUFFER_INITIAL_SIZE = 2 ** 16; var ARRAY_BUFFER_MAX_SIZE = 2 ** 32; var BufferTarget = class extends Target { /** Creates a new {@link BufferTarget}. The buffer holding the data will be created and managed internally. */ constructor(options = {}) { super(); this.buffer = null; this._maxPos = 0; if (!options || typeof options !== "object") { throw new TypeError("BufferTarget options, when provided, must be an object."); } if (options.onFinalize !== void 0 && typeof options.onFinalize !== "function") { throw new TypeError("options.onFinalize, when provided, must be a function."); } this._options = options; this._supportsResize = "resize" in new ArrayBuffer(0); if (this._supportsResize) { try { this._buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE, { maxByteLength: ARRAY_BUFFER_MAX_SIZE }); } catch { this._buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE); this._supportsResize = false; } } else { this._buffer = new ArrayBuffer(ARRAY_BUFFER_INITIAL_SIZE); } this._bytes = new Uint8Array(this._buffer); } /** @internal */ _ensureSize(size) { let newLength = this._buffer.byteLength; while (newLength < size) newLength *= 2; if (newLength === this._buffer.byteLength) return; if (newLength > ARRAY_BUFFER_MAX_SIZE) { throw new Error(\`ArrayBuffer exceeded maximum size of \${ARRAY_BUFFER_MAX_SIZE} bytes. Please consider using another target.\`); } if (this._supportsResize) { this._buffer.resize(newLength); } else { const newBuffer = new ArrayBuffer(newLength); const newBytes = new Uint8Array(newBuffer); newBytes.set(this._bytes, 0); this._buffer = newBuffer; this._bytes = newBytes; } } /** @internal */ _start() { } /** @internal */ _write(data, pos) { this._ensureSize(pos + data.byteLength); this._bytes.set(data, pos); this._maxPos = Math.max(this._maxPos, pos + data.byteLength); this._dispatchWrite(pos, pos + data.byteLength); } /** @internal */ async _flush() { } /** @internal */ async _finalize() { this.buffer = this._buffer.slice(0, this._maxPos); if (this._options.onFinalize) { await this._options.onFinalize(this.buffer); } this._emit("finalized"); } /** @internal */ async _close() { } /** @internal */ _getSlice(start, end) { return this._bytes.slice(start, end); } }; var DEFAULT_CHUNK_SIZE = 2 ** 24; var RangedTarget = class extends Target { /** @internal */ constructor(baseTarget, offset) { super(); this._baseTarget = baseTarget; this._offset = offset; } /** @internal */ _start() { } /** @internal */ _write(data, pos) { this._baseTarget._write(data, this._offset + pos); this._dispatchWrite(pos, pos + data.byteLength); } /** @internal */ _flush() { return this._baseTarget._flush(); } /** @internal */ async _finalize() { this._emit("finalized"); } /** @internal */ async _close() { } /** @internal */ _setMonotonicity(monotonicity) { super._setMonotonicity(monotonicity); this._baseTarget._setMonotonicity(monotonicity); } }; var PathedTarget = class { /** Creates a new {@link PathedTarget} from a root path and a callback. */ constructor(rootPath, getTarget) { this.rootPath = rootPath; this.getTarget = getTarget; if (typeof rootPath !== "string") { throw new TypeError("rootPath must be a string."); } if (typeof getTarget !== "function") { throw new TypeError("getTarget must be a function."); } } }; // node_modules/mediabunny/dist/modules/src/isobmff/isobmff-muxer.js var GLOBAL_TIMESCALE = 57600; var TIMESTAMP_OFFSET = 2082844800; var getTrackMetadata = (trackData) => { const metadata = {}; const track = trackData.track; if (track.metadata.name !== void 0) { metadata.name = track.metadata.name; } return metadata; }; var intoTimescale = (timeInSeconds, timescale, round = true) => { const value = timeInSeconds * timescale; return round ? Math.round(value) : value; }; var IsobmffMuxer = class extends Muxer { constructor(output, format) { super(output); this.writer = null; this.boxWriter = null; this.initWriter = null; this.initBoxWriter = null; this.auxTarget = new BufferTarget(); this.auxWriter = new Writer(this.auxTarget, false); this.auxBoxWriter = new IsobmffBoxWriter(this.auxWriter); this.mdat = null; this.ftypSize = null; this.trackDatas = []; this.allTracksKnown = promiseWithResolvers(); this.creationTime = Math.floor(Date.now() / 1e3) + TIMESTAMP_OFFSET; this.finalizedChunks = []; this.nextFragmentNumber = 1; this.maxWrittenTimestamp = -Infinity; this.minWrittenTimestamp = Infinity; this.maxWrittenEndTimestamp = -Infinity; this.segmentHeaderSize = null; this.format = format; this.isQuickTime = format instanceof MovOutputFormat; this.isCmaf = format instanceof CmafOutputFormat; this.minimumFragmentDuration = format._options.minimumFragmentDuration ?? (format instanceof CmafOutputFormat ? Infinity : 1); } async start() { const release = await this.mutex.acquire(); if (!this.isCmaf) { this.writer = await this.output._getRootWriter((target) => this.format._options.fastStart !== void 0 ? this.format._options.fastStart === "fragmented" : target instanceof BufferTarget); this.boxWriter = new IsobmffBoxWriter(this.writer); this.fastStart = this.format._options.fastStart ?? (this.writer.target instanceof BufferTarget ? "in-memory" : false); this.isFragmented = this.fastStart === "fragmented"; } else { this.fastStart = "fragmented"; this.isFragmented = true; } if (this.isCmaf) { if (!this.output._hasInitTarget()) { throw new Error(\`CMAF outputs require the initTarget field in OutputOptions to be set; the init segment will be written to it.\`); } const initTarget = await this.output._getInitTarget(); const initWriter = new Writer(initTarget, true); initWriter.start(); this.initWriter = initWriter; this.initBoxWriter = new IsobmffBoxWriter(initWriter); } const holdsAvc = this.output._tracks.some((x) => x.isVideoTrack() && x.source._codec === "avc"); { const boxWriter = this.initBoxWriter ?? this.boxWriter; assert(boxWriter); if (this.format._options.onFtyp) { boxWriter.writer.startTrackingWrites(); } boxWriter.writeBox(ftyp({ isQuickTime: this.isQuickTime, holdsAvc, fragmented: this.isFragmented, cmaf: this.isCmaf })); if (this.format._options.onFtyp) { const { data, start } = boxWriter.writer.stopTrackingWrites(); this.format._options.onFtyp(data, start); } this.ftypSize = boxWriter.writer.getPos(); if (this.isCmaf) { await this.initWriter.flush(); } } if (this.fastStart === "in-memory") { } else if (this.fastStart === "reserve") { for (const track of this.output._tracks) { if (track.metadata.maximumPacketCount === void 0) { throw new Error("All tracks must specify maximumPacketCount in their metadata when using fastStart: 'reserve'."); } } } else if (this.isFragmented) { } else { assert(this.writer); assert(this.boxWriter); if (this.format._options.onMdat) { this.writer.startTrackingWrites(); } this.mdat = mdat(true); this.boxWriter.writeBox(this.mdat); } await this.writer?.flush(); release(); } allTracksAreKnown() { for (const track of this.output._tracks) { if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) { return false; } } return true; } async getMimeType() { await this.allTracksKnown.promise; const codecStrings = this.trackDatas.map((trackData) => { if (trackData.type === "video") { return trackData.info.decoderConfig.codec; } else if (trackData.type === "audio") { return trackData.info.decoderConfig.codec; } else { const map = { webvtt: "wvtt" }; return map[trackData.track.source._codec]; } }); return buildIsobmffMimeType({ isQuickTime: this.isQuickTime, hasVideo: this.trackDatas.some((x) => x.type === "video"), hasAudio: this.trackDatas.some((x) => x.type === "audio"), codecStrings }); } getVideoTrackData(track, packet, meta) { const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } validateVideoChunkMetadata(meta); assert(meta); assert(meta.decoderConfig); const decoderConfig = { ...meta.decoderConfig }; assert(decoderConfig.codedWidth !== void 0); assert(decoderConfig.codedHeight !== void 0); let requiresAnnexBTransformation = false; if (track.source._codec === "avc" && !decoderConfig.description) { const decoderConfigurationRecord = extractAvcDecoderConfigurationRecord(packet.data); if (!decoderConfigurationRecord) { throw new Error("Couldn't extract an AVCDecoderConfigurationRecord from the AVC packet. Make sure the packets are in Annex B format (as specified in ITU-T-REC-H.264) when not providing a description, or provide a description (must be an AVCDecoderConfigurationRecord as specified in ISO 14496-15) and ensure the packets are in AVCC format."); } decoderConfig.description = serializeAvcDecoderConfigurationRecord(decoderConfigurationRecord); requiresAnnexBTransformation = true; } else if (track.source._codec === "hevc" && !decoderConfig.description) { const decoderConfigurationRecord = extractHevcDecoderConfigurationRecord(packet.data); if (!decoderConfigurationRecord) { throw new Error("Couldn't extract an HEVCDecoderConfigurationRecord from the HEVC packet. Make sure the packets are in Annex B format (as specified in ITU-T-REC-H.265) when not providing a description, or provide a description (must be an HEVCDecoderConfigurationRecord as specified in ISO 14496-15) and ensure the packets are in HEVC format."); } decoderConfig.description = serializeHevcDecoderConfigurationRecord(decoderConfigurationRecord); requiresAnnexBTransformation = true; } const timescale = computeRationalApproximation(1 / (track.metadata.frameRate ?? GLOBAL_TIMESCALE), 1e6).den; const displayAspectWidth = decoderConfig.displayAspectWidth; const displayAspectHeight = decoderConfig.displayAspectHeight; const pixelAspectRatio = displayAspectWidth === void 0 || displayAspectHeight === void 0 ? { num: 1, den: 1 } : simplifyRational({ num: displayAspectWidth * decoderConfig.codedHeight, den: displayAspectHeight * decoderConfig.codedWidth }); const newTrackData = { muxer: this, track, type: "video", info: { width: decoderConfig.codedWidth, height: decoderConfig.codedHeight, pixelAspectRatio, decoderConfig, requiresAnnexBTransformation }, timescale, samples: [], sampleQueue: [], timestampProcessingQueue: [], timeToSampleTable: [], compositionTimeOffsetTable: [], lastTimescaleUnits: null, lastSample: null, startTimestampOffset: null, finalizedChunks: [], currentChunk: null, compactlyCodedChunkTable: [], closed: false }; this.trackDatas.push(newTrackData); this.trackDatas.sort((a2, b) => a2.track.id - b.track.id); if (this.allTracksAreKnown()) { this.allTracksKnown.resolve(); } return newTrackData; } getAudioTrackData(track, packet, meta) { const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } validateAudioChunkMetadata(meta); assert(meta); assert(meta.decoderConfig); const decoderConfig = { ...meta.decoderConfig }; let requiresAdtsStripping = false; if (track.source._codec === "aac" && !decoderConfig.description) { const adtsFrame = readAdtsFrameHeader(FileSlice.tempFromBytes(packet.data)); if (!adtsFrame) { throw new Error("Couldn't parse ADTS header from the AAC packet. Make sure the packets are in ADTS format (as specified in ISO 13818-7) when not providing a description, or provide a description (must be an AudioSpecificConfig as specified in ISO 14496-3) and ensure the packets are raw AAC data."); } const sampleRate = aacFrequencyTable[adtsFrame.samplingFrequencyIndex]; const numberOfChannels = aacChannelMap[adtsFrame.channelConfiguration]; if (sampleRate === void 0 || numberOfChannels === void 0) { throw new Error("Invalid ADTS frame header."); } decoderConfig.description = buildAacAudioSpecificConfig({ objectType: adtsFrame.objectType, sampleRate, numberOfChannels }); requiresAdtsStripping = true; } const newTrackData = { muxer: this, track, type: "audio", info: { numberOfChannels: meta.decoderConfig.numberOfChannels, sampleRate: meta.decoderConfig.sampleRate, decoderConfig, requiresPcmTransformation: !this.isFragmented && PCM_AUDIO_CODECS.includes(track.source._codec), expectedNextPcmPacketTimestamp: null, requiresAdtsStripping, firstPacket: packet }, timescale: decoderConfig.sampleRate, samples: [], sampleQueue: [], timestampProcessingQueue: [], timeToSampleTable: [], compositionTimeOffsetTable: [], lastTimescaleUnits: null, lastSample: null, startTimestampOffset: null, finalizedChunks: [], currentChunk: null, compactlyCodedChunkTable: [], closed: false }; this.trackDatas.push(newTrackData); this.trackDatas.sort((a2, b) => a2.track.id - b.track.id); if (this.allTracksAreKnown()) { this.allTracksKnown.resolve(); } return newTrackData; } getSubtitleTrackData(track, meta) { const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } validateSubtitleMetadata(meta); assert(meta); assert(meta.config); const newTrackData = { muxer: this, track, type: "subtitle", info: { config: meta.config }, timescale: 1e3, // Reasonable samples: [], sampleQueue: [], timestampProcessingQueue: [], timeToSampleTable: [], compositionTimeOffsetTable: [], lastTimescaleUnits: null, lastSample: null, startTimestampOffset: null, finalizedChunks: [], currentChunk: null, compactlyCodedChunkTable: [], closed: false, lastCueEndTimestamp: 0, cueQueue: [], nextSourceId: 0, cueToSourceId: /* @__PURE__ */ new WeakMap() }; this.trackDatas.push(newTrackData); this.trackDatas.sort((a2, b) => a2.track.id - b.track.id); if (this.allTracksAreKnown()) { this.allTracksKnown.resolve(); } return newTrackData; } async addEncodedVideoPacket(track, packet, meta) { const release = await this.mutex.acquire(); try { const trackData = this.getVideoTrackData(track, packet, meta); let packetData = packet.data; if (trackData.info.requiresAnnexBTransformation) { const nalUnits = [...iterateNalUnitsInAnnexB(packetData)].map((loc) => packetData.subarray(loc.offset, loc.offset + loc.length)); if (nalUnits.length === 0) { throw new Error("Failed to transform packet data. Make sure all packets are provided in Annex B format, as specified in ITU-T-REC-H.264 and ITU-T-REC-H.265."); } packetData = concatNalUnitsInLengthPrefixed(nalUnits, 4); } this.validateTimestamp(trackData.track, packet.timestamp, packet.type === "key"); const internalSample = this.createSampleForTrack(trackData, packetData, packet.timestamp, packet.duration, packet.type); await this.registerSample(trackData, internalSample); } finally { release(); } } async addEncodedAudioPacket(track, packet, meta) { const release = await this.mutex.acquire(); try { const trackData = this.getAudioTrackData(track, packet, meta); let packetData = packet.data; if (trackData.info.requiresAdtsStripping) { const adtsFrame = readAdtsFrameHeader(FileSlice.tempFromBytes(packetData)); if (!adtsFrame) { throw new Error("Expected ADTS frame, didn't get one."); } const headerLength = adtsFrame.crcCheck === null ? MIN_ADTS_FRAME_HEADER_SIZE : MAX_ADTS_FRAME_HEADER_SIZE; packetData = packetData.subarray(headerLength); } this.validateTimestamp(trackData.track, packet.timestamp, packet.type === "key"); let timestamp = packet.timestamp; let duration = packet.duration; if (trackData.info.requiresPcmTransformation) { const pcmInfo = parsePcmCodec(trackData.info.decoderConfig.codec); const frameSize = pcmInfo.sampleSize * trackData.info.numberOfChannels; duration = packetData.byteLength / frameSize / trackData.info.sampleRate; if (trackData.info.expectedNextPcmPacketTimestamp !== null) { const diff = timestamp - trackData.info.expectedNextPcmPacketTimestamp; if (diff < 0.01) { timestamp = trackData.info.expectedNextPcmPacketTimestamp; } else { const paddedDuration = await this.padWithSilence(trackData, trackData.info.expectedNextPcmPacketTimestamp, diff); timestamp = trackData.info.expectedNextPcmPacketTimestamp + paddedDuration; } } trackData.info.expectedNextPcmPacketTimestamp = timestamp + duration; } const internalSample = this.createSampleForTrack(trackData, packetData, timestamp, duration, packet.type); await this.registerSample(trackData, internalSample); } finally { release(); } } async padWithSilence(trackData, timestamp, duration) { const deltaInTimescale = intoTimescale(duration, trackData.timescale); duration = deltaInTimescale / trackData.timescale; if (deltaInTimescale > 0) { const { sampleSize, silentValue } = parsePcmCodec(trackData.info.decoderConfig.codec); const samplesNeeded = deltaInTimescale * trackData.info.numberOfChannels; const data = new Uint8Array(sampleSize * samplesNeeded).fill(silentValue); const paddingSample = this.createSampleForTrack(trackData, new Uint8Array(data.buffer), timestamp, duration, "key"); await this.registerSample(trackData, paddingSample); } return duration; } async addSubtitleCue(track, cue, meta) { const release = await this.mutex.acquire(); try { const trackData = this.getSubtitleTrackData(track, meta); this.validateTimestamp(trackData.track, cue.timestamp, true); if (track.source._codec === "webvtt") { trackData.cueQueue.push(cue); await this.processWebVTTCues(trackData, cue.timestamp); } else { } } finally { release(); } } async processWebVTTCues(trackData, until) { while (trackData.cueQueue.length > 0) { const timestamps = /* @__PURE__ */ new Set([]); for (const cue of trackData.cueQueue) { assert(cue.timestamp <= until); assert(trackData.lastCueEndTimestamp <= cue.timestamp + cue.duration); timestamps.add(Math.max(cue.timestamp, trackData.lastCueEndTimestamp)); timestamps.add(cue.timestamp + cue.duration); } const sortedTimestamps = [...timestamps].sort((a2, b) => a2 - b); const sampleStart = sortedTimestamps[0]; const sampleEnd = sortedTimestamps[1] ?? sampleStart; if (until < sampleEnd) { break; } if (trackData.lastCueEndTimestamp < sampleStart) { this.auxWriter.seek(0); const box2 = vtte(); this.auxBoxWriter.writeBox(box2); const body2 = this.auxTarget._getSlice(0, this.auxWriter.getPos()); const sample2 = this.createSampleForTrack(trackData, body2, trackData.lastCueEndTimestamp, sampleStart - trackData.lastCueEndTimestamp, "key"); await this.registerSample(trackData, sample2); trackData.lastCueEndTimestamp = sampleStart; } this.auxWriter.seek(0); for (let i2 = 0; i2 < trackData.cueQueue.length; i2++) { const cue = trackData.cueQueue[i2]; if (cue.timestamp >= sampleEnd) { break; } inlineTimestampRegex.lastIndex = 0; const containsTimestamp = inlineTimestampRegex.test(cue.text); const endTimestamp = cue.timestamp + cue.duration; let sourceId = trackData.cueToSourceId.get(cue); if (sourceId === void 0 && sampleEnd < endTimestamp) { sourceId = trackData.nextSourceId++; trackData.cueToSourceId.set(cue, sourceId); } if (cue.notes) { const box3 = vtta(cue.notes); this.auxBoxWriter.writeBox(box3); } const box2 = vttc(cue.text, containsTimestamp ? sampleStart : null, cue.identifier ?? null, cue.settings ?? null, sourceId ?? null); this.auxBoxWriter.writeBox(box2); if (endTimestamp === sampleEnd) { trackData.cueQueue.splice(i2--, 1); } } const body = this.auxTarget._getSlice(0, this.auxWriter.getPos()); const sample = this.createSampleForTrack(trackData, body, sampleStart, sampleEnd - sampleStart, "key"); await this.registerSample(trackData, sample); trackData.lastCueEndTimestamp = sampleEnd; } } createSampleForTrack(trackData, data, timestamp, duration, type) { const sample = { timestamp, decodeTimestamp: timestamp, // This may be refined later duration, data, size: data.byteLength, type, timescaleUnitsToNextSample: intoTimescale(duration, trackData.timescale) // Will be refined }; return sample; } processTimestamps(trackData, nextSample) { if (trackData.timestampProcessingQueue.length === 0) { return; } if (trackData.type === "audio" && trackData.info.requiresPcmTransformation) { if (!this.isFragmented) { trackData.startTimestampOffset ??= trackData.timestampProcessingQueue[0].timestamp; } let totalDuration = 0; for (let i2 = 0; i2 < trackData.timestampProcessingQueue.length; i2++) { const sample = trackData.timestampProcessingQueue[i2]; const duration = intoTimescale(sample.duration, trackData.timescale); totalDuration += duration; } if (trackData.timeToSampleTable.length === 0) { trackData.timeToSampleTable.push({ sampleCount: totalDuration, sampleDelta: 1 }); } else { const lastEntry = last(trackData.timeToSampleTable); lastEntry.sampleCount += totalDuration; } trackData.timestampProcessingQueue.length = 0; return; } const sortedTimestamps = trackData.timestampProcessingQueue.map((x) => x.timestamp).sort((a2, b) => a2 - b); if (!this.isFragmented) { trackData.startTimestampOffset ??= sortedTimestamps[0]; } for (let i2 = 0; i2 < trackData.timestampProcessingQueue.length; i2++) { const sample = trackData.timestampProcessingQueue[i2]; sample.decodeTimestamp = sortedTimestamps[i2]; const sampleCompositionTimeOffset = intoTimescale(sample.timestamp - sample.decodeTimestamp, trackData.timescale); const durationInTimescale = intoTimescale(sample.duration, trackData.timescale); if (trackData.lastTimescaleUnits !== null) { assert(trackData.lastSample); const timescaleUnits = intoTimescale(sample.decodeTimestamp, trackData.timescale, false); const delta = Math.round(timescaleUnits - trackData.lastTimescaleUnits); assert(delta >= 0); trackData.lastTimescaleUnits += delta; trackData.lastSample.timescaleUnitsToNextSample = delta; if (!this.isFragmented) { let lastTableEntry = last(trackData.timeToSampleTable); assert(lastTableEntry); if (lastTableEntry.sampleCount === 1) { lastTableEntry.sampleDelta = delta; const entryBefore = trackData.timeToSampleTable[trackData.timeToSampleTable.length - 2]; if (entryBefore && entryBefore.sampleDelta === delta) { entryBefore.sampleCount++; trackData.timeToSampleTable.pop(); lastTableEntry = entryBefore; } } else if (lastTableEntry.sampleDelta !== delta) { lastTableEntry.sampleCount--; trackData.timeToSampleTable.push(lastTableEntry = { sampleCount: 1, sampleDelta: delta }); } if (lastTableEntry.sampleDelta === durationInTimescale) { lastTableEntry.sampleCount++; } else { trackData.timeToSampleTable.push({ sampleCount: 1, sampleDelta: durationInTimescale }); } const lastCompositionTimeOffsetTableEntry = last(trackData.compositionTimeOffsetTable); assert(lastCompositionTimeOffsetTableEntry); if (lastCompositionTimeOffsetTableEntry.sampleCompositionTimeOffset === sampleCompositionTimeOffset) { lastCompositionTimeOffsetTableEntry.sampleCount++; } else { trackData.compositionTimeOffsetTable.push({ sampleCount: 1, sampleCompositionTimeOffset }); } } } else { trackData.lastTimescaleUnits = intoTimescale(sample.decodeTimestamp, trackData.timescale, false); if (!this.isFragmented) { trackData.timeToSampleTable.push({ sampleCount: 1, sampleDelta: durationInTimescale }); trackData.compositionTimeOffsetTable.push({ sampleCount: 1, sampleCompositionTimeOffset }); } } trackData.lastSample = sample; } trackData.timestampProcessingQueue.length = 0; assert(trackData.lastSample); assert(trackData.lastTimescaleUnits !== null); if (nextSample !== void 0 && trackData.lastSample.timescaleUnitsToNextSample === 0) { assert(nextSample.type === "key"); const timescaleUnits = intoTimescale(nextSample.timestamp, trackData.timescale, false); const delta = Math.round(timescaleUnits - trackData.lastTimescaleUnits); trackData.lastSample.timescaleUnitsToNextSample = delta; } } async registerSample(trackData, sample) { if (sample.type === "key") { this.processTimestamps(trackData, sample); } trackData.timestampProcessingQueue.push(sample); if (this.isFragmented) { trackData.sampleQueue.push(sample); await this.interleaveSamples(); } else if (this.fastStart === "reserve") { await this.registerSampleFastStartReserve(trackData, sample); } else { await this.addSampleToTrack(trackData, sample); } } async addSampleToTrack(trackData, sample) { if (!this.isFragmented) { trackData.samples.push(sample); if (this.fastStart === "reserve") { const maximumPacketCount = trackData.track.metadata.maximumPacketCount; assert(maximumPacketCount !== void 0); if (trackData.samples.length > maximumPacketCount) { throw new Error(\`Track #\${trackData.track.id} has already reached the maximum packet count (\${maximumPacketCount}). Either add less packets or increase the maximum packet count.\`); } } } let beginNewChunk = false; if (!trackData.currentChunk) { beginNewChunk = true; } else { trackData.currentChunk.startTimestamp = Math.min(trackData.currentChunk.startTimestamp, sample.timestamp); const currentChunkDuration = sample.timestamp - trackData.currentChunk.startTimestamp; if (this.isFragmented) { const keyFrameQueuedEverywhere = this.trackDatas.every((otherTrackData) => { if (trackData === otherTrackData) { return sample.type === "key"; } const firstQueuedSample = otherTrackData.sampleQueue[0]; if (firstQueuedSample) { return firstQueuedSample.type === "key"; } return otherTrackData.closed; }); if (currentChunkDuration >= this.minimumFragmentDuration && keyFrameQueuedEverywhere && sample.timestamp > this.maxWrittenTimestamp) { beginNewChunk = true; await this.finalizeFragment(); } } else { beginNewChunk = currentChunkDuration >= 0.5; } } if (beginNewChunk) { if (trackData.currentChunk) { await this.finalizeCurrentChunk(trackData); } trackData.currentChunk = { startTimestamp: sample.timestamp, samples: [], offset: null, moofOffset: null }; } assert(trackData.currentChunk); trackData.currentChunk.samples.push(sample); if (this.isFragmented) { this.maxWrittenTimestamp = Math.max(this.maxWrittenTimestamp, sample.timestamp); this.maxWrittenEndTimestamp = Math.max(this.maxWrittenEndTimestamp, sample.timestamp + sample.duration); this.minWrittenTimestamp = Math.min(this.minWrittenTimestamp, sample.timestamp); } } async finalizeCurrentChunk(trackData) { assert(!this.isFragmented); assert(this.writer); if (!trackData.currentChunk) return; trackData.finalizedChunks.push(trackData.currentChunk); this.finalizedChunks.push(trackData.currentChunk); let sampleCount = trackData.currentChunk.samples.length; if (trackData.type === "audio" && trackData.info.requiresPcmTransformation) { sampleCount = trackData.currentChunk.samples.reduce((acc, sample) => acc + intoTimescale(sample.duration, trackData.timescale), 0); } if (trackData.compactlyCodedChunkTable.length === 0 || last(trackData.compactlyCodedChunkTable).samplesPerChunk !== sampleCount) { trackData.compactlyCodedChunkTable.push({ firstChunk: trackData.finalizedChunks.length, // 1-indexed samplesPerChunk: sampleCount }); } if (this.fastStart === "in-memory") { trackData.currentChunk.offset = 0; return; } trackData.currentChunk.offset = this.writer.getPos(); for (const sample of trackData.currentChunk.samples) { assert(sample.data); this.writer.write(sample.data); sample.data = null; } await this.writer.flush(); } async interleaveSamples(isFinalCall = false) { assert(this.isFragmented); if (!isFinalCall && !this.allTracksAreKnown()) { return; } outer: while (true) { let trackWithMinTimestamp = null; let minTimestamp = Infinity; for (const trackData of this.trackDatas) { if (!isFinalCall && trackData.sampleQueue.length === 0 && !trackData.closed) { break outer; } if (trackData.sampleQueue.length > 0 && trackData.sampleQueue[0].timestamp < minTimestamp) { trackWithMinTimestamp = trackData; minTimestamp = trackData.sampleQueue[0].timestamp; } } if (!trackWithMinTimestamp) { break; } const sample = trackWithMinTimestamp.sampleQueue.shift(); await this.addSampleToTrack(trackWithMinTimestamp, sample); } } async finalizeFragment(flushWriter = !this.isCmaf) { assert(this.isFragmented); const fragmentNumber = this.nextFragmentNumber++; if (fragmentNumber === 1) { const boxWriter = this.initBoxWriter ?? this.boxWriter; assert(boxWriter); if (this.format._options.onMoov) { boxWriter.writer.startTrackingWrites(); } this.ensureOneEnabledTrack(); const movieBox = moov(this); boxWriter.writeBox(movieBox); if (this.format._options.onMoov) { const { data, start } = boxWriter.writer.stopTrackingWrites(); this.format._options.onMoov(data, start); } if (this.isCmaf) { assert(this.initWriter); await this.initWriter.flush(); await this.initWriter.finalize(); this.writer = await this.output._getRootWriter(true); this.boxWriter = new IsobmffBoxWriter(this.writer); const stypSize = this.boxWriter.measureBox(styp()); const sidxSize = this.boxWriter.measureBox(sidx(this, 0)); this.segmentHeaderSize = stypSize + sidxSize; this.writer.seek(this.segmentHeaderSize); } } assert(this.writer); assert(this.boxWriter); const tracksInFragment = this.trackDatas.filter((x) => x.currentChunk); const moofBox = moof(fragmentNumber, tracksInFragment); const moofOffset = this.writer.getPos(); const mdatStartPos = moofOffset + this.boxWriter.measureBox(moofBox); let currentPos = mdatStartPos + MIN_BOX_HEADER_SIZE; let fragmentStartTimestamp = Infinity; for (const trackData of tracksInFragment) { trackData.currentChunk.offset = currentPos; trackData.currentChunk.moofOffset = moofOffset; for (const sample of trackData.currentChunk.samples) { currentPos += sample.size; } fragmentStartTimestamp = Math.min(fragmentStartTimestamp, trackData.currentChunk.startTimestamp); } const mdatSize = currentPos - mdatStartPos; const needsLargeMdatSize = mdatSize >= 2 ** 32; if (needsLargeMdatSize) { for (const trackData of tracksInFragment) { trackData.currentChunk.offset += MAX_BOX_HEADER_SIZE - MIN_BOX_HEADER_SIZE; } } if (this.format._options.onMoof) { this.writer.startTrackingWrites(); } const newMoofBox = moof(fragmentNumber, tracksInFragment); this.boxWriter.writeBox(newMoofBox); if (this.format._options.onMoof) { const { data, start } = this.writer.stopTrackingWrites(); this.format._options.onMoof(data, start, fragmentStartTimestamp); } assert(this.writer.getPos() === mdatStartPos); if (this.format._options.onMdat) { this.writer.startTrackingWrites(); } const mdatBox = mdat(needsLargeMdatSize); mdatBox.size = mdatSize; this.boxWriter.writeBox(mdatBox); this.writer.seek(mdatStartPos + (needsLargeMdatSize ? MAX_BOX_HEADER_SIZE : MIN_BOX_HEADER_SIZE)); for (const trackData of tracksInFragment) { for (const sample of trackData.currentChunk.samples) { this.writer.write(sample.data); sample.data = null; } } if (this.format._options.onMdat) { const { data, start } = this.writer.stopTrackingWrites(); this.format._options.onMdat(data, start); } for (const trackData of tracksInFragment) { trackData.finalizedChunks.push(trackData.currentChunk); this.finalizedChunks.push(trackData.currentChunk); trackData.currentChunk = null; } if (flushWriter) { await this.writer.flush(); } } async registerSampleFastStartReserve(trackData, sample) { assert(this.writer); assert(this.boxWriter); if (this.allTracksAreKnown()) { if (!this.mdat) { this.ensureOneEnabledTrack(); const moovBox = moov(this); const moovSize = this.boxWriter.measureBox(moovBox); const reservedSize = moovSize + this.computeSampleTableSizeUpperBound() + 4096; assert(this.ftypSize !== null); this.writer.seek(this.ftypSize + reservedSize); if (this.format._options.onMdat) { this.writer.startTrackingWrites(); } this.mdat = mdat(true); this.boxWriter.writeBox(this.mdat); for (const trackData2 of this.trackDatas) { for (const sample2 of trackData2.sampleQueue) { await this.addSampleToTrack(trackData2, sample2); } trackData2.sampleQueue.length = 0; } } await this.addSampleToTrack(trackData, sample); } else { trackData.sampleQueue.push(sample); } } computeSampleTableSizeUpperBound() { assert(this.fastStart === "reserve"); let upperBound = 0; for (const trackData of this.trackDatas) { const n2 = trackData.track.metadata.maximumPacketCount; assert(n2 !== void 0); upperBound += (4 + 4) * Math.ceil(2 / 3 * n2); upperBound += 4 * n2; upperBound += (4 + 4) * Math.ceil(2 / 3 * n2); upperBound += (4 + 4 + 4) * Math.ceil(2 / 3 * n2); upperBound += 4 * n2; upperBound += 8 * n2; } return upperBound; } // eslint-disable-next-line @typescript-eslint/no-misused-promises async onTrackClose(track) { const release = await this.mutex.acquire(); const trackData = this.trackDatas.find((x) => x.track === track); if (trackData) { trackData.closed = true; if (trackData.type === "subtitle" && track.source._codec === "webvtt") { await this.processWebVTTCues(trackData, Infinity); } this.processTimestamps(trackData); } if (this.allTracksAreKnown()) { this.allTracksKnown.resolve(); } if (this.isFragmented) { await this.interleaveSamples(); } release(); } ensureOneEnabledTrack() { for (const type of ["video", "audio", "subtitle"]) { const tracks = this.trackDatas.filter((t2) => t2.type === type); if (tracks.length === 0) { continue; } const hasEnabled = tracks.some((t2) => t2.track.metadata.disposition?.default !== false); if (!hasEnabled) { const firstTrack = tracks[0]; firstTrack.track.metadata.disposition = { ...firstTrack.track.metadata.disposition, default: true }; } } } /** Finalizes the file, making it ready for use. Must be called after all video and audio chunks have been added. */ async finalize() { const release = await this.mutex.acquire(); this.allTracksKnown.resolve(); this.ensureOneEnabledTrack(); for (const trackData of this.trackDatas) { trackData.closed = true; if (trackData.type === "subtitle" && trackData.track.source._codec === "webvtt") { await this.processWebVTTCues(trackData, Infinity); } this.processTimestamps(trackData); } if (this.isFragmented) { await this.interleaveSamples(true); await this.finalizeFragment(false); } else { for (const trackData of this.trackDatas) { await this.finalizeCurrentChunk(trackData); assert(trackData.startTimestampOffset !== null); for (let i2 = 0; i2 < trackData.samples.length; i2++) { const sample = trackData.samples[i2]; sample.timestamp -= trackData.startTimestampOffset; sample.decodeTimestamp -= trackData.startTimestampOffset; } } } assert(this.writer); assert(this.boxWriter); if (this.fastStart === "in-memory") { this.mdat = mdat(false); let mdatSize; for (let i2 = 0; i2 < 2; i2++) { const movieBox2 = moov(this); const movieBoxSize = this.boxWriter.measureBox(movieBox2); mdatSize = this.boxWriter.measureBox(this.mdat); let currentChunkPos = this.writer.getPos() + movieBoxSize + mdatSize; for (const chunk of this.finalizedChunks) { chunk.offset = currentChunkPos; for (const { data } of chunk.samples) { assert(data); currentChunkPos += data.byteLength; mdatSize += data.byteLength; } } if (currentChunkPos < 2 ** 32) break; if (mdatSize >= 2 ** 32) this.mdat.largeSize = true; } if (this.format._options.onMoov) { this.writer.startTrackingWrites(); } const movieBox = moov(this); this.boxWriter.writeBox(movieBox); if (this.format._options.onMoov) { const { data, start } = this.writer.stopTrackingWrites(); this.format._options.onMoov(data, start); } if (this.format._options.onMdat) { this.writer.startTrackingWrites(); } this.mdat.size = mdatSize; this.boxWriter.writeBox(this.mdat); for (const chunk of this.finalizedChunks) { for (const sample of chunk.samples) { assert(sample.data); this.writer.write(sample.data); sample.data = null; } } if (this.format._options.onMdat) { const { data, start } = this.writer.stopTrackingWrites(); this.format._options.onMdat(data, start); } } else if (this.isFragmented) { if (this.isCmaf) { const contentSize = this.segmentHeaderSize !== null ? this.writer.getPos() - this.segmentHeaderSize : 0; this.writer.seek(0); this.boxWriter.writeBox(styp()); this.boxWriter.writeBox(sidx(this, contentSize)); } else { const startPos = this.writer.getPos(); const mfraBox = mfra(this.trackDatas); this.boxWriter.writeBox(mfraBox); const mfraBoxSize = this.writer.getPos() - startPos; this.writer.seek(this.writer.getPos() - 4); this.boxWriter.writeU32(mfraBoxSize); } } else { assert(this.mdat); const mdatPos = this.boxWriter.offsets.get(this.mdat); assert(mdatPos !== void 0); const mdatSize = this.writer.getPos() - mdatPos; this.mdat.size = mdatSize; this.mdat.largeSize = mdatSize >= 2 ** 32; this.boxWriter.patchBox(this.mdat); if (this.format._options.onMdat) { const { data, start } = this.writer.stopTrackingWrites(); this.format._options.onMdat(data, start); } const movieBox = moov(this); if (this.fastStart === "reserve") { assert(this.ftypSize !== null); this.writer.seek(this.ftypSize); if (this.format._options.onMoov) { this.writer.startTrackingWrites(); } this.boxWriter.writeBox(movieBox); const remainingSpace = this.boxWriter.offsets.get(this.mdat) - this.writer.getPos(); this.boxWriter.writeBox(free(remainingSpace)); } else { if (this.format._options.onMoov) { this.writer.startTrackingWrites(); } this.boxWriter.writeBox(movieBox); } if (this.format._options.onMoov) { const { data, start } = this.writer.stopTrackingWrites(); this.format._options.onMoov(data, start); } } release(); } }; // node_modules/mediabunny/dist/modules/src/matroska/matroska-muxer.js var MIN_CLUSTER_TIMESTAMP_MS = -(2 ** 15); var MAX_CLUSTER_TIMESTAMP_MS = 2 ** 15 - 1; var APP_NAME = "Mediabunny"; var SEGMENT_SIZE_BYTES = 6; var CLUSTER_SIZE_BYTES = 5; var TRACK_TYPE_MAP = { video: 1, audio: 2, subtitle: 17 }; var MatroskaMuxer = class extends Muxer { constructor(output, format) { super(output); this.trackDatas = []; this.allTracksKnown = promiseWithResolvers(); this.segment = null; this.segmentInfo = null; this.seekHead = null; this.tracksElement = null; this.tagsElement = null; this.attachmentsElement = null; this.segmentDuration = null; this.cues = null; this.currentCluster = null; this.currentClusterStartMsTimestamp = null; this.currentClusterMaxMsTimestamp = null; this.trackDatasInCurrentCluster = /* @__PURE__ */ new Map(); this.startTimestamp = Infinity; this.endTimestamp = -Infinity; this.format = format; } async start() { const release = await this.mutex.acquire(); this.writer = await this.output._getRootWriter(!!this.format._options.appendOnly); this.ebmlWriter = new EBMLWriter(this.writer); this.writeEBMLHeader(); this.createSegmentInfo(); this.createCues(); await this.writer.flush(); release(); } writeEBMLHeader() { if (this.format._options.onEbmlHeader) { this.writer.startTrackingWrites(); } const ebmlHeader = { id: EBMLId.EBML, data: [ { id: EBMLId.EBMLVersion, data: 1 }, { id: EBMLId.EBMLReadVersion, data: 1 }, { id: EBMLId.EBMLMaxIDLength, data: 4 }, { id: EBMLId.EBMLMaxSizeLength, data: 8 }, { id: EBMLId.DocType, data: this.format instanceof WebMOutputFormat ? "webm" : "matroska" }, { id: EBMLId.DocTypeVersion, data: 2 }, { id: EBMLId.DocTypeReadVersion, data: 2 } ] }; this.ebmlWriter.writeEBML(ebmlHeader); if (this.format._options.onEbmlHeader) { const { data, start } = this.writer.stopTrackingWrites(); this.format._options.onEbmlHeader(data, start); } } /** * Creates a SeekHead element which is positioned near the start of the file and allows the media player to seek to * relevant sections more easily. Since we don't know the positions of those sections yet, we'll set them later. */ maybeCreateSeekHead(writeOffsets) { if (this.format._options.appendOnly) { return; } const kaxCues = new Uint8Array([28, 83, 187, 107]); const kaxInfo = new Uint8Array([21, 73, 169, 102]); const kaxTracks = new Uint8Array([22, 84, 174, 107]); const kaxAttachments = new Uint8Array([25, 65, 164, 105]); const kaxTags = new Uint8Array([18, 84, 195, 103]); const seekHead = { id: EBMLId.SeekHead, data: [ { id: EBMLId.Seek, data: [ { id: EBMLId.SeekID, data: kaxCues }, { id: EBMLId.SeekPosition, size: 5, data: writeOffsets ? this.ebmlWriter.offsets.get(this.cues) - this.segmentDataOffset : 0 } ] }, { id: EBMLId.Seek, data: [ { id: EBMLId.SeekID, data: kaxInfo }, { id: EBMLId.SeekPosition, size: 5, data: writeOffsets ? this.ebmlWriter.offsets.get(this.segmentInfo) - this.segmentDataOffset : 0 } ] }, { id: EBMLId.Seek, data: [ { id: EBMLId.SeekID, data: kaxTracks }, { id: EBMLId.SeekPosition, size: 5, data: writeOffsets ? this.ebmlWriter.offsets.get(this.tracksElement) - this.segmentDataOffset : 0 } ] }, this.attachmentsElement ? { id: EBMLId.Seek, data: [ { id: EBMLId.SeekID, data: kaxAttachments }, { id: EBMLId.SeekPosition, size: 5, data: writeOffsets ? this.ebmlWriter.offsets.get(this.attachmentsElement) - this.segmentDataOffset : 0 } ] } : null, this.tagsElement ? { id: EBMLId.Seek, data: [ { id: EBMLId.SeekID, data: kaxTags }, { id: EBMLId.SeekPosition, size: 5, data: writeOffsets ? this.ebmlWriter.offsets.get(this.tagsElement) - this.segmentDataOffset : 0 } ] } : null ] }; this.seekHead = seekHead; } createSegmentInfo() { const segmentDuration = { id: EBMLId.Duration, data: new EBMLFloat64(0) }; this.segmentDuration = segmentDuration; const segmentInfo = { id: EBMLId.Info, data: [ { id: EBMLId.TimestampScale, data: 1e6 }, { id: EBMLId.MuxingApp, data: APP_NAME }, { id: EBMLId.WritingApp, data: APP_NAME }, !this.format._options.appendOnly ? segmentDuration : null ] }; this.segmentInfo = segmentInfo; } createTracks() { const tracksElement = { id: EBMLId.Tracks, data: [] }; this.tracksElement = tracksElement; for (const trackData of this.trackDatas) { const codecId = CODEC_STRING_MAP[trackData.track.source._codec]; assert(codecId); let seekPreRollNs = 0; if (trackData.type === "audio" && trackData.track.source._codec === "opus") { seekPreRollNs = 1e6 * 80; const description = trackData.info.decoderConfig.description; if (description) { const bytes2 = toUint8Array(description); const header = parseOpusIdentificationHeader(bytes2); seekPreRollNs = Math.round(1e9 * (header.preSkip / OPUS_SAMPLE_RATE)); } } tracksElement.data.push({ id: EBMLId.TrackEntry, data: [ { id: EBMLId.TrackNumber, data: trackData.track.id }, { id: EBMLId.TrackUID, data: trackData.track.id }, { id: EBMLId.TrackType, data: TRACK_TYPE_MAP[trackData.type] }, trackData.track.metadata.disposition?.default === false ? { id: EBMLId.FlagDefault, data: 0 } : null, trackData.track.metadata.disposition?.forced ? { id: EBMLId.FlagForced, data: 1 } : null, trackData.track.metadata.disposition?.hearingImpaired ? { id: EBMLId.FlagHearingImpaired, data: 1 } : null, trackData.track.metadata.disposition?.visuallyImpaired ? { id: EBMLId.FlagVisualImpaired, data: 1 } : null, trackData.track.metadata.disposition?.original ? { id: EBMLId.FlagOriginal, data: 1 } : null, trackData.track.metadata.disposition?.commentary ? { id: EBMLId.FlagCommentary, data: 1 } : null, { id: EBMLId.FlagLacing, data: 0 }, { id: EBMLId.Language, data: trackData.track.metadata.languageCode ?? UNDETERMINED_LANGUAGE }, { id: EBMLId.CodecID, data: codecId }, { id: EBMLId.CodecDelay, data: 0 }, { id: EBMLId.SeekPreRoll, data: seekPreRollNs }, trackData.track.metadata.name !== void 0 ? { id: EBMLId.Name, data: new EBMLUnicodeString(trackData.track.metadata.name) } : null, trackData.type === "video" ? this.videoSpecificTrackInfo(trackData) : null, trackData.type === "audio" ? this.audioSpecificTrackInfo(trackData) : null, trackData.type === "subtitle" ? this.subtitleSpecificTrackInfo(trackData) : null ] }); } } videoSpecificTrackInfo(trackData) { const { frameRate, rotation } = trackData.track.metadata; const elements = [ trackData.info.decoderConfig.description ? { id: EBMLId.CodecPrivate, data: toUint8Array(trackData.info.decoderConfig.description) } : null, frameRate ? { id: EBMLId.DefaultDuration, data: 1e9 / frameRate } : null ]; const flippedRotation = rotation ? normalizeRotation(-rotation) : 0; const hasNonSquarePixelAspectRatio = !!trackData.info.aspectRatio && trackData.info.aspectRatio.num * trackData.info.height !== trackData.info.aspectRatio.den * trackData.info.width; const colorSpace = trackData.info.decoderConfig.colorSpace; const videoElement = { id: EBMLId.Video, data: [ { id: EBMLId.PixelWidth, data: trackData.info.width }, { id: EBMLId.PixelHeight, data: trackData.info.height }, hasNonSquarePixelAspectRatio ? { id: EBMLId.DisplayWidth, data: trackData.info.aspectRatio.num } : null, hasNonSquarePixelAspectRatio ? { id: EBMLId.DisplayHeight, data: trackData.info.aspectRatio.den } : null, hasNonSquarePixelAspectRatio ? { id: EBMLId.DisplayUnit, data: 3 } : null, // 3 = display aspect ratio trackData.info.alphaMode ? { id: EBMLId.AlphaMode, data: 1 } : null, colorSpaceIsComplete(colorSpace) ? { id: EBMLId.Colour, data: [ { id: EBMLId.MatrixCoefficients, data: MATRIX_COEFFICIENTS_MAP[colorSpace.matrix] }, { id: EBMLId.TransferCharacteristics, data: TRANSFER_CHARACTERISTICS_MAP[colorSpace.transfer] }, { id: EBMLId.Primaries, data: COLOR_PRIMARIES_MAP[colorSpace.primaries] }, { id: EBMLId.Range, data: colorSpace.fullRange ? 2 : 1 } ] } : null, flippedRotation ? { id: EBMLId.Projection, data: [ { id: EBMLId.ProjectionType, data: 0 // rectangular }, { id: EBMLId.ProjectionPoseRoll, data: new EBMLFloat32((flippedRotation + 180) % 360 - 180) // [0, 270] -> [-180, 90] } ] } : null ] }; elements.push(videoElement); return elements; } audioSpecificTrackInfo(trackData) { const pcmInfo = PCM_AUDIO_CODECS.includes(trackData.track.source._codec) ? parsePcmCodec(trackData.track.source._codec) : null; return [ trackData.info.decoderConfig.description ? { id: EBMLId.CodecPrivate, data: toUint8Array(trackData.info.decoderConfig.description) } : null, { id: EBMLId.Audio, data: [ { id: EBMLId.SamplingFrequency, data: new EBMLFloat32(trackData.info.sampleRate) }, { id: EBMLId.Channels, data: trackData.info.numberOfChannels }, pcmInfo ? { id: EBMLId.BitDepth, data: 8 * pcmInfo.sampleSize } : null ] } ]; } subtitleSpecificTrackInfo(trackData) { return [ { id: EBMLId.CodecPrivate, data: textEncoder.encode(trackData.info.config.description) } ]; } maybeCreateTags() { const simpleTags = []; const addSimpleTag = (key, value) => { simpleTags.push({ id: EBMLId.SimpleTag, data: [ { id: EBMLId.TagName, data: new EBMLUnicodeString(key) }, typeof value === "string" ? { id: EBMLId.TagString, data: new EBMLUnicodeString(value) } : { id: EBMLId.TagBinary, data: value } ] }); }; const metadataTags = this.output._metadataTags; const writtenTags = /* @__PURE__ */ new Set(); for (const { key, value } of keyValueIterator(metadataTags)) { switch (key) { case "title": { addSimpleTag("TITLE", value); writtenTags.add("TITLE"); } ; break; case "description": { addSimpleTag("DESCRIPTION", value); writtenTags.add("DESCRIPTION"); } ; break; case "artist": { addSimpleTag("ARTIST", value); writtenTags.add("ARTIST"); } ; break; case "album": { addSimpleTag("ALBUM", value); writtenTags.add("ALBUM"); } ; break; case "albumArtist": { addSimpleTag("ALBUM_ARTIST", value); writtenTags.add("ALBUM_ARTIST"); } ; break; case "genre": { addSimpleTag("GENRE", value); writtenTags.add("GENRE"); } ; break; case "comment": { addSimpleTag("COMMENT", value); writtenTags.add("COMMENT"); } ; break; case "lyrics": { addSimpleTag("LYRICS", value); writtenTags.add("LYRICS"); } ; break; case "date": { addSimpleTag("DATE", value.toISOString().slice(0, 10)); writtenTags.add("DATE"); } ; break; case "trackNumber": { const string = metadataTags.tracksTotal !== void 0 ? \`\${value}/\${metadataTags.tracksTotal}\` : value.toString(); addSimpleTag("PART_NUMBER", string); writtenTags.add("PART_NUMBER"); } ; break; case "discNumber": { const string = metadataTags.discsTotal !== void 0 ? \`\${value}/\${metadataTags.discsTotal}\` : value.toString(); addSimpleTag("DISC", string); writtenTags.add("DISC"); } ; break; case "tracksTotal": case "discsTotal": { } ; break; case "images": case "raw": { } ; break; default: assertNever(key); } } if (metadataTags.raw) { for (const key in metadataTags.raw) { const value = metadataTags.raw[key]; if (value == null || writtenTags.has(key)) { continue; } if (typeof value === "string" || value instanceof Uint8Array) { addSimpleTag(key, value); } } } if (simpleTags.length === 0) { return; } this.tagsElement = { id: EBMLId.Tags, data: [{ id: EBMLId.Tag, data: [ { id: EBMLId.Targets, data: [ { id: EBMLId.TargetTypeValue, data: 50 }, { id: EBMLId.TargetType, data: "MOVIE" } ] }, ...simpleTags ] }] }; } maybeCreateAttachments() { const metadataTags = this.output._metadataTags; const elements = []; const existingFileUids = /* @__PURE__ */ new Set(); const images = metadataTags.images ?? []; for (const image of images) { let imageName = image.name; if (imageName === void 0) { const baseName = image.kind === "coverFront" ? "cover" : image.kind === "coverBack" ? "back" : "image"; imageName = baseName + (imageMimeTypeToExtension(image.mimeType) ?? ""); } let fileUid; while (true) { fileUid = 0n; for (let i2 = 0; i2 < 8; i2++) { fileUid <<= 8n; fileUid |= BigInt(Math.floor(Math.random() * 256)); } if (fileUid !== 0n && !existingFileUids.has(fileUid)) { break; } } existingFileUids.add(fileUid); elements.push({ id: EBMLId.AttachedFile, data: [ image.description !== void 0 ? { id: EBMLId.FileDescription, data: new EBMLUnicodeString(image.description) } : null, { id: EBMLId.FileName, data: new EBMLUnicodeString(imageName) }, { id: EBMLId.FileMediaType, data: image.mimeType }, { id: EBMLId.FileData, data: image.data }, { id: EBMLId.FileUID, data: fileUid } ] }); } for (const [key, value] of Object.entries(metadataTags.raw ?? {})) { if (!(value instanceof AttachedFile)) { continue; } const keyIsNumeric = /^\\d+$/.test(key); if (!keyIsNumeric) { continue; } if (images.find((x) => x.mimeType === value.mimeType && uint8ArraysAreEqual(x.data, value.data))) { continue; } elements.push({ id: EBMLId.AttachedFile, data: [ value.description !== void 0 ? { id: EBMLId.FileDescription, data: new EBMLUnicodeString(value.description) } : null, { id: EBMLId.FileName, data: new EBMLUnicodeString(value.name ?? "") }, { id: EBMLId.FileMediaType, data: value.mimeType ?? "" }, { id: EBMLId.FileData, data: value.data }, { id: EBMLId.FileUID, data: BigInt(key) } ] }); } if (elements.length === 0) { return; } this.attachmentsElement = { id: EBMLId.Attachments, data: elements }; } createSegment() { this.createTracks(); this.maybeCreateTags(); this.maybeCreateAttachments(); this.maybeCreateSeekHead(false); const segment = { id: EBMLId.Segment, size: this.format._options.appendOnly ? -1 : SEGMENT_SIZE_BYTES, data: [ this.seekHead, // null if append-only this.segmentInfo, this.tracksElement, // Matroska spec says put this at the end of the file, but I think placing it before the first cluster // makes more sense, and FFmpeg agrees (argumentum ad ffmpegum fallacy) this.attachmentsElement, this.tagsElement ] }; this.segment = segment; if (this.format._options.onSegmentHeader) { this.writer.startTrackingWrites(); } this.ebmlWriter.writeEBML(segment); if (this.format._options.onSegmentHeader) { const { data, start } = this.writer.stopTrackingWrites(); this.format._options.onSegmentHeader(data, start); } } createCues() { this.cues = { id: EBMLId.Cues, data: [] }; } get segmentDataOffset() { assert(this.segment); return this.ebmlWriter.dataOffsets.get(this.segment); } allTracksAreKnown() { for (const track of this.output._tracks) { if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) { return false; } } return true; } async getMimeType() { await this.allTracksKnown.promise; const codecStrings = this.trackDatas.map((trackData) => { if (trackData.type === "video") { return trackData.info.decoderConfig.codec; } else if (trackData.type === "audio") { return trackData.info.decoderConfig.codec; } else { const map = { webvtt: "wvtt" }; return map[trackData.track.source._codec]; } }); return buildMatroskaMimeType({ isWebM: this.format instanceof WebMOutputFormat, hasVideo: this.trackDatas.some((x) => x.type === "video"), hasAudio: this.trackDatas.some((x) => x.type === "audio"), codecStrings }); } getVideoTrackData(track, packet, meta) { const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } validateVideoChunkMetadata(meta); assert(meta); assert(meta.decoderConfig); assert(meta.decoderConfig.codedWidth !== void 0); assert(meta.decoderConfig.codedHeight !== void 0); const displayAspectWidth = meta.decoderConfig.displayAspectWidth; const displayAspectHeight = meta.decoderConfig.displayAspectHeight; const aspectRatio = displayAspectWidth === void 0 || displayAspectHeight === void 0 ? null : simplifyRational({ num: displayAspectWidth, den: displayAspectHeight }); const newTrackData = { track, type: "video", info: { width: meta.decoderConfig.codedWidth, height: meta.decoderConfig.codedHeight, aspectRatio, decoderConfig: meta.decoderConfig, alphaMode: !!packet.sideData.alpha // The first packet determines if this track has alpha or not }, chunkQueue: [], lastWrittenMsTimestamp: null, closed: false }; if (track.source._codec === "vp9") { newTrackData.info.decoderConfig = { ...newTrackData.info.decoderConfig, description: new Uint8Array(generateVp9CodecConfigurationFromCodecString(newTrackData.info.decoderConfig.codec)) }; } else if (track.source._codec === "av1") { newTrackData.info.decoderConfig = { ...newTrackData.info.decoderConfig, description: new Uint8Array(generateAv1CodecConfigurationFromCodecString(newTrackData.info.decoderConfig.codec)) }; } this.trackDatas.push(newTrackData); this.trackDatas.sort((a2, b) => a2.track.id - b.track.id); if (this.allTracksAreKnown()) { this.allTracksKnown.resolve(); } return newTrackData; } getAudioTrackData(track, packet, meta) { const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } validateAudioChunkMetadata(meta); assert(meta); assert(meta.decoderConfig); const decoderConfig = { ...meta.decoderConfig }; let requiresAdtsStripping = false; if (track.source._codec === "aac" && !decoderConfig.description) { const adtsFrame = readAdtsFrameHeader(FileSlice.tempFromBytes(packet.data)); if (!adtsFrame) { throw new Error("Couldn't parse ADTS header from the AAC packet. Make sure the packets are in ADTS format (as specified in ISO 13818-7) when not providing a description, or provide a description (must be an AudioSpecificConfig as specified in ISO 14496-3) and ensure the packets are raw AAC data."); } const sampleRate = aacFrequencyTable[adtsFrame.samplingFrequencyIndex]; const numberOfChannels = aacChannelMap[adtsFrame.channelConfiguration]; if (sampleRate === void 0 || numberOfChannels === void 0) { throw new Error("Invalid ADTS frame header."); } decoderConfig.description = buildAacAudioSpecificConfig({ objectType: adtsFrame.objectType, sampleRate, numberOfChannels }); requiresAdtsStripping = true; } const newTrackData = { track, type: "audio", info: { numberOfChannels: meta.decoderConfig.numberOfChannels, sampleRate: meta.decoderConfig.sampleRate, decoderConfig, requiresAdtsStripping }, chunkQueue: [], lastWrittenMsTimestamp: null, closed: false }; this.trackDatas.push(newTrackData); this.trackDatas.sort((a2, b) => a2.track.id - b.track.id); if (this.allTracksAreKnown()) { this.allTracksKnown.resolve(); } return newTrackData; } getSubtitleTrackData(track, meta) { const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } validateSubtitleMetadata(meta); assert(meta); assert(meta.config); const newTrackData = { track, type: "subtitle", info: { config: meta.config }, chunkQueue: [], lastWrittenMsTimestamp: null, closed: false }; this.trackDatas.push(newTrackData); this.trackDatas.sort((a2, b) => a2.track.id - b.track.id); if (this.allTracksAreKnown()) { this.allTracksKnown.resolve(); } return newTrackData; } async addEncodedVideoPacket(track, packet, meta) { const release = await this.mutex.acquire(); try { const trackData = this.getVideoTrackData(track, packet, meta); const isKeyFrame = packet.type === "key"; this.validateTimestamp(trackData.track, packet.timestamp, isKeyFrame); let timestamp = packet.timestamp; let duration = packet.duration; if (track.metadata.frameRate !== void 0) { timestamp = roundToDivisor(timestamp, track.metadata.frameRate); duration = roundToDivisor(duration, track.metadata.frameRate); } const additions = trackData.info.alphaMode ? packet.sideData.alpha ?? null : null; const videoChunk = this.createInternalChunk(packet.data, timestamp, duration, packet.type, additions); if (track.source._codec === "vp9") this.fixVP9ColorSpace(trackData, videoChunk); trackData.chunkQueue.push(videoChunk); await this.interleaveChunks(); } finally { release(); } } async addEncodedAudioPacket(track, packet, meta) { const release = await this.mutex.acquire(); try { const trackData = this.getAudioTrackData(track, packet, meta); let packetData = packet.data; if (trackData.info.requiresAdtsStripping) { const adtsFrame = readAdtsFrameHeader(FileSlice.tempFromBytes(packetData)); if (!adtsFrame) { throw new Error("Expected ADTS frame, didn't get one."); } const headerLength = adtsFrame.crcCheck === null ? MIN_ADTS_FRAME_HEADER_SIZE : MAX_ADTS_FRAME_HEADER_SIZE; packetData = packetData.subarray(headerLength); } const isKeyFrame = packet.type === "key"; this.validateTimestamp(trackData.track, packet.timestamp, isKeyFrame); const audioChunk = this.createInternalChunk(packetData, packet.timestamp, packet.duration, packet.type); trackData.chunkQueue.push(audioChunk); await this.interleaveChunks(); } finally { release(); } } async addSubtitleCue(track, cue, meta) { const release = await this.mutex.acquire(); try { const trackData = this.getSubtitleTrackData(track, meta); this.validateTimestamp(trackData.track, cue.timestamp, true); let bodyText = cue.text; const timestampMs = Math.round(cue.timestamp * 1e3); inlineTimestampRegex.lastIndex = 0; bodyText = bodyText.replace(inlineTimestampRegex, (match) => { const time = parseSubtitleTimestamp(match.slice(1, -1)); const offsetTime = time - timestampMs; return \`<\${formatSubtitleTimestamp(offsetTime)}>\`; }); const body = textEncoder.encode(bodyText); const additions = \`\${cue.settings ?? ""} \${cue.identifier ?? ""} \${cue.notes ?? ""}\`; const subtitleChunk = this.createInternalChunk(body, cue.timestamp, cue.duration, "key", additions.trim() ? textEncoder.encode(additions) : null); trackData.chunkQueue.push(subtitleChunk); await this.interleaveChunks(); } finally { release(); } } async interleaveChunks(isFinalCall = false) { if (!isFinalCall && !this.allTracksAreKnown()) { return; } outer: while (true) { let trackWithMinTimestamp = null; let minTimestamp = Infinity; for (const trackData of this.trackDatas) { if (!isFinalCall && trackData.chunkQueue.length === 0 && !trackData.closed) { break outer; } if (trackData.chunkQueue.length > 0 && trackData.chunkQueue[0].timestamp < minTimestamp) { trackWithMinTimestamp = trackData; minTimestamp = trackData.chunkQueue[0].timestamp; } } if (!trackWithMinTimestamp) { break; } const chunk = trackWithMinTimestamp.chunkQueue.shift(); this.writeBlock(trackWithMinTimestamp, chunk); } if (!isFinalCall) { await this.writer.flush(); } } /** * Due to [a bug in Chromium](https://bugs.chromium.org/p/chromium/issues/detail?id=1377842), VP9 streams often * lack color space information. This method patches in that information. */ fixVP9ColorSpace(trackData, chunk) { if (chunk.type !== "key") return; if (!trackData.info.decoderConfig.colorSpace || !trackData.info.decoderConfig.colorSpace.matrix) return; const bitstream = new Bitstream(chunk.data); bitstream.skipBits(2); const profileLowBit = bitstream.readBits(1); const profileHighBit = bitstream.readBits(1); const profile = (profileHighBit << 1) + profileLowBit; if (profile === 3) bitstream.skipBits(1); const showExistingFrame = bitstream.readBits(1); if (showExistingFrame) return; const frameType = bitstream.readBits(1); if (frameType !== 0) return; bitstream.skipBits(2); const syncCode = bitstream.readBits(24); if (syncCode !== 4817730) return; if (profile >= 2) bitstream.skipBits(1); const colorSpaceID = { rgb: 7, bt709: 2, bt470bg: 1, smpte170m: 3 }[trackData.info.decoderConfig.colorSpace.matrix]; writeBits(chunk.data, bitstream.pos, bitstream.pos + 3, colorSpaceID); } /** Converts a read-only external chunk into an internal one for easier use. */ createInternalChunk(data, timestamp, duration, type, additions = null) { const internalChunk = { data, type, timestamp, duration, additions }; return internalChunk; } /** Writes a block containing media data to the file. */ writeBlock(trackData, chunk) { if (!this.segment) { this.createSegment(); } const msTimestamp = Math.round(1e3 * chunk.timestamp); const keyFrameQueuedEverywhere = this.trackDatas.every((otherTrackData) => { if (trackData === otherTrackData) { return chunk.type === "key"; } const firstQueuedSample = otherTrackData.chunkQueue[0]; if (firstQueuedSample) { return firstQueuedSample.type === "key"; } return otherTrackData.closed; }); let shouldCreateNewCluster = false; if (!this.currentCluster) { shouldCreateNewCluster = true; } else { assert(this.currentClusterStartMsTimestamp !== null); assert(this.currentClusterMaxMsTimestamp !== null); const relativeTimestamp2 = msTimestamp - this.currentClusterStartMsTimestamp; shouldCreateNewCluster = keyFrameQueuedEverywhere && msTimestamp > this.currentClusterMaxMsTimestamp && relativeTimestamp2 >= 1e3 * (this.format._options.minimumClusterDuration ?? 1) || relativeTimestamp2 > MAX_CLUSTER_TIMESTAMP_MS; } if (shouldCreateNewCluster) { this.createNewCluster(msTimestamp); } const relativeTimestamp = msTimestamp - this.currentClusterStartMsTimestamp; if (relativeTimestamp < MIN_CLUSTER_TIMESTAMP_MS) { return; } const prelude = new Uint8Array(4); const view2 = new DataView(prelude.buffer); view2.setUint8(0, 128 | trackData.track.id); view2.setInt16(1, relativeTimestamp, false); const msDuration = Math.round(1e3 * chunk.duration); if (!chunk.additions) { view2.setUint8(3, Number(chunk.type === "key") << 7); const simpleBlock = { id: EBMLId.SimpleBlock, data: [ prelude, chunk.data ] }; this.ebmlWriter.writeEBML(simpleBlock); } else { const blockGroup = { id: EBMLId.BlockGroup, data: [ { id: EBMLId.Block, data: [ prelude, chunk.data ] }, chunk.type === "delta" ? { id: EBMLId.ReferenceBlock, data: new EBMLSignedInt(trackData.lastWrittenMsTimestamp - msTimestamp) } : null, chunk.additions ? { id: EBMLId.BlockAdditions, data: [ { id: EBMLId.BlockMore, data: [ { id: EBMLId.BlockAddID, data: 1 }, // Some players expect BlockAddID to come first { id: EBMLId.BlockAdditional, data: chunk.additions } ] } ] } : null, msDuration > 0 ? { id: EBMLId.BlockDuration, data: msDuration } : null ] }; this.ebmlWriter.writeEBML(blockGroup); } this.startTimestamp = Math.min(this.startTimestamp, msTimestamp); this.endTimestamp = Math.max(this.endTimestamp, msTimestamp + msDuration); trackData.lastWrittenMsTimestamp = msTimestamp; if (!this.trackDatasInCurrentCluster.has(trackData)) { this.trackDatasInCurrentCluster.set(trackData, { firstMsTimestamp: msTimestamp }); } this.currentClusterMaxMsTimestamp = Math.max(this.currentClusterMaxMsTimestamp, msTimestamp); } /** Creates a new Cluster element to contain media chunks. */ createNewCluster(msTimestamp) { if (this.currentCluster) { this.finalizeCurrentCluster(); } if (this.format._options.onCluster) { this.writer.startTrackingWrites(); } this.currentCluster = { id: EBMLId.Cluster, size: this.format._options.appendOnly ? -1 : CLUSTER_SIZE_BYTES, data: [ { id: EBMLId.Timestamp, data: msTimestamp } ] }; this.ebmlWriter.writeEBML(this.currentCluster); this.currentClusterStartMsTimestamp = msTimestamp; this.currentClusterMaxMsTimestamp = msTimestamp; this.trackDatasInCurrentCluster.clear(); } finalizeCurrentCluster() { assert(this.currentCluster); if (!this.format._options.appendOnly) { const clusterSize = this.writer.getPos() - this.ebmlWriter.dataOffsets.get(this.currentCluster); const endPos = this.writer.getPos(); this.writer.seek(this.ebmlWriter.offsets.get(this.currentCluster) + 4); this.ebmlWriter.writeVarInt(clusterSize, CLUSTER_SIZE_BYTES); this.writer.seek(endPos); } if (this.format._options.onCluster) { assert(this.currentClusterStartMsTimestamp !== null); const { data, start } = this.writer.stopTrackingWrites(); this.format._options.onCluster(data, start, this.currentClusterStartMsTimestamp / 1e3); } const clusterOffsetFromSegment = this.ebmlWriter.offsets.get(this.currentCluster) - this.segmentDataOffset; const groupedByTimestamp = /* @__PURE__ */ new Map(); for (const [trackData, { firstMsTimestamp }] of this.trackDatasInCurrentCluster) { if (!groupedByTimestamp.has(firstMsTimestamp)) { groupedByTimestamp.set(firstMsTimestamp, []); } groupedByTimestamp.get(firstMsTimestamp).push(trackData); } const groupedAndSortedByTimestamp = [...groupedByTimestamp.entries()].sort((a2, b) => a2[0] - b[0]); for (const [msTimestamp, trackDatas] of groupedAndSortedByTimestamp) { assert(this.cues); this.cues.data.push({ id: EBMLId.CuePoint, data: [ { id: EBMLId.CueTime, data: msTimestamp }, // Create CueTrackPositions for each track that starts at this timestamp ...trackDatas.map((trackData) => { return { id: EBMLId.CueTrackPositions, data: [ { id: EBMLId.CueTrack, data: trackData.track.id }, { id: EBMLId.CueClusterPosition, data: clusterOffsetFromSegment } ] }; }) ] }); } } // eslint-disable-next-line @typescript-eslint/no-misused-promises async onTrackClose(track) { const release = await this.mutex.acquire(); const trackData = this.trackDatas.find((x) => x.track === track); if (trackData) { trackData.closed = true; } if (this.allTracksAreKnown()) { this.allTracksKnown.resolve(); } await this.interleaveChunks(); release(); } /** Finalizes the file, making it ready for use. Must be called after all media chunks have been added. */ async finalize() { const release = await this.mutex.acquire(); this.allTracksKnown.resolve(); for (const trackData of this.trackDatas) { trackData.closed = true; } if (!this.segment) { this.createSegment(); } await this.interleaveChunks(true); if (this.currentCluster) { this.finalizeCurrentCluster(); } assert(this.cues); this.ebmlWriter.writeEBML(this.cues); if (!this.format._options.appendOnly) { const segmentSize = this.writer.getPos() - this.segmentDataOffset; this.writer.seek(this.ebmlWriter.offsets.get(this.segment) + 4); this.ebmlWriter.writeVarInt(segmentSize, SEGMENT_SIZE_BYTES); const duration = this.startTimestamp === Infinity ? 0 : this.endTimestamp - this.startTimestamp; this.segmentDuration.data = new EBMLFloat64(duration); this.writer.seek(this.ebmlWriter.offsets.get(this.segmentDuration)); this.ebmlWriter.writeEBML(this.segmentDuration); assert(this.seekHead); this.writer.seek(this.ebmlWriter.offsets.get(this.seekHead)); this.maybeCreateSeekHead(true); this.ebmlWriter.writeEBML(this.seekHead); } release(); } }; // node_modules/mediabunny/dist/modules/src/media-source.js var MediaSource = class { constructor() { this._connectedTrack = null; this._closingPromise = null; this._closed = false; } /** @internal */ _ensureValidAdd() { if (!this._connectedTrack) { throw new Error("Source is not connected to an output track."); } if (this._connectedTrack.output.state === "canceled") { throw new Error("Output has been canceled."); } if (this._connectedTrack.output.state === "finalizing" || this._connectedTrack.output.state === "finalized") { throw new Error("Output has been finalized."); } if (this._connectedTrack.output.state === "pending") { throw new Error("Output has not started."); } if (this._closed) { throw new Error("Source is closed."); } } /** @internal */ async _start() { } /** @internal */ // eslint-disable-next-line @typescript-eslint/no-unused-vars async _flushAndClose(forceClose) { } /** * Closes this source. This prevents future samples from being added and signals to the output file that no further * samples will come in for this track. Calling \`.close()\` is optional but recommended after adding the * last sample - for improved performance and reduced memory usage. */ close() { if (this._closingPromise) { return; } const connectedTrack = this._connectedTrack; if (!connectedTrack) { throw new Error("Cannot call close without connecting the source to an output track."); } if (connectedTrack.output.state === "pending") { throw new Error("Cannot call close before output has been started."); } this._closingPromise = (async () => { await this._flushAndClose(false); this._closed = true; if (connectedTrack.output.state === "finalizing" || connectedTrack.output.state === "finalized") { return; } connectedTrack.output._muxer.onTrackClose(connectedTrack); })(); } /** @internal */ async _flushOrWaitForOngoingClose(forceClose) { return this._closingPromise ??= (async () => { await this._flushAndClose(forceClose); this._closed = true; })(); } }; var VideoSource = class extends MediaSource { /** Internal constructor. */ constructor(codec) { super(); this._connectedTrack = null; if (!VIDEO_CODECS.includes(codec)) { throw new TypeError(\`Invalid video codec '\${codec}'. Must be one of: \${VIDEO_CODECS.join(", ")}.\`); } this._codec = codec; } }; var maybeEnsureIsKeyPacket = (track, packet) => { if (track.metadata.hasOnlyKeyPackets && packet.type !== "key") { throw new Error("Cannot add non-key packets to a hasOnlyKeyPackets video track."); } }; var VideoEncoderWrapper = class { constructor(source, encodingConfig) { this.source = source; this.encodingConfig = encodingConfig; this.ensureEncoderPromise = null; this.encoderInitialized = false; this.encoder = null; this.muxer = null; this.lastMultipleOfKeyFrameInterval = -1; this.emittedEncoderPackets = 0; this.codedWidth = null; this.codedHeight = null; this.outputWidth = null; this.outputHeight = null; this.frameRateLastSample = null; this.frameRateLastTimestamp = null; this.frameRateLastEndTimestamp = null; this.preciseTimings = []; this.customEncoder = null; this.customEncoderCallSerializer = new CallSerializer(); this.customEncoderQueueSize = 0; this.alphaEncoder = null; this.splitter = null; this.splitterCreationFailed = false; this.alphaFrameQueue = []; this.error = null; this.closed = false; this.lastMuxerPromise = Promise.resolve(); } async add(videoSample, shouldClose, encodeOptions) { const originalSample = videoSample; try { this.checkForEncoderError(); this.source._ensureValidAdd(); const config = this.encodingConfig; const sizeChangeBehavior = config.sizeChangeBehavior ?? "deny"; let isSizeChange = false; if (this.codedWidth !== null && this.codedHeight !== null) { if (videoSample.codedWidth !== this.codedWidth || videoSample.codedHeight !== this.codedHeight) { isSizeChange = true; if (sizeChangeBehavior === "deny") { throw new Error(\`Video sample size must remain constant. Expected \${this.codedWidth}x\${this.codedHeight}, got \${videoSample.codedWidth}x\${videoSample.codedHeight}. To allow the sample size to change over time, set \\\`sizeChangeBehavior\\\` to a value other than 'deny' in the encoding options.\`); } } } else { this.codedWidth = videoSample.codedWidth; this.codedHeight = videoSample.codedHeight; } const hasTransformConfig = config.transform?.width !== void 0 || config.transform?.height !== void 0 || config.transform?.rotate !== void 0 || config.transform?.crop !== void 0 || config.transform?.force === true; const needsTransform = hasTransformConfig || isSizeChange && sizeChangeBehavior !== "passThrough"; if (needsTransform) { let targetWidth = config.transform?.width; let targetHeight = config.transform?.height; let appliedFit = config.transform?.fit ?? "fill"; if (isSizeChange && sizeChangeBehavior !== "passThrough") { assert(this.outputWidth); assert(this.outputHeight); assert(sizeChangeBehavior !== "deny"); targetWidth = this.outputWidth; targetHeight = this.outputHeight; appliedFit = sizeChangeBehavior; } const transformed = await videoSample.transform({ width: targetWidth, height: targetHeight, roundDimensionsTo: 2, crop: config.transform?.crop, rotate: config.transform?.rotate, fit: appliedFit, alpha: config.alpha }); if (this.outputWidth === null || this.outputHeight === null) { this.outputWidth = transformed.displayWidth; this.outputHeight = transformed.displayHeight; } if (shouldClose) { videoSample.close(); } videoSample = transformed; shouldClose = true; } else { if (this.outputWidth === null || this.outputHeight === null) { this.outputWidth = videoSample.codedWidth; this.outputHeight = videoSample.codedHeight; } } const frameRate = config.transform?.frameRate; if (frameRate !== void 0) { const originalEndTimestamp = videoSample.timestamp + videoSample.duration; const alignedTimestamp = floorToDivisor(videoSample.timestamp, frameRate); if (this.frameRateLastSample !== null) { if (alignedTimestamp <= this.frameRateLastTimestamp) { this.frameRateLastSample.close(); this.frameRateLastSample = videoSample.clone(); this.frameRateLastEndTimestamp = originalEndTimestamp; return; } else { await this.padFrameRate(alignedTimestamp, encodeOptions); } } if (videoSample === originalSample) { videoSample = videoSample.clone(); shouldClose = true; } videoSample.setTimestamp(alignedTimestamp); videoSample.setDuration(1 / frameRate); this.frameRateLastSample?.close(); this.frameRateLastSample = videoSample.clone(); this.frameRateLastTimestamp = alignedTimestamp; this.frameRateLastEndTimestamp = originalEndTimestamp; } await this.processAndEncode(videoSample, encodeOptions); } finally { if (shouldClose) { videoSample.close(); } } } /** * Runs the process function (if any) and encodes the resulting samples. */ async processAndEncode(videoSample, encodeOptions) { const config = this.encodingConfig; let samplesToEncode; if (config.transform?.process) { let processed = config.transform.process(videoSample); if (processed instanceof Promise) { processed = await processed; } if (processed === null) { return; } if (!Array.isArray(processed)) { processed = [processed]; } samplesToEncode = processed.map((x) => { if (x instanceof VideoSample) { return x; } if (typeof VideoFrame !== "undefined" && x instanceof VideoFrame) { return new VideoSample(x); } return new VideoSample(x, { timestamp: videoSample.timestamp, duration: videoSample.duration }); }); } else { samplesToEncode = [videoSample]; } try { for (const sampleToEncode of samplesToEncode) { if (!this.encoderInitialized) { if (!this.ensureEncoderPromise) { this.ensureEncoder(sampleToEncode); } if (!this.encoderInitialized) { await this.ensureEncoderPromise; } } assert(this.encoderInitialized); if (this.closed) { break; } const keyFrameInterval = this.encodingConfig.keyFrameInterval ?? 2; const multipleOfKeyFrameInterval = Math.floor(sampleToEncode.timestamp / keyFrameInterval); const mergedEncodeOptions = { ...sampleToEncode.encodeOptions, ...encodeOptions }; const finalEncodeOptions = { ...mergedEncodeOptions, keyFrame: mergedEncodeOptions.keyFrame !== void 0 ? mergedEncodeOptions.keyFrame : keyFrameInterval === 0 || multipleOfKeyFrameInterval !== this.lastMultipleOfKeyFrameInterval }; this.lastMultipleOfKeyFrameInterval = multipleOfKeyFrameInterval; this.encodingConfig.onEncodedSample?.(sampleToEncode); if (this.customEncoder) { this.customEncoderQueueSize++; const clonedSample = sampleToEncode.clone(); const promise = this.customEncoderCallSerializer.call(() => this.customEncoder.encode(clonedSample, finalEncodeOptions)).then(() => this.customEncoderQueueSize--).catch((error) => this.error ??= error).finally(() => { clonedSample.close(); }); if (this.customEncoderQueueSize >= 4) { await promise; } } else { assert(this.encoder); const videoFrame = sampleToEncode.toVideoFrame(); const preciseTimingIndex = binarySearchLessOrEqual(this.preciseTimings, videoFrame.timestamp, (x) => x.microsecondTimestamp); const existingEntry = preciseTimingIndex !== -1 ? this.preciseTimings[preciseTimingIndex] : null; if (existingEntry && existingEntry.microsecondTimestamp === videoFrame.timestamp) { if (existingEntry.timestamp !== sampleToEncode.timestamp) { existingEntry.timestampIsValid = false; } if (existingEntry.duration !== sampleToEncode.duration) { existingEntry.durationIsValid = false; } } else { this.preciseTimings.splice(preciseTimingIndex + 1, 0, { microsecondTimestamp: videoFrame.timestamp, timestamp: sampleToEncode.timestamp, duration: sampleToEncode.duration, timestampIsValid: true, durationIsValid: true }); if (this.preciseTimings.length > 128) { this.preciseTimings.shift(); } } if (!this.alphaEncoder) { this.encoder.encode(videoFrame, finalEncodeOptions); videoFrame.close(); } else { const frameDefinitelyHasNoAlpha = !!videoFrame.format && !videoFrame.format.includes("A"); if (frameDefinitelyHasNoAlpha || this.splitterCreationFailed) { this.alphaFrameQueue.push(null); this.encoder.encode(videoFrame, finalEncodeOptions); videoFrame.close(); } else { const width = videoFrame.displayWidth; const height = videoFrame.displayHeight; if (!this.splitter) { this.splitter = new ColorAlphaSplitter(width, height); } const { colorFrame, alphaFrame } = await this.splitter.update(videoFrame); this.alphaFrameQueue.push(alphaFrame); this.encoder.encode(colorFrame, finalEncodeOptions); colorFrame.close(); } } if (this.encoder.encodeQueueSize >= 4) { await new Promise((resolve) => this.encoder.addEventListener("dequeue", resolve, { once: true })); } } await this.lastMuxerPromise; } } finally { for (const sample of samplesToEncode) { if (sample !== videoSample) { sample.close(); } } } } /** Repeats the last frame rate sample to fill the gap up to the given timestamp. */ async padFrameRate(until, encodeOptions) { const frameRate = this.encodingConfig.transform.frameRate; assert(this.frameRateLastSample); const frameDifference = Math.round((until - this.frameRateLastTimestamp) * frameRate); for (let i2 = 1; i2 < frameDifference; i2++) { const sample = this.frameRateLastSample.clone(); sample.setTimestamp(this.frameRateLastTimestamp + i2 / frameRate); sample.setDuration(1 / frameRate); await this.processAndEncode(sample, encodeOptions); sample.close(); } } ensureEncoder(videoSample) { this.ensureEncoderPromise = (async () => { const encoderConfig = buildVideoEncoderConfig({ ...this.encodingConfig, width: videoSample.codedWidth, height: videoSample.codedHeight, squarePixelWidth: videoSample.squarePixelWidth, squarePixelHeight: videoSample.squarePixelHeight, framerate: this.source._connectedTrack?.metadata.frameRate }); this.encodingConfig.onEncoderConfig?.(encoderConfig); const MatchingCustomEncoder = customVideoEncoders.find((x) => x.supports(this.encodingConfig.codec, encoderConfig)); if (MatchingCustomEncoder) { this.customEncoder = new MatchingCustomEncoder(); this.customEncoder.codec = this.encodingConfig.codec; this.customEncoder.config = encoderConfig; this.customEncoder.onPacket = (packet, meta) => { if (!(packet instanceof EncodedPacket)) { throw new TypeError("The first argument passed to onPacket must be an EncodedPacket."); } if (meta !== void 0 && (!meta || typeof meta !== "object")) { throw new TypeError("The second argument passed to onPacket must be an object or undefined."); } maybeEnsureIsKeyPacket(this.source._connectedTrack, packet); this.encodingConfig.onEncodedPacket?.(packet, meta); this.lastMuxerPromise = this.muxer.addEncodedVideoPacket(this.source._connectedTrack, packet, meta).catch((error) => { this.error ??= error; }); }; await this.customEncoder.init(); } else { if (typeof VideoEncoder === "undefined") { throw new Error("VideoEncoder is not supported by this browser."); } encoderConfig.alpha = "discard"; if (this.encodingConfig.alpha === "keep") { encoderConfig.latencyMode = "quality"; } const hasOddDimension = encoderConfig.width % 2 === 1 || encoderConfig.height % 2 === 1; if (hasOddDimension && (this.encodingConfig.codec === "avc" || this.encodingConfig.codec === "hevc")) { throw new Error(\`The dimensions \${encoderConfig.width}x\${encoderConfig.height} are not supported for codec '\${this.encodingConfig.codec}'; both width and height must be even numbers. Make sure to round your dimensions to the nearest even number.\`); } const support = await VideoEncoder.isConfigSupported(encoderConfig); if (!support.supported) { throw new Error(\`This specific encoder configuration (\${encoderConfig.codec}, \${encoderConfig.bitrate} bps, \${encoderConfig.width}x\${encoderConfig.height}, hardware acceleration: \${encoderConfig.hardwareAcceleration ?? "no-preference"}) is not supported by this browser. Consider using another codec or changing your video parameters.\`); } const colorChunkQueue = []; const nullAlphaChunkQueue = []; let encodedAlphaChunkCount = 0; let alphaEncoderQueue = 0; const addPacket = (colorChunk, alphaChunk, meta) => { const sideData = {}; if (alphaChunk) { const alphaData = new Uint8Array(alphaChunk.byteLength); alphaChunk.copyTo(alphaData); sideData.alpha = alphaData; } let packet = EncodedPacket.fromEncodedChunk(colorChunk, sideData); const preciseTimingIndex = binarySearchLessOrEqual(this.preciseTimings, colorChunk.timestamp, (x) => x.microsecondTimestamp); const entry = preciseTimingIndex !== -1 ? this.preciseTimings[preciseTimingIndex] : null; let actualType = null; if (this.emittedEncoderPackets === 0 && packet.type === "delta" && meta?.decoderConfig) { actualType = determineVideoPacketType(this.encodingConfig.codec, meta.decoderConfig, packet.data); } if (entry && entry.microsecondTimestamp === colorChunk.timestamp || actualType !== null) { packet = packet.clone({ timestamp: entry?.timestampIsValid ? entry.timestamp : void 0, duration: entry?.durationIsValid ? entry.duration : void 0, type: actualType ?? void 0 }); } maybeEnsureIsKeyPacket(this.source._connectedTrack, packet); this.encodingConfig.onEncodedPacket?.(packet, meta); this.lastMuxerPromise = this.muxer.addEncodedVideoPacket(this.source._connectedTrack, packet, meta).catch((error) => { this.error ??= error; }); this.emittedEncoderPackets++; }; const stack = new Error("Encoding error").stack; this.encoder = new VideoEncoder({ output: (chunk, meta) => { if (!this.alphaEncoder) { addPacket(chunk, null, meta); return; } const alphaFrame = this.alphaFrameQueue.shift(); assert(alphaFrame !== void 0); if (alphaFrame) { this.alphaEncoder.encode(alphaFrame, { // Crucial: The alpha frame is forced to be a key frame whenever the color frame // also is. Without this, playback can glitch and even crash in some browsers. // This is the reason why the two encoders are wired in series and not in parallel. keyFrame: chunk.type === "key" }); alphaEncoderQueue++; alphaFrame.close(); colorChunkQueue.push({ chunk, meta }); } else { if (alphaEncoderQueue === 0) { addPacket(chunk, null, meta); } else { nullAlphaChunkQueue.push(encodedAlphaChunkCount + alphaEncoderQueue); colorChunkQueue.push({ chunk, meta }); } } }, error: (error) => { error.stack = stack; this.error ??= error; } }); this.encoder.configure(encoderConfig); if (this.encodingConfig.alpha === "keep") { const stack2 = new Error("Encoding error").stack; this.alphaEncoder = new VideoEncoder({ // We ignore the alpha chunk's metadata // eslint-disable-next-line @typescript-eslint/no-unused-vars output: (chunk, meta) => { alphaEncoderQueue--; const colorChunk = colorChunkQueue.shift(); assert(colorChunk !== void 0); addPacket(colorChunk.chunk, chunk, colorChunk.meta); encodedAlphaChunkCount++; while (nullAlphaChunkQueue.length > 0 && nullAlphaChunkQueue[0] === encodedAlphaChunkCount) { nullAlphaChunkQueue.shift(); const colorChunk2 = colorChunkQueue.shift(); assert(colorChunk2 !== void 0); addPacket(colorChunk2.chunk, null, colorChunk2.meta); } }, error: (error) => { error.stack = stack2; this.error ??= error; } }); this.alphaEncoder.configure(encoderConfig); } } assert(this.source._connectedTrack); this.muxer = this.source._connectedTrack.output._muxer; this.encoderInitialized = true; })(); } async flushAndClose(forceClose) { if (!forceClose) { this.checkForEncoderError(); } if (!forceClose && this.frameRateLastSample) { const frameRate = this.encodingConfig.transform.frameRate; const alignedEnd = floorToDivisor(this.frameRateLastEndTimestamp, frameRate); await this.padFrameRate(alignedEnd); } this.closed = true; this.frameRateLastSample?.close(); this.frameRateLastSample = null; if (this.customEncoder) { if (!forceClose) { void this.customEncoderCallSerializer.call(() => this.customEncoder.flush()); } await this.customEncoderCallSerializer.call(() => this.customEncoder.close()); } else if (this.encoder) { if (!forceClose) { await this.encoder.flush(); await this.alphaEncoder?.flush(); } if (this.encoder.state !== "closed") { this.encoder.close(); } if (this.alphaEncoder && this.alphaEncoder.state !== "closed") { this.alphaEncoder.close(); } this.alphaFrameQueue.forEach((x) => x?.close()); this.splitter?.close(); } if (!forceClose) { this.checkForEncoderError(); } } getQueueSize() { if (this.customEncoder) { return this.customEncoderQueueSize; } else { return this.encoder?.encodeQueueSize ?? 0; } } checkForEncoderError() { if (this.error) { throw this.error; } } }; var splitterGpuUnavailable = false; var ColorAlphaSplitter = class _ColorAlphaSplitter { constructor(initialWidth, initialHeight) { this.canvas = null; this.gl = null; this.colorProgram = null; this.alphaProgram = null; this.vao = null; this.sourceTexture = null; this.alphaResolutionLocation = null; this.worker = null; this.pendingRequests = /* @__PURE__ */ new Map(); this.nextRequestId = 0; const canMakeCanvas = typeof OffscreenCanvas !== "undefined" || typeof document !== "undefined" && typeof document.createElement === "function"; if (!_ColorAlphaSplitter.forceCpu && canMakeCanvas && !splitterGpuUnavailable) { try { if (typeof OffscreenCanvas !== "undefined") { this.canvas = new OffscreenCanvas(initialWidth, initialHeight); } else { this.canvas = document.createElement("canvas"); this.canvas.width = initialWidth; this.canvas.height = initialHeight; } const gl = this.canvas.getContext("webgl2", { alpha: true // Needed due to the YUV thing we do for alpha }); if (!gl) { throw new Error("Couldn't acquire WebGL 2 context."); } this.gl = gl; this.colorProgram = this.createColorProgram(); this.alphaProgram = this.createAlphaProgram(); this.vao = this.createVAO(); this.sourceTexture = this.createTexture(); this.alphaResolutionLocation = this.gl.getUniformLocation(this.alphaProgram, "u_resolution"); this.gl.useProgram(this.colorProgram); this.gl.uniform1i(this.gl.getUniformLocation(this.colorProgram, "u_sourceTexture"), 0); this.gl.useProgram(this.alphaProgram); this.gl.uniform1i(this.gl.getUniformLocation(this.alphaProgram, "u_sourceTexture"), 0); } catch (error) { this.gl = null; this.canvas = null; splitterGpuUnavailable = true; Logging._warn("Falling back to CPU for color/alpha splitting.", error); } } } async update(sourceFrame) { if (this.gl) { return this.updateGpu(sourceFrame); } else { return this.updateCpu(sourceFrame); } } updateGpu(sourceFrame) { assert(this.gl); assert(this.canvas); if (sourceFrame.displayWidth !== this.canvas.width || sourceFrame.displayHeight !== this.canvas.height) { this.canvas.width = sourceFrame.displayWidth; this.canvas.height = sourceFrame.displayHeight; } this.gl.activeTexture(this.gl.TEXTURE0); this.gl.bindTexture(this.gl.TEXTURE_2D, this.sourceTexture); this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, sourceFrame); const colorFrame = this.runColorProgram(sourceFrame); const alphaFrame = this.runAlphaProgram(sourceFrame); sourceFrame.close(); return { colorFrame, alphaFrame }; } createVertexShader() { assert(this.gl); return this.createShader(this.gl.VERTEX_SHADER, \`#version 300 es in vec2 a_position; in vec2 a_texCoord; out vec2 v_texCoord; void main() { gl_Position = vec4(a_position, 0.0, 1.0); v_texCoord = a_texCoord; } \`); } createColorProgram() { assert(this.gl); const vertexShader = this.createVertexShader(); const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, \`#version 300 es precision highp float; uniform sampler2D u_sourceTexture; in vec2 v_texCoord; out vec4 fragColor; void main() { vec4 source = texture(u_sourceTexture, v_texCoord); fragColor = vec4(source.rgb, 1.0); } \`); const program = this.gl.createProgram(); this.gl.attachShader(program, vertexShader); this.gl.attachShader(program, fragmentShader); this.gl.linkProgram(program); return program; } createAlphaProgram() { assert(this.gl); const vertexShader = this.createVertexShader(); const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, \`#version 300 es precision highp float; uniform sampler2D u_sourceTexture; uniform vec2 u_resolution; // The width and height of the canvas in vec2 v_texCoord; out vec4 fragColor; // This function determines the value for a single byte in the YUV stream float getByteValue(float byteOffset) { float width = u_resolution.x; float height = u_resolution.y; float yPlaneSize = width * height; if (byteOffset < yPlaneSize) { // This byte is in the luma plane. Find the corresponding pixel coordinates to sample from float y = floor(byteOffset / width); float x = mod(byteOffset, width); // Add 0.5 to sample the center of the texel vec2 sampleCoord = (vec2(x, y) + 0.5) / u_resolution; // The luma value is the alpha from the source texture return texture(u_sourceTexture, sampleCoord).a; } else { // Write a fixed value for chroma and beyond return 128.0 / 255.0; } } void main() { // Each fragment writes 4 bytes (R, G, B, A) float pixelIndex = floor(gl_FragCoord.y) * u_resolution.x + floor(gl_FragCoord.x); float baseByteOffset = pixelIndex * 4.0; vec4 result; for (int i = 0; i < 4; i++) { float currentByteOffset = baseByteOffset + float(i); result[i] = getByteValue(currentByteOffset); } fragColor = result; } \`); const program = this.gl.createProgram(); this.gl.attachShader(program, vertexShader); this.gl.attachShader(program, fragmentShader); this.gl.linkProgram(program); return program; } createShader(type, source) { assert(this.gl); const shader = this.gl.createShader(type); this.gl.shaderSource(shader, source); this.gl.compileShader(shader); if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { Logging._error("Shader compile error:", this.gl.getShaderInfoLog(shader)); } return shader; } createVAO() { assert(this.gl); assert(this.colorProgram); const vao = this.gl.createVertexArray(); this.gl.bindVertexArray(vao); const vertices = new Float32Array([ -1, -1, 0, 1, 1, -1, 1, 1, -1, 1, 0, 0, 1, 1, 1, 0 ]); const buffer = this.gl.createBuffer(); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer); this.gl.bufferData(this.gl.ARRAY_BUFFER, vertices, this.gl.STATIC_DRAW); const positionLocation = this.gl.getAttribLocation(this.colorProgram, "a_position"); const texCoordLocation = this.gl.getAttribLocation(this.colorProgram, "a_texCoord"); this.gl.enableVertexAttribArray(positionLocation); this.gl.vertexAttribPointer(positionLocation, 2, this.gl.FLOAT, false, 16, 0); this.gl.enableVertexAttribArray(texCoordLocation); this.gl.vertexAttribPointer(texCoordLocation, 2, this.gl.FLOAT, false, 16, 8); return vao; } createTexture() { assert(this.gl); const texture = this.gl.createTexture(); this.gl.bindTexture(this.gl.TEXTURE_2D, texture); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR); this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR); return texture; } runColorProgram(sourceFrame) { assert(this.gl); assert(this.canvas); this.gl.useProgram(this.colorProgram); this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); this.gl.clear(this.gl.COLOR_BUFFER_BIT); this.gl.bindVertexArray(this.vao); this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4); return new VideoFrame(this.canvas, { timestamp: sourceFrame.timestamp, duration: sourceFrame.duration ?? void 0, alpha: "discard" }); } runAlphaProgram(sourceFrame) { assert(this.gl); assert(this.canvas); this.gl.useProgram(this.alphaProgram); this.gl.uniform2f(this.alphaResolutionLocation, this.canvas.width, this.canvas.height); this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); this.gl.clear(this.gl.COLOR_BUFFER_BIT); this.gl.bindVertexArray(this.vao); this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4); const { width, height } = this.canvas; const chromaSamples = Math.ceil(width / 2) * Math.ceil(height / 2); const yuvSize = width * height + chromaSamples * 2; const requiredHeight = Math.ceil(yuvSize / (width * 4)); let yuv = new Uint8Array(4 * width * requiredHeight); this.gl.readPixels(0, 0, width, requiredHeight, this.gl.RGBA, this.gl.UNSIGNED_BYTE, yuv); yuv = yuv.subarray(0, yuvSize); assert(yuv[width * height] === 128); assert(yuv[yuv.length - 1] === 128); const init = { format: "I420", codedWidth: width, codedHeight: height, timestamp: sourceFrame.timestamp, duration: sourceFrame.duration ?? void 0, transfer: [yuv.buffer] }; return new VideoFrame(yuv, init); } updateCpu(sourceFrame) { if (!this.worker) { const blob = new Blob([\`(\${colorAlphaSplitterWorkerCode.toString()})()\`], { type: "application/javascript" }); const url2 = URL.createObjectURL(blob); this.worker = new Worker(url2); URL.revokeObjectURL(url2); this.worker.addEventListener("message", (event) => { const data = event.data; const pending2 = this.pendingRequests.get(data.id); if (!pending2) { return; } this.pendingRequests.delete(data.id); if ("error" in data) { pending2.reject(new Error(data.error)); } else { pending2.resolve({ colorFrame: data.colorFrame, alphaFrame: data.alphaFrame }); } }); this.worker.addEventListener("error", (event) => { const error = new Error(event.message || "Color/alpha splitter worker error."); for (const pending2 of this.pendingRequests.values()) { pending2.reject(error); } this.pendingRequests.clear(); }); } const id = this.nextRequestId++; const pending = promiseWithResolvers(); this.pendingRequests.set(id, pending); this.worker.postMessage({ id, sourceFrame }, { transfer: [sourceFrame] }); return pending.promise; } close() { this.gl?.getExtension("WEBGL_lose_context")?.loseContext(); this.gl = null; this.canvas = null; this.worker?.terminate(); this.worker = null; const error = new Error("Color/alpha splitter closed."); for (const pending of this.pendingRequests.values()) { pending.reject(error); } this.pendingRequests.clear(); } }; ColorAlphaSplitter.forceCpu = true; var colorAlphaSplitterWorkerCode = () => { let cpuSourceBuffer = null; let chain = Promise.resolve(); self.addEventListener("message", (event) => { const { id, sourceFrame } = event.data; chain = chain.then(async () => { try { const { colorFrame, alphaFrame } = await split(sourceFrame); self.postMessage({ id, colorFrame, alphaFrame }, { transfer: [colorFrame, alphaFrame] }); } catch (error) { self.postMessage({ id, error: error.message }); } finally { sourceFrame.close(); } }); }); const split = async (sourceFrame) => { const format = sourceFrame.format; if (!format) { throw new Error("CPU color/alpha splitting requires a known VideoFrame format."); } const width = sourceFrame.codedWidth; const height = sourceFrame.codedHeight; const sourceSize = sourceFrame.allocationSize(); if (!cpuSourceBuffer || cpuSourceBuffer.byteLength !== sourceSize) { cpuSourceBuffer = new Uint8Array(sourceSize); } await sourceFrame.copyTo(cpuSourceBuffer); if (format === "RGBA" || format === "BGRA") { return splitInterleavedRgba(cpuSourceBuffer, width, height, format, sourceFrame); } else if (format === "I420A" || format === "I420AP10" || format === "I420AP12" || format === "I422A" || format === "I422AP10" || format === "I422AP12" || format === "I444A" || format === "I444AP10" || format === "I444AP12") { return splitPlanarYuvA(cpuSourceBuffer, width, height, format, sourceFrame); } throw new Error(\`CPU color/alpha splitting does not support format '\${format}'.\`); }; const splitInterleavedRgba = (source, width, height, format, sourceFrame) => { const pixelCount = width * height; const chromaW = Math.ceil(width / 2); const chromaH = Math.ceil(height / 2); const alphaSize = pixelCount + chromaW * chromaH * 2; const alphaBuffer = new Uint8Array(alphaSize); for (let i2 = 0, j = 3; i2 < pixelCount; i2++, j += 4) { alphaBuffer[i2] = source[j]; } alphaBuffer.fill(128, pixelCount); const colorFrame = new VideoFrame(source, { format: format === "RGBA" ? "RGBX" : "BGRX", codedWidth: width, codedHeight: height, timestamp: sourceFrame.timestamp, duration: sourceFrame.duration ?? void 0 // No transfer! }); const alphaInit = { format: "I420", codedWidth: width, codedHeight: height, timestamp: sourceFrame.timestamp, duration: sourceFrame.duration ?? void 0, transfer: [alphaBuffer.buffer] }; const alphaFrame = new VideoFrame(alphaBuffer, alphaInit); return { colorFrame, alphaFrame }; }; const splitPlanarYuvA = (source, width, height, format, sourceFrame) => { const is10 = format.includes("P10"); const is12 = format.includes("P12"); const bytesPerSample = is10 || is12 ? 2 : 1; let chromaW; let chromaH; if (format.startsWith("I420")) { chromaW = Math.ceil(width / 2); chromaH = Math.ceil(height / 2); } else if (format.startsWith("I422")) { chromaW = Math.ceil(width / 2); chromaH = height; } else { chromaW = width; chromaH = height; } const ySamples = width * height; const uvSamples = chromaW * chromaH; const yBytes = ySamples * bytesPerSample; const uvBytes = uvSamples * bytesPerSample; const aBytes = ySamples * bytesPerSample; const colorBytes = yBytes + uvBytes * 2; const colorFormat = format.replace("A", ""); const alphaChromaW = Math.ceil(width / 2); const alphaChromaH = Math.ceil(height / 2); const alphaUvSamples = alphaChromaW * alphaChromaH; const alphaUvBytes = alphaUvSamples * bytesPerSample; const alphaSize = aBytes + 2 * alphaUvBytes; const alphaBuffer = new Uint8Array(alphaSize); const aPlaneStart = colorBytes; alphaBuffer.set(source.subarray(aPlaneStart, aPlaneStart + aBytes), 0); const uvOffset = aBytes; const neutralChroma = is10 ? 512 : is12 ? 2048 : 128; if (bytesPerSample === 1) { alphaBuffer.fill(neutralChroma, uvOffset); } else { const uvView = new Uint16Array(alphaBuffer.buffer, uvOffset, 2 * alphaUvSamples); uvView.fill(neutralChroma); } const alphaFormat = is10 ? "I420P10" : is12 ? "I420P12" : "I420"; const colorFrame = new VideoFrame(source.subarray(0, colorBytes), { format: colorFormat, codedWidth: width, codedHeight: height, timestamp: sourceFrame.timestamp, duration: sourceFrame.duration ?? void 0 }); const alphaInit = { format: alphaFormat, codedWidth: width, codedHeight: height, timestamp: sourceFrame.timestamp, duration: sourceFrame.duration ?? void 0, transfer: [alphaBuffer.buffer] }; const alphaFrame = new VideoFrame(alphaBuffer, alphaInit); return { colorFrame, alphaFrame }; }; }; var VideoSampleSource = class extends VideoSource { /** * Creates a new {@link VideoSampleSource} whose samples are encoded according to the specified * {@link VideoEncodingConfig}. */ constructor(encodingConfig) { validateVideoEncodingConfig(encodingConfig); super(encodingConfig.codec); this._encoder = new VideoEncoderWrapper(this, encodingConfig); } /** * Encodes a video sample (frame) and then adds it to the output. * * @returns A Promise that resolves once the output is ready to receive more samples. You should await this Promise * to respect writer and encoder backpressure. */ add(videoSample, encodeOptions) { if (!(videoSample instanceof VideoSample)) { throw new TypeError("videoSample must be a VideoSample."); } return this._encoder.add(videoSample, false, encodeOptions); } /** @internal */ _flushAndClose(forceClose) { return this._encoder.flushAndClose(forceClose); } }; var AudioSource = class extends MediaSource { /** Internal constructor. */ constructor(codec) { super(); this._connectedTrack = null; if (!AUDIO_CODECS.includes(codec)) { throw new TypeError(\`Invalid audio codec '\${codec}'. Must be one of: \${AUDIO_CODECS.join(", ")}.\`); } this._codec = codec; } }; var SubtitleSource = class extends MediaSource { /** Internal constructor. */ constructor(codec) { super(); this._connectedTrack = null; if (!SUBTITLE_CODECS.includes(codec)) { throw new TypeError(\`Invalid subtitle codec '\${codec}'. Must be one of: \${SUBTITLE_CODECS.join(", ")}.\`); } this._codec = codec; } }; // node_modules/mediabunny/dist/modules/src/output-format.js var OutputFormat = class { /** Returns a list of video codecs that this output format can contain. */ getSupportedVideoCodecs() { return this.getSupportedCodecs().filter((codec) => VIDEO_CODECS.includes(codec)); } /** Returns a list of audio codecs that this output format can contain. */ getSupportedAudioCodecs() { return this.getSupportedCodecs().filter((codec) => AUDIO_CODECS.includes(codec)); } /** Returns a list of subtitle codecs that this output format can contain. */ getSupportedSubtitleCodecs() { return this.getSupportedCodecs().filter((codec) => SUBTITLE_CODECS.includes(codec)); } /** @internal */ // eslint-disable-next-line @typescript-eslint/no-unused-vars _codecUnsupportedHint(codec) { return ""; } }; var IsobmffOutputFormat = class extends OutputFormat { /** Internal constructor. */ constructor(options = {}) { if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } if (options.fastStart !== void 0 && ![false, "in-memory", "reserve", "fragmented"].includes(options.fastStart)) { throw new TypeError("options.fastStart, when provided, must be false, 'in-memory', 'reserve', or 'fragmented'."); } if (options.minimumFragmentDuration !== void 0 && (!Number.isFinite(options.minimumFragmentDuration) || options.minimumFragmentDuration < 0)) { throw new TypeError("options.minimumFragmentDuration, when provided, must be a non-negative number."); } if (options.onFtyp !== void 0 && typeof options.onFtyp !== "function") { throw new TypeError("options.onFtyp, when provided, must be a function."); } if (options.onMoov !== void 0 && typeof options.onMoov !== "function") { throw new TypeError("options.onMoov, when provided, must be a function."); } if (options.onMdat !== void 0 && typeof options.onMdat !== "function") { throw new TypeError("options.onMdat, when provided, must be a function."); } if (options.onMoof !== void 0 && typeof options.onMoof !== "function") { throw new TypeError("options.onMoof, when provided, must be a function."); } if (options.metadataFormat !== void 0 && !["mdir", "mdta", "udta", "auto"].includes(options.metadataFormat)) { throw new TypeError("options.metadataFormat, when provided, must be either 'auto', 'mdir', 'mdta', or 'udta'."); } super(); this._options = options; } getSupportedTrackCounts() { const max = 2 ** 32 - 1; return { video: { min: 0, max }, audio: { min: 0, max }, subtitle: { min: 0, max }, total: { min: 1, max } }; } get supportsVideoRotationMetadata() { return true; } get supportsTimestampedMediaData() { return true; } /** @internal */ _createMuxer(output) { return new IsobmffMuxer(output, this); } }; var Mp4OutputFormat = class extends IsobmffOutputFormat { /** Creates a new {@link Mp4OutputFormat} configured with the specified \`options\`. */ constructor(options) { super(options); } /** @internal */ get _name() { return "MP4"; } get fileExtension() { return ".mp4"; } get mimeType() { return "video/mp4"; } getSupportedCodecs() { return [ ...VIDEO_CODECS, ...NON_PCM_AUDIO_CODECS, // These are supported via ISO/IEC 23003-5: "pcm-s16", "pcm-s16be", "pcm-s24", "pcm-s24be", "pcm-s32", "pcm-s32be", "pcm-f32", "pcm-f32be", "pcm-f64", "pcm-f64be", ...SUBTITLE_CODECS ]; } /** @internal */ _codecUnsupportedHint(codec) { if (new MovOutputFormat().getSupportedCodecs().includes(codec)) { return " Switching to MOV will grant support for this codec."; } return ""; } }; var CmafOutputFormat = class extends IsobmffOutputFormat { /** Creates a new {@link CmafOutputFormat} configured with the specified \`options\`. */ constructor(options) { super(options); } /** @internal */ get _name() { return "CMAF"; } get fileExtension() { return ".m4s"; } get mimeType() { return "video/mp4"; } getSupportedCodecs() { return [ ...VIDEO_CODECS, ...NON_PCM_AUDIO_CODECS, // These are supported via ISO/IEC 23003-5: "pcm-s16", "pcm-s16be", "pcm-s24", "pcm-s24be", "pcm-s32", "pcm-s32be", "pcm-f32", "pcm-f32be", "pcm-f64", "pcm-f64be", ...SUBTITLE_CODECS ]; } }; var MovOutputFormat = class extends IsobmffOutputFormat { /** Creates a new {@link MovOutputFormat} configured with the specified \`options\`. */ constructor(options) { super(options); } /** @internal */ get _name() { return "MOV"; } get fileExtension() { return ".mov"; } get mimeType() { return "video/quicktime"; } getSupportedCodecs() { return [ ...VIDEO_CODECS, ...AUDIO_CODECS ]; } /** @internal */ _codecUnsupportedHint(codec) { if (new Mp4OutputFormat().getSupportedCodecs().includes(codec)) { return " Switching to MP4 will grant support for this codec."; } return ""; } }; var MkvOutputFormat = class extends OutputFormat { /** Creates a new {@link MkvOutputFormat} configured with the specified \`options\`. */ constructor(options = {}) { if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } if (options.appendOnly !== void 0 && typeof options.appendOnly !== "boolean") { throw new TypeError("options.appendOnly, when provided, must be a boolean."); } if (options.minimumClusterDuration !== void 0 && (!Number.isFinite(options.minimumClusterDuration) || options.minimumClusterDuration < 0)) { throw new TypeError("options.minimumClusterDuration, when provided, must be a non-negative number."); } if (options.onEbmlHeader !== void 0 && typeof options.onEbmlHeader !== "function") { throw new TypeError("options.onEbmlHeader, when provided, must be a function."); } if (options.onSegmentHeader !== void 0 && typeof options.onSegmentHeader !== "function") { throw new TypeError("options.onHeader, when provided, must be a function."); } if (options.onCluster !== void 0 && typeof options.onCluster !== "function") { throw new TypeError("options.onCluster, when provided, must be a function."); } super(); this._options = options; } /** @internal */ _createMuxer(output) { return new MatroskaMuxer(output, this); } /** @internal */ get _name() { return "Matroska"; } getSupportedTrackCounts() { const max = 127; return { video: { min: 0, max }, audio: { min: 0, max }, subtitle: { min: 0, max }, total: { min: 1, max } }; } get fileExtension() { return ".mkv"; } get mimeType() { return "video/x-matroska"; } getSupportedCodecs() { return [ ...VIDEO_CODECS, ...NON_PCM_AUDIO_CODECS, ...PCM_AUDIO_CODECS.filter((codec) => !["pcm-s8", "pcm-f32be", "pcm-f64be", "ulaw", "alaw"].includes(codec)), ...SUBTITLE_CODECS ]; } get supportsVideoRotationMetadata() { return false; } get supportsTimestampedMediaData() { return true; } }; var WebMOutputFormat = class extends MkvOutputFormat { /** Creates a new {@link WebMOutputFormat} configured with the specified \`options\`. */ constructor(options) { super(options); } getSupportedCodecs() { return [ ...VIDEO_CODECS.filter((codec) => ["vp8", "vp9", "av1"].includes(codec)), ...AUDIO_CODECS.filter((codec) => ["opus", "vorbis"].includes(codec)), ...SUBTITLE_CODECS ]; } /** @internal */ get _name() { return "WebM"; } get fileExtension() { return ".webm"; } get mimeType() { return "video/webm"; } /** @internal */ _codecUnsupportedHint(codec) { if (new MkvOutputFormat().getSupportedCodecs().includes(codec)) { return " Switching to MKV will grant support for this codec."; } return ""; } }; // node_modules/mediabunny/dist/modules/src/output.js var ALL_TRACK_TYPES = ["video", "audio", "subtitle"]; var OutputTrack = class _OutputTrack { /** @internal */ constructor(id, output, type, source, metadata) { this.id = id; this.output = output; this.type = type; this.source = source; this.metadata = metadata; } /** Returns true if and only if this track is a video track. */ isVideoTrack() { return this.type === "video"; } /** Returns true if and only if this track is an audio track. */ isAudioTrack() { return this.type === "audio"; } /** Returns true if and only if this track is a subtitle track. */ isSubtitleTrack() { return this.type === "subtitle"; } /** * Returns true if and only if this track can be paired with the given other track. Pairability can be set using * the {@link BaseTrackMetadata.group} option. */ canBePairedWith(other) { if (!(other instanceof _OutputTrack)) { throw new TypeError("other must be an OutputTrack."); } if (this === other) { return false; } const thisGroups = toArray(this.metadata.group); const otherGroups = toArray(other.metadata.group); for (const aGroup of thisGroups) { const pairableInSameGroup = this.type !== other.type && otherGroups.some((bGroup) => aGroup === bGroup); if (pairableInSameGroup) { return true; } const pairableAcrossGroups = otherGroups.some((bGroup) => aGroup._pairedGroups.has(bGroup)); if (pairableAcrossGroups) { return true; } } return false; } }; var OutputVideoTrack = class extends OutputTrack { /** @internal */ constructor(id, output, source, metadata) { super(id, output, "video", source, metadata); } }; var OutputAudioTrack = class extends OutputTrack { /** @internal */ constructor(id, output, source, metadata) { super(id, output, "audio", source, metadata); } }; var OutputSubtitleTrack = class extends OutputTrack { /** @internal */ constructor(id, output, source, metadata) { super(id, output, "subtitle", source, metadata); } }; var OutputTrackGroup = class _OutputTrackGroup { /** Creates a new {@link OutputTrackGroup}. */ constructor() { this._pairedGroups = /* @__PURE__ */ new Set(); } /** * Marks this group as being pairable with another group, symmetrically. Output tracks where each track is assigned * to one half of a group pairing are then considered pairable. * * You cannot pair a group with itself. */ pairWith(other) { if (!(other instanceof _OutputTrackGroup)) { throw new TypeError("other must be an OutputTrackGroup."); } if (this === other) { throw new TypeError("Cannot pair a group with itself."); } this._pairedGroups.add(other); other._pairedGroups.add(this); } }; var validateBaseTrackMetadata = (metadata) => { if (!metadata || typeof metadata !== "object") { throw new TypeError("metadata must be an object."); } if (metadata.languageCode !== void 0 && !isIso639Dash2LanguageCode(metadata.languageCode)) { throw new TypeError("metadata.languageCode, when provided, must be a three-letter, ISO 639-2/T language code."); } if (metadata.name !== void 0 && typeof metadata.name !== "string") { throw new TypeError("metadata.name, when provided, must be a string."); } if (metadata.disposition !== void 0) { validateTrackDisposition(metadata.disposition); } if (metadata.maximumPacketCount !== void 0 && (!Number.isInteger(metadata.maximumPacketCount) || metadata.maximumPacketCount < 0)) { throw new TypeError("metadata.maximumPacketCount, when provided, must be a non-negative integer."); } if (metadata.group !== void 0 && !(metadata.group instanceof OutputTrackGroup) && (!Array.isArray(metadata.group) || metadata.group.some((group) => !(group instanceof OutputTrackGroup)))) { throw new TypeError("metadata.group, when provided, must be an OutputTrackGroup instance or an array of OutputTrackGroup instances."); } }; var Output = class extends EventEmitter { /** * The target to which the root file will be written. Throws when using {@link PathedTarget} with an async callback; * prefer the \`'target'\` event for those cases. */ get target() { const errorMessage = "Output.target cannot be used when using PathedTarget with an async callback. Use the 'target' event instead."; if (this._rootTargetPromise) { throw new TypeError(errorMessage); } const rootTargetResult = this._getRootTarget(); if (rootTargetResult instanceof Promise) { throw new TypeError(errorMessage); } return rootTargetResult; } /** * Creates a new instance of {@link Output} which can then be used to create a new media file according to the * specified {@link OutputOptions}. */ constructor(options) { super(); this.state = "pending"; this.defaultTrackGroup = new OutputTrackGroup(); this._onFinalize = null; this._unfinalizedTargets = /* @__PURE__ */ new Set(); this._rootWriterPromise = null; this._tracks = []; this._startPromise = null; this._cancelPromise = null; this._finalizePromise = null; this._mutex = new AsyncMutex(); this._metadataTags = {}; this._rootTarget = null; this._rootTargetPromise = null; this._firstMediaStreamTimestamp = null; if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } if (!(options.format instanceof OutputFormat)) { throw new TypeError("options.format must be an OutputFormat."); } if (!(options.target instanceof Target || options.target instanceof PathedTarget)) { throw new TypeError("options.target must be a Target or a PathedTarget."); } if (options.target instanceof Target) { this._rememberTarget(options.target); } if (options.initTarget !== void 0 && !(options.initTarget instanceof Target) && typeof options.initTarget !== "function") { throw new Error("options.initTarget, when provided, must be a Target or a function that returns or resolves to a Target."); } if (options.onFinalize !== void 0 && typeof options.onFinalize !== "function") { throw new TypeError("options.onFinalize, when provided, must be a function."); } this.format = options.format; this._target = options.target; this._onFinalize = options.onFinalize ?? null; this._initTarget = options.initTarget ?? null; if (this._initTarget instanceof Target) { this._rememberTarget(this._initTarget); } this._muxer = options.format._createMuxer(this); } /** @internal */ _getTargetValidated(request) { assert(this._target instanceof PathedTarget); const result = this._target.getTarget(request); const handleResult = (result2) => { if (!(result2 instanceof Target)) { throw new TypeError("getTarget must return a Target."); } return result2; }; if (result instanceof Promise) { return result.then(handleResult); } else { return handleResult(result); } } /** @internal */ async _getTarget(request) { assert(this._target instanceof PathedTarget); const target = await this._getTargetValidated(request); this._emit("target", { target, request, isRoot: request.isRoot }); if (this.state === "canceled") { await target._close(); } else { this._rememberTarget(target); } return target; } /** @internal */ _rememberTarget(target) { this._unfinalizedTargets.add(target); target.on("finalized", () => this._unfinalizedTargets.delete(target), { once: true }); } /** @internal */ async _getInitTarget() { assert(this._initTarget !== null); if (this._initTarget instanceof Target) { return this._initTarget; } const target = await this._initTarget(); if (this.state === "canceled") { await target._close(); } else { this._rememberTarget(target); } return target; } /** @internal */ _hasInitTarget() { return this._initTarget !== null; } /** @internal */ _getRootTarget() { if (this._rootTarget) { return this._rootTarget; } if (this._rootTargetPromise) { return this._rootTargetPromise; } if (this._target instanceof Target) { this._emit("target", { target: this._target, request: null, isRoot: true }); this._rootTarget = this._target; return this._target; } const request = { path: this._target.rootPath, isRoot: true, mimeType: this.format.mimeType }; const result = this._getTargetValidated(request); const handleResult = (target) => { if (this.state === "canceled") { void target._close(); } else { this._rememberTarget(target); } this._emit("target", { target, request, isRoot: true }); this._rootTarget = target; return target; }; if (result instanceof Promise) { return this._rootTargetPromise = result.then(handleResult); } else { return handleResult(result); } } /** @internal */ _getRootWriter(isMonotonic) { return this._rootWriterPromise ??= (async () => { const target = await this._getRootTarget(); const writer = new Writer(target, typeof isMonotonic === "boolean" ? isMonotonic : isMonotonic(target)); writer.start(); return writer; })(); } /** Adds a video track to the output with the given source. Can only be called before the output is started. */ addVideoTrack(source, metadata = {}) { if (!(source instanceof VideoSource)) { throw new TypeError("source must be a VideoSource."); } validateBaseTrackMetadata(metadata); if (metadata.rotation !== void 0 && ![0, 90, 180, 270].includes(metadata.rotation)) { throw new TypeError(\`Invalid video rotation: \${metadata.rotation}. Has to be 0, 90, 180 or 270.\`); } if (!this.format.supportsVideoRotationMetadata && metadata.rotation) { throw new Error(\`\${this.format._name} does not support video rotation metadata.\`); } if (metadata.frameRate !== void 0 && (!Number.isFinite(metadata.frameRate) || metadata.frameRate <= 0)) { throw new TypeError(\`Invalid video frame rate: \${metadata.frameRate}. Must be a positive number.\`); } const metadataCopy = { ...metadata }; metadataCopy.group ??= this.defaultTrackGroup; return this._addTrack(new OutputVideoTrack(this._tracks.length + 1, this, source, metadataCopy)); } /** Adds an audio track to the output with the given source. Can only be called before the output is started. */ addAudioTrack(source, metadata = {}) { if (!(source instanceof AudioSource)) { throw new TypeError("source must be an AudioSource."); } validateBaseTrackMetadata(metadata); const metadataCopy = { ...metadata }; metadataCopy.group ??= this.defaultTrackGroup; return this._addTrack(new OutputAudioTrack(this._tracks.length + 1, this, source, metadataCopy)); } /** Adds a subtitle track to the output with the given source. Can only be called before the output is started. */ addSubtitleTrack(source, metadata = {}) { if (!(source instanceof SubtitleSource)) { throw new TypeError("source must be a SubtitleSource."); } validateBaseTrackMetadata(metadata); const metadataCopy = { ...metadata }; metadataCopy.group ??= this.defaultTrackGroup; return this._addTrack(new OutputSubtitleTrack(this._tracks.length + 1, this, source, metadataCopy)); } /** * Sets descriptive metadata tags about the media file, such as title, author, date, or cover art. When called * multiple times, only the metadata from the last call will be used. * * Can only be called before the output is started. */ setMetadataTags(tags) { validateMetadataTags(tags); if (this.state !== "pending") { throw new Error("Cannot set metadata tags after output has been started or canceled."); } this._metadataTags = tags; } /** @internal */ _addTrack(track) { if (this.state !== "pending") { throw new Error("Cannot add track after output has been started or canceled."); } if (track.source._connectedTrack) { throw new Error("Source is already used for a track."); } const supportedTrackCounts = this.format.getSupportedTrackCounts(); const presentTracksOfThisType = this._tracks.reduce((count, t2) => count + (t2.type === track.type ? 1 : 0), 0); const maxCount = supportedTrackCounts[track.type].max; if (presentTracksOfThisType === maxCount) { throw new Error(maxCount === 0 ? \`\${this.format._name} does not support \${track.type} tracks.\` : \`\${this.format._name} does not support more than \${maxCount} \${track.type} track\${maxCount === 1 ? "" : "s"}.\`); } const maxTotalCount = supportedTrackCounts.total.max; if (this._tracks.length === maxTotalCount) { throw new Error(\`\${this.format._name} does not support more than \${maxTotalCount} tracks\${maxTotalCount === 1 ? "" : "s"} in total.\`); } if (track.isVideoTrack()) { const supportedVideoCodecs = this.format.getSupportedVideoCodecs(); if (supportedVideoCodecs.length === 0) { throw new Error(\`\${this.format._name} does not support video tracks.\` + this.format._codecUnsupportedHint(track.source._codec)); } else if (!supportedVideoCodecs.includes(track.source._codec)) { throw new Error(\`Codec '\${track.source._codec}' cannot be contained within \${this.format._name}. Supported video codecs are: \${supportedVideoCodecs.map((codec) => \`'\${codec}'\`).join(", ")}.\` + this.format._codecUnsupportedHint(track.source._codec)); } } else if (track.isAudioTrack()) { const supportedAudioCodecs = this.format.getSupportedAudioCodecs(); if (supportedAudioCodecs.length === 0) { throw new Error(\`\${this.format._name} does not support audio tracks.\` + this.format._codecUnsupportedHint(track.source._codec)); } else if (!supportedAudioCodecs.includes(track.source._codec)) { throw new Error(\`Codec '\${track.source._codec}' cannot be contained within \${this.format._name}. Supported audio codecs are: \${supportedAudioCodecs.map((codec) => \`'\${codec}'\`).join(", ")}.\` + this.format._codecUnsupportedHint(track.source._codec)); } } else if (track.isSubtitleTrack()) { const supportedSubtitleCodecs = this.format.getSupportedSubtitleCodecs(); if (supportedSubtitleCodecs.length === 0) { throw new Error(\`\${this.format._name} does not support subtitle tracks.\` + this.format._codecUnsupportedHint(track.source._codec)); } else if (!supportedSubtitleCodecs.includes(track.source._codec)) { throw new Error(\`Codec '\${track.source._codec}' cannot be contained within \${this.format._name}. Supported subtitle codecs are: \${supportedSubtitleCodecs.map((codec) => \`'\${codec}'\`).join(", ")}.\` + this.format._codecUnsupportedHint(track.source._codec)); } } this._tracks.push(track); track.source._connectedTrack = track; return track; } /** * Starts the creation of the output file. This method should be called after all tracks have been added. Only after * the output has started can media samples be added to the tracks. * * @returns A promise that resolves when the output has successfully started and is ready to receive media samples. */ async start() { const supportedTrackCounts = this.format.getSupportedTrackCounts(); for (const trackType of ALL_TRACK_TYPES) { const presentTracksOfThisType = this._tracks.reduce((count, track) => count + (track.type === trackType ? 1 : 0), 0); const minCount = supportedTrackCounts[trackType].min; if (presentTracksOfThisType < minCount) { throw new Error(minCount === supportedTrackCounts[trackType].max ? \`\${this.format._name} requires exactly \${minCount} \${trackType} track\${minCount === 1 ? "" : "s"}.\` : \`\${this.format._name} requires at least \${minCount} \${trackType} track\${minCount === 1 ? "" : "s"}.\`); } } const totalMinCount = supportedTrackCounts.total.min; if (this._tracks.length < totalMinCount) { throw new Error(totalMinCount === supportedTrackCounts.total.max ? \`\${this.format._name} requires exactly \${totalMinCount} track\${totalMinCount === 1 ? "" : "s"}.\` : \`\${this.format._name} requires at least \${totalMinCount} track\${totalMinCount === 1 ? "" : "s"}.\`); } if (this.state === "canceled") { throw new Error("Output has been canceled."); } if (this._startPromise) { Logging._warn("Output has already been started."); return this._startPromise; } return this._startPromise = (async () => { this.state = "started"; const release = await this._mutex.acquire(); try { await this._muxer.start(); const promises = this._tracks.map((track) => track.source._start()); await Promise.all(promises); } finally { release(); } })(); } /** * Resolves with the full MIME type of the output file, including track codecs. * * The returned promise will resolve only once the precise codec strings of all tracks are known. */ getMimeType() { return this._muxer.getMimeType(); } /** * Cancels the creation of the output file, releasing internal resources like encoders and preventing further * samples from being added. * * @returns A promise that resolves once all internal resources have been released. */ async cancel() { if (this._cancelPromise) { Logging._warn("Output has already been canceled."); return this._cancelPromise; } else if (this.state === "finalizing" || this.state === "finalized") { if (this.state === "finalized") { Logging._warn("Output has already been finalized."); } return; } return this._cancelPromise = (async () => { this.state = "canceled"; const release = await this._mutex.acquire(); try { const promises = this._tracks.map((x) => x.source._flushOrWaitForOngoingClose(true)); await Promise.all(promises); await Promise.all([...this._unfinalizedTargets].map((target) => target._close())); this._unfinalizedTargets.clear(); } finally { release(); } })(); } /** * Finalizes the output file. This method must be called after all media samples across all tracks have been added. * Once the Promise returned by this method completes, the output file is ready. */ async finalize() { if (this.state === "pending") { throw new Error("Cannot finalize before starting."); } if (this.state === "canceled") { throw new Error("Cannot finalize after canceling."); } if (this._finalizePromise) { Logging._warn("Output has already been finalized."); return this._finalizePromise; } return this._finalizePromise = (async () => { this.state = "finalizing"; const release = await this._mutex.acquire(); try { const promises = this._tracks.map((x) => x.source._flushOrWaitForOngoingClose(false)); await Promise.all(promises); await this._muxer.finalize(); if (this._rootWriterPromise) { const rootWriter = await this._rootWriterPromise; if (!rootWriter.finalized) { await rootWriter.flush(); await rootWriter.finalize(); } } if (this._onFinalize) { await this._onFinalize(); } this.state = "finalized"; } finally { release(); } })(); } }; // node_modules/mediabunny/dist/modules/src/index.js var MEDIABUNNY_LOADED_SYMBOL = /* @__PURE__ */ Symbol.for("mediabunny loaded"); if (globalThis[MEDIABUNNY_LOADED_SYMBOL]) { Logging._error("[WARNING]\\nMediabunny was loaded twice. This will likely cause Mediabunny not to work correctly. Check if multiple dependencies are importing different versions of Mediabunny, or if something is being bundled incorrectly."); } globalThis[MEDIABUNNY_LOADED_SYMBOL] = true; // packages/video-conversion/src/index.ts var inProgressOperations = /* @__PURE__ */ new Set(); var GIF_DEFAULT_FRAME_DURATION_US = 1e5; var UNSUPPORTED_ERROR_PREFIX = "Unsupported"; var SIZE_LIMIT_ERROR_PREFIX = \`\${UNSUPPORTED_ERROR_PREFIX}: GIF exceeds maximum conversion size\`; var DEFAULT_MAX_TOTAL_PIXELS = 3e8; var operationLock = Promise.resolve(); async function cancelOperations(id) { return inProgressOperations.delete(id); } function padToEven(value) { return value % 2 === 0 ? value : value + 1; } async function convertGifToVideo(id, gifSource, outputMimeType, maxDimensions, maxTotalPixels) { inProgressOperations.add(id); const previousLock = operationLock; let releaseLock = () => { }; operationLock = new Promise((resolve) => { releaseLock = resolve; }); try { await previousLock; if (!inProgressOperations.has(id)) { throw new Error("Operation cancelled"); } if (typeof ImageDecoder === "undefined" || typeof VideoEncoder === "undefined") { throw new Error( \`\${UNSUPPORTED_ERROR_PREFIX}: WebCodecs unavailable\` ); } const isWebm = outputMimeType === "video/webm"; const codec = isWebm ? "vp9" : "avc"; if (!await canEncodeVideo(codec)) { throw new Error( \`\${UNSUPPORTED_ERROR_PREFIX}: encoder codec not supported\` ); } if (!inProgressOperations.has(id)) { throw new Error("Operation cancelled"); } const data = gifSource instanceof ArrayBuffer ? gifSource : await gifSource.arrayBuffer(); if (!inProgressOperations.has(id)) { throw new Error("Operation cancelled"); } const decoder = new ImageDecoder({ data, type: "image/gif" }); try { await decoder.tracks.ready; if (!inProgressOperations.has(id)) { throw new Error("Operation cancelled"); } const track = decoder.tracks.selectedTrack; const frameCount = track?.frameCount ?? 0; if (frameCount === 0) { throw new Error("GIF contains no decodable frames"); } const pixelBudget = maxTotalPixels ?? DEFAULT_MAX_TOTAL_PIXELS; if (pixelBudget > 0) { const { image: probe } = await decoder.decode({ frameIndex: 0 }); const probeWidth = probe.displayWidth; const probeHeight = probe.displayHeight; probe.close(); if (!inProgressOperations.has(id)) { throw new Error("Operation cancelled"); } const totalPixels = probeWidth * probeHeight * frameCount; if (totalPixels > pixelBudget) { throw new Error( \`\${SIZE_LIMIT_ERROR_PREFIX} (\${probeWidth}x\${probeHeight} x \${frameCount} frames = \${totalPixels} pixels; limit is \${pixelBudget})\` ); } } const source = new VideoSampleSource({ codec, bitrate: QUALITY_HIGH, /* * A sparser key frame cadence than mediabunny's 2s default * roughly halves the output size for long GIFs at no * encode-time or quality cost. These looping, autoplaying * GIF replacements don't need fine seek granularity. */ keyFrameInterval: 10 }); const target = new BufferTarget(); const output = new Output({ format: isWebm ? new WebMOutputFormat() : new Mp4OutputFormat(), target }); output.addVideoTrack(source); await output.start(); let timestampSec = 0; for (let i2 = 0; i2 < frameCount; i2++) { if (!inProgressOperations.has(id)) { throw new Error("Operation cancelled"); } const { image } = await decoder.decode({ frameIndex: i2 }); const durationUs = image.duration ?? GIF_DEFAULT_FRAME_DURATION_US; const durationSec = durationUs / 1e6; const srcW = image.displayWidth; const srcH = image.displayHeight; let targetW = srcW; let targetH = srcH; if (maxDimensions && (srcW > maxDimensions || srcH > maxDimensions)) { const scale = Math.min( maxDimensions / srcW, maxDimensions / srcH ); targetW = Math.round(srcW * scale); targetH = Math.round(srcH * scale); } targetW = padToEven(targetW); targetH = padToEven(targetH); let frameForEncode = image; if (targetW !== srcW || targetH !== srcH) { const canvas = new OffscreenCanvas(targetW, targetH); const ctx = canvas.getContext("2d"); if (!ctx) { throw new Error("Failed to create 2D canvas context"); } ctx.drawImage(image, 0, 0, targetW, targetH); frameForEncode = new VideoFrame(canvas, { timestamp: Math.round(timestampSec * 1e6), duration: durationUs }); image.close(); } const sample = new VideoSample(frameForEncode, { timestamp: timestampSec, duration: durationSec }); try { await source.add(sample); } finally { sample.close(); frameForEncode.close(); } timestampSec += durationSec; } await output.finalize(); const out = target.buffer; if (!out || out.byteLength === 0) { throw new Error("Encoder produced empty output"); } return out; } finally { decoder.close(); } } finally { inProgressOperations.delete(id); releaseLock(); } } // packages/video-conversion/src/worker.ts var api = { cancelOperations, convertGifToVideo }; expose(api); /*! Bundled license information: mediabunny/dist/modules/src/misc.js: mediabunny/dist/modules/src/logging.js: mediabunny/dist/modules/src/metadata.js: mediabunny/dist/modules/shared/bitstream.js: mediabunny/dist/modules/shared/aac-misc.js: mediabunny/dist/modules/src/codec.js: mediabunny/dist/modules/shared/ac3-misc.js: mediabunny/dist/modules/src/codec-data.js: mediabunny/dist/modules/src/packet.js: mediabunny/dist/modules/src/isobmff/isobmff-misc.js: mediabunny/dist/modules/src/isobmff/isobmff-reader.js: mediabunny/dist/modules/src/matroska/ebml.js: mediabunny/dist/modules/src/matroska/matroska-misc.js: mediabunny/dist/modules/src/adts/adts-reader.js: mediabunny/dist/modules/src/sample.js: mediabunny/dist/modules/src/encode.js: mediabunny/dist/modules/src/custom-coder.js: mediabunny/dist/modules/src/reader.js: mediabunny/dist/modules/src/muxer.js: mediabunny/dist/modules/src/subtitles.js: mediabunny/dist/modules/src/isobmff/isobmff-boxes.js: mediabunny/dist/modules/src/writer.js: mediabunny/dist/modules/src/target.js: mediabunny/dist/modules/src/isobmff/isobmff-muxer.js: mediabunny/dist/modules/src/matroska/matroska-muxer.js: mediabunny/dist/modules/src/media-source.js: mediabunny/dist/modules/src/output-format.js: mediabunny/dist/modules/src/output.js: mediabunny/dist/modules/src/index.js: (*! * 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/. *) */ `;var B,v,T;function F(){if(v===void 0){let e=new Blob([O],{type:"application/javascript"});T=URL.createObjectURL(e),B=new Worker(T,{type:"module"}),v=_(B)}return v}async function ce(e,n,t,a,i){return F().convertGifToVideo(e,n,t,a,i)}async function de(e){return F().cancelOperations(e)}function le(){v&&(I(v),v=void 0,B=void 0),T&&(URL.revokeObjectURL(T),T=void 0)}export{de as cancelGifToVideoOperations,ce as convertGifToVideo,le as terminateVideoConversionWorker};
| ver. 1.4 |
Github
|
.
| PHP 8.3.33 | ���֧ߧ֧�ѧ�ڧ� ����ѧߧڧ��: 0.02 |
proxy
|
phpinfo
|
���ѧ����ۧܧ�