From 78505bca8244c533034880b49a1845b7f4ec0ac3 Mon Sep 17 00:00:00 2001 From: Vanilagy <1696106+Vanilagy@users.noreply.github.com> Date: Mon, 25 Nov 2024 21:18:44 +0100 Subject: [PATCH] Add api-extractor and add required exports --- .gitignore | 4 +- api-extractor.json | 33 ++ api_sketch.ts => api-sketch.ts | 0 append-namespace.mjs | 3 + dist/metamuxer.d.ts | 202 +++++++ dist/metamuxer.js | 338 ++++++------ dist/metamuxer.min.js | 10 +- dist/metamuxer.min.mjs | 10 +- dist/metamuxer.mjs | 338 ++++++------ package-lock.json | 505 ++++++++++++++++++ package.json | 13 +- src/codec.ts | 1 + src/index.ts | 7 +- .../{isobmff_boxes.ts => isobmff-boxes.ts} | 14 +- .../{isobmff_muxer.ts => isobmff-muxer.ts} | 22 +- .../{matroska_muxer.ts => matroska-muxer.ts} | 22 +- src/misc.ts | 1 + src/muxer.ts | 6 +- src/{output_format.ts => output-format.ts} | 17 +- src/output.ts | 77 +-- src/source.ts | 269 ++++++---- src/target.ts | 16 +- tsconfig.json | 6 +- 23 files changed, 1423 insertions(+), 491 deletions(-) create mode 100644 api-extractor.json rename api_sketch.ts => api-sketch.ts (100%) create mode 100644 append-namespace.mjs create mode 100644 dist/metamuxer.d.ts rename src/isobmff/{isobmff_boxes.ts => isobmff-boxes.ts} (98%) rename src/isobmff/{isobmff_muxer.ts => isobmff-muxer.ts} (97%) rename src/matroska/{matroska_muxer.ts => matroska-muxer.ts} (98%) rename src/{output_format.ts => output-format.ts} (76%) diff --git a/.gitignore b/.gitignore index b512c09..9244b7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -node_modules \ No newline at end of file +/node_modules +/build +tsdoc-metadata.json \ No newline at end of file diff --git a/api-extractor.json b/api-extractor.json new file mode 100644 index 0000000..6ebda5d --- /dev/null +++ b/api-extractor.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "mainEntryPointFilePath": "build/index.d.ts", + "bundledPackages": [], + "compiler": {}, + "apiReport": { + "enabled": false + }, + "docModel": { + "enabled": false + }, + "dtsRollup": { + "enabled": true + }, + "tsdocMetadata": {}, + "messages": { + "compilerMessageReporting": { + "default": { + "logLevel": "warning" + } + }, + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + } + }, + "tsdocMessageReporting": { + "default": { + "logLevel": "warning" + } + } + } +} diff --git a/api_sketch.ts b/api-sketch.ts similarity index 100% rename from api_sketch.ts rename to api-sketch.ts diff --git a/append-namespace.mjs b/append-namespace.mjs new file mode 100644 index 0000000..78d29d3 --- /dev/null +++ b/append-namespace.mjs @@ -0,0 +1,3 @@ +import { appendFileSync } from 'fs'; + +appendFileSync('dist/metamuxer.d.ts', '\nexport as namespace Metamuxer;'); \ No newline at end of file diff --git a/dist/metamuxer.d.ts b/dist/metamuxer.d.ts new file mode 100644 index 0000000..925f470 --- /dev/null +++ b/dist/metamuxer.d.ts @@ -0,0 +1,202 @@ +/** @public */ +export declare class ArrayBufferTarget extends Target { + buffer: ArrayBuffer | null; +} + +/** @public */ +export declare const AUDIO_CODECS: readonly ["aac", "opus"]; + +/** @public */ +export declare class AudioBufferSource extends AudioSource { + constructor(codecConfig: AudioCodecConfig); + digest(audioBuffer: AudioBuffer): void; +} + +/** @public */ +export declare type AudioCodec = typeof AUDIO_CODECS[number]; + +/** @public */ +export declare type AudioCodecConfig = { + codec: AudioCodec; + bitrate: number; +}; + +/** @public */ +export declare class AudioDataSource extends AudioSource { + constructor(codecConfig: AudioCodecConfig); + digest(audioData: AudioData): void; +} + +/** @public */ +export declare abstract class AudioSource extends MediaSource_2 { + constructor(codec: AudioCodec); +} + +/** @public */ +export declare type AudioTrackMetadata = {}; + +/** @public */ +export declare class CanvasSource extends VideoSource { + constructor(canvas: HTMLCanvasElement, codecConfig: VideoCodecConfig); + digest(timestamp: number, duration?: number): void; +} + +/** @public */ +export declare class EncodedAudioChunkSource extends AudioSource { + constructor(codec: AudioCodec); + digest(chunk: EncodedAudioChunk, meta?: EncodedAudioChunkMetadata): void; +} + +/** @public */ +export declare class EncodedVideoChunkSource extends VideoSource { + constructor(codec: VideoCodec); + digest(chunk: EncodedVideoChunk, meta?: EncodedVideoChunkMetadata): void; +} + +/** @public */ +export declare class FileSystemWritableFileStreamTarget extends Target { + stream: FileSystemWritableFileStream; + options?: { + chunkSize?: number; + } | undefined; + constructor(stream: FileSystemWritableFileStream, options?: { + chunkSize?: number; + } | undefined); +} + +/** @public */ +declare abstract class MediaSource_2 { + close(): void; +} +export { MediaSource_2 as MediaSource } + +/** @public */ +export declare class MediaStreamAudioTrackSource extends AudioSource { + _offsetTimestamps: boolean; + constructor(track: MediaStreamAudioTrack, codecConfig: AudioCodecConfig); +} + +/** @public */ +export declare class MediaStreamVideoTrackSource extends VideoSource { + constructor(track: MediaStreamVideoTrack, codecConfig: VideoCodecConfig); +} + +/** @public */ +export declare class MkvOutputFormat extends OutputFormat { + options: { + streamable?: boolean; + }; + constructor(options?: { + streamable?: boolean; + }); +} + +/** @public */ +export declare class Mp4OutputFormat extends OutputFormat { + options: { + fastStart?: false | 'in-memory' | 'fragmented'; + }; + constructor(options?: { + fastStart?: false | 'in-memory' | 'fragmented'; + }); +} + +/** @public */ +export declare class Output { + constructor(options: OutputOptions); + addVideoTrack(source: VideoSource, metadata?: VideoTrackMetadata): void; + addAudioTrack(source: AudioSource, metadata?: AudioTrackMetadata): void; + addSubtitleTrack(source: SubtitleSource, metadata?: SubtitleTrackMetadata): void; + start(): void; + finalize(): Promise; +} + +/** @public */ +export declare abstract class OutputFormat { +} + +/** @public */ +export declare type OutputOptions = { + format: OutputFormat; + target: Target; +}; + +/** @public */ +export declare class StreamTarget extends Target { + options: { + onData?: (data: Uint8Array, position: number) => void; + chunked?: boolean; + chunkSize?: number; + }; + constructor(options: { + onData?: (data: Uint8Array, position: number) => void; + chunked?: boolean; + chunkSize?: number; + }); +} + +/** @public */ +export declare const SUBTITLE_CODECS: readonly ["webvtt"]; + +/** @public */ +export declare type SubtitleCodec = typeof SUBTITLE_CODECS[number]; + +/** @public */ +export declare abstract class SubtitleSource extends MediaSource_2 { + constructor(codec: SubtitleCodec); +} + +/** @public */ +export declare type SubtitleTrackMetadata = {}; + +/** @public */ +export declare abstract class Target { + output: Output | null; +} + +/** @public */ +export declare class TextSubtitleSource extends SubtitleSource { + constructor(codec: SubtitleCodec); + digest(text: string): void; +} + +/** @public */ +export declare type TransformationMatrix = [number, number, number, number, number, number, number, number, number]; + +/** @public */ +export declare const VIDEO_CODECS: readonly ["avc", "hevc", "vp8", "vp9", "av1"]; + +/** @public */ +export declare type VideoCodec = typeof VIDEO_CODECS[number]; + +/** @public */ +export declare type VideoCodecConfig = { + codec: VideoCodec; + bitrate: number; + latencyMode?: VideoEncoderConfig['latencyMode']; +}; + +/** @public */ +export declare class VideoFrameSource extends VideoSource { + constructor(codecConfig: VideoCodecConfig); + digest(videoFrame: VideoFrame): void; +} + +/** @public */ +export declare abstract class VideoSource extends MediaSource_2 { + constructor(codec: VideoCodec); +} + +/** @public */ +export declare type VideoTrackMetadata = { + rotation?: 0 | 90 | 180 | 270 | TransformationMatrix; + frameRate?: number; +}; + +/** @public */ +export declare class WebMOutputFormat extends MkvOutputFormat { +} + +export { } + +export as namespace Metamuxer; \ No newline at end of file diff --git a/dist/metamuxer.js b/dist/metamuxer.js index 4d29cb1..e0054d4 100644 --- a/dist/metamuxer.js +++ b/dist/metamuxer.js @@ -21,22 +21,30 @@ var Metamuxer = (() => { // src/index.ts var src_exports = {}; __export(src_exports, { + AUDIO_CODECS: () => AUDIO_CODECS, ArrayBufferTarget: () => ArrayBufferTarget, AudioBufferSource: () => AudioBufferSource, AudioDataSource: () => AudioDataSource, + AudioSource: () => AudioSource, CanvasSource: () => CanvasSource, EncodedAudioChunkSource: () => EncodedAudioChunkSource, EncodedVideoChunkSource: () => EncodedVideoChunkSource, FileSystemWritableFileStreamTarget: () => FileSystemWritableFileStreamTarget, + MediaSource: () => MediaSource, MediaStreamAudioTrackSource: () => MediaStreamAudioTrackSource, MediaStreamVideoTrackSource: () => MediaStreamVideoTrackSource, MkvOutputFormat: () => MkvOutputFormat2, Mp4OutputFormat: () => Mp4OutputFormat, Output: () => Output, + OutputFormat: () => OutputFormat, + SUBTITLE_CODECS: () => SUBTITLE_CODECS, StreamTarget: () => StreamTarget, + SubtitleSource: () => SubtitleSource, Target: () => Target, TextSubtitleSource: () => TextSubtitleSource, + VIDEO_CODECS: () => VIDEO_CODECS, VideoFrameSource: () => VideoFrameSource, + VideoSource: () => VideoSource, WebMOutputFormat: () => WebMOutputFormat }); @@ -196,7 +204,7 @@ var Metamuxer = (() => { return hours.toString().padStart(2, "0") + ":" + minutes.toString().padStart(2, "0") + ":" + seconds.toString().padStart(2, "0") + "." + milliseconds.toString().padStart(3, "0"); }; - // src/isobmff/isobmff_boxes.ts + // src/isobmff/isobmff-boxes.ts var IsobmffBoxWriter = class { constructor(writer) { this.writer = writer; @@ -604,17 +612,17 @@ var Metamuxer = (() => { let sampleDescription; if (trackData.type === "video") { sampleDescription = videoSampleDescription( - VIDEO_CODEC_TO_BOX_NAME[trackData.track.source.codec], + VIDEO_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData ); } else if (trackData.type === "audio") { sampleDescription = soundSampleDescription( - AUDIO_CODEC_TO_BOX_NAME[trackData.track.source.codec], + AUDIO_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData ); } else if (trackData.type === "subtitle") { sampleDescription = subtitleSampleDescription( - SUBTITLE_CODEC_TO_BOX_NAME[trackData.track.source.codec], + SUBTITLE_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData ); } @@ -656,7 +664,7 @@ var Metamuxer = (() => { i16(65535) // Pre-defined ], [ - VIDEO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source.codec](trackData), + VIDEO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData), colorSpaceIsComplete(trackData.info.decoderConfig.colorSpace) ? colr(trackData) : null ]); var colr = (trackData) => box("colr", [ @@ -744,7 +752,7 @@ var Metamuxer = (() => { fixed_16_16(trackData.info.sampleRate) // Sample rate ], [ - AUDIO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source.codec](trackData) + AUDIO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData) ]); var esds = (trackData) => { let description = toUint8Array(trackData.info.decoderConfig.description ?? new ArrayBuffer(0)); @@ -821,7 +829,7 @@ var Metamuxer = (() => { u16(1) // Data reference index ], [ - SUBTITLE_CODEC_TO_CONFIGURATION_BOX[trackData.track.source.codec](trackData) + SUBTITLE_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData) ]); var vttC = (trackData) => box("vttC", [ ...textEncoder.encode(trackData.info.config.description) @@ -1119,12 +1127,12 @@ var Metamuxer = (() => { } timestampInfo = { timestampOffset: timestampInSeconds, - maxTimestamp: track.source.offsetTimestamps ? 0 : timestampInSeconds, - lastKeyFrameTimestamp: track.source.offsetTimestamps ? 0 : timestampInSeconds + maxTimestamp: track.source._offsetTimestamps ? 0 : timestampInSeconds, + lastKeyFrameTimestamp: track.source._offsetTimestamps ? 0 : timestampInSeconds }; this.trackTimestampInfo.set(track, timestampInfo); } - if (track.source.offsetTimestamps) { + if (track.source._offsetTimestamps) { timestampInSeconds -= timestampInfo.timestampOffset; } if (timestampInSeconds < 0) { @@ -1155,7 +1163,8 @@ var Metamuxer = (() => { super(...arguments); this.buffer = null; } - createWriter() { + /** @internal */ + _createWriter() { return new ArrayBufferTargetWriter(this); } }; @@ -1183,7 +1192,8 @@ var Metamuxer = (() => { throw new TypeError("options.chunkSize, when provided, must be a positive integer."); } } - createWriter() { + /** @internal */ + _createWriter() { return this.options.chunked ? new ChunkedStreamTargetWriter(this) : new StreamTargetWriter(this); } }; @@ -1204,7 +1214,8 @@ var Metamuxer = (() => { } } } - createWriter() { + /** @internal */ + _createWriter() { return new FileSystemWritableFileStreamTargetWriter(this); } }; @@ -1639,7 +1650,7 @@ var Metamuxer = (() => { } }; - // src/isobmff/isobmff_muxer.ts + // src/isobmff/isobmff-muxer.ts var GLOBAL_TIMESCALE = 1e3; var TIMESTAMP_OFFSET = 2082844800; var intoTimescale = (timeInSeconds, timescale, round = true) => { @@ -1651,7 +1662,7 @@ var Metamuxer = (() => { super(output); this.timestampsMustStartAtZero = true; this.#auxTarget = new ArrayBufferTarget(); - this.#auxWriter = this.#auxTarget.createWriter(); + this.#auxWriter = this.#auxTarget._createWriter(); this.#auxBoxWriter = new IsobmffBoxWriter(this.#auxWriter); this.#ftypSize = null; this.#mdat = null; @@ -1659,7 +1670,7 @@ var Metamuxer = (() => { this.#creationTime = Math.floor(Date.now() / 1e3) + TIMESTAMP_OFFSET; this.#finalizedChunks = []; this.#nextFragmentNumber = 1; - this.#writer = output.writer; + 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); @@ -1681,7 +1692,7 @@ var Metamuxer = (() => { #finalizedChunks; #nextFragmentNumber; start() { - const holdsAvc = this.output.tracks.some((x) => x.type === "video" && x.source.codec === "avc"); + const holdsAvc = this.output._tracks.some((x) => x.type === "video" && x.source._codec === "avc"); this.#boxWriter.writeBox(ftyp({ holdsAvc, fragmented: this.#fastStart === "fragmented" @@ -1817,7 +1828,7 @@ var Metamuxer = (() => { addSubtitleCue(track, cue, meta) { const trackData = this.#getSubtitleTrackData(track, meta); this.validateAndNormalizeTimestamp(trackData.track, 1e6 * cue.timestamp, true); - if (track.source.codec === "webvtt") { + if (track.source._codec === "webvtt") { trackData.cueQueue.push(cue); this.#processWebVTTCues(trackData, cue.timestamp); } else { @@ -2038,8 +2049,8 @@ var Metamuxer = (() => { } #interleaveSamples() { assert(this.#fastStart === "fragmented"); - for (const track of this.output.tracks) { - if (!track.source.closed && !this.#trackDatas.some((x) => x.track === track)) { + for (const track of this.output._tracks) { + if (!track.source._closed && !this.#trackDatas.some((x) => x.track === track)) { return; } } @@ -2048,7 +2059,7 @@ var Metamuxer = (() => { let trackWithMinTimestamp = null; let minTimestamp = Infinity; for (let trackData of this.#trackDatas) { - if (trackData.sampleQueue.length === 0 && !trackData.track.source.closed) { + if (trackData.sampleQueue.length === 0 && !trackData.track.source._closed) { break outer; } if (trackData.sampleQueue.length > 0 && trackData.sampleQueue[0].timestamp < minTimestamp) { @@ -2113,7 +2124,7 @@ var Metamuxer = (() => { } } onTrackClose(track) { - if (track.type === "subtitle" && track.source.codec === "webvtt") { + if (track.type === "subtitle" && track.source._codec === "webvtt") { let trackData = this.#trackDatas.find((x) => x.track === track); if (trackData) { this.#processWebVTTCues(trackData, Infinity); @@ -2126,7 +2137,7 @@ var Metamuxer = (() => { /** 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) { - if (trackData.type === "subtitle" && trackData.track.source.codec === "webvtt") { + if (trackData.type === "subtitle" && trackData.track.source._codec === "webvtt") { this.#processWebVTTCues(trackData, Infinity); } } @@ -2267,7 +2278,7 @@ var Metamuxer = (() => { } }; - // src/matroska/matroska_muxer.ts + // src/matroska/matroska-muxer.ts var MAX_CHUNK_LENGTH_MS = 2 ** 15; var APP_NAME = "https://github.com/Vanilagy/webm-muxer"; var SEGMENT_SIZE_BYTES = 6; @@ -2311,7 +2322,7 @@ var Metamuxer = (() => { this.#currentClusterMsTimestamp = null; this.#trackDatasInCurrentCluster = /* @__PURE__ */ new Set(); this.#duration = 0; - this.#writer = output.writer; + this.#writer = output._writer; this.#format = format; if (this.#format.options.streamable) { this.#writer.ensureMonotonicity = true; @@ -2473,15 +2484,15 @@ var Metamuxer = (() => { return; } if (track.type === "video") { - if (!["vp8", "vp9", "av1"].includes(track.source.codec)) { + if (!["vp8", "vp9", "av1"].includes(track.source._codec)) { throw new Error(`WebM only supports VP8, VP9 and AV1 as video codecs. Switching to MKV removes this restriction.`); } } else if (track.type === "audio") { - if (!["opus", "vorbis"].includes(track.source.codec)) { + if (!["opus", "vorbis"].includes(track.source._codec)) { throw new Error(`WebM only supports Opus and Vorbis as audio codecs. Switching to MKV removes this restriction.`); } } else if (track.type === "subtitle") { - if (track.source.codec !== "webvtt") { + if (track.source._codec !== "webvtt") { throw new Error(`WebM only supports WebVTT as subtitle codec. Switching to MKV removes this restriction.`); } } else { @@ -2552,7 +2563,7 @@ var Metamuxer = (() => { { id: 215 /* TrackNumber */, data: trackData.track.id }, { id: 29637 /* TrackUID */, data: trackData.track.id }, { id: 131 /* TrackType */, data: TRACK_TYPE_MAP[trackData.type] }, - { id: 134 /* CodecID */, data: CODEC_STRING_MAP[trackData.track.source.codec] }, + { id: 134 /* CodecID */, data: CODEC_STRING_MAP[trackData.track.source._codec] }, ...trackData.type === "video" ? [ trackData.info.decoderConfig.description ? { id: 25506 /* CodecPrivate */, data: toUint8Array(trackData.info.decoderConfig.description) } : null, trackData.track.metadata.frameRate ? { id: 2352003 /* DefaultDuration */, data: 1e9 / trackData.track.metadata.frameRate } : null, @@ -2685,7 +2696,7 @@ var Metamuxer = (() => { 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); + if (track.source._codec === "vp9") this.#fixVP9ColorSpace(trackData, videoChunk); trackData.chunkQueue.push(videoChunk); this.#interleaveChunks(); } @@ -2718,8 +2729,8 @@ ${cue.notes ?? ""}`; this.#interleaveChunks(); } #interleaveChunks() { - for (const track of this.output.tracks) { - if (!track.source.closed && !this.#trackDatas.some((x) => x.track === track)) { + for (const track of this.output._tracks) { + if (!track.source._closed && !this.#trackDatas.some((x) => x.track === track)) { return; } } @@ -2728,7 +2739,7 @@ ${cue.notes ?? ""}`; let trackWithMinTimestamp = null; let minTimestamp = Infinity; for (let trackData of this.#trackDatas) { - if (trackData.chunkQueue.length === 0 && !trackData.track.source.closed) { + if (trackData.chunkQueue.length === 0 && !trackData.track.source._closed) { break outer; } if (trackData.chunkQueue.length > 0 && trackData.chunkQueue[0].timestamp < minTimestamp) { @@ -2794,7 +2805,7 @@ ${cue.notes ?? ""}`; } let msTimestamp = Math.floor(1e3 * chunk.timestamp); const keyFrameQueuedEverywhere = this.#trackDatas.every((otherTrackData) => { - if (otherTrackData.track.source.closed) { + if (otherTrackData.track.source._closed) { return true; } if (trackData === otherTrackData) { @@ -2922,7 +2933,7 @@ ${cue.notes ?? ""}`; } }; - // src/output_format.ts + // src/output-format.ts var OutputFormat = class { }; var Mp4OutputFormat = class extends OutputFormat { @@ -2936,7 +2947,8 @@ ${cue.notes ?? ""}`; super(); this.options = options; } - createMuxer(output) { + /** @internal */ + _createMuxer(output) { return new IsobmffMuxer(output, this); } }; @@ -2951,7 +2963,8 @@ ${cue.notes ?? ""}`; super(); this.options = options; } - createMuxer(output) { + /** @internal */ + _createMuxer(output) { return new MatroskaMuxer(output, this); } }; @@ -2964,55 +2977,60 @@ ${cue.notes ?? ""}`; var SUBTITLE_CODECS = ["webvtt"]; var MediaSource = class { constructor() { - this.connectedTrack = null; - this.closed = false; - this.offsetTimestamps = false; + /** @internal */ + this._connectedTrack = null; + /** @internal */ + this._closed = false; + /** @internal */ + this._offsetTimestamps = false; } - // TODO this is also just internal: - ensureValidDigest() { - if (!this.connectedTrack) { + /** @internal */ + _ensureValidDigest() { + if (!this._connectedTrack) { throw new Error("Cannot call digest without connecting the source to an output track."); } - if (!this.connectedTrack.output.started) { + if (!this._connectedTrack.output._started) { throw new Error("Cannot call digest before output has been started."); } - if (this.connectedTrack.output.finalizing) { + if (this._connectedTrack.output._finalizing) { throw new Error("Cannot call digest after output has started finalizing."); } - if (this.closed) { + if (this._closed) { throw new Error("Cannot call digest after source has been closed."); } } - // TODO: These are should not be called from the outside lib - start() { + /** @internal */ + _start() { } - async flush() { + /** @internal */ + async _flush() { } close() { - if (this.closed) { + if (this._closed) { throw new Error("Source already closed."); } - if (!this.connectedTrack) { + if (!this._connectedTrack) { throw new Error("Cannot call close without connecting the source to an output track."); } - if (!this.connectedTrack.output.started) { + if (!this._connectedTrack.output._started) { throw new Error("Cannot call close before output has been started."); } - this.closed = true; - if (this.connectedTrack.output.finalizing) { + this._closed = true; + if (this._connectedTrack.output._finalizing) { return; } - this.connectedTrack.output.muxer.onTrackClose(this.connectedTrack); + this._connectedTrack.output._muxer.onTrackClose(this._connectedTrack); } }; var VideoSource = class extends MediaSource { constructor(codec) { super(); - this.connectedTrack = null; + /** @internal */ + this._connectedTrack = null; if (!VIDEO_CODECS.includes(codec)) { throw new TypeError(`Invalid video codec '${codec}'. Must be one of: ${VIDEO_CODECS.join(", ")}.`); } - this.codec = codec; + this._codec = codec; } }; var EncodedVideoChunkSource = class extends VideoSource { @@ -3023,8 +3041,8 @@ ${cue.notes ?? ""}`; if (!(chunk instanceof EncodedVideoChunk)) { throw new TypeError("chunk must be an EncodedVideoChunk."); } - this.ensureValidDigest(); - this.connectedTrack?.output.muxer.addEncodedVideoChunk(this.connectedTrack, chunk, meta); + this._ensureValidDigest(); + this._connectedTrack?.output._muxer.addEncodedVideoChunk(this._connectedTrack, chunk, meta); } }; var KEY_FRAME_INTERVAL = 5; @@ -3053,7 +3071,7 @@ ${cue.notes ?? ""}`; validateVideoCodecConfig(codecConfig); } digest(videoFrame) { - this.source.ensureValidDigest(); + this.source._ensureValidDigest(); if (this.lastWidth !== null && this.lastHeight !== null) { if (videoFrame.codedWidth !== this.lastWidth || videoFrame.codedHeight !== this.lastHeight) { throw new Error(`Video frame size must remain constant. Expected ${this.lastWidth}x${this.lastHeight}, got ${videoFrame.codedWidth}x${videoFrame.codedHeight}.`); @@ -3073,7 +3091,7 @@ ${cue.notes ?? ""}`; return; } this.encoder = new VideoEncoder({ - output: (chunk, meta) => this.source.connectedTrack?.output.muxer.addEncodedVideoChunk(this.source.connectedTrack, chunk, meta), + output: (chunk, meta) => this.source._connectedTrack?.output._muxer.addEncodedVideoChunk(this.source._connectedTrack, chunk, meta), error: (error) => console.error("Video encode error:", error) }); this.encoder.configure({ @@ -3081,7 +3099,7 @@ ${cue.notes ?? ""}`; width: videoFrame.codedWidth, height: videoFrame.codedHeight, bitrate: this.codecConfig.bitrate, - framerate: this.source.connectedTrack?.metadata.frameRate, + framerate: this.source._connectedTrack?.metadata.frameRate, latencyMode: this.codecConfig.latencyMode }); } @@ -3092,16 +3110,17 @@ ${cue.notes ?? ""}`; var VideoFrameSource = class extends VideoSource { constructor(codecConfig) { super(codecConfig.codec); - this.encoder = new VideoEncoderWrapper(this, codecConfig); + this._encoder = new VideoEncoderWrapper(this, codecConfig); } digest(videoFrame) { if (!(videoFrame instanceof VideoFrame)) { throw new TypeError("videoFrame must be a VideoFrame."); } - this.encoder.digest(videoFrame); + this._encoder.digest(videoFrame); } - flush() { - return this.encoder.flush(); + /** @internal */ + _flush() { + return this._encoder.flush(); } }; var CanvasSource = class extends VideoSource { @@ -3110,8 +3129,8 @@ ${cue.notes ?? ""}`; throw new TypeError("canvas must be an HTMLCanvasElement."); } super(codecConfig.codec); - this.canvas = canvas; - this.encoder = new VideoEncoderWrapper(this, codecConfig); + this._encoder = new VideoEncoderWrapper(this, codecConfig); + this._canvas = canvas; } digest(timestamp, duration = 0) { if (!Number.isFinite(timestamp) || timestamp < 0) { @@ -3120,16 +3139,17 @@ ${cue.notes ?? ""}`; if (!Number.isFinite(duration) || duration < 0) { throw new TypeError("duration must be a non-negative number."); } - const frame = new VideoFrame(this.canvas, { + const frame = new VideoFrame(this._canvas, { timestamp: Math.round(1e6 * timestamp), duration: Math.round(1e6 * duration), alpha: "discard" }); - this.encoder.digest(frame); + this._encoder.digest(frame); frame.close(); } - flush() { - return this.encoder.flush(); + /** @internal */ + _flush() { + return this._encoder.flush(); } }; var MediaStreamVideoTrackSource = class extends VideoSource { @@ -3137,44 +3157,53 @@ ${cue.notes ?? ""}`; if (!(track instanceof MediaStreamTrack) || track.kind !== "video") { throw new TypeError("track must be a video MediaStreamTrack."); } + codecConfig = { + ...codecConfig, + latencyMode: "realtime" + }; super(codecConfig.codec); - this.track = track; - this.abortController = null; - this.offsetTimestamps = true; - this.encoder = new VideoEncoderWrapper(this, codecConfig); + /** @internal */ + this._abortController = null; + /** @internal */ + this._offsetTimestamps = true; + this._encoder = new VideoEncoderWrapper(this, codecConfig); + this._track = track; } - start() { - this.abortController = new AbortController(); - const processor = new MediaStreamTrackProcessor({ track: this.track }); + /** @internal */ + _start() { + this._abortController = new AbortController(); + const processor = new MediaStreamTrackProcessor({ track: this._track }); const consumer = new WritableStream({ write: (videoFrame) => { - this.encoder.digest(videoFrame); + this._encoder.digest(videoFrame); videoFrame.close(); } }); processor.readable.pipeTo(consumer, { - signal: this.abortController.signal + signal: this._abortController.signal }).catch((err) => { if (err instanceof DOMException && err.name === "AbortError") return; console.error("Pipe error:", err); }); } - async flush() { - if (this.abortController) { - this.abortController.abort(); - this.abortController = null; + /** @internal */ + async _flush() { + if (this._abortController) { + this._abortController.abort(); + this._abortController = null; } - await this.encoder.flush(); + await this._encoder.flush(); } }; var AudioSource = class extends MediaSource { constructor(codec) { super(); - this.connectedTrack = null; + /** @internal */ + this._connectedTrack = null; if (!AUDIO_CODECS.includes(codec)) { throw new TypeError(`Invalid audio codec '${codec}'. Must be one of: ${AUDIO_CODECS.join(", ")}.`); } - this.codec = codec; + this._codec = codec; } }; var EncodedAudioChunkSource = class extends AudioSource { @@ -3185,8 +3214,8 @@ ${cue.notes ?? ""}`; if (!(chunk instanceof EncodedAudioChunk)) { throw new TypeError("chunk must be an EncodedAudioChunk."); } - this.ensureValidDigest(); - this.connectedTrack?.output.muxer.addEncodedAudioChunk(this.connectedTrack, chunk, meta); + this._ensureValidDigest(); + this._connectedTrack?.output._muxer.addEncodedAudioChunk(this._connectedTrack, chunk, meta); } }; var validateAudioCodecConfig = (config) => { @@ -3210,7 +3239,7 @@ ${cue.notes ?? ""}`; validateAudioCodecConfig(codecConfig); } digest(audioData) { - this.source.ensureValidDigest(); + this.source._ensureValidDigest(); if (this.lastNumberOfChannels !== null && this.lastSampleRate !== null) { if (audioData.numberOfChannels !== this.lastNumberOfChannels || audioData.sampleRate !== this.lastSampleRate) { throw new Error(`Audio parameters must remain constant. Expected ${this.lastNumberOfChannels} channels at ${this.lastSampleRate} Hz, got ${audioData.numberOfChannels} channels at ${audioData.sampleRate} Hz.`); @@ -3228,7 +3257,7 @@ ${cue.notes ?? ""}`; return; } this.encoder = new AudioEncoder({ - output: (chunk, meta) => this.source.connectedTrack?.output.muxer.addEncodedAudioChunk(this.source.connectedTrack, chunk, meta), + output: (chunk, meta) => this.source._connectedTrack?.output._muxer.addEncodedAudioChunk(this.source._connectedTrack, chunk, meta), error: (error) => console.error("Audio encode error:", error) }); this.encoder.configure({ @@ -3245,23 +3274,25 @@ ${cue.notes ?? ""}`; var AudioDataSource = class extends AudioSource { constructor(codecConfig) { super(codecConfig.codec); - this.encoder = new AudioEncoderWrapper(this, codecConfig); + this._encoder = new AudioEncoderWrapper(this, codecConfig); } digest(audioData) { if (!(audioData instanceof AudioData)) { throw new TypeError("audioData must be an AudioData."); } - this.encoder.digest(audioData); + this._encoder.digest(audioData); } - flush() { - return this.encoder.flush(); + /** @internal */ + _flush() { + return this._encoder.flush(); } }; var AudioBufferSource = class extends AudioSource { constructor(codecConfig) { super(codecConfig.codec); - this.accumulatedFrameCount = 0; - this.encoder = new AudioEncoderWrapper(this, codecConfig); + /** @internal */ + this._accumulatedFrameCount = 0; + this._encoder = new AudioEncoderWrapper(this, codecConfig); } digest(audioBuffer) { if (!(audioBuffer instanceof AudioBuffer)) { @@ -3280,15 +3311,16 @@ ${cue.notes ?? ""}`; sampleRate, numberOfFrames, numberOfChannels, - timestamp: Math.round(1e6 * this.accumulatedFrameCount / sampleRate), + timestamp: Math.round(1e6 * this._accumulatedFrameCount / sampleRate), data }); - this.encoder.digest(audioData); + this._encoder.digest(audioData); audioData.close(); - this.accumulatedFrameCount += numberOfFrames; + this._accumulatedFrameCount += numberOfFrames; } - flush() { - return this.encoder.flush(); + /** @internal */ + _flush() { + return this._encoder.flush(); } }; var MediaStreamAudioTrackSource = class extends AudioSource { @@ -3297,51 +3329,55 @@ ${cue.notes ?? ""}`; throw new TypeError("track must be an audio MediaStreamTrack."); } super(codecConfig.codec); - this.track = track; - this.abortController = null; - this.offsetTimestamps = true; - this.encoder = new AudioEncoderWrapper(this, codecConfig); + /** @internal */ + this._abortController = null; + this._offsetTimestamps = true; + this._encoder = new AudioEncoderWrapper(this, codecConfig); + this._track = track; } - start() { - this.abortController = new AbortController(); - const processor = new MediaStreamTrackProcessor({ track: this.track }); + /** @internal */ + _start() { + this._abortController = new AbortController(); + const processor = new MediaStreamTrackProcessor({ track: this._track }); const consumer = new WritableStream({ write: (audioData) => { - this.encoder.digest(audioData); + this._encoder.digest(audioData); audioData.close(); } }); processor.readable.pipeTo(consumer, { - signal: this.abortController.signal + signal: this._abortController.signal }).catch((err) => { if (err instanceof DOMException && err.name === "AbortError") return; console.error("Pipe error:", err); }); } - async flush() { - if (this.abortController) { - this.abortController.abort(); - this.abortController = null; + /** @internal */ + async _flush() { + if (this._abortController) { + this._abortController.abort(); + this._abortController = null; } - await this.encoder.flush(); + await this._encoder.flush(); } }; var SubtitleSource = class extends MediaSource { constructor(codec) { super(); - this.connectedTrack = null; + /** @internal */ + this._connectedTrack = null; if (!SUBTITLE_CODECS.includes(codec)) { throw new TypeError(`Invalid subtitle codec '${codec}'. Must be one of: ${SUBTITLE_CODECS.join(", ")}.`); } - this.codec = codec; + this._codec = codec; } }; var TextSubtitleSource = class extends SubtitleSource { constructor(codec) { super(codec); - this.parser = new SubtitleParser({ + this._parser = new SubtitleParser({ codec, - output: (cue, metadata) => this.connectedTrack?.output.muxer.addSubtitleCue(this.connectedTrack, cue, metadata), + output: (cue, metadata) => this._connectedTrack?.output._muxer.addSubtitleCue(this._connectedTrack, cue, metadata), error: (error) => console.error("Subtitle parse error:", error) }); } @@ -3349,17 +3385,20 @@ ${cue.notes ?? ""}`; if (typeof text !== "string") { throw new TypeError("text must be a string."); } - this.ensureValidDigest(); - this.parser.parse(text); + this._ensureValidDigest(); + this._parser.parse(text); } }; // src/output.ts var Output = class { constructor(options) { - this.tracks = []; - this.started = false; - this.finalizing = false; + /** @internal */ + this._tracks = []; + /** @internal */ + this._started = false; + /** @internal */ + this._finalizing = false; if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } @@ -3373,8 +3412,8 @@ ${cue.notes ?? ""}`; throw new Error("Target is already used for another output."); } options.target.output = this; - this.writer = options.target.createWriter(); - this.muxer = options.format.createMuxer(this); + this._writer = options.target._createWriter(); + this._muxer = options.format._createMuxer(this); } addVideoTrack(source, metadata = {}) { if (!(source instanceof VideoSource)) { @@ -3393,7 +3432,7 @@ ${cue.notes ?? ""}`; `Invalid video frame rate: ${metadata.frameRate}. Must be a positive integer.` ); } - this.addTrack("video", source, metadata); + this._addTrack("video", source, metadata); } addAudioTrack(source, metadata = {}) { if (!(source instanceof AudioSource)) { @@ -3402,7 +3441,7 @@ ${cue.notes ?? ""}`; if (!metadata || typeof metadata !== "object") { throw new TypeError("metadata must be an object."); } - this.addTrack("audio", source, metadata); + this._addTrack("audio", source, metadata); } addSubtitleTrack(source, metadata = {}) { if (!(source instanceof SubtitleSource)) { @@ -3411,46 +3450,47 @@ ${cue.notes ?? ""}`; if (!metadata || typeof metadata !== "object") { throw new TypeError("metadata must be an object."); } - this.addTrack("subtitle", source, metadata); + this._addTrack("subtitle", source, metadata); } - addTrack(type, source, metadata) { - if (this.started) { + /** @internal */ + _addTrack(type, source, metadata) { + if (this._started) { throw new Error("Cannot add track after output has started."); } - if (source.connectedTrack) { + if (source._connectedTrack) { throw new Error("Source is already used for a track."); } const track = { - id: this.tracks.length + 1, + id: this._tracks.length + 1, output: this, type, source, metadata }; - this.muxer.beforeTrackAdd(track); - this.tracks.push(track); - source.connectedTrack = track; + this._muxer.beforeTrackAdd(track); + this._tracks.push(track); + source._connectedTrack = track; } start() { - if (this.started) { + if (this._started) { throw new Error("Output already started."); } - this.started = true; - this.muxer.start(); - for (const track of this.tracks) { - track.source.start(); + this._started = true; + this._muxer.start(); + for (const track of this._tracks) { + track.source._start(); } } async finalize() { - if (this.finalizing) { + if (this._finalizing) { throw new Error("Cannot call finalize twice."); } - this.finalizing = true; - const promises = this.tracks.map((x) => x.source.flush()); + this._finalizing = true; + const promises = this._tracks.map((x) => x.source._flush()); await Promise.all(promises); - this.muxer.finalize(); - this.writer.flush(); - this.writer.finalize(); + this._muxer.finalize(); + this._writer.flush(); + this._writer.finalize(); } }; return __toCommonJS(src_exports); diff --git a/dist/metamuxer.min.js b/dist/metamuxer.min.js index 89da3dd..7860fd5 100644 --- a/dist/metamuxer.min.js +++ b/dist/metamuxer.min.js @@ -1,10 +1,10 @@ -"use strict";var Metamuxer=(()=>{var Ue=Object.defineProperty;var dt=Object.getOwnPropertyDescriptor;var ct=Object.getOwnPropertyNames;var ft=Object.prototype.hasOwnProperty;var mt=(r,t)=>{for(var e in t)Ue(r,e,{get:t[e],enumerable:!0})},ht=(r,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of ct(t))!ft.call(r,s)&&s!==e&&Ue(r,s,{get:()=>t[s],enumerable:!(i=dt(t,s))||i.enumerable});return r};var pt=r=>ht(Ue({},"__esModule",{value:!0}),r);var kr={};mt(kr,{ArrayBufferTarget:()=>$,AudioBufferSource:()=>Ve,AudioDataSource:()=>Oe,CanvasSource:()=>Ae,EncodedAudioChunkSource:()=>Ee,EncodedVideoChunkSource:()=>xe,FileSystemWritableFileStreamTarget:()=>fe,MediaStreamAudioTrackSource:()=>Me,MediaStreamVideoTrackSource:()=>ve,MkvOutputFormat:()=>te,Mp4OutputFormat:()=>ye,Output:()=>ze,StreamTarget:()=>j,Target:()=>E,TextSubtitleSource:()=>Ie,VideoFrameSource:()=>Se,WebMOutputFormat:()=>R});function d(r){if(!r)throw new Error("Assertion failed.")}var B=r=>r&&r[r.length-1],I=r=>r>=0&&r<2**32,z=(r,t,e)=>{let i=0;for(let s=t;s>a;i<<=1,i|=u}return i},$e=(r,t,e,i)=>{for(let s=t;s>e-s-1<r instanceof ArrayBuffer?new Uint8Array(r):new Uint8Array(r.buffer,r.byteOffset,r.byteLength),x=new TextEncoder,N={bt709:1,bt470bg:5,smpte170m:6},F={bt709:1,smpte170m:6,"iec61966-2-1":13},W={rgb:0,bt709:1,bt470bg:5,smpte170m:6},ne=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 G=/(?:(.+?)\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}/,D=/<(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})>/g,ae=class{#e;#r=null;#i=!1;constructor(t){this.#e=t}parse(t){t=t.replaceAll(`\r +"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 `,` `).replaceAll("\r",` -`),G.lastIndex=0;let e;if(!this.#r){if(!bt.test(t)){let s=new Error("WebVTT preamble incorrect.");throw this.#e.error(s),s}e=G.exec(t);let i=t.slice(0,e?.index??t.length).trimEnd();if(!i){let s=new Error("No WebVTT preamble provided.");throw this.#e.error(s),s}this.#r=i,e&&(t=t.slice(e.index),G.lastIndex=0)}for(;e=G.exec(t);){let i=t.slice(0,e.index),s=e[1],o=e.index+e[0].length,n=t.indexOf(` -`,o)+1,a=t.slice(o,n).trim(),u=t.indexOf(` +`),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(` -`,o);u===-1&&(u=t.length);let f=ue(e[2]),m=ue(e[3])-f,g=t.slice(n,u).trim();t=t.slice(u).trimStart(),G.lastIndex=0;let O={timestamp:f/1e3,duration:m/1e3,text:g,identifier:s,settings:a,notes:i},y={};this.#i||(y.config={description:this.#r},this.#i=!0),this.#e.output(O,y)}}},Tt=/(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})/,ue=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])},le=r=>{let t=Math.floor(r/36e5),e=Math.floor(r%(60*60*1e3)/(60*1e3)),i=Math.floor(r%(60*1e3)/1e3),s=r%1e3;return t.toString().padStart(2,"0")+":"+e.toString().padStart(2,"0")+":"+i.toString().padStart(2,"0")+"."+s.toString().padStart(3,"0")};var L=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=>(v.setUint16(0,r,!1),[h[0],h[1]]),gt=r=>(v.setInt16(0,r,!1),[h[0],h[1]]),Qe=r=>(v.setUint32(0,r,!1),[h[1],h[2],h[3]]),l=r=>(v.setUint32(0,r,!1),[h[0],h[1],h[2],h[3]]),Ke=r=>(v.setInt32(0,r,!1),[h[0],h[1],h[2],h[3]]),P=r=>(v.setUint32(0,Math.floor(r/2**32),!1),v.setUint32(4,r,!1),[h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7]]),Be=r=>(v.setInt16(0,2**8*r,!1),[h[0],h[1]]),A=r=>(v.setInt32(0,2**16*r,!1),[h[0],h[1],h[2],h[3]]),Re=r=>(v.setInt32(0,2**30*r,!1),[h[0],h[1],h[2],h[3]]),_e=(r,t)=>{let e=[],i=r;do{let s=i&127;i>>=7,e.length>0&&(s|=128),e.push(s),t!==void 0&&t--}while(i>0||t);return e.reverse()},w=(r,t=!1)=>{let e=Array(r.length).fill(null).map((i,s)=>r.charCodeAt(s));return t&&e.push(0),e},Ne=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),Le=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,s)=>b(r,[C(t),Qe(e),i??[]],s),qe=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")])},ce=r=>({type:"mdat",largeSize:r}),Ye=r=>({type:"free",size:r}),q=(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=Ne(n.samples);return a.timestamp+a.duration})),de),i=Math.max(0,...t.map(n=>n.track.id))+1,s=!I(r)||!I(e),o=s?P:l;return T("mvhd",+s,0,[o(r),o(r),l(de),o(e),A(1),Be(1),Array(10).fill(0),Le(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=Ne(r.samples),i=S(e?e.timestamp+e.duration:0,de),s=!I(t)||!I(i),o=s?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",+s,3,[o(t),o(t),l(r.track.id),l(0),o(i),Array(8).fill(0),p(0),p(r.track.id),Be(r.type==="audio"?1:0),p(0),Le(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),vt(r),Et(r)]),xt=(r,t)=>{let e=Ne(r.samples),i=S(e?e.timestamp+e.duration:0,r.timescale),s=!I(t)||!I(i),o=s?P:l;return T("mdhd",+s,0,[o(t),o(t),l(r.timescale),o(i),p(21956),p(0)])},St={video:"vide",audio:"soun",subtitle:"text"},At={video:"VideoHandler",audio:"SoundHandler",subtitle:"TextHandler"},vt=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,[It[r.type](),zt(),Rt(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),It={video:Ot,audio:Vt,subtitle:Mt},zt=()=>b("dinf",void 0,[Ut()]),Ut=()=>T("dref",0,0,[l(1)],[Pt()]),Pt=()=>T("url ",0,1),Rt=r=>{let t=r.compositionTimeOffsetTable.length>1||r.compositionTimeOffsetTable.some(e=>e.sampleCompositionTimeOffset!==0);return b("stbl",void 0,[_t(r),Xt(r),Gt(r),Lt(r),qt(r),Yt(r),t?Zt(r):null])},_t=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),ne(t.info.decoderConfig.colorSpace)?Nt(t):null]),Nt=r=>b("colr",[w("nclx"),p(N[r.info.decoderConfig.colorSpace.primaries]),p(F[r.info.decoderConfig.colorSpace.transfer]),p(W[r.info.decoderConfig.colorSpace.matrix]),C((r.info.decoderConfig.colorSpace.fullRange?1:0)<<7)]),Ft=r=>r.info.decoderConfig&&b("avcC",[...U(r.info.decoderConfig.description)]),Wt=r=>r.info.decoderConfig&&b("hvcC",[...U(r.info.decoderConfig.description)]),je=r=>{if(!r.info.decoderConfig)return null;let t=r.info.decoderConfig;d(t.colorSpace);let e=t.codec.split("."),i=Number(e[1]),s=Number(e[2]),a=(Number(e[3])<<4)+(0<<1)+Number(t.colorSpace.fullRange);return T("vpcC",1,0,[C(i),C(s),C(a),C(2),C(2),C(2),p(0)])},Dt=()=>{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=[...U(r.info.decoderConfig.description??new ArrayBuffer(0))];return e=[...C(64),...C(21),...Qe(0),...l(0),...l(0),...C(5),..._e(e.length),...e],e=[...p(1),...C(0),...C(4),..._e(e.length),...e,...C(6),...C(1),...C(2)],e=[...C(3),..._e(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 s=ArrayBuffer.isView(i)?new DataView(i.buffer,i.byteOffset,i.byteLength):new DataView(i);t=s.getUint16(10,!0),e=s.getInt16(14,!0)}return b("dOps",[C(0),C(r.info.numberOfChannels),p(t),l(r.info.sampleRate),Be(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 Xt=r=>T("stts",0,0,[l(r.timeToSampleTable.length),r.timeToSampleTable.map(t=>[l(t.sampleCount),l(t.sampleDelta)])]),Gt=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))])},Lt=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&&B(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)]),Ze=r=>{let t=0,e=0,i=0,s=0,o=r.type==="delta";return e|=+o,o?t|=1:t|=2,t<<24|e<<16|i<<8|s},rr=r=>b("traf",void 0,[ir(r),sr(r),or(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:Ze(e)};return T("tfhd",0,t,[l(r.track.id),l(i.duration),l(i.size),l(i.flags)])},sr=r=>(d(r.currentChunk),T("tfdt",1,0,[P(S(r.currentChunk.startTimestamp,r.timescale))])),or=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(Ze),s=r.currentChunk.samples.map(k=>S(k.timestamp-k.decodeTimestamp,r.timescale)),o=new Set(t),n=new Set(e),a=new Set(i),u=new Set(s),f=a.size===2&&i[0]!==i[1],c=o.size>1,m=n.size>1,g=!f&&a.size>1,O=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*+O,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,_)=>[c?l(t[_]):[],m?l(e[_]):[],g?l(i[_]):[],O?Ke(s[_]):[]])])},Je=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)]),et=()=>b("vtte"),tt=(r,t,e,i,s)=>b("vttc",void 0,[s!==null?b("vsid",[Ke(s)]):null,e!==null?b("iden",[...x.encode(e)]):null,t!==null?b("ctim",[...x.encode(le(t))]):null,i!==null?b("sttg",[...x.encode(i)]):null,b("payl",[...x.encode(r)])]),rt=r=>b("vtta",[...x.encode(r)]),ur={avc:"avc1",hevc:"hvc1",vp8:"vp08",vp9:"vp09",av1:"av01"},lr={avc:Ft,hevc:Wt,vp8:je,vp9:je,av1:Dt},dr={aac:"mp4a",opus:"Opus"},cr={aac:$t,opus:jt},fr={webvtt:"wvtt"},mr={webvtt:Kt};var H=class{constructor(t){this.trackTimestampInfo=new WeakMap;this.output=t}beforeTrackAdd(t){}onTrackClose(t){}validateAndNormalizeTimestamp(t,e,i){let s=e/1e6,o=this.trackTimestampInfo.get(t);if(!o){if(!i)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:t.source.offsetTimestamps?0:s,lastKeyFrameTimestamp:t.source.offsetTimestamps?0:s},this.trackTimestampInfo.set(t,o)}if(t.source.offsetTimestamps&&(s-=o.timestampOffset),s<0)throw new Error(`Timestamps must be non-negative (got ${s}s).`);if(si.start-s.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[o+1].start;)t.written[o].end=Math.max(t.written[o].end,t.written[o+1].end),t.written.splice(o+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((s,o)=>s.start-o.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 it=(r,t,e)=>{if(r==="avc"){let i=100;t<=768&&e<=432?i=66:t<=1920&&e<=1080&&(i=77);let s=0,o=t>1920||e>1080?50:41,n=i.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(r==="hevc"){let i=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 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)}${s}.${n}.${a}${u}.B0`}else{if(r==="vp8")return"vp8";if(r==="vp9"){let i="00",s;return t<=854&&e<=480?s="21":t<=1280&&e<=720?s="31":t<=1920&&e<=1080?s="41":t<=3840&&e<=2160?s="51":s="61",`vp09.${i}.${s}.08`}else if(r==="av1"){let s;return t<=854&&e<=480?s="01":t<=1280&&e<=720?s="03":t<=1920&&e<=1080?s="04":t<=3840&&e<=2160?s="07":s="09",`av01.0.${s}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}'.`)},pe=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(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 i=Object.keys(F);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 s=Object.keys(W);if(t.matrix!=null&&!s.includes(t.matrix))throw new TypeError(`Video chunk metadata decoder configuration colorSpace matrix, when defined, must be one of ${s.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.")},be=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.")},Te=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 de=1e3,br=2082844800,S=(r,t,e=!0)=>{let i=r*t;return e?Math.round(i):i},ge=class extends H{constructor(e,i){super(e);this.timestampsMustStartAtZero=!0;this.#o=new $;this.#a=this.#o.createWriter();this.#c=new L(this.#a);this.#l=null;this.#n=null;this.#s=[];this.#d=Math.floor(Date.now()/1e3)+br;this.#u=[];this.#m=1;this.#e=e.writer,this.#r=new L(this.#e),this.#i=i,this.#t=i.options.fastStart??(this.#e instanceof Q?"in-memory":!1),(this.#t==="in-memory"||this.#t==="fragmented")&&(this.#e.ensureMonotonicity=!0)}#e;#r;#i;#t;#o;#a;#c;#l;#n;#s;#d;#u;#m;start(){let e=this.output.tracks.some(i=>i.type==="video"&&i.source.codec==="avc");this.#r.writeBox(qe({holdsAvc:e,fragmented:this.#t==="fragmented"})),this.#l=this.#e.getPos(),this.#t==="in-memory"?this.#n=ce(!1):this.#t==="fragmented"||(this.#n=ce(!0),this.#r.writeBox(this.#n)),this.#e.flush()}#b(e,i){let s=this.#s.find(n=>n.track===e);if(s)return s;pe(i),d(i),d(i.decoderConfig),d(i.decoderConfig.codedWidth!==void 0),d(i.decoderConfig.codedHeight!==void 0);let o={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.#s.push(o),this.#s.sort((n,a)=>n.track.id-a.track.id),o}#T(e,i){let s=this.#s.find(n=>n.track===e);if(s)return s;be(i),d(i),d(i.decoderConfig);let o={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.#s.push(o),this.#s.sort((n,a)=>n.track.id-a.track.id),o}#A(e,i){let s=this.#s.find(n=>n.track===e);if(s)return s;Te(i),d(i),d(i.config);let o={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.#s.push(o),this.#s.sort((n,a)=>n.track.id-a.track.id),this.validateAndNormalizeTimestamp(e,0,!0),o}addEncodedVideoChunk(e,i,s){let o=this.#b(e,s),n=new Uint8Array(i.byteLength);i.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,i.timestamp,i.type==="key"),u=this.#h(o,n,a,(i.duration??0)/1e6,i.type);this.#p(o,u)}addEncodedAudioChunk(e,i,s){let o=this.#T(e,s),n=new Uint8Array(i.byteLength);i.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,i.timestamp,i.type==="key"),u=this.#h(o,n,a,(i.duration??0)/1e6,i.type);this.#p(o,u)}addSubtitleCue(e,i,s){let o=this.#A(e,s);this.validateAndNormalizeTimestamp(o.track,1e6*i.timestamp,!0),e.source.codec==="webvtt"&&(o.cueQueue.push(i),this.#g(o,i.timestamp))}#g(e,i){for(;e.cueQueue.length>0;){let s=new Set([]);for(let c of e.cueQueue)d(c.timestamp<=i),d(e.lastCueEndTimestamp<=c.timestamp+c.duration),s.add(Math.max(c.timestamp,e.lastCueEndTimestamp)),s.add(c.timestamp+c.duration);let o=[...s].sort((c,m)=>c-m),n=o[0],a=o[1]??n;if(i=a)break;D.lastIndex=0;let g=D.test(m.text),O=m.timestamp+m.duration,y=e.cueToSourceId.get(m);if(y===void 0&&as.timestamp).sort((s,o)=>s-o);for(let s=0;s{if(e===a)return i.type==="key";let u=a.sampleQueue[0];return u&&u.type==="key"});o>=1&&n&&(s=!0,this.#x())}else s=o>=.5}s&&(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||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 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.#s.some(i=>i.track===e))return;e:for(;;){let e=null,i=1/0;for(let o of this.#s){if(o.sampleQueue.length===0&&!o.track.source.closed)break e;o.sampleQueue.length>0&&o.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.#s){u.currentChunk.offset=this.#e.getPos(),u.currentChunk.moofOffset=s;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(o));let a=Fe(i,this.#s);this.#r.writeBox(a),this.#e.seek(n);for(let u of this.#s)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.#s.find(s=>s.track===e);i&&this.#g(i,1/0)}this.#t==="fragmented"&&this.#k()}finalize(){for(let e of this.#s)e.type==="subtitle"&&e.track.source.codec==="webvtt"&&this.#g(e,1/0);if(this.#t==="fragmented"){for(let e of this.#s){for(let i of e.sampleQueue)this.#C(e,i);this.#f(e)}this.#x(!1)}else for(let e of this.#s)this.#f(e),this.#w(e);if(this.#t==="in-memory"){d(this.#n);let e;for(let s=0;s<2;s++){let o=q(this.#s,this.#d),n=this.#r.measureBox(o);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=q(this.#s,this.#d);this.#r.writeBox(i),this.#n.size=e,this.#r.writeBox(this.#n);for(let s of this.#u)for(let o of s.samples)d(o.data),this.#e.write(o.data),o.data=null}else if(this.#t==="fragmented"){let e=this.#e.getPos(),i=Je(this.#s);this.#r.writeBox(i);let s=this.#e.getPos()-e;this.#e.seek(this.#e.getPos()-4),this.#r.writeU32(s)}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 s=q(this.#s,this.#d);if(typeof this.#t=="object"){this.#e.seek(this.#l),this.#r.writeBox(s);let o=e-this.#e.getPos();this.#r.writeBox(Ye(o))}else this.#r.writeBox(s)}}};var J=class{constructor(t){this.value=t}},K=class{constructor(t){this.value=t}},ee=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,De=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,ot=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 He=2**15,nt="https://github.com/Vanilagy/webm-muxer",at=6,ut=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},Ce=class extends H{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.#o=[];this.#a=null;this.#c=null;this.#l=null;this.#n=null;this.#s=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;#o;#a;#c;#l;#n;#s;#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 s=0;switch(i){case 6:this.#t.setUint8(s++,e/2**40|0);case 5:this.#t.setUint8(s++,e/2**32|0);case 4:this.#t.setUint8(s++,e>>24);case 3:this.#t.setUint8(s++,e>>16);case 2:this.#t.setUint8(s++,e>>8);case 1:this.#t.setUint8(s++,e);break;default:throw new Error("Bad UINT size "+i)}this.#e.write(this.#i.subarray(0,s))}#p(e,i=De(e)){e<0&&(e+=2**(i*8)),this.#f(e,i)}writeEBMLVarInt(e,i=ot(e)){let s=0;switch(i){case 1:this.#t.setUint8(s++,128|e);break;case 2:this.#t.setUint8(s++,64|e>>8),this.#t.setUint8(s++,e);break;case 3:this.#t.setUint8(s++,32|e>>16),this.#t.setUint8(s++,e>>8),this.#t.setUint8(s++,e);break;case 4:this.#t.setUint8(s++,16|e>>24),this.#t.setUint8(s++,e>>16),this.#t.setUint8(s++,e>>8),this.#t.setUint8(s++,e);break;case 5:this.#t.setUint8(s++,8|e/2**32&7),this.#t.setUint8(s++,e>>24),this.#t.setUint8(s++,e>>16),this.#t.setUint8(s++,e>>8),this.#t.setUint8(s++,e);break;case 6:this.#t.setUint8(s++,4|e/2**40&3),this.#t.setUint8(s++,e/2**32|0),this.#t.setUint8(s++,e>>24),this.#t.setUint8(s++,e>>16),this.#t.setUint8(s++,e>>8),this.#t.setUint8(s++,e);break;default:throw new Error("Bad EBML VINT size "+i)}this.#e.write(this.#i.subarray(0,s))}#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(),s=e.size===-1?1:e.size??4;e.size===-1?this.#A(255):this.#e.seek(this.#e.getPos()+s);let o=this.#e.getPos();if(this.dataOffsets.set(e,o),this.writeEBML(e.data),e.size!==-1){let n=this.#e.getPos()-o,a=this.#e.getPos();this.#e.seek(i),this.writeEBMLVarInt(n,s),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 J)this.writeEBMLVarInt(4),this.#g(e.data.value);else if(e.data instanceof K)this.writeEBMLVarInt(8),this.#h(e.data.value);else if(e.data instanceof ee){let i=e.size??De(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.#I(),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]),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:i},{id:21420,size:5,data:0}]},{id:19899,data:[{id:21419,data:s},{id:21420,size:5,data:0}]}]};this.#l=o}#x(){let e={id:17545,data:new K(0)};this.#s=e;let i={id:357149030,data:[{id:2807729,data:1e6},{id:19840,data:nt},{id:22337,data:nt},this.#r.options.streamable?null:e]};this.#c=i}#E(){let e={id:374648427,data:[]};this.#n=e;for(let i of this.#o)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:U(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 s=i.info.decoderConfig.colorSpace;return ne(s)?{id:21936,data:[{id:21937,data:W[s.matrix]},{id:21946,data:F[s.transfer]},{id:21947,data:N[s.primaries]},{id:21945,data:[1,2][Number(s.fullRange)]}]}:null}return null})()]}]:[],...i.type==="audio"?[i.info.decoderConfig.description?{id:25506,data:U(i.info.decoderConfig.description)}:null,{id:225,data:[{id:181,data:new J(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:at,data:[this.#r.options.streamable?null:this.#l,this.#c,this.#n]};this.#a=e,this.writeEBML(e)}#I(){this.#d={id:475249515,data:[]}}get#y(){return d(this.#a),this.dataOffsets.get(this.#a)}#z(e,i){let s=this.#o.find(n=>n.track===e);if(s)return s;pe(i),d(i),d(i.decoderConfig),d(i.decoderConfig.codedWidth!==void 0),d(i.decoderConfig.codedHeight!==void 0);let o={track:e,type:"video",info:{width:i.decoderConfig.codedWidth,height:i.decoderConfig.codedHeight,decoderConfig:i.decoderConfig},chunkQueue:[],lastWrittenMsTimestamp:null};return this.#o.push(o),this.#o.sort((n,a)=>n.track.id-a.track.id),o}#U(e,i){let s=this.#o.find(n=>n.track===e);if(s)return s;be(i),d(i),d(i.decoderConfig);let o={track:e,type:"audio",info:{numberOfChannels:i.decoderConfig.numberOfChannels,sampleRate:i.decoderConfig.sampleRate,decoderConfig:i.decoderConfig},chunkQueue:[],lastWrittenMsTimestamp:null};return this.#o.push(o),this.#o.sort((n,a)=>n.track.id-a.track.id),o}#P(e,i){let s=this.#o.find(n=>n.track===e);if(s)return s;Te(i),d(i),d(i.config);let o={track:e,type:"subtitle",info:{config:i.config},chunkQueue:[],lastWrittenMsTimestamp:null};return this.#o.push(o),this.#o.sort((n,a)=>n.track.id-a.track.id),o}addEncodedVideoChunk(e,i,s){let o=this.#z(e,s),n=new Uint8Array(i.byteLength);i.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,i.timestamp,i.type==="key"),u=this.#v(n,a,(i.duration??0)/1e6,i.type);e.source.codec==="vp9"&&this.#R(o,u),o.chunkQueue.push(u),this.#S()}addEncodedAudioChunk(e,i,s){let o=this.#U(e,s),n=new Uint8Array(i.byteLength);i.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,i.timestamp,i.type==="key"),u=this.#v(n,a,(i.duration??0)/1e6,i.type);o.chunkQueue.push(u),this.#S()}addSubtitleCue(e,i,s){let o=this.#P(e,s),n=this.validateAndNormalizeTimestamp(o.track,1e6*i.timestamp,!0),a=i.text,u=Math.floor(n*1e3);D.lastIndex=0,a=a.replace(D,g=>{let y=ue(g.slice(1,-1))-u;return`<${le(y)}>`});let f=x.encode(a),c=`${i.settings??""} +`,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.#v(f,n,i.duration,"key",c.trim()?x.encode(c):null);o.chunkQueue.push(m),this.#S()}#S(){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 o of this.#o){if(o.chunkQueue.length===0&&!o.track.source.closed)break e;o.chunkQueue.length>0&&o.chunkQueue[0].timestamp=2&&s++;let f={rgb:7,bt709:2,bt470bg:1,smpte170m:3}[e.info.decoderConfig.colorSpace.matrix];$e(i.data,s+0,s+3,f)}#v(e,i,s,o,n=null){return{data:e,type:o,timestamp:i,duration:s,additions:n}}#V(e,i){this.#a||(this.#E(),this.#O());let s=Math.floor(1e3*i.timestamp),o=this.#o.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||o&&s-this.#m>=1e3)&&this.#_(s);let n=s-this.#m;if(n<0)return;if(n>=He)throw new Error(`Current Matroska cluster exceeded its maximum allowed length of ${He} milliseconds. In order to produce a correct WebM file, you must pass in a key frame at least every ${He} 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 ee(e.lastWrittenMsTimestamp-s)}: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,s+c),e.lastWrittenMsTimestamp=s,this.#b.add(e)}#_(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),i=this.#e.getPos();this.#e.seek(this.offsets.get(this.#u)+4),this.writeEBMLVarInt(e,ut),this.#e.seek(i);let s=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(o=>({id:183,data:[{id:247,data:o.track.id},{id:241,data:s}]}))]})}onTrackClose(){this.#S()}finalize(){this.#a||(this.#E(),this.#O());for(let e of this.#o)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,at),this.#s.data=new K(this.#T),this.#e.seek(this.offsets.get(this.#s)),this.writeEBML(this.#s),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 X=class{},ye=class extends X{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)}},te=class extends X{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)}},R=class extends te{};var we=["avc","hevc","vp8","vp9","av1"],ke=["aac","opus"],lt=["webvtt"],re=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)}},V=class extends re{constructor(e){super();this.connectedTrack=null;if(!we.includes(e))throw new TypeError(`Invalid video codec '${e}'. Must be one of: ${we.join(", ")}.`);this.codec=e}},xe=class extends V{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(!we.includes(r.codec))throw new TypeError(`Invalid video codec '${r.codec}'. Must be one of: ${we.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'.")},ie=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: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()}},Se=class extends V{constructor(t){super(t.codec),this.encoder=new ie(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()}},Ae=class extends V{constructor(e,i){if(!(e instanceof HTMLCanvasElement))throw new TypeError("canvas must be an HTMLCanvasElement.");super(i.codec);this.canvas=e;this.encoder=new ie(this,i)}digest(e,i=0){if(!Number.isFinite(e)||e<0)throw new TypeError("timestamp must be a non-negative number.");if(!Number.isFinite(i)||i<0)throw new TypeError("duration must be a non-negative number.");let s=new VideoFrame(this.canvas,{timestamp:Math.round(1e6*e),duration:Math.round(1e6*i),alpha:"discard"});this.encoder.digest(s),s.close()}flush(){return this.encoder.flush()}},ve=class extends V{constructor(e,i){if(!(e instanceof MediaStreamTrack)||e.kind!=="video")throw new TypeError("track must be a video MediaStreamTrack.");super(i.codec);this.track=e;this.abortController=null;this.offsetTimestamps=!0;this.encoder=new ie(this,i)}start(){this.abortController=new AbortController;let e=new MediaStreamTrackProcessor({track:this.track}),i=new WritableStream({write:s=>{this.encoder.digest(s),s.close()}});e.readable.pipeTo(i,{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()}},M=class extends re{constructor(e){super();this.connectedTrack=null;if(!ke.includes(e))throw new TypeError(`Invalid audio codec '${e}'. Must be one of: ${ke.join(", ")}.`);this.codec=e}},Ee=class extends M{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(!ke.includes(r.codec))throw new TypeError(`Invalid audio codec '${r.codec}'. Must be one of: ${ke.join(", ")}.`);if(!Number.isInteger(r.bitrate)||r.bitrate<=0)throw new TypeError("config.bitrate must be a positive integer.")},se=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()}},Oe=class extends M{constructor(t){super(t.codec),this.encoder=new se(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()}},Ve=class extends M{constructor(e){super(e.codec);this.accumulatedFrameCount=0;this.encoder=new se(this,e)}digest(e){if(!(e instanceof AudioBuffer))throw new TypeError("audioBuffer must be an AudioBuffer.");let i=e.numberOfChannels,s=e.sampleRate,o=e.length,n=new Float32Array(i*o);for(let u=0;u{this.encoder.digest(s),s.close()}});e.readable.pipeTo(i,{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()}},oe=class extends re{constructor(e){super();this.connectedTrack=null;if(!lt.includes(e))throw new TypeError(`Invalid subtitle codec '${e}'. Must be one of: ${lt.join(", ")}.`);this.codec=e}},Ie=class extends oe{constructor(t){super(t),this.parser=new ae({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 ze=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 X))throw new TypeError("options.format must be an OutputFormat.");if(!(t.target instanceof E))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 V))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 M))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 oe))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 s={id:this.tracks.length+1,output:this,type:t,source:e,metadata:i};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 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);})(); +${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);})(); 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 185fcd8..e8e1b36 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 _=i=>i&&i[i.length-1],I=i=>i>=0&&i<2**32,z=(i,t,e)=>{let r=0;for(let s=t;s>a;r<<=1,r|=u}return r},He=(i,t,e,r)=>{for(let s=t;s>e-s-1<i instanceof ArrayBuffer?new Uint8Array(i):new Uint8Array(i.buffer,i.byteOffset,i.byteLength),x=new TextEncoder,B={bt709:1,bt470bg:5,smpte170m:6},N={bt709:1,smpte170m:6,"iec61966-2-1":13},F={rgb:0,bt709:1,bt470bg:5,smpte170m:6},oe=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 K=/(?:(.+?)\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,ne=class{#e;#r=null;#i=!1;constructor(t){this.#e=t}parse(t){t=t.replaceAll(`\r +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 `,` `).replaceAll("\r",` -`),K.lastIndex=0;let e;if(!this.#r){if(!lt.test(t)){let s=new Error("WebVTT preamble incorrect.");throw this.#e.error(s),s}e=K.exec(t);let r=t.slice(0,e?.index??t.length).trimEnd();if(!r){let s=new Error("No WebVTT preamble provided.");throw this.#e.error(s),s}this.#r=r,e&&(t=t.slice(e.index),K.lastIndex=0)}for(;e=K.exec(t);){let r=t.slice(0,e.index),s=e[1],o=e.index+e[0].length,n=t.indexOf(` -`,o)+1,a=t.slice(o,n).trim(),u=t.indexOf(` +`),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(` -`,o);u===-1&&(u=t.length);let f=ae(e[2]),m=ae(e[3])-f,g=t.slice(n,u).trim();t=t.slice(u).trimStart(),K.lastIndex=0;let E={timestamp:f/1e3,duration:m/1e3,text:g,identifier:s,settings:a,notes:r},y={};this.#i||(y.config={description:this.#r},this.#i=!0),this.#e.output(E,y)}}},dt=/(?:(\d{2}):)?(\d{2}):(\d{2}).(\d{3})/,ae=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])},ue=i=>{let t=Math.floor(i/36e5),e=Math.floor(i%(60*60*1e3)/(60*1e3)),r=Math.floor(i%(60*1e3)/1e3),s=i%1e3;return t.toString().padStart(2,"0")+":"+e.toString().padStart(2,"0")+":"+r.toString().padStart(2,"0")+"."+s.toString().padStart(3,"0")};var X=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=>(v.setUint16(0,i,!1),[h[0],h[1]]),ct=i=>(v.setInt16(0,i,!1),[h[0],h[1]]),je=i=>(v.setUint32(0,i,!1),[h[1],h[2],h[3]]),l=i=>(v.setUint32(0,i,!1),[h[0],h[1],h[2],h[3]]),Qe=i=>(v.setInt32(0,i,!1),[h[0],h[1],h[2],h[3]]),P=i=>(v.setUint32(0,Math.floor(i/2**32),!1),v.setUint32(4,i,!1),[h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7]]),Se=i=>(v.setInt16(0,2**8*i,!1),[h[0],h[1]]),A=i=>(v.setInt32(0,2**16*i,!1),[h[0],h[1],h[2],h[3]]),ke=i=>(v.setInt32(0,2**30*i,!1),[h[0],h[1],h[2],h[3]]),xe=(i,t)=>{let e=[],r=i;do{let s=r&127;r>>=7,e.length>0&&(s|=128),e.push(s),t!==void 0&&t--}while(r>0||t);return e.reverse()},w=(i,t=!1)=>{let e=Array(i.length).fill(null).map((r,s)=>i.charCodeAt(s));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},Ke=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=Ke(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,s)=>b(i,[C(t),je(e),r??[]],s),Le=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")])},de=i=>({type:"mdat",largeSize:i}),qe=i=>({type:"free",size:i}),G=(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})),le),r=Math.max(0,...t.map(n=>n.track.id))+1,s=!I(i)||!I(e),o=s?P:l;return T("mvhd",+s,0,[o(i),o(i),l(le),o(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,le),s=!I(t)||!I(r),o=s?P:l,n;if(i.type==="video"){let a=i.track.metadata.rotation;n=a===void 0||typeof a=="number"?Ke(a??0):a}else n=Xe;return T("tkhd",+s,3,[o(t),o(t),l(i.track.id),l(0),o(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),s=!I(t)||!I(r),o=s?P:l;return T("mdhd",+s,0,[o(t),o(t),l(i.timescale),o(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,[vt()]),vt=()=>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),Wt(i),Dt(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=Rt(ir[i.track.source.codec],i):i.type==="subtitle"&&(t=Nt(or[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),oe(t.info.decoderConfig.colorSpace)?It(t):null]),It=i=>b("colr",[w("nclx"),p(B[i.info.decoderConfig.colorSpace.primaries]),p(N[i.info.decoderConfig.colorSpace.transfer]),p(F[i.info.decoderConfig.colorSpace.matrix]),C((i.info.decoderConfig.colorSpace.fullRange?1:0)<<7)]),zt=i=>i.info.decoderConfig&&b("avcC",[...U(i.info.decoderConfig.description)]),Ut=i=>i.info.decoderConfig&&b("hvcC",[...U(i.info.decoderConfig.description)]),$e=i=>{if(!i.info.decoderConfig)return null;let t=i.info.decoderConfig;d(t.colorSpace);let e=t.codec.split("."),r=Number(e[1]),s=Number(e[2]),a=(Number(e[3])<<4)+(0<<1)+Number(t.colorSpace.fullRange);return T("vpcC",1,0,[C(r),C(s),C(a),C(2),C(2),C(2),p(0)])},Pt=()=>{let e=(1<<7)+1;return b("av1C",[e,0,0,0])},Rt=(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)],[sr[t.track.source.codec](t)]),_t=i=>{let e=[...U(i.info.decoderConfig.description??new ArrayBuffer(0))];return e=[...C(64),...C(21),...je(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 s=ArrayBuffer.isView(r)?new DataView(r.buffer,r.byteOffset,r.byteLength):new DataView(r);t=s.getUint16(10,!0),e=s.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)]),Ft=i=>b("vttC",[...x.encode(i.info.config.description)]);var Wt=i=>T("stts",0,0,[l(i.timeToSampleTable.length),i.timeToSampleTable.map(t=>[l(t.sampleCount),l(t.sampleDelta)])]),Dt=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&&_(i.finalizedChunks).offset>=2**32?T("co64",0,0,[l(i.finalizedChunks.length),i.finalizedChunks.map(t=>P(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(Xt)),Xt=i=>T("trex",0,0,[l(i.track.id),l(1),l(0),l(0),l(0)]),ve=(i,t)=>b("moof",void 0,[Gt(i),...t.map(Lt)]),Gt=i=>T("mfhd",0,0,[l(i)]),Ye=i=>{let t=0,e=0,r=0,s=0,o=i.type==="delta";return e|=+o,o?t|=1:t|=2,t<<24|e<<16|r<<8|s},Lt=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:Ye(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,[P(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(Ye),s=i.currentChunk.samples.map(k=>S(k.timestamp-k.decodeTimestamp,i.timescale)),o=new Set(t),n=new Set(e),a=new Set(r),u=new Set(s),f=a.size===2&&r[0]!==r[1],c=o.size>1,m=n.size>1,g=!f&&a.size>1,E=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*+E,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]):[],E?Qe(s[R]):[]])])},Ze=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=>[P(S(r.startTimestamp,i.timescale)),P(r.moofOffset),l(t+1),l(1),l(1)])]),er=()=>T("mfro",0,0,[l(0)]),Je=()=>b("vtte"),et=(i,t,e,r,s)=>b("vttc",void 0,[s!==null?b("vsid",[Qe(s)]):null,e!==null?b("iden",[...x.encode(e)]):null,t!==null?b("ctim",[...x.encode(ue(t))]):null,r!==null?b("sttg",[...x.encode(r)]):null,b("payl",[...x.encode(i)])]),tt=i=>b("vtta",[...x.encode(i)]),tr={avc:"avc1",hevc:"hvc1",vp8:"vp08",vp9:"vp09",av1:"av01"},rr={avc:zt,hevc:Ut,vp8:$e,vp9:$e,av1:Pt},ir={aac:"mp4a",opus:"Opus"},sr={aac:_t,opus:Bt},or={webvtt:"wvtt"},nr={webvtt:Ft};var D=class{constructor(t){this.trackTimestampInfo=new WeakMap;this.output=t}beforeTrackAdd(t){}onTrackClose(t){}validateAndNormalizeTimestamp(t,e,r){let s=e/1e6,o=this.trackTimestampInfo.get(t);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:t.source.offsetTimestamps?0:s,lastKeyFrameTimestamp:t.source.offsetTimestamps?0:s},this.trackTimestampInfo.set(t,o)}if(t.source.offsetTimestamps&&(s-=o.timestampOffset),s<0)throw new Error(`Timestamps must be non-negative (got ${s}s).`);if(sr.start-s.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[o+1].start;)t.written[o].end=Math.max(t.written[o].end,t.written[o+1].end),t.written.splice(o+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((s,o)=>s.start-o.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 rt=(i,t,e)=>{if(i==="avc"){let r=100;t<=768&&e<=432?r=66:t<=1920&&e<=1080&&(r=77);let s=0,o=t>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(i==="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 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)}${s}.${n}.${a}${u}.B0`}else{if(i==="vp8")return"vp8";if(i==="vp9"){let r="00",s;return t<=854&&e<=480?s="21":t<=1280&&e<=720?s="31":t<=1920&&e<=1080?s="41":t<=3840&&e<=2160?s="51":s="61",`vp09.${r}.${s}.08`}else if(i==="av1"){let s;return t<=854&&e<=480?s="01":t<=1280&&e<=720?s="03":t<=1920&&e<=1080?s="04":t<=3840&&e<=2160?s="07":s="09",`av01.0.${s}M.08`}}throw new TypeError(`Unhandled codec '${i}'.`)},it=(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}'.`)},me=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(B);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(N);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 s=Object.keys(F);if(t.matrix!=null&&!s.includes(t.matrix))throw new TypeError(`Video chunk metadata decoder configuration colorSpace matrix, when defined, must be one of ${s.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.")},he=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.")},pe=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 le=1e3,lr=2082844800,S=(i,t,e=!0)=>{let r=i*t;return e?Math.round(r):r},be=class extends D{constructor(e,r){super(e);this.timestampsMustStartAtZero=!0;this.#o=new L;this.#a=this.#o.createWriter();this.#c=new X(this.#a);this.#l=null;this.#n=null;this.#s=[];this.#d=Math.floor(Date.now()/1e3)+lr;this.#u=[];this.#m=1;this.#e=e.writer,this.#r=new X(this.#e),this.#i=r,this.#t=r.options.fastStart??(this.#e instanceof H?"in-memory":!1),(this.#t==="in-memory"||this.#t==="fragmented")&&(this.#e.ensureMonotonicity=!0)}#e;#r;#i;#t;#o;#a;#c;#l;#n;#s;#d;#u;#m;start(){let e=this.output.tracks.some(r=>r.type==="video"&&r.source.codec==="avc");this.#r.writeBox(Le({holdsAvc:e,fragmented:this.#t==="fragmented"})),this.#l=this.#e.getPos(),this.#t==="in-memory"?this.#n=de(!1):this.#t==="fragmented"||(this.#n=de(!0),this.#r.writeBox(this.#n)),this.#e.flush()}#b(e,r){let s=this.#s.find(n=>n.track===e);if(s)return s;me(r),d(r),d(r.decoderConfig),d(r.decoderConfig.codedWidth!==void 0),d(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.#s.push(o),this.#s.sort((n,a)=>n.track.id-a.track.id),o}#T(e,r){let s=this.#s.find(n=>n.track===e);if(s)return s;he(r),d(r),d(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.#s.push(o),this.#s.sort((n,a)=>n.track.id-a.track.id),o}#A(e,r){let s=this.#s.find(n=>n.track===e);if(s)return s;pe(r),d(r),d(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.#s.push(o),this.#s.sort((n,a)=>n.track.id-a.track.id),this.validateAndNormalizeTimestamp(e,0,!0),o}addEncodedVideoChunk(e,r,s){let o=this.#b(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.#h(o,n,a,(r.duration??0)/1e6,r.type);this.#p(o,u)}addEncodedAudioChunk(e,r,s){let o=this.#T(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.#h(o,n,a,(r.duration??0)/1e6,r.type);this.#p(o,u)}addSubtitleCue(e,r,s){let o=this.#A(e,s);this.validateAndNormalizeTimestamp(o.track,1e6*r.timestamp,!0),e.source.codec==="webvtt"&&(o.cueQueue.push(r),this.#g(o,r.timestamp))}#g(e,r){for(;e.cueQueue.length>0;){let s=new Set([]);for(let c of e.cueQueue)d(c.timestamp<=r),d(e.lastCueEndTimestamp<=c.timestamp+c.duration),s.add(Math.max(c.timestamp,e.lastCueEndTimestamp)),s.add(c.timestamp+c.duration);let o=[...s].sort((c,m)=>c-m),n=o[0],a=o[1]??n;if(r=a)break;W.lastIndex=0;let g=W.test(m.text),E=m.timestamp+m.duration,y=e.cueToSourceId.get(m);if(y===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.#x())}else s=o>=.5}s&&(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||_(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.#s.some(r=>r.track===e))return;e:for(;;){let e=null,r=1/0;for(let o of this.#s){if(o.sampleQueue.length===0&&!o.track.source.closed)break e;o.sampleQueue.length>0&&o.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.#s){u.currentChunk.offset=this.#e.getPos(),u.currentChunk.moofOffset=s;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(o));let a=ve(r,this.#s);this.#r.writeBox(a),this.#e.seek(n);for(let u of this.#s)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.#s.find(s=>s.track===e);r&&this.#g(r,1/0)}this.#t==="fragmented"&&this.#k()}finalize(){for(let e of this.#s)e.type==="subtitle"&&e.track.source.codec==="webvtt"&&this.#g(e,1/0);if(this.#t==="fragmented"){for(let e of this.#s){for(let r of e.sampleQueue)this.#C(e,r);this.#f(e)}this.#x(!1)}else for(let e of this.#s)this.#f(e),this.#w(e);if(this.#t==="in-memory"){d(this.#n);let e;for(let s=0;s<2;s++){let o=G(this.#s,this.#d),n=this.#r.measureBox(o);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=G(this.#s,this.#d);this.#r.writeBox(r),this.#n.size=e,this.#r.writeBox(this.#n);for(let s of this.#u)for(let o of s.samples)d(o.data),this.#e.write(o.data),o.data=null}else if(this.#t==="fragmented"){let e=this.#e.getPos(),r=Ze(this.#s);this.#r.writeBox(r);let s=this.#e.getPos()-e;this.#e.seek(this.#e.getPos()-4),this.#r.writeU32(s)}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 s=G(this.#s,this.#d);if(typeof this.#t=="object"){this.#e.seek(this.#l),this.#r.writeBox(s);let o=e-this.#e.getPos();this.#r.writeBox(qe(o))}else this.#r.writeBox(s)}}};var J=class{constructor(t){this.value=t}},$=class{constructor(t){this.value=t}},ee=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,ot="https://github.com/Vanilagy/webm-muxer",nt=6,at=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},Te=class extends D{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.#o=[];this.#a=null;this.#c=null;this.#l=null;this.#n=null;this.#s=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;#o;#a;#c;#l;#n;#s;#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 s=0;switch(r){case 6:this.#t.setUint8(s++,e/2**40|0);case 5:this.#t.setUint8(s++,e/2**32|0);case 4:this.#t.setUint8(s++,e>>24);case 3:this.#t.setUint8(s++,e>>16);case 2:this.#t.setUint8(s++,e>>8);case 1:this.#t.setUint8(s++,e);break;default:throw new Error("Bad UINT size "+r)}this.#e.write(this.#i.subarray(0,s))}#p(e,r=Ve(e)){e<0&&(e+=2**(r*8)),this.#f(e,r)}writeEBMLVarInt(e,r=st(e)){let s=0;switch(r){case 1:this.#t.setUint8(s++,128|e);break;case 2:this.#t.setUint8(s++,64|e>>8),this.#t.setUint8(s++,e);break;case 3:this.#t.setUint8(s++,32|e>>16),this.#t.setUint8(s++,e>>8),this.#t.setUint8(s++,e);break;case 4:this.#t.setUint8(s++,16|e>>24),this.#t.setUint8(s++,e>>16),this.#t.setUint8(s++,e>>8),this.#t.setUint8(s++,e);break;case 5:this.#t.setUint8(s++,8|e/2**32&7),this.#t.setUint8(s++,e>>24),this.#t.setUint8(s++,e>>16),this.#t.setUint8(s++,e>>8),this.#t.setUint8(s++,e);break;case 6:this.#t.setUint8(s++,4|e/2**40&3),this.#t.setUint8(s++,e/2**32|0),this.#t.setUint8(s++,e>>24),this.#t.setUint8(s++,e>>16),this.#t.setUint8(s++,e>>8),this.#t.setUint8(s++,e);break;default:throw new Error("Bad EBML VINT size "+r)}this.#e.write(this.#i.subarray(0,s))}#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(),s=e.size===-1?1:e.size??4;e.size===-1?this.#A(255):this.#e.seek(this.#e.getPos()+s);let o=this.#e.getPos();if(this.dataOffsets.set(e,o),this.writeEBML(e.data),e.size!==-1){let n=this.#e.getPos()-o,a=this.#e.getPos();this.#e.seek(r),this.writeEBMLVarInt(n,s),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 J)this.writeEBMLVarInt(4),this.#g(e.data.value);else if(e.data instanceof $)this.writeEBMLVarInt(8),this.#h(e.data.value);else if(e.data instanceof ee){let r=e.size??Ve(e.data.value);this.writeEBMLVarInt(r),this.#p(e.data.value,r)}}}beforeTrackAdd(e){if(this.#r instanceof j)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.#I(),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 j?"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]),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.#l=o}#x(){let e={id:17545,data:new $(0)};this.#s=e;let r={id:357149030,data:[{id:2807729,data:1e6},{id:19840,data:ot},{id:22337,data:ot},this.#r.options.streamable?null:e]};this.#c=r}#E(){let e={id:374648427,data:[]};this.#n=e;for(let r of this.#o)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 s=r.info.decoderConfig.colorSpace;return oe(s)?{id:21936,data:[{id:21937,data:F[s.matrix]},{id:21946,data:N[s.transfer]},{id:21947,data:B[s.primaries]},{id:21945,data:[1,2][Number(s.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 J(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:nt,data:[this.#r.options.streamable?null:this.#l,this.#c,this.#n]};this.#a=e,this.writeEBML(e)}#I(){this.#d={id:475249515,data:[]}}get#y(){return d(this.#a),this.dataOffsets.get(this.#a)}#z(e,r){let s=this.#o.find(n=>n.track===e);if(s)return s;me(r),d(r),d(r.decoderConfig),d(r.decoderConfig.codedWidth!==void 0),d(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.#o.push(o),this.#o.sort((n,a)=>n.track.id-a.track.id),o}#U(e,r){let s=this.#o.find(n=>n.track===e);if(s)return s;he(r),d(r),d(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.#o.push(o),this.#o.sort((n,a)=>n.track.id-a.track.id),o}#P(e,r){let s=this.#o.find(n=>n.track===e);if(s)return s;pe(r),d(r),d(r.config);let o={track:e,type:"subtitle",info:{config:r.config},chunkQueue:[],lastWrittenMsTimestamp:null};return this.#o.push(o),this.#o.sort((n,a)=>n.track.id-a.track.id),o}addEncodedVideoChunk(e,r,s){let o=this.#z(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.#v(n,a,(r.duration??0)/1e6,r.type);e.source.codec==="vp9"&&this.#R(o,u),o.chunkQueue.push(u),this.#S()}addEncodedAudioChunk(e,r,s){let o=this.#U(e,s),n=new Uint8Array(r.byteLength);r.copyTo(n);let a=this.validateAndNormalizeTimestamp(o.track,r.timestamp,r.type==="key"),u=this.#v(n,a,(r.duration??0)/1e6,r.type);o.chunkQueue.push(u),this.#S()}addSubtitleCue(e,r,s){let o=this.#P(e,s),n=this.validateAndNormalizeTimestamp(o.track,1e6*r.timestamp,!0),a=r.text,u=Math.floor(n*1e3);W.lastIndex=0,a=a.replace(W,g=>{let y=ae(g.slice(1,-1))-u;return`<${ue(y)}>`});let f=x.encode(a),c=`${r.settings??""} +`,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??""} ${r.identifier??""} -${r.notes??""}`,m=this.#v(f,n,r.duration,"key",c.trim()?x.encode(c):null);o.chunkQueue.push(m),this.#S()}#S(){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 o of this.#o){if(o.chunkQueue.length===0&&!o.track.source.closed)break e;o.chunkQueue.length>0&&o.chunkQueue[0].timestamp=2&&s++;let f={rgb:7,bt709:2,bt470bg:1,smpte170m:3}[e.info.decoderConfig.colorSpace.matrix];He(r.data,s+0,s+3,f)}#v(e,r,s,o,n=null){return{data:e,type:o,timestamp:r,duration:s,additions:n}}#V(e,r){this.#a||(this.#E(),this.#O());let s=Math.floor(1e3*r.timestamp),o=this.#o.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||o&&s-this.#m>=1e3)&&this.#_(s);let n=s-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 ee(e.lastWrittenMsTimestamp-s)}: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,s+c),e.lastWrittenMsTimestamp=s,this.#b.add(e)}#_(e){this.#u&&!this.#r.options.streamable&&this.#M(),this.#u={id:524531317,size:this.#r.options.streamable?-1:at,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,at),this.#e.seek(r);let s=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(o=>({id:183,data:[{id:247,data:o.track.id},{id:241,data:s}]}))]})}onTrackClose(){this.#S()}finalize(){this.#a||(this.#E(),this.#O());for(let e of this.#o)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,nt),this.#s.data=new $(this.#T),this.#e.seek(this.offsets.get(this.#s)),this.writeEBML(this.#s),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 Q=class{},Ie=class extends Q{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 be(e,this)}},ge=class extends Q{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 Te(e,this)}},j=class extends ge{};var Ce=["avc","hevc","vp8","vp9","av1"],ye=["aac","opus"],ut=["webvtt"],te=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)}},V=class extends te{constructor(e){super();this.connectedTrack=null;if(!Ce.includes(e))throw new TypeError(`Invalid video codec '${e}'. Must be one of: ${Ce.join(", ")}.`);this.codec=e}},ze=class extends V{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(!Ce.includes(i.codec))throw new TypeError(`Invalid video codec '${i.codec}'. Must be one of: ${Ce.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'.")},re=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:rt(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()}},Ue=class extends V{constructor(t){super(t.codec),this.encoder=new re(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 V{constructor(e,r){if(!(e instanceof HTMLCanvasElement))throw new TypeError("canvas must be an HTMLCanvasElement.");super(r.codec);this.canvas=e;this.encoder=new re(this,r)}digest(e,r=0){if(!Number.isFinite(e)||e<0)throw new TypeError("timestamp must be a non-negative number.");if(!Number.isFinite(r)||r<0)throw new TypeError("duration must be a non-negative number.");let s=new VideoFrame(this.canvas,{timestamp:Math.round(1e6*e),duration:Math.round(1e6*r),alpha:"discard"});this.encoder.digest(s),s.close()}flush(){return this.encoder.flush()}},Re=class extends V{constructor(e,r){if(!(e instanceof MediaStreamTrack)||e.kind!=="video")throw new TypeError("track must be a video MediaStreamTrack.");super(r.codec);this.track=e;this.abortController=null;this.offsetTimestamps=!0;this.encoder=new re(this,r)}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()}},M=class extends te{constructor(e){super();this.connectedTrack=null;if(!ye.includes(e))throw new TypeError(`Invalid audio codec '${e}'. Must be one of: ${ye.join(", ")}.`);this.codec=e}},_e=class extends M{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(!ye.includes(i.codec))throw new TypeError(`Invalid audio codec '${i.codec}'. Must be one of: ${ye.join(", ")}.`);if(!Number.isInteger(i.bitrate)||i.bitrate<=0)throw new TypeError("config.bitrate must be a positive integer.")},ie=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:it(this.codecConfig.codec,t.numberOfChannels,t.sampleRate),numberOfChannels:t.numberOfChannels,sampleRate:t.sampleRate,bitrate:this.codecConfig.bitrate}))}async flush(){return this.encoder?.flush()}},Be=class extends M{constructor(t){super(t.codec),this.encoder=new ie(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()}},Ne=class extends M{constructor(e){super(e.codec);this.accumulatedFrameCount=0;this.encoder=new ie(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()}},se=class extends te{constructor(e){super();this.connectedTrack=null;if(!ut.includes(e))throw new TypeError(`Invalid subtitle codec '${e}'. Must be one of: ${ut.join(", ")}.`);this.codec=e}},We=class extends se{constructor(t){super(t),this.parser=new ne({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 De=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 Q))throw new TypeError("options.format must be an OutputFormat.");if(!(t.target instanceof O))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 V))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 M))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 se))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 s={id:this.tracks.length+1,output:this,type:t,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 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{L as ArrayBufferTarget,Ne as AudioBufferSource,Be as AudioDataSource,Pe as CanvasSource,_e as EncodedAudioChunkSource,ze as EncodedVideoChunkSource,Ee as FileSystemWritableFileStreamTarget,Fe as MediaStreamAudioTrackSource,Re as MediaStreamVideoTrackSource,ge as MkvOutputFormat,Ie as Mp4OutputFormat,De as Output,q as StreamTarget,O as Target,We as TextSubtitleSource,Ue as VideoFrameSource,j as WebMOutputFormat}; +${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}; diff --git a/dist/metamuxer.mjs b/dist/metamuxer.mjs index abe00fe..843339e 100644 --- a/dist/metamuxer.mjs +++ b/dist/metamuxer.mjs @@ -154,7 +154,7 @@ var formatSubtitleTimestamp = (timestamp) => { return hours.toString().padStart(2, "0") + ":" + minutes.toString().padStart(2, "0") + ":" + seconds.toString().padStart(2, "0") + "." + milliseconds.toString().padStart(3, "0"); }; -// src/isobmff/isobmff_boxes.ts +// src/isobmff/isobmff-boxes.ts var IsobmffBoxWriter = class { constructor(writer) { this.writer = writer; @@ -562,17 +562,17 @@ var stsd = (trackData) => { let sampleDescription; if (trackData.type === "video") { sampleDescription = videoSampleDescription( - VIDEO_CODEC_TO_BOX_NAME[trackData.track.source.codec], + VIDEO_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData ); } else if (trackData.type === "audio") { sampleDescription = soundSampleDescription( - AUDIO_CODEC_TO_BOX_NAME[trackData.track.source.codec], + AUDIO_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData ); } else if (trackData.type === "subtitle") { sampleDescription = subtitleSampleDescription( - SUBTITLE_CODEC_TO_BOX_NAME[trackData.track.source.codec], + SUBTITLE_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData ); } @@ -614,7 +614,7 @@ var videoSampleDescription = (compressionType, trackData) => box(compressionType i16(65535) // Pre-defined ], [ - VIDEO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source.codec](trackData), + VIDEO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData), colorSpaceIsComplete(trackData.info.decoderConfig.colorSpace) ? colr(trackData) : null ]); var colr = (trackData) => box("colr", [ @@ -702,7 +702,7 @@ var soundSampleDescription = (compressionType, trackData) => box(compressionType fixed_16_16(trackData.info.sampleRate) // Sample rate ], [ - AUDIO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source.codec](trackData) + AUDIO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData) ]); var esds = (trackData) => { let description = toUint8Array(trackData.info.decoderConfig.description ?? new ArrayBuffer(0)); @@ -779,7 +779,7 @@ var subtitleSampleDescription = (compressionType, trackData) => box(compressionT u16(1) // Data reference index ], [ - SUBTITLE_CODEC_TO_CONFIGURATION_BOX[trackData.track.source.codec](trackData) + SUBTITLE_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData) ]); var vttC = (trackData) => box("vttC", [ ...textEncoder.encode(trackData.info.config.description) @@ -1077,12 +1077,12 @@ var Muxer = class { } timestampInfo = { timestampOffset: timestampInSeconds, - maxTimestamp: track.source.offsetTimestamps ? 0 : timestampInSeconds, - lastKeyFrameTimestamp: track.source.offsetTimestamps ? 0 : timestampInSeconds + maxTimestamp: track.source._offsetTimestamps ? 0 : timestampInSeconds, + lastKeyFrameTimestamp: track.source._offsetTimestamps ? 0 : timestampInSeconds }; this.trackTimestampInfo.set(track, timestampInfo); } - if (track.source.offsetTimestamps) { + if (track.source._offsetTimestamps) { timestampInSeconds -= timestampInfo.timestampOffset; } if (timestampInSeconds < 0) { @@ -1113,7 +1113,8 @@ var ArrayBufferTarget = class extends Target { super(...arguments); this.buffer = null; } - createWriter() { + /** @internal */ + _createWriter() { return new ArrayBufferTargetWriter(this); } }; @@ -1141,7 +1142,8 @@ var StreamTarget = class extends Target { throw new TypeError("options.chunkSize, when provided, must be a positive integer."); } } - createWriter() { + /** @internal */ + _createWriter() { return this.options.chunked ? new ChunkedStreamTargetWriter(this) : new StreamTargetWriter(this); } }; @@ -1162,7 +1164,8 @@ var FileSystemWritableFileStreamTarget = class extends Target { } } } - createWriter() { + /** @internal */ + _createWriter() { return new FileSystemWritableFileStreamTargetWriter(this); } }; @@ -1597,7 +1600,7 @@ var validateSubtitleMetadata = (metadata) => { } }; -// src/isobmff/isobmff_muxer.ts +// src/isobmff/isobmff-muxer.ts var GLOBAL_TIMESCALE = 1e3; var TIMESTAMP_OFFSET = 2082844800; var intoTimescale = (timeInSeconds, timescale, round = true) => { @@ -1609,7 +1612,7 @@ var IsobmffMuxer = class extends Muxer { super(output); this.timestampsMustStartAtZero = true; this.#auxTarget = new ArrayBufferTarget(); - this.#auxWriter = this.#auxTarget.createWriter(); + this.#auxWriter = this.#auxTarget._createWriter(); this.#auxBoxWriter = new IsobmffBoxWriter(this.#auxWriter); this.#ftypSize = null; this.#mdat = null; @@ -1617,7 +1620,7 @@ var IsobmffMuxer = class extends Muxer { this.#creationTime = Math.floor(Date.now() / 1e3) + TIMESTAMP_OFFSET; this.#finalizedChunks = []; this.#nextFragmentNumber = 1; - this.#writer = output.writer; + 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); @@ -1639,7 +1642,7 @@ var IsobmffMuxer = class extends Muxer { #finalizedChunks; #nextFragmentNumber; start() { - const holdsAvc = this.output.tracks.some((x) => x.type === "video" && x.source.codec === "avc"); + const holdsAvc = this.output._tracks.some((x) => x.type === "video" && x.source._codec === "avc"); this.#boxWriter.writeBox(ftyp({ holdsAvc, fragmented: this.#fastStart === "fragmented" @@ -1775,7 +1778,7 @@ var IsobmffMuxer = class extends Muxer { addSubtitleCue(track, cue, meta) { const trackData = this.#getSubtitleTrackData(track, meta); this.validateAndNormalizeTimestamp(trackData.track, 1e6 * cue.timestamp, true); - if (track.source.codec === "webvtt") { + if (track.source._codec === "webvtt") { trackData.cueQueue.push(cue); this.#processWebVTTCues(trackData, cue.timestamp); } else { @@ -1996,8 +1999,8 @@ var IsobmffMuxer = class extends Muxer { } #interleaveSamples() { assert(this.#fastStart === "fragmented"); - for (const track of this.output.tracks) { - if (!track.source.closed && !this.#trackDatas.some((x) => x.track === track)) { + for (const track of this.output._tracks) { + if (!track.source._closed && !this.#trackDatas.some((x) => x.track === track)) { return; } } @@ -2006,7 +2009,7 @@ var IsobmffMuxer = class extends Muxer { let trackWithMinTimestamp = null; let minTimestamp = Infinity; for (let trackData of this.#trackDatas) { - if (trackData.sampleQueue.length === 0 && !trackData.track.source.closed) { + if (trackData.sampleQueue.length === 0 && !trackData.track.source._closed) { break outer; } if (trackData.sampleQueue.length > 0 && trackData.sampleQueue[0].timestamp < minTimestamp) { @@ -2071,7 +2074,7 @@ var IsobmffMuxer = class extends Muxer { } } onTrackClose(track) { - if (track.type === "subtitle" && track.source.codec === "webvtt") { + if (track.type === "subtitle" && track.source._codec === "webvtt") { let trackData = this.#trackDatas.find((x) => x.track === track); if (trackData) { this.#processWebVTTCues(trackData, Infinity); @@ -2084,7 +2087,7 @@ var IsobmffMuxer = class extends Muxer { /** 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) { - if (trackData.type === "subtitle" && trackData.track.source.codec === "webvtt") { + if (trackData.type === "subtitle" && trackData.track.source._codec === "webvtt") { this.#processWebVTTCues(trackData, Infinity); } } @@ -2225,7 +2228,7 @@ var measureEBMLVarInt = (value) => { } }; -// src/matroska/matroska_muxer.ts +// src/matroska/matroska-muxer.ts var MAX_CHUNK_LENGTH_MS = 2 ** 15; var APP_NAME = "https://github.com/Vanilagy/webm-muxer"; var SEGMENT_SIZE_BYTES = 6; @@ -2269,7 +2272,7 @@ var MatroskaMuxer = class extends Muxer { this.#currentClusterMsTimestamp = null; this.#trackDatasInCurrentCluster = /* @__PURE__ */ new Set(); this.#duration = 0; - this.#writer = output.writer; + this.#writer = output._writer; this.#format = format; if (this.#format.options.streamable) { this.#writer.ensureMonotonicity = true; @@ -2431,15 +2434,15 @@ var MatroskaMuxer = class extends Muxer { return; } if (track.type === "video") { - if (!["vp8", "vp9", "av1"].includes(track.source.codec)) { + if (!["vp8", "vp9", "av1"].includes(track.source._codec)) { throw new Error(`WebM only supports VP8, VP9 and AV1 as video codecs. Switching to MKV removes this restriction.`); } } else if (track.type === "audio") { - if (!["opus", "vorbis"].includes(track.source.codec)) { + if (!["opus", "vorbis"].includes(track.source._codec)) { throw new Error(`WebM only supports Opus and Vorbis as audio codecs. Switching to MKV removes this restriction.`); } } else if (track.type === "subtitle") { - if (track.source.codec !== "webvtt") { + if (track.source._codec !== "webvtt") { throw new Error(`WebM only supports WebVTT as subtitle codec. Switching to MKV removes this restriction.`); } } else { @@ -2510,7 +2513,7 @@ var MatroskaMuxer = class extends Muxer { { id: 215 /* TrackNumber */, data: trackData.track.id }, { id: 29637 /* TrackUID */, data: trackData.track.id }, { id: 131 /* TrackType */, data: TRACK_TYPE_MAP[trackData.type] }, - { id: 134 /* CodecID */, data: CODEC_STRING_MAP[trackData.track.source.codec] }, + { id: 134 /* CodecID */, data: CODEC_STRING_MAP[trackData.track.source._codec] }, ...trackData.type === "video" ? [ trackData.info.decoderConfig.description ? { id: 25506 /* CodecPrivate */, data: toUint8Array(trackData.info.decoderConfig.description) } : null, trackData.track.metadata.frameRate ? { id: 2352003 /* DefaultDuration */, data: 1e9 / trackData.track.metadata.frameRate } : null, @@ -2643,7 +2646,7 @@ var MatroskaMuxer = class extends Muxer { 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); + if (track.source._codec === "vp9") this.#fixVP9ColorSpace(trackData, videoChunk); trackData.chunkQueue.push(videoChunk); this.#interleaveChunks(); } @@ -2676,8 +2679,8 @@ ${cue.notes ?? ""}`; this.#interleaveChunks(); } #interleaveChunks() { - for (const track of this.output.tracks) { - if (!track.source.closed && !this.#trackDatas.some((x) => x.track === track)) { + for (const track of this.output._tracks) { + if (!track.source._closed && !this.#trackDatas.some((x) => x.track === track)) { return; } } @@ -2686,7 +2689,7 @@ ${cue.notes ?? ""}`; let trackWithMinTimestamp = null; let minTimestamp = Infinity; for (let trackData of this.#trackDatas) { - if (trackData.chunkQueue.length === 0 && !trackData.track.source.closed) { + if (trackData.chunkQueue.length === 0 && !trackData.track.source._closed) { break outer; } if (trackData.chunkQueue.length > 0 && trackData.chunkQueue[0].timestamp < minTimestamp) { @@ -2752,7 +2755,7 @@ ${cue.notes ?? ""}`; } let msTimestamp = Math.floor(1e3 * chunk.timestamp); const keyFrameQueuedEverywhere = this.#trackDatas.every((otherTrackData) => { - if (otherTrackData.track.source.closed) { + if (otherTrackData.track.source._closed) { return true; } if (trackData === otherTrackData) { @@ -2880,7 +2883,7 @@ ${cue.notes ?? ""}`; } }; -// src/output_format.ts +// src/output-format.ts var OutputFormat = class { }; var Mp4OutputFormat = class extends OutputFormat { @@ -2894,7 +2897,8 @@ var Mp4OutputFormat = class extends OutputFormat { super(); this.options = options; } - createMuxer(output) { + /** @internal */ + _createMuxer(output) { return new IsobmffMuxer(output, this); } }; @@ -2909,7 +2913,8 @@ var MkvOutputFormat2 = class extends OutputFormat { super(); this.options = options; } - createMuxer(output) { + /** @internal */ + _createMuxer(output) { return new MatroskaMuxer(output, this); } }; @@ -2922,55 +2927,60 @@ var AUDIO_CODECS = ["aac", "opus"]; var SUBTITLE_CODECS = ["webvtt"]; var MediaSource = class { constructor() { - this.connectedTrack = null; - this.closed = false; - this.offsetTimestamps = false; + /** @internal */ + this._connectedTrack = null; + /** @internal */ + this._closed = false; + /** @internal */ + this._offsetTimestamps = false; } - // TODO this is also just internal: - ensureValidDigest() { - if (!this.connectedTrack) { + /** @internal */ + _ensureValidDigest() { + if (!this._connectedTrack) { throw new Error("Cannot call digest without connecting the source to an output track."); } - if (!this.connectedTrack.output.started) { + if (!this._connectedTrack.output._started) { throw new Error("Cannot call digest before output has been started."); } - if (this.connectedTrack.output.finalizing) { + if (this._connectedTrack.output._finalizing) { throw new Error("Cannot call digest after output has started finalizing."); } - if (this.closed) { + if (this._closed) { throw new Error("Cannot call digest after source has been closed."); } } - // TODO: These are should not be called from the outside lib - start() { + /** @internal */ + _start() { } - async flush() { + /** @internal */ + async _flush() { } close() { - if (this.closed) { + if (this._closed) { throw new Error("Source already closed."); } - if (!this.connectedTrack) { + if (!this._connectedTrack) { throw new Error("Cannot call close without connecting the source to an output track."); } - if (!this.connectedTrack.output.started) { + if (!this._connectedTrack.output._started) { throw new Error("Cannot call close before output has been started."); } - this.closed = true; - if (this.connectedTrack.output.finalizing) { + this._closed = true; + if (this._connectedTrack.output._finalizing) { return; } - this.connectedTrack.output.muxer.onTrackClose(this.connectedTrack); + this._connectedTrack.output._muxer.onTrackClose(this._connectedTrack); } }; var VideoSource = class extends MediaSource { constructor(codec) { super(); - this.connectedTrack = null; + /** @internal */ + this._connectedTrack = null; if (!VIDEO_CODECS.includes(codec)) { throw new TypeError(`Invalid video codec '${codec}'. Must be one of: ${VIDEO_CODECS.join(", ")}.`); } - this.codec = codec; + this._codec = codec; } }; var EncodedVideoChunkSource = class extends VideoSource { @@ -2981,8 +2991,8 @@ var EncodedVideoChunkSource = class extends VideoSource { if (!(chunk instanceof EncodedVideoChunk)) { throw new TypeError("chunk must be an EncodedVideoChunk."); } - this.ensureValidDigest(); - this.connectedTrack?.output.muxer.addEncodedVideoChunk(this.connectedTrack, chunk, meta); + this._ensureValidDigest(); + this._connectedTrack?.output._muxer.addEncodedVideoChunk(this._connectedTrack, chunk, meta); } }; var KEY_FRAME_INTERVAL = 5; @@ -3011,7 +3021,7 @@ var VideoEncoderWrapper = class { validateVideoCodecConfig(codecConfig); } digest(videoFrame) { - this.source.ensureValidDigest(); + this.source._ensureValidDigest(); if (this.lastWidth !== null && this.lastHeight !== null) { if (videoFrame.codedWidth !== this.lastWidth || videoFrame.codedHeight !== this.lastHeight) { throw new Error(`Video frame size must remain constant. Expected ${this.lastWidth}x${this.lastHeight}, got ${videoFrame.codedWidth}x${videoFrame.codedHeight}.`); @@ -3031,7 +3041,7 @@ var VideoEncoderWrapper = class { return; } this.encoder = new VideoEncoder({ - output: (chunk, meta) => this.source.connectedTrack?.output.muxer.addEncodedVideoChunk(this.source.connectedTrack, chunk, meta), + output: (chunk, meta) => this.source._connectedTrack?.output._muxer.addEncodedVideoChunk(this.source._connectedTrack, chunk, meta), error: (error) => console.error("Video encode error:", error) }); this.encoder.configure({ @@ -3039,7 +3049,7 @@ var VideoEncoderWrapper = class { width: videoFrame.codedWidth, height: videoFrame.codedHeight, bitrate: this.codecConfig.bitrate, - framerate: this.source.connectedTrack?.metadata.frameRate, + framerate: this.source._connectedTrack?.metadata.frameRate, latencyMode: this.codecConfig.latencyMode }); } @@ -3050,16 +3060,17 @@ var VideoEncoderWrapper = class { var VideoFrameSource = class extends VideoSource { constructor(codecConfig) { super(codecConfig.codec); - this.encoder = new VideoEncoderWrapper(this, codecConfig); + this._encoder = new VideoEncoderWrapper(this, codecConfig); } digest(videoFrame) { if (!(videoFrame instanceof VideoFrame)) { throw new TypeError("videoFrame must be a VideoFrame."); } - this.encoder.digest(videoFrame); + this._encoder.digest(videoFrame); } - flush() { - return this.encoder.flush(); + /** @internal */ + _flush() { + return this._encoder.flush(); } }; var CanvasSource = class extends VideoSource { @@ -3068,8 +3079,8 @@ var CanvasSource = class extends VideoSource { throw new TypeError("canvas must be an HTMLCanvasElement."); } super(codecConfig.codec); - this.canvas = canvas; - this.encoder = new VideoEncoderWrapper(this, codecConfig); + this._encoder = new VideoEncoderWrapper(this, codecConfig); + this._canvas = canvas; } digest(timestamp, duration = 0) { if (!Number.isFinite(timestamp) || timestamp < 0) { @@ -3078,16 +3089,17 @@ var CanvasSource = class extends VideoSource { if (!Number.isFinite(duration) || duration < 0) { throw new TypeError("duration must be a non-negative number."); } - const frame = new VideoFrame(this.canvas, { + const frame = new VideoFrame(this._canvas, { timestamp: Math.round(1e6 * timestamp), duration: Math.round(1e6 * duration), alpha: "discard" }); - this.encoder.digest(frame); + this._encoder.digest(frame); frame.close(); } - flush() { - return this.encoder.flush(); + /** @internal */ + _flush() { + return this._encoder.flush(); } }; var MediaStreamVideoTrackSource = class extends VideoSource { @@ -3095,44 +3107,53 @@ var MediaStreamVideoTrackSource = class extends VideoSource { if (!(track instanceof MediaStreamTrack) || track.kind !== "video") { throw new TypeError("track must be a video MediaStreamTrack."); } + codecConfig = { + ...codecConfig, + latencyMode: "realtime" + }; super(codecConfig.codec); - this.track = track; - this.abortController = null; - this.offsetTimestamps = true; - this.encoder = new VideoEncoderWrapper(this, codecConfig); + /** @internal */ + this._abortController = null; + /** @internal */ + this._offsetTimestamps = true; + this._encoder = new VideoEncoderWrapper(this, codecConfig); + this._track = track; } - start() { - this.abortController = new AbortController(); - const processor = new MediaStreamTrackProcessor({ track: this.track }); + /** @internal */ + _start() { + this._abortController = new AbortController(); + const processor = new MediaStreamTrackProcessor({ track: this._track }); const consumer = new WritableStream({ write: (videoFrame) => { - this.encoder.digest(videoFrame); + this._encoder.digest(videoFrame); videoFrame.close(); } }); processor.readable.pipeTo(consumer, { - signal: this.abortController.signal + signal: this._abortController.signal }).catch((err) => { if (err instanceof DOMException && err.name === "AbortError") return; console.error("Pipe error:", err); }); } - async flush() { - if (this.abortController) { - this.abortController.abort(); - this.abortController = null; + /** @internal */ + async _flush() { + if (this._abortController) { + this._abortController.abort(); + this._abortController = null; } - await this.encoder.flush(); + await this._encoder.flush(); } }; var AudioSource = class extends MediaSource { constructor(codec) { super(); - this.connectedTrack = null; + /** @internal */ + this._connectedTrack = null; if (!AUDIO_CODECS.includes(codec)) { throw new TypeError(`Invalid audio codec '${codec}'. Must be one of: ${AUDIO_CODECS.join(", ")}.`); } - this.codec = codec; + this._codec = codec; } }; var EncodedAudioChunkSource = class extends AudioSource { @@ -3143,8 +3164,8 @@ var EncodedAudioChunkSource = class extends AudioSource { if (!(chunk instanceof EncodedAudioChunk)) { throw new TypeError("chunk must be an EncodedAudioChunk."); } - this.ensureValidDigest(); - this.connectedTrack?.output.muxer.addEncodedAudioChunk(this.connectedTrack, chunk, meta); + this._ensureValidDigest(); + this._connectedTrack?.output._muxer.addEncodedAudioChunk(this._connectedTrack, chunk, meta); } }; var validateAudioCodecConfig = (config) => { @@ -3168,7 +3189,7 @@ var AudioEncoderWrapper = class { validateAudioCodecConfig(codecConfig); } digest(audioData) { - this.source.ensureValidDigest(); + this.source._ensureValidDigest(); if (this.lastNumberOfChannels !== null && this.lastSampleRate !== null) { if (audioData.numberOfChannels !== this.lastNumberOfChannels || audioData.sampleRate !== this.lastSampleRate) { throw new Error(`Audio parameters must remain constant. Expected ${this.lastNumberOfChannels} channels at ${this.lastSampleRate} Hz, got ${audioData.numberOfChannels} channels at ${audioData.sampleRate} Hz.`); @@ -3186,7 +3207,7 @@ var AudioEncoderWrapper = class { return; } this.encoder = new AudioEncoder({ - output: (chunk, meta) => this.source.connectedTrack?.output.muxer.addEncodedAudioChunk(this.source.connectedTrack, chunk, meta), + output: (chunk, meta) => this.source._connectedTrack?.output._muxer.addEncodedAudioChunk(this.source._connectedTrack, chunk, meta), error: (error) => console.error("Audio encode error:", error) }); this.encoder.configure({ @@ -3203,23 +3224,25 @@ var AudioEncoderWrapper = class { var AudioDataSource = class extends AudioSource { constructor(codecConfig) { super(codecConfig.codec); - this.encoder = new AudioEncoderWrapper(this, codecConfig); + this._encoder = new AudioEncoderWrapper(this, codecConfig); } digest(audioData) { if (!(audioData instanceof AudioData)) { throw new TypeError("audioData must be an AudioData."); } - this.encoder.digest(audioData); + this._encoder.digest(audioData); } - flush() { - return this.encoder.flush(); + /** @internal */ + _flush() { + return this._encoder.flush(); } }; var AudioBufferSource = class extends AudioSource { constructor(codecConfig) { super(codecConfig.codec); - this.accumulatedFrameCount = 0; - this.encoder = new AudioEncoderWrapper(this, codecConfig); + /** @internal */ + this._accumulatedFrameCount = 0; + this._encoder = new AudioEncoderWrapper(this, codecConfig); } digest(audioBuffer) { if (!(audioBuffer instanceof AudioBuffer)) { @@ -3238,15 +3261,16 @@ var AudioBufferSource = class extends AudioSource { sampleRate, numberOfFrames, numberOfChannels, - timestamp: Math.round(1e6 * this.accumulatedFrameCount / sampleRate), + timestamp: Math.round(1e6 * this._accumulatedFrameCount / sampleRate), data }); - this.encoder.digest(audioData); + this._encoder.digest(audioData); audioData.close(); - this.accumulatedFrameCount += numberOfFrames; + this._accumulatedFrameCount += numberOfFrames; } - flush() { - return this.encoder.flush(); + /** @internal */ + _flush() { + return this._encoder.flush(); } }; var MediaStreamAudioTrackSource = class extends AudioSource { @@ -3255,51 +3279,55 @@ var MediaStreamAudioTrackSource = class extends AudioSource { throw new TypeError("track must be an audio MediaStreamTrack."); } super(codecConfig.codec); - this.track = track; - this.abortController = null; - this.offsetTimestamps = true; - this.encoder = new AudioEncoderWrapper(this, codecConfig); + /** @internal */ + this._abortController = null; + this._offsetTimestamps = true; + this._encoder = new AudioEncoderWrapper(this, codecConfig); + this._track = track; } - start() { - this.abortController = new AbortController(); - const processor = new MediaStreamTrackProcessor({ track: this.track }); + /** @internal */ + _start() { + this._abortController = new AbortController(); + const processor = new MediaStreamTrackProcessor({ track: this._track }); const consumer = new WritableStream({ write: (audioData) => { - this.encoder.digest(audioData); + this._encoder.digest(audioData); audioData.close(); } }); processor.readable.pipeTo(consumer, { - signal: this.abortController.signal + signal: this._abortController.signal }).catch((err) => { if (err instanceof DOMException && err.name === "AbortError") return; console.error("Pipe error:", err); }); } - async flush() { - if (this.abortController) { - this.abortController.abort(); - this.abortController = null; + /** @internal */ + async _flush() { + if (this._abortController) { + this._abortController.abort(); + this._abortController = null; } - await this.encoder.flush(); + await this._encoder.flush(); } }; var SubtitleSource = class extends MediaSource { constructor(codec) { super(); - this.connectedTrack = null; + /** @internal */ + this._connectedTrack = null; if (!SUBTITLE_CODECS.includes(codec)) { throw new TypeError(`Invalid subtitle codec '${codec}'. Must be one of: ${SUBTITLE_CODECS.join(", ")}.`); } - this.codec = codec; + this._codec = codec; } }; var TextSubtitleSource = class extends SubtitleSource { constructor(codec) { super(codec); - this.parser = new SubtitleParser({ + this._parser = new SubtitleParser({ codec, - output: (cue, metadata) => this.connectedTrack?.output.muxer.addSubtitleCue(this.connectedTrack, cue, metadata), + output: (cue, metadata) => this._connectedTrack?.output._muxer.addSubtitleCue(this._connectedTrack, cue, metadata), error: (error) => console.error("Subtitle parse error:", error) }); } @@ -3307,17 +3335,20 @@ var TextSubtitleSource = class extends SubtitleSource { if (typeof text !== "string") { throw new TypeError("text must be a string."); } - this.ensureValidDigest(); - this.parser.parse(text); + this._ensureValidDigest(); + this._parser.parse(text); } }; // src/output.ts var Output = class { constructor(options) { - this.tracks = []; - this.started = false; - this.finalizing = false; + /** @internal */ + this._tracks = []; + /** @internal */ + this._started = false; + /** @internal */ + this._finalizing = false; if (!options || typeof options !== "object") { throw new TypeError("options must be an object."); } @@ -3331,8 +3362,8 @@ var Output = class { throw new Error("Target is already used for another output."); } options.target.output = this; - this.writer = options.target.createWriter(); - this.muxer = options.format.createMuxer(this); + this._writer = options.target._createWriter(); + this._muxer = options.format._createMuxer(this); } addVideoTrack(source, metadata = {}) { if (!(source instanceof VideoSource)) { @@ -3351,7 +3382,7 @@ var Output = class { `Invalid video frame rate: ${metadata.frameRate}. Must be a positive integer.` ); } - this.addTrack("video", source, metadata); + this._addTrack("video", source, metadata); } addAudioTrack(source, metadata = {}) { if (!(source instanceof AudioSource)) { @@ -3360,7 +3391,7 @@ var Output = class { if (!metadata || typeof metadata !== "object") { throw new TypeError("metadata must be an object."); } - this.addTrack("audio", source, metadata); + this._addTrack("audio", source, metadata); } addSubtitleTrack(source, metadata = {}) { if (!(source instanceof SubtitleSource)) { @@ -3369,64 +3400,73 @@ var Output = class { if (!metadata || typeof metadata !== "object") { throw new TypeError("metadata must be an object."); } - this.addTrack("subtitle", source, metadata); + this._addTrack("subtitle", source, metadata); } - addTrack(type, source, metadata) { - if (this.started) { + /** @internal */ + _addTrack(type, source, metadata) { + if (this._started) { throw new Error("Cannot add track after output has started."); } - if (source.connectedTrack) { + if (source._connectedTrack) { throw new Error("Source is already used for a track."); } const track = { - id: this.tracks.length + 1, + id: this._tracks.length + 1, output: this, type, source, metadata }; - this.muxer.beforeTrackAdd(track); - this.tracks.push(track); - source.connectedTrack = track; + this._muxer.beforeTrackAdd(track); + this._tracks.push(track); + source._connectedTrack = track; } start() { - if (this.started) { + if (this._started) { throw new Error("Output already started."); } - this.started = true; - this.muxer.start(); - for (const track of this.tracks) { - track.source.start(); + this._started = true; + this._muxer.start(); + for (const track of this._tracks) { + track.source._start(); } } async finalize() { - if (this.finalizing) { + if (this._finalizing) { throw new Error("Cannot call finalize twice."); } - this.finalizing = true; - const promises = this.tracks.map((x) => x.source.flush()); + this._finalizing = true; + const promises = this._tracks.map((x) => x.source._flush()); await Promise.all(promises); - this.muxer.finalize(); - this.writer.flush(); - this.writer.finalize(); + this._muxer.finalize(); + this._writer.flush(); + this._writer.finalize(); } }; export { + AUDIO_CODECS, ArrayBufferTarget, AudioBufferSource, AudioDataSource, + AudioSource, CanvasSource, EncodedAudioChunkSource, EncodedVideoChunkSource, FileSystemWritableFileStreamTarget, + MediaSource, MediaStreamAudioTrackSource, MediaStreamVideoTrackSource, MkvOutputFormat2 as MkvOutputFormat, Mp4OutputFormat, Output, + OutputFormat, + SUBTITLE_CODECS, StreamTarget, + SubtitleSource, Target, TextSubtitleSource, + VIDEO_CODECS, VideoFrameSource, + VideoSource, WebMOutputFormat }; diff --git a/package-lock.json b/package-lock.json index ee8840e..3d71e49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@types/dom-webcodecs": "^0.1.11" }, "devDependencies": { + "@microsoft/api-extractor": "^7.48.0", "esbuild": "^0.23.1", "typescript": "^5.5.4" } @@ -401,6 +402,158 @@ "node": ">=18" } }, + "node_modules/@microsoft/api-extractor": { + "version": "7.48.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.48.0.tgz", + "integrity": "sha512-FMFgPjoilMUWeZXqYRlJ3gCVRhB7WU/HN88n8OLqEsmsG4zBdX/KQdtJfhq95LQTQ++zfu0Em1LLb73NqRCLYQ==", + "dev": true, + "dependencies": { + "@microsoft/api-extractor-model": "7.30.0", + "@microsoft/tsdoc": "~0.15.1", + "@microsoft/tsdoc-config": "~0.17.1", + "@rushstack/node-core-library": "5.10.0", + "@rushstack/rig-package": "0.5.3", + "@rushstack/terminal": "0.14.3", + "@rushstack/ts-command-line": "4.23.1", + "lodash": "~4.17.15", + "minimatch": "~3.0.3", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.4.2" + }, + "bin": { + "api-extractor": "bin/api-extractor" + } + }, + "node_modules/@microsoft/api-extractor-model": { + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.30.0.tgz", + "integrity": "sha512-26/LJZBrsWDKAkOWRiQbdVgcfd1F3nyJnAiJzsAgpouPk7LtOIj7PK9aJtBaw/pUXrkotEg27RrT+Jm/q0bbug==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "~0.15.1", + "@microsoft/tsdoc-config": "~0.17.1", + "@rushstack/node-core-library": "5.10.0" + } + }, + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz", + "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==", + "dev": true + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.1.tgz", + "integrity": "sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.15.1", + "ajv": "~8.12.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" + } + }, + "node_modules/@rushstack/node-core-library": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.10.0.tgz", + "integrity": "sha512-2pPLCuS/3x7DCd7liZkqOewGM0OzLyCacdvOe8j6Yrx9LkETGnxul1t7603bIaB8nUAooORcct9fFDOQMbWAgw==", + "dev": true, + "dependencies": { + "ajv": "~8.13.0", + "ajv-draft-04": "~1.0.0", + "ajv-formats": "~3.0.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.22.1", + "semver": "~7.5.4" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/node-core-library/node_modules/ajv": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", + "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@rushstack/rig-package": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.3.tgz", + "integrity": "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==", + "dev": true, + "dependencies": { + "resolve": "~1.22.1", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/@rushstack/terminal": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.14.3.tgz", + "integrity": "sha512-csXbZsAdab/v8DbU1sz7WC2aNaKArcdS/FPmXMOXEj/JBBZMvDK0+1b4Qao0kkG0ciB1Qe86/Mb68GjH6/TnMw==", + "dev": true, + "dependencies": { + "@rushstack/node-core-library": "5.10.0", + "supports-color": "~8.1.1" + }, + "peerDependencies": { + "@types/node": "*" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@rushstack/ts-command-line": { + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.1.tgz", + "integrity": "sha512-40jTmYoiu/xlIpkkRsVfENtBq4CW3R4azbL0Vmda+fMwHWqss6wwf/Cy/UJmMqIzpfYc2OTnjYP1ZLD3CmyeCA==", + "dev": true, + "dependencies": { + "@rushstack/terminal": "0.14.3", + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "string-argv": "~0.3.1" + } + }, + "node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "dev": true + }, "node_modules/@types/dom-mediacapture-transform": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@types/dom-mediacapture-transform/-/dom-mediacapture-transform-0.1.10.tgz", @@ -414,6 +567,84 @@ "resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.11.tgz", "integrity": "sha512-yPEZ3z7EohrmOxbk/QTAa0yonMFkNkjnVXqbGb7D4rMr+F1dGQ8ZUFxXkyLLJuiICPejZ0AZE9Rrk9wUCczx4A==" }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, "node_modules/esbuild": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", @@ -453,6 +684,256 @@ "@esbuild/win32-x64": "0.23.1" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { "version": "5.5.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", @@ -465,6 +946,30 @@ "engines": { "node": ">=14.17" } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } } diff --git a/package.json b/package.json index 6c59f3a..d4afe29 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,17 @@ "name": "metamuxer", "version": "0.1.0", "description": "TODO", - "main": "index.js", + "main": "./dist/metamuxer.js", + "module": "./dist/metamuxer.mjs", + "types": "./dist/metamuxer.d.ts", + "exports": { + "types": "./dist/metamuxer.d.ts", + "import": "./dist/metamuxer.mjs", + "require": "./dist/metamuxer.js" + }, "scripts": { - "build": "node build.mjs", + "build": "node build.mjs && tsc && api-extractor run && node append-namespace.mjs", + "build-local": "node build.mjs && tsc && api-extractor run --local --verbose && node append-namespace.mjs", "watch": "node build.mjs --watch" }, "author": "", @@ -14,6 +22,7 @@ "@types/dom-webcodecs": "^0.1.11" }, "devDependencies": { + "@microsoft/api-extractor": "^7.48.0", "esbuild": "^0.23.1", "typescript": "^5.5.4" } diff --git a/src/codec.ts b/src/codec.ts index 77746fc..f9d06b7 100644 --- a/src/codec.ts +++ b/src/codec.ts @@ -14,6 +14,7 @@ export const buildVideoCodecString = (codec: VideoCodec, width: number, height: const profileCompatibility = 0x00; + // TODO this is not correct. Fails for 3000x3000 for example. This logic needs to be more complex // Default to Level 4.1 (0x29) for most content, only bump to Level 5.0 (0x32) for 4K content const levelIndication = (width > 1920 || height > 1080) ? 0x32 : 0x29; diff --git a/src/index.ts b/src/index.ts index 99a83a9..4282401 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ -export { Output } from './output'; -export { Mp4OutputFormat, MkvOutputFormat, WebMOutputFormat } from './output_format'; -export { EncodedVideoChunkSource, VideoFrameSource, CanvasSource, MediaStreamVideoTrackSource, EncodedAudioChunkSource, AudioDataSource, AudioBufferSource, MediaStreamAudioTrackSource, TextSubtitleSource } from './source'; +export { Output, OutputOptions, VideoTrackMetadata, AudioTrackMetadata, SubtitleTrackMetadata } from './output'; +export { OutputFormat, Mp4OutputFormat, MkvOutputFormat, WebMOutputFormat } from './output-format'; +export { VIDEO_CODECS, VideoCodec, VideoCodecConfig, AUDIO_CODECS, AudioCodec, AudioCodecConfig, SUBTITLE_CODECS, SubtitleCodec, MediaSource, VideoSource, EncodedVideoChunkSource, VideoFrameSource, CanvasSource, MediaStreamVideoTrackSource, AudioSource, EncodedAudioChunkSource, AudioDataSource, AudioBufferSource, MediaStreamAudioTrackSource, SubtitleSource, TextSubtitleSource } from './source'; export { Target, ArrayBufferTarget, StreamTarget, FileSystemWritableFileStreamTarget } from './target'; +export { TransformationMatrix } from './misc'; \ No newline at end of file diff --git a/src/isobmff/isobmff_boxes.ts b/src/isobmff/isobmff-boxes.ts similarity index 98% rename from src/isobmff/isobmff_boxes.ts rename to src/isobmff/isobmff-boxes.ts index a4b7231..815be82 100644 --- a/src/isobmff/isobmff_boxes.ts +++ b/src/isobmff/isobmff-boxes.ts @@ -2,7 +2,7 @@ import { toUint8Array, assert, isU32, last, TransformationMatrix, textEncoder, C import { AudioCodec, AudioSource, SubtitleCodec, VideoCodec, VideoSource } from '../source'; import { formatSubtitleTimestamp } from '../subtitles'; import { Writer } from '../writer'; -import { GLOBAL_TIMESCALE, intoTimescale, IsobmffAudioTrackData, IsobmffSubtitleTrackData, IsobmffTrackData, IsobmffVideoTrackData, Sample } from './isobmff_muxer'; +import { GLOBAL_TIMESCALE, intoTimescale, IsobmffAudioTrackData, IsobmffSubtitleTrackData, IsobmffTrackData, IsobmffVideoTrackData, Sample } from './isobmff-muxer'; export class IsobmffBoxWriter { private helper = new Uint8Array(8); @@ -506,17 +506,17 @@ export const stsd = (trackData: IsobmffTrackData) => { if (trackData.type === 'video') { sampleDescription = videoSampleDescription( - VIDEO_CODEC_TO_BOX_NAME[trackData.track.source.codec], + VIDEO_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData ) } else if (trackData.type === 'audio') { sampleDescription = soundSampleDescription( - AUDIO_CODEC_TO_BOX_NAME[trackData.track.source.codec], + AUDIO_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData ); } else if (trackData.type === 'subtitle') { sampleDescription = subtitleSampleDescription( - SUBTITLE_CODEC_TO_BOX_NAME[trackData.track.source.codec], + SUBTITLE_CODEC_TO_BOX_NAME[trackData.track.source._codec], trackData ); } @@ -550,7 +550,7 @@ export const videoSampleDescription = ( u16(0x0018), // Depth i16(0xffff) // Pre-defined ], [ - VIDEO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source.codec](trackData), + VIDEO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData), colorSpaceIsComplete(trackData.info.decoderConfig.colorSpace) ? colr(trackData) : null ]); @@ -644,7 +644,7 @@ export const soundSampleDescription = ( u16(0), // Packet size fixed_16_16(trackData.info.sampleRate) // Sample rate ], [ - AUDIO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source.codec](trackData) + AUDIO_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData) ]); /** MPEG-4 Elementary Stream Descriptor Box. */ @@ -722,7 +722,7 @@ export const subtitleSampleDescription = ( Array(6).fill(0), // Reserved u16(1), // Data reference index ], [ - SUBTITLE_CODEC_TO_CONFIGURATION_BOX[trackData.track.source.codec](trackData) + SUBTITLE_CODEC_TO_CONFIGURATION_BOX[trackData.track.source._codec](trackData) ]) export const vttC = (trackData: IsobmffSubtitleTrackData) => box('vttC', [ diff --git a/src/isobmff/isobmff_muxer.ts b/src/isobmff/isobmff-muxer.ts similarity index 97% rename from src/isobmff/isobmff_muxer.ts rename to src/isobmff/isobmff-muxer.ts index 6249e64..3161d93 100644 --- a/src/isobmff/isobmff_muxer.ts +++ b/src/isobmff/isobmff-muxer.ts @@ -1,9 +1,9 @@ -import { Box, free, ftyp, IsobmffBoxWriter, mdat, mfra, moof, moov, vtta, vttc, vtte } from './isobmff_boxes'; +import { Box, free, ftyp, IsobmffBoxWriter, mdat, mfra, moof, moov, vtta, vttc, vtte } from './isobmff-boxes'; import { Muxer } from '../muxer'; import { Output, OutputAudioTrack, OutputSubtitleTrack, OutputTrack, OutputVideoTrack } from '../output'; import { ArrayBufferTargetWriter, Writer } from '../writer'; import { assert, last, TransformationMatrix } from '../misc'; -import { Mp4OutputFormat } from '../output_format'; +import { Mp4OutputFormat } from '../output-format'; import { inlineTimestampRegex, SubtitleConfig, SubtitleCue, SubtitleMetadata } from '../subtitles'; import { ArrayBufferTarget } from '../target'; import { validateAudioChunkMetadata, validateSubtitleMetadata, validateVideoChunkMetadata } from '../codec'; @@ -92,7 +92,7 @@ export class IsobmffMuxer extends Muxer { #fastStart: NonNullable; #auxTarget = new ArrayBufferTarget(); - #auxWriter = this.#auxTarget.createWriter(); + #auxWriter = this.#auxTarget._createWriter(); #auxBoxWriter = new IsobmffBoxWriter(this.#auxWriter); #ftypSize: number | null = null; @@ -108,7 +108,7 @@ export class IsobmffMuxer extends Muxer { constructor(output: Output, format: Mp4OutputFormat) { super(output); - this.#writer = output.writer; + this.#writer = output._writer; this.#boxWriter = new IsobmffBoxWriter(this.#writer); this.#format = format; @@ -122,7 +122,7 @@ export class IsobmffMuxer extends Muxer { } start() { - const holdsAvc = this.output.tracks.some(x => x.type === 'video' && x.source.codec === 'avc'); + const holdsAvc = this.output._tracks.some(x => x.type === 'video' && x.source._codec === 'avc'); // Write the header this.#boxWriter.writeBox(ftyp({ @@ -295,7 +295,7 @@ export class IsobmffMuxer extends Muxer { this.validateAndNormalizeTimestamp(trackData.track, 1e6 * cue.timestamp, true); - if (track.source.codec === 'webvtt') { + if (track.source._codec === 'webvtt') { trackData.cueQueue.push(cue); this.#processWebVTTCues(trackData, cue.timestamp); } else { @@ -603,8 +603,8 @@ export class IsobmffMuxer extends Muxer { #interleaveSamples() { assert(this.#fastStart === 'fragmented'); - for (const track of this.output.tracks) { - if (!track.source.closed && !this.#trackDatas.some(x => x.track === track)) { + for (const track of this.output._tracks) { + if (!track.source._closed && !this.#trackDatas.some(x => x.track === track)) { return; // We haven't seen a sample from this open track yet } } @@ -615,7 +615,7 @@ export class IsobmffMuxer extends Muxer { let minTimestamp = Infinity; for (let trackData of this.#trackDatas) { - if (trackData.sampleQueue.length === 0 && !trackData.track.source.closed) { + if (trackData.sampleQueue.length === 0 && !trackData.track.source._closed) { break outer; } @@ -704,7 +704,7 @@ export class IsobmffMuxer extends Muxer { } override onTrackClose(track: OutputTrack) { - if (track.type === 'subtitle' && track.source.codec === 'webvtt') { + if (track.type === 'subtitle' && track.source._codec === 'webvtt') { let trackData = this.#trackDatas.find(x => x.track === track) as IsobmffSubtitleTrackData; if (trackData) { this.#processWebVTTCues(trackData, Infinity); @@ -720,7 +720,7 @@ export class IsobmffMuxer extends Muxer { /** 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) { - if (trackData.type === 'subtitle' && trackData.track.source.codec === 'webvtt') { + if (trackData.type === 'subtitle' && trackData.track.source._codec === 'webvtt') { this.#processWebVTTCues(trackData, Infinity); } } diff --git a/src/matroska/matroska_muxer.ts b/src/matroska/matroska-muxer.ts similarity index 98% rename from src/matroska/matroska_muxer.ts rename to src/matroska/matroska-muxer.ts index 63c0328..e3431a1 100644 --- a/src/matroska/matroska_muxer.ts +++ b/src/matroska/matroska-muxer.ts @@ -2,7 +2,7 @@ import { validateAudioChunkMetadata, validateSubtitleMetadata, validateVideoChun import { assert, COLOR_PRIMARIES_MAP, colorSpaceIsComplete, MATRIX_COEFFICIENTS_MAP, readBits, textEncoder, toUint8Array, TRANSFER_CHARACTERISTICS_MAP, writeBits } from '../misc'; import { Muxer } from '../muxer'; import { Output, OutputAudioTrack, OutputSubtitleTrack, OutputTrack, OutputVideoTrack } from '../output'; -import { MkvOutputFormat, WebMOutputFormat } from '../output_format'; +import { MkvOutputFormat, WebMOutputFormat } from '../output-format'; import { AudioCodec, SubtitleCodec, VideoCodec } from '../source'; import { formatSubtitleTimestamp, inlineTimestampRegex, parseSubtitleTimestamp, SubtitleConfig, SubtitleCue, SubtitleMetadata } from '../subtitles'; import { Writer } from '../writer'; @@ -120,7 +120,7 @@ export class MatroskaMuxer extends Muxer { constructor(output: Output, format: MkvOutputFormat) { super(output); - this.#writer = output.writer; + this.#writer = output._writer; this.#format = format; if (this.#format.options.streamable) { @@ -298,15 +298,15 @@ export class MatroskaMuxer extends Muxer { } if (track.type === 'video') { - if (!['vp8', 'vp9', 'av1'].includes(track.source.codec)) { + if (!['vp8', 'vp9', 'av1'].includes(track.source._codec)) { throw new Error(`WebM only supports VP8, VP9 and AV1 as video codecs. Switching to MKV removes this restriction.`); } } else if (track.type === 'audio') { - if (!['opus', 'vorbis'].includes(track.source.codec)) { + if (!['opus', 'vorbis'].includes(track.source._codec)) { throw new Error(`WebM only supports Opus and Vorbis as audio codecs. Switching to MKV removes this restriction.`); } } else if (track.type === 'subtitle') { - if (track.source.codec !== 'webvtt') { + if (track.source._codec !== 'webvtt') { throw new Error(`WebM only supports WebVTT as subtitle codec. Switching to MKV removes this restriction.`); } } else { @@ -388,7 +388,7 @@ export class MatroskaMuxer extends Muxer { { id: EBMLId.TrackNumber, data: trackData.track.id }, { id: EBMLId.TrackUID, data: trackData.track.id }, { id: EBMLId.TrackType, data: TRACK_TYPE_MAP[trackData.type] }, - { id: EBMLId.CodecID, data: CODEC_STRING_MAP[trackData.track.source.codec] }, + { id: EBMLId.CodecID, data: CODEC_STRING_MAP[trackData.track.source._codec] }, ...(trackData.type === 'video' ? [ (trackData.info.decoderConfig.description ? { id: EBMLId.CodecPrivate, data: toUint8Array(trackData.info.decoderConfig.description) } : null), (trackData.track.metadata.frameRate ? { id: EBMLId.DefaultDuration, data: 1e9 / trackData.track.metadata.frameRate } : null), @@ -555,7 +555,7 @@ export class MatroskaMuxer extends Muxer { 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); + if (track.source._codec === 'vp9') this.#fixVP9ColorSpace(trackData, videoChunk); trackData.chunkQueue.push(videoChunk); this.#interleaveChunks(); @@ -601,8 +601,8 @@ export class MatroskaMuxer extends Muxer { } #interleaveChunks() { - for (const track of this.output.tracks) { - if (!track.source.closed && !this.#trackDatas.some(x => x.track === track)) { + for (const track of this.output._tracks) { + if (!track.source._closed && !this.#trackDatas.some(x => x.track === track)) { return; // We haven't seen a sample from this open track yet } } @@ -613,7 +613,7 @@ export class MatroskaMuxer extends Muxer { let minTimestamp = Infinity; for (let trackData of this.#trackDatas) { - if (trackData.chunkQueue.length === 0 && !trackData.track.source.closed) { + if (trackData.chunkQueue.length === 0 && !trackData.track.source._closed) { break outer; } @@ -701,7 +701,7 @@ export class MatroskaMuxer extends Muxer { // 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 => { - if (otherTrackData.track.source.closed) { + if (otherTrackData.track.source._closed) { return true; } diff --git a/src/misc.ts b/src/misc.ts index 0687434..890767e 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -4,6 +4,7 @@ export function assert(x: unknown): asserts x { } } +/** @public */ export type TransformationMatrix = [number, number, number, number, number, number, number, number, number]; export const last = (arr: T[]) => { diff --git a/src/muxer.ts b/src/muxer.ts index b4689d2..8dd874f 100644 --- a/src/muxer.ts +++ b/src/muxer.ts @@ -39,13 +39,13 @@ export abstract class Muxer { timestampInfo = { timestampOffset: timestampInSeconds, - maxTimestamp: track.source.offsetTimestamps ? 0 : timestampInSeconds, - lastKeyFrameTimestamp: track.source.offsetTimestamps ? 0 : timestampInSeconds + maxTimestamp: track.source._offsetTimestamps ? 0 : timestampInSeconds, + lastKeyFrameTimestamp: track.source._offsetTimestamps ? 0 : timestampInSeconds }; this.trackTimestampInfo.set(track, timestampInfo); } - if (track.source.offsetTimestamps) { + if (track.source._offsetTimestamps) { timestampInSeconds -= timestampInfo.timestampOffset; } diff --git a/src/output_format.ts b/src/output-format.ts similarity index 76% rename from src/output_format.ts rename to src/output-format.ts index cc4141a..3d08dfe 100644 --- a/src/output_format.ts +++ b/src/output-format.ts @@ -1,12 +1,15 @@ -import { IsobmffMuxer } from "./isobmff/isobmff_muxer"; -import { MatroskaMuxer } from "./matroska/matroska_muxer"; +import { IsobmffMuxer } from "./isobmff/isobmff-muxer"; +import { MatroskaMuxer } from "./matroska/matroska-muxer"; import { Muxer } from "./muxer"; import { Output } from "./output"; +/** @public */ export abstract class OutputFormat { - abstract createMuxer(output: Output): Muxer; + /** @internal */ + abstract _createMuxer(output: Output): Muxer; } +/** @public */ export class Mp4OutputFormat extends OutputFormat { constructor(public options: { fastStart?: false | 'in-memory' | 'fragmented', @@ -21,11 +24,13 @@ export class Mp4OutputFormat extends OutputFormat { super(); } - override createMuxer(output: Output) { + /** @internal */ + override _createMuxer(output: Output) { return new IsobmffMuxer(output, this); } } +/** @public */ export class MkvOutputFormat extends OutputFormat { constructor(public options: { streamable?: boolean @@ -40,9 +45,11 @@ export class MkvOutputFormat extends OutputFormat { super(); } - override createMuxer(output: Output) { + /** @internal */ + override _createMuxer(output: Output) { return new MatroskaMuxer(output, this); } } +/** @public */ export class WebMOutputFormat extends MkvOutputFormat {} \ No newline at end of file diff --git a/src/output.ts b/src/output.ts index 164fea7..34fc415 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,11 +1,12 @@ import { TransformationMatrix } from "./misc"; import { Muxer } from "./muxer"; -import { OutputFormat } from "./output_format"; +import { OutputFormat } from "./output-format"; import { AudioSource, MediaSource, SubtitleSource, VideoSource } from "./source"; import { Target } from "./target"; import { Writer } from "./writer"; -type OutputOptions = { +/** @public */ +export type OutputOptions = { format: OutputFormat, target: Target }; @@ -31,19 +32,28 @@ export type OutputVideoTrack = OutputTrack & { type: 'video' }; export type OutputAudioTrack = OutputTrack & { type: 'audio' }; export type OutputSubtitleTrack = OutputTrack & { type: 'subtitle' }; -type VideoTrackMetadata = { +/** @public */ +export type VideoTrackMetadata = { rotation?: 0 | 90 | 180 | 270 | TransformationMatrix, // TODO respect this field for Matroska frameRate?: number }; -type AudioTrackMetadata = {}; -type SubtitleTrackMetadata = {}; +/** @public */ +export type AudioTrackMetadata = {}; +/** @public */ +export type SubtitleTrackMetadata = {}; +/** @public */ export class Output { - muxer: Muxer; - writer: Writer; - tracks: OutputTrack[] = []; - started = false; - finalizing = false; + /** @internal */ + _muxer: Muxer; + /** @internal */ + _writer: Writer; + /** @internal */ + _tracks: OutputTrack[] = []; + /** @internal */ + _started = false; + /** @internal */ + _finalizing = false; constructor(options: OutputOptions) { if (!options || typeof options !== 'object') { @@ -61,8 +71,8 @@ export class Output { } options.target.output = this; - this.writer = options.target.createWriter(); - this.muxer = options.format.createMuxer(this); + this._writer = options.target._createWriter(); + this._muxer = options.format._createMuxer(this); } addVideoTrack(source: VideoSource, metadata: VideoTrackMetadata = {}) { @@ -89,7 +99,7 @@ export class Output { ); } - this.addTrack('video', source, metadata); + this._addTrack('video', source, metadata); } addAudioTrack(source: AudioSource, metadata: AudioTrackMetadata = {}) { @@ -100,7 +110,7 @@ export class Output { throw new TypeError('metadata must be an object.'); } - this.addTrack('audio', source, metadata); + this._addTrack('audio', source, metadata); } addSubtitleTrack(source: SubtitleSource, metadata: SubtitleTrackMetadata = {}) { @@ -111,56 +121,57 @@ export class Output { throw new TypeError('metadata must be an object.'); } - this.addTrack('subtitle', source, metadata); + this._addTrack('subtitle', source, metadata); } - private addTrack(type: OutputTrack['type'], source: MediaSource, metadata: object) { - if (this.started) { + /** @internal */ + private _addTrack(type: OutputTrack['type'], source: MediaSource, metadata: object) { + if (this._started) { throw new Error('Cannot add track after output has started.'); } - if (source.connectedTrack) { + if (source._connectedTrack) { throw new Error('Source is already used for a track.'); } const track = { - id: this.tracks.length + 1, + id: this._tracks.length + 1, output: this, type, source: source as any, metadata } as OutputTrack; - this.muxer.beforeTrackAdd(track); + this._muxer.beforeTrackAdd(track); - this.tracks.push(track); - source.connectedTrack = track; + this._tracks.push(track); + source._connectedTrack = track; } start() { - if (this.started) { + if (this._started) { throw new Error('Output already started.'); } - this.started = true; - this.muxer.start(); + this._started = true; + this._muxer.start(); - for (const track of this.tracks) { - track.source.start(); + for (const track of this._tracks) { + track.source._start(); } } async finalize() { - if (this.finalizing) { + if (this._finalizing) { throw new Error('Cannot call finalize twice.'); } - this.finalizing = true; + this._finalizing = true; - const promises = this.tracks.map(x => x.source.flush()); + const promises = this._tracks.map(x => x.source._flush()); await Promise.all(promises); - this.muxer.finalize(); + this._muxer.finalize(); - this.writer.flush(); - this.writer.finalize(); + this._writer.flush(); + this._writer.finalize(); } } \ No newline at end of file diff --git a/src/source.ts b/src/source.ts index ef8c27a..4cd16f8 100644 --- a/src/source.ts +++ b/src/source.ts @@ -3,68 +3,82 @@ import { assert } from "./misc"; import { OutputAudioTrack, OutputSubtitleTrack, OutputTrack, OutputVideoTrack } from "./output"; import { SubtitleParser } from "./subtitles"; -const VIDEO_CODECS = ['avc', 'hevc', 'vp8', 'vp9', 'av1'] as const; -const AUDIO_CODECS = ['aac', 'opus'] as const; // TODO add the rest -const SUBTITLE_CODECS = ['webvtt'] as const; // TODO add the rest +/** @public */ +export const VIDEO_CODECS = ['avc', 'hevc', 'vp8', 'vp9', 'av1'] as const; +/** @public */ +export const AUDIO_CODECS = ['aac', 'opus'] as const; // TODO add the rest +/** @public */ +export const SUBTITLE_CODECS = ['webvtt'] as const; // TODO add the rest +/** @public */ export type VideoCodec = typeof VIDEO_CODECS[number]; +/** @public */ export type AudioCodec = typeof AUDIO_CODECS[number]; +/** @public */ export type SubtitleCodec = typeof SUBTITLE_CODECS[number]; +/** @public */ export abstract class MediaSource { - connectedTrack: OutputTrack | null = null; - closed = false; - offsetTimestamps = false; + /** @internal */ + _connectedTrack: OutputTrack | null = null; + /** @internal */ + _closed = false; + /** @internal */ + _offsetTimestamps = false; - // TODO this is also just internal: - ensureValidDigest() { - if (!this.connectedTrack) { + /** @internal */ + _ensureValidDigest() { + if (!this._connectedTrack) { throw new Error('Cannot call digest without connecting the source to an output track.'); } - if (!this.connectedTrack.output.started) { + if (!this._connectedTrack.output._started) { throw new Error('Cannot call digest before output has been started.'); } - if (this.connectedTrack.output.finalizing) { + if (this._connectedTrack.output._finalizing) { throw new Error('Cannot call digest after output has started finalizing.'); } - if (this.closed) { + if (this._closed) { throw new Error('Cannot call digest after source has been closed.'); } } - // TODO: These are should not be called from the outside lib - start() {} - async flush() {} + /** @internal */ + _start() {} + /** @internal */ + async _flush() {} close() { - if (this.closed) { + if (this._closed) { throw new Error('Source already closed.'); } - if (!this.connectedTrack) { + if (!this._connectedTrack) { throw new Error('Cannot call close without connecting the source to an output track.'); } - if (!this.connectedTrack.output.started) { + if (!this._connectedTrack.output._started) { throw new Error('Cannot call close before output has been started.'); } - this.closed = true; + this._closed = true; - if (this.connectedTrack.output.finalizing) { + if (this._connectedTrack.output._finalizing) { return; } - this.connectedTrack.output.muxer.onTrackClose(this.connectedTrack); + this._connectedTrack.output._muxer.onTrackClose(this._connectedTrack); } } +/** @public */ export abstract class VideoSource extends MediaSource { - override connectedTrack: OutputVideoTrack | null = null; - codec: VideoCodec; + /** @internal */ + override _connectedTrack: OutputVideoTrack | null = null; + /** @internal */ + _codec: VideoCodec; constructor(codec: VideoCodec) { super(); @@ -73,10 +87,11 @@ export abstract class VideoSource extends MediaSource { throw new TypeError(`Invalid video codec '${codec}'. Must be one of: ${VIDEO_CODECS.join(', ')}.`); } - this.codec = codec; + this._codec = codec; } } +/** @public */ export class EncodedVideoChunkSource extends VideoSource { constructor(codec: VideoCodec) { super(codec); @@ -88,14 +103,15 @@ export class EncodedVideoChunkSource extends VideoSource { throw new TypeError('chunk must be an EncodedVideoChunk.'); } - this.ensureValidDigest(); - this.connectedTrack?.output.muxer.addEncodedVideoChunk(this.connectedTrack, chunk, meta); + this._ensureValidDigest(); + this._connectedTrack?.output._muxer.addEncodedVideoChunk(this._connectedTrack, chunk, meta); } } const KEY_FRAME_INTERVAL = 5; -type VideoCodecConfig = { +/** @public */ +export type VideoCodecConfig = { codec: VideoCodec, bitrate: number, latencyMode?: VideoEncoderConfig['latencyMode'] @@ -127,7 +143,7 @@ class VideoEncoderWrapper { } digest(videoFrame: VideoFrame) { - this.source.ensureValidDigest(); + this.source._ensureValidDigest(); // Ensure video frame size remains constant if (this.lastWidth !== null && this.lastHeight !== null) { @@ -158,7 +174,7 @@ class VideoEncoderWrapper { } this.encoder = new VideoEncoder({ - output: (chunk, meta) => this.source.connectedTrack?.output.muxer.addEncodedVideoChunk(this.source.connectedTrack, chunk, meta), + output: (chunk, meta) => this.source._connectedTrack?.output._muxer.addEncodedVideoChunk(this.source._connectedTrack, chunk, meta), error: (error) => console.error('Video encode error:', error), }); @@ -167,7 +183,7 @@ class VideoEncoderWrapper { width: videoFrame.codedWidth, height: videoFrame.codedHeight, bitrate: this.codecConfig.bitrate, - framerate: this.source.connectedTrack?.metadata.frameRate, + framerate: this.source._connectedTrack?.metadata.frameRate, latencyMode: this.codecConfig.latencyMode, }); } @@ -177,12 +193,14 @@ class VideoEncoderWrapper { } } +/** @public */ export class VideoFrameSource extends VideoSource { - private encoder: VideoEncoderWrapper; + /** @internal */ + private _encoder: VideoEncoderWrapper; constructor(codecConfig: VideoCodecConfig) { super(codecConfig.codec); - this.encoder = new VideoEncoderWrapper(this, codecConfig); + this._encoder = new VideoEncoderWrapper(this, codecConfig); } digest(videoFrame: VideoFrame) { @@ -190,24 +208,30 @@ export class VideoFrameSource extends VideoSource { throw new TypeError('videoFrame must be a VideoFrame.'); } - this.encoder.digest(videoFrame); + this._encoder.digest(videoFrame); } - override flush() { - return this.encoder.flush(); + /** @internal */ + override _flush() { + return this._encoder.flush(); } } +/** @public */ export class CanvasSource extends VideoSource { - private encoder: VideoEncoderWrapper; + /** @internal */ + private _encoder: VideoEncoderWrapper; + /** @internal */ + private _canvas: HTMLCanvasElement; - constructor(private canvas: HTMLCanvasElement, codecConfig: VideoCodecConfig) { + constructor(canvas: HTMLCanvasElement, codecConfig: VideoCodecConfig) { if (!(canvas instanceof HTMLCanvasElement)) { throw new TypeError('canvas must be an HTMLCanvasElement.'); } super(codecConfig.codec); - this.encoder = new VideoEncoderWrapper(this, codecConfig); + this._encoder = new VideoEncoderWrapper(this, codecConfig); + this._canvas = canvas; } digest(timestamp: number, duration = 0) { @@ -218,49 +242,64 @@ export class CanvasSource extends VideoSource { throw new TypeError('duration must be a non-negative number.'); } - const frame = new VideoFrame(this.canvas, { + const frame = new VideoFrame(this._canvas, { timestamp: Math.round(1e6 * timestamp), duration: Math.round(1e6 * duration), alpha: 'discard', }); - this.encoder.digest(frame); + this._encoder.digest(frame); frame.close(); } - override flush() { - return this.encoder.flush(); + /** @internal */ + override _flush() { + return this._encoder.flush(); } } +/** @public */ export class MediaStreamVideoTrackSource extends VideoSource { - private encoder: VideoEncoderWrapper; - private abortController: AbortController | null = null; + /** @internal */ + private _encoder: VideoEncoderWrapper; + /** @internal */ + private _abortController: AbortController | null = null; + /** @internal */ + private _track: MediaStreamVideoTrack; - override offsetTimestamps = true; + /** @internal */ + override _offsetTimestamps = true; - constructor(private track: MediaStreamVideoTrack, codecConfig: VideoCodecConfig) { + constructor(track: MediaStreamVideoTrack, codecConfig: VideoCodecConfig) { if (!(track instanceof MediaStreamTrack) || track.kind !== 'video') { throw new TypeError('track must be a video MediaStreamTrack.'); } + codecConfig = { + ...codecConfig, + latencyMode: 'realtime' + }; + super(codecConfig.codec); - this.encoder = new VideoEncoderWrapper(this, codecConfig); + this._encoder = new VideoEncoderWrapper(this, codecConfig); + this._track = track; } - override start() { - this.abortController = new AbortController(); + /** @internal */ + override _start() { + this._abortController = new AbortController(); - const processor = new MediaStreamTrackProcessor({ track: this.track }); + const processor = new MediaStreamTrackProcessor({ track: this._track }); const consumer = new WritableStream({ write: (videoFrame) => { - this.encoder.digest(videoFrame); + // TODO: Drop frames if encoder overloaded + this._encoder.digest(videoFrame); videoFrame.close(); } }); processor.readable.pipeTo(consumer, { - signal: this.abortController.signal + signal: this._abortController.signal }).catch(err => { // Handle abort error silently if (err instanceof DOMException && err.name === 'AbortError') return; @@ -269,19 +308,23 @@ export class MediaStreamVideoTrackSource extends VideoSource { }); } - override async flush() { - if (this.abortController) { - this.abortController.abort(); - this.abortController = null; + /** @internal */ + override async _flush() { + if (this._abortController) { + this._abortController.abort(); + this._abortController = null; } - await this.encoder.flush(); + await this._encoder.flush(); } } +/** @public */ export abstract class AudioSource extends MediaSource { - override connectedTrack: OutputAudioTrack | null = null; - codec: AudioCodec; + /** @internal */ + override _connectedTrack: OutputAudioTrack | null = null; + /** @internal */ + _codec: AudioCodec; constructor(codec: AudioCodec) { super(); @@ -290,10 +333,11 @@ export abstract class AudioSource extends MediaSource { throw new TypeError(`Invalid audio codec '${codec}'. Must be one of: ${AUDIO_CODECS.join(', ')}.`); } - this.codec = codec; + this._codec = codec; } } +/** @public */ export class EncodedAudioChunkSource extends AudioSource { constructor(codec: AudioCodec) { super(codec); @@ -305,12 +349,12 @@ export class EncodedAudioChunkSource extends AudioSource { throw new TypeError('chunk must be an EncodedAudioChunk.'); } - this.ensureValidDigest(); - this.connectedTrack?.output.muxer.addEncodedAudioChunk(this.connectedTrack, chunk, meta); + this._ensureValidDigest(); + this._connectedTrack?.output._muxer.addEncodedAudioChunk(this._connectedTrack, chunk, meta); } } - -type AudioCodecConfig = { +/** @public */ +export type AudioCodecConfig = { codec: AudioCodec, bitrate: number }; @@ -337,7 +381,7 @@ class AudioEncoderWrapper { } digest(audioData: AudioData) { - this.source.ensureValidDigest(); + this.source._ensureValidDigest(); // Ensure audio parameters remain constant if (this.lastNumberOfChannels !== null && this.lastSampleRate !== null) { @@ -361,7 +405,7 @@ class AudioEncoderWrapper { } this.encoder = new AudioEncoder({ - output: (chunk, meta) => this.source.connectedTrack?.output.muxer.addEncodedAudioChunk(this.source.connectedTrack, chunk, meta), + output: (chunk, meta) => this.source._connectedTrack?.output._muxer.addEncodedAudioChunk(this.source._connectedTrack, chunk, meta), error: (error) => console.error('Audio encode error:', error), }); @@ -378,12 +422,14 @@ class AudioEncoderWrapper { } } +/** @public */ export class AudioDataSource extends AudioSource { - private encoder: AudioEncoderWrapper; + /** @internal */ + private _encoder: AudioEncoderWrapper; constructor(codecConfig: AudioCodecConfig) { super(codecConfig.codec); - this.encoder = new AudioEncoderWrapper(this, codecConfig); + this._encoder = new AudioEncoderWrapper(this, codecConfig); } digest(audioData: AudioData) { @@ -391,21 +437,25 @@ export class AudioDataSource extends AudioSource { throw new TypeError('audioData must be an AudioData.'); } - this.encoder.digest(audioData); + this._encoder.digest(audioData); } - override flush() { - return this.encoder.flush(); + /** @internal */ + override _flush() { + return this._encoder.flush(); } } +/** @public */ export class AudioBufferSource extends AudioSource { - private encoder: AudioEncoderWrapper; - private accumulatedFrameCount = 0; + /** @internal */ + private _encoder: AudioEncoderWrapper; + /** @internal */ + private _accumulatedFrameCount = 0; constructor(codecConfig: AudioCodecConfig) { super(codecConfig.codec); - this.encoder = new AudioEncoderWrapper(this, codecConfig); + this._encoder = new AudioEncoderWrapper(this, codecConfig); } digest(audioBuffer: AudioBuffer) { @@ -429,49 +479,58 @@ export class AudioBufferSource extends AudioSource { sampleRate, numberOfFrames, numberOfChannels, - timestamp: Math.round(1e6 * this.accumulatedFrameCount / sampleRate), + timestamp: Math.round(1e6 * this._accumulatedFrameCount / sampleRate), data: data }); - this.encoder.digest(audioData); + this._encoder.digest(audioData); audioData.close(); - this.accumulatedFrameCount += numberOfFrames; + this._accumulatedFrameCount += numberOfFrames; } - override flush() { - return this.encoder.flush(); + /** @internal */ + override _flush() { + return this._encoder.flush(); } } +/** @public */ export class MediaStreamAudioTrackSource extends AudioSource { - private encoder: AudioEncoderWrapper; - private abortController: AbortController | null = null; + /** @internal */ + private _encoder: AudioEncoderWrapper; + /** @internal */ + private _abortController: AbortController | null = null; + /** @internal */ + private _track: MediaStreamAudioTrack; - override offsetTimestamps = true; + override _offsetTimestamps = true; - constructor(private track: MediaStreamAudioTrack, codecConfig: AudioCodecConfig) { + constructor(track: MediaStreamAudioTrack, codecConfig: AudioCodecConfig) { if (!(track instanceof MediaStreamTrack) || track.kind !== 'audio') { throw new TypeError('track must be an audio MediaStreamTrack.'); } super(codecConfig.codec); - this.encoder = new AudioEncoderWrapper(this, codecConfig); + this._encoder = new AudioEncoderWrapper(this, codecConfig); + this._track = track; } - override start() { - this.abortController = new AbortController(); + /** @internal */ + override _start() { + this._abortController = new AbortController(); - const processor = new MediaStreamTrackProcessor({ track: this.track }); + const processor = new MediaStreamTrackProcessor({ track: this._track }); const consumer = new WritableStream({ write: (audioData) => { - this.encoder.digest(audioData); + // TODO: Drop frames if encoder overloaded + this._encoder.digest(audioData); audioData.close(); } }); processor.readable.pipeTo(consumer, { - signal: this.abortController.signal + signal: this._abortController.signal }).catch(err => { // Handle abort error silently if (err instanceof DOMException && err.name === 'AbortError') return; @@ -480,19 +539,23 @@ export class MediaStreamAudioTrackSource extends AudioSource { }); } - override async flush() { - if (this.abortController) { - this.abortController.abort(); - this.abortController = null; + /** @internal */ + override async _flush() { + if (this._abortController) { + this._abortController.abort(); + this._abortController = null; } - await this.encoder.flush(); + await this._encoder.flush(); } } +/** @public */ export abstract class SubtitleSource extends MediaSource { - override connectedTrack: OutputSubtitleTrack | null = null; - codec: SubtitleCodec; + /** @internal */ + override _connectedTrack: OutputSubtitleTrack | null = null; + /** @internal */ + _codec: SubtitleCodec; constructor(codec: SubtitleCodec) { super(); @@ -501,19 +564,21 @@ export abstract class SubtitleSource extends MediaSource { throw new TypeError(`Invalid subtitle codec '${codec}'. Must be one of: ${SUBTITLE_CODECS.join(', ')}.`); } - this.codec = codec; + this._codec = codec; } } +/** @public */ export class TextSubtitleSource extends SubtitleSource { - private parser: SubtitleParser; + /** @internal */ + private _parser: SubtitleParser; constructor(codec: SubtitleCodec) { super(codec); - this.parser = new SubtitleParser({ + this._parser = new SubtitleParser({ codec, - output: (cue, metadata) => this.connectedTrack?.output.muxer.addSubtitleCue(this.connectedTrack, cue, metadata), + output: (cue, metadata) => this._connectedTrack?.output._muxer.addSubtitleCue(this._connectedTrack, cue, metadata), error: (error) => console.error('Subtitle parse error:', error) }); } @@ -523,7 +588,7 @@ export class TextSubtitleSource extends SubtitleSource { throw new TypeError('text must be a string.'); } - this.ensureValidDigest(); - this.parser.parse(text); + this._ensureValidDigest(); + this._parser.parse(text); } } \ No newline at end of file diff --git a/src/target.ts b/src/target.ts index 54175c3..022cf39 100644 --- a/src/target.ts +++ b/src/target.ts @@ -1,20 +1,25 @@ import { Output } from "./output"; import { ArrayBufferTargetWriter, ChunkedStreamTargetWriter, FileSystemWritableFileStreamTargetWriter, StreamTargetWriter, Writer } from "./writer"; +/** @public */ export abstract class Target { output: Output | null = null; - abstract createWriter(): Writer; + /** @internal */ + abstract _createWriter(): Writer; } +/** @public */ export class ArrayBufferTarget extends Target { buffer: ArrayBuffer | null = null; - createWriter() { + /** @internal */ + _createWriter() { return new ArrayBufferTargetWriter(this); } } +/** @public */ export class StreamTarget extends Target { constructor(public options: { onData?: (data: Uint8Array, position: number) => void, @@ -48,11 +53,13 @@ export class StreamTarget extends Target { } } - createWriter() { + /** @internal */ + _createWriter() { return this.options.chunked ? new ChunkedStreamTargetWriter(this) : new StreamTargetWriter(this); } } +/** @public */ export class FileSystemWritableFileStreamTarget extends Target { constructor( public stream: FileSystemWritableFileStream, @@ -73,7 +80,8 @@ export class FileSystemWritableFileStreamTarget extends Target { } } - createWriter() { + /** @internal */ + _createWriter() { return new FileSystemWritableFileStreamTargetWriter(this); } } diff --git a/tsconfig.json b/tsconfig.json index 55cea2d..03263c0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,11 @@ "noImplicitAny": true, "noImplicitOverride": true, "noUncheckedIndexedAccess": true, - "noPropertyAccessFromIndexSignature": true + "noPropertyAccessFromIndexSignature": true, + "rootDir": "src", + "outDir": "build", + "declaration": true, + "stripInternal": true }, "include": [ "src/**/*"