diff --git a/dist/metamuxer.js b/dist/metamuxer.js index e0054d4..c1def09 100644 --- a/dist/metamuxer.js +++ b/dist/metamuxer.js @@ -128,30 +128,29 @@ var Metamuxer = (() => { var preambleStartRegex = /^WEBVTT(.|\n)*?\n{2}/; var inlineTimestampRegex = /<(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})>/g; var SubtitleParser = class { - #options; - #preambleText = null; - #preambleEmitted = false; constructor(options) { - this.#options = options; + this.preambleText = null; + this.preambleEmitted = false; + this.options = options; } parse(text) { text = text.replaceAll("\r\n", "\n").replaceAll("\r", "\n"); cueBlockHeaderRegex.lastIndex = 0; let match; - if (!this.#preambleText) { + if (!this.preambleText) { if (!preambleStartRegex.test(text)) { let error = new Error("WebVTT preamble incorrect."); - this.#options.error(error); + this.options.error(error); throw error; } match = cueBlockHeaderRegex.exec(text); let preamble = text.slice(0, match?.index ?? text.length).trimEnd(); if (!preamble) { let error = new Error("No WebVTT preamble provided."); - this.#options.error(error); + this.options.error(error); throw error; } - this.#preambleText = preamble; + this.preambleText = preamble; if (match) { text = text.slice(match.index); cueBlockHeaderRegex.lastIndex = 0; @@ -180,13 +179,13 @@ var Metamuxer = (() => { notes }; let meta = {}; - if (!this.#preambleEmitted) { + if (!this.preambleEmitted) { meta.config = { - description: this.#preambleText + description: this.preambleText }; - this.#preambleEmitted = true; + this.preambleEmitted = true; } - this.#options.output(cue, meta); + this.options.output(cue, meta); } } }; @@ -1228,73 +1227,71 @@ var Metamuxer = (() => { } }; var ArrayBufferTargetWriter = class extends Writer2 { - #pos = 0; - #target; - #buffer = new ArrayBuffer(2 ** 16); - #bytes = new Uint8Array(this.#buffer); - #maxPos = 0; constructor(target) { super(); - this.#target = target; + this.pos = 0; + this.buffer = new ArrayBuffer(2 ** 16); + this.bytes = new Uint8Array(this.buffer); + this.maxPos = 0; + this.target = target; } - #ensureSize(size) { - let newLength = this.#buffer.byteLength; + ensureSize(size) { + let newLength = this.buffer.byteLength; while (newLength < size) newLength *= 2; - if (newLength === this.#buffer.byteLength) return; + if (newLength === this.buffer.byteLength) return; let newBuffer = new ArrayBuffer(newLength); let newBytes = new Uint8Array(newBuffer); - newBytes.set(this.#bytes, 0); - this.#buffer = newBuffer; - this.#bytes = newBytes; + newBytes.set(this.bytes, 0); + this.buffer = newBuffer; + this.bytes = newBytes; } write(data) { - this.#ensureSize(this.#pos + data.byteLength); - this.#bytes.set(data, this.#pos); - this.#pos += data.byteLength; - this.#maxPos = Math.max(this.#maxPos, this.#pos); + this.ensureSize(this.pos + data.byteLength); + this.bytes.set(data, this.pos); + this.pos += data.byteLength; + this.maxPos = Math.max(this.maxPos, this.pos); } seek(newPos) { - this.#pos = newPos; + this.pos = newPos; } getPos() { - return this.#pos; + return this.pos; } flush() { } finalize() { - this.#ensureSize(this.#pos); - this.#target.buffer = this.#buffer.slice(0, Math.max(this.#maxPos, this.#pos)); + this.ensureSize(this.pos); + this.target.buffer = this.buffer.slice(0, Math.max(this.maxPos, this.pos)); } getSlice(start, end) { - return this.#bytes.slice(start, end); + return this.bytes.slice(start, end); } }; var StreamTargetWriter = class extends Writer2 { - #pos = 0; - #target; - #sections = []; - #lastFlushEnd = 0; constructor(target) { super(); - this.#target = target; + this.pos = 0; + this.sections = []; + this.lastFlushEnd = 0; + this.target = target; } write(data) { - this.#sections.push({ + this.sections.push({ data: data.slice(), - start: this.#pos + start: this.pos }); - this.#pos += data.byteLength; + this.pos += data.byteLength; } seek(newPos) { - this.#pos = newPos; + this.pos = newPos; } getPos() { - return this.#pos; + return this.pos; } flush() { - if (this.#sections.length === 0) return; + if (this.sections.length === 0) return; let chunks = []; - let sorted = [...this.#sections].sort((a, b) => a.start - b.start); + let sorted = [...this.sections].sort((a, b) => a.start - b.start); chunks.push({ start: sorted[0].start, size: sorted[0].data.byteLength @@ -1313,18 +1310,18 @@ var Metamuxer = (() => { } for (let chunk of chunks) { chunk.data = new Uint8Array(chunk.size); - for (let section of this.#sections) { + for (let section of this.sections) { if (chunk.start <= section.start && section.start < chunk.start + chunk.size) { chunk.data.set(section.data, section.start - chunk.start); } } - if (this.ensureMonotonicity && chunk.start !== this.#lastFlushEnd) { + if (this.ensureMonotonicity && chunk.start !== this.lastFlushEnd) { throw new Error("Internal error: Monotonicity violation."); } - this.#target.options.onData?.(chunk.data, chunk.start); - this.#lastFlushEnd = chunk.start + chunk.data.byteLength; + this.target.options.onData?.(chunk.data, chunk.start); + this.lastFlushEnd = chunk.start + chunk.data.byteLength; } - this.#sections.length = 0; + this.sections.length = 0; } finalize() { } @@ -1332,60 +1329,58 @@ var Metamuxer = (() => { var DEFAULT_CHUNK_SIZE = 2 ** 24; var MAX_CHUNKS_AT_ONCE = 2; var ChunkedStreamTargetWriter = class extends Writer2 { - #pos = 0; - #target; - #chunkSize; - /** - * The data is divided up into fixed-size chunks, whose contents are first filled in RAM and then flushed out. - * A chunk is flushed if all of its contents have been written. - */ - #chunks = []; - #lastFlushEnd = 0; constructor(target) { super(); - this.#target = target; - this.#chunkSize = target.options?.chunkSize ?? DEFAULT_CHUNK_SIZE; - if (!Number.isInteger(this.#chunkSize) || this.#chunkSize < 2 ** 10) { + this.pos = 0; + /** + * The data is divided up into fixed-size chunks, whose contents are first filled in RAM and then flushed out. + * A chunk is flushed if all of its contents have been written. + */ + this.chunks = []; + this.lastFlushEnd = 0; + this.target = target; + this.chunkSize = target.options?.chunkSize ?? DEFAULT_CHUNK_SIZE; + if (!Number.isInteger(this.chunkSize) || this.chunkSize < 2 ** 10) { throw new Error("Invalid StreamTarget options: chunkSize must be an integer not smaller than 1024."); } } write(data) { - this.#writeDataIntoChunks(data, this.#pos); - this.#flushChunks(); - this.#pos += data.byteLength; + this.writeDataIntoChunks(data, this.pos); + this.flushChunks(); + this.pos += data.byteLength; } seek(newPos) { - this.#pos = newPos; + this.pos = newPos; } getPos() { - return this.#pos; + return this.pos; } - #writeDataIntoChunks(data, position) { - let chunkIndex = this.#chunks.findIndex((x) => x.start <= position && position < x.start + this.#chunkSize); - if (chunkIndex === -1) chunkIndex = this.#createChunk(position); - let chunk = this.#chunks[chunkIndex]; + writeDataIntoChunks(data, position) { + let chunkIndex = this.chunks.findIndex((x) => x.start <= position && position < x.start + this.chunkSize); + if (chunkIndex === -1) chunkIndex = this.createChunk(position); + let chunk = this.chunks[chunkIndex]; let relativePosition = position - chunk.start; - let toWrite = data.subarray(0, Math.min(this.#chunkSize - relativePosition, data.byteLength)); + let toWrite = data.subarray(0, Math.min(this.chunkSize - relativePosition, data.byteLength)); chunk.data.set(toWrite, relativePosition); let section = { start: relativePosition, end: relativePosition + toWrite.byteLength }; - this.#insertSectionIntoChunk(chunk, section); - if (chunk.written[0].start === 0 && chunk.written[0].end === this.#chunkSize) { + this.insertSectionIntoChunk(chunk, section); + if (chunk.written[0].start === 0 && chunk.written[0].end === this.chunkSize) { chunk.shouldFlush = true; } - if (this.#chunks.length > MAX_CHUNKS_AT_ONCE) { - for (let i = 0; i < this.#chunks.length - 1; i++) { - this.#chunks[i].shouldFlush = true; + if (this.chunks.length > MAX_CHUNKS_AT_ONCE) { + for (let i = 0; i < this.chunks.length - 1; i++) { + this.chunks[i].shouldFlush = true; } - this.#flushChunks(); + this.flushChunks(); } if (toWrite.byteLength < data.byteLength) { - this.#writeDataIntoChunks(data.subarray(toWrite.byteLength), position + toWrite.byteLength); + this.writeDataIntoChunks(data.subarray(toWrite.byteLength), position + toWrite.byteLength); } } - #insertSectionIntoChunk(chunk, section) { + insertSectionIntoChunk(chunk, section) { let low = 0; let high = chunk.written.length - 1; let index = -1; @@ -1405,39 +1400,39 @@ var Metamuxer = (() => { chunk.written.splice(index + 1, 1); } } - #createChunk(includesPosition) { - let start = Math.floor(includesPosition / this.#chunkSize) * this.#chunkSize; + createChunk(includesPosition) { + let start = Math.floor(includesPosition / this.chunkSize) * this.chunkSize; let chunk = { start, - data: new Uint8Array(this.#chunkSize), + data: new Uint8Array(this.chunkSize), written: [], shouldFlush: false }; - this.#chunks.push(chunk); - this.#chunks.sort((a, b) => a.start - b.start); - return this.#chunks.indexOf(chunk); + this.chunks.push(chunk); + this.chunks.sort((a, b) => a.start - b.start); + return this.chunks.indexOf(chunk); } - #flushChunks(force = false) { - for (let i = 0; i < this.#chunks.length; i++) { - let chunk = this.#chunks[i]; + flushChunks(force = false) { + for (let i = 0; i < this.chunks.length; i++) { + let chunk = this.chunks[i]; if (!chunk.shouldFlush && !force) continue; for (let section of chunk.written) { - if (this.ensureMonotonicity && chunk.start + section.start !== this.#lastFlushEnd) { + if (this.ensureMonotonicity && chunk.start + section.start !== this.lastFlushEnd) { throw new Error("Internal error: Monotonicity violation."); } - this.#target.options.onData?.( + this.target.options.onData?.( chunk.data.subarray(section.start, section.end), chunk.start + section.start ); - this.#lastFlushEnd = chunk.start + section.end; + this.lastFlushEnd = chunk.start + section.end; } - this.#chunks.splice(i--, 1); + this.chunks.splice(i--, 1); } } flush() { } finalize() { - this.#flushChunks(true); + this.flushChunks(true); } }; var FileSystemWritableFileStreamTargetWriter = class extends ChunkedStreamTargetWriter { @@ -1661,54 +1656,40 @@ var Metamuxer = (() => { constructor(output, format) { super(output); this.timestampsMustStartAtZero = true; - this.#auxTarget = new ArrayBufferTarget(); - this.#auxWriter = this.#auxTarget._createWriter(); - this.#auxBoxWriter = new IsobmffBoxWriter(this.#auxWriter); - this.#ftypSize = null; - this.#mdat = null; - this.#trackDatas = []; - this.#creationTime = Math.floor(Date.now() / 1e3) + TIMESTAMP_OFFSET; - this.#finalizedChunks = []; - this.#nextFragmentNumber = 1; - this.#writer = output._writer; - this.#boxWriter = new IsobmffBoxWriter(this.#writer); - this.#format = format; - this.#fastStart = format.options.fastStart ?? (this.#writer instanceof ArrayBufferTargetWriter ? "in-memory" : false); - if (this.#fastStart === "in-memory" || this.#fastStart === "fragmented") { - this.#writer.ensureMonotonicity = true; + this.auxTarget = new ArrayBufferTarget(); + this.auxWriter = this.auxTarget._createWriter(); + this.auxBoxWriter = new IsobmffBoxWriter(this.auxWriter); + this.ftypSize = null; + this.mdat = null; + this.trackDatas = []; + this.creationTime = Math.floor(Date.now() / 1e3) + TIMESTAMP_OFFSET; + this.finalizedChunks = []; + this.nextFragmentNumber = 1; + this.writer = output._writer; + this.boxWriter = new IsobmffBoxWriter(this.writer); + this.fastStart = format.options.fastStart ?? (this.writer instanceof ArrayBufferTargetWriter ? "in-memory" : false); + if (this.fastStart === "in-memory" || this.fastStart === "fragmented") { + this.writer.ensureMonotonicity = true; } } - #writer; - #boxWriter; - #format; - #fastStart; - #auxTarget; - #auxWriter; - #auxBoxWriter; - #ftypSize; - #mdat; - #trackDatas; - #creationTime; - #finalizedChunks; - #nextFragmentNumber; start() { const holdsAvc = this.output._tracks.some((x) => x.type === "video" && x.source._codec === "avc"); - this.#boxWriter.writeBox(ftyp({ + this.boxWriter.writeBox(ftyp({ holdsAvc, - fragmented: this.#fastStart === "fragmented" + fragmented: this.fastStart === "fragmented" })); - this.#ftypSize = this.#writer.getPos(); - if (this.#fastStart === "in-memory") { - this.#mdat = mdat(false); - } else if (this.#fastStart === "fragmented") { + this.ftypSize = this.writer.getPos(); + if (this.fastStart === "in-memory") { + this.mdat = mdat(false); + } else if (this.fastStart === "fragmented") { } else { - this.#mdat = mdat(true); - this.#boxWriter.writeBox(this.#mdat); + this.mdat = mdat(true); + this.boxWriter.writeBox(this.mdat); } - this.#writer.flush(); + this.writer.flush(); } - #getVideoTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getVideoTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -1737,12 +1718,12 @@ var Metamuxer = (() => { currentChunk: null, compactlyCodedChunkTable: [] }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getAudioTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getAudioTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -1769,12 +1750,12 @@ var Metamuxer = (() => { currentChunk: null, compactlyCodedChunkTable: [] }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getSubtitleTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getSubtitleTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -1804,37 +1785,37 @@ var Metamuxer = (() => { nextSourceId: 0, cueToSourceId: /* @__PURE__ */ new WeakMap() }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); this.validateAndNormalizeTimestamp(track, 0, true); return newTrackData; } addEncodedVideoChunk(track, chunk, meta) { - const trackData = this.#getVideoTrackData(track, meta); + const trackData = this.getVideoTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === "key"); - let sample = this.#createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); - this.#registerSample(trackData, sample); + let sample = this.createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + this.registerSample(trackData, sample); } addEncodedAudioChunk(track, chunk, meta) { - const trackData = this.#getAudioTrackData(track, meta); + const trackData = this.getAudioTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === "key"); - let sample = this.#createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); - this.#registerSample(trackData, sample); + let sample = this.createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + this.registerSample(trackData, sample); } addSubtitleCue(track, cue, meta) { - const trackData = this.#getSubtitleTrackData(track, meta); + const trackData = this.getSubtitleTrackData(track, meta); this.validateAndNormalizeTimestamp(trackData.track, 1e6 * cue.timestamp, true); if (track.source._codec === "webvtt") { trackData.cueQueue.push(cue); - this.#processWebVTTCues(trackData, cue.timestamp); + this.processWebVTTCues(trackData, cue.timestamp); } else { } } - #processWebVTTCues(trackData, until) { + processWebVTTCues(trackData, until) { while (trackData.cueQueue.length > 0) { let timestamps = /* @__PURE__ */ new Set([]); for (let cue of trackData.cueQueue) { @@ -1850,15 +1831,15 @@ var Metamuxer = (() => { break; } if (trackData.lastCueEndTimestamp < sampleStart) { - this.#auxWriter.seek(0); + this.auxWriter.seek(0); let box2 = vtte(); - this.#auxBoxWriter.writeBox(box2); - let body2 = this.#auxWriter.getSlice(0, this.#auxWriter.getPos()); - let sample2 = this.#createSampleForTrack(trackData, body2, trackData.lastCueEndTimestamp, sampleStart - trackData.lastCueEndTimestamp, "key"); - this.#registerSample(trackData, sample2); + this.auxBoxWriter.writeBox(box2); + let body2 = this.auxWriter.getSlice(0, this.auxWriter.getPos()); + let sample2 = this.createSampleForTrack(trackData, body2, trackData.lastCueEndTimestamp, sampleStart - trackData.lastCueEndTimestamp, "key"); + this.registerSample(trackData, sample2); trackData.lastCueEndTimestamp = sampleStart; } - this.#auxWriter.seek(0); + this.auxWriter.seek(0); for (let i = 0; i < trackData.cueQueue.length; i++) { let cue = trackData.cueQueue[i]; if (cue.timestamp >= sampleEnd) { @@ -1874,21 +1855,21 @@ var Metamuxer = (() => { } if (cue.notes) { let box3 = vtta(cue.notes); - this.#auxBoxWriter.writeBox(box3); + this.auxBoxWriter.writeBox(box3); } let box2 = vttc(cue.text, containsTimestamp ? sampleStart : null, cue.identifier ?? null, cue.settings ?? null, sourceId ?? null); - this.#auxBoxWriter.writeBox(box2); + this.auxBoxWriter.writeBox(box2); if (endTimestamp === sampleEnd) { trackData.cueQueue.splice(i--, 1); } } - let body = this.#auxWriter.getSlice(0, this.#auxWriter.getPos()); - let sample = this.#createSampleForTrack(trackData, body, sampleStart, sampleEnd - sampleStart, "key"); - this.#registerSample(trackData, sample); + let body = this.auxWriter.getSlice(0, this.auxWriter.getPos()); + let sample = this.createSampleForTrack(trackData, body, sampleStart, sampleEnd - sampleStart, "key"); + this.registerSample(trackData, sample); trackData.lastCueEndTimestamp = sampleEnd; } } - #createSampleForTrack(trackData, data, timestamp, duration, type) { + createSampleForTrack(trackData, data, timestamp, duration, type) { let sample = { timestamp, decodeTimestamp: timestamp, @@ -1902,7 +1883,7 @@ var Metamuxer = (() => { }; return sample; } - #processTimestamps(trackData) { + processTimestamps(trackData) { if (trackData.timestampProcessingQueue.length === 0) { return; } @@ -1918,7 +1899,7 @@ var Metamuxer = (() => { let delta = Math.round(timescaleUnits - trackData.lastTimescaleUnits); trackData.lastTimescaleUnits += delta; trackData.lastSample.timescaleUnitsToNextSample = delta; - if (this.#fastStart !== "fragmented") { + if (this.fastStart !== "fragmented") { let lastTableEntry = last(trackData.timeToSampleTable); assert(lastTableEntry); if (lastTableEntry.sampleCount === 1) { @@ -1957,7 +1938,7 @@ var Metamuxer = (() => { } } else { trackData.lastTimescaleUnits = 0; - if (this.#fastStart !== "fragmented") { + if (this.fastStart !== "fragmented") { trackData.timeToSampleTable.push({ sampleCount: 1, sampleDelta: durationInTimescale @@ -1972,19 +1953,19 @@ var Metamuxer = (() => { } trackData.timestampProcessingQueue.length = 0; } - #registerSample(trackData, sample) { - if (this.#fastStart === "fragmented") { + registerSample(trackData, sample) { + if (this.fastStart === "fragmented") { trackData.sampleQueue.push(sample); - this.#interleaveSamples(); + this.interleaveSamples(); } else { - this.#addSampleToTrack(trackData, sample); + this.addSampleToTrack(trackData, sample); } } - #addSampleToTrack(trackData, sample) { + addSampleToTrack(trackData, sample) { if (sample.type === "key") { - this.#processTimestamps(trackData); + this.processTimestamps(trackData); } - if (this.#fastStart !== "fragmented") { + if (this.fastStart !== "fragmented") { trackData.samples.push(sample); } let beginNewChunk = false; @@ -1992,8 +1973,8 @@ var Metamuxer = (() => { beginNewChunk = true; } else { let currentChunkDuration = sample.timestamp - trackData.currentChunk.startTimestamp; - if (this.#fastStart === "fragmented") { - const keyFrameQueuedEverywhere = this.#trackDatas.every((otherTrackData) => { + if (this.fastStart === "fragmented") { + const keyFrameQueuedEverywhere = this.trackDatas.every((otherTrackData) => { if (trackData === otherTrackData) { return sample.type === "key"; } @@ -2002,7 +1983,7 @@ var Metamuxer = (() => { }); if (currentChunkDuration >= 1 && keyFrameQueuedEverywhere) { beginNewChunk = true; - this.#finalizeFragment(); + this.finalizeFragment(); } } else { beginNewChunk = currentChunkDuration >= 0.5; @@ -2010,7 +1991,7 @@ var Metamuxer = (() => { } if (beginNewChunk) { if (trackData.currentChunk) { - this.#finalizeCurrentChunk(trackData); + this.finalizeCurrentChunk(trackData); } trackData.currentChunk = { startTimestamp: sample.timestamp, @@ -2023,11 +2004,11 @@ var Metamuxer = (() => { trackData.currentChunk.samples.push(sample); trackData.timestampProcessingQueue.push(sample); } - #finalizeCurrentChunk(trackData) { - assert(this.#fastStart !== "fragmented"); + finalizeCurrentChunk(trackData) { + assert(this.fastStart !== "fragmented"); if (!trackData.currentChunk) return; trackData.finalizedChunks.push(trackData.currentChunk); - this.#finalizedChunks.push(trackData.currentChunk); + this.finalizedChunks.push(trackData.currentChunk); if (trackData.compactlyCodedChunkTable.length === 0 || last(trackData.compactlyCodedChunkTable).samplesPerChunk !== trackData.currentChunk.samples.length) { trackData.compactlyCodedChunkTable.push({ firstChunk: trackData.finalizedChunks.length, @@ -2035,22 +2016,22 @@ var Metamuxer = (() => { samplesPerChunk: trackData.currentChunk.samples.length }); } - if (this.#fastStart === "in-memory") { + if (this.fastStart === "in-memory") { trackData.currentChunk.offset = 0; return; } - trackData.currentChunk.offset = this.#writer.getPos(); + trackData.currentChunk.offset = this.writer.getPos(); for (let sample of trackData.currentChunk.samples) { assert(sample.data); - this.#writer.write(sample.data); + this.writer.write(sample.data); sample.data = null; } - this.#writer.flush(); + this.writer.flush(); } - #interleaveSamples() { - assert(this.#fastStart === "fragmented"); + interleaveSamples() { + assert(this.fastStart === "fragmented"); for (const track of this.output._tracks) { - if (!track.source._closed && !this.#trackDatas.some((x) => x.track === track)) { + if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) { return; } } @@ -2058,7 +2039,7 @@ var Metamuxer = (() => { while (true) { let trackWithMinTimestamp = null; let minTimestamp = Infinity; - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { if (trackData.sampleQueue.length === 0 && !trackData.track.source._closed) { break outer; } @@ -2071,99 +2052,99 @@ var Metamuxer = (() => { break; } let sample = trackWithMinTimestamp.sampleQueue.shift(); - this.#addSampleToTrack(trackWithMinTimestamp, sample); + this.addSampleToTrack(trackWithMinTimestamp, sample); } } - #finalizeFragment(flushWriter = true) { - assert(this.#fastStart === "fragmented"); - let fragmentNumber = this.#nextFragmentNumber++; + finalizeFragment(flushWriter = true) { + assert(this.fastStart === "fragmented"); + let fragmentNumber = this.nextFragmentNumber++; if (fragmentNumber === 1) { - let movieBox = moov(this.#trackDatas, this.#creationTime, true); - this.#boxWriter.writeBox(movieBox); + let movieBox = moov(this.trackDatas, this.creationTime, true); + this.boxWriter.writeBox(movieBox); } - let moofOffset = this.#writer.getPos(); - let moofBox = moof(fragmentNumber, this.#trackDatas); - this.#boxWriter.writeBox(moofBox); + let moofOffset = this.writer.getPos(); + let moofBox = moof(fragmentNumber, this.trackDatas); + this.boxWriter.writeBox(moofBox); { let mdatBox = mdat(false); let totalTrackSampleSize = 0; - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { assert(trackData.currentChunk); for (let sample of trackData.currentChunk.samples) { totalTrackSampleSize += sample.size; } } - let mdatSize = this.#boxWriter.measureBox(mdatBox) + totalTrackSampleSize; + let mdatSize = this.boxWriter.measureBox(mdatBox) + totalTrackSampleSize; if (mdatSize >= 2 ** 32) { mdatBox.largeSize = true; - mdatSize = this.#boxWriter.measureBox(mdatBox) + totalTrackSampleSize; + mdatSize = this.boxWriter.measureBox(mdatBox) + totalTrackSampleSize; } mdatBox.size = mdatSize; - this.#boxWriter.writeBox(mdatBox); + this.boxWriter.writeBox(mdatBox); } - for (let trackData of this.#trackDatas) { - trackData.currentChunk.offset = this.#writer.getPos(); + for (let trackData of this.trackDatas) { + trackData.currentChunk.offset = this.writer.getPos(); trackData.currentChunk.moofOffset = moofOffset; for (let sample of trackData.currentChunk.samples) { - this.#writer.write(sample.data); + this.writer.write(sample.data); sample.data = null; } } - let endPos = this.#writer.getPos(); - this.#writer.seek(this.#boxWriter.offsets.get(moofBox)); - let newMoofBox = moof(fragmentNumber, this.#trackDatas); - this.#boxWriter.writeBox(newMoofBox); - this.#writer.seek(endPos); - for (let trackData of this.#trackDatas) { + let endPos = this.writer.getPos(); + this.writer.seek(this.boxWriter.offsets.get(moofBox)); + let newMoofBox = moof(fragmentNumber, this.trackDatas); + this.boxWriter.writeBox(newMoofBox); + this.writer.seek(endPos); + for (let trackData of this.trackDatas) { trackData.finalizedChunks.push(trackData.currentChunk); - this.#finalizedChunks.push(trackData.currentChunk); + this.finalizedChunks.push(trackData.currentChunk); trackData.currentChunk = null; } if (flushWriter) { - this.#writer.flush(); + this.writer.flush(); } } onTrackClose(track) { if (track.type === "subtitle" && track.source._codec === "webvtt") { - let trackData = this.#trackDatas.find((x) => x.track === track); + let trackData = this.trackDatas.find((x) => x.track === track); if (trackData) { - this.#processWebVTTCues(trackData, Infinity); + this.processWebVTTCues(trackData, Infinity); } } - if (this.#fastStart === "fragmented") { - this.#interleaveSamples(); + if (this.fastStart === "fragmented") { + this.interleaveSamples(); } } /** Finalizes the file, making it ready for use. Must be called after all video and audio chunks have been added. */ finalize() { - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { if (trackData.type === "subtitle" && trackData.track.source._codec === "webvtt") { - this.#processWebVTTCues(trackData, Infinity); + this.processWebVTTCues(trackData, Infinity); } } - if (this.#fastStart === "fragmented") { - for (let trackData of this.#trackDatas) { + if (this.fastStart === "fragmented") { + for (let trackData of this.trackDatas) { for (let sample of trackData.sampleQueue) { - this.#addSampleToTrack(trackData, sample); + this.addSampleToTrack(trackData, sample); } - this.#processTimestamps(trackData); + this.processTimestamps(trackData); } - this.#finalizeFragment(false); + this.finalizeFragment(false); } else { - for (let trackData of this.#trackDatas) { - this.#processTimestamps(trackData); - this.#finalizeCurrentChunk(trackData); + for (let trackData of this.trackDatas) { + this.processTimestamps(trackData); + this.finalizeCurrentChunk(trackData); } } - if (this.#fastStart === "in-memory") { - assert(this.#mdat); + if (this.fastStart === "in-memory") { + assert(this.mdat); let mdatSize; for (let i = 0; i < 2; i++) { - let movieBox2 = moov(this.#trackDatas, this.#creationTime); - let movieBoxSize = this.#boxWriter.measureBox(movieBox2); - mdatSize = this.#boxWriter.measureBox(this.#mdat); - let currentChunkPos = this.#writer.getPos() + movieBoxSize + mdatSize; - for (let chunk of this.#finalizedChunks) { + let movieBox2 = moov(this.trackDatas, this.creationTime); + let movieBoxSize = this.boxWriter.measureBox(movieBox2); + mdatSize = this.boxWriter.measureBox(this.mdat); + let currentChunkPos = this.writer.getPos() + movieBoxSize + mdatSize; + for (let chunk of this.finalizedChunks) { chunk.offset = currentChunkPos; for (let { data } of chunk.samples) { assert(data); @@ -2172,43 +2153,43 @@ var Metamuxer = (() => { } } if (currentChunkPos < 2 ** 32) break; - if (mdatSize >= 2 ** 32) this.#mdat.largeSize = true; + if (mdatSize >= 2 ** 32) this.mdat.largeSize = true; } - let movieBox = moov(this.#trackDatas, this.#creationTime); - this.#boxWriter.writeBox(movieBox); - this.#mdat.size = mdatSize; - this.#boxWriter.writeBox(this.#mdat); - for (let chunk of this.#finalizedChunks) { + let movieBox = moov(this.trackDatas, this.creationTime); + this.boxWriter.writeBox(movieBox); + this.mdat.size = mdatSize; + this.boxWriter.writeBox(this.mdat); + for (let chunk of this.finalizedChunks) { for (let sample of chunk.samples) { assert(sample.data); - this.#writer.write(sample.data); + this.writer.write(sample.data); sample.data = null; } } - } else if (this.#fastStart === "fragmented") { - let startPos = this.#writer.getPos(); - let mfraBox = mfra(this.#trackDatas); - this.#boxWriter.writeBox(mfraBox); - let mfraBoxSize = this.#writer.getPos() - startPos; - this.#writer.seek(this.#writer.getPos() - 4); - this.#boxWriter.writeU32(mfraBoxSize); + } else if (this.fastStart === "fragmented") { + let startPos = this.writer.getPos(); + let mfraBox = mfra(this.trackDatas); + this.boxWriter.writeBox(mfraBox); + let mfraBoxSize = this.writer.getPos() - startPos; + this.writer.seek(this.writer.getPos() - 4); + this.boxWriter.writeU32(mfraBoxSize); } else { - assert(this.#mdat); - assert(this.#ftypSize !== null); - let mdatPos = this.#boxWriter.offsets.get(this.#mdat); + assert(this.mdat); + assert(this.ftypSize !== null); + let mdatPos = this.boxWriter.offsets.get(this.mdat); assert(mdatPos !== void 0); - let mdatSize = this.#writer.getPos() - mdatPos; - this.#mdat.size = mdatSize; - this.#mdat.largeSize = mdatSize >= 2 ** 32; - this.#boxWriter.patchBox(this.#mdat); - let movieBox = moov(this.#trackDatas, this.#creationTime); - if (typeof this.#fastStart === "object") { - this.#writer.seek(this.#ftypSize); - this.#boxWriter.writeBox(movieBox); - let remainingBytes = mdatPos - this.#writer.getPos(); - this.#boxWriter.writeBox(free(remainingBytes)); + let mdatSize = this.writer.getPos() - mdatPos; + this.mdat.size = mdatSize; + this.mdat.largeSize = mdatSize >= 2 ** 32; + this.boxWriter.patchBox(this.mdat); + let movieBox = moov(this.trackDatas, this.creationTime); + if (typeof this.fastStart === "object") { + this.writer.seek(this.ftypSize); + this.boxWriter.writeBox(movieBox); + let remainingBytes = mdatPos - this.writer.getPos(); + this.boxWriter.writeBox(free(remainingBytes)); } else { - this.#boxWriter.writeBox(movieBox); + this.boxWriter.writeBox(movieBox); } } } @@ -2302,8 +2283,8 @@ var Metamuxer = (() => { constructor(output, format) { super(output); this.timestampsMustStartAtZero = false; - this.#helper = new Uint8Array(8); - this.#helperView = new DataView(this.#helper.buffer); + this.helper = new Uint8Array(8); + this.helperView = new DataView(this.helper.buffer); /** * Stores the position from the start of the file to where EBML elements have been written. This is used to * rewrite/edit elements that were already added before, and to measure sizes of things. @@ -2311,176 +2292,161 @@ var Metamuxer = (() => { this.offsets = /* @__PURE__ */ new WeakMap(); /** Same as offsets, but stores position where the element's data starts (after ID and size fields). */ this.dataOffsets = /* @__PURE__ */ new WeakMap(); - this.#trackDatas = []; - this.#segment = null; - this.#segmentInfo = null; - this.#seekHead = null; - this.#tracksElement = null; - this.#segmentDuration = null; - this.#cues = null; - this.#currentCluster = null; - this.#currentClusterMsTimestamp = null; - this.#trackDatasInCurrentCluster = /* @__PURE__ */ new Set(); - this.#duration = 0; - this.#writer = output._writer; - this.#format = format; - if (this.#format.options.streamable) { - this.#writer.ensureMonotonicity = true; + this.trackDatas = []; + this.segment = null; + this.segmentInfo = null; + this.seekHead = null; + this.tracksElement = null; + this.segmentDuration = null; + this.cues = null; + this.currentCluster = null; + this.currentClusterMsTimestamp = null; + this.trackDatasInCurrentCluster = /* @__PURE__ */ new Set(); + this.duration = 0; + this.writer = output._writer; + this.format = format; + if (this.format.options.streamable) { + this.writer.ensureMonotonicity = true; } } - #writer; - #format; - #helper; - #helperView; - #trackDatas; - #segment; - #segmentInfo; - #seekHead; - #tracksElement; - #segmentDuration; - #cues; - #currentCluster; - #currentClusterMsTimestamp; - #trackDatasInCurrentCluster; - #duration; - #writeByte(value) { - this.#helperView.setUint8(0, value); - this.#writer.write(this.#helper.subarray(0, 1)); + 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)); + 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); + writeFloat64(value) { + this.helperView.setFloat64(0, value, false); + this.writer.write(this.helper); } - #writeUnsignedInt(value, width = measureUnsignedInt(value)) { + writeUnsignedInt(value, width = measureUnsignedInt(value)) { let pos = 0; switch (width) { case 6: - this.#helperView.setUint8(pos++, value / 2 ** 40 | 0); + this.helperView.setUint8(pos++, value / 2 ** 40 | 0); case 5: - this.#helperView.setUint8(pos++, value / 2 ** 32 | 0); + this.helperView.setUint8(pos++, value / 2 ** 32 | 0); case 4: - this.#helperView.setUint8(pos++, value >> 24); + this.helperView.setUint8(pos++, value >> 24); case 3: - this.#helperView.setUint8(pos++, value >> 16); + this.helperView.setUint8(pos++, value >> 16); case 2: - this.#helperView.setUint8(pos++, value >> 8); + this.helperView.setUint8(pos++, value >> 8); case 1: - this.#helperView.setUint8(pos++, value); + this.helperView.setUint8(pos++, value); break; default: throw new Error("Bad UINT size " + width); } - this.#writer.write(this.#helper.subarray(0, pos)); + this.writer.write(this.helper.subarray(0, pos)); } - #writeSignedInt(value, width = measureSignedInt(value)) { + writeSignedInt(value, width = measureSignedInt(value)) { if (value < 0) { value += 2 ** (width * 8); } - this.#writeUnsignedInt(value, width); + this.writeUnsignedInt(value, width); } writeEBMLVarInt(value, width = measureEBMLVarInt(value)) { let pos = 0; switch (width) { case 1: - this.#helperView.setUint8(pos++, 1 << 7 | value); + this.helperView.setUint8(pos++, 1 << 7 | value); break; case 2: - this.#helperView.setUint8(pos++, 1 << 6 | value >> 8); - this.#helperView.setUint8(pos++, value); + 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); + 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); + 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); + 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); + 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 VINT size " + width); } - this.#writer.write(this.#helper.subarray(0, pos)); + this.writer.write(this.helper.subarray(0, pos)); } // Assumes the string is ASCII - #writeString(str) { - this.#writer.write(new Uint8Array(str.split("").map((x) => x.charCodeAt(0)))); + writeString(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); + this.writer.write(data); } else if (Array.isArray(data)) { for (let elem of data) { this.writeEBML(elem); } } else { - this.offsets.set(data, this.#writer.getPos()); - this.#writeUnsignedInt(data.id); + this.offsets.set(data, this.writer.getPos()); + this.writeUnsignedInt(data.id); if (Array.isArray(data.data)) { - let sizePos = this.#writer.getPos(); + let sizePos = this.writer.getPos(); let sizeSize = data.size === -1 ? 1 : data.size ?? 4; if (data.size === -1) { - this.#writeByte(255); + this.writeByte(255); } else { - this.#writer.seek(this.#writer.getPos() + sizeSize); + this.writer.seek(this.writer.getPos() + sizeSize); } - let startPos = this.#writer.getPos(); + let startPos = this.writer.getPos(); this.dataOffsets.set(data, startPos); this.writeEBML(data.data); if (data.size !== -1) { - let size = this.#writer.getPos() - startPos; - let endPos = this.#writer.getPos(); - this.#writer.seek(sizePos); + let size = this.writer.getPos() - startPos; + let endPos = this.writer.getPos(); + this.writer.seek(sizePos); this.writeEBMLVarInt(size, sizeSize); - this.#writer.seek(endPos); + this.writer.seek(endPos); } } else if (typeof data.data === "number") { let size = data.size ?? measureUnsignedInt(data.data); this.writeEBMLVarInt(size); - this.#writeUnsignedInt(data.data, size); + this.writeUnsignedInt(data.data, size); } else if (typeof data.data === "string") { this.writeEBMLVarInt(data.data.length); - this.#writeString(data.data); + this.writeString(data.data); } else if (data.data instanceof Uint8Array) { this.writeEBMLVarInt(data.data.byteLength, data.size); - this.#writer.write(data.data); + this.writer.write(data.data); } else if (data.data instanceof EBMLFloat32) { this.writeEBMLVarInt(4); - this.#writeFloat32(data.data.value); + this.writeFloat32(data.data.value); } else if (data.data instanceof EBMLFloat64) { this.writeEBMLVarInt(8); - this.#writeFloat64(data.data.value); + this.writeFloat64(data.data.value); } else if (data.data instanceof EBMLSignedInt) { let size = data.size ?? measureSignedInt(data.data.value); this.writeEBMLVarInt(size); - this.#writeSignedInt(data.data.value, size); + this.writeSignedInt(data.data.value, size); } } } beforeTrackAdd(track) { - if (!(this.#format instanceof WebMOutputFormat)) { + if (!(this.format instanceof WebMOutputFormat)) { return; } if (track.type === "video") { @@ -2500,21 +2466,21 @@ var Metamuxer = (() => { } } start() { - this.#writeEBMLHeader(); - if (!this.#format.options.streamable) { - this.#createSeekHead(); + this.writeEBMLHeader(); + if (!this.format.options.streamable) { + this.createSeekHead(); } - this.#createSegmentInfo(); - this.#createCues(); - this.#writer.flush(); + this.createSegmentInfo(); + this.createCues(); + this.writer.flush(); } - #writeEBMLHeader() { + writeEBMLHeader() { let ebmlHeader = { id: 440786851 /* EBML */, data: [ { id: 17030 /* EBMLVersion */, data: 1 }, { id: 17143 /* EBMLReadVersion */, data: 1 }, { id: 17138 /* EBMLMaxIDLength */, data: 4 }, { id: 17139 /* EBMLMaxSizeLength */, data: 8 }, - { id: 17026 /* DocType */, data: this.#format instanceof WebMOutputFormat ? "webm" : "matroska" }, + { id: 17026 /* DocType */, data: this.format instanceof WebMOutputFormat ? "webm" : "matroska" }, { id: 17031 /* DocTypeVersion */, data: 2 }, { id: 17029 /* DocTypeReadVersion */, data: 2 } ] }; @@ -2524,7 +2490,7 @@ var Metamuxer = (() => { * 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. */ - #createSeekHead() { + createSeekHead() { const kaxCues = new Uint8Array([28, 83, 187, 107]); const kaxInfo = new Uint8Array([21, 73, 169, 102]); const kaxTracks = new Uint8Array([22, 84, 174, 107]); @@ -2542,23 +2508,23 @@ var Metamuxer = (() => { { id: 21420 /* SeekPosition */, size: 5, data: 0 } ] } ] }; - this.#seekHead = seekHead; + this.seekHead = seekHead; } - #createSegmentInfo() { + createSegmentInfo() { let segmentDuration = { id: 17545 /* Duration */, data: new EBMLFloat64(0) }; - this.#segmentDuration = segmentDuration; + this.segmentDuration = segmentDuration; let segmentInfo = { id: 357149030 /* Info */, data: [ { id: 2807729 /* TimestampScale */, data: 1e6 }, { id: 19840 /* MuxingApp */, data: APP_NAME }, { id: 22337 /* WritingApp */, data: APP_NAME }, - !this.#format.options.streamable ? segmentDuration : null + !this.format.options.streamable ? segmentDuration : null ] }; - this.#segmentInfo = segmentInfo; + this.segmentInfo = segmentInfo; } - #createTracks() { + createTracks() { let tracksElement = { id: 374648427 /* Tracks */, data: [] }; - this.#tracksElement = tracksElement; - for (let trackData of this.#trackDatas) { + this.tracksElement = tracksElement; + for (let trackData of this.trackDatas) { tracksElement.data.push({ id: 174 /* TrackEntry */, data: [ { id: 215 /* TrackNumber */, data: trackData.track.id }, { id: 29637 /* TrackUID */, data: trackData.track.id }, @@ -2601,28 +2567,28 @@ var Metamuxer = (() => { ] }); } } - #createSegment() { + createSegment() { let segment = { id: 408125543 /* Segment */, - size: this.#format.options.streamable ? -1 : SEGMENT_SIZE_BYTES, + size: this.format.options.streamable ? -1 : SEGMENT_SIZE_BYTES, data: [ - !this.#format.options.streamable ? this.#seekHead : null, - this.#segmentInfo, - this.#tracksElement + !this.format.options.streamable ? this.seekHead : null, + this.segmentInfo, + this.tracksElement ] }; - this.#segment = segment; + this.segment = segment; this.writeEBML(segment); } - #createCues() { - this.#cues = { id: 475249515 /* Cues */, data: [] }; + createCues() { + this.cues = { id: 475249515 /* Cues */, data: [] }; } - get #segmentDataOffset() { - assert(this.#segment); - return this.dataOffsets.get(this.#segment); + get segmentDataOffset() { + assert(this.segment); + return this.dataOffsets.get(this.segment); } - #getVideoTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getVideoTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -2642,12 +2608,12 @@ var Metamuxer = (() => { chunkQueue: [], lastWrittenMsTimestamp: null }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getAudioTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getAudioTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -2665,12 +2631,12 @@ var Metamuxer = (() => { chunkQueue: [], lastWrittenMsTimestamp: null }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getSubtitleTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getSubtitleTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -2686,31 +2652,31 @@ var Metamuxer = (() => { chunkQueue: [], lastWrittenMsTimestamp: null }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } addEncodedVideoChunk(track, chunk, meta) { - const trackData = this.#getVideoTrackData(track, meta); + const trackData = this.getVideoTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === "key"); - let videoChunk = this.#createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); - if (track.source._codec === "vp9") this.#fixVP9ColorSpace(trackData, videoChunk); + let videoChunk = this.createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + if (track.source._codec === "vp9") this.fixVP9ColorSpace(trackData, videoChunk); trackData.chunkQueue.push(videoChunk); - this.#interleaveChunks(); + this.interleaveChunks(); } addEncodedAudioChunk(track, chunk, meta) { - const trackData = this.#getAudioTrackData(track, meta); + const trackData = this.getAudioTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === "key"); - let audioChunk = this.#createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + let audioChunk = this.createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); trackData.chunkQueue.push(audioChunk); - this.#interleaveChunks(); + this.interleaveChunks(); } addSubtitleCue(track, cue, meta) { - const trackData = this.#getSubtitleTrackData(track, meta); + const trackData = this.getSubtitleTrackData(track, meta); const timestamp = this.validateAndNormalizeTimestamp(trackData.track, 1e6 * cue.timestamp, true); let bodyText = cue.text; const timestampMs = Math.floor(timestamp * 1e3); @@ -2724,13 +2690,13 @@ var Metamuxer = (() => { const additions = `${cue.settings ?? ""} ${cue.identifier ?? ""} ${cue.notes ?? ""}`; - let subtitleChunk = this.#createInternalChunk(body, timestamp, cue.duration, "key", additions.trim() ? textEncoder.encode(additions) : null); + let subtitleChunk = this.createInternalChunk(body, timestamp, cue.duration, "key", additions.trim() ? textEncoder.encode(additions) : null); trackData.chunkQueue.push(subtitleChunk); - this.#interleaveChunks(); + this.interleaveChunks(); } - #interleaveChunks() { + interleaveChunks() { for (const track of this.output._tracks) { - if (!track.source._closed && !this.#trackDatas.some((x) => x.track === track)) { + if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) { return; } } @@ -2738,7 +2704,7 @@ ${cue.notes ?? ""}`; while (true) { let trackWithMinTimestamp = null; let minTimestamp = Infinity; - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { if (trackData.chunkQueue.length === 0 && !trackData.track.source._closed) { break outer; } @@ -2751,14 +2717,14 @@ ${cue.notes ?? ""}`; break; } let chunk = trackWithMinTimestamp.chunkQueue.shift(); - this.#writeBlock(trackWithMinTimestamp, chunk); + this.writeBlock(trackWithMinTimestamp, chunk); } - this.#writer.flush(); + 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. */ // http://downloads.webmproject.org/docs/vp9/vp9-bitstream_superframe-and-uncompressed-header_v1.0.pdf - #fixVP9ColorSpace(trackData, chunk) { + fixVP9ColorSpace(trackData, chunk) { if (chunk.type !== "key") return; if (!trackData.info.decoderConfig.colorSpace || !trackData.info.decoderConfig.colorSpace.matrix) return; let i = 0; @@ -2787,7 +2753,7 @@ ${cue.notes ?? ""}`; writeBits(chunk.data, i + 0, i + 3, colorSpaceID); } /** Converts a read-only external chunk into an internal one for easier use. */ - #createInternalChunk(data, timestamp, duration, type, additions = null) { + createInternalChunk(data, timestamp, duration, type, additions = null) { let internalChunk = { data, type, @@ -2798,13 +2764,13 @@ ${cue.notes ?? ""}`; return internalChunk; } /** Writes a block containing media data to the file. */ - #writeBlock(trackData, chunk) { - if (!this.#segment) { - this.#createTracks(); - this.#createSegment(); + writeBlock(trackData, chunk) { + if (!this.segment) { + this.createTracks(); + this.createSegment(); } let msTimestamp = Math.floor(1e3 * chunk.timestamp); - const keyFrameQueuedEverywhere = this.#trackDatas.every((otherTrackData) => { + const keyFrameQueuedEverywhere = this.trackDatas.every((otherTrackData) => { if (otherTrackData.track.source._closed) { return true; } @@ -2814,10 +2780,10 @@ ${cue.notes ?? ""}`; const firstQueuedSample = otherTrackData.chunkQueue[0]; return firstQueuedSample && firstQueuedSample.type === "key"; }); - if (!this.#currentCluster || keyFrameQueuedEverywhere && msTimestamp - this.#currentClusterMsTimestamp >= 1e3) { - this.#createNewCluster(msTimestamp); + if (!this.currentCluster || keyFrameQueuedEverywhere && msTimestamp - this.currentClusterMsTimestamp >= 1e3) { + this.createNewCluster(msTimestamp); } - let relativeTimestamp = msTimestamp - this.#currentClusterMsTimestamp; + let relativeTimestamp = msTimestamp - this.currentClusterMsTimestamp; if (relativeTimestamp < 0) { return; } @@ -2856,39 +2822,39 @@ ${cue.notes ?? ""}`; ] }; this.writeEBML(blockGroup); } - this.#duration = Math.max(this.#duration, msTimestamp + msDuration); + this.duration = Math.max(this.duration, msTimestamp + msDuration); trackData.lastWrittenMsTimestamp = msTimestamp; - this.#trackDatasInCurrentCluster.add(trackData); + this.trackDatasInCurrentCluster.add(trackData); } /** Creates a new Cluster element to contain media chunks. */ - #createNewCluster(msTimestamp) { - if (this.#currentCluster && !this.#format.options.streamable) { - this.#finalizeCurrentCluster(); + createNewCluster(msTimestamp) { + if (this.currentCluster && !this.format.options.streamable) { + this.finalizeCurrentCluster(); } - this.#currentCluster = { + this.currentCluster = { id: 524531317 /* Cluster */, - size: this.#format.options.streamable ? -1 : CLUSTER_SIZE_BYTES, + size: this.format.options.streamable ? -1 : CLUSTER_SIZE_BYTES, data: [ { id: 231 /* Timestamp */, data: msTimestamp } ] }; - this.writeEBML(this.#currentCluster); - this.#currentClusterMsTimestamp = msTimestamp; - this.#trackDatasInCurrentCluster.clear(); + this.writeEBML(this.currentCluster); + this.currentClusterMsTimestamp = msTimestamp; + this.trackDatasInCurrentCluster.clear(); } - #finalizeCurrentCluster() { - assert(this.#currentCluster); - let clusterSize = this.#writer.getPos() - this.dataOffsets.get(this.#currentCluster); - let endPos = this.#writer.getPos(); - this.#writer.seek(this.offsets.get(this.#currentCluster) + 4); + finalizeCurrentCluster() { + assert(this.currentCluster); + let clusterSize = this.writer.getPos() - this.dataOffsets.get(this.currentCluster); + let endPos = this.writer.getPos(); + this.writer.seek(this.offsets.get(this.currentCluster) + 4); this.writeEBMLVarInt(clusterSize, CLUSTER_SIZE_BYTES); - this.#writer.seek(endPos); - let clusterOffsetFromSegment = this.offsets.get(this.#currentCluster) - this.#segmentDataOffset; - assert(this.#cues); - this.#cues.data.push({ id: 187 /* CuePoint */, data: [ - { id: 179 /* CueTime */, data: this.#currentClusterMsTimestamp }, + this.writer.seek(endPos); + let clusterOffsetFromSegment = this.offsets.get(this.currentCluster) - this.segmentDataOffset; + assert(this.cues); + this.cues.data.push({ id: 187 /* CuePoint */, data: [ + { id: 179 /* CueTime */, data: this.currentClusterMsTimestamp }, // We only write out cues for tracks that have at least one chunk in this cluster - ...[...this.#trackDatasInCurrentCluster].map((trackData) => { + ...[...this.trackDatasInCurrentCluster].map((trackData) => { return { id: 183 /* CueTrackPositions */, data: [ { id: 247 /* CueTrack */, data: trackData.track.id }, { id: 241 /* CueClusterPosition */, data: clusterOffsetFromSegment } @@ -2897,38 +2863,38 @@ ${cue.notes ?? ""}`; ] }); } onTrackClose() { - this.#interleaveChunks(); + this.interleaveChunks(); } /** Finalizes the file, making it ready for use. Must be called after all media chunks have been added. */ finalize() { - if (!this.#segment) { - this.#createTracks(); - this.#createSegment(); + if (!this.segment) { + this.createTracks(); + this.createSegment(); } - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { while (trackData.chunkQueue.length > 0) { - this.#writeBlock(trackData, trackData.chunkQueue.shift()); + this.writeBlock(trackData, trackData.chunkQueue.shift()); } } - if (!this.#format.options.streamable && this.#currentCluster) { - this.#finalizeCurrentCluster(); + if (!this.format.options.streamable && this.currentCluster) { + this.finalizeCurrentCluster(); } - assert(this.#cues); - this.writeEBML(this.#cues); - if (!this.#format.options.streamable) { - let endPos = this.#writer.getPos(); - let segmentSize = this.#writer.getPos() - this.#segmentDataOffset; - this.#writer.seek(this.offsets.get(this.#segment) + 4); + assert(this.cues); + this.writeEBML(this.cues); + if (!this.format.options.streamable) { + let endPos = this.writer.getPos(); + let segmentSize = this.writer.getPos() - this.segmentDataOffset; + this.writer.seek(this.offsets.get(this.segment) + 4); this.writeEBMLVarInt(segmentSize, SEGMENT_SIZE_BYTES); - this.#segmentDuration.data = new EBMLFloat64(this.#duration); - this.#writer.seek(this.offsets.get(this.#segmentDuration)); - this.writeEBML(this.#segmentDuration); - this.#seekHead.data[0].data[1].data = this.offsets.get(this.#cues) - this.#segmentDataOffset; - this.#seekHead.data[1].data[1].data = this.offsets.get(this.#segmentInfo) - this.#segmentDataOffset; - this.#seekHead.data[2].data[1].data = this.offsets.get(this.#tracksElement) - this.#segmentDataOffset; - this.#writer.seek(this.offsets.get(this.#seekHead)); - this.writeEBML(this.#seekHead); - this.#writer.seek(endPos); + this.segmentDuration.data = new EBMLFloat64(this.duration); + this.writer.seek(this.offsets.get(this.segmentDuration)); + this.writeEBML(this.segmentDuration); + this.seekHead.data[0].data[1].data = this.offsets.get(this.cues) - this.segmentDataOffset; + this.seekHead.data[1].data[1].data = this.offsets.get(this.segmentInfo) - this.segmentDataOffset; + this.seekHead.data[2].data[1].data = this.offsets.get(this.tracksElement) - this.segmentDataOffset; + this.writer.seek(this.offsets.get(this.seekHead)); + this.writeEBML(this.seekHead); + this.writer.seek(endPos); } } }; diff --git a/dist/metamuxer.min.js b/dist/metamuxer.min.js index 7860fd5..4dd1785 100644 --- a/dist/metamuxer.min.js +++ b/dist/metamuxer.min.js @@ -1,10 +1,10 @@ -"use strict";var Metamuxer=(()=>{var ze=Object.defineProperty;var dt=Object.getOwnPropertyDescriptor;var ct=Object.getOwnPropertyNames;var ft=Object.prototype.hasOwnProperty;var mt=(r,t)=>{for(var e in t)ze(r,e,{get:t[e],enumerable:!0})},ht=(r,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of ct(t))!ft.call(r,o)&&o!==e&&ze(r,o,{get:()=>t[o],enumerable:!(i=dt(t,o))||i.enumerable});return r};var pt=r=>ht(ze({},"__esModule",{value:!0}),r);var kr={};mt(kr,{AUDIO_CODECS:()=>Y,ArrayBufferTarget:()=>K,AudioBufferSource:()=>Me,AudioDataSource:()=>ve,AudioSource:()=>O,CanvasSource:()=>_e,EncodedAudioChunkSource:()=>Oe,EncodedVideoChunkSource:()=>Se,FileSystemWritableFileStreamTarget:()=>he,MediaSource:()=>B,MediaStreamAudioTrackSource:()=>Ve,MediaStreamVideoTrackSource:()=>Ee,MkvOutputFormat:()=>se,Mp4OutputFormat:()=>ke,Output:()=>Ue,OutputFormat:()=>V,SUBTITLE_CODECS:()=>xe,StreamTarget:()=>L,SubtitleSource:()=>N,Target:()=>v,TextSubtitleSource:()=>Ie,VIDEO_CODECS:()=>q,VideoFrameSource:()=>Ae,VideoSource:()=>E,WebMOutputFormat:()=>R});function d(r){if(!r)throw new Error("Assertion failed.")}var F=r=>r&&r[r.length-1],I=r=>r>=0&&r<2**32,U=(r,t,e)=>{let i=0;for(let o=t;o>a;i<<=1,i|=u}return i},je=(r,t,e,i)=>{for(let o=t;o>e-o-1<r instanceof ArrayBuffer?new Uint8Array(r):new Uint8Array(r.buffer,r.byteOffset,r.byteLength),x=new TextEncoder,W={bt709:1,bt470bg:5,smpte170m:6},H={bt709:1,smpte170m:6,"iec61966-2-1":13},$={rgb:0,bt709:1,bt470bg:5,smpte170m:6},ue=r=>!!r&&!!r.primaries&&!!r.transfer&&!!r.matrix&&r.fullRange!==void 0,Pe=r=>r instanceof ArrayBuffer||typeof SharedArrayBuffer<"u"&&r instanceof SharedArrayBuffer||ArrayBuffer.isView(r)&&!(r instanceof DataView);var Z=/(?:(.+?)\n)?((?:\d{2}:)?\d{2}:\d{2}.\d{3})\s+-->\s+((?:\d{2}:)?\d{2}:\d{2}.\d{3})/g,bt=/^WEBVTT(.|\n)*?\n{2}/,j=/<(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})>/g,le=class{#e;#r=null;#i=!1;constructor(t){this.#e=t}parse(t){t=t.replaceAll(`\r +"use strict";var Metamuxer=(()=>{var De=Object.defineProperty;var ct=Object.getOwnPropertyDescriptor;var dt=Object.getOwnPropertyNames;var ft=Object.prototype.hasOwnProperty;var mt=(t,i)=>{for(var e in i)De(t,e,{get:i[e],enumerable:!0})},pt=(t,i,e,r)=>{if(i&&typeof i=="object"||typeof i=="function")for(let s of dt(i))!ft.call(t,s)&&s!==e&&De(t,s,{get:()=>i[s],enumerable:!(r=ct(i,s))||r.enumerable});return t};var ht=t=>pt(De({},"__esModule",{value:!0}),t);var yr={};mt(yr,{AUDIO_CODECS:()=>Y,ArrayBufferTarget:()=>K,AudioBufferSource:()=>Ve,AudioDataSource:()=>Oe,AudioSource:()=>E,CanvasSource:()=>Ae,EncodedAudioChunkSource:()=>Ee,EncodedVideoChunkSource:()=>xe,FileSystemWritableFileStreamTarget:()=>pe,MediaSource:()=>W,MediaStreamAudioTrackSource:()=>Me,MediaStreamVideoTrackSource:()=>_e,MkvOutputFormat:()=>oe,Mp4OutputFormat:()=>ye,Output:()=>ze,OutputFormat:()=>M,SUBTITLE_CODECS:()=>Se,StreamTarget:()=>L,SubtitleSource:()=>R,Target:()=>O,TextSubtitleSource:()=>Ie,VIDEO_CODECS:()=>q,VideoFrameSource:()=>ve,VideoSource:()=>_,WebMOutputFormat:()=>P});function c(t){if(!t)throw new Error("Assertion failed.")}var F=t=>t&&t[t.length-1],I=t=>t>=0&&t<2**32,z=(t,i,e)=>{let r=0;for(let s=i;s>a;r<<=1,r|=u}return r},je=(t,i,e,r)=>{for(let s=i;s>e-s-1<t instanceof ArrayBuffer?new Uint8Array(t):new Uint8Array(t.buffer,t.byteOffset,t.byteLength),S=new TextEncoder,N={bt709:1,bt470bg:5,smpte170m:6},H={bt709:1,smpte170m:6,"iec61966-2-1":13},$={rgb:0,bt709:1,bt470bg:5,smpte170m:6},ue=t=>!!t&&!!t.primaries&&!!t.transfer&&!!t.matrix&&t.fullRange!==void 0,Ue=t=>t instanceof ArrayBuffer||typeof SharedArrayBuffer<"u"&&t instanceof SharedArrayBuffer||ArrayBuffer.isView(t)&&!(t instanceof DataView);var Z=/(?:(.+?)\n)?((?:\d{2}:)?\d{2}:\d{2}.\d{3})\s+-->\s+((?:\d{2}:)?\d{2}:\d{2}.\d{3})/g,bt=/^WEBVTT(.|\n)*?\n{2}/,j=/<(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})>/g,le=class{constructor(i){this.preambleText=null;this.preambleEmitted=!1;this.options=i}parse(i){i=i.replaceAll(`\r `,` `).replaceAll("\r",` -`),Z.lastIndex=0;let e;if(!this.#r){if(!bt.test(t)){let o=new Error("WebVTT preamble incorrect.");throw this.#e.error(o),o}e=Z.exec(t);let i=t.slice(0,e?.index??t.length).trimEnd();if(!i){let o=new Error("No WebVTT preamble provided.");throw this.#e.error(o),o}this.#r=i,e&&(t=t.slice(e.index),Z.lastIndex=0)}for(;e=Z.exec(t);){let i=t.slice(0,e.index),o=e[1],s=e.index+e[0].length,n=t.indexOf(` -`,s)+1,a=t.slice(s,n).trim(),u=t.indexOf(` +`),Z.lastIndex=0;let e;if(!this.preambleText){if(!bt.test(i)){let s=new Error("WebVTT preamble incorrect.");throw this.options.error(s),s}e=Z.exec(i);let r=i.slice(0,e?.index??i.length).trimEnd();if(!r){let s=new Error("No WebVTT preamble provided.");throw this.options.error(s),s}this.preambleText=r,e&&(i=i.slice(e.index),Z.lastIndex=0)}for(;e=Z.exec(i);){let r=i.slice(0,e.index),s=e[1],o=e.index+e[0].length,n=i.indexOf(` +`,o)+1,a=i.slice(o,n).trim(),u=i.indexOf(` -`,s);u===-1&&(u=t.length);let f=de(e[2]),m=de(e[3])-f,g=t.slice(n,u).trim();t=t.slice(u).trimStart(),Z.lastIndex=0;let M={timestamp:f/1e3,duration:m/1e3,text:g,identifier:o,settings:a,notes:i},y={};this.#i||(y.config={description:this.#r},this.#i=!0),this.#e.output(M,y)}}},Tt=/(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})/,de=r=>{let t=Tt.exec(r);if(!t)throw new Error("Expected match.");return 60*60*1e3*Number(t[1]||"0")+60*1e3*Number(t[2])+1e3*Number(t[3])+Number(t[4])},ce=r=>{let t=Math.floor(r/36e5),e=Math.floor(r%(60*60*1e3)/(60*1e3)),i=Math.floor(r%(60*1e3)/1e3),o=r%1e3;return t.toString().padStart(2,"0")+":"+e.toString().padStart(2,"0")+":"+i.toString().padStart(2,"0")+"."+o.toString().padStart(3,"0")};var J=class{constructor(t){this.writer=t;this.helper=new Uint8Array(8);this.helperView=new DataView(this.helper.buffer);this.offsets=new WeakMap}writeU32(t){this.helperView.setUint32(0,t,!1),this.writer.write(this.helper.subarray(0,4))}writeU64(t){this.helperView.setUint32(0,Math.floor(t/2**32),!1),this.helperView.setUint32(4,t,!1),this.writer.write(this.helper.subarray(0,8))}writeAscii(t){for(let e=0;e[(r%256+256)%256],p=r=>(_.setUint16(0,r,!1),[h[0],h[1]]),gt=r=>(_.setInt16(0,r,!1),[h[0],h[1]]),Ke=r=>(_.setUint32(0,r,!1),[h[1],h[2],h[3]]),l=r=>(_.setUint32(0,r,!1),[h[0],h[1],h[2],h[3]]),Le=r=>(_.setInt32(0,r,!1),[h[0],h[1],h[2],h[3]]),P=r=>(_.setUint32(0,Math.floor(r/2**32),!1),_.setUint32(4,r,!1),[h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7]]),Ne=r=>(_.setInt16(0,2**8*r,!1),[h[0],h[1]]),A=r=>(_.setInt32(0,2**16*r,!1),[h[0],h[1],h[2],h[3]]),Re=r=>(_.setInt32(0,2**30*r,!1),[h[0],h[1],h[2],h[3]]),Be=(r,t)=>{let e=[],i=r;do{let o=i&127;i>>=7,e.length>0&&(o|=128),e.push(o),t!==void 0&&t--}while(i>0||t);return e.reverse()},w=(r,t=!1)=>{let e=Array(r.length).fill(null).map((i,o)=>r.charCodeAt(o));return t&&e.push(0),e},De=r=>{let t=null;for(let e of r)(!t||e.timestamp>t.timestamp)&&(t=e);return t},Xe=r=>{let t=r*(Math.PI/180),e=Math.cos(t),i=Math.sin(t);return[e,i,0,-i,e,0,0,0,1]},Ge=Xe(0),qe=r=>[A(r[0]),A(r[1]),Re(r[2]),A(r[3]),A(r[4]),Re(r[5]),A(r[6]),A(r[7]),Re(r[8])],b=(r,t,e)=>({type:r,contents:t&&new Uint8Array(t.flat(10)),children:e}),T=(r,t,e,i,o)=>b(r,[C(t),Ke(e),i??[]],o),Ye=r=>{let t=512;return r.fragmented?b("ftyp",[w("iso5"),l(t),w("iso5"),w("iso6"),w("mp41")]):b("ftyp",[w("isom"),l(t),w("isom"),r.holdsAvc?w("avc1"):[],w("mp41")])},me=r=>({type:"mdat",largeSize:r}),Ze=r=>({type:"free",size:r}),ee=(r,t,e=!1)=>b("moov",void 0,[Ct(t,r),...r.map(i=>yt(i,t)),e?Jt(r):null]),Ct=(r,t)=>{let e=S(Math.max(0,...t.filter(n=>n.samples.length>0).map(n=>{let a=De(n.samples);return a.timestamp+a.duration})),fe),i=Math.max(0,...t.map(n=>n.track.id))+1,o=!I(r)||!I(e),s=o?P:l;return T("mvhd",+o,0,[s(r),s(r),l(fe),s(e),A(1),Ne(1),Array(10).fill(0),qe(Ge),Array(24).fill(0),l(i)])},yt=(r,t)=>b("trak",void 0,[wt(r,t),kt(r,t)]),wt=(r,t)=>{let e=De(r.samples),i=S(e?e.timestamp+e.duration:0,fe),o=!I(t)||!I(i),s=o?P:l,n;if(r.type==="video"){let a=r.track.metadata.rotation;n=a===void 0||typeof a=="number"?Xe(a??0):a}else n=Ge;return T("tkhd",+o,3,[s(t),s(t),l(r.track.id),l(0),s(i),Array(8).fill(0),p(0),p(r.track.id),Ne(r.type==="audio"?1:0),p(0),qe(n),A(r.type==="video"?r.info.width:0),A(r.type==="video"?r.info.height:0)])},kt=(r,t)=>b("mdia",void 0,[xt(r,t),_t(r),Et(r)]),xt=(r,t)=>{let e=De(r.samples),i=S(e?e.timestamp+e.duration:0,r.timescale),o=!I(t)||!I(i),s=o?P:l;return T("mdhd",+o,0,[s(t),s(t),l(r.timescale),s(i),p(21956),p(0)])},St={video:"vide",audio:"soun",subtitle:"text"},At={video:"VideoHandler",audio:"SoundHandler",subtitle:"TextHandler"},_t=r=>T("hdlr",0,0,[w("mhlr"),w(St[r.type]),l(0),l(0),l(0),w(At[r.type],!0)]),Et=r=>b("minf",void 0,[Vt[r.type](),It(),Pt(r)]),Ot=()=>T("vmhd",0,1,[p(0),p(0),p(0),p(0)]),vt=()=>T("smhd",0,0,[p(0),p(0)]),Mt=()=>T("nmhd",0,0),Vt={video:Ot,audio:vt,subtitle:Mt},It=()=>b("dinf",void 0,[Ut()]),Ut=()=>T("dref",0,0,[l(1)],[zt()]),zt=()=>T("url ",0,1),Pt=r=>{let t=r.compositionTimeOffsetTable.length>1||r.compositionTimeOffsetTable.some(e=>e.sampleCompositionTimeOffset!==0);return b("stbl",void 0,[Rt(r),Lt(r),Xt(r),Gt(r),qt(r),Yt(r),t?Zt(r):null])},Rt=r=>{let t;return r.type==="video"?t=Bt(ur[r.track.source._codec],r):r.type==="audio"?t=Ht(dr[r.track.source._codec],r):r.type==="subtitle"&&(t=Qt(fr[r.track.source._codec],r)),d(t),T("stsd",0,0,[l(1)],[t])},Bt=(r,t)=>b(r,[Array(6).fill(0),p(1),p(0),p(0),Array(12).fill(0),p(t.info.width),p(t.info.height),l(4718592),l(4718592),l(0),p(1),Array(32).fill(0),p(24),gt(65535)],[lr[t.track.source._codec](t),ue(t.info.decoderConfig.colorSpace)?Nt(t):null]),Nt=r=>b("colr",[w("nclx"),p(W[r.info.decoderConfig.colorSpace.primaries]),p(H[r.info.decoderConfig.colorSpace.transfer]),p($[r.info.decoderConfig.colorSpace.matrix]),C((r.info.decoderConfig.colorSpace.fullRange?1:0)<<7)]),Dt=r=>r.info.decoderConfig&&b("avcC",[...z(r.info.decoderConfig.description)]),Ft=r=>r.info.decoderConfig&&b("hvcC",[...z(r.info.decoderConfig.description)]),Qe=r=>{if(!r.info.decoderConfig)return null;let t=r.info.decoderConfig;d(t.colorSpace);let e=t.codec.split("."),i=Number(e[1]),o=Number(e[2]),a=(Number(e[3])<<4)+(0<<1)+Number(t.colorSpace.fullRange);return T("vpcC",1,0,[C(i),C(o),C(a),C(2),C(2),C(2),p(0)])},Wt=()=>{let e=(1<<7)+1;return b("av1C",[e,0,0,0])},Ht=(r,t)=>b(r,[Array(6).fill(0),p(1),p(0),p(0),l(0),p(t.info.numberOfChannels),p(16),p(0),p(0),A(t.info.sampleRate)],[cr[t.track.source._codec](t)]),$t=r=>{let e=[...z(r.info.decoderConfig.description??new ArrayBuffer(0))];return e=[...C(64),...C(21),...Ke(0),...l(0),...l(0),...C(5),...Be(e.length),...e],e=[...p(1),...C(0),...C(4),...Be(e.length),...e,...C(6),...C(1),...C(2)],e=[...C(3),...Be(e.length),...e],T("esds",0,0,e)},jt=r=>{let t=3840,e=0,i=r.info.decoderConfig?.description;if(i){d(i.byteLength<18);let o=ArrayBuffer.isView(i)?new DataView(i.buffer,i.byteOffset,i.byteLength):new DataView(i);t=o.getUint16(10,!0),e=o.getInt16(14,!0)}return b("dOps",[C(0),C(r.info.numberOfChannels),p(t),l(r.info.sampleRate),Ne(e),C(0)])},Qt=(r,t)=>b(r,[Array(6).fill(0),p(1)],[mr[t.track.source._codec](t)]),Kt=r=>b("vttC",[...x.encode(r.info.config.description)]);var Lt=r=>T("stts",0,0,[l(r.timeToSampleTable.length),r.timeToSampleTable.map(t=>[l(t.sampleCount),l(t.sampleDelta)])]),Xt=r=>{if(r.samples.every(e=>e.type==="key"))return null;let t=[...r.samples.entries()].filter(([,e])=>e.type==="key");return T("stss",0,0,[l(t.length),t.map(([e])=>l(e+1))])},Gt=r=>T("stsc",0,0,[l(r.compactlyCodedChunkTable.length),r.compactlyCodedChunkTable.map(t=>[l(t.firstChunk),l(t.samplesPerChunk),l(1)])]),qt=r=>T("stsz",0,0,[l(0),l(r.samples.length),r.samples.map(t=>l(t.size))]),Yt=r=>r.finalizedChunks.length>0&&F(r.finalizedChunks).offset>=2**32?T("co64",0,0,[l(r.finalizedChunks.length),r.finalizedChunks.map(t=>P(t.offset))]):T("stco",0,0,[l(r.finalizedChunks.length),r.finalizedChunks.map(t=>l(t.offset))]),Zt=r=>T("ctts",0,0,[l(r.compositionTimeOffsetTable.length),r.compositionTimeOffsetTable.map(t=>[l(t.sampleCount),l(t.sampleCompositionTimeOffset)])]),Jt=r=>b("mvex",void 0,r.map(er)),er=r=>T("trex",0,0,[l(r.track.id),l(1),l(0),l(0),l(0)]),Fe=(r,t)=>b("moof",void 0,[tr(r),...t.map(rr)]),tr=r=>T("mfhd",0,0,[l(r)]),Je=r=>{let t=0,e=0,i=0,o=0,s=r.type==="delta";return e|=+s,s?t|=1:t|=2,t<<24|e<<16|i<<8|o},rr=r=>b("traf",void 0,[ir(r),or(r),sr(r)]),ir=r=>{d(r.currentChunk);let t=0;t|=8,t|=16,t|=32,t|=131072;let e=r.currentChunk.samples[1]??r.currentChunk.samples[0],i={duration:e.timescaleUnitsToNextSample,size:e.size,flags:Je(e)};return T("tfhd",0,t,[l(r.track.id),l(i.duration),l(i.size),l(i.flags)])},or=r=>(d(r.currentChunk),T("tfdt",1,0,[P(S(r.currentChunk.startTimestamp,r.timescale))])),sr=r=>{d(r.currentChunk);let t=r.currentChunk.samples.map(k=>k.timescaleUnitsToNextSample),e=r.currentChunk.samples.map(k=>k.size),i=r.currentChunk.samples.map(Je),o=r.currentChunk.samples.map(k=>S(k.timestamp-k.decodeTimestamp,r.timescale)),s=new Set(t),n=new Set(e),a=new Set(i),u=new Set(o),f=a.size===2&&i[0]!==i[1],c=s.size>1,m=n.size>1,g=!f&&a.size>1,M=u.size>1||[...u].some(k=>k!==0),y=0;return y|=1,y|=4*+f,y|=256*+c,y|=512*+m,y|=1024*+g,y|=2048*+M,T("trun",1,y,[l(r.currentChunk.samples.length),l(r.currentChunk.offset-r.currentChunk.moofOffset||0),f?l(i[0]):[],r.currentChunk.samples.map((k,D)=>[c?l(t[D]):[],m?l(e[D]):[],g?l(i[D]):[],M?Le(o[D]):[]])])},et=r=>b("mfra",void 0,[...r.map(nr),ar()]),nr=(r,t)=>T("tfra",1,0,[l(r.track.id),l(63),l(r.finalizedChunks.length),r.finalizedChunks.map(i=>[P(S(i.startTimestamp,r.timescale)),P(i.moofOffset),l(t+1),l(1),l(1)])]),ar=()=>T("mfro",0,0,[l(0)]),tt=()=>b("vtte"),rt=(r,t,e,i,o)=>b("vttc",void 0,[o!==null?b("vsid",[Le(o)]):null,e!==null?b("iden",[...x.encode(e)]):null,t!==null?b("ctim",[...x.encode(ce(t))]):null,i!==null?b("sttg",[...x.encode(i)]):null,b("payl",[...x.encode(r)])]),it=r=>b("vtta",[...x.encode(r)]),ur={avc:"avc1",hevc:"hvc1",vp8:"vp08",vp9:"vp09",av1:"av01"},lr={avc:Dt,hevc:Ft,vp8:Qe,vp9:Qe,av1:Wt},dr={aac:"mp4a",opus:"Opus"},cr={aac:$t,opus:jt},fr={webvtt:"wvtt"},mr={webvtt:Kt};var Q=class{constructor(t){this.trackTimestampInfo=new WeakMap;this.output=t}beforeTrackAdd(t){}onTrackClose(t){}validateAndNormalizeTimestamp(t,e,i){let o=e/1e6,s=this.trackTimestampInfo.get(t);if(!s){if(!i)throw new Error("First frame must be a key frame.");if(this.timestampsMustStartAtZero&&o>0)throw new Error(`Timestamps must start at zero (got ${o}s).`);s={timestampOffset:o,maxTimestamp:t.source._offsetTimestamps?0:o,lastKeyFrameTimestamp:t.source._offsetTimestamps?0:o},this.trackTimestampInfo.set(t,s)}if(t.source._offsetTimestamps&&(o-=s.timestampOffset),o<0)throw new Error(`Timestamps must be non-negative (got ${o}s).`);if(oi.start-o.start);t.push({start:e[0].start,size:e[0].data.byteLength});for(let i=1;iu.start<=e&&epr){for(let u=0;u=t.written[s+1].start;)t.written[s].end=Math.max(t.written[s].end,t.written[s+1].end),t.written.splice(s+1,1)}#l(t){let i={start:Math.floor(t/this.#i)*this.#i,data:new Uint8Array(this.#i),written:[],shouldFlush:!1};return this.#t.push(i),this.#t.sort((o,s)=>o.start-s.start),this.#t.indexOf(i)}#n(t=!1){for(let e=0;et.stream.write({type:"write",data:e,position:i}),chunkSize:t.options?.chunkSize}))}};var ot=(r,t,e)=>{if(r==="avc"){let i=100;t<=768&&e<=432?i=66:t<=1920&&e<=1080&&(i=77);let o=0,s=t>1920||e>1080?50:41,n=i.toString(16).padStart(2,"0"),a=o.toString(16).padStart(2,"0"),u=s.toString(16).padStart(2,"0");return`avc1.${n}${a}${u}`}else if(r==="hevc"){let i=0,o=1,s=Array(32).fill(0);s[o]=1;let n=parseInt(s.reverse().join(""),2).toString(16).replace(/^0+/,""),a="L",u=120;return t<=1280&&e<=720?u=93:t<=1920&&e<=1080?u=120:t<=3840&&e<=2160?u=150:(a="H",u=180),`hev1.${i===0?"":String.fromCharCode(65+i-1)}${o}.${n}.${a}${u}.B0`}else{if(r==="vp8")return"vp8";if(r==="vp9"){let i="00",o;return t<=854&&e<=480?o="21":t<=1280&&e<=720?o="31":t<=1920&&e<=1080?o="41":t<=3840&&e<=2160?o="51":o="61",`vp09.${i}.${o}.08`}else if(r==="av1"){let o;return t<=854&&e<=480?o="01":t<=1280&&e<=720?o="03":t<=1920&&e<=1080?o="04":t<=3840&&e<=2160?o="07":o="09",`av01.0.${o}M.08`}}throw new TypeError(`Unhandled codec '${r}'.`)},st=(r,t,e)=>{if(r==="aac")return t>=2&&e<=24e3?"mp4a.40.29":e<=24e3?"mp4a.40.5":"mp4a.40.2";if(r==="opus")return"opus";if(r==="vorbis")return"vorbis";throw new TypeError(`Unhandled codec '${r}'.`)},Te=r=>{if(!r)throw new TypeError("Video chunk metadata must be provided.");if(typeof r!="object")throw new TypeError("Video chunk metadata must be an object.");if(!r.decoderConfig)throw new TypeError("Video chunk metadata must include a decoder configuration.");if(typeof r.decoderConfig!="object")throw new TypeError("Video chunk metadata decoder configuration must be an object.");if(typeof r.decoderConfig.codec!="string")throw new TypeError("Video chunk metadata decoder configuration must specify a codec string.");if(!Number.isInteger(r.decoderConfig.codedWidth)||r.decoderConfig.codedWidth<=0)throw new TypeError("Video chunk metadata decoder configuration must specify a valid codedWidth (positive integer).");if(!Number.isInteger(r.decoderConfig.codedHeight)||r.decoderConfig.codedHeight<=0)throw new TypeError("Video chunk metadata decoder configuration must specify a valid codedHeight (positive integer).");if(r.decoderConfig.description!==void 0&&!Pe(r.decoderConfig.description))throw new TypeError("Video chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view.");if(r.decoderConfig.colorSpace!==void 0){let{colorSpace:t}=r.decoderConfig;if(typeof t!="object")throw new TypeError("Video chunk metadata decoder configuration colorSpace, when provided, must be an object.");let e=Object.keys(W);if(t.primaries!=null&&!e.includes(t.primaries))throw new TypeError(`Video chunk metadata decoder configuration colorSpace primaries, when defined, must be one of ${e.join(", ")}.`);let i=Object.keys(H);if(t.transfer!=null&&!i.includes(t.transfer))throw new TypeError(`Video chunk metadata decoder configuration colorSpace transfer, when defined, must be one of ${i.join(", ")}.`);let o=Object.keys($);if(t.matrix!=null&&!o.includes(t.matrix))throw new TypeError(`Video chunk metadata decoder configuration colorSpace matrix, when defined, must be one of ${o.join(", ")}.`);if(t.fullRange!=null&&typeof t.fullRange!="boolean")throw new TypeError("Video chunk metadata decoder configuration colorSpace fullRange, when defined, must be a boolean.")}if((r.decoderConfig.codec.startsWith("avc1")||r.decoderConfig.codec.startsWith("avc3"))&&!r.decoderConfig.description)throw new TypeError("Video chunk metadata decoder configuration for AVC must include a description, which is expected to be an AVCDecoderConfigurationRecord as specified in ISO 14496-15.");if((r.decoderConfig.codec.startsWith("hev1")||r.decoderConfig.codec.startsWith("hvc1"))&&!r.decoderConfig.description)throw new TypeError("Video chunk metadata decoder configuration for HEVC must include a description, which is expected to be an HEVCDecoderConfigurationRecord as specified in ISO 14496-15.");if((r.decoderConfig.codec==="vp8"||r.decoderConfig.codec.startsWith("vp09"))&&r.decoderConfig.colorSpace===void 0)throw new TypeError("Video chunk metadata decoder configuration for VP8/VP9 must include a colorSpace.")},ge=r=>{if(!r)throw new TypeError("Audio chunk metadata must be provided.");if(typeof r!="object")throw new TypeError("Audio chunk metadata must be an object.");if(!r.decoderConfig)throw new TypeError("Audio chunk metadata must include a decoder configuration.");if(typeof r.decoderConfig!="object")throw new TypeError("Audio chunk metadata decoder configuration must be an object.");if(typeof r.decoderConfig.codec!="string")throw new TypeError("Audio chunk metadata decoder configuration must specify a codec string.");if(!Number.isInteger(r.decoderConfig.sampleRate)||r.decoderConfig.sampleRate<=0)throw new TypeError("Audio chunk metadata decoder configuration must specify a valid sampleRate (positive integer).");if(!Number.isInteger(r.decoderConfig.numberOfChannels)||r.decoderConfig.numberOfChannels<=0)throw new TypeError("Audio chunk metadata decoder configuration must specify a valid numberOfChannels (positive integer).");if(r.decoderConfig.description!==void 0&&!Pe(r.decoderConfig.description))throw new TypeError("Audio chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view.");if(r.decoderConfig.codec==="opus"&&r.decoderConfig.description&&r.decoderConfig.description.byteLength<18)throw new TypeError("Invalid decoder description provided for Opus; must be at least 18 bytes long.")},Ce=r=>{if(!r)throw new TypeError("Subtitle metadata must be provided.");if(typeof r!="object")throw new TypeError("Subtitle metadata must be an object.");if(!r.config)throw new TypeError("Subtitle metadata must include a config object.");if(typeof r.config!="object")throw new TypeError("Subtitle metadata config must be an object.");if(typeof r.config.description!="string")throw new TypeError("Subtitle metadata config description must be a string.")};var fe=1e3,br=2082844800,S=(r,t,e=!0)=>{let i=r*t;return e?Math.round(i):i},ye=class extends Q{constructor(e,i){super(e);this.timestampsMustStartAtZero=!0;this.#s=new K;this.#a=this.#s._createWriter();this.#c=new J(this.#a);this.#l=null;this.#n=null;this.#o=[];this.#d=Math.floor(Date.now()/1e3)+br;this.#u=[];this.#m=1;this.#e=e._writer,this.#r=new J(this.#e),this.#i=i,this.#t=i.options.fastStart??(this.#e instanceof X?"in-memory":!1),(this.#t==="in-memory"||this.#t==="fragmented")&&(this.#e.ensureMonotonicity=!0)}#e;#r;#i;#t;#s;#a;#c;#l;#n;#o;#d;#u;#m;start(){let e=this.output._tracks.some(i=>i.type==="video"&&i.source._codec==="avc");this.#r.writeBox(Ye({holdsAvc:e,fragmented:this.#t==="fragmented"})),this.#l=this.#e.getPos(),this.#t==="in-memory"?this.#n=me(!1):this.#t==="fragmented"||(this.#n=me(!0),this.#r.writeBox(this.#n)),this.#e.flush()}#b(e,i){let o=this.#o.find(n=>n.track===e);if(o)return o;Te(i),d(i),d(i.decoderConfig),d(i.decoderConfig.codedWidth!==void 0),d(i.decoderConfig.codedHeight!==void 0);let s={track:e,type:"video",info:{width:i.decoderConfig.codedWidth,height:i.decoderConfig.codedHeight,decoderConfig:i.decoderConfig},timescale:e.metadata.frameRate??57600,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[]};return this.#o.push(s),this.#o.sort((n,a)=>n.track.id-a.track.id),s}#T(e,i){let o=this.#o.find(n=>n.track===e);if(o)return o;ge(i),d(i),d(i.decoderConfig);let s={track:e,type:"audio",info:{numberOfChannels:i.decoderConfig.numberOfChannels,sampleRate:i.decoderConfig.sampleRate,decoderConfig:i.decoderConfig},timescale:i.decoderConfig.sampleRate,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[]};return this.#o.push(s),this.#o.sort((n,a)=>n.track.id-a.track.id),s}#A(e,i){let o=this.#o.find(n=>n.track===e);if(o)return o;Ce(i),d(i),d(i.config);let s={track:e,type:"subtitle",info:{config:i.config},timescale:1e3,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[],lastCueEndTimestamp:0,cueQueue:[],nextSourceId:0,cueToSourceId:new WeakMap};return this.#o.push(s),this.#o.sort((n,a)=>n.track.id-a.track.id),this.validateAndNormalizeTimestamp(e,0,!0),s}addEncodedVideoChunk(e,i,o){let s=this.#b(e,o),n=new Uint8Array(i.byteLength);i.copyTo(n);let a=this.validateAndNormalizeTimestamp(s.track,i.timestamp,i.type==="key"),u=this.#h(s,n,a,(i.duration??0)/1e6,i.type);this.#p(s,u)}addEncodedAudioChunk(e,i,o){let s=this.#T(e,o),n=new Uint8Array(i.byteLength);i.copyTo(n);let a=this.validateAndNormalizeTimestamp(s.track,i.timestamp,i.type==="key"),u=this.#h(s,n,a,(i.duration??0)/1e6,i.type);this.#p(s,u)}addSubtitleCue(e,i,o){let s=this.#A(e,o);this.validateAndNormalizeTimestamp(s.track,1e6*i.timestamp,!0),e.source._codec==="webvtt"&&(s.cueQueue.push(i),this.#g(s,i.timestamp))}#g(e,i){for(;e.cueQueue.length>0;){let o=new Set([]);for(let c of e.cueQueue)d(c.timestamp<=i),d(e.lastCueEndTimestamp<=c.timestamp+c.duration),o.add(Math.max(c.timestamp,e.lastCueEndTimestamp)),o.add(c.timestamp+c.duration);let s=[...o].sort((c,m)=>c-m),n=s[0],a=s[1]??n;if(i=a)break;j.lastIndex=0;let g=j.test(m.text),M=m.timestamp+m.duration,y=e.cueToSourceId.get(m);if(y===void 0&&ao.timestamp).sort((o,s)=>o-s);for(let o=0;o{if(e===a)return i.type==="key";let u=a.sampleQueue[0];return u&&u.type==="key"});s>=1&&n&&(o=!0,this.#x())}else o=s>=.5}o&&(e.currentChunk&&this.#w(e),e.currentChunk={startTimestamp:i.timestamp,samples:[],offset:null,moofOffset:null}),d(e.currentChunk),e.currentChunk.samples.push(i),e.timestampProcessingQueue.push(i)}#w(e){if(d(this.#t!=="fragmented"),!!e.currentChunk){if(e.finalizedChunks.push(e.currentChunk),this.#u.push(e.currentChunk),(e.compactlyCodedChunkTable.length===0||F(e.compactlyCodedChunkTable).samplesPerChunk!==e.currentChunk.samples.length)&&e.compactlyCodedChunkTable.push({firstChunk:e.finalizedChunks.length,samplesPerChunk:e.currentChunk.samples.length}),this.#t==="in-memory"){e.currentChunk.offset=0;return}e.currentChunk.offset=this.#e.getPos();for(let i of e.currentChunk.samples)d(i.data),this.#e.write(i.data),i.data=null;this.#e.flush()}}#k(){d(this.#t==="fragmented");for(let e of this.output._tracks)if(!e.source._closed&&!this.#o.some(i=>i.track===e))return;e:for(;;){let e=null,i=1/0;for(let s of this.#o){if(s.sampleQueue.length===0&&!s.track.source._closed)break e;s.sampleQueue.length>0&&s.sampleQueue[0].timestamp=2**32&&(u.largeSize=!0,c=this.#r.measureBox(u)+f),u.size=c,this.#r.writeBox(u)}for(let u of this.#o){u.currentChunk.offset=this.#e.getPos(),u.currentChunk.moofOffset=o;for(let f of u.currentChunk.samples)this.#e.write(f.data),f.data=null}let n=this.#e.getPos();this.#e.seek(this.#r.offsets.get(s));let a=Fe(i,this.#o);this.#r.writeBox(a),this.#e.seek(n);for(let u of this.#o)u.finalizedChunks.push(u.currentChunk),this.#u.push(u.currentChunk),u.currentChunk=null;e&&this.#e.flush()}onTrackClose(e){if(e.type==="subtitle"&&e.source._codec==="webvtt"){let i=this.#o.find(o=>o.track===e);i&&this.#g(i,1/0)}this.#t==="fragmented"&&this.#k()}finalize(){for(let e of this.#o)e.type==="subtitle"&&e.track.source._codec==="webvtt"&&this.#g(e,1/0);if(this.#t==="fragmented"){for(let e of this.#o){for(let i of e.sampleQueue)this.#C(e,i);this.#f(e)}this.#x(!1)}else for(let e of this.#o)this.#f(e),this.#w(e);if(this.#t==="in-memory"){d(this.#n);let e;for(let o=0;o<2;o++){let s=ee(this.#o,this.#d),n=this.#r.measureBox(s);e=this.#r.measureBox(this.#n);let a=this.#e.getPos()+n+e;for(let u of this.#u){u.offset=a;for(let{data:f}of u.samples)d(f),a+=f.byteLength,e+=f.byteLength}if(a<2**32)break;e>=2**32&&(this.#n.largeSize=!0)}let i=ee(this.#o,this.#d);this.#r.writeBox(i),this.#n.size=e,this.#r.writeBox(this.#n);for(let o of this.#u)for(let s of o.samples)d(s.data),this.#e.write(s.data),s.data=null}else if(this.#t==="fragmented"){let e=this.#e.getPos(),i=et(this.#o);this.#r.writeBox(i);let o=this.#e.getPos()-e;this.#e.seek(this.#e.getPos()-4),this.#r.writeU32(o)}else{d(this.#n),d(this.#l!==null);let e=this.#r.offsets.get(this.#n);d(e!==void 0);let i=this.#e.getPos()-e;this.#n.size=i,this.#n.largeSize=i>=2**32,this.#r.patchBox(this.#n);let o=ee(this.#o,this.#d);if(typeof this.#t=="object"){this.#e.seek(this.#l),this.#r.writeBox(o);let s=e-this.#e.getPos();this.#r.writeBox(Ze(s))}else this.#r.writeBox(o)}}};var ie=class{constructor(t){this.value=t}},G=class{constructor(t){this.value=t}},oe=class{constructor(t){this.value=t}};var We=r=>r<256?1:r<65536?2:r<1<<24?3:r<2**32?4:r<2**40?5:6,He=r=>r>=-64&&r<64?1:r>=-8192&&r<8192?2:r>=-(1<<20)&&r<1<<20?3:r>=-(1<<27)&&r<1<<27?4:r>=-(2**34)&&r<2**34?5:6,nt=r=>{if(r<127)return 1;if(r<16383)return 2;if(r<(1<<21)-1)return 3;if(r<(1<<28)-1)return 4;if(r<2**35-1)return 5;if(r<2**42-1)return 6;throw new Error("EBML VINT size not supported "+r)};var $e=2**15,at="https://github.com/Vanilagy/webm-muxer",ut=6,lt=5,Tr={avc:"V_MPEG4/ISO/AVC",hevc:"V_MPEGH/ISO/HEVC",vp8:"V_VP8",vp9:"V_VP9",av1:"V_AV1",aac:"A_AAC",opus:"A_OPUS",webvtt:"S_TEXT/WEBVTT"},gr={video:1,audio:2,subtitle:17},we=class extends Q{constructor(e,i){super(e);this.timestampsMustStartAtZero=!1;this.#i=new Uint8Array(8);this.#t=new DataView(this.#i.buffer);this.offsets=new WeakMap;this.dataOffsets=new WeakMap;this.#s=[];this.#a=null;this.#c=null;this.#l=null;this.#n=null;this.#o=null;this.#d=null;this.#u=null;this.#m=null;this.#b=new Set;this.#T=0;this.#e=e._writer,this.#r=i,this.#r.options.streamable&&(this.#e.ensureMonotonicity=!0)}#e;#r;#i;#t;#s;#a;#c;#l;#n;#o;#d;#u;#m;#b;#T;#A(e){this.#t.setUint8(0,e),this.#e.write(this.#i.subarray(0,1))}#g(e){this.#t.setFloat32(0,e,!1),this.#e.write(this.#i.subarray(0,4))}#h(e){this.#t.setFloat64(0,e,!1),this.#e.write(this.#i)}#f(e,i=We(e)){let o=0;switch(i){case 6:this.#t.setUint8(o++,e/2**40|0);case 5:this.#t.setUint8(o++,e/2**32|0);case 4:this.#t.setUint8(o++,e>>24);case 3:this.#t.setUint8(o++,e>>16);case 2:this.#t.setUint8(o++,e>>8);case 1:this.#t.setUint8(o++,e);break;default:throw new Error("Bad UINT size "+i)}this.#e.write(this.#i.subarray(0,o))}#p(e,i=He(e)){e<0&&(e+=2**(i*8)),this.#f(e,i)}writeEBMLVarInt(e,i=nt(e)){let o=0;switch(i){case 1:this.#t.setUint8(o++,128|e);break;case 2:this.#t.setUint8(o++,64|e>>8),this.#t.setUint8(o++,e);break;case 3:this.#t.setUint8(o++,32|e>>16),this.#t.setUint8(o++,e>>8),this.#t.setUint8(o++,e);break;case 4:this.#t.setUint8(o++,16|e>>24),this.#t.setUint8(o++,e>>16),this.#t.setUint8(o++,e>>8),this.#t.setUint8(o++,e);break;case 5:this.#t.setUint8(o++,8|e/2**32&7),this.#t.setUint8(o++,e>>24),this.#t.setUint8(o++,e>>16),this.#t.setUint8(o++,e>>8),this.#t.setUint8(o++,e);break;case 6:this.#t.setUint8(o++,4|e/2**40&3),this.#t.setUint8(o++,e/2**32|0),this.#t.setUint8(o++,e>>24),this.#t.setUint8(o++,e>>16),this.#t.setUint8(o++,e>>8),this.#t.setUint8(o++,e);break;default:throw new Error("Bad EBML VINT size "+i)}this.#e.write(this.#i.subarray(0,o))}#C(e){this.#e.write(new Uint8Array(e.split("").map(i=>i.charCodeAt(0))))}writeEBML(e){if(e!==null){if(e instanceof Uint8Array)this.#e.write(e);else if(Array.isArray(e))for(let i of e)this.writeEBML(i);else if(this.offsets.set(e,this.#e.getPos()),this.#f(e.id),Array.isArray(e.data)){let i=this.#e.getPos(),o=e.size===-1?1:e.size??4;e.size===-1?this.#A(255):this.#e.seek(this.#e.getPos()+o);let s=this.#e.getPos();if(this.dataOffsets.set(e,s),this.writeEBML(e.data),e.size!==-1){let n=this.#e.getPos()-s,a=this.#e.getPos();this.#e.seek(i),this.writeEBMLVarInt(n,o),this.#e.seek(a)}}else if(typeof e.data=="number"){let i=e.size??We(e.data);this.writeEBMLVarInt(i),this.#f(e.data,i)}else if(typeof e.data=="string")this.writeEBMLVarInt(e.data.length),this.#C(e.data);else if(e.data instanceof Uint8Array)this.writeEBMLVarInt(e.data.byteLength,e.size),this.#e.write(e.data);else if(e.data instanceof ie)this.writeEBMLVarInt(4),this.#g(e.data.value);else if(e.data instanceof G)this.writeEBMLVarInt(8),this.#h(e.data.value);else if(e.data instanceof oe){let i=e.size??He(e.data.value);this.writeEBMLVarInt(i),this.#p(e.data.value,i)}}}beforeTrackAdd(e){if(this.#r instanceof R)if(e.type==="video"){if(!["vp8","vp9","av1"].includes(e.source._codec))throw new Error("WebM only supports VP8, VP9 and AV1 as video codecs. Switching to MKV removes this restriction.")}else if(e.type==="audio"){if(!["opus","vorbis"].includes(e.source._codec))throw new Error("WebM only supports Opus and Vorbis as audio codecs. Switching to MKV removes this restriction.")}else if(e.type==="subtitle"){if(e.source._codec!=="webvtt")throw new Error("WebM only supports WebVTT as subtitle codec. Switching to MKV removes this restriction.")}else throw new Error("WebM only supports video, audio and subtitle tracks. Switching to MKV removes this restriction.")}start(){this.#w(),this.#r.options.streamable||this.#k(),this.#x(),this.#V(),this.#e.flush()}#w(){let e={id:440786851,data:[{id:17030,data:1},{id:17143,data:1},{id:17138,data:4},{id:17139,data:8},{id:17026,data:this.#r instanceof R?"webm":"matroska"},{id:17031,data:2},{id:17029,data:2}]};this.writeEBML(e)}#k(){let e=new Uint8Array([28,83,187,107]),i=new Uint8Array([21,73,169,102]),o=new Uint8Array([22,84,174,107]),s={id:290298740,data:[{id:19899,data:[{id:21419,data:e},{id:21420,size:5,data:0}]},{id:19899,data:[{id:21419,data:i},{id:21420,size:5,data:0}]},{id:19899,data:[{id:21419,data:o},{id:21420,size:5,data:0}]}]};this.#l=s}#x(){let e={id:17545,data:new G(0)};this.#o=e;let i={id:357149030,data:[{id:2807729,data:1e6},{id:19840,data:at},{id:22337,data:at},this.#r.options.streamable?null:e]};this.#c=i}#E(){let e={id:374648427,data:[]};this.#n=e;for(let i of this.#s)e.data.push({id:174,data:[{id:215,data:i.track.id},{id:29637,data:i.track.id},{id:131,data:gr[i.type]},{id:134,data:Tr[i.track.source._codec]},...i.type==="video"?[i.info.decoderConfig.description?{id:25506,data:z(i.info.decoderConfig.description)}:null,i.track.metadata.frameRate?{id:2352003,data:1e9/i.track.metadata.frameRate}:null,{id:224,data:[{id:176,data:i.info.width},{id:186,data:i.info.height},(()=>{if(i.info.decoderConfig.colorSpace){let o=i.info.decoderConfig.colorSpace;return ue(o)?{id:21936,data:[{id:21937,data:$[o.matrix]},{id:21946,data:H[o.transfer]},{id:21947,data:W[o.primaries]},{id:21945,data:[1,2][Number(o.fullRange)]}]}:null}return null})()]}]:[],...i.type==="audio"?[i.info.decoderConfig.description?{id:25506,data:z(i.info.decoderConfig.description)}:null,{id:225,data:[{id:181,data:new ie(i.info.sampleRate)},{id:159,data:i.info.numberOfChannels}]}]:[],...i.type==="subtitle"?[{id:25506,data:x.encode(i.info.config.description)}]:[]]})}#O(){let e={id:408125543,size:this.#r.options.streamable?-1:ut,data:[this.#r.options.streamable?null:this.#l,this.#c,this.#n]};this.#a=e,this.writeEBML(e)}#V(){this.#d={id:475249515,data:[]}}get#y(){return d(this.#a),this.dataOffsets.get(this.#a)}#I(e,i){let o=this.#s.find(n=>n.track===e);if(o)return o;Te(i),d(i),d(i.decoderConfig),d(i.decoderConfig.codedWidth!==void 0),d(i.decoderConfig.codedHeight!==void 0);let s={track:e,type:"video",info:{width:i.decoderConfig.codedWidth,height:i.decoderConfig.codedHeight,decoderConfig:i.decoderConfig},chunkQueue:[],lastWrittenMsTimestamp:null};return this.#s.push(s),this.#s.sort((n,a)=>n.track.id-a.track.id),s}#U(e,i){let o=this.#s.find(n=>n.track===e);if(o)return o;ge(i),d(i),d(i.decoderConfig);let s={track:e,type:"audio",info:{numberOfChannels:i.decoderConfig.numberOfChannels,sampleRate:i.decoderConfig.sampleRate,decoderConfig:i.decoderConfig},chunkQueue:[],lastWrittenMsTimestamp:null};return this.#s.push(s),this.#s.sort((n,a)=>n.track.id-a.track.id),s}#z(e,i){let o=this.#s.find(n=>n.track===e);if(o)return o;Ce(i),d(i),d(i.config);let s={track:e,type:"subtitle",info:{config:i.config},chunkQueue:[],lastWrittenMsTimestamp:null};return this.#s.push(s),this.#s.sort((n,a)=>n.track.id-a.track.id),s}addEncodedVideoChunk(e,i,o){let s=this.#I(e,o),n=new Uint8Array(i.byteLength);i.copyTo(n);let a=this.validateAndNormalizeTimestamp(s.track,i.timestamp,i.type==="key"),u=this.#_(n,a,(i.duration??0)/1e6,i.type);e.source._codec==="vp9"&&this.#P(s,u),s.chunkQueue.push(u),this.#S()}addEncodedAudioChunk(e,i,o){let s=this.#U(e,o),n=new Uint8Array(i.byteLength);i.copyTo(n);let a=this.validateAndNormalizeTimestamp(s.track,i.timestamp,i.type==="key"),u=this.#_(n,a,(i.duration??0)/1e6,i.type);s.chunkQueue.push(u),this.#S()}addSubtitleCue(e,i,o){let s=this.#z(e,o),n=this.validateAndNormalizeTimestamp(s.track,1e6*i.timestamp,!0),a=i.text,u=Math.floor(n*1e3);j.lastIndex=0,a=a.replace(j,g=>{let y=de(g.slice(1,-1))-u;return`<${ce(y)}>`});let f=x.encode(a),c=`${i.settings??""} -${i.identifier??""} -${i.notes??""}`,m=this.#_(f,n,i.duration,"key",c.trim()?x.encode(c):null);s.chunkQueue.push(m),this.#S()}#S(){for(let e of this.output._tracks)if(!e.source._closed&&!this.#s.some(i=>i.track===e))return;e:for(;;){let e=null,i=1/0;for(let s of this.#s){if(s.chunkQueue.length===0&&!s.track.source._closed)break e;s.chunkQueue.length>0&&s.chunkQueue[0].timestamp=2&&o++;let f={rgb:7,bt709:2,bt470bg:1,smpte170m:3}[e.info.decoderConfig.colorSpace.matrix];je(i.data,o+0,o+3,f)}#_(e,i,o,s,n=null){return{data:e,type:s,timestamp:i,duration:o,additions:n}}#v(e,i){this.#a||(this.#E(),this.#O());let o=Math.floor(1e3*i.timestamp),s=this.#s.every(m=>{if(m.track.source._closed)return!0;if(e===m)return i.type==="key";let g=m.chunkQueue[0];return g&&g.type==="key"});(!this.#u||s&&o-this.#m>=1e3)&&this.#R(o);let n=o-this.#m;if(n<0)return;if(n>=$e)throw new Error(`Current Matroska cluster exceeded its maximum allowed length of ${$e} milliseconds. In order to produce a correct WebM file, you must pass in a key frame at least every ${$e} milliseconds.`);let u=new Uint8Array(4),f=new DataView(u.buffer);f.setUint8(0,128|e.track.id),f.setInt16(1,n,!1);let c=Math.floor(1e3*i.duration);if(c===0&&!i.additions){f.setUint8(3,+(i.type==="key")<<7);let m={id:163,data:[u,i.data]};this.writeEBML(m)}else{let m={id:160,data:[{id:161,data:[u,i.data]},i.type==="delta"?{id:251,data:new oe(e.lastWrittenMsTimestamp-o)}:null,i.additions?{id:30113,data:[{id:166,data:[{id:165,data:i.additions},{id:238,data:1}]}]}:null,c>0?{id:155,data:c}:null]};this.writeEBML(m)}this.#T=Math.max(this.#T,o+c),e.lastWrittenMsTimestamp=o,this.#b.add(e)}#R(e){this.#u&&!this.#r.options.streamable&&this.#M(),this.#u={id:524531317,size:this.#r.options.streamable?-1:lt,data:[{id:231,data:e}]},this.writeEBML(this.#u),this.#m=e,this.#b.clear()}#M(){d(this.#u);let e=this.#e.getPos()-this.dataOffsets.get(this.#u),i=this.#e.getPos();this.#e.seek(this.offsets.get(this.#u)+4),this.writeEBMLVarInt(e,lt),this.#e.seek(i);let o=this.offsets.get(this.#u)-this.#y;d(this.#d),this.#d.data.push({id:187,data:[{id:179,data:this.#m},...[...this.#b].map(s=>({id:183,data:[{id:247,data:s.track.id},{id:241,data:o}]}))]})}onTrackClose(){this.#S()}finalize(){this.#a||(this.#E(),this.#O());for(let e of this.#s)for(;e.chunkQueue.length>0;)this.#v(e,e.chunkQueue.shift());if(!this.#r.options.streamable&&this.#u&&this.#M(),d(this.#d),this.writeEBML(this.#d),!this.#r.options.streamable){let e=this.#e.getPos(),i=this.#e.getPos()-this.#y;this.#e.seek(this.offsets.get(this.#a)+4),this.writeEBMLVarInt(i,ut),this.#o.data=new G(this.#T),this.#e.seek(this.offsets.get(this.#o)),this.writeEBML(this.#o),this.#l.data[0].data[1].data=this.offsets.get(this.#d)-this.#y,this.#l.data[1].data[1].data=this.offsets.get(this.#c)-this.#y,this.#l.data[2].data[1].data=this.offsets.get(this.#n)-this.#y,this.#e.seek(this.offsets.get(this.#l)),this.writeEBML(this.#l),this.#e.seek(e)}}};var V=class{},ke=class extends V{constructor(e={}){if(!e||typeof e!="object")throw new TypeError("options must be an object.");if(e.fastStart!==void 0&&![!1,"in-memory","fragmented"].includes(e.fastStart))throw new TypeError('options.fastStart, when provided, must be false, "in-memory", or "fragmented".');super();this.options=e}_createMuxer(e){return new ye(e,this)}},se=class extends V{constructor(e={}){if(!e||typeof e!="object")throw new TypeError("options must be an object.");if(e.streamable!==void 0&&typeof e.streamable!="boolean")throw new TypeError("options.streamable, when provided, must be a boolean.");super();this.options=e}_createMuxer(e){return new we(e,this)}},R=class extends se{};var q=["avc","hevc","vp8","vp9","av1"],Y=["aac","opus"],xe=["webvtt"],B=class{constructor(){this._connectedTrack=null;this._closed=!1;this._offsetTimestamps=!1}_ensureValidDigest(){if(!this._connectedTrack)throw new Error("Cannot call digest without connecting the source to an output track.");if(!this._connectedTrack.output._started)throw new Error("Cannot call digest before output has been started.");if(this._connectedTrack.output._finalizing)throw new Error("Cannot call digest after output has started finalizing.");if(this._closed)throw new Error("Cannot call digest after source has been closed.")}_start(){}async _flush(){}close(){if(this._closed)throw new Error("Source already closed.");if(!this._connectedTrack)throw new Error("Cannot call close without connecting the source to an output track.");if(!this._connectedTrack.output._started)throw new Error("Cannot call close before output has been started.");this._closed=!0,!this._connectedTrack.output._finalizing&&this._connectedTrack.output._muxer.onTrackClose(this._connectedTrack)}},E=class extends B{constructor(e){super();this._connectedTrack=null;if(!q.includes(e))throw new TypeError(`Invalid video codec '${e}'. Must be one of: ${q.join(", ")}.`);this._codec=e}},Se=class extends E{constructor(t){super(t)}digest(t,e){if(!(t instanceof EncodedVideoChunk))throw new TypeError("chunk must be an EncodedVideoChunk.");this._ensureValidDigest(),this._connectedTrack?.output._muxer.addEncodedVideoChunk(this._connectedTrack,t,e)}},Cr=5,yr=r=>{if(!r||typeof r!="object")throw new TypeError("Codec config must be an object.");if(!q.includes(r.codec))throw new TypeError(`Invalid video codec '${r.codec}'. Must be one of: ${q.join(", ")}.`);if(!Number.isInteger(r.bitrate)||r.bitrate<=0)throw new TypeError("config.bitrate must be a positive integer.");if(r.latencyMode!==void 0&&["quality","realtime"].includes(r.latencyMode))throw new TypeError("config.latencyMode, when provided, must be 'quality' or 'realtime'.")},ne=class{constructor(t,e){this.source=t;this.codecConfig=e;this.encoder=null;this.lastMultipleOfKeyFrameInterval=-1;this.lastWidth=null;this.lastHeight=null;yr(e)}digest(t){if(this.source._ensureValidDigest(),this.lastWidth!==null&&this.lastHeight!==null){if(t.codedWidth!==this.lastWidth||t.codedHeight!==this.lastHeight)throw new Error(`Video frame size must remain constant. Expected ${this.lastWidth}x${this.lastHeight}, got ${t.codedWidth}x${t.codedHeight}.`)}else this.lastWidth=t.codedWidth,this.lastHeight=t.codedHeight;this.ensureEncoder(t),d(this.encoder);let e=Math.floor(t.timestamp/1e6/Cr);this.encoder.encode(t,{keyFrame:e!==this.lastMultipleOfKeyFrameInterval}),this.lastMultipleOfKeyFrameInterval=e}ensureEncoder(t){this.encoder||(this.encoder=new VideoEncoder({output:(e,i)=>this.source._connectedTrack?.output._muxer.addEncodedVideoChunk(this.source._connectedTrack,e,i),error:e=>console.error("Video encode error:",e)}),this.encoder.configure({codec:ot(this.codecConfig.codec,t.codedWidth,t.codedHeight),width:t.codedWidth,height:t.codedHeight,bitrate:this.codecConfig.bitrate,framerate:this.source._connectedTrack?.metadata.frameRate,latencyMode:this.codecConfig.latencyMode}))}async flush(){return this.encoder?.flush()}},Ae=class extends E{constructor(t){super(t.codec),this._encoder=new ne(this,t)}digest(t){if(!(t instanceof VideoFrame))throw new TypeError("videoFrame must be a VideoFrame.");this._encoder.digest(t)}_flush(){return this._encoder.flush()}},_e=class extends E{constructor(t,e){if(!(t instanceof HTMLCanvasElement))throw new TypeError("canvas must be an HTMLCanvasElement.");super(e.codec),this._encoder=new ne(this,e),this._canvas=t}digest(t,e=0){if(!Number.isFinite(t)||t<0)throw new TypeError("timestamp must be a non-negative number.");if(!Number.isFinite(e)||e<0)throw new TypeError("duration must be a non-negative number.");let i=new VideoFrame(this._canvas,{timestamp:Math.round(1e6*t),duration:Math.round(1e6*e),alpha:"discard"});this._encoder.digest(i),i.close()}_flush(){return this._encoder.flush()}},Ee=class extends E{constructor(e,i){if(!(e instanceof MediaStreamTrack)||e.kind!=="video")throw new TypeError("track must be a video MediaStreamTrack.");i={...i,latencyMode:"realtime"};super(i.codec);this._abortController=null;this._offsetTimestamps=!0;this._encoder=new ne(this,i),this._track=e}_start(){this._abortController=new AbortController;let e=new MediaStreamTrackProcessor({track:this._track}),i=new WritableStream({write:o=>{this._encoder.digest(o),o.close()}});e.readable.pipeTo(i,{signal:this._abortController.signal}).catch(o=>{o instanceof DOMException&&o.name==="AbortError"||console.error("Pipe error:",o)})}async _flush(){this._abortController&&(this._abortController.abort(),this._abortController=null),await this._encoder.flush()}},O=class extends B{constructor(e){super();this._connectedTrack=null;if(!Y.includes(e))throw new TypeError(`Invalid audio codec '${e}'. Must be one of: ${Y.join(", ")}.`);this._codec=e}},Oe=class extends O{constructor(t){super(t)}digest(t,e){if(!(t instanceof EncodedAudioChunk))throw new TypeError("chunk must be an EncodedAudioChunk.");this._ensureValidDigest(),this._connectedTrack?.output._muxer.addEncodedAudioChunk(this._connectedTrack,t,e)}},wr=r=>{if(!r||typeof r!="object")throw new TypeError("Codec config must be an object.");if(!Y.includes(r.codec))throw new TypeError(`Invalid audio codec '${r.codec}'. Must be one of: ${Y.join(", ")}.`);if(!Number.isInteger(r.bitrate)||r.bitrate<=0)throw new TypeError("config.bitrate must be a positive integer.")},ae=class{constructor(t,e){this.source=t;this.codecConfig=e;this.encoder=null;this.lastNumberOfChannels=null;this.lastSampleRate=null;wr(e)}digest(t){if(this.source._ensureValidDigest(),this.lastNumberOfChannels!==null&&this.lastSampleRate!==null){if(t.numberOfChannels!==this.lastNumberOfChannels||t.sampleRate!==this.lastSampleRate)throw new Error(`Audio parameters must remain constant. Expected ${this.lastNumberOfChannels} channels at ${this.lastSampleRate} Hz, got ${t.numberOfChannels} channels at ${t.sampleRate} Hz.`)}else this.lastNumberOfChannels=t.numberOfChannels,this.lastSampleRate=t.sampleRate;this.ensureEncoder(t),d(this.encoder),this.encoder.encode(t)}ensureEncoder(t){this.encoder||(this.encoder=new AudioEncoder({output:(e,i)=>this.source._connectedTrack?.output._muxer.addEncodedAudioChunk(this.source._connectedTrack,e,i),error:e=>console.error("Audio encode error:",e)}),this.encoder.configure({codec:st(this.codecConfig.codec,t.numberOfChannels,t.sampleRate),numberOfChannels:t.numberOfChannels,sampleRate:t.sampleRate,bitrate:this.codecConfig.bitrate}))}async flush(){return this.encoder?.flush()}},ve=class extends O{constructor(t){super(t.codec),this._encoder=new ae(this,t)}digest(t){if(!(t instanceof AudioData))throw new TypeError("audioData must be an AudioData.");this._encoder.digest(t)}_flush(){return this._encoder.flush()}},Me=class extends O{constructor(e){super(e.codec);this._accumulatedFrameCount=0;this._encoder=new ae(this,e)}digest(e){if(!(e instanceof AudioBuffer))throw new TypeError("audioBuffer must be an AudioBuffer.");let i=e.numberOfChannels,o=e.sampleRate,s=e.length,n=new Float32Array(i*s);for(let u=0;u{this._encoder.digest(o),o.close()}});e.readable.pipeTo(i,{signal:this._abortController.signal}).catch(o=>{o instanceof DOMException&&o.name==="AbortError"||console.error("Pipe error:",o)})}async _flush(){this._abortController&&(this._abortController.abort(),this._abortController=null),await this._encoder.flush()}},N=class extends B{constructor(e){super();this._connectedTrack=null;if(!xe.includes(e))throw new TypeError(`Invalid subtitle codec '${e}'. Must be one of: ${xe.join(", ")}.`);this._codec=e}},Ie=class extends N{constructor(t){super(t),this._parser=new le({codec:t,output:(e,i)=>this._connectedTrack?.output._muxer.addSubtitleCue(this._connectedTrack,e,i),error:e=>console.error("Subtitle parse error:",e)})}digest(t){if(typeof t!="string")throw new TypeError("text must be a string.");this._ensureValidDigest(),this._parser.parse(t)}};var Ue=class{constructor(t){this._tracks=[];this._started=!1;this._finalizing=!1;if(!t||typeof t!="object")throw new TypeError("options must be an object.");if(!(t.format instanceof V))throw new TypeError("options.format must be an OutputFormat.");if(!(t.target instanceof v))throw new TypeError("options.target must be a Target.");if(t.target.output)throw new Error("Target is already used for another output.");t.target.output=this,this._writer=t.target._createWriter(),this._muxer=t.format._createMuxer(this)}addVideoTrack(t,e={}){if(!(t instanceof E))throw new TypeError("source must be a VideoSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");if(typeof e.rotation=="number"&&![0,90,180,270].includes(e.rotation))throw new TypeError(`Invalid video rotation: ${e.rotation}. Has to be 0, 90, 180 or 270.`);if(Array.isArray(e.rotation)&&(e.rotation.length!==9||e.rotation.some(i=>!Number.isFinite(i))))throw new TypeError(`Invalid video transformation matrix: ${e.rotation.join()}`);if(e.frameRate!==void 0&&(!Number.isInteger(e.frameRate)||e.frameRate<=0))throw new TypeError(`Invalid video frame rate: ${e.frameRate}. Must be a positive integer.`);this._addTrack("video",t,e)}addAudioTrack(t,e={}){if(!(t instanceof O))throw new TypeError("source must be an AudioSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");this._addTrack("audio",t,e)}addSubtitleTrack(t,e={}){if(!(t instanceof N))throw new TypeError("source must be a SubtitleSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");this._addTrack("subtitle",t,e)}_addTrack(t,e,i){if(this._started)throw new Error("Cannot add track after output has started.");if(e._connectedTrack)throw new Error("Source is already used for a track.");let o={id:this._tracks.length+1,output:this,type:t,source:e,metadata:i};this._muxer.beforeTrackAdd(o),this._tracks.push(o),e._connectedTrack=o}start(){if(this._started)throw new Error("Output already started.");this._started=!0,this._muxer.start();for(let t of this._tracks)t.source._start()}async finalize(){if(this._finalizing)throw new Error("Cannot call finalize twice.");this._finalizing=!0;let t=this._tracks.map(e=>e.source._flush());await Promise.all(t),this._muxer.finalize(),this._writer.flush(),this._writer.finalize()}};return pt(kr);})(); +`,o);u===-1&&(u=i.length);let d=ce(e[2]),m=ce(e[3])-d,C=i.slice(n,u).trim();i=i.slice(u).trimStart(),Z.lastIndex=0;let V={timestamp:d/1e3,duration:m/1e3,text:C,identifier:s,settings:a,notes:r},k={};this.preambleEmitted||(k.config={description:this.preambleText},this.preambleEmitted=!0),this.options.output(V,k)}}},Tt=/(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})/,ce=t=>{let i=Tt.exec(t);if(!i)throw new Error("Expected match.");return 60*60*1e3*Number(i[1]||"0")+60*1e3*Number(i[2])+1e3*Number(i[3])+Number(i[4])},de=t=>{let i=Math.floor(t/36e5),e=Math.floor(t%(60*60*1e3)/(60*1e3)),r=Math.floor(t%(60*1e3)/1e3),s=t%1e3;return i.toString().padStart(2,"0")+":"+e.toString().padStart(2,"0")+":"+r.toString().padStart(2,"0")+"."+s.toString().padStart(3,"0")};var J=class{constructor(i){this.writer=i;this.helper=new Uint8Array(8);this.helperView=new DataView(this.helper.buffer);this.offsets=new WeakMap}writeU32(i){this.helperView.setUint32(0,i,!1),this.writer.write(this.helper.subarray(0,4))}writeU64(i){this.helperView.setUint32(0,Math.floor(i/2**32),!1),this.helperView.setUint32(4,i,!1),this.writer.write(this.helper.subarray(0,8))}writeAscii(i){for(let e=0;e[(t%256+256)%256],h=t=>(A.setUint16(0,t,!1),[p[0],p[1]]),Ct=t=>(A.setInt16(0,t,!1),[p[0],p[1]]),Ke=t=>(A.setUint32(0,t,!1),[p[1],p[2],p[3]]),l=t=>(A.setUint32(0,t,!1),[p[0],p[1],p[2],p[3]]),Le=t=>(A.setInt32(0,t,!1),[p[0],p[1],p[2],p[3]]),U=t=>(A.setUint32(0,Math.floor(t/2**32),!1),A.setUint32(4,t,!1),[p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]]),Re=t=>(A.setInt16(0,2**8*t,!1),[p[0],p[1]]),v=t=>(A.setInt32(0,2**16*t,!1),[p[0],p[1],p[2],p[3]]),Pe=t=>(A.setInt32(0,2**30*t,!1),[p[0],p[1],p[2],p[3]]),We=(t,i)=>{let e=[],r=t;do{let s=r&127;r>>=7,e.length>0&&(s|=128),e.push(s),i!==void 0&&i--}while(r>0||i);return e.reverse()},w=(t,i=!1)=>{let e=Array(t.length).fill(null).map((r,s)=>t.charCodeAt(s));return i&&e.push(0),e},Be=t=>{let i=null;for(let e of t)(!i||e.timestamp>i.timestamp)&&(i=e);return i},Xe=t=>{let i=t*(Math.PI/180),e=Math.cos(i),r=Math.sin(i);return[e,r,0,-r,e,0,0,0,1]},Ge=Xe(0),qe=t=>[v(t[0]),v(t[1]),Pe(t[2]),v(t[3]),v(t[4]),Pe(t[5]),v(t[6]),v(t[7]),Pe(t[8])],b=(t,i,e)=>({type:t,contents:i&&new Uint8Array(i.flat(10)),children:e}),T=(t,i,e,r,s)=>b(t,[g(i),Ke(e),r??[]],s),Ye=t=>{let i=512;return t.fragmented?b("ftyp",[w("iso5"),l(i),w("iso5"),w("iso6"),w("mp41")]):b("ftyp",[w("isom"),l(i),w("isom"),t.holdsAvc?w("avc1"):[],w("mp41")])},me=t=>({type:"mdat",largeSize:t}),Ze=t=>({type:"free",size:t}),ee=(t,i,e=!1)=>b("moov",void 0,[gt(i,t),...t.map(r=>kt(r,i)),e?Jt(t):null]),gt=(t,i)=>{let e=x(Math.max(0,...i.filter(n=>n.samples.length>0).map(n=>{let a=Be(n.samples);return a.timestamp+a.duration})),fe),r=Math.max(0,...i.map(n=>n.track.id))+1,s=!I(t)||!I(e),o=s?U:l;return T("mvhd",+s,0,[o(t),o(t),l(fe),o(e),v(1),Re(1),Array(10).fill(0),qe(Ge),Array(24).fill(0),l(r)])},kt=(t,i)=>b("trak",void 0,[wt(t,i),yt(t,i)]),wt=(t,i)=>{let e=Be(t.samples),r=x(e?e.timestamp+e.duration:0,fe),s=!I(i)||!I(r),o=s?U:l,n;if(t.type==="video"){let a=t.track.metadata.rotation;n=a===void 0||typeof a=="number"?Xe(a??0):a}else n=Ge;return T("tkhd",+s,3,[o(i),o(i),l(t.track.id),l(0),o(r),Array(8).fill(0),h(0),h(t.track.id),Re(t.type==="audio"?1:0),h(0),qe(n),v(t.type==="video"?t.info.width:0),v(t.type==="video"?t.info.height:0)])},yt=(t,i)=>b("mdia",void 0,[St(t,i),At(t),_t(t)]),St=(t,i)=>{let e=Be(t.samples),r=x(e?e.timestamp+e.duration:0,t.timescale),s=!I(i)||!I(r),o=s?U:l;return T("mdhd",+s,0,[o(i),o(i),l(t.timescale),o(r),h(21956),h(0)])},xt={video:"vide",audio:"soun",subtitle:"text"},vt={video:"VideoHandler",audio:"SoundHandler",subtitle:"TextHandler"},At=t=>T("hdlr",0,0,[w("mhlr"),w(xt[t.type]),l(0),l(0),l(0),w(vt[t.type],!0)]),_t=t=>b("minf",void 0,[Mt[t.type](),It(),Ut(t)]),Et=()=>T("vmhd",0,1,[h(0),h(0),h(0),h(0)]),Ot=()=>T("smhd",0,0,[h(0),h(0)]),Vt=()=>T("nmhd",0,0),Mt={video:Et,audio:Ot,subtitle:Vt},It=()=>b("dinf",void 0,[zt()]),zt=()=>T("dref",0,0,[l(1)],[Dt()]),Dt=()=>T("url ",0,1),Ut=t=>{let i=t.compositionTimeOffsetTable.length>1||t.compositionTimeOffsetTable.some(e=>e.sampleCompositionTimeOffset!==0);return b("stbl",void 0,[Pt(t),Lt(t),Xt(t),Gt(t),qt(t),Yt(t),i?Zt(t):null])},Pt=t=>{let i;return t.type==="video"?i=Wt(ur[t.track.source._codec],t):t.type==="audio"?i=Ht(cr[t.track.source._codec],t):t.type==="subtitle"&&(i=Qt(fr[t.track.source._codec],t)),c(i),T("stsd",0,0,[l(1)],[i])},Wt=(t,i)=>b(t,[Array(6).fill(0),h(1),h(0),h(0),Array(12).fill(0),h(i.info.width),h(i.info.height),l(4718592),l(4718592),l(0),h(1),Array(32).fill(0),h(24),Ct(65535)],[lr[i.track.source._codec](i),ue(i.info.decoderConfig.colorSpace)?Rt(i):null]),Rt=t=>b("colr",[w("nclx"),h(N[t.info.decoderConfig.colorSpace.primaries]),h(H[t.info.decoderConfig.colorSpace.transfer]),h($[t.info.decoderConfig.colorSpace.matrix]),g((t.info.decoderConfig.colorSpace.fullRange?1:0)<<7)]),Bt=t=>t.info.decoderConfig&&b("avcC",[...D(t.info.decoderConfig.description)]),Ft=t=>t.info.decoderConfig&&b("hvcC",[...D(t.info.decoderConfig.description)]),Qe=t=>{if(!t.info.decoderConfig)return null;let i=t.info.decoderConfig;c(i.colorSpace);let e=i.codec.split("."),r=Number(e[1]),s=Number(e[2]),a=(Number(e[3])<<4)+(0<<1)+Number(i.colorSpace.fullRange);return T("vpcC",1,0,[g(r),g(s),g(a),g(2),g(2),g(2),h(0)])},Nt=()=>{let e=(1<<7)+1;return b("av1C",[e,0,0,0])},Ht=(t,i)=>b(t,[Array(6).fill(0),h(1),h(0),h(0),l(0),h(i.info.numberOfChannels),h(16),h(0),h(0),v(i.info.sampleRate)],[dr[i.track.source._codec](i)]),$t=t=>{let e=[...D(t.info.decoderConfig.description??new ArrayBuffer(0))];return e=[...g(64),...g(21),...Ke(0),...l(0),...l(0),...g(5),...We(e.length),...e],e=[...h(1),...g(0),...g(4),...We(e.length),...e,...g(6),...g(1),...g(2)],e=[...g(3),...We(e.length),...e],T("esds",0,0,e)},jt=t=>{let i=3840,e=0,r=t.info.decoderConfig?.description;if(r){c(r.byteLength<18);let s=ArrayBuffer.isView(r)?new DataView(r.buffer,r.byteOffset,r.byteLength):new DataView(r);i=s.getUint16(10,!0),e=s.getInt16(14,!0)}return b("dOps",[g(0),g(t.info.numberOfChannels),h(i),l(t.info.sampleRate),Re(e),g(0)])},Qt=(t,i)=>b(t,[Array(6).fill(0),h(1)],[mr[i.track.source._codec](i)]),Kt=t=>b("vttC",[...S.encode(t.info.config.description)]);var Lt=t=>T("stts",0,0,[l(t.timeToSampleTable.length),t.timeToSampleTable.map(i=>[l(i.sampleCount),l(i.sampleDelta)])]),Xt=t=>{if(t.samples.every(e=>e.type==="key"))return null;let i=[...t.samples.entries()].filter(([,e])=>e.type==="key");return T("stss",0,0,[l(i.length),i.map(([e])=>l(e+1))])},Gt=t=>T("stsc",0,0,[l(t.compactlyCodedChunkTable.length),t.compactlyCodedChunkTable.map(i=>[l(i.firstChunk),l(i.samplesPerChunk),l(1)])]),qt=t=>T("stsz",0,0,[l(0),l(t.samples.length),t.samples.map(i=>l(i.size))]),Yt=t=>t.finalizedChunks.length>0&&F(t.finalizedChunks).offset>=2**32?T("co64",0,0,[l(t.finalizedChunks.length),t.finalizedChunks.map(i=>U(i.offset))]):T("stco",0,0,[l(t.finalizedChunks.length),t.finalizedChunks.map(i=>l(i.offset))]),Zt=t=>T("ctts",0,0,[l(t.compositionTimeOffsetTable.length),t.compositionTimeOffsetTable.map(i=>[l(i.sampleCount),l(i.sampleCompositionTimeOffset)])]),Jt=t=>b("mvex",void 0,t.map(er)),er=t=>T("trex",0,0,[l(t.track.id),l(1),l(0),l(0),l(0)]),Fe=(t,i)=>b("moof",void 0,[tr(t),...i.map(rr)]),tr=t=>T("mfhd",0,0,[l(t)]),Je=t=>{let i=0,e=0,r=0,s=0,o=t.type==="delta";return e|=+o,o?i|=1:i|=2,i<<24|e<<16|r<<8|s},rr=t=>b("traf",void 0,[ir(t),sr(t),or(t)]),ir=t=>{c(t.currentChunk);let i=0;i|=8,i|=16,i|=32,i|=131072;let e=t.currentChunk.samples[1]??t.currentChunk.samples[0],r={duration:e.timescaleUnitsToNextSample,size:e.size,flags:Je(e)};return T("tfhd",0,i,[l(t.track.id),l(r.duration),l(r.size),l(r.flags)])},sr=t=>(c(t.currentChunk),T("tfdt",1,0,[U(x(t.currentChunk.startTimestamp,t.timescale))])),or=t=>{c(t.currentChunk);let i=t.currentChunk.samples.map(y=>y.timescaleUnitsToNextSample),e=t.currentChunk.samples.map(y=>y.size),r=t.currentChunk.samples.map(Je),s=t.currentChunk.samples.map(y=>x(y.timestamp-y.decodeTimestamp,t.timescale)),o=new Set(i),n=new Set(e),a=new Set(r),u=new Set(s),d=a.size===2&&r[0]!==r[1],f=o.size>1,m=n.size>1,C=!d&&a.size>1,V=u.size>1||[...u].some(y=>y!==0),k=0;return k|=1,k|=4*+d,k|=256*+f,k|=512*+m,k|=1024*+C,k|=2048*+V,T("trun",1,k,[l(t.currentChunk.samples.length),l(t.currentChunk.offset-t.currentChunk.moofOffset||0),d?l(r[0]):[],t.currentChunk.samples.map((y,B)=>[f?l(i[B]):[],m?l(e[B]):[],C?l(r[B]):[],V?Le(s[B]):[]])])},et=t=>b("mfra",void 0,[...t.map(nr),ar()]),nr=(t,i)=>T("tfra",1,0,[l(t.track.id),l(63),l(t.finalizedChunks.length),t.finalizedChunks.map(r=>[U(x(r.startTimestamp,t.timescale)),U(r.moofOffset),l(i+1),l(1),l(1)])]),ar=()=>T("mfro",0,0,[l(0)]),tt=()=>b("vtte"),rt=(t,i,e,r,s)=>b("vttc",void 0,[s!==null?b("vsid",[Le(s)]):null,e!==null?b("iden",[...S.encode(e)]):null,i!==null?b("ctim",[...S.encode(de(i))]):null,r!==null?b("sttg",[...S.encode(r)]):null,b("payl",[...S.encode(t)])]),it=t=>b("vtta",[...S.encode(t)]),ur={avc:"avc1",hevc:"hvc1",vp8:"vp08",vp9:"vp09",av1:"av01"},lr={avc:Bt,hevc:Ft,vp8:Qe,vp9:Qe,av1:Nt},cr={aac:"mp4a",opus:"Opus"},dr={aac:$t,opus:jt},fr={webvtt:"wvtt"},mr={webvtt:Kt};var Q=class{constructor(i){this.trackTimestampInfo=new WeakMap;this.output=i}beforeTrackAdd(i){}onTrackClose(i){}validateAndNormalizeTimestamp(i,e,r){let s=e/1e6,o=this.trackTimestampInfo.get(i);if(!o){if(!r)throw new Error("First frame must be a key frame.");if(this.timestampsMustStartAtZero&&s>0)throw new Error(`Timestamps must start at zero (got ${s}s).`);o={timestampOffset:s,maxTimestamp:i.source._offsetTimestamps?0:s,lastKeyFrameTimestamp:i.source._offsetTimestamps?0:s},this.trackTimestampInfo.set(i,o)}if(i.source._offsetTimestamps&&(s-=o.timestampOffset),s<0)throw new Error(`Timestamps must be non-negative (got ${s}s).`);if(ss.start-o.start);e.push({start:r[0].start,size:r[0].data.byteLength});for(let s=1;sd.start<=r&&rhr){for(let d=0;d=e.written[n+1].start;)e.written[n].end=Math.max(e.written[n].end,e.written[n+1].end),e.written.splice(n+1,1)}createChunk(e){let s={start:Math.floor(e/this.chunkSize)*this.chunkSize,data:new Uint8Array(this.chunkSize),written:[],shouldFlush:!1};return this.chunks.push(s),this.chunks.sort((o,n)=>o.start-n.start),this.chunks.indexOf(s)}flushChunks(e=!1){for(let r=0;ri.stream.write({type:"write",data:e,position:r}),chunkSize:i.options?.chunkSize}))}};var st=(t,i,e)=>{if(t==="avc"){let r=100;i<=768&&e<=432?r=66:i<=1920&&e<=1080&&(r=77);let s=0,o=i>1920||e>1080?50:41,n=r.toString(16).padStart(2,"0"),a=s.toString(16).padStart(2,"0"),u=o.toString(16).padStart(2,"0");return`avc1.${n}${a}${u}`}else if(t==="hevc"){let r=0,s=1,o=Array(32).fill(0);o[s]=1;let n=parseInt(o.reverse().join(""),2).toString(16).replace(/^0+/,""),a="L",u=120;return i<=1280&&e<=720?u=93:i<=1920&&e<=1080?u=120:i<=3840&&e<=2160?u=150:(a="H",u=180),`hev1.${r===0?"":String.fromCharCode(65+r-1)}${s}.${n}.${a}${u}.B0`}else{if(t==="vp8")return"vp8";if(t==="vp9"){let r="00",s;return i<=854&&e<=480?s="21":i<=1280&&e<=720?s="31":i<=1920&&e<=1080?s="41":i<=3840&&e<=2160?s="51":s="61",`vp09.${r}.${s}.08`}else if(t==="av1"){let s;return i<=854&&e<=480?s="01":i<=1280&&e<=720?s="03":i<=1920&&e<=1080?s="04":i<=3840&&e<=2160?s="07":s="09",`av01.0.${s}M.08`}}throw new TypeError(`Unhandled codec '${t}'.`)},ot=(t,i,e)=>{if(t==="aac")return i>=2&&e<=24e3?"mp4a.40.29":e<=24e3?"mp4a.40.5":"mp4a.40.2";if(t==="opus")return"opus";if(t==="vorbis")return"vorbis";throw new TypeError(`Unhandled codec '${t}'.`)},Te=t=>{if(!t)throw new TypeError("Video chunk metadata must be provided.");if(typeof t!="object")throw new TypeError("Video chunk metadata must be an object.");if(!t.decoderConfig)throw new TypeError("Video chunk metadata must include a decoder configuration.");if(typeof t.decoderConfig!="object")throw new TypeError("Video chunk metadata decoder configuration must be an object.");if(typeof t.decoderConfig.codec!="string")throw new TypeError("Video chunk metadata decoder configuration must specify a codec string.");if(!Number.isInteger(t.decoderConfig.codedWidth)||t.decoderConfig.codedWidth<=0)throw new TypeError("Video chunk metadata decoder configuration must specify a valid codedWidth (positive integer).");if(!Number.isInteger(t.decoderConfig.codedHeight)||t.decoderConfig.codedHeight<=0)throw new TypeError("Video chunk metadata decoder configuration must specify a valid codedHeight (positive integer).");if(t.decoderConfig.description!==void 0&&!Ue(t.decoderConfig.description))throw new TypeError("Video chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view.");if(t.decoderConfig.colorSpace!==void 0){let{colorSpace:i}=t.decoderConfig;if(typeof i!="object")throw new TypeError("Video chunk metadata decoder configuration colorSpace, when provided, must be an object.");let e=Object.keys(N);if(i.primaries!=null&&!e.includes(i.primaries))throw new TypeError(`Video chunk metadata decoder configuration colorSpace primaries, when defined, must be one of ${e.join(", ")}.`);let r=Object.keys(H);if(i.transfer!=null&&!r.includes(i.transfer))throw new TypeError(`Video chunk metadata decoder configuration colorSpace transfer, when defined, must be one of ${r.join(", ")}.`);let s=Object.keys($);if(i.matrix!=null&&!s.includes(i.matrix))throw new TypeError(`Video chunk metadata decoder configuration colorSpace matrix, when defined, must be one of ${s.join(", ")}.`);if(i.fullRange!=null&&typeof i.fullRange!="boolean")throw new TypeError("Video chunk metadata decoder configuration colorSpace fullRange, when defined, must be a boolean.")}if((t.decoderConfig.codec.startsWith("avc1")||t.decoderConfig.codec.startsWith("avc3"))&&!t.decoderConfig.description)throw new TypeError("Video chunk metadata decoder configuration for AVC must include a description, which is expected to be an AVCDecoderConfigurationRecord as specified in ISO 14496-15.");if((t.decoderConfig.codec.startsWith("hev1")||t.decoderConfig.codec.startsWith("hvc1"))&&!t.decoderConfig.description)throw new TypeError("Video chunk metadata decoder configuration for HEVC must include a description, which is expected to be an HEVCDecoderConfigurationRecord as specified in ISO 14496-15.");if((t.decoderConfig.codec==="vp8"||t.decoderConfig.codec.startsWith("vp09"))&&t.decoderConfig.colorSpace===void 0)throw new TypeError("Video chunk metadata decoder configuration for VP8/VP9 must include a colorSpace.")},Ce=t=>{if(!t)throw new TypeError("Audio chunk metadata must be provided.");if(typeof t!="object")throw new TypeError("Audio chunk metadata must be an object.");if(!t.decoderConfig)throw new TypeError("Audio chunk metadata must include a decoder configuration.");if(typeof t.decoderConfig!="object")throw new TypeError("Audio chunk metadata decoder configuration must be an object.");if(typeof t.decoderConfig.codec!="string")throw new TypeError("Audio chunk metadata decoder configuration must specify a codec string.");if(!Number.isInteger(t.decoderConfig.sampleRate)||t.decoderConfig.sampleRate<=0)throw new TypeError("Audio chunk metadata decoder configuration must specify a valid sampleRate (positive integer).");if(!Number.isInteger(t.decoderConfig.numberOfChannels)||t.decoderConfig.numberOfChannels<=0)throw new TypeError("Audio chunk metadata decoder configuration must specify a valid numberOfChannels (positive integer).");if(t.decoderConfig.description!==void 0&&!Ue(t.decoderConfig.description))throw new TypeError("Audio chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view.");if(t.decoderConfig.codec==="opus"&&t.decoderConfig.description&&t.decoderConfig.description.byteLength<18)throw new TypeError("Invalid decoder description provided for Opus; must be at least 18 bytes long.")},ge=t=>{if(!t)throw new TypeError("Subtitle metadata must be provided.");if(typeof t!="object")throw new TypeError("Subtitle metadata must be an object.");if(!t.config)throw new TypeError("Subtitle metadata must include a config object.");if(typeof t.config!="object")throw new TypeError("Subtitle metadata config must be an object.");if(typeof t.config.description!="string")throw new TypeError("Subtitle metadata config description must be a string.")};var fe=1e3,br=2082844800,x=(t,i,e=!0)=>{let r=t*i;return e?Math.round(r):r},ke=class extends Q{constructor(e,r){super(e);this.timestampsMustStartAtZero=!0;this.auxTarget=new K;this.auxWriter=this.auxTarget._createWriter();this.auxBoxWriter=new J(this.auxWriter);this.ftypSize=null;this.mdat=null;this.trackDatas=[];this.creationTime=Math.floor(Date.now()/1e3)+br;this.finalizedChunks=[];this.nextFragmentNumber=1;this.writer=e._writer,this.boxWriter=new J(this.writer),this.fastStart=r.options.fastStart??(this.writer instanceof X?"in-memory":!1),(this.fastStart==="in-memory"||this.fastStart==="fragmented")&&(this.writer.ensureMonotonicity=!0)}start(){let e=this.output._tracks.some(r=>r.type==="video"&&r.source._codec==="avc");this.boxWriter.writeBox(Ye({holdsAvc:e,fragmented:this.fastStart==="fragmented"})),this.ftypSize=this.writer.getPos(),this.fastStart==="in-memory"?this.mdat=me(!1):this.fastStart==="fragmented"||(this.mdat=me(!0),this.boxWriter.writeBox(this.mdat)),this.writer.flush()}getVideoTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;Te(r),c(r),c(r.decoderConfig),c(r.decoderConfig.codedWidth!==void 0),c(r.decoderConfig.codedHeight!==void 0);let o={track:e,type:"video",info:{width:r.decoderConfig.codedWidth,height:r.decoderConfig.codedHeight,decoderConfig:r.decoderConfig},timescale:e.metadata.frameRate??57600,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[]};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),o}getAudioTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;Ce(r),c(r),c(r.decoderConfig);let o={track:e,type:"audio",info:{numberOfChannels:r.decoderConfig.numberOfChannels,sampleRate:r.decoderConfig.sampleRate,decoderConfig:r.decoderConfig},timescale:r.decoderConfig.sampleRate,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[]};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),o}getSubtitleTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;ge(r),c(r),c(r.config);let o={track:e,type:"subtitle",info:{config:r.config},timescale:1e3,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[],lastCueEndTimestamp:0,cueQueue:[],nextSourceId:0,cueToSourceId:new WeakMap};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),this.validateAndNormalizeTimestamp(e,0,!0),o}addEncodedVideoChunk(e,r,s){let o=this.getVideoTrackData(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.createSampleForTrack(o,n,a,(r.duration??0)/1e6,r.type);this.registerSample(o,u)}addEncodedAudioChunk(e,r,s){let o=this.getAudioTrackData(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.createSampleForTrack(o,n,a,(r.duration??0)/1e6,r.type);this.registerSample(o,u)}addSubtitleCue(e,r,s){let o=this.getSubtitleTrackData(e,s);this.validateAndNormalizeTimestamp(o.track,1e6*r.timestamp,!0),e.source._codec==="webvtt"&&(o.cueQueue.push(r),this.processWebVTTCues(o,r.timestamp))}processWebVTTCues(e,r){for(;e.cueQueue.length>0;){let s=new Set([]);for(let f of e.cueQueue)c(f.timestamp<=r),c(e.lastCueEndTimestamp<=f.timestamp+f.duration),s.add(Math.max(f.timestamp,e.lastCueEndTimestamp)),s.add(f.timestamp+f.duration);let o=[...s].sort((f,m)=>f-m),n=o[0],a=o[1]??n;if(r=a)break;j.lastIndex=0;let C=j.test(m.text),V=m.timestamp+m.duration,k=e.cueToSourceId.get(m);if(k===void 0&&as.timestamp).sort((s,o)=>s-o);for(let s=0;s{if(e===a)return r.type==="key";let u=a.sampleQueue[0];return u&&u.type==="key"});o>=1&&n&&(s=!0,this.finalizeFragment())}else s=o>=.5}s&&(e.currentChunk&&this.finalizeCurrentChunk(e),e.currentChunk={startTimestamp:r.timestamp,samples:[],offset:null,moofOffset:null}),c(e.currentChunk),e.currentChunk.samples.push(r),e.timestampProcessingQueue.push(r)}finalizeCurrentChunk(e){if(c(this.fastStart!=="fragmented"),!!e.currentChunk){if(e.finalizedChunks.push(e.currentChunk),this.finalizedChunks.push(e.currentChunk),(e.compactlyCodedChunkTable.length===0||F(e.compactlyCodedChunkTable).samplesPerChunk!==e.currentChunk.samples.length)&&e.compactlyCodedChunkTable.push({firstChunk:e.finalizedChunks.length,samplesPerChunk:e.currentChunk.samples.length}),this.fastStart==="in-memory"){e.currentChunk.offset=0;return}e.currentChunk.offset=this.writer.getPos();for(let r of e.currentChunk.samples)c(r.data),this.writer.write(r.data),r.data=null;this.writer.flush()}}interleaveSamples(){c(this.fastStart==="fragmented");for(let e of this.output._tracks)if(!e.source._closed&&!this.trackDatas.some(r=>r.track===e))return;e:for(;;){let e=null,r=1/0;for(let o of this.trackDatas){if(o.sampleQueue.length===0&&!o.track.source._closed)break e;o.sampleQueue.length>0&&o.sampleQueue[0].timestamp=2**32&&(u.largeSize=!0,f=this.boxWriter.measureBox(u)+d),u.size=f,this.boxWriter.writeBox(u)}for(let u of this.trackDatas){u.currentChunk.offset=this.writer.getPos(),u.currentChunk.moofOffset=s;for(let d of u.currentChunk.samples)this.writer.write(d.data),d.data=null}let n=this.writer.getPos();this.writer.seek(this.boxWriter.offsets.get(o));let a=Fe(r,this.trackDatas);this.boxWriter.writeBox(a),this.writer.seek(n);for(let u of this.trackDatas)u.finalizedChunks.push(u.currentChunk),this.finalizedChunks.push(u.currentChunk),u.currentChunk=null;e&&this.writer.flush()}onTrackClose(e){if(e.type==="subtitle"&&e.source._codec==="webvtt"){let r=this.trackDatas.find(s=>s.track===e);r&&this.processWebVTTCues(r,1/0)}this.fastStart==="fragmented"&&this.interleaveSamples()}finalize(){for(let e of this.trackDatas)e.type==="subtitle"&&e.track.source._codec==="webvtt"&&this.processWebVTTCues(e,1/0);if(this.fastStart==="fragmented"){for(let e of this.trackDatas){for(let r of e.sampleQueue)this.addSampleToTrack(e,r);this.processTimestamps(e)}this.finalizeFragment(!1)}else for(let e of this.trackDatas)this.processTimestamps(e),this.finalizeCurrentChunk(e);if(this.fastStart==="in-memory"){c(this.mdat);let e;for(let s=0;s<2;s++){let o=ee(this.trackDatas,this.creationTime),n=this.boxWriter.measureBox(o);e=this.boxWriter.measureBox(this.mdat);let a=this.writer.getPos()+n+e;for(let u of this.finalizedChunks){u.offset=a;for(let{data:d}of u.samples)c(d),a+=d.byteLength,e+=d.byteLength}if(a<2**32)break;e>=2**32&&(this.mdat.largeSize=!0)}let r=ee(this.trackDatas,this.creationTime);this.boxWriter.writeBox(r),this.mdat.size=e,this.boxWriter.writeBox(this.mdat);for(let s of this.finalizedChunks)for(let o of s.samples)c(o.data),this.writer.write(o.data),o.data=null}else if(this.fastStart==="fragmented"){let e=this.writer.getPos(),r=et(this.trackDatas);this.boxWriter.writeBox(r);let s=this.writer.getPos()-e;this.writer.seek(this.writer.getPos()-4),this.boxWriter.writeU32(s)}else{c(this.mdat),c(this.ftypSize!==null);let e=this.boxWriter.offsets.get(this.mdat);c(e!==void 0);let r=this.writer.getPos()-e;this.mdat.size=r,this.mdat.largeSize=r>=2**32,this.boxWriter.patchBox(this.mdat);let s=ee(this.trackDatas,this.creationTime);if(typeof this.fastStart=="object"){this.writer.seek(this.ftypSize),this.boxWriter.writeBox(s);let o=e-this.writer.getPos();this.boxWriter.writeBox(Ze(o))}else this.boxWriter.writeBox(s)}}};var ie=class{constructor(i){this.value=i}},G=class{constructor(i){this.value=i}},se=class{constructor(i){this.value=i}};var Ne=t=>t<256?1:t<65536?2:t<1<<24?3:t<2**32?4:t<2**40?5:6,He=t=>t>=-64&&t<64?1:t>=-8192&&t<8192?2:t>=-(1<<20)&&t<1<<20?3:t>=-(1<<27)&&t<1<<27?4:t>=-(2**34)&&t<2**34?5:6,nt=t=>{if(t<127)return 1;if(t<16383)return 2;if(t<(1<<21)-1)return 3;if(t<(1<<28)-1)return 4;if(t<2**35-1)return 5;if(t<2**42-1)return 6;throw new Error("EBML VINT size not supported "+t)};var $e=2**15,at="https://github.com/Vanilagy/webm-muxer",ut=6,lt=5,Tr={avc:"V_MPEG4/ISO/AVC",hevc:"V_MPEGH/ISO/HEVC",vp8:"V_VP8",vp9:"V_VP9",av1:"V_AV1",aac:"A_AAC",opus:"A_OPUS",webvtt:"S_TEXT/WEBVTT"},Cr={video:1,audio:2,subtitle:17},we=class extends Q{constructor(e,r){super(e);this.timestampsMustStartAtZero=!1;this.helper=new Uint8Array(8);this.helperView=new DataView(this.helper.buffer);this.offsets=new WeakMap;this.dataOffsets=new WeakMap;this.trackDatas=[];this.segment=null;this.segmentInfo=null;this.seekHead=null;this.tracksElement=null;this.segmentDuration=null;this.cues=null;this.currentCluster=null;this.currentClusterMsTimestamp=null;this.trackDatasInCurrentCluster=new Set;this.duration=0;this.writer=e._writer,this.format=r,this.format.options.streamable&&(this.writer.ensureMonotonicity=!0)}writeByte(e){this.helperView.setUint8(0,e),this.writer.write(this.helper.subarray(0,1))}writeFloat32(e){this.helperView.setFloat32(0,e,!1),this.writer.write(this.helper.subarray(0,4))}writeFloat64(e){this.helperView.setFloat64(0,e,!1),this.writer.write(this.helper)}writeUnsignedInt(e,r=Ne(e)){let s=0;switch(r){case 6:this.helperView.setUint8(s++,e/2**40|0);case 5:this.helperView.setUint8(s++,e/2**32|0);case 4:this.helperView.setUint8(s++,e>>24);case 3:this.helperView.setUint8(s++,e>>16);case 2:this.helperView.setUint8(s++,e>>8);case 1:this.helperView.setUint8(s++,e);break;default:throw new Error("Bad UINT size "+r)}this.writer.write(this.helper.subarray(0,s))}writeSignedInt(e,r=He(e)){e<0&&(e+=2**(r*8)),this.writeUnsignedInt(e,r)}writeEBMLVarInt(e,r=nt(e)){let s=0;switch(r){case 1:this.helperView.setUint8(s++,128|e);break;case 2:this.helperView.setUint8(s++,64|e>>8),this.helperView.setUint8(s++,e);break;case 3:this.helperView.setUint8(s++,32|e>>16),this.helperView.setUint8(s++,e>>8),this.helperView.setUint8(s++,e);break;case 4:this.helperView.setUint8(s++,16|e>>24),this.helperView.setUint8(s++,e>>16),this.helperView.setUint8(s++,e>>8),this.helperView.setUint8(s++,e);break;case 5:this.helperView.setUint8(s++,8|e/2**32&7),this.helperView.setUint8(s++,e>>24),this.helperView.setUint8(s++,e>>16),this.helperView.setUint8(s++,e>>8),this.helperView.setUint8(s++,e);break;case 6:this.helperView.setUint8(s++,4|e/2**40&3),this.helperView.setUint8(s++,e/2**32|0),this.helperView.setUint8(s++,e>>24),this.helperView.setUint8(s++,e>>16),this.helperView.setUint8(s++,e>>8),this.helperView.setUint8(s++,e);break;default:throw new Error("Bad EBML VINT size "+r)}this.writer.write(this.helper.subarray(0,s))}writeString(e){this.writer.write(new Uint8Array(e.split("").map(r=>r.charCodeAt(0))))}writeEBML(e){if(e!==null){if(e instanceof Uint8Array)this.writer.write(e);else if(Array.isArray(e))for(let r of e)this.writeEBML(r);else if(this.offsets.set(e,this.writer.getPos()),this.writeUnsignedInt(e.id),Array.isArray(e.data)){let r=this.writer.getPos(),s=e.size===-1?1:e.size??4;e.size===-1?this.writeByte(255):this.writer.seek(this.writer.getPos()+s);let o=this.writer.getPos();if(this.dataOffsets.set(e,o),this.writeEBML(e.data),e.size!==-1){let n=this.writer.getPos()-o,a=this.writer.getPos();this.writer.seek(r),this.writeEBMLVarInt(n,s),this.writer.seek(a)}}else if(typeof e.data=="number"){let r=e.size??Ne(e.data);this.writeEBMLVarInt(r),this.writeUnsignedInt(e.data,r)}else if(typeof e.data=="string")this.writeEBMLVarInt(e.data.length),this.writeString(e.data);else if(e.data instanceof Uint8Array)this.writeEBMLVarInt(e.data.byteLength,e.size),this.writer.write(e.data);else if(e.data instanceof ie)this.writeEBMLVarInt(4),this.writeFloat32(e.data.value);else if(e.data instanceof G)this.writeEBMLVarInt(8),this.writeFloat64(e.data.value);else if(e.data instanceof se){let r=e.size??He(e.data.value);this.writeEBMLVarInt(r),this.writeSignedInt(e.data.value,r)}}}beforeTrackAdd(e){if(this.format instanceof P)if(e.type==="video"){if(!["vp8","vp9","av1"].includes(e.source._codec))throw new Error("WebM only supports VP8, VP9 and AV1 as video codecs. Switching to MKV removes this restriction.")}else if(e.type==="audio"){if(!["opus","vorbis"].includes(e.source._codec))throw new Error("WebM only supports Opus and Vorbis as audio codecs. Switching to MKV removes this restriction.")}else if(e.type==="subtitle"){if(e.source._codec!=="webvtt")throw new Error("WebM only supports WebVTT as subtitle codec. Switching to MKV removes this restriction.")}else throw new Error("WebM only supports video, audio and subtitle tracks. Switching to MKV removes this restriction.")}start(){this.writeEBMLHeader(),this.format.options.streamable||this.createSeekHead(),this.createSegmentInfo(),this.createCues(),this.writer.flush()}writeEBMLHeader(){let e={id:440786851,data:[{id:17030,data:1},{id:17143,data:1},{id:17138,data:4},{id:17139,data:8},{id:17026,data:this.format instanceof P?"webm":"matroska"},{id:17031,data:2},{id:17029,data:2}]};this.writeEBML(e)}createSeekHead(){let e=new Uint8Array([28,83,187,107]),r=new Uint8Array([21,73,169,102]),s=new Uint8Array([22,84,174,107]),o={id:290298740,data:[{id:19899,data:[{id:21419,data:e},{id:21420,size:5,data:0}]},{id:19899,data:[{id:21419,data:r},{id:21420,size:5,data:0}]},{id:19899,data:[{id:21419,data:s},{id:21420,size:5,data:0}]}]};this.seekHead=o}createSegmentInfo(){let e={id:17545,data:new G(0)};this.segmentDuration=e;let r={id:357149030,data:[{id:2807729,data:1e6},{id:19840,data:at},{id:22337,data:at},this.format.options.streamable?null:e]};this.segmentInfo=r}createTracks(){let e={id:374648427,data:[]};this.tracksElement=e;for(let r of this.trackDatas)e.data.push({id:174,data:[{id:215,data:r.track.id},{id:29637,data:r.track.id},{id:131,data:Cr[r.type]},{id:134,data:Tr[r.track.source._codec]},...r.type==="video"?[r.info.decoderConfig.description?{id:25506,data:D(r.info.decoderConfig.description)}:null,r.track.metadata.frameRate?{id:2352003,data:1e9/r.track.metadata.frameRate}:null,{id:224,data:[{id:176,data:r.info.width},{id:186,data:r.info.height},(()=>{if(r.info.decoderConfig.colorSpace){let s=r.info.decoderConfig.colorSpace;return ue(s)?{id:21936,data:[{id:21937,data:$[s.matrix]},{id:21946,data:H[s.transfer]},{id:21947,data:N[s.primaries]},{id:21945,data:[1,2][Number(s.fullRange)]}]}:null}return null})()]}]:[],...r.type==="audio"?[r.info.decoderConfig.description?{id:25506,data:D(r.info.decoderConfig.description)}:null,{id:225,data:[{id:181,data:new ie(r.info.sampleRate)},{id:159,data:r.info.numberOfChannels}]}]:[],...r.type==="subtitle"?[{id:25506,data:S.encode(r.info.config.description)}]:[]]})}createSegment(){let e={id:408125543,size:this.format.options.streamable?-1:ut,data:[this.format.options.streamable?null:this.seekHead,this.segmentInfo,this.tracksElement]};this.segment=e,this.writeEBML(e)}createCues(){this.cues={id:475249515,data:[]}}get segmentDataOffset(){return c(this.segment),this.dataOffsets.get(this.segment)}getVideoTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;Te(r),c(r),c(r.decoderConfig),c(r.decoderConfig.codedWidth!==void 0),c(r.decoderConfig.codedHeight!==void 0);let o={track:e,type:"video",info:{width:r.decoderConfig.codedWidth,height:r.decoderConfig.codedHeight,decoderConfig:r.decoderConfig},chunkQueue:[],lastWrittenMsTimestamp:null};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),o}getAudioTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;Ce(r),c(r),c(r.decoderConfig);let o={track:e,type:"audio",info:{numberOfChannels:r.decoderConfig.numberOfChannels,sampleRate:r.decoderConfig.sampleRate,decoderConfig:r.decoderConfig},chunkQueue:[],lastWrittenMsTimestamp:null};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),o}getSubtitleTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;ge(r),c(r),c(r.config);let o={track:e,type:"subtitle",info:{config:r.config},chunkQueue:[],lastWrittenMsTimestamp:null};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),o}addEncodedVideoChunk(e,r,s){let o=this.getVideoTrackData(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.createInternalChunk(n,a,(r.duration??0)/1e6,r.type);e.source._codec==="vp9"&&this.fixVP9ColorSpace(o,u),o.chunkQueue.push(u),this.interleaveChunks()}addEncodedAudioChunk(e,r,s){let o=this.getAudioTrackData(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.createInternalChunk(n,a,(r.duration??0)/1e6,r.type);o.chunkQueue.push(u),this.interleaveChunks()}addSubtitleCue(e,r,s){let o=this.getSubtitleTrackData(e,s),n=this.validateAndNormalizeTimestamp(o.track,1e6*r.timestamp,!0),a=r.text,u=Math.floor(n*1e3);j.lastIndex=0,a=a.replace(j,C=>{let k=ce(C.slice(1,-1))-u;return`<${de(k)}>`});let d=S.encode(a),f=`${r.settings??""} +${r.identifier??""} +${r.notes??""}`,m=this.createInternalChunk(d,n,r.duration,"key",f.trim()?S.encode(f):null);o.chunkQueue.push(m),this.interleaveChunks()}interleaveChunks(){for(let e of this.output._tracks)if(!e.source._closed&&!this.trackDatas.some(r=>r.track===e))return;e:for(;;){let e=null,r=1/0;for(let o of this.trackDatas){if(o.chunkQueue.length===0&&!o.track.source._closed)break e;o.chunkQueue.length>0&&o.chunkQueue[0].timestamp=2&&s++;let d={rgb:7,bt709:2,bt470bg:1,smpte170m:3}[e.info.decoderConfig.colorSpace.matrix];je(r.data,s+0,s+3,d)}createInternalChunk(e,r,s,o,n=null){return{data:e,type:o,timestamp:r,duration:s,additions:n}}writeBlock(e,r){this.segment||(this.createTracks(),this.createSegment());let s=Math.floor(1e3*r.timestamp),o=this.trackDatas.every(m=>{if(m.track.source._closed)return!0;if(e===m)return r.type==="key";let C=m.chunkQueue[0];return C&&C.type==="key"});(!this.currentCluster||o&&s-this.currentClusterMsTimestamp>=1e3)&&this.createNewCluster(s);let n=s-this.currentClusterMsTimestamp;if(n<0)return;if(n>=$e)throw new Error(`Current Matroska cluster exceeded its maximum allowed length of ${$e} milliseconds. In order to produce a correct WebM file, you must pass in a key frame at least every ${$e} milliseconds.`);let u=new Uint8Array(4),d=new DataView(u.buffer);d.setUint8(0,128|e.track.id),d.setInt16(1,n,!1);let f=Math.floor(1e3*r.duration);if(f===0&&!r.additions){d.setUint8(3,+(r.type==="key")<<7);let m={id:163,data:[u,r.data]};this.writeEBML(m)}else{let m={id:160,data:[{id:161,data:[u,r.data]},r.type==="delta"?{id:251,data:new se(e.lastWrittenMsTimestamp-s)}:null,r.additions?{id:30113,data:[{id:166,data:[{id:165,data:r.additions},{id:238,data:1}]}]}:null,f>0?{id:155,data:f}:null]};this.writeEBML(m)}this.duration=Math.max(this.duration,s+f),e.lastWrittenMsTimestamp=s,this.trackDatasInCurrentCluster.add(e)}createNewCluster(e){this.currentCluster&&!this.format.options.streamable&&this.finalizeCurrentCluster(),this.currentCluster={id:524531317,size:this.format.options.streamable?-1:lt,data:[{id:231,data:e}]},this.writeEBML(this.currentCluster),this.currentClusterMsTimestamp=e,this.trackDatasInCurrentCluster.clear()}finalizeCurrentCluster(){c(this.currentCluster);let e=this.writer.getPos()-this.dataOffsets.get(this.currentCluster),r=this.writer.getPos();this.writer.seek(this.offsets.get(this.currentCluster)+4),this.writeEBMLVarInt(e,lt),this.writer.seek(r);let s=this.offsets.get(this.currentCluster)-this.segmentDataOffset;c(this.cues),this.cues.data.push({id:187,data:[{id:179,data:this.currentClusterMsTimestamp},...[...this.trackDatasInCurrentCluster].map(o=>({id:183,data:[{id:247,data:o.track.id},{id:241,data:s}]}))]})}onTrackClose(){this.interleaveChunks()}finalize(){this.segment||(this.createTracks(),this.createSegment());for(let e of this.trackDatas)for(;e.chunkQueue.length>0;)this.writeBlock(e,e.chunkQueue.shift());if(!this.format.options.streamable&&this.currentCluster&&this.finalizeCurrentCluster(),c(this.cues),this.writeEBML(this.cues),!this.format.options.streamable){let e=this.writer.getPos(),r=this.writer.getPos()-this.segmentDataOffset;this.writer.seek(this.offsets.get(this.segment)+4),this.writeEBMLVarInt(r,ut),this.segmentDuration.data=new G(this.duration),this.writer.seek(this.offsets.get(this.segmentDuration)),this.writeEBML(this.segmentDuration),this.seekHead.data[0].data[1].data=this.offsets.get(this.cues)-this.segmentDataOffset,this.seekHead.data[1].data[1].data=this.offsets.get(this.segmentInfo)-this.segmentDataOffset,this.seekHead.data[2].data[1].data=this.offsets.get(this.tracksElement)-this.segmentDataOffset,this.writer.seek(this.offsets.get(this.seekHead)),this.writeEBML(this.seekHead),this.writer.seek(e)}}};var M=class{},ye=class extends M{constructor(e={}){if(!e||typeof e!="object")throw new TypeError("options must be an object.");if(e.fastStart!==void 0&&![!1,"in-memory","fragmented"].includes(e.fastStart))throw new TypeError('options.fastStart, when provided, must be false, "in-memory", or "fragmented".');super();this.options=e}_createMuxer(e){return new ke(e,this)}},oe=class extends M{constructor(e={}){if(!e||typeof e!="object")throw new TypeError("options must be an object.");if(e.streamable!==void 0&&typeof e.streamable!="boolean")throw new TypeError("options.streamable, when provided, must be a boolean.");super();this.options=e}_createMuxer(e){return new we(e,this)}},P=class extends oe{};var q=["avc","hevc","vp8","vp9","av1"],Y=["aac","opus"],Se=["webvtt"],W=class{constructor(){this._connectedTrack=null;this._closed=!1;this._offsetTimestamps=!1}_ensureValidDigest(){if(!this._connectedTrack)throw new Error("Cannot call digest without connecting the source to an output track.");if(!this._connectedTrack.output._started)throw new Error("Cannot call digest before output has been started.");if(this._connectedTrack.output._finalizing)throw new Error("Cannot call digest after output has started finalizing.");if(this._closed)throw new Error("Cannot call digest after source has been closed.")}_start(){}async _flush(){}close(){if(this._closed)throw new Error("Source already closed.");if(!this._connectedTrack)throw new Error("Cannot call close without connecting the source to an output track.");if(!this._connectedTrack.output._started)throw new Error("Cannot call close before output has been started.");this._closed=!0,!this._connectedTrack.output._finalizing&&this._connectedTrack.output._muxer.onTrackClose(this._connectedTrack)}},_=class extends W{constructor(e){super();this._connectedTrack=null;if(!q.includes(e))throw new TypeError(`Invalid video codec '${e}'. Must be one of: ${q.join(", ")}.`);this._codec=e}},xe=class extends _{constructor(i){super(i)}digest(i,e){if(!(i instanceof EncodedVideoChunk))throw new TypeError("chunk must be an EncodedVideoChunk.");this._ensureValidDigest(),this._connectedTrack?.output._muxer.addEncodedVideoChunk(this._connectedTrack,i,e)}},gr=5,kr=t=>{if(!t||typeof t!="object")throw new TypeError("Codec config must be an object.");if(!q.includes(t.codec))throw new TypeError(`Invalid video codec '${t.codec}'. Must be one of: ${q.join(", ")}.`);if(!Number.isInteger(t.bitrate)||t.bitrate<=0)throw new TypeError("config.bitrate must be a positive integer.");if(t.latencyMode!==void 0&&["quality","realtime"].includes(t.latencyMode))throw new TypeError("config.latencyMode, when provided, must be 'quality' or 'realtime'.")},ne=class{constructor(i,e){this.source=i;this.codecConfig=e;this.encoder=null;this.lastMultipleOfKeyFrameInterval=-1;this.lastWidth=null;this.lastHeight=null;kr(e)}digest(i){if(this.source._ensureValidDigest(),this.lastWidth!==null&&this.lastHeight!==null){if(i.codedWidth!==this.lastWidth||i.codedHeight!==this.lastHeight)throw new Error(`Video frame size must remain constant. Expected ${this.lastWidth}x${this.lastHeight}, got ${i.codedWidth}x${i.codedHeight}.`)}else this.lastWidth=i.codedWidth,this.lastHeight=i.codedHeight;this.ensureEncoder(i),c(this.encoder);let e=Math.floor(i.timestamp/1e6/gr);this.encoder.encode(i,{keyFrame:e!==this.lastMultipleOfKeyFrameInterval}),this.lastMultipleOfKeyFrameInterval=e}ensureEncoder(i){this.encoder||(this.encoder=new VideoEncoder({output:(e,r)=>this.source._connectedTrack?.output._muxer.addEncodedVideoChunk(this.source._connectedTrack,e,r),error:e=>console.error("Video encode error:",e)}),this.encoder.configure({codec:st(this.codecConfig.codec,i.codedWidth,i.codedHeight),width:i.codedWidth,height:i.codedHeight,bitrate:this.codecConfig.bitrate,framerate:this.source._connectedTrack?.metadata.frameRate,latencyMode:this.codecConfig.latencyMode}))}async flush(){return this.encoder?.flush()}},ve=class extends _{constructor(i){super(i.codec),this._encoder=new ne(this,i)}digest(i){if(!(i instanceof VideoFrame))throw new TypeError("videoFrame must be a VideoFrame.");this._encoder.digest(i)}_flush(){return this._encoder.flush()}},Ae=class extends _{constructor(i,e){if(!(i instanceof HTMLCanvasElement))throw new TypeError("canvas must be an HTMLCanvasElement.");super(e.codec),this._encoder=new ne(this,e),this._canvas=i}digest(i,e=0){if(!Number.isFinite(i)||i<0)throw new TypeError("timestamp must be a non-negative number.");if(!Number.isFinite(e)||e<0)throw new TypeError("duration must be a non-negative number.");let r=new VideoFrame(this._canvas,{timestamp:Math.round(1e6*i),duration:Math.round(1e6*e),alpha:"discard"});this._encoder.digest(r),r.close()}_flush(){return this._encoder.flush()}},_e=class extends _{constructor(e,r){if(!(e instanceof MediaStreamTrack)||e.kind!=="video")throw new TypeError("track must be a video MediaStreamTrack.");r={...r,latencyMode:"realtime"};super(r.codec);this._abortController=null;this._offsetTimestamps=!0;this._encoder=new ne(this,r),this._track=e}_start(){this._abortController=new AbortController;let e=new MediaStreamTrackProcessor({track:this._track}),r=new WritableStream({write:s=>{this._encoder.digest(s),s.close()}});e.readable.pipeTo(r,{signal:this._abortController.signal}).catch(s=>{s instanceof DOMException&&s.name==="AbortError"||console.error("Pipe error:",s)})}async _flush(){this._abortController&&(this._abortController.abort(),this._abortController=null),await this._encoder.flush()}},E=class extends W{constructor(e){super();this._connectedTrack=null;if(!Y.includes(e))throw new TypeError(`Invalid audio codec '${e}'. Must be one of: ${Y.join(", ")}.`);this._codec=e}},Ee=class extends E{constructor(i){super(i)}digest(i,e){if(!(i instanceof EncodedAudioChunk))throw new TypeError("chunk must be an EncodedAudioChunk.");this._ensureValidDigest(),this._connectedTrack?.output._muxer.addEncodedAudioChunk(this._connectedTrack,i,e)}},wr=t=>{if(!t||typeof t!="object")throw new TypeError("Codec config must be an object.");if(!Y.includes(t.codec))throw new TypeError(`Invalid audio codec '${t.codec}'. Must be one of: ${Y.join(", ")}.`);if(!Number.isInteger(t.bitrate)||t.bitrate<=0)throw new TypeError("config.bitrate must be a positive integer.")},ae=class{constructor(i,e){this.source=i;this.codecConfig=e;this.encoder=null;this.lastNumberOfChannels=null;this.lastSampleRate=null;wr(e)}digest(i){if(this.source._ensureValidDigest(),this.lastNumberOfChannels!==null&&this.lastSampleRate!==null){if(i.numberOfChannels!==this.lastNumberOfChannels||i.sampleRate!==this.lastSampleRate)throw new Error(`Audio parameters must remain constant. Expected ${this.lastNumberOfChannels} channels at ${this.lastSampleRate} Hz, got ${i.numberOfChannels} channels at ${i.sampleRate} Hz.`)}else this.lastNumberOfChannels=i.numberOfChannels,this.lastSampleRate=i.sampleRate;this.ensureEncoder(i),c(this.encoder),this.encoder.encode(i)}ensureEncoder(i){this.encoder||(this.encoder=new AudioEncoder({output:(e,r)=>this.source._connectedTrack?.output._muxer.addEncodedAudioChunk(this.source._connectedTrack,e,r),error:e=>console.error("Audio encode error:",e)}),this.encoder.configure({codec:ot(this.codecConfig.codec,i.numberOfChannels,i.sampleRate),numberOfChannels:i.numberOfChannels,sampleRate:i.sampleRate,bitrate:this.codecConfig.bitrate}))}async flush(){return this.encoder?.flush()}},Oe=class extends E{constructor(i){super(i.codec),this._encoder=new ae(this,i)}digest(i){if(!(i instanceof AudioData))throw new TypeError("audioData must be an AudioData.");this._encoder.digest(i)}_flush(){return this._encoder.flush()}},Ve=class extends E{constructor(e){super(e.codec);this._accumulatedFrameCount=0;this._encoder=new ae(this,e)}digest(e){if(!(e instanceof AudioBuffer))throw new TypeError("audioBuffer must be an AudioBuffer.");let r=e.numberOfChannels,s=e.sampleRate,o=e.length,n=new Float32Array(r*o);for(let u=0;u{this._encoder.digest(s),s.close()}});e.readable.pipeTo(r,{signal:this._abortController.signal}).catch(s=>{s instanceof DOMException&&s.name==="AbortError"||console.error("Pipe error:",s)})}async _flush(){this._abortController&&(this._abortController.abort(),this._abortController=null),await this._encoder.flush()}},R=class extends W{constructor(e){super();this._connectedTrack=null;if(!Se.includes(e))throw new TypeError(`Invalid subtitle codec '${e}'. Must be one of: ${Se.join(", ")}.`);this._codec=e}},Ie=class extends R{constructor(i){super(i),this._parser=new le({codec:i,output:(e,r)=>this._connectedTrack?.output._muxer.addSubtitleCue(this._connectedTrack,e,r),error:e=>console.error("Subtitle parse error:",e)})}digest(i){if(typeof i!="string")throw new TypeError("text must be a string.");this._ensureValidDigest(),this._parser.parse(i)}};var ze=class{constructor(i){this._tracks=[];this._started=!1;this._finalizing=!1;if(!i||typeof i!="object")throw new TypeError("options must be an object.");if(!(i.format instanceof M))throw new TypeError("options.format must be an OutputFormat.");if(!(i.target instanceof O))throw new TypeError("options.target must be a Target.");if(i.target.output)throw new Error("Target is already used for another output.");i.target.output=this,this._writer=i.target._createWriter(),this._muxer=i.format._createMuxer(this)}addVideoTrack(i,e={}){if(!(i instanceof _))throw new TypeError("source must be a VideoSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");if(typeof e.rotation=="number"&&![0,90,180,270].includes(e.rotation))throw new TypeError(`Invalid video rotation: ${e.rotation}. Has to be 0, 90, 180 or 270.`);if(Array.isArray(e.rotation)&&(e.rotation.length!==9||e.rotation.some(r=>!Number.isFinite(r))))throw new TypeError(`Invalid video transformation matrix: ${e.rotation.join()}`);if(e.frameRate!==void 0&&(!Number.isInteger(e.frameRate)||e.frameRate<=0))throw new TypeError(`Invalid video frame rate: ${e.frameRate}. Must be a positive integer.`);this._addTrack("video",i,e)}addAudioTrack(i,e={}){if(!(i instanceof E))throw new TypeError("source must be an AudioSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");this._addTrack("audio",i,e)}addSubtitleTrack(i,e={}){if(!(i instanceof R))throw new TypeError("source must be a SubtitleSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");this._addTrack("subtitle",i,e)}_addTrack(i,e,r){if(this._started)throw new Error("Cannot add track after output has started.");if(e._connectedTrack)throw new Error("Source is already used for a track.");let s={id:this._tracks.length+1,output:this,type:i,source:e,metadata:r};this._muxer.beforeTrackAdd(s),this._tracks.push(s),e._connectedTrack=s}start(){if(this._started)throw new Error("Output already started.");this._started=!0,this._muxer.start();for(let i of this._tracks)i.source._start()}async finalize(){if(this._finalizing)throw new Error("Cannot call finalize twice.");this._finalizing=!0;let i=this._tracks.map(e=>e.source._flush());await Promise.all(i),this._muxer.finalize(),this._writer.flush(),this._writer.finalize()}};return ht(yr);})(); if (typeof module === "object" && typeof module.exports === "object") Object.assign(module.exports, Metamuxer) diff --git a/dist/metamuxer.min.mjs b/dist/metamuxer.min.mjs index e8e1b36..2e6c9d9 100644 --- a/dist/metamuxer.min.mjs +++ b/dist/metamuxer.min.mjs @@ -1,9 +1,9 @@ -function d(i){if(!i)throw new Error("Assertion failed.")}var B=i=>i&&i[i.length-1],V=i=>i>=0&&i<2**32,I=(i,t,e)=>{let r=0;for(let o=t;o>a;r<<=1,r|=u}return r},$e=(i,t,e,r)=>{for(let o=t;o>e-o-1<i instanceof ArrayBuffer?new Uint8Array(i):new Uint8Array(i.buffer,i.byteOffset,i.byteLength),x=new TextEncoder,N={bt709:1,bt470bg:5,smpte170m:6},D={bt709:1,smpte170m:6,"iec61966-2-1":13},F={rgb:0,bt709:1,bt470bg:5,smpte170m:6},ae=i=>!!i&&!!i.primaries&&!!i.transfer&&!!i.matrix&&i.fullRange!==void 0,we=i=>i instanceof ArrayBuffer||typeof SharedArrayBuffer<"u"&&i instanceof SharedArrayBuffer||ArrayBuffer.isView(i)&&!(i instanceof DataView);var X=/(?:(.+?)\n)?((?:\d{2}:)?\d{2}:\d{2}.\d{3})\s+-->\s+((?:\d{2}:)?\d{2}:\d{2}.\d{3})/g,lt=/^WEBVTT(.|\n)*?\n{2}/,W=/<(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})>/g,ue=class{#e;#r=null;#i=!1;constructor(t){this.#e=t}parse(t){t=t.replaceAll(`\r +function c(t){if(!t)throw new Error("Assertion failed.")}var W=t=>t&&t[t.length-1],M=t=>t>=0&&t<2**32,I=(t,i,e)=>{let r=0;for(let s=i;s>a;r<<=1,r|=u}return r},$e=(t,i,e,r)=>{for(let s=i;s>e-s-1<t instanceof ArrayBuffer?new Uint8Array(t):new Uint8Array(t.buffer,t.byteOffset,t.byteLength),S=new TextEncoder,R={bt709:1,bt470bg:5,smpte170m:6},B={bt709:1,smpte170m:6,"iec61966-2-1":13},F={rgb:0,bt709:1,bt470bg:5,smpte170m:6},ae=t=>!!t&&!!t.primaries&&!!t.transfer&&!!t.matrix&&t.fullRange!==void 0,we=t=>t instanceof ArrayBuffer||typeof SharedArrayBuffer<"u"&&t instanceof SharedArrayBuffer||ArrayBuffer.isView(t)&&!(t instanceof DataView);var X=/(?:(.+?)\n)?((?:\d{2}:)?\d{2}:\d{2}.\d{3})\s+-->\s+((?:\d{2}:)?\d{2}:\d{2}.\d{3})/g,lt=/^WEBVTT(.|\n)*?\n{2}/,N=/<(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})>/g,ue=class{constructor(i){this.preambleText=null;this.preambleEmitted=!1;this.options=i}parse(i){i=i.replaceAll(`\r `,` `).replaceAll("\r",` -`),X.lastIndex=0;let e;if(!this.#r){if(!lt.test(t)){let o=new Error("WebVTT preamble incorrect.");throw this.#e.error(o),o}e=X.exec(t);let r=t.slice(0,e?.index??t.length).trimEnd();if(!r){let o=new Error("No WebVTT preamble provided.");throw this.#e.error(o),o}this.#r=r,e&&(t=t.slice(e.index),X.lastIndex=0)}for(;e=X.exec(t);){let r=t.slice(0,e.index),o=e[1],s=e.index+e[0].length,n=t.indexOf(` -`,s)+1,a=t.slice(s,n).trim(),u=t.indexOf(` +`),X.lastIndex=0;let e;if(!this.preambleText){if(!lt.test(i)){let s=new Error("WebVTT preamble incorrect.");throw this.options.error(s),s}e=X.exec(i);let r=i.slice(0,e?.index??i.length).trimEnd();if(!r){let s=new Error("No WebVTT preamble provided.");throw this.options.error(s),s}this.preambleText=r,e&&(i=i.slice(e.index),X.lastIndex=0)}for(;e=X.exec(i);){let r=i.slice(0,e.index),s=e[1],o=e.index+e[0].length,n=i.indexOf(` +`,o)+1,a=i.slice(o,n).trim(),u=i.indexOf(` -`,s);u===-1&&(u=t.length);let f=le(e[2]),m=le(e[3])-f,g=t.slice(n,u).trim();t=t.slice(u).trimStart(),X.lastIndex=0;let v={timestamp:f/1e3,duration:m/1e3,text:g,identifier:o,settings:a,notes:r},y={};this.#i||(y.config={description:this.#r},this.#i=!0),this.#e.output(v,y)}}},dt=/(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})/,le=i=>{let t=dt.exec(i);if(!t)throw new Error("Expected match.");return 60*60*1e3*Number(t[1]||"0")+60*1e3*Number(t[2])+1e3*Number(t[3])+Number(t[4])},de=i=>{let t=Math.floor(i/36e5),e=Math.floor(i%(60*60*1e3)/(60*1e3)),r=Math.floor(i%(60*1e3)/1e3),o=i%1e3;return t.toString().padStart(2,"0")+":"+e.toString().padStart(2,"0")+":"+r.toString().padStart(2,"0")+"."+o.toString().padStart(3,"0")};var G=class{constructor(t){this.writer=t;this.helper=new Uint8Array(8);this.helperView=new DataView(this.helper.buffer);this.offsets=new WeakMap}writeU32(t){this.helperView.setUint32(0,t,!1),this.writer.write(this.helper.subarray(0,4))}writeU64(t){this.helperView.setUint32(0,Math.floor(t/2**32),!1),this.helperView.setUint32(4,t,!1),this.writer.write(this.helper.subarray(0,8))}writeAscii(t){for(let e=0;e[(i%256+256)%256],p=i=>(_.setUint16(0,i,!1),[h[0],h[1]]),ct=i=>(_.setInt16(0,i,!1),[h[0],h[1]]),Qe=i=>(_.setUint32(0,i,!1),[h[1],h[2],h[3]]),l=i=>(_.setUint32(0,i,!1),[h[0],h[1],h[2],h[3]]),Ke=i=>(_.setInt32(0,i,!1),[h[0],h[1],h[2],h[3]]),z=i=>(_.setUint32(0,Math.floor(i/2**32),!1),_.setUint32(4,i,!1),[h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7]]),Se=i=>(_.setInt16(0,2**8*i,!1),[h[0],h[1]]),A=i=>(_.setInt32(0,2**16*i,!1),[h[0],h[1],h[2],h[3]]),ke=i=>(_.setInt32(0,2**30*i,!1),[h[0],h[1],h[2],h[3]]),xe=(i,t)=>{let e=[],r=i;do{let o=r&127;r>>=7,e.length>0&&(o|=128),e.push(o),t!==void 0&&t--}while(r>0||t);return e.reverse()},w=(i,t=!1)=>{let e=Array(i.length).fill(null).map((r,o)=>i.charCodeAt(o));return t&&e.push(0),e},Ae=i=>{let t=null;for(let e of i)(!t||e.timestamp>t.timestamp)&&(t=e);return t},Le=i=>{let t=i*(Math.PI/180),e=Math.cos(t),r=Math.sin(t);return[e,r,0,-r,e,0,0,0,1]},Xe=Le(0),Ge=i=>[A(i[0]),A(i[1]),ke(i[2]),A(i[3]),A(i[4]),ke(i[5]),A(i[6]),A(i[7]),ke(i[8])],b=(i,t,e)=>({type:i,contents:t&&new Uint8Array(t.flat(10)),children:e}),T=(i,t,e,r,o)=>b(i,[C(t),Qe(e),r??[]],o),qe=i=>{let t=512;return i.fragmented?b("ftyp",[w("iso5"),l(t),w("iso5"),w("iso6"),w("mp41")]):b("ftyp",[w("isom"),l(t),w("isom"),i.holdsAvc?w("avc1"):[],w("mp41")])},fe=i=>({type:"mdat",largeSize:i}),Ye=i=>({type:"free",size:i}),q=(i,t,e=!1)=>b("moov",void 0,[ft(t,i),...i.map(r=>mt(r,t)),e?Kt(i):null]),ft=(i,t)=>{let e=S(Math.max(0,...t.filter(n=>n.samples.length>0).map(n=>{let a=Ae(n.samples);return a.timestamp+a.duration})),ce),r=Math.max(0,...t.map(n=>n.track.id))+1,o=!V(i)||!V(e),s=o?z:l;return T("mvhd",+o,0,[s(i),s(i),l(ce),s(e),A(1),Se(1),Array(10).fill(0),Ge(Xe),Array(24).fill(0),l(r)])},mt=(i,t)=>b("trak",void 0,[ht(i,t),pt(i,t)]),ht=(i,t)=>{let e=Ae(i.samples),r=S(e?e.timestamp+e.duration:0,ce),o=!V(t)||!V(r),s=o?z:l,n;if(i.type==="video"){let a=i.track.metadata.rotation;n=a===void 0||typeof a=="number"?Le(a??0):a}else n=Xe;return T("tkhd",+o,3,[s(t),s(t),l(i.track.id),l(0),s(r),Array(8).fill(0),p(0),p(i.track.id),Se(i.type==="audio"?1:0),p(0),Ge(n),A(i.type==="video"?i.info.width:0),A(i.type==="video"?i.info.height:0)])},pt=(i,t)=>b("mdia",void 0,[bt(i,t),Ct(i),yt(i)]),bt=(i,t)=>{let e=Ae(i.samples),r=S(e?e.timestamp+e.duration:0,i.timescale),o=!V(t)||!V(r),s=o?z:l;return T("mdhd",+o,0,[s(t),s(t),l(i.timescale),s(r),p(21956),p(0)])},Tt={video:"vide",audio:"soun",subtitle:"text"},gt={video:"VideoHandler",audio:"SoundHandler",subtitle:"TextHandler"},Ct=i=>T("hdlr",0,0,[w("mhlr"),w(Tt[i.type]),l(0),l(0),l(0),w(gt[i.type],!0)]),yt=i=>b("minf",void 0,[St[i.type](),At(),Ot(i)]),wt=()=>T("vmhd",0,1,[p(0),p(0),p(0),p(0)]),kt=()=>T("smhd",0,0,[p(0),p(0)]),xt=()=>T("nmhd",0,0),St={video:wt,audio:kt,subtitle:xt},At=()=>b("dinf",void 0,[_t()]),_t=()=>T("dref",0,0,[l(1)],[Et()]),Et=()=>T("url ",0,1),Ot=i=>{let t=i.compositionTimeOffsetTable.length>1||i.compositionTimeOffsetTable.some(e=>e.sampleCompositionTimeOffset!==0);return b("stbl",void 0,[vt(i),Ft(i),Wt(i),Ht(i),$t(i),jt(i),t?Qt(i):null])},vt=i=>{let t;return i.type==="video"?t=Mt(tr[i.track.source._codec],i):i.type==="audio"?t=Pt(ir[i.track.source._codec],i):i.type==="subtitle"&&(t=Nt(sr[i.track.source._codec],i)),d(t),T("stsd",0,0,[l(1)],[t])},Mt=(i,t)=>b(i,[Array(6).fill(0),p(1),p(0),p(0),Array(12).fill(0),p(t.info.width),p(t.info.height),l(4718592),l(4718592),l(0),p(1),Array(32).fill(0),p(24),ct(65535)],[rr[t.track.source._codec](t),ae(t.info.decoderConfig.colorSpace)?Vt(t):null]),Vt=i=>b("colr",[w("nclx"),p(N[i.info.decoderConfig.colorSpace.primaries]),p(D[i.info.decoderConfig.colorSpace.transfer]),p(F[i.info.decoderConfig.colorSpace.matrix]),C((i.info.decoderConfig.colorSpace.fullRange?1:0)<<7)]),It=i=>i.info.decoderConfig&&b("avcC",[...U(i.info.decoderConfig.description)]),Ut=i=>i.info.decoderConfig&&b("hvcC",[...U(i.info.decoderConfig.description)]),je=i=>{if(!i.info.decoderConfig)return null;let t=i.info.decoderConfig;d(t.colorSpace);let e=t.codec.split("."),r=Number(e[1]),o=Number(e[2]),a=(Number(e[3])<<4)+(0<<1)+Number(t.colorSpace.fullRange);return T("vpcC",1,0,[C(r),C(o),C(a),C(2),C(2),C(2),p(0)])},zt=()=>{let e=(1<<7)+1;return b("av1C",[e,0,0,0])},Pt=(i,t)=>b(i,[Array(6).fill(0),p(1),p(0),p(0),l(0),p(t.info.numberOfChannels),p(16),p(0),p(0),A(t.info.sampleRate)],[or[t.track.source._codec](t)]),Rt=i=>{let e=[...U(i.info.decoderConfig.description??new ArrayBuffer(0))];return e=[...C(64),...C(21),...Qe(0),...l(0),...l(0),...C(5),...xe(e.length),...e],e=[...p(1),...C(0),...C(4),...xe(e.length),...e,...C(6),...C(1),...C(2)],e=[...C(3),...xe(e.length),...e],T("esds",0,0,e)},Bt=i=>{let t=3840,e=0,r=i.info.decoderConfig?.description;if(r){d(r.byteLength<18);let o=ArrayBuffer.isView(r)?new DataView(r.buffer,r.byteOffset,r.byteLength):new DataView(r);t=o.getUint16(10,!0),e=o.getInt16(14,!0)}return b("dOps",[C(0),C(i.info.numberOfChannels),p(t),l(i.info.sampleRate),Se(e),C(0)])},Nt=(i,t)=>b(i,[Array(6).fill(0),p(1)],[nr[t.track.source._codec](t)]),Dt=i=>b("vttC",[...x.encode(i.info.config.description)]);var Ft=i=>T("stts",0,0,[l(i.timeToSampleTable.length),i.timeToSampleTable.map(t=>[l(t.sampleCount),l(t.sampleDelta)])]),Wt=i=>{if(i.samples.every(e=>e.type==="key"))return null;let t=[...i.samples.entries()].filter(([,e])=>e.type==="key");return T("stss",0,0,[l(t.length),t.map(([e])=>l(e+1))])},Ht=i=>T("stsc",0,0,[l(i.compactlyCodedChunkTable.length),i.compactlyCodedChunkTable.map(t=>[l(t.firstChunk),l(t.samplesPerChunk),l(1)])]),$t=i=>T("stsz",0,0,[l(0),l(i.samples.length),i.samples.map(t=>l(t.size))]),jt=i=>i.finalizedChunks.length>0&&B(i.finalizedChunks).offset>=2**32?T("co64",0,0,[l(i.finalizedChunks.length),i.finalizedChunks.map(t=>z(t.offset))]):T("stco",0,0,[l(i.finalizedChunks.length),i.finalizedChunks.map(t=>l(t.offset))]),Qt=i=>T("ctts",0,0,[l(i.compositionTimeOffsetTable.length),i.compositionTimeOffsetTable.map(t=>[l(t.sampleCount),l(t.sampleCompositionTimeOffset)])]),Kt=i=>b("mvex",void 0,i.map(Lt)),Lt=i=>T("trex",0,0,[l(i.track.id),l(1),l(0),l(0),l(0)]),_e=(i,t)=>b("moof",void 0,[Xt(i),...t.map(Gt)]),Xt=i=>T("mfhd",0,0,[l(i)]),Ze=i=>{let t=0,e=0,r=0,o=0,s=i.type==="delta";return e|=+s,s?t|=1:t|=2,t<<24|e<<16|r<<8|o},Gt=i=>b("traf",void 0,[qt(i),Yt(i),Zt(i)]),qt=i=>{d(i.currentChunk);let t=0;t|=8,t|=16,t|=32,t|=131072;let e=i.currentChunk.samples[1]??i.currentChunk.samples[0],r={duration:e.timescaleUnitsToNextSample,size:e.size,flags:Ze(e)};return T("tfhd",0,t,[l(i.track.id),l(r.duration),l(r.size),l(r.flags)])},Yt=i=>(d(i.currentChunk),T("tfdt",1,0,[z(S(i.currentChunk.startTimestamp,i.timescale))])),Zt=i=>{d(i.currentChunk);let t=i.currentChunk.samples.map(k=>k.timescaleUnitsToNextSample),e=i.currentChunk.samples.map(k=>k.size),r=i.currentChunk.samples.map(Ze),o=i.currentChunk.samples.map(k=>S(k.timestamp-k.decodeTimestamp,i.timescale)),s=new Set(t),n=new Set(e),a=new Set(r),u=new Set(o),f=a.size===2&&r[0]!==r[1],c=s.size>1,m=n.size>1,g=!f&&a.size>1,v=u.size>1||[...u].some(k=>k!==0),y=0;return y|=1,y|=4*+f,y|=256*+c,y|=512*+m,y|=1024*+g,y|=2048*+v,T("trun",1,y,[l(i.currentChunk.samples.length),l(i.currentChunk.offset-i.currentChunk.moofOffset||0),f?l(r[0]):[],i.currentChunk.samples.map((k,R)=>[c?l(t[R]):[],m?l(e[R]):[],g?l(r[R]):[],v?Ke(o[R]):[]])])},Je=i=>b("mfra",void 0,[...i.map(Jt),er()]),Jt=(i,t)=>T("tfra",1,0,[l(i.track.id),l(63),l(i.finalizedChunks.length),i.finalizedChunks.map(r=>[z(S(r.startTimestamp,i.timescale)),z(r.moofOffset),l(t+1),l(1),l(1)])]),er=()=>T("mfro",0,0,[l(0)]),et=()=>b("vtte"),tt=(i,t,e,r,o)=>b("vttc",void 0,[o!==null?b("vsid",[Ke(o)]):null,e!==null?b("iden",[...x.encode(e)]):null,t!==null?b("ctim",[...x.encode(de(t))]):null,r!==null?b("sttg",[...x.encode(r)]):null,b("payl",[...x.encode(i)])]),rt=i=>b("vtta",[...x.encode(i)]),tr={avc:"avc1",hevc:"hvc1",vp8:"vp08",vp9:"vp09",av1:"av01"},rr={avc:It,hevc:Ut,vp8:je,vp9:je,av1:zt},ir={aac:"mp4a",opus:"Opus"},or={aac:Rt,opus:Bt},sr={webvtt:"wvtt"},nr={webvtt:Dt};var H=class{constructor(t){this.trackTimestampInfo=new WeakMap;this.output=t}beforeTrackAdd(t){}onTrackClose(t){}validateAndNormalizeTimestamp(t,e,r){let o=e/1e6,s=this.trackTimestampInfo.get(t);if(!s){if(!r)throw new Error("First frame must be a key frame.");if(this.timestampsMustStartAtZero&&o>0)throw new Error(`Timestamps must start at zero (got ${o}s).`);s={timestampOffset:o,maxTimestamp:t.source._offsetTimestamps?0:o,lastKeyFrameTimestamp:t.source._offsetTimestamps?0:o},this.trackTimestampInfo.set(t,s)}if(t.source._offsetTimestamps&&(o-=s.timestampOffset),o<0)throw new Error(`Timestamps must be non-negative (got ${o}s).`);if(or.start-o.start);t.push({start:e[0].start,size:e[0].data.byteLength});for(let r=1;ru.start<=e&&eur){for(let u=0;u=t.written[s+1].start;)t.written[s].end=Math.max(t.written[s].end,t.written[s+1].end),t.written.splice(s+1,1)}#l(t){let r={start:Math.floor(t/this.#i)*this.#i,data:new Uint8Array(this.#i),written:[],shouldFlush:!1};return this.#t.push(r),this.#t.sort((o,s)=>o.start-s.start),this.#t.indexOf(r)}#n(t=!1){for(let e=0;et.stream.write({type:"write",data:e,position:r}),chunkSize:t.options?.chunkSize}))}};var it=(i,t,e)=>{if(i==="avc"){let r=100;t<=768&&e<=432?r=66:t<=1920&&e<=1080&&(r=77);let o=0,s=t>1920||e>1080?50:41,n=r.toString(16).padStart(2,"0"),a=o.toString(16).padStart(2,"0"),u=s.toString(16).padStart(2,"0");return`avc1.${n}${a}${u}`}else if(i==="hevc"){let r=0,o=1,s=Array(32).fill(0);s[o]=1;let n=parseInt(s.reverse().join(""),2).toString(16).replace(/^0+/,""),a="L",u=120;return t<=1280&&e<=720?u=93:t<=1920&&e<=1080?u=120:t<=3840&&e<=2160?u=150:(a="H",u=180),`hev1.${r===0?"":String.fromCharCode(65+r-1)}${o}.${n}.${a}${u}.B0`}else{if(i==="vp8")return"vp8";if(i==="vp9"){let r="00",o;return t<=854&&e<=480?o="21":t<=1280&&e<=720?o="31":t<=1920&&e<=1080?o="41":t<=3840&&e<=2160?o="51":o="61",`vp09.${r}.${o}.08`}else if(i==="av1"){let o;return t<=854&&e<=480?o="01":t<=1280&&e<=720?o="03":t<=1920&&e<=1080?o="04":t<=3840&&e<=2160?o="07":o="09",`av01.0.${o}M.08`}}throw new TypeError(`Unhandled codec '${i}'.`)},ot=(i,t,e)=>{if(i==="aac")return t>=2&&e<=24e3?"mp4a.40.29":e<=24e3?"mp4a.40.5":"mp4a.40.2";if(i==="opus")return"opus";if(i==="vorbis")return"vorbis";throw new TypeError(`Unhandled codec '${i}'.`)},pe=i=>{if(!i)throw new TypeError("Video chunk metadata must be provided.");if(typeof i!="object")throw new TypeError("Video chunk metadata must be an object.");if(!i.decoderConfig)throw new TypeError("Video chunk metadata must include a decoder configuration.");if(typeof i.decoderConfig!="object")throw new TypeError("Video chunk metadata decoder configuration must be an object.");if(typeof i.decoderConfig.codec!="string")throw new TypeError("Video chunk metadata decoder configuration must specify a codec string.");if(!Number.isInteger(i.decoderConfig.codedWidth)||i.decoderConfig.codedWidth<=0)throw new TypeError("Video chunk metadata decoder configuration must specify a valid codedWidth (positive integer).");if(!Number.isInteger(i.decoderConfig.codedHeight)||i.decoderConfig.codedHeight<=0)throw new TypeError("Video chunk metadata decoder configuration must specify a valid codedHeight (positive integer).");if(i.decoderConfig.description!==void 0&&!we(i.decoderConfig.description))throw new TypeError("Video chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view.");if(i.decoderConfig.colorSpace!==void 0){let{colorSpace:t}=i.decoderConfig;if(typeof t!="object")throw new TypeError("Video chunk metadata decoder configuration colorSpace, when provided, must be an object.");let e=Object.keys(N);if(t.primaries!=null&&!e.includes(t.primaries))throw new TypeError(`Video chunk metadata decoder configuration colorSpace primaries, when defined, must be one of ${e.join(", ")}.`);let r=Object.keys(D);if(t.transfer!=null&&!r.includes(t.transfer))throw new TypeError(`Video chunk metadata decoder configuration colorSpace transfer, when defined, must be one of ${r.join(", ")}.`);let o=Object.keys(F);if(t.matrix!=null&&!o.includes(t.matrix))throw new TypeError(`Video chunk metadata decoder configuration colorSpace matrix, when defined, must be one of ${o.join(", ")}.`);if(t.fullRange!=null&&typeof t.fullRange!="boolean")throw new TypeError("Video chunk metadata decoder configuration colorSpace fullRange, when defined, must be a boolean.")}if((i.decoderConfig.codec.startsWith("avc1")||i.decoderConfig.codec.startsWith("avc3"))&&!i.decoderConfig.description)throw new TypeError("Video chunk metadata decoder configuration for AVC must include a description, which is expected to be an AVCDecoderConfigurationRecord as specified in ISO 14496-15.");if((i.decoderConfig.codec.startsWith("hev1")||i.decoderConfig.codec.startsWith("hvc1"))&&!i.decoderConfig.description)throw new TypeError("Video chunk metadata decoder configuration for HEVC must include a description, which is expected to be an HEVCDecoderConfigurationRecord as specified in ISO 14496-15.");if((i.decoderConfig.codec==="vp8"||i.decoderConfig.codec.startsWith("vp09"))&&i.decoderConfig.colorSpace===void 0)throw new TypeError("Video chunk metadata decoder configuration for VP8/VP9 must include a colorSpace.")},be=i=>{if(!i)throw new TypeError("Audio chunk metadata must be provided.");if(typeof i!="object")throw new TypeError("Audio chunk metadata must be an object.");if(!i.decoderConfig)throw new TypeError("Audio chunk metadata must include a decoder configuration.");if(typeof i.decoderConfig!="object")throw new TypeError("Audio chunk metadata decoder configuration must be an object.");if(typeof i.decoderConfig.codec!="string")throw new TypeError("Audio chunk metadata decoder configuration must specify a codec string.");if(!Number.isInteger(i.decoderConfig.sampleRate)||i.decoderConfig.sampleRate<=0)throw new TypeError("Audio chunk metadata decoder configuration must specify a valid sampleRate (positive integer).");if(!Number.isInteger(i.decoderConfig.numberOfChannels)||i.decoderConfig.numberOfChannels<=0)throw new TypeError("Audio chunk metadata decoder configuration must specify a valid numberOfChannels (positive integer).");if(i.decoderConfig.description!==void 0&&!we(i.decoderConfig.description))throw new TypeError("Audio chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view.");if(i.decoderConfig.codec==="opus"&&i.decoderConfig.description&&i.decoderConfig.description.byteLength<18)throw new TypeError("Invalid decoder description provided for Opus; must be at least 18 bytes long.")},Te=i=>{if(!i)throw new TypeError("Subtitle metadata must be provided.");if(typeof i!="object")throw new TypeError("Subtitle metadata must be an object.");if(!i.config)throw new TypeError("Subtitle metadata must include a config object.");if(typeof i.config!="object")throw new TypeError("Subtitle metadata config must be an object.");if(typeof i.config.description!="string")throw new TypeError("Subtitle metadata config description must be a string.")};var ce=1e3,lr=2082844800,S=(i,t,e=!0)=>{let r=i*t;return e?Math.round(r):r},ge=class extends H{constructor(e,r){super(e);this.timestampsMustStartAtZero=!0;this.#s=new Y;this.#a=this.#s._createWriter();this.#c=new G(this.#a);this.#l=null;this.#n=null;this.#o=[];this.#d=Math.floor(Date.now()/1e3)+lr;this.#u=[];this.#m=1;this.#e=e._writer,this.#r=new G(this.#e),this.#i=r,this.#t=r.options.fastStart??(this.#e instanceof $?"in-memory":!1),(this.#t==="in-memory"||this.#t==="fragmented")&&(this.#e.ensureMonotonicity=!0)}#e;#r;#i;#t;#s;#a;#c;#l;#n;#o;#d;#u;#m;start(){let e=this.output._tracks.some(r=>r.type==="video"&&r.source._codec==="avc");this.#r.writeBox(qe({holdsAvc:e,fragmented:this.#t==="fragmented"})),this.#l=this.#e.getPos(),this.#t==="in-memory"?this.#n=fe(!1):this.#t==="fragmented"||(this.#n=fe(!0),this.#r.writeBox(this.#n)),this.#e.flush()}#b(e,r){let o=this.#o.find(n=>n.track===e);if(o)return o;pe(r),d(r),d(r.decoderConfig),d(r.decoderConfig.codedWidth!==void 0),d(r.decoderConfig.codedHeight!==void 0);let s={track:e,type:"video",info:{width:r.decoderConfig.codedWidth,height:r.decoderConfig.codedHeight,decoderConfig:r.decoderConfig},timescale:e.metadata.frameRate??57600,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[]};return this.#o.push(s),this.#o.sort((n,a)=>n.track.id-a.track.id),s}#T(e,r){let o=this.#o.find(n=>n.track===e);if(o)return o;be(r),d(r),d(r.decoderConfig);let s={track:e,type:"audio",info:{numberOfChannels:r.decoderConfig.numberOfChannels,sampleRate:r.decoderConfig.sampleRate,decoderConfig:r.decoderConfig},timescale:r.decoderConfig.sampleRate,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[]};return this.#o.push(s),this.#o.sort((n,a)=>n.track.id-a.track.id),s}#A(e,r){let o=this.#o.find(n=>n.track===e);if(o)return o;Te(r),d(r),d(r.config);let s={track:e,type:"subtitle",info:{config:r.config},timescale:1e3,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[],lastCueEndTimestamp:0,cueQueue:[],nextSourceId:0,cueToSourceId:new WeakMap};return this.#o.push(s),this.#o.sort((n,a)=>n.track.id-a.track.id),this.validateAndNormalizeTimestamp(e,0,!0),s}addEncodedVideoChunk(e,r,o){let s=this.#b(e,o),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(s.track,r.timestamp,r.type==="key"),u=this.#h(s,n,a,(r.duration??0)/1e6,r.type);this.#p(s,u)}addEncodedAudioChunk(e,r,o){let s=this.#T(e,o),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(s.track,r.timestamp,r.type==="key"),u=this.#h(s,n,a,(r.duration??0)/1e6,r.type);this.#p(s,u)}addSubtitleCue(e,r,o){let s=this.#A(e,o);this.validateAndNormalizeTimestamp(s.track,1e6*r.timestamp,!0),e.source._codec==="webvtt"&&(s.cueQueue.push(r),this.#g(s,r.timestamp))}#g(e,r){for(;e.cueQueue.length>0;){let o=new Set([]);for(let c of e.cueQueue)d(c.timestamp<=r),d(e.lastCueEndTimestamp<=c.timestamp+c.duration),o.add(Math.max(c.timestamp,e.lastCueEndTimestamp)),o.add(c.timestamp+c.duration);let s=[...o].sort((c,m)=>c-m),n=s[0],a=s[1]??n;if(r=a)break;W.lastIndex=0;let g=W.test(m.text),v=m.timestamp+m.duration,y=e.cueToSourceId.get(m);if(y===void 0&&ao.timestamp).sort((o,s)=>o-s);for(let o=0;o{if(e===a)return r.type==="key";let u=a.sampleQueue[0];return u&&u.type==="key"});s>=1&&n&&(o=!0,this.#x())}else o=s>=.5}o&&(e.currentChunk&&this.#w(e),e.currentChunk={startTimestamp:r.timestamp,samples:[],offset:null,moofOffset:null}),d(e.currentChunk),e.currentChunk.samples.push(r),e.timestampProcessingQueue.push(r)}#w(e){if(d(this.#t!=="fragmented"),!!e.currentChunk){if(e.finalizedChunks.push(e.currentChunk),this.#u.push(e.currentChunk),(e.compactlyCodedChunkTable.length===0||B(e.compactlyCodedChunkTable).samplesPerChunk!==e.currentChunk.samples.length)&&e.compactlyCodedChunkTable.push({firstChunk:e.finalizedChunks.length,samplesPerChunk:e.currentChunk.samples.length}),this.#t==="in-memory"){e.currentChunk.offset=0;return}e.currentChunk.offset=this.#e.getPos();for(let r of e.currentChunk.samples)d(r.data),this.#e.write(r.data),r.data=null;this.#e.flush()}}#k(){d(this.#t==="fragmented");for(let e of this.output._tracks)if(!e.source._closed&&!this.#o.some(r=>r.track===e))return;e:for(;;){let e=null,r=1/0;for(let s of this.#o){if(s.sampleQueue.length===0&&!s.track.source._closed)break e;s.sampleQueue.length>0&&s.sampleQueue[0].timestamp=2**32&&(u.largeSize=!0,c=this.#r.measureBox(u)+f),u.size=c,this.#r.writeBox(u)}for(let u of this.#o){u.currentChunk.offset=this.#e.getPos(),u.currentChunk.moofOffset=o;for(let f of u.currentChunk.samples)this.#e.write(f.data),f.data=null}let n=this.#e.getPos();this.#e.seek(this.#r.offsets.get(s));let a=_e(r,this.#o);this.#r.writeBox(a),this.#e.seek(n);for(let u of this.#o)u.finalizedChunks.push(u.currentChunk),this.#u.push(u.currentChunk),u.currentChunk=null;e&&this.#e.flush()}onTrackClose(e){if(e.type==="subtitle"&&e.source._codec==="webvtt"){let r=this.#o.find(o=>o.track===e);r&&this.#g(r,1/0)}this.#t==="fragmented"&&this.#k()}finalize(){for(let e of this.#o)e.type==="subtitle"&&e.track.source._codec==="webvtt"&&this.#g(e,1/0);if(this.#t==="fragmented"){for(let e of this.#o){for(let r of e.sampleQueue)this.#C(e,r);this.#f(e)}this.#x(!1)}else for(let e of this.#o)this.#f(e),this.#w(e);if(this.#t==="in-memory"){d(this.#n);let e;for(let o=0;o<2;o++){let s=q(this.#o,this.#d),n=this.#r.measureBox(s);e=this.#r.measureBox(this.#n);let a=this.#e.getPos()+n+e;for(let u of this.#u){u.offset=a;for(let{data:f}of u.samples)d(f),a+=f.byteLength,e+=f.byteLength}if(a<2**32)break;e>=2**32&&(this.#n.largeSize=!0)}let r=q(this.#o,this.#d);this.#r.writeBox(r),this.#n.size=e,this.#r.writeBox(this.#n);for(let o of this.#u)for(let s of o.samples)d(s.data),this.#e.write(s.data),s.data=null}else if(this.#t==="fragmented"){let e=this.#e.getPos(),r=Je(this.#o);this.#r.writeBox(r);let o=this.#e.getPos()-e;this.#e.seek(this.#e.getPos()-4),this.#r.writeU32(o)}else{d(this.#n),d(this.#l!==null);let e=this.#r.offsets.get(this.#n);d(e!==void 0);let r=this.#e.getPos()-e;this.#n.size=r,this.#n.largeSize=r>=2**32,this.#r.patchBox(this.#n);let o=q(this.#o,this.#d);if(typeof this.#t=="object"){this.#e.seek(this.#l),this.#r.writeBox(o);let s=e-this.#e.getPos();this.#r.writeBox(Ye(s))}else this.#r.writeBox(o)}}};var te=class{constructor(t){this.value=t}},j=class{constructor(t){this.value=t}},re=class{constructor(t){this.value=t}};var Oe=i=>i<256?1:i<65536?2:i<1<<24?3:i<2**32?4:i<2**40?5:6,ve=i=>i>=-64&&i<64?1:i>=-8192&&i<8192?2:i>=-(1<<20)&&i<1<<20?3:i>=-(1<<27)&&i<1<<27?4:i>=-(2**34)&&i<2**34?5:6,st=i=>{if(i<127)return 1;if(i<16383)return 2;if(i<(1<<21)-1)return 3;if(i<(1<<28)-1)return 4;if(i<2**35-1)return 5;if(i<2**42-1)return 6;throw new Error("EBML VINT size not supported "+i)};var Me=2**15,nt="https://github.com/Vanilagy/webm-muxer",at=6,ut=5,dr={avc:"V_MPEG4/ISO/AVC",hevc:"V_MPEGH/ISO/HEVC",vp8:"V_VP8",vp9:"V_VP9",av1:"V_AV1",aac:"A_AAC",opus:"A_OPUS",webvtt:"S_TEXT/WEBVTT"},cr={video:1,audio:2,subtitle:17},Ce=class extends H{constructor(e,r){super(e);this.timestampsMustStartAtZero=!1;this.#i=new Uint8Array(8);this.#t=new DataView(this.#i.buffer);this.offsets=new WeakMap;this.dataOffsets=new WeakMap;this.#s=[];this.#a=null;this.#c=null;this.#l=null;this.#n=null;this.#o=null;this.#d=null;this.#u=null;this.#m=null;this.#b=new Set;this.#T=0;this.#e=e._writer,this.#r=r,this.#r.options.streamable&&(this.#e.ensureMonotonicity=!0)}#e;#r;#i;#t;#s;#a;#c;#l;#n;#o;#d;#u;#m;#b;#T;#A(e){this.#t.setUint8(0,e),this.#e.write(this.#i.subarray(0,1))}#g(e){this.#t.setFloat32(0,e,!1),this.#e.write(this.#i.subarray(0,4))}#h(e){this.#t.setFloat64(0,e,!1),this.#e.write(this.#i)}#f(e,r=Oe(e)){let o=0;switch(r){case 6:this.#t.setUint8(o++,e/2**40|0);case 5:this.#t.setUint8(o++,e/2**32|0);case 4:this.#t.setUint8(o++,e>>24);case 3:this.#t.setUint8(o++,e>>16);case 2:this.#t.setUint8(o++,e>>8);case 1:this.#t.setUint8(o++,e);break;default:throw new Error("Bad UINT size "+r)}this.#e.write(this.#i.subarray(0,o))}#p(e,r=ve(e)){e<0&&(e+=2**(r*8)),this.#f(e,r)}writeEBMLVarInt(e,r=st(e)){let o=0;switch(r){case 1:this.#t.setUint8(o++,128|e);break;case 2:this.#t.setUint8(o++,64|e>>8),this.#t.setUint8(o++,e);break;case 3:this.#t.setUint8(o++,32|e>>16),this.#t.setUint8(o++,e>>8),this.#t.setUint8(o++,e);break;case 4:this.#t.setUint8(o++,16|e>>24),this.#t.setUint8(o++,e>>16),this.#t.setUint8(o++,e>>8),this.#t.setUint8(o++,e);break;case 5:this.#t.setUint8(o++,8|e/2**32&7),this.#t.setUint8(o++,e>>24),this.#t.setUint8(o++,e>>16),this.#t.setUint8(o++,e>>8),this.#t.setUint8(o++,e);break;case 6:this.#t.setUint8(o++,4|e/2**40&3),this.#t.setUint8(o++,e/2**32|0),this.#t.setUint8(o++,e>>24),this.#t.setUint8(o++,e>>16),this.#t.setUint8(o++,e>>8),this.#t.setUint8(o++,e);break;default:throw new Error("Bad EBML VINT size "+r)}this.#e.write(this.#i.subarray(0,o))}#C(e){this.#e.write(new Uint8Array(e.split("").map(r=>r.charCodeAt(0))))}writeEBML(e){if(e!==null){if(e instanceof Uint8Array)this.#e.write(e);else if(Array.isArray(e))for(let r of e)this.writeEBML(r);else if(this.offsets.set(e,this.#e.getPos()),this.#f(e.id),Array.isArray(e.data)){let r=this.#e.getPos(),o=e.size===-1?1:e.size??4;e.size===-1?this.#A(255):this.#e.seek(this.#e.getPos()+o);let s=this.#e.getPos();if(this.dataOffsets.set(e,s),this.writeEBML(e.data),e.size!==-1){let n=this.#e.getPos()-s,a=this.#e.getPos();this.#e.seek(r),this.writeEBMLVarInt(n,o),this.#e.seek(a)}}else if(typeof e.data=="number"){let r=e.size??Oe(e.data);this.writeEBMLVarInt(r),this.#f(e.data,r)}else if(typeof e.data=="string")this.writeEBMLVarInt(e.data.length),this.#C(e.data);else if(e.data instanceof Uint8Array)this.writeEBMLVarInt(e.data.byteLength,e.size),this.#e.write(e.data);else if(e.data instanceof te)this.writeEBMLVarInt(4),this.#g(e.data.value);else if(e.data instanceof j)this.writeEBMLVarInt(8),this.#h(e.data.value);else if(e.data instanceof re){let r=e.size??ve(e.data.value);this.writeEBMLVarInt(r),this.#p(e.data.value,r)}}}beforeTrackAdd(e){if(this.#r instanceof Q)if(e.type==="video"){if(!["vp8","vp9","av1"].includes(e.source._codec))throw new Error("WebM only supports VP8, VP9 and AV1 as video codecs. Switching to MKV removes this restriction.")}else if(e.type==="audio"){if(!["opus","vorbis"].includes(e.source._codec))throw new Error("WebM only supports Opus and Vorbis as audio codecs. Switching to MKV removes this restriction.")}else if(e.type==="subtitle"){if(e.source._codec!=="webvtt")throw new Error("WebM only supports WebVTT as subtitle codec. Switching to MKV removes this restriction.")}else throw new Error("WebM only supports video, audio and subtitle tracks. Switching to MKV removes this restriction.")}start(){this.#w(),this.#r.options.streamable||this.#k(),this.#x(),this.#V(),this.#e.flush()}#w(){let e={id:440786851,data:[{id:17030,data:1},{id:17143,data:1},{id:17138,data:4},{id:17139,data:8},{id:17026,data:this.#r instanceof Q?"webm":"matroska"},{id:17031,data:2},{id:17029,data:2}]};this.writeEBML(e)}#k(){let e=new Uint8Array([28,83,187,107]),r=new Uint8Array([21,73,169,102]),o=new Uint8Array([22,84,174,107]),s={id:290298740,data:[{id:19899,data:[{id:21419,data:e},{id:21420,size:5,data:0}]},{id:19899,data:[{id:21419,data:r},{id:21420,size:5,data:0}]},{id:19899,data:[{id:21419,data:o},{id:21420,size:5,data:0}]}]};this.#l=s}#x(){let e={id:17545,data:new j(0)};this.#o=e;let r={id:357149030,data:[{id:2807729,data:1e6},{id:19840,data:nt},{id:22337,data:nt},this.#r.options.streamable?null:e]};this.#c=r}#E(){let e={id:374648427,data:[]};this.#n=e;for(let r of this.#s)e.data.push({id:174,data:[{id:215,data:r.track.id},{id:29637,data:r.track.id},{id:131,data:cr[r.type]},{id:134,data:dr[r.track.source._codec]},...r.type==="video"?[r.info.decoderConfig.description?{id:25506,data:U(r.info.decoderConfig.description)}:null,r.track.metadata.frameRate?{id:2352003,data:1e9/r.track.metadata.frameRate}:null,{id:224,data:[{id:176,data:r.info.width},{id:186,data:r.info.height},(()=>{if(r.info.decoderConfig.colorSpace){let o=r.info.decoderConfig.colorSpace;return ae(o)?{id:21936,data:[{id:21937,data:F[o.matrix]},{id:21946,data:D[o.transfer]},{id:21947,data:N[o.primaries]},{id:21945,data:[1,2][Number(o.fullRange)]}]}:null}return null})()]}]:[],...r.type==="audio"?[r.info.decoderConfig.description?{id:25506,data:U(r.info.decoderConfig.description)}:null,{id:225,data:[{id:181,data:new te(r.info.sampleRate)},{id:159,data:r.info.numberOfChannels}]}]:[],...r.type==="subtitle"?[{id:25506,data:x.encode(r.info.config.description)}]:[]]})}#O(){let e={id:408125543,size:this.#r.options.streamable?-1:at,data:[this.#r.options.streamable?null:this.#l,this.#c,this.#n]};this.#a=e,this.writeEBML(e)}#V(){this.#d={id:475249515,data:[]}}get#y(){return d(this.#a),this.dataOffsets.get(this.#a)}#I(e,r){let o=this.#s.find(n=>n.track===e);if(o)return o;pe(r),d(r),d(r.decoderConfig),d(r.decoderConfig.codedWidth!==void 0),d(r.decoderConfig.codedHeight!==void 0);let s={track:e,type:"video",info:{width:r.decoderConfig.codedWidth,height:r.decoderConfig.codedHeight,decoderConfig:r.decoderConfig},chunkQueue:[],lastWrittenMsTimestamp:null};return this.#s.push(s),this.#s.sort((n,a)=>n.track.id-a.track.id),s}#U(e,r){let o=this.#s.find(n=>n.track===e);if(o)return o;be(r),d(r),d(r.decoderConfig);let s={track:e,type:"audio",info:{numberOfChannels:r.decoderConfig.numberOfChannels,sampleRate:r.decoderConfig.sampleRate,decoderConfig:r.decoderConfig},chunkQueue:[],lastWrittenMsTimestamp:null};return this.#s.push(s),this.#s.sort((n,a)=>n.track.id-a.track.id),s}#z(e,r){let o=this.#s.find(n=>n.track===e);if(o)return o;Te(r),d(r),d(r.config);let s={track:e,type:"subtitle",info:{config:r.config},chunkQueue:[],lastWrittenMsTimestamp:null};return this.#s.push(s),this.#s.sort((n,a)=>n.track.id-a.track.id),s}addEncodedVideoChunk(e,r,o){let s=this.#I(e,o),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(s.track,r.timestamp,r.type==="key"),u=this.#_(n,a,(r.duration??0)/1e6,r.type);e.source._codec==="vp9"&&this.#P(s,u),s.chunkQueue.push(u),this.#S()}addEncodedAudioChunk(e,r,o){let s=this.#U(e,o),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(s.track,r.timestamp,r.type==="key"),u=this.#_(n,a,(r.duration??0)/1e6,r.type);s.chunkQueue.push(u),this.#S()}addSubtitleCue(e,r,o){let s=this.#z(e,o),n=this.validateAndNormalizeTimestamp(s.track,1e6*r.timestamp,!0),a=r.text,u=Math.floor(n*1e3);W.lastIndex=0,a=a.replace(W,g=>{let y=le(g.slice(1,-1))-u;return`<${de(y)}>`});let f=x.encode(a),c=`${r.settings??""} +`,o);u===-1&&(u=i.length);let d=le(e[2]),m=le(e[3])-d,C=i.slice(n,u).trim();i=i.slice(u).trimStart(),X.lastIndex=0;let O={timestamp:d/1e3,duration:m/1e3,text:C,identifier:s,settings:a,notes:r},k={};this.preambleEmitted||(k.config={description:this.preambleText},this.preambleEmitted=!0),this.options.output(O,k)}}},ct=/(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})/,le=t=>{let i=ct.exec(t);if(!i)throw new Error("Expected match.");return 60*60*1e3*Number(i[1]||"0")+60*1e3*Number(i[2])+1e3*Number(i[3])+Number(i[4])},ce=t=>{let i=Math.floor(t/36e5),e=Math.floor(t%(60*60*1e3)/(60*1e3)),r=Math.floor(t%(60*1e3)/1e3),s=t%1e3;return i.toString().padStart(2,"0")+":"+e.toString().padStart(2,"0")+":"+r.toString().padStart(2,"0")+"."+s.toString().padStart(3,"0")};var G=class{constructor(i){this.writer=i;this.helper=new Uint8Array(8);this.helperView=new DataView(this.helper.buffer);this.offsets=new WeakMap}writeU32(i){this.helperView.setUint32(0,i,!1),this.writer.write(this.helper.subarray(0,4))}writeU64(i){this.helperView.setUint32(0,Math.floor(i/2**32),!1),this.helperView.setUint32(4,i,!1),this.writer.write(this.helper.subarray(0,8))}writeAscii(i){for(let e=0;e[(t%256+256)%256],h=t=>(A.setUint16(0,t,!1),[p[0],p[1]]),dt=t=>(A.setInt16(0,t,!1),[p[0],p[1]]),Qe=t=>(A.setUint32(0,t,!1),[p[1],p[2],p[3]]),l=t=>(A.setUint32(0,t,!1),[p[0],p[1],p[2],p[3]]),Ke=t=>(A.setInt32(0,t,!1),[p[0],p[1],p[2],p[3]]),D=t=>(A.setUint32(0,Math.floor(t/2**32),!1),A.setUint32(4,t,!1),[p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]]),xe=t=>(A.setInt16(0,2**8*t,!1),[p[0],p[1]]),v=t=>(A.setInt32(0,2**16*t,!1),[p[0],p[1],p[2],p[3]]),ye=t=>(A.setInt32(0,2**30*t,!1),[p[0],p[1],p[2],p[3]]),Se=(t,i)=>{let e=[],r=t;do{let s=r&127;r>>=7,e.length>0&&(s|=128),e.push(s),i!==void 0&&i--}while(r>0||i);return e.reverse()},w=(t,i=!1)=>{let e=Array(t.length).fill(null).map((r,s)=>t.charCodeAt(s));return i&&e.push(0),e},ve=t=>{let i=null;for(let e of t)(!i||e.timestamp>i.timestamp)&&(i=e);return i},Le=t=>{let i=t*(Math.PI/180),e=Math.cos(i),r=Math.sin(i);return[e,r,0,-r,e,0,0,0,1]},Xe=Le(0),Ge=t=>[v(t[0]),v(t[1]),ye(t[2]),v(t[3]),v(t[4]),ye(t[5]),v(t[6]),v(t[7]),ye(t[8])],b=(t,i,e)=>({type:t,contents:i&&new Uint8Array(i.flat(10)),children:e}),T=(t,i,e,r,s)=>b(t,[g(i),Qe(e),r??[]],s),qe=t=>{let i=512;return t.fragmented?b("ftyp",[w("iso5"),l(i),w("iso5"),w("iso6"),w("mp41")]):b("ftyp",[w("isom"),l(i),w("isom"),t.holdsAvc?w("avc1"):[],w("mp41")])},fe=t=>({type:"mdat",largeSize:t}),Ye=t=>({type:"free",size:t}),q=(t,i,e=!1)=>b("moov",void 0,[ft(i,t),...t.map(r=>mt(r,i)),e?Kt(t):null]),ft=(t,i)=>{let e=x(Math.max(0,...i.filter(n=>n.samples.length>0).map(n=>{let a=ve(n.samples);return a.timestamp+a.duration})),de),r=Math.max(0,...i.map(n=>n.track.id))+1,s=!M(t)||!M(e),o=s?D:l;return T("mvhd",+s,0,[o(t),o(t),l(de),o(e),v(1),xe(1),Array(10).fill(0),Ge(Xe),Array(24).fill(0),l(r)])},mt=(t,i)=>b("trak",void 0,[pt(t,i),ht(t,i)]),pt=(t,i)=>{let e=ve(t.samples),r=x(e?e.timestamp+e.duration:0,de),s=!M(i)||!M(r),o=s?D:l,n;if(t.type==="video"){let a=t.track.metadata.rotation;n=a===void 0||typeof a=="number"?Le(a??0):a}else n=Xe;return T("tkhd",+s,3,[o(i),o(i),l(t.track.id),l(0),o(r),Array(8).fill(0),h(0),h(t.track.id),xe(t.type==="audio"?1:0),h(0),Ge(n),v(t.type==="video"?t.info.width:0),v(t.type==="video"?t.info.height:0)])},ht=(t,i)=>b("mdia",void 0,[bt(t,i),gt(t),kt(t)]),bt=(t,i)=>{let e=ve(t.samples),r=x(e?e.timestamp+e.duration:0,t.timescale),s=!M(i)||!M(r),o=s?D:l;return T("mdhd",+s,0,[o(i),o(i),l(t.timescale),o(r),h(21956),h(0)])},Tt={video:"vide",audio:"soun",subtitle:"text"},Ct={video:"VideoHandler",audio:"SoundHandler",subtitle:"TextHandler"},gt=t=>T("hdlr",0,0,[w("mhlr"),w(Tt[t.type]),l(0),l(0),l(0),w(Ct[t.type],!0)]),kt=t=>b("minf",void 0,[xt[t.type](),vt(),Et(t)]),wt=()=>T("vmhd",0,1,[h(0),h(0),h(0),h(0)]),yt=()=>T("smhd",0,0,[h(0),h(0)]),St=()=>T("nmhd",0,0),xt={video:wt,audio:yt,subtitle:St},vt=()=>b("dinf",void 0,[At()]),At=()=>T("dref",0,0,[l(1)],[_t()]),_t=()=>T("url ",0,1),Et=t=>{let i=t.compositionTimeOffsetTable.length>1||t.compositionTimeOffsetTable.some(e=>e.sampleCompositionTimeOffset!==0);return b("stbl",void 0,[Ot(t),Ft(t),Nt(t),Ht(t),$t(t),jt(t),i?Qt(t):null])},Ot=t=>{let i;return t.type==="video"?i=Vt(tr[t.track.source._codec],t):t.type==="audio"?i=Ut(ir[t.track.source._codec],t):t.type==="subtitle"&&(i=Rt(or[t.track.source._codec],t)),c(i),T("stsd",0,0,[l(1)],[i])},Vt=(t,i)=>b(t,[Array(6).fill(0),h(1),h(0),h(0),Array(12).fill(0),h(i.info.width),h(i.info.height),l(4718592),l(4718592),l(0),h(1),Array(32).fill(0),h(24),dt(65535)],[rr[i.track.source._codec](i),ae(i.info.decoderConfig.colorSpace)?Mt(i):null]),Mt=t=>b("colr",[w("nclx"),h(R[t.info.decoderConfig.colorSpace.primaries]),h(B[t.info.decoderConfig.colorSpace.transfer]),h(F[t.info.decoderConfig.colorSpace.matrix]),g((t.info.decoderConfig.colorSpace.fullRange?1:0)<<7)]),It=t=>t.info.decoderConfig&&b("avcC",[...z(t.info.decoderConfig.description)]),zt=t=>t.info.decoderConfig&&b("hvcC",[...z(t.info.decoderConfig.description)]),je=t=>{if(!t.info.decoderConfig)return null;let i=t.info.decoderConfig;c(i.colorSpace);let e=i.codec.split("."),r=Number(e[1]),s=Number(e[2]),a=(Number(e[3])<<4)+(0<<1)+Number(i.colorSpace.fullRange);return T("vpcC",1,0,[g(r),g(s),g(a),g(2),g(2),g(2),h(0)])},Dt=()=>{let e=(1<<7)+1;return b("av1C",[e,0,0,0])},Ut=(t,i)=>b(t,[Array(6).fill(0),h(1),h(0),h(0),l(0),h(i.info.numberOfChannels),h(16),h(0),h(0),v(i.info.sampleRate)],[sr[i.track.source._codec](i)]),Pt=t=>{let e=[...z(t.info.decoderConfig.description??new ArrayBuffer(0))];return e=[...g(64),...g(21),...Qe(0),...l(0),...l(0),...g(5),...Se(e.length),...e],e=[...h(1),...g(0),...g(4),...Se(e.length),...e,...g(6),...g(1),...g(2)],e=[...g(3),...Se(e.length),...e],T("esds",0,0,e)},Wt=t=>{let i=3840,e=0,r=t.info.decoderConfig?.description;if(r){c(r.byteLength<18);let s=ArrayBuffer.isView(r)?new DataView(r.buffer,r.byteOffset,r.byteLength):new DataView(r);i=s.getUint16(10,!0),e=s.getInt16(14,!0)}return b("dOps",[g(0),g(t.info.numberOfChannels),h(i),l(t.info.sampleRate),xe(e),g(0)])},Rt=(t,i)=>b(t,[Array(6).fill(0),h(1)],[nr[i.track.source._codec](i)]),Bt=t=>b("vttC",[...S.encode(t.info.config.description)]);var Ft=t=>T("stts",0,0,[l(t.timeToSampleTable.length),t.timeToSampleTable.map(i=>[l(i.sampleCount),l(i.sampleDelta)])]),Nt=t=>{if(t.samples.every(e=>e.type==="key"))return null;let i=[...t.samples.entries()].filter(([,e])=>e.type==="key");return T("stss",0,0,[l(i.length),i.map(([e])=>l(e+1))])},Ht=t=>T("stsc",0,0,[l(t.compactlyCodedChunkTable.length),t.compactlyCodedChunkTable.map(i=>[l(i.firstChunk),l(i.samplesPerChunk),l(1)])]),$t=t=>T("stsz",0,0,[l(0),l(t.samples.length),t.samples.map(i=>l(i.size))]),jt=t=>t.finalizedChunks.length>0&&W(t.finalizedChunks).offset>=2**32?T("co64",0,0,[l(t.finalizedChunks.length),t.finalizedChunks.map(i=>D(i.offset))]):T("stco",0,0,[l(t.finalizedChunks.length),t.finalizedChunks.map(i=>l(i.offset))]),Qt=t=>T("ctts",0,0,[l(t.compositionTimeOffsetTable.length),t.compositionTimeOffsetTable.map(i=>[l(i.sampleCount),l(i.sampleCompositionTimeOffset)])]),Kt=t=>b("mvex",void 0,t.map(Lt)),Lt=t=>T("trex",0,0,[l(t.track.id),l(1),l(0),l(0),l(0)]),Ae=(t,i)=>b("moof",void 0,[Xt(t),...i.map(Gt)]),Xt=t=>T("mfhd",0,0,[l(t)]),Ze=t=>{let i=0,e=0,r=0,s=0,o=t.type==="delta";return e|=+o,o?i|=1:i|=2,i<<24|e<<16|r<<8|s},Gt=t=>b("traf",void 0,[qt(t),Yt(t),Zt(t)]),qt=t=>{c(t.currentChunk);let i=0;i|=8,i|=16,i|=32,i|=131072;let e=t.currentChunk.samples[1]??t.currentChunk.samples[0],r={duration:e.timescaleUnitsToNextSample,size:e.size,flags:Ze(e)};return T("tfhd",0,i,[l(t.track.id),l(r.duration),l(r.size),l(r.flags)])},Yt=t=>(c(t.currentChunk),T("tfdt",1,0,[D(x(t.currentChunk.startTimestamp,t.timescale))])),Zt=t=>{c(t.currentChunk);let i=t.currentChunk.samples.map(y=>y.timescaleUnitsToNextSample),e=t.currentChunk.samples.map(y=>y.size),r=t.currentChunk.samples.map(Ze),s=t.currentChunk.samples.map(y=>x(y.timestamp-y.decodeTimestamp,t.timescale)),o=new Set(i),n=new Set(e),a=new Set(r),u=new Set(s),d=a.size===2&&r[0]!==r[1],f=o.size>1,m=n.size>1,C=!d&&a.size>1,O=u.size>1||[...u].some(y=>y!==0),k=0;return k|=1,k|=4*+d,k|=256*+f,k|=512*+m,k|=1024*+C,k|=2048*+O,T("trun",1,k,[l(t.currentChunk.samples.length),l(t.currentChunk.offset-t.currentChunk.moofOffset||0),d?l(r[0]):[],t.currentChunk.samples.map((y,P)=>[f?l(i[P]):[],m?l(e[P]):[],C?l(r[P]):[],O?Ke(s[P]):[]])])},Je=t=>b("mfra",void 0,[...t.map(Jt),er()]),Jt=(t,i)=>T("tfra",1,0,[l(t.track.id),l(63),l(t.finalizedChunks.length),t.finalizedChunks.map(r=>[D(x(r.startTimestamp,t.timescale)),D(r.moofOffset),l(i+1),l(1),l(1)])]),er=()=>T("mfro",0,0,[l(0)]),et=()=>b("vtte"),tt=(t,i,e,r,s)=>b("vttc",void 0,[s!==null?b("vsid",[Ke(s)]):null,e!==null?b("iden",[...S.encode(e)]):null,i!==null?b("ctim",[...S.encode(ce(i))]):null,r!==null?b("sttg",[...S.encode(r)]):null,b("payl",[...S.encode(t)])]),rt=t=>b("vtta",[...S.encode(t)]),tr={avc:"avc1",hevc:"hvc1",vp8:"vp08",vp9:"vp09",av1:"av01"},rr={avc:It,hevc:zt,vp8:je,vp9:je,av1:Dt},ir={aac:"mp4a",opus:"Opus"},sr={aac:Pt,opus:Wt},or={webvtt:"wvtt"},nr={webvtt:Bt};var H=class{constructor(i){this.trackTimestampInfo=new WeakMap;this.output=i}beforeTrackAdd(i){}onTrackClose(i){}validateAndNormalizeTimestamp(i,e,r){let s=e/1e6,o=this.trackTimestampInfo.get(i);if(!o){if(!r)throw new Error("First frame must be a key frame.");if(this.timestampsMustStartAtZero&&s>0)throw new Error(`Timestamps must start at zero (got ${s}s).`);o={timestampOffset:s,maxTimestamp:i.source._offsetTimestamps?0:s,lastKeyFrameTimestamp:i.source._offsetTimestamps?0:s},this.trackTimestampInfo.set(i,o)}if(i.source._offsetTimestamps&&(s-=o.timestampOffset),s<0)throw new Error(`Timestamps must be non-negative (got ${s}s).`);if(ss.start-o.start);e.push({start:r[0].start,size:r[0].data.byteLength});for(let s=1;sd.start<=r&&rur){for(let d=0;d=e.written[n+1].start;)e.written[n].end=Math.max(e.written[n].end,e.written[n+1].end),e.written.splice(n+1,1)}createChunk(e){let s={start:Math.floor(e/this.chunkSize)*this.chunkSize,data:new Uint8Array(this.chunkSize),written:[],shouldFlush:!1};return this.chunks.push(s),this.chunks.sort((o,n)=>o.start-n.start),this.chunks.indexOf(s)}flushChunks(e=!1){for(let r=0;ri.stream.write({type:"write",data:e,position:r}),chunkSize:i.options?.chunkSize}))}};var it=(t,i,e)=>{if(t==="avc"){let r=100;i<=768&&e<=432?r=66:i<=1920&&e<=1080&&(r=77);let s=0,o=i>1920||e>1080?50:41,n=r.toString(16).padStart(2,"0"),a=s.toString(16).padStart(2,"0"),u=o.toString(16).padStart(2,"0");return`avc1.${n}${a}${u}`}else if(t==="hevc"){let r=0,s=1,o=Array(32).fill(0);o[s]=1;let n=parseInt(o.reverse().join(""),2).toString(16).replace(/^0+/,""),a="L",u=120;return i<=1280&&e<=720?u=93:i<=1920&&e<=1080?u=120:i<=3840&&e<=2160?u=150:(a="H",u=180),`hev1.${r===0?"":String.fromCharCode(65+r-1)}${s}.${n}.${a}${u}.B0`}else{if(t==="vp8")return"vp8";if(t==="vp9"){let r="00",s;return i<=854&&e<=480?s="21":i<=1280&&e<=720?s="31":i<=1920&&e<=1080?s="41":i<=3840&&e<=2160?s="51":s="61",`vp09.${r}.${s}.08`}else if(t==="av1"){let s;return i<=854&&e<=480?s="01":i<=1280&&e<=720?s="03":i<=1920&&e<=1080?s="04":i<=3840&&e<=2160?s="07":s="09",`av01.0.${s}M.08`}}throw new TypeError(`Unhandled codec '${t}'.`)},st=(t,i,e)=>{if(t==="aac")return i>=2&&e<=24e3?"mp4a.40.29":e<=24e3?"mp4a.40.5":"mp4a.40.2";if(t==="opus")return"opus";if(t==="vorbis")return"vorbis";throw new TypeError(`Unhandled codec '${t}'.`)},he=t=>{if(!t)throw new TypeError("Video chunk metadata must be provided.");if(typeof t!="object")throw new TypeError("Video chunk metadata must be an object.");if(!t.decoderConfig)throw new TypeError("Video chunk metadata must include a decoder configuration.");if(typeof t.decoderConfig!="object")throw new TypeError("Video chunk metadata decoder configuration must be an object.");if(typeof t.decoderConfig.codec!="string")throw new TypeError("Video chunk metadata decoder configuration must specify a codec string.");if(!Number.isInteger(t.decoderConfig.codedWidth)||t.decoderConfig.codedWidth<=0)throw new TypeError("Video chunk metadata decoder configuration must specify a valid codedWidth (positive integer).");if(!Number.isInteger(t.decoderConfig.codedHeight)||t.decoderConfig.codedHeight<=0)throw new TypeError("Video chunk metadata decoder configuration must specify a valid codedHeight (positive integer).");if(t.decoderConfig.description!==void 0&&!we(t.decoderConfig.description))throw new TypeError("Video chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view.");if(t.decoderConfig.colorSpace!==void 0){let{colorSpace:i}=t.decoderConfig;if(typeof i!="object")throw new TypeError("Video chunk metadata decoder configuration colorSpace, when provided, must be an object.");let e=Object.keys(R);if(i.primaries!=null&&!e.includes(i.primaries))throw new TypeError(`Video chunk metadata decoder configuration colorSpace primaries, when defined, must be one of ${e.join(", ")}.`);let r=Object.keys(B);if(i.transfer!=null&&!r.includes(i.transfer))throw new TypeError(`Video chunk metadata decoder configuration colorSpace transfer, when defined, must be one of ${r.join(", ")}.`);let s=Object.keys(F);if(i.matrix!=null&&!s.includes(i.matrix))throw new TypeError(`Video chunk metadata decoder configuration colorSpace matrix, when defined, must be one of ${s.join(", ")}.`);if(i.fullRange!=null&&typeof i.fullRange!="boolean")throw new TypeError("Video chunk metadata decoder configuration colorSpace fullRange, when defined, must be a boolean.")}if((t.decoderConfig.codec.startsWith("avc1")||t.decoderConfig.codec.startsWith("avc3"))&&!t.decoderConfig.description)throw new TypeError("Video chunk metadata decoder configuration for AVC must include a description, which is expected to be an AVCDecoderConfigurationRecord as specified in ISO 14496-15.");if((t.decoderConfig.codec.startsWith("hev1")||t.decoderConfig.codec.startsWith("hvc1"))&&!t.decoderConfig.description)throw new TypeError("Video chunk metadata decoder configuration for HEVC must include a description, which is expected to be an HEVCDecoderConfigurationRecord as specified in ISO 14496-15.");if((t.decoderConfig.codec==="vp8"||t.decoderConfig.codec.startsWith("vp09"))&&t.decoderConfig.colorSpace===void 0)throw new TypeError("Video chunk metadata decoder configuration for VP8/VP9 must include a colorSpace.")},be=t=>{if(!t)throw new TypeError("Audio chunk metadata must be provided.");if(typeof t!="object")throw new TypeError("Audio chunk metadata must be an object.");if(!t.decoderConfig)throw new TypeError("Audio chunk metadata must include a decoder configuration.");if(typeof t.decoderConfig!="object")throw new TypeError("Audio chunk metadata decoder configuration must be an object.");if(typeof t.decoderConfig.codec!="string")throw new TypeError("Audio chunk metadata decoder configuration must specify a codec string.");if(!Number.isInteger(t.decoderConfig.sampleRate)||t.decoderConfig.sampleRate<=0)throw new TypeError("Audio chunk metadata decoder configuration must specify a valid sampleRate (positive integer).");if(!Number.isInteger(t.decoderConfig.numberOfChannels)||t.decoderConfig.numberOfChannels<=0)throw new TypeError("Audio chunk metadata decoder configuration must specify a valid numberOfChannels (positive integer).");if(t.decoderConfig.description!==void 0&&!we(t.decoderConfig.description))throw new TypeError("Audio chunk metadata decoder configuration description, when defined, must be an ArrayBuffer or an ArrayBuffer view.");if(t.decoderConfig.codec==="opus"&&t.decoderConfig.description&&t.decoderConfig.description.byteLength<18)throw new TypeError("Invalid decoder description provided for Opus; must be at least 18 bytes long.")},Te=t=>{if(!t)throw new TypeError("Subtitle metadata must be provided.");if(typeof t!="object")throw new TypeError("Subtitle metadata must be an object.");if(!t.config)throw new TypeError("Subtitle metadata must include a config object.");if(typeof t.config!="object")throw new TypeError("Subtitle metadata config must be an object.");if(typeof t.config.description!="string")throw new TypeError("Subtitle metadata config description must be a string.")};var de=1e3,lr=2082844800,x=(t,i,e=!0)=>{let r=t*i;return e?Math.round(r):r},Ce=class extends H{constructor(e,r){super(e);this.timestampsMustStartAtZero=!0;this.auxTarget=new Y;this.auxWriter=this.auxTarget._createWriter();this.auxBoxWriter=new G(this.auxWriter);this.ftypSize=null;this.mdat=null;this.trackDatas=[];this.creationTime=Math.floor(Date.now()/1e3)+lr;this.finalizedChunks=[];this.nextFragmentNumber=1;this.writer=e._writer,this.boxWriter=new G(this.writer),this.fastStart=r.options.fastStart??(this.writer instanceof $?"in-memory":!1),(this.fastStart==="in-memory"||this.fastStart==="fragmented")&&(this.writer.ensureMonotonicity=!0)}start(){let e=this.output._tracks.some(r=>r.type==="video"&&r.source._codec==="avc");this.boxWriter.writeBox(qe({holdsAvc:e,fragmented:this.fastStart==="fragmented"})),this.ftypSize=this.writer.getPos(),this.fastStart==="in-memory"?this.mdat=fe(!1):this.fastStart==="fragmented"||(this.mdat=fe(!0),this.boxWriter.writeBox(this.mdat)),this.writer.flush()}getVideoTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;he(r),c(r),c(r.decoderConfig),c(r.decoderConfig.codedWidth!==void 0),c(r.decoderConfig.codedHeight!==void 0);let o={track:e,type:"video",info:{width:r.decoderConfig.codedWidth,height:r.decoderConfig.codedHeight,decoderConfig:r.decoderConfig},timescale:e.metadata.frameRate??57600,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[]};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),o}getAudioTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;be(r),c(r),c(r.decoderConfig);let o={track:e,type:"audio",info:{numberOfChannels:r.decoderConfig.numberOfChannels,sampleRate:r.decoderConfig.sampleRate,decoderConfig:r.decoderConfig},timescale:r.decoderConfig.sampleRate,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[]};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),o}getSubtitleTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;Te(r),c(r),c(r.config);let o={track:e,type:"subtitle",info:{config:r.config},timescale:1e3,samples:[],sampleQueue:[],timestampProcessingQueue:[],timeToSampleTable:[],compositionTimeOffsetTable:[],lastTimescaleUnits:null,lastSample:null,finalizedChunks:[],currentChunk:null,compactlyCodedChunkTable:[],lastCueEndTimestamp:0,cueQueue:[],nextSourceId:0,cueToSourceId:new WeakMap};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),this.validateAndNormalizeTimestamp(e,0,!0),o}addEncodedVideoChunk(e,r,s){let o=this.getVideoTrackData(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.createSampleForTrack(o,n,a,(r.duration??0)/1e6,r.type);this.registerSample(o,u)}addEncodedAudioChunk(e,r,s){let o=this.getAudioTrackData(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.createSampleForTrack(o,n,a,(r.duration??0)/1e6,r.type);this.registerSample(o,u)}addSubtitleCue(e,r,s){let o=this.getSubtitleTrackData(e,s);this.validateAndNormalizeTimestamp(o.track,1e6*r.timestamp,!0),e.source._codec==="webvtt"&&(o.cueQueue.push(r),this.processWebVTTCues(o,r.timestamp))}processWebVTTCues(e,r){for(;e.cueQueue.length>0;){let s=new Set([]);for(let f of e.cueQueue)c(f.timestamp<=r),c(e.lastCueEndTimestamp<=f.timestamp+f.duration),s.add(Math.max(f.timestamp,e.lastCueEndTimestamp)),s.add(f.timestamp+f.duration);let o=[...s].sort((f,m)=>f-m),n=o[0],a=o[1]??n;if(r=a)break;N.lastIndex=0;let C=N.test(m.text),O=m.timestamp+m.duration,k=e.cueToSourceId.get(m);if(k===void 0&&as.timestamp).sort((s,o)=>s-o);for(let s=0;s{if(e===a)return r.type==="key";let u=a.sampleQueue[0];return u&&u.type==="key"});o>=1&&n&&(s=!0,this.finalizeFragment())}else s=o>=.5}s&&(e.currentChunk&&this.finalizeCurrentChunk(e),e.currentChunk={startTimestamp:r.timestamp,samples:[],offset:null,moofOffset:null}),c(e.currentChunk),e.currentChunk.samples.push(r),e.timestampProcessingQueue.push(r)}finalizeCurrentChunk(e){if(c(this.fastStart!=="fragmented"),!!e.currentChunk){if(e.finalizedChunks.push(e.currentChunk),this.finalizedChunks.push(e.currentChunk),(e.compactlyCodedChunkTable.length===0||W(e.compactlyCodedChunkTable).samplesPerChunk!==e.currentChunk.samples.length)&&e.compactlyCodedChunkTable.push({firstChunk:e.finalizedChunks.length,samplesPerChunk:e.currentChunk.samples.length}),this.fastStart==="in-memory"){e.currentChunk.offset=0;return}e.currentChunk.offset=this.writer.getPos();for(let r of e.currentChunk.samples)c(r.data),this.writer.write(r.data),r.data=null;this.writer.flush()}}interleaveSamples(){c(this.fastStart==="fragmented");for(let e of this.output._tracks)if(!e.source._closed&&!this.trackDatas.some(r=>r.track===e))return;e:for(;;){let e=null,r=1/0;for(let o of this.trackDatas){if(o.sampleQueue.length===0&&!o.track.source._closed)break e;o.sampleQueue.length>0&&o.sampleQueue[0].timestamp=2**32&&(u.largeSize=!0,f=this.boxWriter.measureBox(u)+d),u.size=f,this.boxWriter.writeBox(u)}for(let u of this.trackDatas){u.currentChunk.offset=this.writer.getPos(),u.currentChunk.moofOffset=s;for(let d of u.currentChunk.samples)this.writer.write(d.data),d.data=null}let n=this.writer.getPos();this.writer.seek(this.boxWriter.offsets.get(o));let a=Ae(r,this.trackDatas);this.boxWriter.writeBox(a),this.writer.seek(n);for(let u of this.trackDatas)u.finalizedChunks.push(u.currentChunk),this.finalizedChunks.push(u.currentChunk),u.currentChunk=null;e&&this.writer.flush()}onTrackClose(e){if(e.type==="subtitle"&&e.source._codec==="webvtt"){let r=this.trackDatas.find(s=>s.track===e);r&&this.processWebVTTCues(r,1/0)}this.fastStart==="fragmented"&&this.interleaveSamples()}finalize(){for(let e of this.trackDatas)e.type==="subtitle"&&e.track.source._codec==="webvtt"&&this.processWebVTTCues(e,1/0);if(this.fastStart==="fragmented"){for(let e of this.trackDatas){for(let r of e.sampleQueue)this.addSampleToTrack(e,r);this.processTimestamps(e)}this.finalizeFragment(!1)}else for(let e of this.trackDatas)this.processTimestamps(e),this.finalizeCurrentChunk(e);if(this.fastStart==="in-memory"){c(this.mdat);let e;for(let s=0;s<2;s++){let o=q(this.trackDatas,this.creationTime),n=this.boxWriter.measureBox(o);e=this.boxWriter.measureBox(this.mdat);let a=this.writer.getPos()+n+e;for(let u of this.finalizedChunks){u.offset=a;for(let{data:d}of u.samples)c(d),a+=d.byteLength,e+=d.byteLength}if(a<2**32)break;e>=2**32&&(this.mdat.largeSize=!0)}let r=q(this.trackDatas,this.creationTime);this.boxWriter.writeBox(r),this.mdat.size=e,this.boxWriter.writeBox(this.mdat);for(let s of this.finalizedChunks)for(let o of s.samples)c(o.data),this.writer.write(o.data),o.data=null}else if(this.fastStart==="fragmented"){let e=this.writer.getPos(),r=Je(this.trackDatas);this.boxWriter.writeBox(r);let s=this.writer.getPos()-e;this.writer.seek(this.writer.getPos()-4),this.boxWriter.writeU32(s)}else{c(this.mdat),c(this.ftypSize!==null);let e=this.boxWriter.offsets.get(this.mdat);c(e!==void 0);let r=this.writer.getPos()-e;this.mdat.size=r,this.mdat.largeSize=r>=2**32,this.boxWriter.patchBox(this.mdat);let s=q(this.trackDatas,this.creationTime);if(typeof this.fastStart=="object"){this.writer.seek(this.ftypSize),this.boxWriter.writeBox(s);let o=e-this.writer.getPos();this.boxWriter.writeBox(Ye(o))}else this.boxWriter.writeBox(s)}}};var te=class{constructor(i){this.value=i}},j=class{constructor(i){this.value=i}},re=class{constructor(i){this.value=i}};var Ee=t=>t<256?1:t<65536?2:t<1<<24?3:t<2**32?4:t<2**40?5:6,Oe=t=>t>=-64&&t<64?1:t>=-8192&&t<8192?2:t>=-(1<<20)&&t<1<<20?3:t>=-(1<<27)&&t<1<<27?4:t>=-(2**34)&&t<2**34?5:6,ot=t=>{if(t<127)return 1;if(t<16383)return 2;if(t<(1<<21)-1)return 3;if(t<(1<<28)-1)return 4;if(t<2**35-1)return 5;if(t<2**42-1)return 6;throw new Error("EBML VINT size not supported "+t)};var Ve=2**15,nt="https://github.com/Vanilagy/webm-muxer",at=6,ut=5,cr={avc:"V_MPEG4/ISO/AVC",hevc:"V_MPEGH/ISO/HEVC",vp8:"V_VP8",vp9:"V_VP9",av1:"V_AV1",aac:"A_AAC",opus:"A_OPUS",webvtt:"S_TEXT/WEBVTT"},dr={video:1,audio:2,subtitle:17},ge=class extends H{constructor(e,r){super(e);this.timestampsMustStartAtZero=!1;this.helper=new Uint8Array(8);this.helperView=new DataView(this.helper.buffer);this.offsets=new WeakMap;this.dataOffsets=new WeakMap;this.trackDatas=[];this.segment=null;this.segmentInfo=null;this.seekHead=null;this.tracksElement=null;this.segmentDuration=null;this.cues=null;this.currentCluster=null;this.currentClusterMsTimestamp=null;this.trackDatasInCurrentCluster=new Set;this.duration=0;this.writer=e._writer,this.format=r,this.format.options.streamable&&(this.writer.ensureMonotonicity=!0)}writeByte(e){this.helperView.setUint8(0,e),this.writer.write(this.helper.subarray(0,1))}writeFloat32(e){this.helperView.setFloat32(0,e,!1),this.writer.write(this.helper.subarray(0,4))}writeFloat64(e){this.helperView.setFloat64(0,e,!1),this.writer.write(this.helper)}writeUnsignedInt(e,r=Ee(e)){let s=0;switch(r){case 6:this.helperView.setUint8(s++,e/2**40|0);case 5:this.helperView.setUint8(s++,e/2**32|0);case 4:this.helperView.setUint8(s++,e>>24);case 3:this.helperView.setUint8(s++,e>>16);case 2:this.helperView.setUint8(s++,e>>8);case 1:this.helperView.setUint8(s++,e);break;default:throw new Error("Bad UINT size "+r)}this.writer.write(this.helper.subarray(0,s))}writeSignedInt(e,r=Oe(e)){e<0&&(e+=2**(r*8)),this.writeUnsignedInt(e,r)}writeEBMLVarInt(e,r=ot(e)){let s=0;switch(r){case 1:this.helperView.setUint8(s++,128|e);break;case 2:this.helperView.setUint8(s++,64|e>>8),this.helperView.setUint8(s++,e);break;case 3:this.helperView.setUint8(s++,32|e>>16),this.helperView.setUint8(s++,e>>8),this.helperView.setUint8(s++,e);break;case 4:this.helperView.setUint8(s++,16|e>>24),this.helperView.setUint8(s++,e>>16),this.helperView.setUint8(s++,e>>8),this.helperView.setUint8(s++,e);break;case 5:this.helperView.setUint8(s++,8|e/2**32&7),this.helperView.setUint8(s++,e>>24),this.helperView.setUint8(s++,e>>16),this.helperView.setUint8(s++,e>>8),this.helperView.setUint8(s++,e);break;case 6:this.helperView.setUint8(s++,4|e/2**40&3),this.helperView.setUint8(s++,e/2**32|0),this.helperView.setUint8(s++,e>>24),this.helperView.setUint8(s++,e>>16),this.helperView.setUint8(s++,e>>8),this.helperView.setUint8(s++,e);break;default:throw new Error("Bad EBML VINT size "+r)}this.writer.write(this.helper.subarray(0,s))}writeString(e){this.writer.write(new Uint8Array(e.split("").map(r=>r.charCodeAt(0))))}writeEBML(e){if(e!==null){if(e instanceof Uint8Array)this.writer.write(e);else if(Array.isArray(e))for(let r of e)this.writeEBML(r);else if(this.offsets.set(e,this.writer.getPos()),this.writeUnsignedInt(e.id),Array.isArray(e.data)){let r=this.writer.getPos(),s=e.size===-1?1:e.size??4;e.size===-1?this.writeByte(255):this.writer.seek(this.writer.getPos()+s);let o=this.writer.getPos();if(this.dataOffsets.set(e,o),this.writeEBML(e.data),e.size!==-1){let n=this.writer.getPos()-o,a=this.writer.getPos();this.writer.seek(r),this.writeEBMLVarInt(n,s),this.writer.seek(a)}}else if(typeof e.data=="number"){let r=e.size??Ee(e.data);this.writeEBMLVarInt(r),this.writeUnsignedInt(e.data,r)}else if(typeof e.data=="string")this.writeEBMLVarInt(e.data.length),this.writeString(e.data);else if(e.data instanceof Uint8Array)this.writeEBMLVarInt(e.data.byteLength,e.size),this.writer.write(e.data);else if(e.data instanceof te)this.writeEBMLVarInt(4),this.writeFloat32(e.data.value);else if(e.data instanceof j)this.writeEBMLVarInt(8),this.writeFloat64(e.data.value);else if(e.data instanceof re){let r=e.size??Oe(e.data.value);this.writeEBMLVarInt(r),this.writeSignedInt(e.data.value,r)}}}beforeTrackAdd(e){if(this.format instanceof Q)if(e.type==="video"){if(!["vp8","vp9","av1"].includes(e.source._codec))throw new Error("WebM only supports VP8, VP9 and AV1 as video codecs. Switching to MKV removes this restriction.")}else if(e.type==="audio"){if(!["opus","vorbis"].includes(e.source._codec))throw new Error("WebM only supports Opus and Vorbis as audio codecs. Switching to MKV removes this restriction.")}else if(e.type==="subtitle"){if(e.source._codec!=="webvtt")throw new Error("WebM only supports WebVTT as subtitle codec. Switching to MKV removes this restriction.")}else throw new Error("WebM only supports video, audio and subtitle tracks. Switching to MKV removes this restriction.")}start(){this.writeEBMLHeader(),this.format.options.streamable||this.createSeekHead(),this.createSegmentInfo(),this.createCues(),this.writer.flush()}writeEBMLHeader(){let e={id:440786851,data:[{id:17030,data:1},{id:17143,data:1},{id:17138,data:4},{id:17139,data:8},{id:17026,data:this.format instanceof Q?"webm":"matroska"},{id:17031,data:2},{id:17029,data:2}]};this.writeEBML(e)}createSeekHead(){let e=new Uint8Array([28,83,187,107]),r=new Uint8Array([21,73,169,102]),s=new Uint8Array([22,84,174,107]),o={id:290298740,data:[{id:19899,data:[{id:21419,data:e},{id:21420,size:5,data:0}]},{id:19899,data:[{id:21419,data:r},{id:21420,size:5,data:0}]},{id:19899,data:[{id:21419,data:s},{id:21420,size:5,data:0}]}]};this.seekHead=o}createSegmentInfo(){let e={id:17545,data:new j(0)};this.segmentDuration=e;let r={id:357149030,data:[{id:2807729,data:1e6},{id:19840,data:nt},{id:22337,data:nt},this.format.options.streamable?null:e]};this.segmentInfo=r}createTracks(){let e={id:374648427,data:[]};this.tracksElement=e;for(let r of this.trackDatas)e.data.push({id:174,data:[{id:215,data:r.track.id},{id:29637,data:r.track.id},{id:131,data:dr[r.type]},{id:134,data:cr[r.track.source._codec]},...r.type==="video"?[r.info.decoderConfig.description?{id:25506,data:z(r.info.decoderConfig.description)}:null,r.track.metadata.frameRate?{id:2352003,data:1e9/r.track.metadata.frameRate}:null,{id:224,data:[{id:176,data:r.info.width},{id:186,data:r.info.height},(()=>{if(r.info.decoderConfig.colorSpace){let s=r.info.decoderConfig.colorSpace;return ae(s)?{id:21936,data:[{id:21937,data:F[s.matrix]},{id:21946,data:B[s.transfer]},{id:21947,data:R[s.primaries]},{id:21945,data:[1,2][Number(s.fullRange)]}]}:null}return null})()]}]:[],...r.type==="audio"?[r.info.decoderConfig.description?{id:25506,data:z(r.info.decoderConfig.description)}:null,{id:225,data:[{id:181,data:new te(r.info.sampleRate)},{id:159,data:r.info.numberOfChannels}]}]:[],...r.type==="subtitle"?[{id:25506,data:S.encode(r.info.config.description)}]:[]]})}createSegment(){let e={id:408125543,size:this.format.options.streamable?-1:at,data:[this.format.options.streamable?null:this.seekHead,this.segmentInfo,this.tracksElement]};this.segment=e,this.writeEBML(e)}createCues(){this.cues={id:475249515,data:[]}}get segmentDataOffset(){return c(this.segment),this.dataOffsets.get(this.segment)}getVideoTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;he(r),c(r),c(r.decoderConfig),c(r.decoderConfig.codedWidth!==void 0),c(r.decoderConfig.codedHeight!==void 0);let o={track:e,type:"video",info:{width:r.decoderConfig.codedWidth,height:r.decoderConfig.codedHeight,decoderConfig:r.decoderConfig},chunkQueue:[],lastWrittenMsTimestamp:null};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),o}getAudioTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;be(r),c(r),c(r.decoderConfig);let o={track:e,type:"audio",info:{numberOfChannels:r.decoderConfig.numberOfChannels,sampleRate:r.decoderConfig.sampleRate,decoderConfig:r.decoderConfig},chunkQueue:[],lastWrittenMsTimestamp:null};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),o}getSubtitleTrackData(e,r){let s=this.trackDatas.find(n=>n.track===e);if(s)return s;Te(r),c(r),c(r.config);let o={track:e,type:"subtitle",info:{config:r.config},chunkQueue:[],lastWrittenMsTimestamp:null};return this.trackDatas.push(o),this.trackDatas.sort((n,a)=>n.track.id-a.track.id),o}addEncodedVideoChunk(e,r,s){let o=this.getVideoTrackData(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.createInternalChunk(n,a,(r.duration??0)/1e6,r.type);e.source._codec==="vp9"&&this.fixVP9ColorSpace(o,u),o.chunkQueue.push(u),this.interleaveChunks()}addEncodedAudioChunk(e,r,s){let o=this.getAudioTrackData(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.createInternalChunk(n,a,(r.duration??0)/1e6,r.type);o.chunkQueue.push(u),this.interleaveChunks()}addSubtitleCue(e,r,s){let o=this.getSubtitleTrackData(e,s),n=this.validateAndNormalizeTimestamp(o.track,1e6*r.timestamp,!0),a=r.text,u=Math.floor(n*1e3);N.lastIndex=0,a=a.replace(N,C=>{let k=le(C.slice(1,-1))-u;return`<${ce(k)}>`});let d=S.encode(a),f=`${r.settings??""} ${r.identifier??""} -${r.notes??""}`,m=this.#_(f,n,r.duration,"key",c.trim()?x.encode(c):null);s.chunkQueue.push(m),this.#S()}#S(){for(let e of this.output._tracks)if(!e.source._closed&&!this.#s.some(r=>r.track===e))return;e:for(;;){let e=null,r=1/0;for(let s of this.#s){if(s.chunkQueue.length===0&&!s.track.source._closed)break e;s.chunkQueue.length>0&&s.chunkQueue[0].timestamp=2&&o++;let f={rgb:7,bt709:2,bt470bg:1,smpte170m:3}[e.info.decoderConfig.colorSpace.matrix];$e(r.data,o+0,o+3,f)}#_(e,r,o,s,n=null){return{data:e,type:s,timestamp:r,duration:o,additions:n}}#v(e,r){this.#a||(this.#E(),this.#O());let o=Math.floor(1e3*r.timestamp),s=this.#s.every(m=>{if(m.track.source._closed)return!0;if(e===m)return r.type==="key";let g=m.chunkQueue[0];return g&&g.type==="key"});(!this.#u||s&&o-this.#m>=1e3)&&this.#R(o);let n=o-this.#m;if(n<0)return;if(n>=Me)throw new Error(`Current Matroska cluster exceeded its maximum allowed length of ${Me} milliseconds. In order to produce a correct WebM file, you must pass in a key frame at least every ${Me} milliseconds.`);let u=new Uint8Array(4),f=new DataView(u.buffer);f.setUint8(0,128|e.track.id),f.setInt16(1,n,!1);let c=Math.floor(1e3*r.duration);if(c===0&&!r.additions){f.setUint8(3,+(r.type==="key")<<7);let m={id:163,data:[u,r.data]};this.writeEBML(m)}else{let m={id:160,data:[{id:161,data:[u,r.data]},r.type==="delta"?{id:251,data:new re(e.lastWrittenMsTimestamp-o)}:null,r.additions?{id:30113,data:[{id:166,data:[{id:165,data:r.additions},{id:238,data:1}]}]}:null,c>0?{id:155,data:c}:null]};this.writeEBML(m)}this.#T=Math.max(this.#T,o+c),e.lastWrittenMsTimestamp=o,this.#b.add(e)}#R(e){this.#u&&!this.#r.options.streamable&&this.#M(),this.#u={id:524531317,size:this.#r.options.streamable?-1:ut,data:[{id:231,data:e}]},this.writeEBML(this.#u),this.#m=e,this.#b.clear()}#M(){d(this.#u);let e=this.#e.getPos()-this.dataOffsets.get(this.#u),r=this.#e.getPos();this.#e.seek(this.offsets.get(this.#u)+4),this.writeEBMLVarInt(e,ut),this.#e.seek(r);let o=this.offsets.get(this.#u)-this.#y;d(this.#d),this.#d.data.push({id:187,data:[{id:179,data:this.#m},...[...this.#b].map(s=>({id:183,data:[{id:247,data:s.track.id},{id:241,data:o}]}))]})}onTrackClose(){this.#S()}finalize(){this.#a||(this.#E(),this.#O());for(let e of this.#s)for(;e.chunkQueue.length>0;)this.#v(e,e.chunkQueue.shift());if(!this.#r.options.streamable&&this.#u&&this.#M(),d(this.#d),this.writeEBML(this.#d),!this.#r.options.streamable){let e=this.#e.getPos(),r=this.#e.getPos()-this.#y;this.#e.seek(this.offsets.get(this.#a)+4),this.writeEBMLVarInt(r,at),this.#o.data=new j(this.#T),this.#e.seek(this.offsets.get(this.#o)),this.writeEBML(this.#o),this.#l.data[0].data[1].data=this.offsets.get(this.#d)-this.#y,this.#l.data[1].data[1].data=this.offsets.get(this.#c)-this.#y,this.#l.data[2].data[1].data=this.offsets.get(this.#n)-this.#y,this.#e.seek(this.offsets.get(this.#l)),this.writeEBML(this.#l),this.#e.seek(e)}}};var P=class{},Ve=class extends P{constructor(e={}){if(!e||typeof e!="object")throw new TypeError("options must be an object.");if(e.fastStart!==void 0&&![!1,"in-memory","fragmented"].includes(e.fastStart))throw new TypeError('options.fastStart, when provided, must be false, "in-memory", or "fragmented".');super();this.options=e}_createMuxer(e){return new ge(e,this)}},ye=class extends P{constructor(e={}){if(!e||typeof e!="object")throw new TypeError("options must be an object.");if(e.streamable!==void 0&&typeof e.streamable!="boolean")throw new TypeError("options.streamable, when provided, must be a boolean.");super();this.options=e}_createMuxer(e){return new Ce(e,this)}},Q=class extends ye{};var ie=["avc","hevc","vp8","vp9","av1"],oe=["aac","opus"],Ie=["webvtt"],K=class{constructor(){this._connectedTrack=null;this._closed=!1;this._offsetTimestamps=!1}_ensureValidDigest(){if(!this._connectedTrack)throw new Error("Cannot call digest without connecting the source to an output track.");if(!this._connectedTrack.output._started)throw new Error("Cannot call digest before output has been started.");if(this._connectedTrack.output._finalizing)throw new Error("Cannot call digest after output has started finalizing.");if(this._closed)throw new Error("Cannot call digest after source has been closed.")}_start(){}async _flush(){}close(){if(this._closed)throw new Error("Source already closed.");if(!this._connectedTrack)throw new Error("Cannot call close without connecting the source to an output track.");if(!this._connectedTrack.output._started)throw new Error("Cannot call close before output has been started.");this._closed=!0,!this._connectedTrack.output._finalizing&&this._connectedTrack.output._muxer.onTrackClose(this._connectedTrack)}},E=class extends K{constructor(e){super();this._connectedTrack=null;if(!ie.includes(e))throw new TypeError(`Invalid video codec '${e}'. Must be one of: ${ie.join(", ")}.`);this._codec=e}},Ue=class extends E{constructor(t){super(t)}digest(t,e){if(!(t instanceof EncodedVideoChunk))throw new TypeError("chunk must be an EncodedVideoChunk.");this._ensureValidDigest(),this._connectedTrack?.output._muxer.addEncodedVideoChunk(this._connectedTrack,t,e)}},fr=5,mr=i=>{if(!i||typeof i!="object")throw new TypeError("Codec config must be an object.");if(!ie.includes(i.codec))throw new TypeError(`Invalid video codec '${i.codec}'. Must be one of: ${ie.join(", ")}.`);if(!Number.isInteger(i.bitrate)||i.bitrate<=0)throw new TypeError("config.bitrate must be a positive integer.");if(i.latencyMode!==void 0&&["quality","realtime"].includes(i.latencyMode))throw new TypeError("config.latencyMode, when provided, must be 'quality' or 'realtime'.")},se=class{constructor(t,e){this.source=t;this.codecConfig=e;this.encoder=null;this.lastMultipleOfKeyFrameInterval=-1;this.lastWidth=null;this.lastHeight=null;mr(e)}digest(t){if(this.source._ensureValidDigest(),this.lastWidth!==null&&this.lastHeight!==null){if(t.codedWidth!==this.lastWidth||t.codedHeight!==this.lastHeight)throw new Error(`Video frame size must remain constant. Expected ${this.lastWidth}x${this.lastHeight}, got ${t.codedWidth}x${t.codedHeight}.`)}else this.lastWidth=t.codedWidth,this.lastHeight=t.codedHeight;this.ensureEncoder(t),d(this.encoder);let e=Math.floor(t.timestamp/1e6/fr);this.encoder.encode(t,{keyFrame:e!==this.lastMultipleOfKeyFrameInterval}),this.lastMultipleOfKeyFrameInterval=e}ensureEncoder(t){this.encoder||(this.encoder=new VideoEncoder({output:(e,r)=>this.source._connectedTrack?.output._muxer.addEncodedVideoChunk(this.source._connectedTrack,e,r),error:e=>console.error("Video encode error:",e)}),this.encoder.configure({codec:it(this.codecConfig.codec,t.codedWidth,t.codedHeight),width:t.codedWidth,height:t.codedHeight,bitrate:this.codecConfig.bitrate,framerate:this.source._connectedTrack?.metadata.frameRate,latencyMode:this.codecConfig.latencyMode}))}async flush(){return this.encoder?.flush()}},ze=class extends E{constructor(t){super(t.codec),this._encoder=new se(this,t)}digest(t){if(!(t instanceof VideoFrame))throw new TypeError("videoFrame must be a VideoFrame.");this._encoder.digest(t)}_flush(){return this._encoder.flush()}},Pe=class extends E{constructor(t,e){if(!(t instanceof HTMLCanvasElement))throw new TypeError("canvas must be an HTMLCanvasElement.");super(e.codec),this._encoder=new se(this,e),this._canvas=t}digest(t,e=0){if(!Number.isFinite(t)||t<0)throw new TypeError("timestamp must be a non-negative number.");if(!Number.isFinite(e)||e<0)throw new TypeError("duration must be a non-negative number.");let r=new VideoFrame(this._canvas,{timestamp:Math.round(1e6*t),duration:Math.round(1e6*e),alpha:"discard"});this._encoder.digest(r),r.close()}_flush(){return this._encoder.flush()}},Re=class extends E{constructor(e,r){if(!(e instanceof MediaStreamTrack)||e.kind!=="video")throw new TypeError("track must be a video MediaStreamTrack.");r={...r,latencyMode:"realtime"};super(r.codec);this._abortController=null;this._offsetTimestamps=!0;this._encoder=new se(this,r),this._track=e}_start(){this._abortController=new AbortController;let e=new MediaStreamTrackProcessor({track:this._track}),r=new WritableStream({write:o=>{this._encoder.digest(o),o.close()}});e.readable.pipeTo(r,{signal:this._abortController.signal}).catch(o=>{o instanceof DOMException&&o.name==="AbortError"||console.error("Pipe error:",o)})}async _flush(){this._abortController&&(this._abortController.abort(),this._abortController=null),await this._encoder.flush()}},O=class extends K{constructor(e){super();this._connectedTrack=null;if(!oe.includes(e))throw new TypeError(`Invalid audio codec '${e}'. Must be one of: ${oe.join(", ")}.`);this._codec=e}},Be=class extends O{constructor(t){super(t)}digest(t,e){if(!(t instanceof EncodedAudioChunk))throw new TypeError("chunk must be an EncodedAudioChunk.");this._ensureValidDigest(),this._connectedTrack?.output._muxer.addEncodedAudioChunk(this._connectedTrack,t,e)}},hr=i=>{if(!i||typeof i!="object")throw new TypeError("Codec config must be an object.");if(!oe.includes(i.codec))throw new TypeError(`Invalid audio codec '${i.codec}'. Must be one of: ${oe.join(", ")}.`);if(!Number.isInteger(i.bitrate)||i.bitrate<=0)throw new TypeError("config.bitrate must be a positive integer.")},ne=class{constructor(t,e){this.source=t;this.codecConfig=e;this.encoder=null;this.lastNumberOfChannels=null;this.lastSampleRate=null;hr(e)}digest(t){if(this.source._ensureValidDigest(),this.lastNumberOfChannels!==null&&this.lastSampleRate!==null){if(t.numberOfChannels!==this.lastNumberOfChannels||t.sampleRate!==this.lastSampleRate)throw new Error(`Audio parameters must remain constant. Expected ${this.lastNumberOfChannels} channels at ${this.lastSampleRate} Hz, got ${t.numberOfChannels} channels at ${t.sampleRate} Hz.`)}else this.lastNumberOfChannels=t.numberOfChannels,this.lastSampleRate=t.sampleRate;this.ensureEncoder(t),d(this.encoder),this.encoder.encode(t)}ensureEncoder(t){this.encoder||(this.encoder=new AudioEncoder({output:(e,r)=>this.source._connectedTrack?.output._muxer.addEncodedAudioChunk(this.source._connectedTrack,e,r),error:e=>console.error("Audio encode error:",e)}),this.encoder.configure({codec:ot(this.codecConfig.codec,t.numberOfChannels,t.sampleRate),numberOfChannels:t.numberOfChannels,sampleRate:t.sampleRate,bitrate:this.codecConfig.bitrate}))}async flush(){return this.encoder?.flush()}},Ne=class extends O{constructor(t){super(t.codec),this._encoder=new ne(this,t)}digest(t){if(!(t instanceof AudioData))throw new TypeError("audioData must be an AudioData.");this._encoder.digest(t)}_flush(){return this._encoder.flush()}},De=class extends O{constructor(e){super(e.codec);this._accumulatedFrameCount=0;this._encoder=new ne(this,e)}digest(e){if(!(e instanceof AudioBuffer))throw new TypeError("audioBuffer must be an AudioBuffer.");let r=e.numberOfChannels,o=e.sampleRate,s=e.length,n=new Float32Array(r*s);for(let u=0;u{this._encoder.digest(o),o.close()}});e.readable.pipeTo(r,{signal:this._abortController.signal}).catch(o=>{o instanceof DOMException&&o.name==="AbortError"||console.error("Pipe error:",o)})}async _flush(){this._abortController&&(this._abortController.abort(),this._abortController=null),await this._encoder.flush()}},L=class extends K{constructor(e){super();this._connectedTrack=null;if(!Ie.includes(e))throw new TypeError(`Invalid subtitle codec '${e}'. Must be one of: ${Ie.join(", ")}.`);this._codec=e}},We=class extends L{constructor(t){super(t),this._parser=new ue({codec:t,output:(e,r)=>this._connectedTrack?.output._muxer.addSubtitleCue(this._connectedTrack,e,r),error:e=>console.error("Subtitle parse error:",e)})}digest(t){if(typeof t!="string")throw new TypeError("text must be a string.");this._ensureValidDigest(),this._parser.parse(t)}};var He=class{constructor(t){this._tracks=[];this._started=!1;this._finalizing=!1;if(!t||typeof t!="object")throw new TypeError("options must be an object.");if(!(t.format instanceof P))throw new TypeError("options.format must be an OutputFormat.");if(!(t.target instanceof M))throw new TypeError("options.target must be a Target.");if(t.target.output)throw new Error("Target is already used for another output.");t.target.output=this,this._writer=t.target._createWriter(),this._muxer=t.format._createMuxer(this)}addVideoTrack(t,e={}){if(!(t instanceof E))throw new TypeError("source must be a VideoSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");if(typeof e.rotation=="number"&&![0,90,180,270].includes(e.rotation))throw new TypeError(`Invalid video rotation: ${e.rotation}. Has to be 0, 90, 180 or 270.`);if(Array.isArray(e.rotation)&&(e.rotation.length!==9||e.rotation.some(r=>!Number.isFinite(r))))throw new TypeError(`Invalid video transformation matrix: ${e.rotation.join()}`);if(e.frameRate!==void 0&&(!Number.isInteger(e.frameRate)||e.frameRate<=0))throw new TypeError(`Invalid video frame rate: ${e.frameRate}. Must be a positive integer.`);this._addTrack("video",t,e)}addAudioTrack(t,e={}){if(!(t instanceof O))throw new TypeError("source must be an AudioSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");this._addTrack("audio",t,e)}addSubtitleTrack(t,e={}){if(!(t instanceof L))throw new TypeError("source must be a SubtitleSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");this._addTrack("subtitle",t,e)}_addTrack(t,e,r){if(this._started)throw new Error("Cannot add track after output has started.");if(e._connectedTrack)throw new Error("Source is already used for a track.");let o={id:this._tracks.length+1,output:this,type:t,source:e,metadata:r};this._muxer.beforeTrackAdd(o),this._tracks.push(o),e._connectedTrack=o}start(){if(this._started)throw new Error("Output already started.");this._started=!0,this._muxer.start();for(let t of this._tracks)t.source._start()}async finalize(){if(this._finalizing)throw new Error("Cannot call finalize twice.");this._finalizing=!0;let t=this._tracks.map(e=>e.source._flush());await Promise.all(t),this._muxer.finalize(),this._writer.flush(),this._writer.finalize()}};export{oe as AUDIO_CODECS,Y as ArrayBufferTarget,De as AudioBufferSource,Ne as AudioDataSource,O as AudioSource,Pe as CanvasSource,Be as EncodedAudioChunkSource,Ue as EncodedVideoChunkSource,Ee as FileSystemWritableFileStreamTarget,K as MediaSource,Fe as MediaStreamAudioTrackSource,Re as MediaStreamVideoTrackSource,ye as MkvOutputFormat,Ve as Mp4OutputFormat,He as Output,P as OutputFormat,Ie as SUBTITLE_CODECS,Z as StreamTarget,L as SubtitleSource,M as Target,We as TextSubtitleSource,ie as VIDEO_CODECS,ze as VideoFrameSource,E as VideoSource,Q as WebMOutputFormat}; +${r.notes??""}`,m=this.createInternalChunk(d,n,r.duration,"key",f.trim()?S.encode(f):null);o.chunkQueue.push(m),this.interleaveChunks()}interleaveChunks(){for(let e of this.output._tracks)if(!e.source._closed&&!this.trackDatas.some(r=>r.track===e))return;e:for(;;){let e=null,r=1/0;for(let o of this.trackDatas){if(o.chunkQueue.length===0&&!o.track.source._closed)break e;o.chunkQueue.length>0&&o.chunkQueue[0].timestamp=2&&s++;let d={rgb:7,bt709:2,bt470bg:1,smpte170m:3}[e.info.decoderConfig.colorSpace.matrix];$e(r.data,s+0,s+3,d)}createInternalChunk(e,r,s,o,n=null){return{data:e,type:o,timestamp:r,duration:s,additions:n}}writeBlock(e,r){this.segment||(this.createTracks(),this.createSegment());let s=Math.floor(1e3*r.timestamp),o=this.trackDatas.every(m=>{if(m.track.source._closed)return!0;if(e===m)return r.type==="key";let C=m.chunkQueue[0];return C&&C.type==="key"});(!this.currentCluster||o&&s-this.currentClusterMsTimestamp>=1e3)&&this.createNewCluster(s);let n=s-this.currentClusterMsTimestamp;if(n<0)return;if(n>=Ve)throw new Error(`Current Matroska cluster exceeded its maximum allowed length of ${Ve} milliseconds. In order to produce a correct WebM file, you must pass in a key frame at least every ${Ve} milliseconds.`);let u=new Uint8Array(4),d=new DataView(u.buffer);d.setUint8(0,128|e.track.id),d.setInt16(1,n,!1);let f=Math.floor(1e3*r.duration);if(f===0&&!r.additions){d.setUint8(3,+(r.type==="key")<<7);let m={id:163,data:[u,r.data]};this.writeEBML(m)}else{let m={id:160,data:[{id:161,data:[u,r.data]},r.type==="delta"?{id:251,data:new re(e.lastWrittenMsTimestamp-s)}:null,r.additions?{id:30113,data:[{id:166,data:[{id:165,data:r.additions},{id:238,data:1}]}]}:null,f>0?{id:155,data:f}:null]};this.writeEBML(m)}this.duration=Math.max(this.duration,s+f),e.lastWrittenMsTimestamp=s,this.trackDatasInCurrentCluster.add(e)}createNewCluster(e){this.currentCluster&&!this.format.options.streamable&&this.finalizeCurrentCluster(),this.currentCluster={id:524531317,size:this.format.options.streamable?-1:ut,data:[{id:231,data:e}]},this.writeEBML(this.currentCluster),this.currentClusterMsTimestamp=e,this.trackDatasInCurrentCluster.clear()}finalizeCurrentCluster(){c(this.currentCluster);let e=this.writer.getPos()-this.dataOffsets.get(this.currentCluster),r=this.writer.getPos();this.writer.seek(this.offsets.get(this.currentCluster)+4),this.writeEBMLVarInt(e,ut),this.writer.seek(r);let s=this.offsets.get(this.currentCluster)-this.segmentDataOffset;c(this.cues),this.cues.data.push({id:187,data:[{id:179,data:this.currentClusterMsTimestamp},...[...this.trackDatasInCurrentCluster].map(o=>({id:183,data:[{id:247,data:o.track.id},{id:241,data:s}]}))]})}onTrackClose(){this.interleaveChunks()}finalize(){this.segment||(this.createTracks(),this.createSegment());for(let e of this.trackDatas)for(;e.chunkQueue.length>0;)this.writeBlock(e,e.chunkQueue.shift());if(!this.format.options.streamable&&this.currentCluster&&this.finalizeCurrentCluster(),c(this.cues),this.writeEBML(this.cues),!this.format.options.streamable){let e=this.writer.getPos(),r=this.writer.getPos()-this.segmentDataOffset;this.writer.seek(this.offsets.get(this.segment)+4),this.writeEBMLVarInt(r,at),this.segmentDuration.data=new j(this.duration),this.writer.seek(this.offsets.get(this.segmentDuration)),this.writeEBML(this.segmentDuration),this.seekHead.data[0].data[1].data=this.offsets.get(this.cues)-this.segmentDataOffset,this.seekHead.data[1].data[1].data=this.offsets.get(this.segmentInfo)-this.segmentDataOffset,this.seekHead.data[2].data[1].data=this.offsets.get(this.tracksElement)-this.segmentDataOffset,this.writer.seek(this.offsets.get(this.seekHead)),this.writeEBML(this.seekHead),this.writer.seek(e)}}};var U=class{},Me=class extends U{constructor(e={}){if(!e||typeof e!="object")throw new TypeError("options must be an object.");if(e.fastStart!==void 0&&![!1,"in-memory","fragmented"].includes(e.fastStart))throw new TypeError('options.fastStart, when provided, must be false, "in-memory", or "fragmented".');super();this.options=e}_createMuxer(e){return new Ce(e,this)}},ke=class extends U{constructor(e={}){if(!e||typeof e!="object")throw new TypeError("options must be an object.");if(e.streamable!==void 0&&typeof e.streamable!="boolean")throw new TypeError("options.streamable, when provided, must be a boolean.");super();this.options=e}_createMuxer(e){return new ge(e,this)}},Q=class extends ke{};var ie=["avc","hevc","vp8","vp9","av1"],se=["aac","opus"],Ie=["webvtt"],K=class{constructor(){this._connectedTrack=null;this._closed=!1;this._offsetTimestamps=!1}_ensureValidDigest(){if(!this._connectedTrack)throw new Error("Cannot call digest without connecting the source to an output track.");if(!this._connectedTrack.output._started)throw new Error("Cannot call digest before output has been started.");if(this._connectedTrack.output._finalizing)throw new Error("Cannot call digest after output has started finalizing.");if(this._closed)throw new Error("Cannot call digest after source has been closed.")}_start(){}async _flush(){}close(){if(this._closed)throw new Error("Source already closed.");if(!this._connectedTrack)throw new Error("Cannot call close without connecting the source to an output track.");if(!this._connectedTrack.output._started)throw new Error("Cannot call close before output has been started.");this._closed=!0,!this._connectedTrack.output._finalizing&&this._connectedTrack.output._muxer.onTrackClose(this._connectedTrack)}},_=class extends K{constructor(e){super();this._connectedTrack=null;if(!ie.includes(e))throw new TypeError(`Invalid video codec '${e}'. Must be one of: ${ie.join(", ")}.`);this._codec=e}},ze=class extends _{constructor(i){super(i)}digest(i,e){if(!(i instanceof EncodedVideoChunk))throw new TypeError("chunk must be an EncodedVideoChunk.");this._ensureValidDigest(),this._connectedTrack?.output._muxer.addEncodedVideoChunk(this._connectedTrack,i,e)}},fr=5,mr=t=>{if(!t||typeof t!="object")throw new TypeError("Codec config must be an object.");if(!ie.includes(t.codec))throw new TypeError(`Invalid video codec '${t.codec}'. Must be one of: ${ie.join(", ")}.`);if(!Number.isInteger(t.bitrate)||t.bitrate<=0)throw new TypeError("config.bitrate must be a positive integer.");if(t.latencyMode!==void 0&&["quality","realtime"].includes(t.latencyMode))throw new TypeError("config.latencyMode, when provided, must be 'quality' or 'realtime'.")},oe=class{constructor(i,e){this.source=i;this.codecConfig=e;this.encoder=null;this.lastMultipleOfKeyFrameInterval=-1;this.lastWidth=null;this.lastHeight=null;mr(e)}digest(i){if(this.source._ensureValidDigest(),this.lastWidth!==null&&this.lastHeight!==null){if(i.codedWidth!==this.lastWidth||i.codedHeight!==this.lastHeight)throw new Error(`Video frame size must remain constant. Expected ${this.lastWidth}x${this.lastHeight}, got ${i.codedWidth}x${i.codedHeight}.`)}else this.lastWidth=i.codedWidth,this.lastHeight=i.codedHeight;this.ensureEncoder(i),c(this.encoder);let e=Math.floor(i.timestamp/1e6/fr);this.encoder.encode(i,{keyFrame:e!==this.lastMultipleOfKeyFrameInterval}),this.lastMultipleOfKeyFrameInterval=e}ensureEncoder(i){this.encoder||(this.encoder=new VideoEncoder({output:(e,r)=>this.source._connectedTrack?.output._muxer.addEncodedVideoChunk(this.source._connectedTrack,e,r),error:e=>console.error("Video encode error:",e)}),this.encoder.configure({codec:it(this.codecConfig.codec,i.codedWidth,i.codedHeight),width:i.codedWidth,height:i.codedHeight,bitrate:this.codecConfig.bitrate,framerate:this.source._connectedTrack?.metadata.frameRate,latencyMode:this.codecConfig.latencyMode}))}async flush(){return this.encoder?.flush()}},De=class extends _{constructor(i){super(i.codec),this._encoder=new oe(this,i)}digest(i){if(!(i instanceof VideoFrame))throw new TypeError("videoFrame must be a VideoFrame.");this._encoder.digest(i)}_flush(){return this._encoder.flush()}},Ue=class extends _{constructor(i,e){if(!(i instanceof HTMLCanvasElement))throw new TypeError("canvas must be an HTMLCanvasElement.");super(e.codec),this._encoder=new oe(this,e),this._canvas=i}digest(i,e=0){if(!Number.isFinite(i)||i<0)throw new TypeError("timestamp must be a non-negative number.");if(!Number.isFinite(e)||e<0)throw new TypeError("duration must be a non-negative number.");let r=new VideoFrame(this._canvas,{timestamp:Math.round(1e6*i),duration:Math.round(1e6*e),alpha:"discard"});this._encoder.digest(r),r.close()}_flush(){return this._encoder.flush()}},Pe=class extends _{constructor(e,r){if(!(e instanceof MediaStreamTrack)||e.kind!=="video")throw new TypeError("track must be a video MediaStreamTrack.");r={...r,latencyMode:"realtime"};super(r.codec);this._abortController=null;this._offsetTimestamps=!0;this._encoder=new oe(this,r),this._track=e}_start(){this._abortController=new AbortController;let e=new MediaStreamTrackProcessor({track:this._track}),r=new WritableStream({write:s=>{this._encoder.digest(s),s.close()}});e.readable.pipeTo(r,{signal:this._abortController.signal}).catch(s=>{s instanceof DOMException&&s.name==="AbortError"||console.error("Pipe error:",s)})}async _flush(){this._abortController&&(this._abortController.abort(),this._abortController=null),await this._encoder.flush()}},E=class extends K{constructor(e){super();this._connectedTrack=null;if(!se.includes(e))throw new TypeError(`Invalid audio codec '${e}'. Must be one of: ${se.join(", ")}.`);this._codec=e}},We=class extends E{constructor(i){super(i)}digest(i,e){if(!(i instanceof EncodedAudioChunk))throw new TypeError("chunk must be an EncodedAudioChunk.");this._ensureValidDigest(),this._connectedTrack?.output._muxer.addEncodedAudioChunk(this._connectedTrack,i,e)}},pr=t=>{if(!t||typeof t!="object")throw new TypeError("Codec config must be an object.");if(!se.includes(t.codec))throw new TypeError(`Invalid audio codec '${t.codec}'. Must be one of: ${se.join(", ")}.`);if(!Number.isInteger(t.bitrate)||t.bitrate<=0)throw new TypeError("config.bitrate must be a positive integer.")},ne=class{constructor(i,e){this.source=i;this.codecConfig=e;this.encoder=null;this.lastNumberOfChannels=null;this.lastSampleRate=null;pr(e)}digest(i){if(this.source._ensureValidDigest(),this.lastNumberOfChannels!==null&&this.lastSampleRate!==null){if(i.numberOfChannels!==this.lastNumberOfChannels||i.sampleRate!==this.lastSampleRate)throw new Error(`Audio parameters must remain constant. Expected ${this.lastNumberOfChannels} channels at ${this.lastSampleRate} Hz, got ${i.numberOfChannels} channels at ${i.sampleRate} Hz.`)}else this.lastNumberOfChannels=i.numberOfChannels,this.lastSampleRate=i.sampleRate;this.ensureEncoder(i),c(this.encoder),this.encoder.encode(i)}ensureEncoder(i){this.encoder||(this.encoder=new AudioEncoder({output:(e,r)=>this.source._connectedTrack?.output._muxer.addEncodedAudioChunk(this.source._connectedTrack,e,r),error:e=>console.error("Audio encode error:",e)}),this.encoder.configure({codec:st(this.codecConfig.codec,i.numberOfChannels,i.sampleRate),numberOfChannels:i.numberOfChannels,sampleRate:i.sampleRate,bitrate:this.codecConfig.bitrate}))}async flush(){return this.encoder?.flush()}},Re=class extends E{constructor(i){super(i.codec),this._encoder=new ne(this,i)}digest(i){if(!(i instanceof AudioData))throw new TypeError("audioData must be an AudioData.");this._encoder.digest(i)}_flush(){return this._encoder.flush()}},Be=class extends E{constructor(e){super(e.codec);this._accumulatedFrameCount=0;this._encoder=new ne(this,e)}digest(e){if(!(e instanceof AudioBuffer))throw new TypeError("audioBuffer must be an AudioBuffer.");let r=e.numberOfChannels,s=e.sampleRate,o=e.length,n=new Float32Array(r*o);for(let u=0;u{this._encoder.digest(s),s.close()}});e.readable.pipeTo(r,{signal:this._abortController.signal}).catch(s=>{s instanceof DOMException&&s.name==="AbortError"||console.error("Pipe error:",s)})}async _flush(){this._abortController&&(this._abortController.abort(),this._abortController=null),await this._encoder.flush()}},L=class extends K{constructor(e){super();this._connectedTrack=null;if(!Ie.includes(e))throw new TypeError(`Invalid subtitle codec '${e}'. Must be one of: ${Ie.join(", ")}.`);this._codec=e}},Ne=class extends L{constructor(i){super(i),this._parser=new ue({codec:i,output:(e,r)=>this._connectedTrack?.output._muxer.addSubtitleCue(this._connectedTrack,e,r),error:e=>console.error("Subtitle parse error:",e)})}digest(i){if(typeof i!="string")throw new TypeError("text must be a string.");this._ensureValidDigest(),this._parser.parse(i)}};var He=class{constructor(i){this._tracks=[];this._started=!1;this._finalizing=!1;if(!i||typeof i!="object")throw new TypeError("options must be an object.");if(!(i.format instanceof U))throw new TypeError("options.format must be an OutputFormat.");if(!(i.target instanceof V))throw new TypeError("options.target must be a Target.");if(i.target.output)throw new Error("Target is already used for another output.");i.target.output=this,this._writer=i.target._createWriter(),this._muxer=i.format._createMuxer(this)}addVideoTrack(i,e={}){if(!(i instanceof _))throw new TypeError("source must be a VideoSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");if(typeof e.rotation=="number"&&![0,90,180,270].includes(e.rotation))throw new TypeError(`Invalid video rotation: ${e.rotation}. Has to be 0, 90, 180 or 270.`);if(Array.isArray(e.rotation)&&(e.rotation.length!==9||e.rotation.some(r=>!Number.isFinite(r))))throw new TypeError(`Invalid video transformation matrix: ${e.rotation.join()}`);if(e.frameRate!==void 0&&(!Number.isInteger(e.frameRate)||e.frameRate<=0))throw new TypeError(`Invalid video frame rate: ${e.frameRate}. Must be a positive integer.`);this._addTrack("video",i,e)}addAudioTrack(i,e={}){if(!(i instanceof E))throw new TypeError("source must be an AudioSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");this._addTrack("audio",i,e)}addSubtitleTrack(i,e={}){if(!(i instanceof L))throw new TypeError("source must be a SubtitleSource.");if(!e||typeof e!="object")throw new TypeError("metadata must be an object.");this._addTrack("subtitle",i,e)}_addTrack(i,e,r){if(this._started)throw new Error("Cannot add track after output has started.");if(e._connectedTrack)throw new Error("Source is already used for a track.");let s={id:this._tracks.length+1,output:this,type:i,source:e,metadata:r};this._muxer.beforeTrackAdd(s),this._tracks.push(s),e._connectedTrack=s}start(){if(this._started)throw new Error("Output already started.");this._started=!0,this._muxer.start();for(let i of this._tracks)i.source._start()}async finalize(){if(this._finalizing)throw new Error("Cannot call finalize twice.");this._finalizing=!0;let i=this._tracks.map(e=>e.source._flush());await Promise.all(i),this._muxer.finalize(),this._writer.flush(),this._writer.finalize()}};export{se as AUDIO_CODECS,Y as ArrayBufferTarget,Be as AudioBufferSource,Re as AudioDataSource,E as AudioSource,Ue as CanvasSource,We as EncodedAudioChunkSource,ze as EncodedVideoChunkSource,_e as FileSystemWritableFileStreamTarget,K as MediaSource,Fe as MediaStreamAudioTrackSource,Pe as MediaStreamVideoTrackSource,ke as MkvOutputFormat,Me as Mp4OutputFormat,He as Output,U as OutputFormat,Ie as SUBTITLE_CODECS,Z as StreamTarget,L as SubtitleSource,V as Target,Ne as TextSubtitleSource,ie as VIDEO_CODECS,De as VideoFrameSource,_ as VideoSource,Q as WebMOutputFormat}; diff --git a/dist/metamuxer.mjs b/dist/metamuxer.mjs index 843339e..f9716c6 100644 --- a/dist/metamuxer.mjs +++ b/dist/metamuxer.mjs @@ -78,30 +78,29 @@ var cueBlockHeaderRegex = /(?:(.+?)\n)?((?:\d{2}:)?\d{2}:\d{2}.\d{3})\s+-->\s+(( var preambleStartRegex = /^WEBVTT(.|\n)*?\n{2}/; var inlineTimestampRegex = /<(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})>/g; var SubtitleParser = class { - #options; - #preambleText = null; - #preambleEmitted = false; constructor(options) { - this.#options = options; + this.preambleText = null; + this.preambleEmitted = false; + this.options = options; } parse(text) { text = text.replaceAll("\r\n", "\n").replaceAll("\r", "\n"); cueBlockHeaderRegex.lastIndex = 0; let match; - if (!this.#preambleText) { + if (!this.preambleText) { if (!preambleStartRegex.test(text)) { let error = new Error("WebVTT preamble incorrect."); - this.#options.error(error); + this.options.error(error); throw error; } match = cueBlockHeaderRegex.exec(text); let preamble = text.slice(0, match?.index ?? text.length).trimEnd(); if (!preamble) { let error = new Error("No WebVTT preamble provided."); - this.#options.error(error); + this.options.error(error); throw error; } - this.#preambleText = preamble; + this.preambleText = preamble; if (match) { text = text.slice(match.index); cueBlockHeaderRegex.lastIndex = 0; @@ -130,13 +129,13 @@ var SubtitleParser = class { notes }; let meta = {}; - if (!this.#preambleEmitted) { + if (!this.preambleEmitted) { meta.config = { - description: this.#preambleText + description: this.preambleText }; - this.#preambleEmitted = true; + this.preambleEmitted = true; } - this.#options.output(cue, meta); + this.options.output(cue, meta); } } }; @@ -1178,73 +1177,71 @@ var Writer2 = class { } }; var ArrayBufferTargetWriter = class extends Writer2 { - #pos = 0; - #target; - #buffer = new ArrayBuffer(2 ** 16); - #bytes = new Uint8Array(this.#buffer); - #maxPos = 0; constructor(target) { super(); - this.#target = target; + this.pos = 0; + this.buffer = new ArrayBuffer(2 ** 16); + this.bytes = new Uint8Array(this.buffer); + this.maxPos = 0; + this.target = target; } - #ensureSize(size) { - let newLength = this.#buffer.byteLength; + ensureSize(size) { + let newLength = this.buffer.byteLength; while (newLength < size) newLength *= 2; - if (newLength === this.#buffer.byteLength) return; + if (newLength === this.buffer.byteLength) return; let newBuffer = new ArrayBuffer(newLength); let newBytes = new Uint8Array(newBuffer); - newBytes.set(this.#bytes, 0); - this.#buffer = newBuffer; - this.#bytes = newBytes; + newBytes.set(this.bytes, 0); + this.buffer = newBuffer; + this.bytes = newBytes; } write(data) { - this.#ensureSize(this.#pos + data.byteLength); - this.#bytes.set(data, this.#pos); - this.#pos += data.byteLength; - this.#maxPos = Math.max(this.#maxPos, this.#pos); + this.ensureSize(this.pos + data.byteLength); + this.bytes.set(data, this.pos); + this.pos += data.byteLength; + this.maxPos = Math.max(this.maxPos, this.pos); } seek(newPos) { - this.#pos = newPos; + this.pos = newPos; } getPos() { - return this.#pos; + return this.pos; } flush() { } finalize() { - this.#ensureSize(this.#pos); - this.#target.buffer = this.#buffer.slice(0, Math.max(this.#maxPos, this.#pos)); + this.ensureSize(this.pos); + this.target.buffer = this.buffer.slice(0, Math.max(this.maxPos, this.pos)); } getSlice(start, end) { - return this.#bytes.slice(start, end); + return this.bytes.slice(start, end); } }; var StreamTargetWriter = class extends Writer2 { - #pos = 0; - #target; - #sections = []; - #lastFlushEnd = 0; constructor(target) { super(); - this.#target = target; + this.pos = 0; + this.sections = []; + this.lastFlushEnd = 0; + this.target = target; } write(data) { - this.#sections.push({ + this.sections.push({ data: data.slice(), - start: this.#pos + start: this.pos }); - this.#pos += data.byteLength; + this.pos += data.byteLength; } seek(newPos) { - this.#pos = newPos; + this.pos = newPos; } getPos() { - return this.#pos; + return this.pos; } flush() { - if (this.#sections.length === 0) return; + if (this.sections.length === 0) return; let chunks = []; - let sorted = [...this.#sections].sort((a, b) => a.start - b.start); + let sorted = [...this.sections].sort((a, b) => a.start - b.start); chunks.push({ start: sorted[0].start, size: sorted[0].data.byteLength @@ -1263,18 +1260,18 @@ var StreamTargetWriter = class extends Writer2 { } for (let chunk of chunks) { chunk.data = new Uint8Array(chunk.size); - for (let section of this.#sections) { + for (let section of this.sections) { if (chunk.start <= section.start && section.start < chunk.start + chunk.size) { chunk.data.set(section.data, section.start - chunk.start); } } - if (this.ensureMonotonicity && chunk.start !== this.#lastFlushEnd) { + if (this.ensureMonotonicity && chunk.start !== this.lastFlushEnd) { throw new Error("Internal error: Monotonicity violation."); } - this.#target.options.onData?.(chunk.data, chunk.start); - this.#lastFlushEnd = chunk.start + chunk.data.byteLength; + this.target.options.onData?.(chunk.data, chunk.start); + this.lastFlushEnd = chunk.start + chunk.data.byteLength; } - this.#sections.length = 0; + this.sections.length = 0; } finalize() { } @@ -1282,60 +1279,58 @@ var StreamTargetWriter = class extends Writer2 { var DEFAULT_CHUNK_SIZE = 2 ** 24; var MAX_CHUNKS_AT_ONCE = 2; var ChunkedStreamTargetWriter = class extends Writer2 { - #pos = 0; - #target; - #chunkSize; - /** - * The data is divided up into fixed-size chunks, whose contents are first filled in RAM and then flushed out. - * A chunk is flushed if all of its contents have been written. - */ - #chunks = []; - #lastFlushEnd = 0; constructor(target) { super(); - this.#target = target; - this.#chunkSize = target.options?.chunkSize ?? DEFAULT_CHUNK_SIZE; - if (!Number.isInteger(this.#chunkSize) || this.#chunkSize < 2 ** 10) { + this.pos = 0; + /** + * The data is divided up into fixed-size chunks, whose contents are first filled in RAM and then flushed out. + * A chunk is flushed if all of its contents have been written. + */ + this.chunks = []; + this.lastFlushEnd = 0; + this.target = target; + this.chunkSize = target.options?.chunkSize ?? DEFAULT_CHUNK_SIZE; + if (!Number.isInteger(this.chunkSize) || this.chunkSize < 2 ** 10) { throw new Error("Invalid StreamTarget options: chunkSize must be an integer not smaller than 1024."); } } write(data) { - this.#writeDataIntoChunks(data, this.#pos); - this.#flushChunks(); - this.#pos += data.byteLength; + this.writeDataIntoChunks(data, this.pos); + this.flushChunks(); + this.pos += data.byteLength; } seek(newPos) { - this.#pos = newPos; + this.pos = newPos; } getPos() { - return this.#pos; + return this.pos; } - #writeDataIntoChunks(data, position) { - let chunkIndex = this.#chunks.findIndex((x) => x.start <= position && position < x.start + this.#chunkSize); - if (chunkIndex === -1) chunkIndex = this.#createChunk(position); - let chunk = this.#chunks[chunkIndex]; + writeDataIntoChunks(data, position) { + let chunkIndex = this.chunks.findIndex((x) => x.start <= position && position < x.start + this.chunkSize); + if (chunkIndex === -1) chunkIndex = this.createChunk(position); + let chunk = this.chunks[chunkIndex]; let relativePosition = position - chunk.start; - let toWrite = data.subarray(0, Math.min(this.#chunkSize - relativePosition, data.byteLength)); + let toWrite = data.subarray(0, Math.min(this.chunkSize - relativePosition, data.byteLength)); chunk.data.set(toWrite, relativePosition); let section = { start: relativePosition, end: relativePosition + toWrite.byteLength }; - this.#insertSectionIntoChunk(chunk, section); - if (chunk.written[0].start === 0 && chunk.written[0].end === this.#chunkSize) { + this.insertSectionIntoChunk(chunk, section); + if (chunk.written[0].start === 0 && chunk.written[0].end === this.chunkSize) { chunk.shouldFlush = true; } - if (this.#chunks.length > MAX_CHUNKS_AT_ONCE) { - for (let i = 0; i < this.#chunks.length - 1; i++) { - this.#chunks[i].shouldFlush = true; + if (this.chunks.length > MAX_CHUNKS_AT_ONCE) { + for (let i = 0; i < this.chunks.length - 1; i++) { + this.chunks[i].shouldFlush = true; } - this.#flushChunks(); + this.flushChunks(); } if (toWrite.byteLength < data.byteLength) { - this.#writeDataIntoChunks(data.subarray(toWrite.byteLength), position + toWrite.byteLength); + this.writeDataIntoChunks(data.subarray(toWrite.byteLength), position + toWrite.byteLength); } } - #insertSectionIntoChunk(chunk, section) { + insertSectionIntoChunk(chunk, section) { let low = 0; let high = chunk.written.length - 1; let index = -1; @@ -1355,39 +1350,39 @@ var ChunkedStreamTargetWriter = class extends Writer2 { chunk.written.splice(index + 1, 1); } } - #createChunk(includesPosition) { - let start = Math.floor(includesPosition / this.#chunkSize) * this.#chunkSize; + createChunk(includesPosition) { + let start = Math.floor(includesPosition / this.chunkSize) * this.chunkSize; let chunk = { start, - data: new Uint8Array(this.#chunkSize), + data: new Uint8Array(this.chunkSize), written: [], shouldFlush: false }; - this.#chunks.push(chunk); - this.#chunks.sort((a, b) => a.start - b.start); - return this.#chunks.indexOf(chunk); + this.chunks.push(chunk); + this.chunks.sort((a, b) => a.start - b.start); + return this.chunks.indexOf(chunk); } - #flushChunks(force = false) { - for (let i = 0; i < this.#chunks.length; i++) { - let chunk = this.#chunks[i]; + flushChunks(force = false) { + for (let i = 0; i < this.chunks.length; i++) { + let chunk = this.chunks[i]; if (!chunk.shouldFlush && !force) continue; for (let section of chunk.written) { - if (this.ensureMonotonicity && chunk.start + section.start !== this.#lastFlushEnd) { + if (this.ensureMonotonicity && chunk.start + section.start !== this.lastFlushEnd) { throw new Error("Internal error: Monotonicity violation."); } - this.#target.options.onData?.( + this.target.options.onData?.( chunk.data.subarray(section.start, section.end), chunk.start + section.start ); - this.#lastFlushEnd = chunk.start + section.end; + this.lastFlushEnd = chunk.start + section.end; } - this.#chunks.splice(i--, 1); + this.chunks.splice(i--, 1); } } flush() { } finalize() { - this.#flushChunks(true); + this.flushChunks(true); } }; var FileSystemWritableFileStreamTargetWriter = class extends ChunkedStreamTargetWriter { @@ -1611,54 +1606,40 @@ var IsobmffMuxer = class extends Muxer { constructor(output, format) { super(output); this.timestampsMustStartAtZero = true; - this.#auxTarget = new ArrayBufferTarget(); - this.#auxWriter = this.#auxTarget._createWriter(); - this.#auxBoxWriter = new IsobmffBoxWriter(this.#auxWriter); - this.#ftypSize = null; - this.#mdat = null; - this.#trackDatas = []; - this.#creationTime = Math.floor(Date.now() / 1e3) + TIMESTAMP_OFFSET; - this.#finalizedChunks = []; - this.#nextFragmentNumber = 1; - this.#writer = output._writer; - this.#boxWriter = new IsobmffBoxWriter(this.#writer); - this.#format = format; - this.#fastStart = format.options.fastStart ?? (this.#writer instanceof ArrayBufferTargetWriter ? "in-memory" : false); - if (this.#fastStart === "in-memory" || this.#fastStart === "fragmented") { - this.#writer.ensureMonotonicity = true; + this.auxTarget = new ArrayBufferTarget(); + this.auxWriter = this.auxTarget._createWriter(); + this.auxBoxWriter = new IsobmffBoxWriter(this.auxWriter); + this.ftypSize = null; + this.mdat = null; + this.trackDatas = []; + this.creationTime = Math.floor(Date.now() / 1e3) + TIMESTAMP_OFFSET; + this.finalizedChunks = []; + this.nextFragmentNumber = 1; + this.writer = output._writer; + this.boxWriter = new IsobmffBoxWriter(this.writer); + this.fastStart = format.options.fastStart ?? (this.writer instanceof ArrayBufferTargetWriter ? "in-memory" : false); + if (this.fastStart === "in-memory" || this.fastStart === "fragmented") { + this.writer.ensureMonotonicity = true; } } - #writer; - #boxWriter; - #format; - #fastStart; - #auxTarget; - #auxWriter; - #auxBoxWriter; - #ftypSize; - #mdat; - #trackDatas; - #creationTime; - #finalizedChunks; - #nextFragmentNumber; start() { const holdsAvc = this.output._tracks.some((x) => x.type === "video" && x.source._codec === "avc"); - this.#boxWriter.writeBox(ftyp({ + this.boxWriter.writeBox(ftyp({ holdsAvc, - fragmented: this.#fastStart === "fragmented" + fragmented: this.fastStart === "fragmented" })); - this.#ftypSize = this.#writer.getPos(); - if (this.#fastStart === "in-memory") { - this.#mdat = mdat(false); - } else if (this.#fastStart === "fragmented") { + this.ftypSize = this.writer.getPos(); + if (this.fastStart === "in-memory") { + this.mdat = mdat(false); + } else if (this.fastStart === "fragmented") { } else { - this.#mdat = mdat(true); - this.#boxWriter.writeBox(this.#mdat); + this.mdat = mdat(true); + this.boxWriter.writeBox(this.mdat); } - this.#writer.flush(); + this.writer.flush(); } - #getVideoTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getVideoTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -1687,12 +1668,12 @@ var IsobmffMuxer = class extends Muxer { currentChunk: null, compactlyCodedChunkTable: [] }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getAudioTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getAudioTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -1719,12 +1700,12 @@ var IsobmffMuxer = class extends Muxer { currentChunk: null, compactlyCodedChunkTable: [] }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getSubtitleTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getSubtitleTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -1754,37 +1735,37 @@ var IsobmffMuxer = class extends Muxer { nextSourceId: 0, cueToSourceId: /* @__PURE__ */ new WeakMap() }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); this.validateAndNormalizeTimestamp(track, 0, true); return newTrackData; } addEncodedVideoChunk(track, chunk, meta) { - const trackData = this.#getVideoTrackData(track, meta); + const trackData = this.getVideoTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === "key"); - let sample = this.#createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); - this.#registerSample(trackData, sample); + let sample = this.createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + this.registerSample(trackData, sample); } addEncodedAudioChunk(track, chunk, meta) { - const trackData = this.#getAudioTrackData(track, meta); + const trackData = this.getAudioTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === "key"); - let sample = this.#createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); - this.#registerSample(trackData, sample); + let sample = this.createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + this.registerSample(trackData, sample); } addSubtitleCue(track, cue, meta) { - const trackData = this.#getSubtitleTrackData(track, meta); + const trackData = this.getSubtitleTrackData(track, meta); this.validateAndNormalizeTimestamp(trackData.track, 1e6 * cue.timestamp, true); if (track.source._codec === "webvtt") { trackData.cueQueue.push(cue); - this.#processWebVTTCues(trackData, cue.timestamp); + this.processWebVTTCues(trackData, cue.timestamp); } else { } } - #processWebVTTCues(trackData, until) { + processWebVTTCues(trackData, until) { while (trackData.cueQueue.length > 0) { let timestamps = /* @__PURE__ */ new Set([]); for (let cue of trackData.cueQueue) { @@ -1800,15 +1781,15 @@ var IsobmffMuxer = class extends Muxer { break; } if (trackData.lastCueEndTimestamp < sampleStart) { - this.#auxWriter.seek(0); + this.auxWriter.seek(0); let box2 = vtte(); - this.#auxBoxWriter.writeBox(box2); - let body2 = this.#auxWriter.getSlice(0, this.#auxWriter.getPos()); - let sample2 = this.#createSampleForTrack(trackData, body2, trackData.lastCueEndTimestamp, sampleStart - trackData.lastCueEndTimestamp, "key"); - this.#registerSample(trackData, sample2); + this.auxBoxWriter.writeBox(box2); + let body2 = this.auxWriter.getSlice(0, this.auxWriter.getPos()); + let sample2 = this.createSampleForTrack(trackData, body2, trackData.lastCueEndTimestamp, sampleStart - trackData.lastCueEndTimestamp, "key"); + this.registerSample(trackData, sample2); trackData.lastCueEndTimestamp = sampleStart; } - this.#auxWriter.seek(0); + this.auxWriter.seek(0); for (let i = 0; i < trackData.cueQueue.length; i++) { let cue = trackData.cueQueue[i]; if (cue.timestamp >= sampleEnd) { @@ -1824,21 +1805,21 @@ var IsobmffMuxer = class extends Muxer { } if (cue.notes) { let box3 = vtta(cue.notes); - this.#auxBoxWriter.writeBox(box3); + this.auxBoxWriter.writeBox(box3); } let box2 = vttc(cue.text, containsTimestamp ? sampleStart : null, cue.identifier ?? null, cue.settings ?? null, sourceId ?? null); - this.#auxBoxWriter.writeBox(box2); + this.auxBoxWriter.writeBox(box2); if (endTimestamp === sampleEnd) { trackData.cueQueue.splice(i--, 1); } } - let body = this.#auxWriter.getSlice(0, this.#auxWriter.getPos()); - let sample = this.#createSampleForTrack(trackData, body, sampleStart, sampleEnd - sampleStart, "key"); - this.#registerSample(trackData, sample); + let body = this.auxWriter.getSlice(0, this.auxWriter.getPos()); + let sample = this.createSampleForTrack(trackData, body, sampleStart, sampleEnd - sampleStart, "key"); + this.registerSample(trackData, sample); trackData.lastCueEndTimestamp = sampleEnd; } } - #createSampleForTrack(trackData, data, timestamp, duration, type) { + createSampleForTrack(trackData, data, timestamp, duration, type) { let sample = { timestamp, decodeTimestamp: timestamp, @@ -1852,7 +1833,7 @@ var IsobmffMuxer = class extends Muxer { }; return sample; } - #processTimestamps(trackData) { + processTimestamps(trackData) { if (trackData.timestampProcessingQueue.length === 0) { return; } @@ -1868,7 +1849,7 @@ var IsobmffMuxer = class extends Muxer { let delta = Math.round(timescaleUnits - trackData.lastTimescaleUnits); trackData.lastTimescaleUnits += delta; trackData.lastSample.timescaleUnitsToNextSample = delta; - if (this.#fastStart !== "fragmented") { + if (this.fastStart !== "fragmented") { let lastTableEntry = last(trackData.timeToSampleTable); assert(lastTableEntry); if (lastTableEntry.sampleCount === 1) { @@ -1907,7 +1888,7 @@ var IsobmffMuxer = class extends Muxer { } } else { trackData.lastTimescaleUnits = 0; - if (this.#fastStart !== "fragmented") { + if (this.fastStart !== "fragmented") { trackData.timeToSampleTable.push({ sampleCount: 1, sampleDelta: durationInTimescale @@ -1922,19 +1903,19 @@ var IsobmffMuxer = class extends Muxer { } trackData.timestampProcessingQueue.length = 0; } - #registerSample(trackData, sample) { - if (this.#fastStart === "fragmented") { + registerSample(trackData, sample) { + if (this.fastStart === "fragmented") { trackData.sampleQueue.push(sample); - this.#interleaveSamples(); + this.interleaveSamples(); } else { - this.#addSampleToTrack(trackData, sample); + this.addSampleToTrack(trackData, sample); } } - #addSampleToTrack(trackData, sample) { + addSampleToTrack(trackData, sample) { if (sample.type === "key") { - this.#processTimestamps(trackData); + this.processTimestamps(trackData); } - if (this.#fastStart !== "fragmented") { + if (this.fastStart !== "fragmented") { trackData.samples.push(sample); } let beginNewChunk = false; @@ -1942,8 +1923,8 @@ var IsobmffMuxer = class extends Muxer { beginNewChunk = true; } else { let currentChunkDuration = sample.timestamp - trackData.currentChunk.startTimestamp; - if (this.#fastStart === "fragmented") { - const keyFrameQueuedEverywhere = this.#trackDatas.every((otherTrackData) => { + if (this.fastStart === "fragmented") { + const keyFrameQueuedEverywhere = this.trackDatas.every((otherTrackData) => { if (trackData === otherTrackData) { return sample.type === "key"; } @@ -1952,7 +1933,7 @@ var IsobmffMuxer = class extends Muxer { }); if (currentChunkDuration >= 1 && keyFrameQueuedEverywhere) { beginNewChunk = true; - this.#finalizeFragment(); + this.finalizeFragment(); } } else { beginNewChunk = currentChunkDuration >= 0.5; @@ -1960,7 +1941,7 @@ var IsobmffMuxer = class extends Muxer { } if (beginNewChunk) { if (trackData.currentChunk) { - this.#finalizeCurrentChunk(trackData); + this.finalizeCurrentChunk(trackData); } trackData.currentChunk = { startTimestamp: sample.timestamp, @@ -1973,11 +1954,11 @@ var IsobmffMuxer = class extends Muxer { trackData.currentChunk.samples.push(sample); trackData.timestampProcessingQueue.push(sample); } - #finalizeCurrentChunk(trackData) { - assert(this.#fastStart !== "fragmented"); + finalizeCurrentChunk(trackData) { + assert(this.fastStart !== "fragmented"); if (!trackData.currentChunk) return; trackData.finalizedChunks.push(trackData.currentChunk); - this.#finalizedChunks.push(trackData.currentChunk); + this.finalizedChunks.push(trackData.currentChunk); if (trackData.compactlyCodedChunkTable.length === 0 || last(trackData.compactlyCodedChunkTable).samplesPerChunk !== trackData.currentChunk.samples.length) { trackData.compactlyCodedChunkTable.push({ firstChunk: trackData.finalizedChunks.length, @@ -1985,22 +1966,22 @@ var IsobmffMuxer = class extends Muxer { samplesPerChunk: trackData.currentChunk.samples.length }); } - if (this.#fastStart === "in-memory") { + if (this.fastStart === "in-memory") { trackData.currentChunk.offset = 0; return; } - trackData.currentChunk.offset = this.#writer.getPos(); + trackData.currentChunk.offset = this.writer.getPos(); for (let sample of trackData.currentChunk.samples) { assert(sample.data); - this.#writer.write(sample.data); + this.writer.write(sample.data); sample.data = null; } - this.#writer.flush(); + this.writer.flush(); } - #interleaveSamples() { - assert(this.#fastStart === "fragmented"); + interleaveSamples() { + assert(this.fastStart === "fragmented"); for (const track of this.output._tracks) { - if (!track.source._closed && !this.#trackDatas.some((x) => x.track === track)) { + if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) { return; } } @@ -2008,7 +1989,7 @@ var IsobmffMuxer = class extends Muxer { while (true) { let trackWithMinTimestamp = null; let minTimestamp = Infinity; - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { if (trackData.sampleQueue.length === 0 && !trackData.track.source._closed) { break outer; } @@ -2021,99 +2002,99 @@ var IsobmffMuxer = class extends Muxer { break; } let sample = trackWithMinTimestamp.sampleQueue.shift(); - this.#addSampleToTrack(trackWithMinTimestamp, sample); + this.addSampleToTrack(trackWithMinTimestamp, sample); } } - #finalizeFragment(flushWriter = true) { - assert(this.#fastStart === "fragmented"); - let fragmentNumber = this.#nextFragmentNumber++; + finalizeFragment(flushWriter = true) { + assert(this.fastStart === "fragmented"); + let fragmentNumber = this.nextFragmentNumber++; if (fragmentNumber === 1) { - let movieBox = moov(this.#trackDatas, this.#creationTime, true); - this.#boxWriter.writeBox(movieBox); + let movieBox = moov(this.trackDatas, this.creationTime, true); + this.boxWriter.writeBox(movieBox); } - let moofOffset = this.#writer.getPos(); - let moofBox = moof(fragmentNumber, this.#trackDatas); - this.#boxWriter.writeBox(moofBox); + let moofOffset = this.writer.getPos(); + let moofBox = moof(fragmentNumber, this.trackDatas); + this.boxWriter.writeBox(moofBox); { let mdatBox = mdat(false); let totalTrackSampleSize = 0; - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { assert(trackData.currentChunk); for (let sample of trackData.currentChunk.samples) { totalTrackSampleSize += sample.size; } } - let mdatSize = this.#boxWriter.measureBox(mdatBox) + totalTrackSampleSize; + let mdatSize = this.boxWriter.measureBox(mdatBox) + totalTrackSampleSize; if (mdatSize >= 2 ** 32) { mdatBox.largeSize = true; - mdatSize = this.#boxWriter.measureBox(mdatBox) + totalTrackSampleSize; + mdatSize = this.boxWriter.measureBox(mdatBox) + totalTrackSampleSize; } mdatBox.size = mdatSize; - this.#boxWriter.writeBox(mdatBox); + this.boxWriter.writeBox(mdatBox); } - for (let trackData of this.#trackDatas) { - trackData.currentChunk.offset = this.#writer.getPos(); + for (let trackData of this.trackDatas) { + trackData.currentChunk.offset = this.writer.getPos(); trackData.currentChunk.moofOffset = moofOffset; for (let sample of trackData.currentChunk.samples) { - this.#writer.write(sample.data); + this.writer.write(sample.data); sample.data = null; } } - let endPos = this.#writer.getPos(); - this.#writer.seek(this.#boxWriter.offsets.get(moofBox)); - let newMoofBox = moof(fragmentNumber, this.#trackDatas); - this.#boxWriter.writeBox(newMoofBox); - this.#writer.seek(endPos); - for (let trackData of this.#trackDatas) { + let endPos = this.writer.getPos(); + this.writer.seek(this.boxWriter.offsets.get(moofBox)); + let newMoofBox = moof(fragmentNumber, this.trackDatas); + this.boxWriter.writeBox(newMoofBox); + this.writer.seek(endPos); + for (let trackData of this.trackDatas) { trackData.finalizedChunks.push(trackData.currentChunk); - this.#finalizedChunks.push(trackData.currentChunk); + this.finalizedChunks.push(trackData.currentChunk); trackData.currentChunk = null; } if (flushWriter) { - this.#writer.flush(); + this.writer.flush(); } } onTrackClose(track) { if (track.type === "subtitle" && track.source._codec === "webvtt") { - let trackData = this.#trackDatas.find((x) => x.track === track); + let trackData = this.trackDatas.find((x) => x.track === track); if (trackData) { - this.#processWebVTTCues(trackData, Infinity); + this.processWebVTTCues(trackData, Infinity); } } - if (this.#fastStart === "fragmented") { - this.#interleaveSamples(); + if (this.fastStart === "fragmented") { + this.interleaveSamples(); } } /** Finalizes the file, making it ready for use. Must be called after all video and audio chunks have been added. */ finalize() { - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { if (trackData.type === "subtitle" && trackData.track.source._codec === "webvtt") { - this.#processWebVTTCues(trackData, Infinity); + this.processWebVTTCues(trackData, Infinity); } } - if (this.#fastStart === "fragmented") { - for (let trackData of this.#trackDatas) { + if (this.fastStart === "fragmented") { + for (let trackData of this.trackDatas) { for (let sample of trackData.sampleQueue) { - this.#addSampleToTrack(trackData, sample); + this.addSampleToTrack(trackData, sample); } - this.#processTimestamps(trackData); + this.processTimestamps(trackData); } - this.#finalizeFragment(false); + this.finalizeFragment(false); } else { - for (let trackData of this.#trackDatas) { - this.#processTimestamps(trackData); - this.#finalizeCurrentChunk(trackData); + for (let trackData of this.trackDatas) { + this.processTimestamps(trackData); + this.finalizeCurrentChunk(trackData); } } - if (this.#fastStart === "in-memory") { - assert(this.#mdat); + if (this.fastStart === "in-memory") { + assert(this.mdat); let mdatSize; for (let i = 0; i < 2; i++) { - let movieBox2 = moov(this.#trackDatas, this.#creationTime); - let movieBoxSize = this.#boxWriter.measureBox(movieBox2); - mdatSize = this.#boxWriter.measureBox(this.#mdat); - let currentChunkPos = this.#writer.getPos() + movieBoxSize + mdatSize; - for (let chunk of this.#finalizedChunks) { + let movieBox2 = moov(this.trackDatas, this.creationTime); + let movieBoxSize = this.boxWriter.measureBox(movieBox2); + mdatSize = this.boxWriter.measureBox(this.mdat); + let currentChunkPos = this.writer.getPos() + movieBoxSize + mdatSize; + for (let chunk of this.finalizedChunks) { chunk.offset = currentChunkPos; for (let { data } of chunk.samples) { assert(data); @@ -2122,43 +2103,43 @@ var IsobmffMuxer = class extends Muxer { } } if (currentChunkPos < 2 ** 32) break; - if (mdatSize >= 2 ** 32) this.#mdat.largeSize = true; + if (mdatSize >= 2 ** 32) this.mdat.largeSize = true; } - let movieBox = moov(this.#trackDatas, this.#creationTime); - this.#boxWriter.writeBox(movieBox); - this.#mdat.size = mdatSize; - this.#boxWriter.writeBox(this.#mdat); - for (let chunk of this.#finalizedChunks) { + let movieBox = moov(this.trackDatas, this.creationTime); + this.boxWriter.writeBox(movieBox); + this.mdat.size = mdatSize; + this.boxWriter.writeBox(this.mdat); + for (let chunk of this.finalizedChunks) { for (let sample of chunk.samples) { assert(sample.data); - this.#writer.write(sample.data); + this.writer.write(sample.data); sample.data = null; } } - } else if (this.#fastStart === "fragmented") { - let startPos = this.#writer.getPos(); - let mfraBox = mfra(this.#trackDatas); - this.#boxWriter.writeBox(mfraBox); - let mfraBoxSize = this.#writer.getPos() - startPos; - this.#writer.seek(this.#writer.getPos() - 4); - this.#boxWriter.writeU32(mfraBoxSize); + } else if (this.fastStart === "fragmented") { + let startPos = this.writer.getPos(); + let mfraBox = mfra(this.trackDatas); + this.boxWriter.writeBox(mfraBox); + let mfraBoxSize = this.writer.getPos() - startPos; + this.writer.seek(this.writer.getPos() - 4); + this.boxWriter.writeU32(mfraBoxSize); } else { - assert(this.#mdat); - assert(this.#ftypSize !== null); - let mdatPos = this.#boxWriter.offsets.get(this.#mdat); + assert(this.mdat); + assert(this.ftypSize !== null); + let mdatPos = this.boxWriter.offsets.get(this.mdat); assert(mdatPos !== void 0); - let mdatSize = this.#writer.getPos() - mdatPos; - this.#mdat.size = mdatSize; - this.#mdat.largeSize = mdatSize >= 2 ** 32; - this.#boxWriter.patchBox(this.#mdat); - let movieBox = moov(this.#trackDatas, this.#creationTime); - if (typeof this.#fastStart === "object") { - this.#writer.seek(this.#ftypSize); - this.#boxWriter.writeBox(movieBox); - let remainingBytes = mdatPos - this.#writer.getPos(); - this.#boxWriter.writeBox(free(remainingBytes)); + let mdatSize = this.writer.getPos() - mdatPos; + this.mdat.size = mdatSize; + this.mdat.largeSize = mdatSize >= 2 ** 32; + this.boxWriter.patchBox(this.mdat); + let movieBox = moov(this.trackDatas, this.creationTime); + if (typeof this.fastStart === "object") { + this.writer.seek(this.ftypSize); + this.boxWriter.writeBox(movieBox); + let remainingBytes = mdatPos - this.writer.getPos(); + this.boxWriter.writeBox(free(remainingBytes)); } else { - this.#boxWriter.writeBox(movieBox); + this.boxWriter.writeBox(movieBox); } } } @@ -2252,8 +2233,8 @@ var MatroskaMuxer = class extends Muxer { constructor(output, format) { super(output); this.timestampsMustStartAtZero = false; - this.#helper = new Uint8Array(8); - this.#helperView = new DataView(this.#helper.buffer); + this.helper = new Uint8Array(8); + this.helperView = new DataView(this.helper.buffer); /** * Stores the position from the start of the file to where EBML elements have been written. This is used to * rewrite/edit elements that were already added before, and to measure sizes of things. @@ -2261,176 +2242,161 @@ var MatroskaMuxer = class extends Muxer { this.offsets = /* @__PURE__ */ new WeakMap(); /** Same as offsets, but stores position where the element's data starts (after ID and size fields). */ this.dataOffsets = /* @__PURE__ */ new WeakMap(); - this.#trackDatas = []; - this.#segment = null; - this.#segmentInfo = null; - this.#seekHead = null; - this.#tracksElement = null; - this.#segmentDuration = null; - this.#cues = null; - this.#currentCluster = null; - this.#currentClusterMsTimestamp = null; - this.#trackDatasInCurrentCluster = /* @__PURE__ */ new Set(); - this.#duration = 0; - this.#writer = output._writer; - this.#format = format; - if (this.#format.options.streamable) { - this.#writer.ensureMonotonicity = true; + this.trackDatas = []; + this.segment = null; + this.segmentInfo = null; + this.seekHead = null; + this.tracksElement = null; + this.segmentDuration = null; + this.cues = null; + this.currentCluster = null; + this.currentClusterMsTimestamp = null; + this.trackDatasInCurrentCluster = /* @__PURE__ */ new Set(); + this.duration = 0; + this.writer = output._writer; + this.format = format; + if (this.format.options.streamable) { + this.writer.ensureMonotonicity = true; } } - #writer; - #format; - #helper; - #helperView; - #trackDatas; - #segment; - #segmentInfo; - #seekHead; - #tracksElement; - #segmentDuration; - #cues; - #currentCluster; - #currentClusterMsTimestamp; - #trackDatasInCurrentCluster; - #duration; - #writeByte(value) { - this.#helperView.setUint8(0, value); - this.#writer.write(this.#helper.subarray(0, 1)); + 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)); + 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); + writeFloat64(value) { + this.helperView.setFloat64(0, value, false); + this.writer.write(this.helper); } - #writeUnsignedInt(value, width = measureUnsignedInt(value)) { + writeUnsignedInt(value, width = measureUnsignedInt(value)) { let pos = 0; switch (width) { case 6: - this.#helperView.setUint8(pos++, value / 2 ** 40 | 0); + this.helperView.setUint8(pos++, value / 2 ** 40 | 0); case 5: - this.#helperView.setUint8(pos++, value / 2 ** 32 | 0); + this.helperView.setUint8(pos++, value / 2 ** 32 | 0); case 4: - this.#helperView.setUint8(pos++, value >> 24); + this.helperView.setUint8(pos++, value >> 24); case 3: - this.#helperView.setUint8(pos++, value >> 16); + this.helperView.setUint8(pos++, value >> 16); case 2: - this.#helperView.setUint8(pos++, value >> 8); + this.helperView.setUint8(pos++, value >> 8); case 1: - this.#helperView.setUint8(pos++, value); + this.helperView.setUint8(pos++, value); break; default: throw new Error("Bad UINT size " + width); } - this.#writer.write(this.#helper.subarray(0, pos)); + this.writer.write(this.helper.subarray(0, pos)); } - #writeSignedInt(value, width = measureSignedInt(value)) { + writeSignedInt(value, width = measureSignedInt(value)) { if (value < 0) { value += 2 ** (width * 8); } - this.#writeUnsignedInt(value, width); + this.writeUnsignedInt(value, width); } writeEBMLVarInt(value, width = measureEBMLVarInt(value)) { let pos = 0; switch (width) { case 1: - this.#helperView.setUint8(pos++, 1 << 7 | value); + this.helperView.setUint8(pos++, 1 << 7 | value); break; case 2: - this.#helperView.setUint8(pos++, 1 << 6 | value >> 8); - this.#helperView.setUint8(pos++, value); + 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); + 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); + 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); + 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); + 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 VINT size " + width); } - this.#writer.write(this.#helper.subarray(0, pos)); + this.writer.write(this.helper.subarray(0, pos)); } // Assumes the string is ASCII - #writeString(str) { - this.#writer.write(new Uint8Array(str.split("").map((x) => x.charCodeAt(0)))); + writeString(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); + this.writer.write(data); } else if (Array.isArray(data)) { for (let elem of data) { this.writeEBML(elem); } } else { - this.offsets.set(data, this.#writer.getPos()); - this.#writeUnsignedInt(data.id); + this.offsets.set(data, this.writer.getPos()); + this.writeUnsignedInt(data.id); if (Array.isArray(data.data)) { - let sizePos = this.#writer.getPos(); + let sizePos = this.writer.getPos(); let sizeSize = data.size === -1 ? 1 : data.size ?? 4; if (data.size === -1) { - this.#writeByte(255); + this.writeByte(255); } else { - this.#writer.seek(this.#writer.getPos() + sizeSize); + this.writer.seek(this.writer.getPos() + sizeSize); } - let startPos = this.#writer.getPos(); + let startPos = this.writer.getPos(); this.dataOffsets.set(data, startPos); this.writeEBML(data.data); if (data.size !== -1) { - let size = this.#writer.getPos() - startPos; - let endPos = this.#writer.getPos(); - this.#writer.seek(sizePos); + let size = this.writer.getPos() - startPos; + let endPos = this.writer.getPos(); + this.writer.seek(sizePos); this.writeEBMLVarInt(size, sizeSize); - this.#writer.seek(endPos); + this.writer.seek(endPos); } } else if (typeof data.data === "number") { let size = data.size ?? measureUnsignedInt(data.data); this.writeEBMLVarInt(size); - this.#writeUnsignedInt(data.data, size); + this.writeUnsignedInt(data.data, size); } else if (typeof data.data === "string") { this.writeEBMLVarInt(data.data.length); - this.#writeString(data.data); + this.writeString(data.data); } else if (data.data instanceof Uint8Array) { this.writeEBMLVarInt(data.data.byteLength, data.size); - this.#writer.write(data.data); + this.writer.write(data.data); } else if (data.data instanceof EBMLFloat32) { this.writeEBMLVarInt(4); - this.#writeFloat32(data.data.value); + this.writeFloat32(data.data.value); } else if (data.data instanceof EBMLFloat64) { this.writeEBMLVarInt(8); - this.#writeFloat64(data.data.value); + this.writeFloat64(data.data.value); } else if (data.data instanceof EBMLSignedInt) { let size = data.size ?? measureSignedInt(data.data.value); this.writeEBMLVarInt(size); - this.#writeSignedInt(data.data.value, size); + this.writeSignedInt(data.data.value, size); } } } beforeTrackAdd(track) { - if (!(this.#format instanceof WebMOutputFormat)) { + if (!(this.format instanceof WebMOutputFormat)) { return; } if (track.type === "video") { @@ -2450,21 +2416,21 @@ var MatroskaMuxer = class extends Muxer { } } start() { - this.#writeEBMLHeader(); - if (!this.#format.options.streamable) { - this.#createSeekHead(); + this.writeEBMLHeader(); + if (!this.format.options.streamable) { + this.createSeekHead(); } - this.#createSegmentInfo(); - this.#createCues(); - this.#writer.flush(); + this.createSegmentInfo(); + this.createCues(); + this.writer.flush(); } - #writeEBMLHeader() { + writeEBMLHeader() { let ebmlHeader = { id: 440786851 /* EBML */, data: [ { id: 17030 /* EBMLVersion */, data: 1 }, { id: 17143 /* EBMLReadVersion */, data: 1 }, { id: 17138 /* EBMLMaxIDLength */, data: 4 }, { id: 17139 /* EBMLMaxSizeLength */, data: 8 }, - { id: 17026 /* DocType */, data: this.#format instanceof WebMOutputFormat ? "webm" : "matroska" }, + { id: 17026 /* DocType */, data: this.format instanceof WebMOutputFormat ? "webm" : "matroska" }, { id: 17031 /* DocTypeVersion */, data: 2 }, { id: 17029 /* DocTypeReadVersion */, data: 2 } ] }; @@ -2474,7 +2440,7 @@ var MatroskaMuxer = class extends Muxer { * 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. */ - #createSeekHead() { + createSeekHead() { const kaxCues = new Uint8Array([28, 83, 187, 107]); const kaxInfo = new Uint8Array([21, 73, 169, 102]); const kaxTracks = new Uint8Array([22, 84, 174, 107]); @@ -2492,23 +2458,23 @@ var MatroskaMuxer = class extends Muxer { { id: 21420 /* SeekPosition */, size: 5, data: 0 } ] } ] }; - this.#seekHead = seekHead; + this.seekHead = seekHead; } - #createSegmentInfo() { + createSegmentInfo() { let segmentDuration = { id: 17545 /* Duration */, data: new EBMLFloat64(0) }; - this.#segmentDuration = segmentDuration; + this.segmentDuration = segmentDuration; let segmentInfo = { id: 357149030 /* Info */, data: [ { id: 2807729 /* TimestampScale */, data: 1e6 }, { id: 19840 /* MuxingApp */, data: APP_NAME }, { id: 22337 /* WritingApp */, data: APP_NAME }, - !this.#format.options.streamable ? segmentDuration : null + !this.format.options.streamable ? segmentDuration : null ] }; - this.#segmentInfo = segmentInfo; + this.segmentInfo = segmentInfo; } - #createTracks() { + createTracks() { let tracksElement = { id: 374648427 /* Tracks */, data: [] }; - this.#tracksElement = tracksElement; - for (let trackData of this.#trackDatas) { + this.tracksElement = tracksElement; + for (let trackData of this.trackDatas) { tracksElement.data.push({ id: 174 /* TrackEntry */, data: [ { id: 215 /* TrackNumber */, data: trackData.track.id }, { id: 29637 /* TrackUID */, data: trackData.track.id }, @@ -2551,28 +2517,28 @@ var MatroskaMuxer = class extends Muxer { ] }); } } - #createSegment() { + createSegment() { let segment = { id: 408125543 /* Segment */, - size: this.#format.options.streamable ? -1 : SEGMENT_SIZE_BYTES, + size: this.format.options.streamable ? -1 : SEGMENT_SIZE_BYTES, data: [ - !this.#format.options.streamable ? this.#seekHead : null, - this.#segmentInfo, - this.#tracksElement + !this.format.options.streamable ? this.seekHead : null, + this.segmentInfo, + this.tracksElement ] }; - this.#segment = segment; + this.segment = segment; this.writeEBML(segment); } - #createCues() { - this.#cues = { id: 475249515 /* Cues */, data: [] }; + createCues() { + this.cues = { id: 475249515 /* Cues */, data: [] }; } - get #segmentDataOffset() { - assert(this.#segment); - return this.dataOffsets.get(this.#segment); + get segmentDataOffset() { + assert(this.segment); + return this.dataOffsets.get(this.segment); } - #getVideoTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getVideoTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -2592,12 +2558,12 @@ var MatroskaMuxer = class extends Muxer { chunkQueue: [], lastWrittenMsTimestamp: null }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getAudioTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getAudioTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -2615,12 +2581,12 @@ var MatroskaMuxer = class extends Muxer { chunkQueue: [], lastWrittenMsTimestamp: null }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getSubtitleTrackData(track, meta) { - const existingTrackData = this.#trackDatas.find((x) => x.track === track); + getSubtitleTrackData(track, meta) { + const existingTrackData = this.trackDatas.find((x) => x.track === track); if (existingTrackData) { return existingTrackData; } @@ -2636,31 +2602,31 @@ var MatroskaMuxer = class extends Muxer { chunkQueue: [], lastWrittenMsTimestamp: null }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } addEncodedVideoChunk(track, chunk, meta) { - const trackData = this.#getVideoTrackData(track, meta); + const trackData = this.getVideoTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === "key"); - let videoChunk = this.#createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); - if (track.source._codec === "vp9") this.#fixVP9ColorSpace(trackData, videoChunk); + let videoChunk = this.createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + if (track.source._codec === "vp9") this.fixVP9ColorSpace(trackData, videoChunk); trackData.chunkQueue.push(videoChunk); - this.#interleaveChunks(); + this.interleaveChunks(); } addEncodedAudioChunk(track, chunk, meta) { - const trackData = this.#getAudioTrackData(track, meta); + const trackData = this.getAudioTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === "key"); - let audioChunk = this.#createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + let audioChunk = this.createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); trackData.chunkQueue.push(audioChunk); - this.#interleaveChunks(); + this.interleaveChunks(); } addSubtitleCue(track, cue, meta) { - const trackData = this.#getSubtitleTrackData(track, meta); + const trackData = this.getSubtitleTrackData(track, meta); const timestamp = this.validateAndNormalizeTimestamp(trackData.track, 1e6 * cue.timestamp, true); let bodyText = cue.text; const timestampMs = Math.floor(timestamp * 1e3); @@ -2674,13 +2640,13 @@ var MatroskaMuxer = class extends Muxer { const additions = `${cue.settings ?? ""} ${cue.identifier ?? ""} ${cue.notes ?? ""}`; - let subtitleChunk = this.#createInternalChunk(body, timestamp, cue.duration, "key", additions.trim() ? textEncoder.encode(additions) : null); + let subtitleChunk = this.createInternalChunk(body, timestamp, cue.duration, "key", additions.trim() ? textEncoder.encode(additions) : null); trackData.chunkQueue.push(subtitleChunk); - this.#interleaveChunks(); + this.interleaveChunks(); } - #interleaveChunks() { + interleaveChunks() { for (const track of this.output._tracks) { - if (!track.source._closed && !this.#trackDatas.some((x) => x.track === track)) { + if (!track.source._closed && !this.trackDatas.some((x) => x.track === track)) { return; } } @@ -2688,7 +2654,7 @@ ${cue.notes ?? ""}`; while (true) { let trackWithMinTimestamp = null; let minTimestamp = Infinity; - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { if (trackData.chunkQueue.length === 0 && !trackData.track.source._closed) { break outer; } @@ -2701,14 +2667,14 @@ ${cue.notes ?? ""}`; break; } let chunk = trackWithMinTimestamp.chunkQueue.shift(); - this.#writeBlock(trackWithMinTimestamp, chunk); + this.writeBlock(trackWithMinTimestamp, chunk); } - this.#writer.flush(); + 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. */ // http://downloads.webmproject.org/docs/vp9/vp9-bitstream_superframe-and-uncompressed-header_v1.0.pdf - #fixVP9ColorSpace(trackData, chunk) { + fixVP9ColorSpace(trackData, chunk) { if (chunk.type !== "key") return; if (!trackData.info.decoderConfig.colorSpace || !trackData.info.decoderConfig.colorSpace.matrix) return; let i = 0; @@ -2737,7 +2703,7 @@ ${cue.notes ?? ""}`; writeBits(chunk.data, i + 0, i + 3, colorSpaceID); } /** Converts a read-only external chunk into an internal one for easier use. */ - #createInternalChunk(data, timestamp, duration, type, additions = null) { + createInternalChunk(data, timestamp, duration, type, additions = null) { let internalChunk = { data, type, @@ -2748,13 +2714,13 @@ ${cue.notes ?? ""}`; return internalChunk; } /** Writes a block containing media data to the file. */ - #writeBlock(trackData, chunk) { - if (!this.#segment) { - this.#createTracks(); - this.#createSegment(); + writeBlock(trackData, chunk) { + if (!this.segment) { + this.createTracks(); + this.createSegment(); } let msTimestamp = Math.floor(1e3 * chunk.timestamp); - const keyFrameQueuedEverywhere = this.#trackDatas.every((otherTrackData) => { + const keyFrameQueuedEverywhere = this.trackDatas.every((otherTrackData) => { if (otherTrackData.track.source._closed) { return true; } @@ -2764,10 +2730,10 @@ ${cue.notes ?? ""}`; const firstQueuedSample = otherTrackData.chunkQueue[0]; return firstQueuedSample && firstQueuedSample.type === "key"; }); - if (!this.#currentCluster || keyFrameQueuedEverywhere && msTimestamp - this.#currentClusterMsTimestamp >= 1e3) { - this.#createNewCluster(msTimestamp); + if (!this.currentCluster || keyFrameQueuedEverywhere && msTimestamp - this.currentClusterMsTimestamp >= 1e3) { + this.createNewCluster(msTimestamp); } - let relativeTimestamp = msTimestamp - this.#currentClusterMsTimestamp; + let relativeTimestamp = msTimestamp - this.currentClusterMsTimestamp; if (relativeTimestamp < 0) { return; } @@ -2806,39 +2772,39 @@ ${cue.notes ?? ""}`; ] }; this.writeEBML(blockGroup); } - this.#duration = Math.max(this.#duration, msTimestamp + msDuration); + this.duration = Math.max(this.duration, msTimestamp + msDuration); trackData.lastWrittenMsTimestamp = msTimestamp; - this.#trackDatasInCurrentCluster.add(trackData); + this.trackDatasInCurrentCluster.add(trackData); } /** Creates a new Cluster element to contain media chunks. */ - #createNewCluster(msTimestamp) { - if (this.#currentCluster && !this.#format.options.streamable) { - this.#finalizeCurrentCluster(); + createNewCluster(msTimestamp) { + if (this.currentCluster && !this.format.options.streamable) { + this.finalizeCurrentCluster(); } - this.#currentCluster = { + this.currentCluster = { id: 524531317 /* Cluster */, - size: this.#format.options.streamable ? -1 : CLUSTER_SIZE_BYTES, + size: this.format.options.streamable ? -1 : CLUSTER_SIZE_BYTES, data: [ { id: 231 /* Timestamp */, data: msTimestamp } ] }; - this.writeEBML(this.#currentCluster); - this.#currentClusterMsTimestamp = msTimestamp; - this.#trackDatasInCurrentCluster.clear(); + this.writeEBML(this.currentCluster); + this.currentClusterMsTimestamp = msTimestamp; + this.trackDatasInCurrentCluster.clear(); } - #finalizeCurrentCluster() { - assert(this.#currentCluster); - let clusterSize = this.#writer.getPos() - this.dataOffsets.get(this.#currentCluster); - let endPos = this.#writer.getPos(); - this.#writer.seek(this.offsets.get(this.#currentCluster) + 4); + finalizeCurrentCluster() { + assert(this.currentCluster); + let clusterSize = this.writer.getPos() - this.dataOffsets.get(this.currentCluster); + let endPos = this.writer.getPos(); + this.writer.seek(this.offsets.get(this.currentCluster) + 4); this.writeEBMLVarInt(clusterSize, CLUSTER_SIZE_BYTES); - this.#writer.seek(endPos); - let clusterOffsetFromSegment = this.offsets.get(this.#currentCluster) - this.#segmentDataOffset; - assert(this.#cues); - this.#cues.data.push({ id: 187 /* CuePoint */, data: [ - { id: 179 /* CueTime */, data: this.#currentClusterMsTimestamp }, + this.writer.seek(endPos); + let clusterOffsetFromSegment = this.offsets.get(this.currentCluster) - this.segmentDataOffset; + assert(this.cues); + this.cues.data.push({ id: 187 /* CuePoint */, data: [ + { id: 179 /* CueTime */, data: this.currentClusterMsTimestamp }, // We only write out cues for tracks that have at least one chunk in this cluster - ...[...this.#trackDatasInCurrentCluster].map((trackData) => { + ...[...this.trackDatasInCurrentCluster].map((trackData) => { return { id: 183 /* CueTrackPositions */, data: [ { id: 247 /* CueTrack */, data: trackData.track.id }, { id: 241 /* CueClusterPosition */, data: clusterOffsetFromSegment } @@ -2847,38 +2813,38 @@ ${cue.notes ?? ""}`; ] }); } onTrackClose() { - this.#interleaveChunks(); + this.interleaveChunks(); } /** Finalizes the file, making it ready for use. Must be called after all media chunks have been added. */ finalize() { - if (!this.#segment) { - this.#createTracks(); - this.#createSegment(); + if (!this.segment) { + this.createTracks(); + this.createSegment(); } - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { while (trackData.chunkQueue.length > 0) { - this.#writeBlock(trackData, trackData.chunkQueue.shift()); + this.writeBlock(trackData, trackData.chunkQueue.shift()); } } - if (!this.#format.options.streamable && this.#currentCluster) { - this.#finalizeCurrentCluster(); + if (!this.format.options.streamable && this.currentCluster) { + this.finalizeCurrentCluster(); } - assert(this.#cues); - this.writeEBML(this.#cues); - if (!this.#format.options.streamable) { - let endPos = this.#writer.getPos(); - let segmentSize = this.#writer.getPos() - this.#segmentDataOffset; - this.#writer.seek(this.offsets.get(this.#segment) + 4); + assert(this.cues); + this.writeEBML(this.cues); + if (!this.format.options.streamable) { + let endPos = this.writer.getPos(); + let segmentSize = this.writer.getPos() - this.segmentDataOffset; + this.writer.seek(this.offsets.get(this.segment) + 4); this.writeEBMLVarInt(segmentSize, SEGMENT_SIZE_BYTES); - this.#segmentDuration.data = new EBMLFloat64(this.#duration); - this.#writer.seek(this.offsets.get(this.#segmentDuration)); - this.writeEBML(this.#segmentDuration); - this.#seekHead.data[0].data[1].data = this.offsets.get(this.#cues) - this.#segmentDataOffset; - this.#seekHead.data[1].data[1].data = this.offsets.get(this.#segmentInfo) - this.#segmentDataOffset; - this.#seekHead.data[2].data[1].data = this.offsets.get(this.#tracksElement) - this.#segmentDataOffset; - this.#writer.seek(this.offsets.get(this.#seekHead)); - this.writeEBML(this.#seekHead); - this.#writer.seek(endPos); + this.segmentDuration.data = new EBMLFloat64(this.duration); + this.writer.seek(this.offsets.get(this.segmentDuration)); + this.writeEBML(this.segmentDuration); + this.seekHead.data[0].data[1].data = this.offsets.get(this.cues) - this.segmentDataOffset; + this.seekHead.data[1].data[1].data = this.offsets.get(this.segmentInfo) - this.segmentDataOffset; + this.seekHead.data[2].data[1].data = this.offsets.get(this.tracksElement) - this.segmentDataOffset; + this.writer.seek(this.offsets.get(this.seekHead)); + this.writeEBML(this.seekHead); + this.writer.seek(endPos); } } }; diff --git a/src/isobmff/isobmff-muxer.ts b/src/isobmff/isobmff-muxer.ts index 3161d93..a3b3247 100644 --- a/src/isobmff/isobmff-muxer.ts +++ b/src/isobmff/isobmff-muxer.ts @@ -86,38 +86,36 @@ export const intoTimescale = (timeInSeconds: number, timescale: number, round = export class IsobmffMuxer extends Muxer { override timestampsMustStartAtZero = true; - #writer: Writer; - #boxWriter: IsobmffBoxWriter; - #format: Mp4OutputFormat; - #fastStart: NonNullable; + private writer: Writer; + private boxWriter: IsobmffBoxWriter; + private fastStart: NonNullable; - #auxTarget = new ArrayBufferTarget(); - #auxWriter = this.#auxTarget._createWriter(); - #auxBoxWriter = new IsobmffBoxWriter(this.#auxWriter); + private auxTarget = new ArrayBufferTarget(); + private auxWriter = this.auxTarget._createWriter(); + private auxBoxWriter = new IsobmffBoxWriter(this.auxWriter); - #ftypSize: number | null = null; - #mdat: Box | null = null; + private ftypSize: number | null = null; + private mdat: Box | null = null; - #trackDatas: IsobmffTrackData[] = []; + private trackDatas: IsobmffTrackData[] = []; - #creationTime = Math.floor(Date.now() / 1000) + TIMESTAMP_OFFSET; - #finalizedChunks: Chunk[] = []; + private creationTime = Math.floor(Date.now() / 1000) + TIMESTAMP_OFFSET; + private finalizedChunks: Chunk[] = []; - #nextFragmentNumber = 1; + private nextFragmentNumber = 1; constructor(output: Output, format: Mp4OutputFormat) { super(output); - this.#writer = output._writer; - this.#boxWriter = new IsobmffBoxWriter(this.#writer); - this.#format = format; + this.writer = output._writer; + this.boxWriter = new IsobmffBoxWriter(this.writer); // If the fastStart option isn't defined, enable in-memory fast start if the target is an ArrayBuffer, as the // memory usage remains identical - this.#fastStart = format.options.fastStart ?? (this.#writer instanceof ArrayBufferTargetWriter ? 'in-memory' : false); + this.fastStart = format.options.fastStart ?? (this.writer instanceof ArrayBufferTargetWriter ? 'in-memory' : false); - if (this.#fastStart === 'in-memory' || this.#fastStart === 'fragmented') { - this.#writer.ensureMonotonicity = true; + if (this.fastStart === 'in-memory' || this.fastStart === 'fragmented') { + this.writer.ensureMonotonicity = true; } } @@ -125,27 +123,27 @@ export class IsobmffMuxer extends Muxer { const holdsAvc = this.output._tracks.some(x => x.type === 'video' && x.source._codec === 'avc'); // Write the header - this.#boxWriter.writeBox(ftyp({ + this.boxWriter.writeBox(ftyp({ holdsAvc: holdsAvc, - fragmented: this.#fastStart === 'fragmented' + fragmented: this.fastStart === 'fragmented' })); - this.#ftypSize = this.#writer.getPos(); + this.ftypSize = this.writer.getPos(); - if (this.#fastStart === 'in-memory') { - this.#mdat = mdat(false); - } else if (this.#fastStart === 'fragmented') { + if (this.fastStart === 'in-memory') { + this.mdat = mdat(false); + } else if (this.fastStart === 'fragmented') { // We write the moov box once we write out the first fragment to make sure we get the decoder configs } else { - this.#mdat = mdat(true); // Reserve large size by default, can refine this when finalizing. - this.#boxWriter.writeBox(this.#mdat); + this.mdat = mdat(true); // Reserve large size by default, can refine this when finalizing. + this.boxWriter.writeBox(this.mdat); } - this.#writer.flush(); + this.writer.flush(); } - #getVideoTrackData(track: OutputVideoTrack, meta?: EncodedVideoChunkMetadata) { - const existingTrackData = this.#trackDatas.find(x => x.track === track); + private getVideoTrackData(track: OutputVideoTrack, meta?: EncodedVideoChunkMetadata) { + const existingTrackData = this.trackDatas.find(x => x.track === track); if (existingTrackData) { return existingTrackData as IsobmffVideoTrackData; } @@ -178,14 +176,14 @@ export class IsobmffMuxer extends Muxer { compactlyCodedChunkTable: [] }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getAudioTrackData(track: OutputAudioTrack, meta?: EncodedAudioChunkMetadata) { - const existingTrackData = this.#trackDatas.find(x => x.track === track); + private getAudioTrackData(track: OutputAudioTrack, meta?: EncodedAudioChunkMetadata) { + const existingTrackData = this.trackDatas.find(x => x.track === track); if (existingTrackData) { return existingTrackData as IsobmffAudioTrackData; } @@ -216,14 +214,14 @@ export class IsobmffMuxer extends Muxer { compactlyCodedChunkTable: [] }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getSubtitleTrackData(track: OutputSubtitleTrack, meta?: SubtitleMetadata) { - const existingTrackData = this.#trackDatas.find(x => x.track === track); + private getSubtitleTrackData(track: OutputSubtitleTrack, meta?: SubtitleMetadata) { + const existingTrackData = this.trackDatas.find(x => x.track === track); if (existingTrackData) { return existingTrackData as IsobmffSubtitleTrackData; } @@ -256,8 +254,8 @@ export class IsobmffMuxer extends Muxer { cueToSourceId: new WeakMap() }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); // Subtitle cues don't need to start at 0, so let's register a timestamp at 0 to satisfy the // "timestamps must start a zero" constraint @@ -267,43 +265,43 @@ export class IsobmffMuxer extends Muxer { } addEncodedVideoChunk(track: OutputVideoTrack, chunk: EncodedVideoChunk, meta?: EncodedVideoChunkMetadata) { - const trackData = this.#getVideoTrackData(track, meta); + const trackData = this.getVideoTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === 'key'); - let sample = this.#createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + let sample = this.createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); - this.#registerSample(trackData, sample); + this.registerSample(trackData, sample); } addEncodedAudioChunk(track: OutputAudioTrack, chunk: EncodedAudioChunk, meta?: EncodedAudioChunkMetadata) { - const trackData = this.#getAudioTrackData(track, meta); + const trackData = this.getAudioTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === 'key'); - let sample = this.#createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + let sample = this.createSampleForTrack(trackData, data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); - this.#registerSample(trackData, sample); + this.registerSample(trackData, sample); } addSubtitleCue(track: OutputSubtitleTrack, cue: SubtitleCue, meta?: SubtitleMetadata) { - const trackData = this.#getSubtitleTrackData(track, meta); + const trackData = this.getSubtitleTrackData(track, meta); this.validateAndNormalizeTimestamp(trackData.track, 1e6 * cue.timestamp, true); if (track.source._codec === 'webvtt') { trackData.cueQueue.push(cue); - this.#processWebVTTCues(trackData, cue.timestamp); + this.processWebVTTCues(trackData, cue.timestamp); } else { // TODO } } - #processWebVTTCues(trackData: IsobmffSubtitleTrackData, until: number) { + private processWebVTTCues(trackData: IsobmffSubtitleTrackData, until: number) { // WebVTT cues need to undergo special processing as empty sections need to be padded out with samples, and // overlapping samples require special logic. The algorithm produces the format specified in ISO 14496-30. @@ -329,18 +327,18 @@ export class IsobmffMuxer extends Muxer { // We may need to pad out empty space with an vtte box if (trackData.lastCueEndTimestamp < sampleStart) { - this.#auxWriter.seek(0); + this.auxWriter.seek(0); let box = vtte(); - this.#auxBoxWriter.writeBox(box); + this.auxBoxWriter.writeBox(box); - let body = this.#auxWriter.getSlice(0, this.#auxWriter.getPos()); - let sample = this.#createSampleForTrack(trackData, body, trackData.lastCueEndTimestamp, sampleStart - trackData.lastCueEndTimestamp, 'key'); + let body = this.auxWriter.getSlice(0, this.auxWriter.getPos()); + let sample = this.createSampleForTrack(trackData, body, trackData.lastCueEndTimestamp, sampleStart - trackData.lastCueEndTimestamp, 'key'); - this.#registerSample(trackData, sample); + this.registerSample(trackData, sample); trackData.lastCueEndTimestamp = sampleStart; } - this.#auxWriter.seek(0); + this.auxWriter.seek(0); for (let i = 0; i < trackData.cueQueue.length; i++) { let cue = trackData.cueQueue[i]! @@ -364,11 +362,11 @@ export class IsobmffMuxer extends Muxer { if (cue.notes) { // Any notes/comments are included in a special vtta box let box = vtta(cue.notes); - this.#auxBoxWriter.writeBox(box); + this.auxBoxWriter.writeBox(box); } let box = vttc(cue.text, containsTimestamp ? sampleStart : null, cue.identifier ?? null, cue.settings ?? null, sourceId ?? null); - this.#auxBoxWriter.writeBox(box); + this.auxBoxWriter.writeBox(box); if (endTimestamp === sampleEnd) { // The cue won't appear in any future sample, so we're done with it @@ -376,15 +374,15 @@ export class IsobmffMuxer extends Muxer { } } - let body = this.#auxWriter.getSlice(0, this.#auxWriter.getPos()); - let sample = this.#createSampleForTrack(trackData, body, sampleStart, sampleEnd - sampleStart, 'key'); + let body = this.auxWriter.getSlice(0, this.auxWriter.getPos()); + let sample = this.createSampleForTrack(trackData, body, sampleStart, sampleEnd - sampleStart, 'key'); - this.#registerSample(trackData, sample); + this.registerSample(trackData, sample); trackData.lastCueEndTimestamp = sampleEnd; } } - #createSampleForTrack( + private createSampleForTrack( trackData: IsobmffTrackData, data: Uint8Array, timestamp: number, @@ -405,7 +403,7 @@ export class IsobmffMuxer extends Muxer { return sample; } - #processTimestamps(trackData: IsobmffTrackData) { + private processTimestamps(trackData: IsobmffTrackData) { if (trackData.timestampProcessingQueue.length === 0) { return; } @@ -433,7 +431,7 @@ export class IsobmffMuxer extends Muxer { trackData.lastTimescaleUnits += delta; trackData.lastSample.timescaleUnitsToNextSample = delta; - if (this.#fastStart !== 'fragmented') { + if (this.fastStart !== 'fragmented') { let lastTableEntry = last(trackData.timeToSampleTable); assert(lastTableEntry); @@ -485,7 +483,7 @@ export class IsobmffMuxer extends Muxer { } else { trackData.lastTimescaleUnits = 0; - if (this.#fastStart !== 'fragmented') { + if (this.fastStart !== 'fragmented') { trackData.timeToSampleTable.push({ sampleCount: 1, sampleDelta: durationInTimescale @@ -503,21 +501,21 @@ export class IsobmffMuxer extends Muxer { trackData.timestampProcessingQueue.length = 0; } - #registerSample(trackData: IsobmffTrackData, sample: Sample) { - if (this.#fastStart === 'fragmented') { + private registerSample(trackData: IsobmffTrackData, sample: Sample) { + if (this.fastStart === 'fragmented') { trackData.sampleQueue.push(sample); - this.#interleaveSamples(); + this.interleaveSamples(); } else { - this.#addSampleToTrack(trackData, sample); + this.addSampleToTrack(trackData, sample); } } - #addSampleToTrack(trackData: IsobmffTrackData, sample: Sample) { + private addSampleToTrack(trackData: IsobmffTrackData, sample: Sample) { if (sample.type === 'key') { - this.#processTimestamps(trackData); + this.processTimestamps(trackData); } - if (this.#fastStart !== 'fragmented') { + if (this.fastStart !== 'fragmented') { trackData.samples.push(sample); } @@ -527,10 +525,10 @@ export class IsobmffMuxer extends Muxer { } else { let currentChunkDuration = sample.timestamp - trackData.currentChunk.startTimestamp; - if (this.#fastStart === 'fragmented') { + if (this.fastStart === 'fragmented') { // We can only finalize this fragment (and begin a new one) if we know that each track will be able to // start the new one with a key frame. - const keyFrameQueuedEverywhere = this.#trackDatas.every(otherTrackData => { + const keyFrameQueuedEverywhere = this.trackDatas.every(otherTrackData => { if (trackData === otherTrackData) { return sample.type === 'key'; } @@ -541,7 +539,7 @@ export class IsobmffMuxer extends Muxer { if (currentChunkDuration >= 1.0 && keyFrameQueuedEverywhere) { beginNewChunk = true; - this.#finalizeFragment(); + this.finalizeFragment(); } } else { beginNewChunk = currentChunkDuration >= 0.5; // Chunk is long enough, we need a new one @@ -550,7 +548,7 @@ export class IsobmffMuxer extends Muxer { if (beginNewChunk) { if (trackData.currentChunk) { - this.#finalizeCurrentChunk(trackData); + this.finalizeCurrentChunk(trackData); } trackData.currentChunk = { @@ -566,13 +564,13 @@ export class IsobmffMuxer extends Muxer { trackData.timestampProcessingQueue.push(sample); } - #finalizeCurrentChunk(trackData: IsobmffTrackData) { - assert(this.#fastStart !== 'fragmented'); + private finalizeCurrentChunk(trackData: IsobmffTrackData) { + assert(this.fastStart !== 'fragmented'); if (!trackData.currentChunk) return; trackData.finalizedChunks.push(trackData.currentChunk); - this.#finalizedChunks.push(trackData.currentChunk); + this.finalizedChunks.push(trackData.currentChunk); if ( trackData.compactlyCodedChunkTable.length === 0 @@ -584,27 +582,27 @@ export class IsobmffMuxer extends Muxer { }); } - if (this.#fastStart === 'in-memory') { + if (this.fastStart === 'in-memory') { trackData.currentChunk.offset = 0; // We'll compute the proper offset when finalizing return; } // Write out the data - trackData.currentChunk.offset = this.#writer.getPos(); + trackData.currentChunk.offset = this.writer.getPos(); for (let sample of trackData.currentChunk.samples) { assert(sample.data); - this.#writer.write(sample.data); + this.writer.write(sample.data); sample.data = null; // Can be GC'd } - this.#writer.flush(); + this.writer.flush(); } - #interleaveSamples() { - assert(this.#fastStart === 'fragmented'); + private interleaveSamples() { + assert(this.fastStart === 'fragmented'); for (const track of this.output._tracks) { - if (!track.source._closed && !this.#trackDatas.some(x => x.track === track)) { + if (!track.source._closed && !this.trackDatas.some(x => x.track === track)) { return; // We haven't seen a sample from this open track yet } } @@ -614,7 +612,7 @@ export class IsobmffMuxer extends Muxer { let trackWithMinTimestamp: IsobmffTrackData | null = null; let minTimestamp = Infinity; - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { if (trackData.sampleQueue.length === 0 && !trackData.track.source._closed) { break outer; } @@ -630,25 +628,25 @@ export class IsobmffMuxer extends Muxer { } let sample = trackWithMinTimestamp.sampleQueue.shift()!; - this.#addSampleToTrack(trackWithMinTimestamp, sample); + this.addSampleToTrack(trackWithMinTimestamp, sample); } } - #finalizeFragment(flushWriter = true) { - assert(this.#fastStart === 'fragmented'); + private finalizeFragment(flushWriter = true) { + assert(this.fastStart === 'fragmented'); - let fragmentNumber = this.#nextFragmentNumber++; + let fragmentNumber = this.nextFragmentNumber++; if (fragmentNumber === 1) { // Write the moov box now that we have all decoder configs - let movieBox = moov(this.#trackDatas, this.#creationTime, true); - this.#boxWriter.writeBox(movieBox); + let movieBox = moov(this.trackDatas, this.creationTime, true); + this.boxWriter.writeBox(movieBox); } // Write out an initial moof box; will be overwritten later once actual chunk offsets are known - let moofOffset = this.#writer.getPos(); - let moofBox = moof(fragmentNumber, this.#trackDatas); - this.#boxWriter.writeBox(moofBox); + let moofOffset = this.writer.getPos(); + let moofBox = moof(fragmentNumber, this.trackDatas); + this.boxWriter.writeBox(moofBox); // Create the mdat box { @@ -656,94 +654,94 @@ export class IsobmffMuxer extends Muxer { let totalTrackSampleSize = 0; // Compute the size of the mdat box - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { assert(trackData.currentChunk); for (let sample of trackData.currentChunk.samples) { totalTrackSampleSize += sample.size; } } - let mdatSize = this.#boxWriter.measureBox(mdatBox) + totalTrackSampleSize; + let mdatSize = this.boxWriter.measureBox(mdatBox) + totalTrackSampleSize; if (mdatSize >= 2**32) { // Fragment is larger than 4 GiB, we need to use the large size mdatBox.largeSize = true; - mdatSize = this.#boxWriter.measureBox(mdatBox) + totalTrackSampleSize; + mdatSize = this.boxWriter.measureBox(mdatBox) + totalTrackSampleSize; } mdatBox.size = mdatSize; - this.#boxWriter.writeBox(mdatBox); + this.boxWriter.writeBox(mdatBox); } // Write sample data - for (let trackData of this.#trackDatas) { - trackData.currentChunk!.offset = this.#writer.getPos(); + for (let trackData of this.trackDatas) { + trackData.currentChunk!.offset = this.writer.getPos(); trackData.currentChunk!.moofOffset = moofOffset; for (let sample of trackData.currentChunk!.samples) { - this.#writer.write(sample.data!); + this.writer.write(sample.data!); sample.data = null; // Can be GC'd } } // Now that we set the actual chunk offsets, fix the moof box - let endPos = this.#writer.getPos(); - this.#writer.seek(this.#boxWriter.offsets.get(moofBox)!); - let newMoofBox = moof(fragmentNumber, this.#trackDatas); - this.#boxWriter.writeBox(newMoofBox); - this.#writer.seek(endPos); + let endPos = this.writer.getPos(); + this.writer.seek(this.boxWriter.offsets.get(moofBox)!); + let newMoofBox = moof(fragmentNumber, this.trackDatas); + this.boxWriter.writeBox(newMoofBox); + this.writer.seek(endPos); - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { trackData.finalizedChunks.push(trackData.currentChunk!); - this.#finalizedChunks.push(trackData.currentChunk!); + this.finalizedChunks.push(trackData.currentChunk!); trackData.currentChunk = null; } if (flushWriter) { - this.#writer.flush(); + this.writer.flush(); } } override onTrackClose(track: OutputTrack) { if (track.type === 'subtitle' && track.source._codec === 'webvtt') { - let trackData = this.#trackDatas.find(x => x.track === track) as IsobmffSubtitleTrackData; + let trackData = this.trackDatas.find(x => x.track === track) as IsobmffSubtitleTrackData; if (trackData) { - this.#processWebVTTCues(trackData, Infinity); + this.processWebVTTCues(trackData, Infinity); } } - if (this.#fastStart === 'fragmented') { + if (this.fastStart === 'fragmented') { // Since a track is now closed, we may be able to write out chunks that were previously waiting - this.#interleaveSamples(); + this.interleaveSamples(); } } /** Finalizes the file, making it ready for use. Must be called after all video and audio chunks have been added. */ finalize() { - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { if (trackData.type === 'subtitle' && trackData.track.source._codec === 'webvtt') { - this.#processWebVTTCues(trackData, Infinity); + this.processWebVTTCues(trackData, Infinity); } } - if (this.#fastStart === 'fragmented') { - for (let trackData of this.#trackDatas) { + if (this.fastStart === 'fragmented') { + for (let trackData of this.trackDatas) { for (let sample of trackData.sampleQueue) { - this.#addSampleToTrack(trackData, sample); + this.addSampleToTrack(trackData, sample); } - this.#processTimestamps(trackData); + this.processTimestamps(trackData); } - this.#finalizeFragment(false); // Don't flush the last fragment as we will flush it with the mfra box soon + this.finalizeFragment(false); // Don't flush the last fragment as we will flush it with the mfra box soon } else { - for (let trackData of this.#trackDatas) { - this.#processTimestamps(trackData); - this.#finalizeCurrentChunk(trackData); + for (let trackData of this.trackDatas) { + this.processTimestamps(trackData); + this.finalizeCurrentChunk(trackData); } } - if (this.#fastStart === 'in-memory') { - assert(this.#mdat); + if (this.fastStart === 'in-memory') { + assert(this.mdat); let mdatSize: number; // We know how many chunks there are, but computing the chunk positions requires an iterative approach: @@ -755,12 +753,12 @@ export class IsobmffMuxer extends Muxer { // size of the moov box and can compute the proper chunk positions. for (let i = 0; i < 2; i++) { - let movieBox = moov(this.#trackDatas, this.#creationTime); - let movieBoxSize = this.#boxWriter.measureBox(movieBox); - mdatSize = this.#boxWriter.measureBox(this.#mdat); - let currentChunkPos = this.#writer.getPos() + movieBoxSize + mdatSize; + let movieBox = moov(this.trackDatas, this.creationTime); + let movieBoxSize = this.boxWriter.measureBox(movieBox); + mdatSize = this.boxWriter.measureBox(this.mdat); + let currentChunkPos = this.writer.getPos() + movieBoxSize + mdatSize; - for (let chunk of this.#finalizedChunks) { + for (let chunk of this.finalizedChunks) { chunk.offset = currentChunkPos; for (let { data } of chunk.samples) { assert(data); @@ -770,53 +768,53 @@ export class IsobmffMuxer extends Muxer { } if (currentChunkPos < 2**32) break; - if (mdatSize >= 2**32) this.#mdat.largeSize = true; + if (mdatSize >= 2**32) this.mdat.largeSize = true; } - let movieBox = moov(this.#trackDatas, this.#creationTime); - this.#boxWriter.writeBox(movieBox); + let movieBox = moov(this.trackDatas, this.creationTime); + this.boxWriter.writeBox(movieBox); - this.#mdat.size = mdatSize!; - this.#boxWriter.writeBox(this.#mdat); + this.mdat.size = mdatSize!; + this.boxWriter.writeBox(this.mdat); - for (let chunk of this.#finalizedChunks) { + for (let chunk of this.finalizedChunks) { for (let sample of chunk.samples) { assert(sample.data); - this.#writer.write(sample.data); + this.writer.write(sample.data); sample.data = null; } } - } else if (this.#fastStart === 'fragmented') { + } else if (this.fastStart === 'fragmented') { // Append the mfra box to the end of the file for better random access - let startPos = this.#writer.getPos(); - let mfraBox = mfra(this.#trackDatas); - this.#boxWriter.writeBox(mfraBox); + let startPos = this.writer.getPos(); + let mfraBox = mfra(this.trackDatas); + this.boxWriter.writeBox(mfraBox); // Patch the 'size' field of the mfro box at the end of the mfra box now that we know its actual size - let mfraBoxSize = this.#writer.getPos() - startPos; - this.#writer.seek(this.#writer.getPos() - 4); - this.#boxWriter.writeU32(mfraBoxSize); + let mfraBoxSize = this.writer.getPos() - startPos; + this.writer.seek(this.writer.getPos() - 4); + this.boxWriter.writeU32(mfraBoxSize); } else { - assert(this.#mdat); - assert(this.#ftypSize !== null); + assert(this.mdat); + assert(this.ftypSize !== null); - let mdatPos = this.#boxWriter.offsets.get(this.#mdat); + let mdatPos = this.boxWriter.offsets.get(this.mdat); assert(mdatPos !== undefined); - let mdatSize = this.#writer.getPos() - mdatPos; - this.#mdat.size = mdatSize; - this.#mdat.largeSize = mdatSize >= 2**32; // Only use the large size if we need it - this.#boxWriter.patchBox(this.#mdat); + let mdatSize = this.writer.getPos() - mdatPos; + this.mdat.size = mdatSize; + this.mdat.largeSize = mdatSize >= 2**32; // Only use the large size if we need it + this.boxWriter.patchBox(this.mdat); - let movieBox = moov(this.#trackDatas, this.#creationTime); + let movieBox = moov(this.trackDatas, this.creationTime); - if (typeof this.#fastStart === 'object') { - this.#writer.seek(this.#ftypSize); - this.#boxWriter.writeBox(movieBox); + if (typeof this.fastStart === 'object') { + this.writer.seek(this.ftypSize); + this.boxWriter.writeBox(movieBox); - let remainingBytes = mdatPos - this.#writer.getPos(); - this.#boxWriter.writeBox(free(remainingBytes)); + let remainingBytes = mdatPos - this.writer.getPos(); + this.boxWriter.writeBox(free(remainingBytes)); } else { - this.#boxWriter.writeBox(movieBox); + this.boxWriter.writeBox(movieBox); } } } diff --git a/src/matroska/matroska-muxer.ts b/src/matroska/matroska-muxer.ts index e3431a1..8dd87b3 100644 --- a/src/matroska/matroska-muxer.ts +++ b/src/matroska/matroska-muxer.ts @@ -88,94 +88,94 @@ const TRACK_TYPE_MAP: Record = { export class MatroskaMuxer extends Muxer { override timestampsMustStartAtZero = false; - #writer: Writer; - #format: WebMOutputFormat | MkvOutputFormat; + private writer: Writer; + private format: WebMOutputFormat | MkvOutputFormat; - #helper = new Uint8Array(8); - #helperView = new DataView(this.#helper.buffer); + private helper = new Uint8Array(8); + private helperView = new DataView(this.helper.buffer); /** * Stores the position from the start of the file to where EBML elements have been written. This is used to * rewrite/edit elements that were already added before, and to measure sizes of things. */ - offsets = new WeakMap(); + private offsets = new WeakMap(); /** Same as offsets, but stores position where the element's data starts (after ID and size fields). */ - dataOffsets = new WeakMap(); + private dataOffsets = new WeakMap(); - #trackDatas: MatroskaTrackData[] = []; + private trackDatas: MatroskaTrackData[] = []; - #segment: EBMLElement | null = null; - #segmentInfo: EBMLElement | null = null; - #seekHead: SeekHead | null = null; - #tracksElement: EBMLElement | null = null; - #segmentDuration: EBMLElement | null = null; - #cues: EBMLElement | null = null; + private segment: EBMLElement | null = null; + private segmentInfo: EBMLElement | null = null; + private seekHead: SeekHead | null = null; + private tracksElement: EBMLElement | null = null; + private segmentDuration: EBMLElement | null = null; + private cues: EBMLElement | null = null; - #currentCluster: EBMLElement | null = null; - #currentClusterMsTimestamp: number | null = null; - #trackDatasInCurrentCluster = new Set(); + private currentCluster: EBMLElement | null = null; + private currentClusterMsTimestamp: number | null = null; + private trackDatasInCurrentCluster = new Set(); - #duration = 0; + private duration = 0; constructor(output: Output, format: MkvOutputFormat) { super(output); - this.#writer = output._writer; - this.#format = format; + this.writer = output._writer; + this.format = format; - if (this.#format.options.streamable) { - this.#writer.ensureMonotonicity = true; + if (this.format.options.streamable) { + this.writer.ensureMonotonicity = true; } } - #writeByte(value: number) { - this.#helperView.setUint8(0, value); - this.#writer.write(this.#helper.subarray(0, 1)); + private writeByte(value: number) { + this.helperView.setUint8(0, value); + this.writer.write(this.helper.subarray(0, 1)); } - #writeFloat32(value: number) { - this.#helperView.setFloat32(0, value, false); - this.#writer.write(this.#helper.subarray(0, 4)); + private writeFloat32(value: number) { + this.helperView.setFloat32(0, value, false); + this.writer.write(this.helper.subarray(0, 4)); } - #writeFloat64(value: number) { - this.#helperView.setFloat64(0, value, false); - this.#writer.write(this.#helper); + private writeFloat64(value: number) { + this.helperView.setFloat64(0, value, false); + this.writer.write(this.helper); } - #writeUnsignedInt(value: number, width = measureUnsignedInt(value)) { + private writeUnsignedInt(value: number, width = measureUnsignedInt(value)) { let pos = 0; // Each case falls through: switch (width) { case 6: // Need to use division to access >32 bits of floating point var - this.#helperView.setUint8(pos++, (value / 2**40) | 0); + this.helperView.setUint8(pos++, (value / 2**40) | 0); case 5: - this.#helperView.setUint8(pos++, (value / 2**32) | 0); + this.helperView.setUint8(pos++, (value / 2**32) | 0); case 4: - this.#helperView.setUint8(pos++, value >> 24); + this.helperView.setUint8(pos++, value >> 24); case 3: - this.#helperView.setUint8(pos++, value >> 16); + this.helperView.setUint8(pos++, value >> 16); case 2: - this.#helperView.setUint8(pos++, value >> 8); + this.helperView.setUint8(pos++, value >> 8); case 1: - this.#helperView.setUint8(pos++, value); + this.helperView.setUint8(pos++, value); break; default: throw new Error('Bad UINT size ' + width); } - this.#writer.write(this.#helper.subarray(0, pos)); + this.writer.write(this.helper.subarray(0, pos)); } - #writeSignedInt(value: number, width = measureSignedInt(value)) { + private writeSignedInt(value: number, width = measureSignedInt(value)) { if (value < 0) { // Two's complement stuff value += 2 ** (width * 8); } - this.#writeUnsignedInt(value, width); + this.writeUnsignedInt(value, width); } writeEBMLVarInt(value: number, width = measureEBMLVarInt(value)) { @@ -183,22 +183,22 @@ export class MatroskaMuxer extends Muxer { switch (width) { case 1: - this.#helperView.setUint8(pos++, (1 << 7) | value); + this.helperView.setUint8(pos++, (1 << 7) | value); break; case 2: - this.#helperView.setUint8(pos++, (1 << 6) | (value >> 8)); - this.#helperView.setUint8(pos++, value); + 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); + 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); + 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: /** @@ -206,94 +206,94 @@ export class MatroskaMuxer extends Muxer { * operations, so we need to do a division by 2^32 instead of a * right-shift of 32 to retain those top 3 bits */ - this.#helperView.setUint8(pos++, (1 << 3) | ((value / 2**32) & 0x7)); - this.#helperView.setUint8(pos++, value >> 24); - this.#helperView.setUint8(pos++, value >> 16); - this.#helperView.setUint8(pos++, value >> 8); - this.#helperView.setUint8(pos++, value); + this.helperView.setUint8(pos++, (1 << 3) | ((value / 2**32) & 0x7)); + 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) & 0x3)); - 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); + this.helperView.setUint8(pos++, (1 << 2) | ((value / 2**40) & 0x3)); + 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 VINT size ' + width); } - this.#writer.write(this.#helper.subarray(0, pos)); + this.writer.write(this.helper.subarray(0, pos)); } // Assumes the string is ASCII - #writeString(str: string) { - this.#writer.write(new Uint8Array(str.split('').map(x => x.charCodeAt(0)))); + private writeString(str: string) { + this.writer.write(new Uint8Array(str.split('').map(x => x.charCodeAt(0)))); } - writeEBML(data: EBML | null) { + private writeEBML(data: EBML | null) { if (data === null) return; if (data instanceof Uint8Array) { - this.#writer.write(data); + this.writer.write(data); } else if (Array.isArray(data)) { for (let elem of data) { this.writeEBML(elem); } } else { - this.offsets.set(data, this.#writer.getPos()); + this.offsets.set(data, this.writer.getPos()); - this.#writeUnsignedInt(data.id); // ID field + this.writeUnsignedInt(data.id); // ID field if (Array.isArray(data.data)) { - let sizePos = this.#writer.getPos(); + let sizePos = this.writer.getPos(); let sizeSize = data.size === -1 ? 1 : (data.size ?? 4); if (data.size === -1) { // Write the reserved all-one-bits marker for unknown/unbounded size. - this.#writeByte(0xff); + this.writeByte(0xff); } else { - this.#writer.seek(this.#writer.getPos() + sizeSize); + this.writer.seek(this.writer.getPos() + sizeSize); } - let startPos = this.#writer.getPos(); + let startPos = this.writer.getPos(); this.dataOffsets.set(data, startPos); this.writeEBML(data.data); if (data.size !== -1) { - let size = this.#writer.getPos() - startPos; - let endPos = this.#writer.getPos(); - this.#writer.seek(sizePos); + let size = this.writer.getPos() - startPos; + let endPos = this.writer.getPos(); + this.writer.seek(sizePos); this.writeEBMLVarInt(size, sizeSize); - this.#writer.seek(endPos); + this.writer.seek(endPos); } } else if (typeof data.data === 'number') { let size = data.size ?? measureUnsignedInt(data.data); this.writeEBMLVarInt(size); - this.#writeUnsignedInt(data.data, size); + this.writeUnsignedInt(data.data, size); } else if (typeof data.data === 'string') { this.writeEBMLVarInt(data.data.length); - this.#writeString(data.data); + this.writeString(data.data); } else if (data.data instanceof Uint8Array) { this.writeEBMLVarInt(data.data.byteLength, data.size); - this.#writer.write(data.data); + this.writer.write(data.data); } else if (data.data instanceof EBMLFloat32) { this.writeEBMLVarInt(4); - this.#writeFloat32(data.data.value); + this.writeFloat32(data.data.value); } else if (data.data instanceof EBMLFloat64) { this.writeEBMLVarInt(8); - this.#writeFloat64(data.data.value); + this.writeFloat64(data.data.value); } else if (data.data instanceof EBMLSignedInt) { let size = data.size ?? measureSignedInt(data.data.value); this.writeEBMLVarInt(size); - this.#writeSignedInt(data.data.value, size); + this.writeSignedInt(data.data.value, size); } } } override beforeTrackAdd(track: OutputTrack) { - if (!(this.#format instanceof WebMOutputFormat)) { + if (!(this.format instanceof WebMOutputFormat)) { return; } @@ -315,25 +315,25 @@ export class MatroskaMuxer extends Muxer { } start() { - this.#writeEBMLHeader(); + this.writeEBMLHeader(); - if (!this.#format.options.streamable) { - this.#createSeekHead(); + if (!this.format.options.streamable) { + this.createSeekHead(); } - this.#createSegmentInfo(); - this.#createCues(); + this.createSegmentInfo(); + this.createCues(); - this.#writer.flush(); + this.writer.flush(); } - #writeEBMLHeader() { + private writeEBMLHeader() { let ebmlHeader: EBML = { 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.DocType, data: this.format instanceof WebMOutputFormat ? 'webm' : 'matroska' }, { id: EBMLId.DocTypeVersion, data: 2 }, { id: EBMLId.DocTypeReadVersion, data: 2 } ] }; @@ -344,7 +344,7 @@ export class MatroskaMuxer extends Muxer { * 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. */ - #createSeekHead() { + private createSeekHead() { const kaxCues = new Uint8Array([ 0x1c, 0x53, 0xbb, 0x6b ]); const kaxInfo = new Uint8Array([ 0x15, 0x49, 0xa9, 0x66 ]); const kaxTracks = new Uint8Array([ 0x16, 0x54, 0xae, 0x6b ]); @@ -363,27 +363,27 @@ export class MatroskaMuxer extends Muxer { { id: EBMLId.SeekPosition, size: 5, data: 0 } ] } ] }; - this.#seekHead = seekHead; + this.seekHead = seekHead; } - #createSegmentInfo() { + private createSegmentInfo() { let segmentDuration: EBML = { id: EBMLId.Duration, data: new EBMLFloat64(0) }; - this.#segmentDuration = segmentDuration; + this.segmentDuration = segmentDuration; let segmentInfo: EBML = { id: EBMLId.Info, data: [ { id: EBMLId.TimestampScale, data: 1e6 }, { id: EBMLId.MuxingApp, data: APP_NAME }, { id: EBMLId.WritingApp, data: APP_NAME }, - !this.#format.options.streamable ? segmentDuration : null + !this.format.options.streamable ? segmentDuration : null ] }; - this.#segmentInfo = segmentInfo; + this.segmentInfo = segmentInfo; } - #createTracks() { + private createTracks() { let tracksElement = { id: EBMLId.Tracks, data: [] as EBML[] }; - this.#tracksElement = tracksElement; + this.tracksElement = tracksElement; - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { tracksElement.data.push({ id: EBMLId.TrackEntry, data: [ { id: EBMLId.TrackNumber, data: trackData.track.id }, { id: EBMLId.TrackUID, data: trackData.track.id }, @@ -429,17 +429,17 @@ export class MatroskaMuxer extends Muxer { } } - #createSegment() { + private createSegment() { let segment: EBML = { id: EBMLId.Segment, - size: this.#format.options.streamable ? -1 : SEGMENT_SIZE_BYTES, + size: this.format.options.streamable ? -1 : SEGMENT_SIZE_BYTES, data: [ - !this.#format.options.streamable ? this.#seekHead as EBML : null, - this.#segmentInfo, - this.#tracksElement + !this.format.options.streamable ? this.seekHead as EBML : null, + this.segmentInfo, + this.tracksElement ] }; - this.#segment = segment; + this.segment = segment; this.writeEBML(segment); @@ -451,17 +451,17 @@ export class MatroskaMuxer extends Muxer { */ } - #createCues() { - this.#cues = { id: EBMLId.Cues, data: [] }; + private createCues() { + this.cues = { id: EBMLId.Cues, data: [] }; } - get #segmentDataOffset() { - assert(this.#segment); - return this.dataOffsets.get(this.#segment)!; + private get segmentDataOffset() { + assert(this.segment); + return this.dataOffsets.get(this.segment)!; } - #getVideoTrackData(track: OutputVideoTrack, meta?: EncodedVideoChunkMetadata) { - const existingTrackData = this.#trackDatas.find(x => x.track === track); + private getVideoTrackData(track: OutputVideoTrack, meta?: EncodedVideoChunkMetadata) { + const existingTrackData = this.trackDatas.find(x => x.track === track); if (existingTrackData) { return existingTrackData as MatroskaVideoTrackData; } @@ -485,14 +485,14 @@ export class MatroskaMuxer extends Muxer { lastWrittenMsTimestamp: null }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getAudioTrackData(track: OutputAudioTrack, meta?: EncodedAudioChunkMetadata) { - const existingTrackData = this.#trackDatas.find(x => x.track === track); + private getAudioTrackData(track: OutputAudioTrack, meta?: EncodedAudioChunkMetadata) { + const existingTrackData = this.trackDatas.find(x => x.track === track); if (existingTrackData) { return existingTrackData as MatroskaAudioTrackData; } @@ -514,14 +514,14 @@ export class MatroskaMuxer extends Muxer { lastWrittenMsTimestamp: null }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } - #getSubtitleTrackData(track: OutputSubtitleTrack, meta?: SubtitleMetadata) { - const existingTrackData = this.#trackDatas.find(x => x.track === track); + private getSubtitleTrackData(track: OutputSubtitleTrack, meta?: SubtitleMetadata) { + const existingTrackData = this.trackDatas.find(x => x.track === track); if (existingTrackData) { return existingTrackData as MatroskaAudioTrackData; } @@ -541,41 +541,41 @@ export class MatroskaMuxer extends Muxer { lastWrittenMsTimestamp: null }; - this.#trackDatas.push(newTrackData); - this.#trackDatas.sort((a, b) => a.track.id - b.track.id); + this.trackDatas.push(newTrackData); + this.trackDatas.sort((a, b) => a.track.id - b.track.id); return newTrackData; } addEncodedVideoChunk(track: OutputVideoTrack, chunk: EncodedVideoChunk, meta?: EncodedVideoChunkMetadata) { - const trackData = this.#getVideoTrackData(track, meta); + const trackData = this.getVideoTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === 'key'); - let videoChunk = this.#createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); - if (track.source._codec === 'vp9') this.#fixVP9ColorSpace(trackData, videoChunk); + let videoChunk = this.createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + if (track.source._codec === 'vp9') this.fixVP9ColorSpace(trackData, videoChunk); trackData.chunkQueue.push(videoChunk); - this.#interleaveChunks(); + this.interleaveChunks(); } addEncodedAudioChunk(track: OutputAudioTrack, chunk: EncodedAudioChunk, meta?: EncodedAudioChunkMetadata) { - const trackData = this.#getAudioTrackData(track, meta); + const trackData = this.getAudioTrackData(track, meta); let data = new Uint8Array(chunk.byteLength); chunk.copyTo(data); let timestamp = this.validateAndNormalizeTimestamp(trackData.track, chunk.timestamp, chunk.type === 'key'); - let audioChunk = this.#createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); + let audioChunk = this.createInternalChunk(data, timestamp, (chunk.duration ?? 0) / 1e6, chunk.type); trackData.chunkQueue.push(audioChunk); - this.#interleaveChunks(); + this.interleaveChunks(); } addSubtitleCue(track: OutputSubtitleTrack, cue: SubtitleCue, meta?: SubtitleMetadata) { - const trackData = this.#getSubtitleTrackData(track, meta); + const trackData = this.getSubtitleTrackData(track, meta); const timestamp = this.validateAndNormalizeTimestamp(trackData.track, 1e6 * cue.timestamp, true); @@ -594,15 +594,15 @@ export class MatroskaMuxer extends Muxer { const body = textEncoder.encode(bodyText); const additions = `${cue.settings ?? ''}\n${cue.identifier ?? ''}\n${cue.notes ?? ''}`; - let subtitleChunk = this.#createInternalChunk(body, timestamp, cue.duration, 'key', additions.trim() ? textEncoder.encode(additions) : null); + let subtitleChunk = this.createInternalChunk(body, timestamp, cue.duration, 'key', additions.trim() ? textEncoder.encode(additions) : null); trackData.chunkQueue.push(subtitleChunk); - this.#interleaveChunks(); + this.interleaveChunks(); } - #interleaveChunks() { + private interleaveChunks() { for (const track of this.output._tracks) { - if (!track.source._closed && !this.#trackDatas.some(x => x.track === track)) { + if (!track.source._closed && !this.trackDatas.some(x => x.track === track)) { return; // We haven't seen a sample from this open track yet } } @@ -612,7 +612,7 @@ export class MatroskaMuxer extends Muxer { let trackWithMinTimestamp: MatroskaTrackData | null = null; let minTimestamp = Infinity; - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { if (trackData.chunkQueue.length === 0 && !trackData.track.source._closed) { break outer; } @@ -628,16 +628,16 @@ export class MatroskaMuxer extends Muxer { } let chunk = trackWithMinTimestamp.chunkQueue.shift()!; - this.#writeBlock(trackWithMinTimestamp, chunk); + this.writeBlock(trackWithMinTimestamp, chunk); } - this.#writer.flush(); + 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. */ // http://downloads.webmproject.org/docs/vp9/vp9-bitstream_superframe-and-uncompressed-header_v1.0.pdf - #fixVP9ColorSpace(trackData: MatroskaVideoTrackData, chunk: InternalMediaChunk) { + private fixVP9ColorSpace(trackData: MatroskaVideoTrackData, chunk: InternalMediaChunk) { if (chunk.type !== 'key') return; if (!trackData.info.decoderConfig.colorSpace || !trackData.info.decoderConfig.colorSpace.matrix) return; @@ -671,7 +671,7 @@ export class MatroskaMuxer extends Muxer { } /** Converts a read-only external chunk into an internal one for easier use. */ - #createInternalChunk( + private createInternalChunk( data: Uint8Array, timestamp: number, duration: number, @@ -690,17 +690,17 @@ export class MatroskaMuxer extends Muxer { } /** Writes a block containing media data to the file. */ - #writeBlock(trackData: MatroskaTrackData, chunk: InternalMediaChunk) { + private writeBlock(trackData: MatroskaTrackData, chunk: InternalMediaChunk) { // Due to the interlacing algorithm, this code will be run once we've seen one chunk from every media track. - if (!this.#segment) { - this.#createTracks(); - this.#createSegment(); + if (!this.segment) { + this.createTracks(); + this.createSegment(); } let msTimestamp = Math.floor(1000 * chunk.timestamp); // We can only finalize this fragment (and begin a new one) if we know that each track will be able to // start the new one with a key frame. - const keyFrameQueuedEverywhere = this.#trackDatas.every(otherTrackData => { + const keyFrameQueuedEverywhere = this.trackDatas.every(otherTrackData => { if (otherTrackData.track.source._closed) { return true; } @@ -714,13 +714,13 @@ export class MatroskaMuxer extends Muxer { }); if ( - !this.#currentCluster || - (keyFrameQueuedEverywhere && msTimestamp - this.#currentClusterMsTimestamp! >= 1000) + !this.currentCluster || + (keyFrameQueuedEverywhere && msTimestamp - this.currentClusterMsTimestamp! >= 1000) ) { - this.#createNewCluster(msTimestamp); + this.createNewCluster(msTimestamp); } - let relativeTimestamp = msTimestamp - this.#currentClusterMsTimestamp!; + let relativeTimestamp = msTimestamp - this.currentClusterMsTimestamp!; if (relativeTimestamp < 0) { // The chunk lies outside of the current cluster return; @@ -770,16 +770,16 @@ export class MatroskaMuxer extends Muxer { this.writeEBML(blockGroup); } - this.#duration = Math.max(this.#duration, msTimestamp + msDuration); + this.duration = Math.max(this.duration, msTimestamp + msDuration); trackData.lastWrittenMsTimestamp = msTimestamp; - this.#trackDatasInCurrentCluster.add(trackData); + this.trackDatasInCurrentCluster.add(trackData); } /** Creates a new Cluster element to contain media chunks. */ - #createNewCluster(msTimestamp: number) { - if (this.#currentCluster && !this.#format.options.streamable) { - this.#finalizeCurrentCluster(); + private createNewCluster(msTimestamp: number) { + if (this.currentCluster && !this.format.options.streamable) { + this.finalizeCurrentCluster(); } /* @@ -788,28 +788,28 @@ export class MatroskaMuxer extends Muxer { } */ - this.#currentCluster = { + this.currentCluster = { id: EBMLId.Cluster, - size: this.#format.options.streamable ? -1 : CLUSTER_SIZE_BYTES, + size: this.format.options.streamable ? -1 : CLUSTER_SIZE_BYTES, data: [ { id: EBMLId.Timestamp, data: msTimestamp } ] }; - this.writeEBML(this.#currentCluster); + this.writeEBML(this.currentCluster); - this.#currentClusterMsTimestamp = msTimestamp; - this.#trackDatasInCurrentCluster.clear(); + this.currentClusterMsTimestamp = msTimestamp; + this.trackDatasInCurrentCluster.clear(); } - #finalizeCurrentCluster() { - assert(this.#currentCluster); - let clusterSize = this.#writer.getPos() - this.dataOffsets.get(this.#currentCluster)!; - let endPos = this.#writer.getPos(); + private finalizeCurrentCluster() { + assert(this.currentCluster); + let clusterSize = this.writer.getPos() - this.dataOffsets.get(this.currentCluster)!; + let endPos = this.writer.getPos(); // Write the size now that we know it - this.#writer.seek(this.offsets.get(this.#currentCluster)! + 4); + this.writer.seek(this.offsets.get(this.currentCluster)! + 4); this.writeEBMLVarInt(clusterSize, CLUSTER_SIZE_BYTES); - this.#writer.seek(endPos); + this.writer.seek(endPos); /* if (this.#writer instanceof BaseStreamTargetWriter && this.#writer.target.options.onCluster) { @@ -819,15 +819,15 @@ export class MatroskaMuxer extends Muxer { */ let clusterOffsetFromSegment = - this.offsets.get(this.#currentCluster)! - this.#segmentDataOffset; + this.offsets.get(this.currentCluster)! - this.segmentDataOffset; - assert(this.#cues); + assert(this.cues); // Add a CuePoint to the Cues element for better seeking - (this.#cues.data as EBML[]).push({ id: EBMLId.CuePoint, data: [ - { id: EBMLId.CueTime, data: this.#currentClusterMsTimestamp! }, + (this.cues.data as EBML[]).push({ id: EBMLId.CuePoint, data: [ + { id: EBMLId.CueTime, data: this.currentClusterMsTimestamp! }, // We only write out cues for tracks that have at least one chunk in this cluster - ...[...this.#trackDatasInCurrentCluster].map(trackData => { + ...[...this.trackDatasInCurrentCluster].map(trackData => { return { id: EBMLId.CueTrackPositions, data: [ { id: EBMLId.CueTrack, data: trackData.track.id }, { id: EBMLId.CueClusterPosition, data: clusterOffsetFromSegment } @@ -838,55 +838,55 @@ export class MatroskaMuxer extends Muxer { override onTrackClose() { // Since a track is now closed, we may be able to write out chunks that were previously waiting - this.#interleaveChunks(); + this.interleaveChunks(); } /** Finalizes the file, making it ready for use. Must be called after all media chunks have been added. */ finalize() { - if (!this.#segment) { - this.#createTracks(); - this.#createSegment(); + if (!this.segment) { + this.createTracks(); + this.createSegment(); } // Flush any remaining queued chunks to the file - for (let trackData of this.#trackDatas) { + for (let trackData of this.trackDatas) { while (trackData.chunkQueue.length > 0) { - this.#writeBlock(trackData, trackData.chunkQueue.shift()!); + this.writeBlock(trackData, trackData.chunkQueue.shift()!); } } - if (!this.#format.options.streamable && this.#currentCluster) { - this.#finalizeCurrentCluster(); + if (!this.format.options.streamable && this.currentCluster) { + this.finalizeCurrentCluster(); } - assert(this.#cues); - this.writeEBML(this.#cues); + assert(this.cues); + this.writeEBML(this.cues); - if (!this.#format.options.streamable) { - let endPos = this.#writer.getPos(); + if (!this.format.options.streamable) { + let endPos = this.writer.getPos(); // Write the Segment size - let segmentSize = this.#writer.getPos() - this.#segmentDataOffset; - this.#writer.seek(this.offsets.get(this.#segment!)! + 4); + let segmentSize = this.writer.getPos() - this.segmentDataOffset; + this.writer.seek(this.offsets.get(this.segment!)! + 4); this.writeEBMLVarInt(segmentSize, SEGMENT_SIZE_BYTES); // Write the duration of the media to the Segment - this.#segmentDuration!.data = new EBMLFloat64(this.#duration); - this.#writer.seek(this.offsets.get(this.#segmentDuration!)!); - this.writeEBML(this.#segmentDuration!); + this.segmentDuration!.data = new EBMLFloat64(this.duration); + this.writer.seek(this.offsets.get(this.segmentDuration!)!); + this.writeEBML(this.segmentDuration!); // Fill in SeekHead position data and write it again - this.#seekHead!.data[0]!.data[1]!.data = - this.offsets.get(this.#cues)! - this.#segmentDataOffset; - this.#seekHead!.data[1]!.data[1]!.data = - this.offsets.get(this.#segmentInfo!)! - this.#segmentDataOffset; - this.#seekHead!.data[2]!.data[1]!.data = - this.offsets.get(this.#tracksElement!)! - this.#segmentDataOffset; + this.seekHead!.data[0]!.data[1]!.data = + this.offsets.get(this.cues)! - this.segmentDataOffset; + this.seekHead!.data[1]!.data[1]!.data = + this.offsets.get(this.segmentInfo!)! - this.segmentDataOffset; + this.seekHead!.data[2]!.data[1]!.data = + this.offsets.get(this.tracksElement!)! - this.segmentDataOffset; - this.#writer.seek(this.offsets.get(this.#seekHead!)!); - this.writeEBML(this.#seekHead!); + this.writer.seek(this.offsets.get(this.seekHead!)!); + this.writeEBML(this.seekHead!); - this.#writer.seek(endPos); + this.writer.seek(endPos); } } } \ No newline at end of file diff --git a/src/subtitles.ts b/src/subtitles.ts index 8badafb..c1ff2ae 100644 --- a/src/subtitles.ts +++ b/src/subtitles.ts @@ -26,12 +26,12 @@ const preambleStartRegex = /^WEBVTT(.|\n)*?\n{2}/; export const inlineTimestampRegex = /<(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})>/g; export class SubtitleParser { - #options: SubtitleParserOptions; - #preambleText: string | null = null; - #preambleEmitted = false; + private options: SubtitleParserOptions; + private preambleText: string | null = null; + private preambleEmitted = false; constructor(options: SubtitleParserOptions) { - this.#options = options; + this.options = options; } parse(text: string) { @@ -40,10 +40,10 @@ export class SubtitleParser { cueBlockHeaderRegex.lastIndex = 0; let match: RegExpMatchArray | null; - if (!this.#preambleText) { + if (!this.preambleText) { if (!preambleStartRegex.test(text)) { let error = new Error('WebVTT preamble incorrect.'); - this.#options.error(error); + this.options.error(error); throw error; } @@ -52,11 +52,11 @@ export class SubtitleParser { if (!preamble) { let error = new Error('No WebVTT preamble provided.'); - this.#options.error(error); + this.options.error(error); throw error; } - this.#preambleText = preamble; + this.preambleText = preamble; if (match) { text = text.slice(match.index); @@ -92,14 +92,14 @@ export class SubtitleParser { }; let meta: SubtitleMetadata = {}; - if (!this.#preambleEmitted) { + if (!this.preambleEmitted) { meta.config = { - description: this.#preambleText + description: this.preambleText }; - this.#preambleEmitted = true; + this.preambleEmitted = true; } - this.#options.output(cue, meta); + this.options.output(cue, meta); } } } diff --git a/src/writer.ts b/src/writer.ts index 5bd384e..1cfe6ae 100644 --- a/src/writer.ts +++ b/src/writer.ts @@ -21,58 +21,58 @@ export abstract class Writer { * written to the ArrayBufferTarget once the muxing finishes. */ export class ArrayBufferTargetWriter extends Writer { - #pos = 0; - #target: ArrayBufferTarget; - #buffer = new ArrayBuffer(2**16); - #bytes = new Uint8Array(this.#buffer); - #maxPos = 0; + private pos = 0; + private target: ArrayBufferTarget; + private buffer = new ArrayBuffer(2**16); + private bytes = new Uint8Array(this.buffer); + private maxPos = 0; constructor(target: ArrayBufferTarget) { super(); - this.#target = target; + this.target = target; } - #ensureSize(size: number) { - let newLength = this.#buffer.byteLength; + private ensureSize(size: number) { + let newLength = this.buffer.byteLength; while (newLength < size) newLength *= 2; - if (newLength === this.#buffer.byteLength) return; + if (newLength === this.buffer.byteLength) return; let newBuffer = new ArrayBuffer(newLength); let newBytes = new Uint8Array(newBuffer); - newBytes.set(this.#bytes, 0); + newBytes.set(this.bytes, 0); - this.#buffer = newBuffer; - this.#bytes = newBytes; + this.buffer = newBuffer; + this.bytes = newBytes; } write(data: Uint8Array) { - this.#ensureSize(this.#pos + data.byteLength); + this.ensureSize(this.pos + data.byteLength); - this.#bytes.set(data, this.#pos); - this.#pos += data.byteLength; + this.bytes.set(data, this.pos); + this.pos += data.byteLength; - this.#maxPos = Math.max(this.#maxPos, this.#pos); + this.maxPos = Math.max(this.maxPos, this.pos); } seek(newPos: number) { - this.#pos = newPos; + this.pos = newPos; } getPos() { - return this.#pos; + return this.pos; } flush() {} finalize() { - this.#ensureSize(this.#pos); - this.#target.buffer = this.#buffer.slice(0, Math.max(this.#maxPos, this.#pos)); + this.ensureSize(this.pos); + this.target.buffer = this.buffer.slice(0, Math.max(this.maxPos, this.pos)); } getSlice(start: number, end: number) { - return this.#bytes.slice(start, end); + return this.bytes.slice(start, end); } } @@ -81,45 +81,45 @@ export class ArrayBufferTargetWriter extends Writer { * last flush. This is useful for streaming applications, like piping the output to disk. */ export class StreamTargetWriter extends Writer { - #pos = 0; - #target: StreamTarget; - #sections: { + private pos = 0; + private target: StreamTarget; + private sections: { data: Uint8Array, start: number }[] = []; - #lastFlushEnd = 0; + private lastFlushEnd = 0; constructor(target: StreamTarget) { super(); - this.#target = target; + this.target = target; } write(data: Uint8Array) { - this.#sections.push({ + this.sections.push({ data: data.slice(), - start: this.#pos + start: this.pos }); - this.#pos += data.byteLength; + this.pos += data.byteLength; } seek(newPos: number) { - this.#pos = newPos; + this.pos = newPos; } getPos() { - return this.#pos; + return this.pos; } flush() { - if (this.#sections.length === 0) return; + if (this.sections.length === 0) return; let chunks: { start: number, size: number, data?: Uint8Array }[] = []; - let sorted = [...this.#sections].sort((a, b) => a.start - b.start); + let sorted = [...this.sections].sort((a, b) => a.start - b.start); chunks.push({ start: sorted[0]!.start, @@ -145,22 +145,22 @@ export class StreamTargetWriter extends Writer { chunk.data = new Uint8Array(chunk.size); // Make sure to write the data in the correct order for correct overwriting - for (let section of this.#sections) { + for (let section of this.sections) { // Check if the section is in the chunk if (chunk.start <= section.start && section.start < chunk.start + chunk.size) { chunk.data.set(section.data, section.start - chunk.start); } } - if (this.ensureMonotonicity && chunk.start !== this.#lastFlushEnd) { + if (this.ensureMonotonicity && chunk.start !== this.lastFlushEnd) { throw new Error('Internal error: Monotonicity violation.'); } - this.#target.options.onData?.(chunk.data, chunk.start); - this.#lastFlushEnd = chunk.start + chunk.data.byteLength; + this.target.options.onData?.(chunk.data, chunk.start); + this.lastFlushEnd = chunk.start + chunk.data.byteLength; } - this.#sections.length = 0; + this.sections.length = 0; } finalize() {} @@ -186,51 +186,51 @@ interface ChunkSection { * size, which is when it is piped to the StreamTarget. This is helpful for reducing the total amount of writes. */ export class ChunkedStreamTargetWriter extends Writer { - #pos = 0; - #target: StreamTarget; - #chunkSize: number; + private pos = 0; + private target: StreamTarget; + private chunkSize: number; /** * The data is divided up into fixed-size chunks, whose contents are first filled in RAM and then flushed out. * A chunk is flushed if all of its contents have been written. */ - #chunks: Chunk[] = []; - #lastFlushEnd = 0; + private chunks: Chunk[] = []; + private lastFlushEnd = 0; constructor(target: StreamTarget) { super(); - this.#target = target; - this.#chunkSize = target.options?.chunkSize ?? DEFAULT_CHUNK_SIZE; + this.target = target; + this.chunkSize = target.options?.chunkSize ?? DEFAULT_CHUNK_SIZE; - if (!Number.isInteger(this.#chunkSize) || this.#chunkSize < 2**10) { + if (!Number.isInteger(this.chunkSize) || this.chunkSize < 2**10) { throw new Error('Invalid StreamTarget options: chunkSize must be an integer not smaller than 1024.'); } } write(data: Uint8Array) { - this.#writeDataIntoChunks(data, this.#pos); - this.#flushChunks(); + this.writeDataIntoChunks(data, this.pos); + this.flushChunks(); - this.#pos += data.byteLength; + this.pos += data.byteLength; } seek(newPos: number) { - this.#pos = newPos; + this.pos = newPos; } getPos() { - return this.#pos; + return this.pos; } - #writeDataIntoChunks(data: Uint8Array, position: number) { + private writeDataIntoChunks(data: Uint8Array, position: number) { // First, find the chunk to write the data into, or create one if none exists - let chunkIndex = this.#chunks.findIndex(x => x.start <= position && position < x.start + this.#chunkSize); - if (chunkIndex === -1) chunkIndex = this.#createChunk(position); - let chunk = this.#chunks[chunkIndex]!; + let chunkIndex = this.chunks.findIndex(x => x.start <= position && position < x.start + this.chunkSize); + if (chunkIndex === -1) chunkIndex = this.createChunk(position); + let chunk = this.chunks[chunkIndex]!; // Figure out how much to write to the chunk, and then write to the chunk let relativePosition = position - chunk.start; - let toWrite = data.subarray(0, Math.min(this.#chunkSize - relativePosition, data.byteLength)); + let toWrite = data.subarray(0, Math.min(this.chunkSize - relativePosition, data.byteLength)); chunk.data.set(toWrite, relativePosition); // Create a section describing the region of data that was just written to @@ -238,29 +238,29 @@ export class ChunkedStreamTargetWriter extends Writer { start: relativePosition, end: relativePosition + toWrite.byteLength }; - this.#insertSectionIntoChunk(chunk, section); + this.insertSectionIntoChunk(chunk, section); // Queue chunk for flushing to target if it has been fully written to - if (chunk.written[0]!.start === 0 && chunk.written[0]!.end === this.#chunkSize) { + if (chunk.written[0]!.start === 0 && chunk.written[0]!.end === this.chunkSize) { chunk.shouldFlush = true; } // Make sure we don't hold too many chunks in memory at once to keep memory usage down - if (this.#chunks.length > MAX_CHUNKS_AT_ONCE) { + if (this.chunks.length > MAX_CHUNKS_AT_ONCE) { // Flush all but the last chunk - for (let i = 0; i < this.#chunks.length-1; i++) { - this.#chunks[i]!.shouldFlush = true; + for (let i = 0; i < this.chunks.length-1; i++) { + this.chunks[i]!.shouldFlush = true; } - this.#flushChunks(); + this.flushChunks(); } // If the data didn't fit in one chunk, recurse with the remaining datas if (toWrite.byteLength < data.byteLength) { - this.#writeDataIntoChunks(data.subarray(toWrite.byteLength), position + toWrite.byteLength); + this.writeDataIntoChunks(data.subarray(toWrite.byteLength), position + toWrite.byteLength); } } - #insertSectionIntoChunk(chunk: Chunk, section: ChunkSection) { + private insertSectionIntoChunk(chunk: Chunk, section: ChunkSection) { let low = 0; let high = chunk.written.length - 1; let index = -1; @@ -288,37 +288,37 @@ export class ChunkedStreamTargetWriter extends Writer { } } - #createChunk(includesPosition: number) { - let start = Math.floor(includesPosition / this.#chunkSize) * this.#chunkSize; + private createChunk(includesPosition: number) { + let start = Math.floor(includesPosition / this.chunkSize) * this.chunkSize; let chunk: Chunk = { start, - data: new Uint8Array(this.#chunkSize), + data: new Uint8Array(this.chunkSize), written: [], shouldFlush: false }; - this.#chunks.push(chunk); - this.#chunks.sort((a, b) => a.start - b.start); + this.chunks.push(chunk); + this.chunks.sort((a, b) => a.start - b.start); - return this.#chunks.indexOf(chunk); + return this.chunks.indexOf(chunk); } - #flushChunks(force = false) { - for (let i = 0; i < this.#chunks.length; i++) { - let chunk = this.#chunks[i]!; + private flushChunks(force = false) { + for (let i = 0; i < this.chunks.length; i++) { + let chunk = this.chunks[i]!; if (!chunk.shouldFlush && !force) continue; for (let section of chunk.written) { - if (this.ensureMonotonicity && chunk.start + section.start !== this.#lastFlushEnd) { + if (this.ensureMonotonicity && chunk.start + section.start !== this.lastFlushEnd) { throw new Error('Internal error: Monotonicity violation.'); } - this.#target.options.onData?.( + this.target.options.onData?.( chunk.data.subarray(section.start, section.end), chunk.start + section.start ); - this.#lastFlushEnd = chunk.start + section.end; + this.lastFlushEnd = chunk.start + section.end; } - this.#chunks.splice(i--, 1); + this.chunks.splice(i--, 1); } } @@ -327,7 +327,7 @@ export class ChunkedStreamTargetWriter extends Writer { } finalize() { - this.#flushChunks(true); + this.flushChunks(true); } }