jack 3 weeks ago
parent
commit
e972fc5c4c
4 changed files with 4073 additions and 91 deletions
  1. 448 50
      package-lock.json
  2. 1 0
      package.json
  3. 3524 0
      public/webcodecs-worker.js
  4. 100 41
      src/hooks/useImport.ts

File diff suppressed because it is too large
+ 448 - 50
package-lock.json


+ 1 - 0
package.json

@@ -55,6 +55,7 @@
     "vue": "^3.5.17",
     "vue": "^3.5.17",
     "vuedraggable": "^4.1.0",
     "vuedraggable": "^4.1.0",
     "wangeditor": "^4.7.15",
     "wangeditor": "^4.7.15",
+    "webcodecs-encoder": "^0.3.2",
     "y-websocket": "^3.0.0",
     "y-websocket": "^3.0.0",
     "yjs": "^13.6.27"
     "yjs": "^13.6.27"
   },
   },

+ 3524 - 0
public/webcodecs-worker.js

@@ -0,0 +1,3524 @@
+"use strict";
+var EncoderWorkerGlobal = (() => {
+  var __create = Object.create;
+  var __defProp = Object.defineProperty;
+  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+  var __getOwnPropNames = Object.getOwnPropertyNames;
+  var __getProtoOf = Object.getPrototypeOf;
+  var __hasOwnProp = Object.prototype.hasOwnProperty;
+  var __commonJS = (cb, mod) => function __require() {
+    return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
+  };
+  var __copyProps = (to, from, except, desc) => {
+    if (from && typeof from === "object" || typeof from === "function") {
+      for (let key of __getOwnPropNames(from))
+        if (!__hasOwnProp.call(to, key) && key !== except)
+          __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
+    }
+    return to;
+  };
+  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
+    // If the importer is in node compatibility mode or this is not an ESM
+    // file that has been converted to a CommonJS file using a Babel-
+    // compatible transform (i.e. "__esModule" has not been set), then set
+    // "default" to the CommonJS "module.exports" for node compatibility.
+    isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
+    mod
+  ));
+
+  // node_modules/webm-muxer/build/webm-muxer.js
+  var require_webm_muxer = __commonJS({
+    "node_modules/webm-muxer/build/webm-muxer.js"(exports, module) {
+      "use strict";
+      var WebMMuxer2 = (() => {
+        var __defProp3 = Object.defineProperty;
+        var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
+        var __getOwnPropNames2 = Object.getOwnPropertyNames;
+        var __hasOwnProp3 = Object.prototype.hasOwnProperty;
+        var __pow2 = Math.pow;
+        var __export = (target, all) => {
+          for (var name in all)
+            __defProp3(target, name, { get: all[name], enumerable: true });
+        };
+        var __copyProps2 = (to, from, except, desc) => {
+          if (from && typeof from === "object" || typeof from === "function") {
+            for (let key of __getOwnPropNames2(from))
+              if (!__hasOwnProp3.call(to, key) && key !== except)
+                __defProp3(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
+          }
+          return to;
+        };
+        var __toCommonJS = (mod) => __copyProps2(__defProp3({}, "__esModule", { value: true }), mod);
+        var __accessCheck2 = (obj, member, msg) => {
+          if (!member.has(obj))
+            throw TypeError("Cannot " + msg);
+        };
+        var __privateGet2 = (obj, member, getter) => {
+          __accessCheck2(obj, member, "read from private field");
+          return getter ? getter.call(obj) : member.get(obj);
+        };
+        var __privateAdd2 = (obj, member, value) => {
+          if (member.has(obj))
+            throw TypeError("Cannot add the same private member more than once");
+          member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
+        };
+        var __privateSet2 = (obj, member, value, setter) => {
+          __accessCheck2(obj, member, "write to private field");
+          setter ? setter.call(obj, value) : member.set(obj, value);
+          return value;
+        };
+        var __privateMethod2 = (obj, member, method) => {
+          __accessCheck2(obj, member, "access private method");
+          return method;
+        };
+        var main_exports = {};
+        __export(main_exports, {
+          default: () => main_default
+        });
+        var EBMLFloat32 = class {
+          constructor(value) {
+            this.value = value;
+          }
+        };
+        var EBMLFloat64 = class {
+          constructor(value) {
+            this.value = value;
+          }
+        };
+        var WriteTarget = class {
+          constructor() {
+            this.pos = 0;
+            this.helper = new Uint8Array(8);
+            this.helperView = new DataView(this.helper.buffer);
+            this.offsets = /* @__PURE__ */ new WeakMap();
+            this.dataOffsets = /* @__PURE__ */ new WeakMap();
+          }
+          writeFloat32(value) {
+            this.helperView.setFloat32(0, value, false);
+            this.write(this.helper.subarray(0, 4));
+          }
+          writeFloat64(value) {
+            this.helperView.setFloat64(0, value, false);
+            this.write(this.helper);
+          }
+          writeUnsignedInt(value, width = measureUnsignedInt(value)) {
+            let pos = 0;
+            switch (width) {
+              case 6:
+                this.helperView.setUint8(pos++, value / __pow2(2, 40) | 0);
+              case 5:
+                this.helperView.setUint8(pos++, value / __pow2(2, 32) | 0);
+              case 4:
+                this.helperView.setUint8(pos++, value >> 24);
+              case 3:
+                this.helperView.setUint8(pos++, value >> 16);
+              case 2:
+                this.helperView.setUint8(pos++, value >> 8);
+              case 1:
+                this.helperView.setUint8(pos++, value);
+                break;
+              default:
+                throw new Error("Bad UINT size " + width);
+            }
+            this.write(this.helper.subarray(0, pos));
+          }
+          writeEBMLVarInt(value, width = measureEBMLVarInt(value)) {
+            let pos = 0;
+            switch (width) {
+              case 1:
+                this.helperView.setUint8(pos++, 1 << 7 | value);
+                break;
+              case 2:
+                this.helperView.setUint8(pos++, 1 << 6 | value >> 8);
+                this.helperView.setUint8(pos++, value);
+                break;
+              case 3:
+                this.helperView.setUint8(pos++, 1 << 5 | value >> 16);
+                this.helperView.setUint8(pos++, value >> 8);
+                this.helperView.setUint8(pos++, value);
+                break;
+              case 4:
+                this.helperView.setUint8(pos++, 1 << 4 | value >> 24);
+                this.helperView.setUint8(pos++, value >> 16);
+                this.helperView.setUint8(pos++, value >> 8);
+                this.helperView.setUint8(pos++, value);
+                break;
+              case 5:
+                this.helperView.setUint8(pos++, 1 << 3 | value / __pow2(2, 32) & 7);
+                this.helperView.setUint8(pos++, value >> 24);
+                this.helperView.setUint8(pos++, value >> 16);
+                this.helperView.setUint8(pos++, value >> 8);
+                this.helperView.setUint8(pos++, value);
+                break;
+              case 6:
+                this.helperView.setUint8(pos++, 1 << 2 | value / __pow2(2, 40) & 3);
+                this.helperView.setUint8(pos++, value / __pow2(2, 32) | 0);
+                this.helperView.setUint8(pos++, value >> 24);
+                this.helperView.setUint8(pos++, value >> 16);
+                this.helperView.setUint8(pos++, value >> 8);
+                this.helperView.setUint8(pos++, value);
+                break;
+              default:
+                throw new Error("Bad EBML VINT size " + width);
+            }
+            this.write(this.helper.subarray(0, pos));
+          }
+          writeString(str) {
+            this.write(new Uint8Array(str.split("").map((x) => x.charCodeAt(0))));
+          }
+          writeEBML(data) {
+            var _a, _b;
+            if (data instanceof Uint8Array) {
+              this.write(data);
+            } else if (Array.isArray(data)) {
+              for (let elem of data) {
+                this.writeEBML(elem);
+              }
+            } else {
+              this.offsets.set(data, this.pos);
+              this.writeUnsignedInt(data.id);
+              if (Array.isArray(data.data)) {
+                let sizePos = this.pos;
+                let sizeSize = (_a = data.size) != null ? _a : 4;
+                this.seek(this.pos + sizeSize);
+                let startPos = this.pos;
+                this.dataOffsets.set(data, startPos);
+                this.writeEBML(data.data);
+                let size = this.pos - startPos;
+                let endPos = this.pos;
+                this.seek(sizePos);
+                this.writeEBMLVarInt(size, sizeSize);
+                this.seek(endPos);
+              } else if (typeof data.data === "number") {
+                let size = (_b = data.size) != null ? _b : measureUnsignedInt(data.data);
+                this.writeEBMLVarInt(size);
+                this.writeUnsignedInt(data.data, size);
+              } else if (typeof data.data === "string") {
+                this.writeEBMLVarInt(data.data.length);
+                this.writeString(data.data);
+              } else if (data.data instanceof Uint8Array) {
+                this.writeEBMLVarInt(data.data.byteLength, data.size);
+                this.write(data.data);
+              } else if (data.data instanceof EBMLFloat32) {
+                this.writeEBMLVarInt(4);
+                this.writeFloat32(data.data.value);
+              } else if (data.data instanceof EBMLFloat64) {
+                this.writeEBMLVarInt(8);
+                this.writeFloat64(data.data.value);
+              }
+            }
+          }
+        };
+        var measureUnsignedInt = (value) => {
+          if (value < 1 << 8) {
+            return 1;
+          } else if (value < 1 << 16) {
+            return 2;
+          } else if (value < 1 << 24) {
+            return 3;
+          } else if (value < __pow2(2, 32)) {
+            return 4;
+          } else if (value < __pow2(2, 40)) {
+            return 5;
+          } else {
+            return 6;
+          }
+        };
+        var measureEBMLVarInt = (value) => {
+          if (value < (1 << 7) - 1) {
+            return 1;
+          } else if (value < (1 << 14) - 1) {
+            return 2;
+          } else if (value < (1 << 21) - 1) {
+            return 3;
+          } else if (value < (1 << 28) - 1) {
+            return 4;
+          } else if (value < __pow2(2, 35) - 1) {
+            return 5;
+          } else if (value < __pow2(2, 42) - 1) {
+            return 6;
+          } else {
+            throw new Error("EBML VINT size not supported " + value);
+          }
+        };
+        var ArrayBufferWriteTarget = class extends WriteTarget {
+          constructor() {
+            super();
+            this.buffer = new ArrayBuffer(__pow2(2, 16));
+            this.bytes = new Uint8Array(this.buffer);
+          }
+          ensureSize(size) {
+            let newLength = this.buffer.byteLength;
+            while (newLength < size)
+              newLength *= 2;
+            if (newLength === this.buffer.byteLength)
+              return;
+            let newBuffer = new ArrayBuffer(newLength);
+            let newBytes = new Uint8Array(newBuffer);
+            newBytes.set(this.bytes, 0);
+            this.buffer = newBuffer;
+            this.bytes = newBytes;
+          }
+          write(data) {
+            this.ensureSize(this.pos + data.byteLength);
+            this.bytes.set(data, this.pos);
+            this.pos += data.byteLength;
+          }
+          seek(newPos) {
+            this.pos = newPos;
+          }
+          finalize() {
+            this.ensureSize(this.pos);
+            return this.buffer.slice(0, this.pos);
+          }
+        };
+        var FILE_CHUNK_SIZE = __pow2(2, 24);
+        var MAX_CHUNKS_AT_ONCE2 = 2;
+        var FileSystemWritableFileStreamWriteTarget = class extends WriteTarget {
+          constructor(stream) {
+            super();
+            this.chunks = [];
+            this.stream = stream;
+          }
+          write(data) {
+            this.writeDataIntoChunks(data, this.pos);
+            this.flushChunks();
+            this.pos += data.byteLength;
+          }
+          writeDataIntoChunks(data, position) {
+            let chunkIndex = this.chunks.findIndex((x) => x.start <= position && position < x.start + FILE_CHUNK_SIZE);
+            if (chunkIndex === -1)
+              chunkIndex = this.createChunk(position);
+            let chunk = this.chunks[chunkIndex];
+            let relativePosition = position - chunk.start;
+            let toWrite = data.subarray(0, Math.min(FILE_CHUNK_SIZE - relativePosition, data.byteLength));
+            chunk.data.set(toWrite, relativePosition);
+            let section = {
+              start: relativePosition,
+              end: relativePosition + toWrite.byteLength
+            };
+            insertSectionIntoFileChunk(chunk, section);
+            if (chunk.written[0].start === 0 && chunk.written[0].end === FILE_CHUNK_SIZE) {
+              chunk.shouldFlush = true;
+            }
+            if (this.chunks.length > MAX_CHUNKS_AT_ONCE2) {
+              for (let i = 0; i < this.chunks.length - 1; i++) {
+                this.chunks[i].shouldFlush = true;
+              }
+              this.flushChunks();
+            }
+            if (toWrite.byteLength < data.byteLength) {
+              this.writeDataIntoChunks(data.subarray(toWrite.byteLength), position + toWrite.byteLength);
+            }
+          }
+          createChunk(includesPosition) {
+            let start = Math.floor(includesPosition / FILE_CHUNK_SIZE) * FILE_CHUNK_SIZE;
+            let chunk = {
+              start,
+              data: new Uint8Array(FILE_CHUNK_SIZE),
+              written: [],
+              shouldFlush: false
+            };
+            this.chunks.push(chunk);
+            this.chunks.sort((a, b) => a.start - b.start);
+            return this.chunks.indexOf(chunk);
+          }
+          flushChunks(force = false) {
+            for (let i = 0; i < this.chunks.length; i++) {
+              let chunk = this.chunks[i];
+              if (!chunk.shouldFlush && !force)
+                continue;
+              for (let section of chunk.written) {
+                this.stream.write({
+                  type: "write",
+                  data: chunk.data.subarray(section.start, section.end),
+                  position: chunk.start + section.start
+                });
+              }
+              this.chunks.splice(i--, 1);
+            }
+          }
+          seek(newPos) {
+            this.pos = newPos;
+          }
+          finalize() {
+            this.flushChunks(true);
+          }
+        };
+        var insertSectionIntoFileChunk = (chunk, section) => {
+          let low = 0;
+          let high = chunk.written.length - 1;
+          let index = -1;
+          while (low <= high) {
+            let mid = Math.floor(low + (high - low + 1) / 2);
+            if (chunk.written[mid].start <= section.start) {
+              low = mid + 1;
+              index = mid;
+            } else {
+              high = mid - 1;
+            }
+          }
+          chunk.written.splice(index + 1, 0, section);
+          if (index === -1 || chunk.written[index].end < section.start)
+            index++;
+          while (index < chunk.written.length - 1 && chunk.written[index].end >= chunk.written[index + 1].start) {
+            chunk.written[index].end = Math.max(chunk.written[index].end, chunk.written[index + 1].end);
+            chunk.written.splice(index + 1, 1);
+          }
+        };
+        var VIDEO_TRACK_NUMBER = 1;
+        var AUDIO_TRACK_NUMBER = 2;
+        var VIDEO_TRACK_TYPE = 1;
+        var AUDIO_TRACK_TYPE = 2;
+        var MAX_CHUNK_LENGTH_MS = __pow2(2, 15);
+        var CODEC_PRIVATE_MAX_SIZE = __pow2(2, 12);
+        var APP_NAME = "https://github.com/Vanilagy/webm-muxer";
+        var SEGMENT_SIZE_BYTES = 6;
+        var CLUSTER_SIZE_BYTES = 5;
+        var _target4, _options2, _segment, _segmentInfo, _seekHead, _tracksElement, _segmentDuration, _colourElement, _videoCodecPrivate, _audioCodecPrivate, _cues, _currentCluster, _currentClusterTimestamp, _duration, _videoChunkQueue, _audioChunkQueue, _lastVideoTimestamp, _lastAudioTimestamp, _colorSpace, _finalized2, _validateOptions2, validateOptions_fn2, _createFileHeader, createFileHeader_fn, _writeEBMLHeader, writeEBMLHeader_fn, _createSeekHead, createSeekHead_fn, _createSegmentInfo, createSegmentInfo_fn, _createTracks, createTracks_fn, _createSegment, createSegment_fn, _createCues, createCues_fn, _segmentDataOffset, segmentDataOffset_get, _writeVideoDecoderConfig, writeVideoDecoderConfig_fn, _fixVP9ColorSpace, fixVP9ColorSpace_fn, _createInternalChunk, createInternalChunk_fn, _writeSimpleBlock, writeSimpleBlock_fn, _writeCodecPrivate, writeCodecPrivate_fn, _createNewCluster, createNewCluster_fn, _finalizeCurrentCluster, finalizeCurrentCluster_fn, _ensureNotFinalized2, ensureNotFinalized_fn2;
+        var WebMMuxer3 = class {
+          constructor(options) {
+            __privateAdd2(this, _validateOptions2);
+            __privateAdd2(this, _createFileHeader);
+            __privateAdd2(this, _writeEBMLHeader);
+            __privateAdd2(this, _createSeekHead);
+            __privateAdd2(this, _createSegmentInfo);
+            __privateAdd2(this, _createTracks);
+            __privateAdd2(this, _createSegment);
+            __privateAdd2(this, _createCues);
+            __privateAdd2(this, _segmentDataOffset);
+            __privateAdd2(this, _writeVideoDecoderConfig);
+            __privateAdd2(this, _fixVP9ColorSpace);
+            __privateAdd2(this, _createInternalChunk);
+            __privateAdd2(this, _writeSimpleBlock);
+            __privateAdd2(this, _writeCodecPrivate);
+            __privateAdd2(this, _createNewCluster);
+            __privateAdd2(this, _finalizeCurrentCluster);
+            __privateAdd2(this, _ensureNotFinalized2);
+            __privateAdd2(this, _target4, void 0);
+            __privateAdd2(this, _options2, void 0);
+            __privateAdd2(this, _segment, void 0);
+            __privateAdd2(this, _segmentInfo, void 0);
+            __privateAdd2(this, _seekHead, void 0);
+            __privateAdd2(this, _tracksElement, void 0);
+            __privateAdd2(this, _segmentDuration, void 0);
+            __privateAdd2(this, _colourElement, void 0);
+            __privateAdd2(this, _videoCodecPrivate, void 0);
+            __privateAdd2(this, _audioCodecPrivate, void 0);
+            __privateAdd2(this, _cues, void 0);
+            __privateAdd2(this, _currentCluster, void 0);
+            __privateAdd2(this, _currentClusterTimestamp, void 0);
+            __privateAdd2(this, _duration, 0);
+            __privateAdd2(this, _videoChunkQueue, []);
+            __privateAdd2(this, _audioChunkQueue, []);
+            __privateAdd2(this, _lastVideoTimestamp, 0);
+            __privateAdd2(this, _lastAudioTimestamp, 0);
+            __privateAdd2(this, _colorSpace, void 0);
+            __privateAdd2(this, _finalized2, false);
+            __privateMethod2(this, _validateOptions2, validateOptions_fn2).call(this, options);
+            __privateSet2(this, _options2, options);
+            if (options.target === "buffer") {
+              __privateSet2(this, _target4, new ArrayBufferWriteTarget());
+            } else {
+              __privateSet2(this, _target4, new FileSystemWritableFileStreamWriteTarget(options.target));
+            }
+            __privateMethod2(this, _createFileHeader, createFileHeader_fn).call(this);
+          }
+          addVideoChunk(chunk, meta, timestamp) {
+            let data = new Uint8Array(chunk.byteLength);
+            chunk.copyTo(data);
+            this.addVideoChunkRaw(data, chunk.type, timestamp != null ? timestamp : chunk.timestamp, meta);
+          }
+          addVideoChunkRaw(data, type, timestamp, meta) {
+            __privateMethod2(this, _ensureNotFinalized2, ensureNotFinalized_fn2).call(this);
+            if (!__privateGet2(this, _options2).video)
+              throw new Error("No video track declared.");
+            if (meta)
+              __privateMethod2(this, _writeVideoDecoderConfig, writeVideoDecoderConfig_fn).call(this, meta);
+            let internalChunk = __privateMethod2(this, _createInternalChunk, createInternalChunk_fn).call(this, data, type, timestamp, VIDEO_TRACK_NUMBER);
+            if (__privateGet2(this, _options2).video.codec === "V_VP9")
+              __privateMethod2(this, _fixVP9ColorSpace, fixVP9ColorSpace_fn).call(this, internalChunk);
+            __privateSet2(this, _lastVideoTimestamp, internalChunk.timestamp);
+            while (__privateGet2(this, _audioChunkQueue).length > 0 && __privateGet2(this, _audioChunkQueue)[0].timestamp <= internalChunk.timestamp) {
+              let audioChunk = __privateGet2(this, _audioChunkQueue).shift();
+              __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, audioChunk);
+            }
+            if (!__privateGet2(this, _options2).audio || internalChunk.timestamp <= __privateGet2(this, _lastAudioTimestamp)) {
+              __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, internalChunk);
+            } else {
+              __privateGet2(this, _videoChunkQueue).push(internalChunk);
+            }
+          }
+          addAudioChunk(chunk, meta, timestamp) {
+            let data = new Uint8Array(chunk.byteLength);
+            chunk.copyTo(data);
+            this.addAudioChunkRaw(data, chunk.type, timestamp != null ? timestamp : chunk.timestamp, meta);
+          }
+          addAudioChunkRaw(data, type, timestamp, meta) {
+            __privateMethod2(this, _ensureNotFinalized2, ensureNotFinalized_fn2).call(this);
+            if (!__privateGet2(this, _options2).audio)
+              throw new Error("No audio track declared.");
+            let internalChunk = __privateMethod2(this, _createInternalChunk, createInternalChunk_fn).call(this, data, type, timestamp, AUDIO_TRACK_NUMBER);
+            __privateSet2(this, _lastAudioTimestamp, internalChunk.timestamp);
+            while (__privateGet2(this, _videoChunkQueue).length > 0 && __privateGet2(this, _videoChunkQueue)[0].timestamp <= internalChunk.timestamp) {
+              let videoChunk = __privateGet2(this, _videoChunkQueue).shift();
+              __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, videoChunk);
+            }
+            if (!__privateGet2(this, _options2).video || internalChunk.timestamp <= __privateGet2(this, _lastVideoTimestamp)) {
+              __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, internalChunk);
+            } else {
+              __privateGet2(this, _audioChunkQueue).push(internalChunk);
+            }
+            if (meta == null ? void 0 : meta.decoderConfig) {
+              __privateMethod2(this, _writeCodecPrivate, writeCodecPrivate_fn).call(this, __privateGet2(this, _audioCodecPrivate), meta.decoderConfig.description);
+            }
+          }
+          finalize() {
+            while (__privateGet2(this, _videoChunkQueue).length > 0)
+              __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, __privateGet2(this, _videoChunkQueue).shift());
+            while (__privateGet2(this, _audioChunkQueue).length > 0)
+              __privateMethod2(this, _writeSimpleBlock, writeSimpleBlock_fn).call(this, __privateGet2(this, _audioChunkQueue).shift());
+            __privateMethod2(this, _finalizeCurrentCluster, finalizeCurrentCluster_fn).call(this);
+            __privateGet2(this, _target4).writeEBML(__privateGet2(this, _cues));
+            let endPos = __privateGet2(this, _target4).pos;
+            let segmentSize = __privateGet2(this, _target4).pos - __privateGet2(this, _segmentDataOffset, segmentDataOffset_get);
+            __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(__privateGet2(this, _segment)) + 4);
+            __privateGet2(this, _target4).writeEBMLVarInt(segmentSize, SEGMENT_SIZE_BYTES);
+            __privateGet2(this, _segmentDuration).data = new EBMLFloat64(__privateGet2(this, _duration));
+            __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(__privateGet2(this, _segmentDuration)));
+            __privateGet2(this, _target4).writeEBML(__privateGet2(this, _segmentDuration));
+            __privateGet2(this, _seekHead).data[0].data[1].data = __privateGet2(this, _target4).offsets.get(__privateGet2(this, _cues)) - __privateGet2(this, _segmentDataOffset, segmentDataOffset_get);
+            __privateGet2(this, _seekHead).data[1].data[1].data = __privateGet2(this, _target4).offsets.get(__privateGet2(this, _segmentInfo)) - __privateGet2(this, _segmentDataOffset, segmentDataOffset_get);
+            __privateGet2(this, _seekHead).data[2].data[1].data = __privateGet2(this, _target4).offsets.get(__privateGet2(this, _tracksElement)) - __privateGet2(this, _segmentDataOffset, segmentDataOffset_get);
+            __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(__privateGet2(this, _seekHead)));
+            __privateGet2(this, _target4).writeEBML(__privateGet2(this, _seekHead));
+            __privateGet2(this, _target4).seek(endPos);
+            __privateSet2(this, _finalized2, true);
+            if (__privateGet2(this, _target4) instanceof ArrayBufferWriteTarget) {
+              return __privateGet2(this, _target4).finalize();
+            } else if (__privateGet2(this, _target4) instanceof FileSystemWritableFileStreamWriteTarget) {
+              __privateGet2(this, _target4).finalize();
+            }
+            return null;
+          }
+        };
+        _target4 = /* @__PURE__ */ new WeakMap();
+        _options2 = /* @__PURE__ */ new WeakMap();
+        _segment = /* @__PURE__ */ new WeakMap();
+        _segmentInfo = /* @__PURE__ */ new WeakMap();
+        _seekHead = /* @__PURE__ */ new WeakMap();
+        _tracksElement = /* @__PURE__ */ new WeakMap();
+        _segmentDuration = /* @__PURE__ */ new WeakMap();
+        _colourElement = /* @__PURE__ */ new WeakMap();
+        _videoCodecPrivate = /* @__PURE__ */ new WeakMap();
+        _audioCodecPrivate = /* @__PURE__ */ new WeakMap();
+        _cues = /* @__PURE__ */ new WeakMap();
+        _currentCluster = /* @__PURE__ */ new WeakMap();
+        _currentClusterTimestamp = /* @__PURE__ */ new WeakMap();
+        _duration = /* @__PURE__ */ new WeakMap();
+        _videoChunkQueue = /* @__PURE__ */ new WeakMap();
+        _audioChunkQueue = /* @__PURE__ */ new WeakMap();
+        _lastVideoTimestamp = /* @__PURE__ */ new WeakMap();
+        _lastAudioTimestamp = /* @__PURE__ */ new WeakMap();
+        _colorSpace = /* @__PURE__ */ new WeakMap();
+        _finalized2 = /* @__PURE__ */ new WeakMap();
+        _validateOptions2 = /* @__PURE__ */ new WeakSet();
+        validateOptions_fn2 = function(options) {
+          if (options.type && options.type !== "webm" && options.type !== "matroska") {
+            throw new Error(`Invalid type: ${options.type}`);
+          }
+        };
+        _createFileHeader = /* @__PURE__ */ new WeakSet();
+        createFileHeader_fn = function() {
+          __privateMethod2(this, _writeEBMLHeader, writeEBMLHeader_fn).call(this);
+          __privateMethod2(this, _createSeekHead, createSeekHead_fn).call(this);
+          __privateMethod2(this, _createSegmentInfo, createSegmentInfo_fn).call(this);
+          __privateMethod2(this, _createTracks, createTracks_fn).call(this);
+          __privateMethod2(this, _createSegment, createSegment_fn).call(this);
+          __privateMethod2(this, _createCues, createCues_fn).call(this);
+        };
+        _writeEBMLHeader = /* @__PURE__ */ new WeakSet();
+        writeEBMLHeader_fn = function() {
+          var _a;
+          let ebmlHeader = { id: 440786851, data: [
+            { id: 17030, data: 1 },
+            { id: 17143, data: 1 },
+            { id: 17138, data: 4 },
+            { id: 17139, data: 8 },
+            { id: 17026, data: (_a = __privateGet2(this, _options2).type) != null ? _a : "webm" },
+            { id: 17031, data: 2 },
+            { id: 17029, data: 2 }
+          ] };
+          __privateGet2(this, _target4).writeEBML(ebmlHeader);
+        };
+        _createSeekHead = /* @__PURE__ */ new WeakSet();
+        createSeekHead_fn = function() {
+          const kaxCues = new Uint8Array([28, 83, 187, 107]);
+          const kaxInfo = new Uint8Array([21, 73, 169, 102]);
+          const kaxTracks = new Uint8Array([22, 84, 174, 107]);
+          let seekHead = { id: 290298740, data: [
+            { id: 19899, data: [
+              { id: 21419, data: kaxCues },
+              { id: 21420, size: 5, data: 0 }
+            ] },
+            { id: 19899, data: [
+              { id: 21419, data: kaxInfo },
+              { id: 21420, size: 5, data: 0 }
+            ] },
+            { id: 19899, data: [
+              { id: 21419, data: kaxTracks },
+              { id: 21420, size: 5, data: 0 }
+            ] }
+          ] };
+          __privateSet2(this, _seekHead, seekHead);
+        };
+        _createSegmentInfo = /* @__PURE__ */ new WeakSet();
+        createSegmentInfo_fn = function() {
+          let segmentDuration = { id: 17545, data: new EBMLFloat64(0) };
+          __privateSet2(this, _segmentDuration, segmentDuration);
+          let segmentInfo = { id: 357149030, data: [
+            { id: 2807729, data: 1e6 },
+            { id: 19840, data: APP_NAME },
+            { id: 22337, data: APP_NAME },
+            segmentDuration
+          ] };
+          __privateSet2(this, _segmentInfo, segmentInfo);
+        };
+        _createTracks = /* @__PURE__ */ new WeakSet();
+        createTracks_fn = function() {
+          let tracksElement = { id: 374648427, data: [] };
+          __privateSet2(this, _tracksElement, tracksElement);
+          if (__privateGet2(this, _options2).video) {
+            __privateSet2(this, _videoCodecPrivate, { id: 236, size: 4, data: new Uint8Array(CODEC_PRIVATE_MAX_SIZE) });
+            let colourElement = { id: 21936, data: [
+              { id: 21937, data: 2 },
+              { id: 21946, data: 2 },
+              { id: 21947, data: 2 },
+              { id: 21945, data: 0 }
+            ] };
+            __privateSet2(this, _colourElement, colourElement);
+            tracksElement.data.push({ id: 174, data: [
+              { id: 215, data: VIDEO_TRACK_NUMBER },
+              { id: 29637, data: VIDEO_TRACK_NUMBER },
+              { id: 131, data: VIDEO_TRACK_TYPE },
+              { id: 134, data: __privateGet2(this, _options2).video.codec },
+              __privateGet2(this, _videoCodecPrivate),
+              __privateGet2(this, _options2).video.frameRate ? { id: 2352003, data: 1e9 / __privateGet2(this, _options2).video.frameRate } : null,
+              { id: 224, data: [
+                { id: 176, data: __privateGet2(this, _options2).video.width },
+                { id: 186, data: __privateGet2(this, _options2).video.height },
+                colourElement
+              ] }
+            ].filter(Boolean) });
+          }
+          if (__privateGet2(this, _options2).audio) {
+            __privateSet2(this, _audioCodecPrivate, { id: 236, size: 4, data: new Uint8Array(CODEC_PRIVATE_MAX_SIZE) });
+            tracksElement.data.push({ id: 174, data: [
+              { id: 215, data: AUDIO_TRACK_NUMBER },
+              { id: 29637, data: AUDIO_TRACK_NUMBER },
+              { id: 131, data: AUDIO_TRACK_TYPE },
+              { id: 134, data: __privateGet2(this, _options2).audio.codec },
+              __privateGet2(this, _audioCodecPrivate),
+              { id: 225, data: [
+                { id: 181, data: new EBMLFloat32(__privateGet2(this, _options2).audio.sampleRate) },
+                { id: 159, data: __privateGet2(this, _options2).audio.numberOfChannels },
+                __privateGet2(this, _options2).audio.bitDepth ? { id: 25188, data: __privateGet2(this, _options2).audio.bitDepth } : null
+              ].filter(Boolean) }
+            ] });
+          }
+        };
+        _createSegment = /* @__PURE__ */ new WeakSet();
+        createSegment_fn = function() {
+          let segment = { id: 408125543, size: SEGMENT_SIZE_BYTES, data: [
+            __privateGet2(this, _seekHead),
+            __privateGet2(this, _segmentInfo),
+            __privateGet2(this, _tracksElement)
+          ] };
+          __privateSet2(this, _segment, segment);
+          __privateGet2(this, _target4).writeEBML(segment);
+        };
+        _createCues = /* @__PURE__ */ new WeakSet();
+        createCues_fn = function() {
+          __privateSet2(this, _cues, { id: 475249515, data: [] });
+        };
+        _segmentDataOffset = /* @__PURE__ */ new WeakSet();
+        segmentDataOffset_get = function() {
+          return __privateGet2(this, _target4).dataOffsets.get(__privateGet2(this, _segment));
+        };
+        _writeVideoDecoderConfig = /* @__PURE__ */ new WeakSet();
+        writeVideoDecoderConfig_fn = function(meta) {
+          if (meta.decoderConfig) {
+            if (meta.decoderConfig.colorSpace) {
+              let colorSpace = meta.decoderConfig.colorSpace;
+              __privateSet2(this, _colorSpace, colorSpace);
+              __privateGet2(this, _colourElement).data = [
+                { id: 21937, data: {
+                  "rgb": 1,
+                  "bt709": 1,
+                  "bt470bg": 5,
+                  "smpte170m": 6
+                }[colorSpace.matrix] },
+                { id: 21946, data: {
+                  "bt709": 1,
+                  "smpte170m": 6,
+                  "iec61966-2-1": 13
+                }[colorSpace.transfer] },
+                { id: 21947, data: {
+                  "bt709": 1,
+                  "bt470bg": 5,
+                  "smpte170m": 6
+                }[colorSpace.primaries] },
+                { id: 21945, data: [1, 2][Number(colorSpace.fullRange)] }
+              ];
+              let endPos = __privateGet2(this, _target4).pos;
+              __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(__privateGet2(this, _colourElement)));
+              __privateGet2(this, _target4).writeEBML(__privateGet2(this, _colourElement));
+              __privateGet2(this, _target4).seek(endPos);
+            }
+            if (meta.decoderConfig.description) {
+              __privateMethod2(this, _writeCodecPrivate, writeCodecPrivate_fn).call(this, __privateGet2(this, _videoCodecPrivate), meta.decoderConfig.description);
+            }
+          }
+        };
+        _fixVP9ColorSpace = /* @__PURE__ */ new WeakSet();
+        fixVP9ColorSpace_fn = function(chunk) {
+          if (chunk.type !== "key")
+            return;
+          if (!__privateGet2(this, _colorSpace))
+            return;
+          let i = 0;
+          if (readBits(chunk.data, 0, 2) !== 2)
+            return;
+          i += 2;
+          let profile = (readBits(chunk.data, i + 1, i + 2) << 1) + readBits(chunk.data, i + 0, i + 1);
+          i += 2;
+          if (profile === 3)
+            i++;
+          let showExistingFrame = readBits(chunk.data, i + 0, i + 1);
+          i++;
+          if (showExistingFrame)
+            return;
+          let frameType = readBits(chunk.data, i + 0, i + 1);
+          i++;
+          if (frameType !== 0)
+            return;
+          i += 2;
+          let syncCode = readBits(chunk.data, i + 0, i + 24);
+          i += 24;
+          if (syncCode !== 4817730)
+            return;
+          if (profile >= 2)
+            i++;
+          let colorSpaceID = {
+            "rgb": 7,
+            "bt709": 2,
+            "bt470bg": 1,
+            "smpte170m": 3
+          }[__privateGet2(this, _colorSpace).matrix];
+          writeBits(chunk.data, i + 0, i + 3, colorSpaceID);
+        };
+        _createInternalChunk = /* @__PURE__ */ new WeakSet();
+        createInternalChunk_fn = function(data, type, timestamp, trackNumber) {
+          let internalChunk = {
+            data,
+            type,
+            timestamp,
+            trackNumber
+          };
+          return internalChunk;
+        };
+        _writeSimpleBlock = /* @__PURE__ */ new WeakSet();
+        writeSimpleBlock_fn = function(chunk) {
+          let msTime = Math.floor(chunk.timestamp / 1e3);
+          let clusterIsTooLong = chunk.type !== "key" && msTime - __privateGet2(this, _currentClusterTimestamp) >= MAX_CHUNK_LENGTH_MS;
+          if (clusterIsTooLong) {
+            throw new Error(
+              `Current Matroska cluster exceeded its maximum allowed length of ${MAX_CHUNK_LENGTH_MS} milliseconds. In order to produce a correct WebM file, you must pass in a video key frame at least every ${MAX_CHUNK_LENGTH_MS} milliseconds.`
+            );
+          }
+          let shouldCreateNewClusterFromKeyFrame = (chunk.trackNumber === VIDEO_TRACK_NUMBER || !__privateGet2(this, _options2).video) && chunk.type === "key" && msTime - __privateGet2(this, _currentClusterTimestamp) >= 1e3;
+          if (!__privateGet2(this, _currentCluster) || shouldCreateNewClusterFromKeyFrame) {
+            __privateMethod2(this, _createNewCluster, createNewCluster_fn).call(this, msTime);
+          }
+          let prelude = new Uint8Array(4);
+          let view2 = new DataView(prelude.buffer);
+          view2.setUint8(0, 128 | chunk.trackNumber);
+          view2.setUint16(1, msTime - __privateGet2(this, _currentClusterTimestamp), false);
+          view2.setUint8(3, Number(chunk.type === "key") << 7);
+          let simpleBlock = { id: 163, data: [
+            prelude,
+            chunk.data
+          ] };
+          __privateGet2(this, _target4).writeEBML(simpleBlock);
+          __privateSet2(this, _duration, Math.max(__privateGet2(this, _duration), msTime));
+        };
+        _writeCodecPrivate = /* @__PURE__ */ new WeakSet();
+        writeCodecPrivate_fn = function(element, data) {
+          let endPos = __privateGet2(this, _target4).pos;
+          __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(element));
+          element = [
+            { id: 25506, size: 4, data: new Uint8Array(data) },
+            { id: 236, size: 4, data: new Uint8Array(CODEC_PRIVATE_MAX_SIZE - 2 - 4 - data.byteLength) }
+          ];
+          __privateGet2(this, _target4).writeEBML(element);
+          __privateGet2(this, _target4).seek(endPos);
+        };
+        _createNewCluster = /* @__PURE__ */ new WeakSet();
+        createNewCluster_fn = function(timestamp) {
+          if (__privateGet2(this, _currentCluster)) {
+            __privateMethod2(this, _finalizeCurrentCluster, finalizeCurrentCluster_fn).call(this);
+          }
+          __privateSet2(this, _currentCluster, { id: 524531317, size: CLUSTER_SIZE_BYTES, data: [
+            { id: 231, data: timestamp }
+          ] });
+          __privateGet2(this, _target4).writeEBML(__privateGet2(this, _currentCluster));
+          __privateSet2(this, _currentClusterTimestamp, timestamp);
+          let clusterOffsetFromSegment = __privateGet2(this, _target4).offsets.get(__privateGet2(this, _currentCluster)) - __privateGet2(this, _segmentDataOffset, segmentDataOffset_get);
+          __privateGet2(this, _cues).data.push({ id: 187, data: [
+            { id: 179, data: timestamp },
+            { id: 183, data: [
+              { id: 247, data: VIDEO_TRACK_NUMBER },
+              { id: 241, data: clusterOffsetFromSegment }
+            ] }
+          ] });
+        };
+        _finalizeCurrentCluster = /* @__PURE__ */ new WeakSet();
+        finalizeCurrentCluster_fn = function() {
+          let clusterSize = __privateGet2(this, _target4).pos - __privateGet2(this, _target4).dataOffsets.get(__privateGet2(this, _currentCluster));
+          let endPos = __privateGet2(this, _target4).pos;
+          __privateGet2(this, _target4).seek(__privateGet2(this, _target4).offsets.get(__privateGet2(this, _currentCluster)) + 4);
+          __privateGet2(this, _target4).writeEBMLVarInt(clusterSize, CLUSTER_SIZE_BYTES);
+          __privateGet2(this, _target4).seek(endPos);
+        };
+        _ensureNotFinalized2 = /* @__PURE__ */ new WeakSet();
+        ensureNotFinalized_fn2 = function() {
+          if (__privateGet2(this, _finalized2)) {
+            throw new Error("Cannot add new video or audio chunks after the file has been finalized.");
+          }
+        };
+        var main_default = WebMMuxer3;
+        var readBits = (bytes2, start, end) => {
+          let result = 0;
+          for (let i = start; i < end; i++) {
+            let byteIndex = Math.floor(i / 8);
+            let byte = bytes2[byteIndex];
+            let bitIndex = 7 - (i & 7);
+            let bit = (byte & 1 << bitIndex) >> bitIndex;
+            result <<= 1;
+            result |= bit;
+          }
+          return result;
+        };
+        var writeBits = (bytes2, start, end, value) => {
+          for (let i = start; i < end; i++) {
+            let byteIndex = Math.floor(i / 8);
+            let byte = bytes2[byteIndex];
+            let bitIndex = 7 - (i & 7);
+            byte &= ~(1 << bitIndex);
+            byte |= (value & 1 << end - i - 1) >> end - i - 1 << bitIndex;
+            bytes2[byteIndex] = byte;
+          }
+        };
+        return __toCommonJS(main_exports);
+      })();
+      WebMMuxer2 = WebMMuxer2.default;
+      if (typeof module === "object" && typeof module.exports === "object") module.exports = WebMMuxer2;
+    }
+  });
+
+  // node_modules/mp4-muxer/build/mp4-muxer.mjs
+  var __defProp2 = Object.defineProperty;
+  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
+  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
+  var __propIsEnum = Object.prototype.propertyIsEnumerable;
+  var __pow = Math.pow;
+  var __defNormalProp = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
+  var __spreadValues = (a, b) => {
+    for (var prop in b || (b = {}))
+      if (__hasOwnProp2.call(b, prop))
+        __defNormalProp(a, prop, b[prop]);
+    if (__getOwnPropSymbols)
+      for (var prop of __getOwnPropSymbols(b)) {
+        if (__propIsEnum.call(b, prop))
+          __defNormalProp(a, prop, b[prop]);
+      }
+    return a;
+  };
+  var __accessCheck = (obj, member, msg) => {
+    if (!member.has(obj))
+      throw TypeError("Cannot " + msg);
+  };
+  var __privateGet = (obj, member, getter) => {
+    __accessCheck(obj, member, "read from private field");
+    return getter ? getter.call(obj) : member.get(obj);
+  };
+  var __privateAdd = (obj, member, value) => {
+    if (member.has(obj))
+      throw TypeError("Cannot add the same private member more than once");
+    member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
+  };
+  var __privateSet = (obj, member, value, setter) => {
+    __accessCheck(obj, member, "write to private field");
+    setter ? setter.call(obj, value) : member.set(obj, value);
+    return value;
+  };
+  var __privateMethod = (obj, member, method) => {
+    __accessCheck(obj, member, "access private method");
+    return method;
+  };
+  var bytes = new Uint8Array(8);
+  var view = new DataView(bytes.buffer);
+  var u8 = (value) => {
+    return [(value % 256 + 256) % 256];
+  };
+  var u16 = (value) => {
+    view.setUint16(0, value, false);
+    return [bytes[0], bytes[1]];
+  };
+  var i16 = (value) => {
+    view.setInt16(0, value, false);
+    return [bytes[0], bytes[1]];
+  };
+  var u24 = (value) => {
+    view.setUint32(0, value, false);
+    return [bytes[1], bytes[2], bytes[3]];
+  };
+  var u32 = (value) => {
+    view.setUint32(0, value, false);
+    return [bytes[0], bytes[1], bytes[2], bytes[3]];
+  };
+  var u64 = (value) => {
+    view.setUint32(0, Math.floor(value / __pow(2, 32)), false);
+    view.setUint32(4, value, false);
+    return [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]];
+  };
+  var fixed_8_8 = (value) => {
+    view.setInt16(0, __pow(2, 8) * value, false);
+    return [bytes[0], bytes[1]];
+  };
+  var fixed_16_16 = (value) => {
+    view.setInt32(0, __pow(2, 16) * value, false);
+    return [bytes[0], bytes[1], bytes[2], bytes[3]];
+  };
+  var fixed_2_30 = (value) => {
+    view.setInt32(0, __pow(2, 30) * value, false);
+    return [bytes[0], bytes[1], bytes[2], bytes[3]];
+  };
+  var ascii = (text, nullTerminated = false) => {
+    let bytes2 = Array(text.length).fill(null).map((_, i) => text.charCodeAt(i));
+    if (nullTerminated)
+      bytes2.push(0);
+    return bytes2;
+  };
+  var last = (arr) => {
+    return arr && arr[arr.length - 1];
+  };
+  var intoTimescale = (timeInSeconds, timescale, round = true) => {
+    let value = timeInSeconds * timescale;
+    return round ? Math.round(value) : value;
+  };
+  var rotationMatrix = (rotationInDegrees) => {
+    let theta = rotationInDegrees * (Math.PI / 180);
+    let cosTheta = Math.cos(theta);
+    let sinTheta = Math.sin(theta);
+    return [
+      cosTheta,
+      sinTheta,
+      0,
+      -sinTheta,
+      cosTheta,
+      0,
+      0,
+      0,
+      1
+    ];
+  };
+  var IDENTITY_MATRIX = rotationMatrix(0);
+  var matrixToBytes = (matrix) => {
+    return [
+      fixed_16_16(matrix[0]),
+      fixed_16_16(matrix[1]),
+      fixed_2_30(matrix[2]),
+      fixed_16_16(matrix[3]),
+      fixed_16_16(matrix[4]),
+      fixed_2_30(matrix[5]),
+      fixed_16_16(matrix[6]),
+      fixed_16_16(matrix[7]),
+      fixed_2_30(matrix[8])
+    ];
+  };
+  var box = (type, contents, children) => ({
+    type,
+    contents: contents && new Uint8Array(contents.flat(10)),
+    children
+  });
+  var fullBox = (type, version, flags, contents, children) => box(
+    type,
+    [u8(version), u24(flags), contents != null ? contents : []],
+    children
+  );
+  var ftyp = (holdsHevc) => {
+    if (holdsHevc)
+      return box("ftyp", [
+        ascii("isom"),
+        // Major brand
+        u32(0),
+        // Minor version
+        ascii("iso4"),
+        // Compatible brand 1
+        ascii("hvc1")
+        // Compatible brand 2
+      ]);
+    return box("ftyp", [
+      ascii("isom"),
+      // Major brand
+      u32(0),
+      // Minor version
+      ascii("isom"),
+      // Compatible brand 1
+      ascii("avc1"),
+      // Compatible brand 2
+      ascii("mp41")
+      // Compatible brand 3
+    ]);
+  };
+  var mdat = () => ({ type: "mdat", largeSize: true });
+  var moov = (tracks, creationTime) => box("moov", null, [
+    mvhd(creationTime, tracks),
+    ...tracks.map((x) => trak(x, creationTime))
+  ]);
+  var mvhd = (creationTime, tracks) => {
+    let duration = intoTimescale(Math.max(
+      0,
+      ...tracks.filter((x) => x.samples.length > 0).map((x) => last(x.samples).timestamp + last(x.samples).duration)
+    ), GLOBAL_TIMESCALE);
+    let nextTrackId = Math.max(...tracks.map((x) => x.id)) + 1;
+    return fullBox("mvhd", 0, 0, [
+      u32(creationTime),
+      // Creation time
+      u32(creationTime),
+      // Modification time
+      u32(GLOBAL_TIMESCALE),
+      // Timescale
+      u32(duration),
+      // Duration
+      fixed_16_16(1),
+      // Preferred rate
+      fixed_8_8(1),
+      // Preferred volume
+      Array(10).fill(0),
+      // Reserved
+      matrixToBytes(IDENTITY_MATRIX),
+      // Matrix
+      Array(24).fill(0),
+      // Pre-defined
+      u32(nextTrackId)
+      // Next track ID
+    ]);
+  };
+  var trak = (track, creationTime) => box("trak", null, [
+    tkhd(track, creationTime),
+    mdia(track, creationTime)
+  ]);
+  var tkhd = (track, creationTime) => {
+    let lastSample = last(track.samples);
+    let durationInGlobalTimescale = intoTimescale(
+      lastSample ? lastSample.timestamp + lastSample.duration : 0,
+      GLOBAL_TIMESCALE
+    );
+    return fullBox("tkhd", 0, 3, [
+      u32(creationTime),
+      // Creation time
+      u32(creationTime),
+      // Modification time
+      u32(track.id),
+      // Track ID
+      u32(0),
+      // Reserved
+      u32(durationInGlobalTimescale),
+      // Duration
+      Array(8).fill(0),
+      // Reserved
+      u16(0),
+      // Layer
+      u16(0),
+      // Alternate group
+      fixed_8_8(track.info.type === "audio" ? 1 : 0),
+      // Volume
+      u16(0),
+      // Reserved
+      matrixToBytes(rotationMatrix(track.info.type === "video" ? track.info.rotation : 0)),
+      // Matrix
+      fixed_16_16(track.info.type === "video" ? track.info.width : 0),
+      // Track width
+      fixed_16_16(track.info.type === "video" ? track.info.height : 0)
+      // Track height
+    ]);
+  };
+  var mdia = (track, creationTime) => box("mdia", null, [
+    mdhd(track, creationTime),
+    hdlr(track.info.type === "video" ? "vide" : "soun"),
+    minf(track)
+  ]);
+  var mdhd = (track, creationTime) => {
+    let lastSample = last(track.samples);
+    let localDuration = intoTimescale(
+      lastSample ? lastSample.timestamp + lastSample.duration : 0,
+      track.timescale
+    );
+    return fullBox("mdhd", 0, 0, [
+      u32(creationTime),
+      // Creation time
+      u32(creationTime),
+      // Modification time
+      u32(track.timescale),
+      // Timescale
+      u32(localDuration),
+      // Duration
+      u16(21956),
+      // Language ("und", undetermined)
+      u16(0)
+      // Quality
+    ]);
+  };
+  var hdlr = (componentSubtype) => fullBox("hdlr", 0, 0, [
+    ascii("mhlr"),
+    // Component type
+    ascii(componentSubtype),
+    // Component subtype
+    u32(0),
+    // Component manufacturer
+    u32(0),
+    // Component flags
+    u32(0),
+    // Component flags mask
+    ascii("mp4-muxer-hdlr")
+    // Component name
+  ]);
+  var minf = (track) => box("minf", null, [
+    track.info.type === "video" ? vmhd() : smhd(),
+    dinf(),
+    stbl(track)
+  ]);
+  var vmhd = () => fullBox("vmhd", 0, 1, [
+    u16(0),
+    // Graphics mode
+    u16(0),
+    // Opcolor R
+    u16(0),
+    // Opcolor G
+    u16(0)
+    // Opcolor B
+  ]);
+  var smhd = () => fullBox("smhd", 0, 0, [
+    u16(0),
+    // Balance
+    u16(0)
+    // Reserved
+  ]);
+  var dinf = () => box("dinf", null, [
+    dref()
+  ]);
+  var dref = () => fullBox("dref", 0, 0, [
+    u32(1)
+    // Entry count
+  ], [
+    url()
+  ]);
+  var url = () => fullBox("url ", 0, 1);
+  var stbl = (track) => box("stbl", null, [
+    stsd(track),
+    stts(track),
+    stss(track),
+    stsc(track),
+    stsz(track),
+    stco(track)
+  ]);
+  var stsd = (track) => fullBox("stsd", 0, 0, [
+    u32(1)
+    // Entry count
+  ], [
+    track.info.type === "video" ? videoSampleDescription(
+      VIDEO_CODEC_TO_BOX_NAME[track.info.codec],
+      track
+    ) : soundSampleDescription(
+      AUDIO_CODEC_TO_BOX_NAME[track.info.codec],
+      track
+    )
+  ]);
+  var videoSampleDescription = (compressionType, track) => box(compressionType, [
+    Array(6).fill(0),
+    // Reserved
+    u16(1),
+    // Data reference index
+    u16(0),
+    // Pre-defined
+    u16(0),
+    // Reserved
+    Array(12).fill(0),
+    // Pre-defined
+    u16(track.info.width),
+    // Width
+    u16(track.info.height),
+    // Height
+    u32(4718592),
+    // Horizontal resolution
+    u32(4718592),
+    // Vertical resolution
+    u32(0),
+    // Reserved
+    u16(1),
+    // Frame count
+    Array(32).fill(0),
+    // Compressor name
+    u16(24),
+    // Depth
+    i16(65535)
+    // Pre-defined
+  ], [
+    VIDEO_CODEC_TO_CONFIGURATION_BOX[track.info.codec](track)
+  ]);
+  var avcC = (track) => track.codecPrivate && box("avcC", [...track.codecPrivate]);
+  var hvcC = (track) => track.codecPrivate && box("hvcC", [...track.codecPrivate]);
+  var vpcC = (track) => track.codecPrivate && box("vpcC", [...track.codecPrivate]);
+  var av1C = (track) => track.codecPrivate && box("av1C", [...track.codecPrivate]);
+  var soundSampleDescription = (compressionType, track) => box(compressionType, [
+    Array(6).fill(0),
+    // Reserved
+    u16(1),
+    // Data reference index
+    u16(0),
+    // Version
+    u16(0),
+    // Revision level
+    u32(0),
+    // Vendor
+    u16(track.info.numberOfChannels),
+    // Number of channels
+    u16(16),
+    // Sample size (bits)
+    u16(0),
+    // Compression ID
+    u16(0),
+    // Packet size
+    fixed_16_16(track.info.sampleRate)
+    // Sample rate
+  ], [
+    AUDIO_CODEC_TO_CONFIGURATION_BOX[track.info.codec](track)
+  ]);
+  var esds = (track) => fullBox("esds", 0, 0, [
+    // https://stackoverflow.com/a/54803118
+    u32(58753152),
+    // TAG(3) = Object Descriptor ([2])
+    u8(32 + track.codecPrivate.byteLength),
+    // length of this OD (which includes the next 2 tags)
+    u16(1),
+    // ES_ID = 1
+    u8(0),
+    // flags etc = 0
+    u32(75530368),
+    // TAG(4) = ES Descriptor ([2]) embedded in above OD
+    u8(18 + track.codecPrivate.byteLength),
+    // length of this ESD
+    u8(64),
+    // MPEG-4 Audio
+    u8(21),
+    // stream type(6bits)=5 audio, flags(2bits)=1
+    u24(0),
+    // 24bit buffer size
+    u32(130071),
+    // max bitrate
+    u32(130071),
+    // avg bitrate
+    u32(92307584),
+    // TAG(5) = ASC ([2],[3]) embedded in above OD
+    u8(track.codecPrivate.byteLength),
+    // length
+    ...track.codecPrivate,
+    u32(109084800),
+    // TAG(6)
+    u8(1),
+    // length
+    u8(2)
+    // data
+  ]);
+  var dOps = (track) => box("dOps", [
+    u8(0),
+    // Version
+    u8(track.info.numberOfChannels),
+    // OutputChannelCount
+    u16(3840),
+    // PreSkip, should be at least 80 milliseconds worth of playback, measured in 48000 Hz samples
+    u32(track.info.sampleRate),
+    // InputSampleRate
+    fixed_8_8(0),
+    // OutputGain
+    u8(0)
+    // ChannelMappingFamily
+  ]);
+  var stts = (track) => {
+    return fullBox("stts", 0, 0, [
+      u32(track.timeToSampleTable.length),
+      // Number of entries
+      track.timeToSampleTable.map((x) => [
+        // Time-to-sample table
+        u32(x.sampleCount),
+        // Sample count
+        u32(x.sampleDelta)
+        // Sample duration
+      ])
+    ]);
+  };
+  var stss = (track) => {
+    if (track.samples.every((x) => x.type === "key"))
+      return null;
+    let keySamples = [...track.samples.entries()].filter(([, sample]) => sample.type === "key");
+    return fullBox("stss", 0, 0, [
+      u32(keySamples.length),
+      // Number of entries
+      keySamples.map(([index]) => u32(index + 1))
+      // Sync sample table
+    ]);
+  };
+  var stsc = (track) => {
+    return fullBox("stsc", 0, 0, [
+      u32(track.compactlyCodedChunkTable.length),
+      // Number of entries
+      track.compactlyCodedChunkTable.map((x) => [
+        // Sample-to-chunk table
+        u32(x.firstChunk),
+        // First chunk
+        u32(x.samplesPerChunk),
+        // Samples per chunk
+        u32(1)
+        // Sample description index
+      ])
+    ]);
+  };
+  var stsz = (track) => fullBox("stsz", 0, 0, [
+    u32(0),
+    // Sample size (0 means non-constant size)
+    u32(track.samples.length),
+    // Number of entries
+    track.samples.map((x) => u32(x.size))
+    // Sample size table
+  ]);
+  var stco = (track) => {
+    if (track.writtenChunks.length > 0 && last(track.writtenChunks).offset >= __pow(2, 32)) {
+      return fullBox("co64", 0, 0, [
+        u32(track.writtenChunks.length),
+        // Number of entries
+        track.writtenChunks.map((x) => u64(x.offset))
+        // Chunk offset table
+      ]);
+    }
+    return fullBox("stco", 0, 0, [
+      u32(track.writtenChunks.length),
+      // Number of entries
+      track.writtenChunks.map((x) => u32(x.offset))
+      // Chunk offset table
+    ]);
+  };
+  var VIDEO_CODEC_TO_BOX_NAME = {
+    "avc": "avc1",
+    "hevc": "hvc1",
+    "vp9": "vp09",
+    "av1": "av01"
+  };
+  var VIDEO_CODEC_TO_CONFIGURATION_BOX = {
+    "avc": avcC,
+    "hevc": hvcC,
+    "vp9": vpcC,
+    "av1": av1C
+  };
+  var AUDIO_CODEC_TO_BOX_NAME = {
+    "aac": "mp4a",
+    "opus": "opus"
+  };
+  var AUDIO_CODEC_TO_CONFIGURATION_BOX = {
+    "aac": esds,
+    "opus": dOps
+  };
+  var ArrayBufferTarget = class {
+    constructor() {
+      this.buffer = null;
+    }
+  };
+  var StreamTarget = class {
+    constructor(onData, onDone, options) {
+      this.onData = onData;
+      this.onDone = onDone;
+      this.options = options;
+    }
+  };
+  var FileSystemWritableFileStreamTarget = class {
+    constructor(stream, options) {
+      this.stream = stream;
+      this.options = options;
+    }
+  };
+  var _helper;
+  var _helperView;
+  var Writer = class {
+    constructor() {
+      this.pos = 0;
+      __privateAdd(this, _helper, new Uint8Array(8));
+      __privateAdd(this, _helperView, new DataView(__privateGet(this, _helper).buffer));
+      this.offsets = /* @__PURE__ */ new WeakMap();
+    }
+    /** Sets the current position for future writes to a new one. */
+    seek(newPos) {
+      this.pos = newPos;
+    }
+    writeU32(value) {
+      __privateGet(this, _helperView).setUint32(0, value, false);
+      this.write(__privateGet(this, _helper).subarray(0, 4));
+    }
+    writeU64(value) {
+      __privateGet(this, _helperView).setUint32(0, Math.floor(value / __pow(2, 32)), false);
+      __privateGet(this, _helperView).setUint32(4, value, false);
+      this.write(__privateGet(this, _helper).subarray(0, 8));
+    }
+    writeAscii(text) {
+      for (let i = 0; i < text.length; i++) {
+        __privateGet(this, _helperView).setUint8(i % 8, text.charCodeAt(i));
+        if (i % 8 === 7)
+          this.write(__privateGet(this, _helper));
+      }
+      if (text.length % 8 !== 0) {
+        this.write(__privateGet(this, _helper).subarray(0, text.length % 8));
+      }
+    }
+    writeBox(box2) {
+      var _a, _b;
+      this.offsets.set(box2, this.pos);
+      if (box2.contents && !box2.children) {
+        this.writeBoxHeader(box2, (_a = box2.size) != null ? _a : box2.contents.byteLength + 8);
+        this.write(box2.contents);
+      } else {
+        let startPos = this.pos;
+        this.writeBoxHeader(box2, 0);
+        if (box2.contents)
+          this.write(box2.contents);
+        if (box2.children) {
+          for (let child of box2.children)
+            if (child)
+              this.writeBox(child);
+        }
+        let endPos = this.pos;
+        let size = (_b = box2.size) != null ? _b : endPos - startPos;
+        this.seek(startPos);
+        this.writeBoxHeader(box2, size);
+        this.seek(endPos);
+      }
+    }
+    writeBoxHeader(box2, size) {
+      this.writeU32(box2.largeSize ? 1 : size);
+      this.writeAscii(box2.type);
+      if (box2.largeSize)
+        this.writeU64(size);
+    }
+    patchBox(box2) {
+      let endPos = this.pos;
+      this.seek(this.offsets.get(box2));
+      this.writeBox(box2);
+      this.seek(endPos);
+    }
+  };
+  _helper = /* @__PURE__ */ new WeakMap();
+  _helperView = /* @__PURE__ */ new WeakMap();
+  var _target;
+  var _buffer;
+  var _bytes;
+  var _ensureSize;
+  var ensureSize_fn;
+  var ArrayBufferTargetWriter = class extends Writer {
+    constructor(target) {
+      super();
+      __privateAdd(this, _ensureSize);
+      __privateAdd(this, _target, void 0);
+      __privateAdd(this, _buffer, new ArrayBuffer(__pow(2, 16)));
+      __privateAdd(this, _bytes, new Uint8Array(__privateGet(this, _buffer)));
+      __privateSet(this, _target, target);
+    }
+    write(data) {
+      __privateMethod(this, _ensureSize, ensureSize_fn).call(this, this.pos + data.byteLength);
+      __privateGet(this, _bytes).set(data, this.pos);
+      this.pos += data.byteLength;
+    }
+    finalize() {
+      __privateMethod(this, _ensureSize, ensureSize_fn).call(this, this.pos);
+      __privateGet(this, _target).buffer = __privateGet(this, _buffer).slice(0, this.pos);
+    }
+  };
+  _target = /* @__PURE__ */ new WeakMap();
+  _buffer = /* @__PURE__ */ new WeakMap();
+  _bytes = /* @__PURE__ */ new WeakMap();
+  _ensureSize = /* @__PURE__ */ new WeakSet();
+  ensureSize_fn = function(size) {
+    let newLength = __privateGet(this, _buffer).byteLength;
+    while (newLength < size)
+      newLength *= 2;
+    if (newLength === __privateGet(this, _buffer).byteLength)
+      return;
+    let newBuffer = new ArrayBuffer(newLength);
+    let newBytes = new Uint8Array(newBuffer);
+    newBytes.set(__privateGet(this, _bytes), 0);
+    __privateSet(this, _buffer, newBuffer);
+    __privateSet(this, _bytes, newBytes);
+  };
+  var _target2;
+  var _sections;
+  var StreamTargetWriter = class extends Writer {
+    constructor(target) {
+      super();
+      __privateAdd(this, _target2, void 0);
+      __privateAdd(this, _sections, []);
+      __privateSet(this, _target2, target);
+    }
+    write(data) {
+      __privateGet(this, _sections).push({
+        data: data.slice(),
+        start: this.pos
+      });
+      this.pos += data.byteLength;
+    }
+    flush() {
+      if (__privateGet(this, _sections).length === 0)
+        return;
+      let chunks = [];
+      let sorted = [...__privateGet(this, _sections)].sort((a, b) => a.start - b.start);
+      chunks.push({
+        start: sorted[0].start,
+        size: sorted[0].data.byteLength
+      });
+      for (let i = 1; i < sorted.length; i++) {
+        let lastChunk = chunks[chunks.length - 1];
+        let section = sorted[i];
+        if (section.start <= lastChunk.start + lastChunk.size) {
+          lastChunk.size = Math.max(lastChunk.size, section.start + section.data.byteLength - lastChunk.start);
+        } else {
+          chunks.push({
+            start: section.start,
+            size: section.data.byteLength
+          });
+        }
+      }
+      for (let chunk of chunks) {
+        chunk.data = new Uint8Array(chunk.size);
+        for (let section of __privateGet(this, _sections)) {
+          if (chunk.start <= section.start && section.start < chunk.start + chunk.size) {
+            chunk.data.set(section.data, section.start - chunk.start);
+          }
+        }
+        __privateGet(this, _target2).onData(chunk.data, chunk.start);
+      }
+      __privateGet(this, _sections).length = 0;
+    }
+    finalize() {
+      var _a, _b;
+      (_b = (_a = __privateGet(this, _target2)).onDone) == null ? void 0 : _b.call(_a);
+    }
+  };
+  _target2 = /* @__PURE__ */ new WeakMap();
+  _sections = /* @__PURE__ */ new WeakMap();
+  var DEFAULT_CHUNK_SIZE = __pow(2, 24);
+  var MAX_CHUNKS_AT_ONCE = 2;
+  var _target3;
+  var _chunkSize;
+  var _chunks;
+  var _writeDataIntoChunks;
+  var writeDataIntoChunks_fn;
+  var _insertSectionIntoChunk;
+  var insertSectionIntoChunk_fn;
+  var _createChunk;
+  var createChunk_fn;
+  var _flushChunks;
+  var flushChunks_fn;
+  var ChunkedStreamTargetWriter = class extends Writer {
+    constructor(target) {
+      var _a, _b;
+      super();
+      __privateAdd(this, _writeDataIntoChunks);
+      __privateAdd(this, _insertSectionIntoChunk);
+      __privateAdd(this, _createChunk);
+      __privateAdd(this, _flushChunks);
+      __privateAdd(this, _target3, void 0);
+      __privateAdd(this, _chunkSize, void 0);
+      __privateAdd(this, _chunks, []);
+      __privateSet(this, _target3, target);
+      __privateSet(this, _chunkSize, (_b = (_a = target.options) == null ? void 0 : _a.chunkSize) != null ? _b : DEFAULT_CHUNK_SIZE);
+      if (!Number.isInteger(__privateGet(this, _chunkSize)) || __privateGet(this, _chunkSize) < __pow(2, 10)) {
+        throw new Error("Invalid StreamTarget options: chunkSize must be an integer not smaller than 1024.");
+      }
+    }
+    write(data) {
+      __privateMethod(this, _writeDataIntoChunks, writeDataIntoChunks_fn).call(this, data, this.pos);
+      __privateMethod(this, _flushChunks, flushChunks_fn).call(this);
+      this.pos += data.byteLength;
+    }
+    finalize() {
+      var _a, _b;
+      __privateMethod(this, _flushChunks, flushChunks_fn).call(this, true);
+      (_b = (_a = __privateGet(this, _target3)).onDone) == null ? void 0 : _b.call(_a);
+    }
+  };
+  _target3 = /* @__PURE__ */ new WeakMap();
+  _chunkSize = /* @__PURE__ */ new WeakMap();
+  _chunks = /* @__PURE__ */ new WeakMap();
+  _writeDataIntoChunks = /* @__PURE__ */ new WeakSet();
+  writeDataIntoChunks_fn = function(data, position) {
+    let chunkIndex = __privateGet(this, _chunks).findIndex((x) => x.start <= position && position < x.start + __privateGet(this, _chunkSize));
+    if (chunkIndex === -1)
+      chunkIndex = __privateMethod(this, _createChunk, createChunk_fn).call(this, position);
+    let chunk = __privateGet(this, _chunks)[chunkIndex];
+    let relativePosition = position - chunk.start;
+    let toWrite = data.subarray(0, Math.min(__privateGet(this, _chunkSize) - relativePosition, data.byteLength));
+    chunk.data.set(toWrite, relativePosition);
+    let section = {
+      start: relativePosition,
+      end: relativePosition + toWrite.byteLength
+    };
+    __privateMethod(this, _insertSectionIntoChunk, insertSectionIntoChunk_fn).call(this, chunk, section);
+    if (chunk.written[0].start === 0 && chunk.written[0].end === __privateGet(this, _chunkSize)) {
+      chunk.shouldFlush = true;
+    }
+    if (__privateGet(this, _chunks).length > MAX_CHUNKS_AT_ONCE) {
+      for (let i = 0; i < __privateGet(this, _chunks).length - 1; i++) {
+        __privateGet(this, _chunks)[i].shouldFlush = true;
+      }
+      __privateMethod(this, _flushChunks, flushChunks_fn).call(this);
+    }
+    if (toWrite.byteLength < data.byteLength) {
+      __privateMethod(this, _writeDataIntoChunks, writeDataIntoChunks_fn).call(this, data.subarray(toWrite.byteLength), position + toWrite.byteLength);
+    }
+  };
+  _insertSectionIntoChunk = /* @__PURE__ */ new WeakSet();
+  insertSectionIntoChunk_fn = function(chunk, section) {
+    let low = 0;
+    let high = chunk.written.length - 1;
+    let index = -1;
+    while (low <= high) {
+      let mid = Math.floor(low + (high - low + 1) / 2);
+      if (chunk.written[mid].start <= section.start) {
+        low = mid + 1;
+        index = mid;
+      } else {
+        high = mid - 1;
+      }
+    }
+    chunk.written.splice(index + 1, 0, section);
+    if (index === -1 || chunk.written[index].end < section.start)
+      index++;
+    while (index < chunk.written.length - 1 && chunk.written[index].end >= chunk.written[index + 1].start) {
+      chunk.written[index].end = Math.max(chunk.written[index].end, chunk.written[index + 1].end);
+      chunk.written.splice(index + 1, 1);
+    }
+  };
+  _createChunk = /* @__PURE__ */ new WeakSet();
+  createChunk_fn = function(includesPosition) {
+    let start = Math.floor(includesPosition / __privateGet(this, _chunkSize)) * __privateGet(this, _chunkSize);
+    let chunk = {
+      start,
+      data: new Uint8Array(__privateGet(this, _chunkSize)),
+      written: [],
+      shouldFlush: false
+    };
+    __privateGet(this, _chunks).push(chunk);
+    __privateGet(this, _chunks).sort((a, b) => a.start - b.start);
+    return __privateGet(this, _chunks).indexOf(chunk);
+  };
+  _flushChunks = /* @__PURE__ */ new WeakSet();
+  flushChunks_fn = function(force = false) {
+    for (let i = 0; i < __privateGet(this, _chunks).length; i++) {
+      let chunk = __privateGet(this, _chunks)[i];
+      if (!chunk.shouldFlush && !force)
+        continue;
+      for (let section of chunk.written) {
+        __privateGet(this, _target3).onData(
+          chunk.data.subarray(section.start, section.end),
+          chunk.start + section.start
+        );
+      }
+      __privateGet(this, _chunks).splice(i--, 1);
+    }
+  };
+  var FileSystemWritableFileStreamTargetWriter = class extends ChunkedStreamTargetWriter {
+    constructor(target) {
+      var _a;
+      super(new StreamTarget(
+        (data, position) => target.stream.write({
+          type: "write",
+          data,
+          position
+        }),
+        void 0,
+        { chunkSize: (_a = target.options) == null ? void 0 : _a.chunkSize }
+      ));
+    }
+  };
+  var GLOBAL_TIMESCALE = 1e3;
+  var SUPPORTED_VIDEO_CODECS2 = ["avc", "hevc", "vp9", "av1"];
+  var SUPPORTED_AUDIO_CODECS2 = ["aac", "opus"];
+  var TIMESTAMP_OFFSET = 2082844800;
+  var MAX_CHUNK_DURATION = 0.5;
+  var FIRST_TIMESTAMP_BEHAVIORS = ["strict", "offset"];
+  var _options;
+  var _writer;
+  var _mdat;
+  var _videoTrack;
+  var _audioTrack;
+  var _creationTime;
+  var _finalized;
+  var _validateOptions;
+  var validateOptions_fn;
+  var _writeHeader;
+  var writeHeader_fn;
+  var _prepareTracks;
+  var prepareTracks_fn;
+  var _generateMpeg4AudioSpecificConfig;
+  var generateMpeg4AudioSpecificConfig_fn;
+  var _addSampleToTrack;
+  var addSampleToTrack_fn;
+  var _validateTimestamp;
+  var validateTimestamp_fn;
+  var _writeCurrentChunk;
+  var writeCurrentChunk_fn;
+  var _maybeFlushStreamingTargetWriter;
+  var maybeFlushStreamingTargetWriter_fn;
+  var _ensureNotFinalized;
+  var ensureNotFinalized_fn;
+  var Muxer = class {
+    constructor(options) {
+      __privateAdd(this, _validateOptions);
+      __privateAdd(this, _writeHeader);
+      __privateAdd(this, _prepareTracks);
+      __privateAdd(this, _generateMpeg4AudioSpecificConfig);
+      __privateAdd(this, _addSampleToTrack);
+      __privateAdd(this, _validateTimestamp);
+      __privateAdd(this, _writeCurrentChunk);
+      __privateAdd(this, _maybeFlushStreamingTargetWriter);
+      __privateAdd(this, _ensureNotFinalized);
+      __privateAdd(this, _options, void 0);
+      __privateAdd(this, _writer, void 0);
+      __privateAdd(this, _mdat, void 0);
+      __privateAdd(this, _videoTrack, null);
+      __privateAdd(this, _audioTrack, null);
+      __privateAdd(this, _creationTime, Math.floor(Date.now() / 1e3) + TIMESTAMP_OFFSET);
+      __privateAdd(this, _finalized, false);
+      var _a;
+      __privateMethod(this, _validateOptions, validateOptions_fn).call(this, options);
+      this.target = options.target;
+      __privateSet(this, _options, __spreadValues({
+        firstTimestampBehavior: "strict"
+      }, options));
+      if (options.target instanceof ArrayBufferTarget) {
+        __privateSet(this, _writer, new ArrayBufferTargetWriter(options.target));
+      } else if (options.target instanceof StreamTarget) {
+        __privateSet(this, _writer, ((_a = options.target.options) == null ? void 0 : _a.chunked) ? new ChunkedStreamTargetWriter(options.target) : new StreamTargetWriter(options.target));
+      } else if (options.target instanceof FileSystemWritableFileStreamTarget) {
+        __privateSet(this, _writer, new FileSystemWritableFileStreamTargetWriter(options.target));
+      } else {
+        throw new Error(`Invalid target: ${options.target}`);
+      }
+      __privateMethod(this, _writeHeader, writeHeader_fn).call(this);
+      __privateMethod(this, _prepareTracks, prepareTracks_fn).call(this);
+    }
+    addVideoChunk(sample, meta, timestamp) {
+      let data = new Uint8Array(sample.byteLength);
+      sample.copyTo(data);
+      this.addVideoChunkRaw(data, sample.type, timestamp != null ? timestamp : sample.timestamp, sample.duration, meta);
+    }
+    addVideoChunkRaw(data, type, timestamp, duration, meta) {
+      __privateMethod(this, _ensureNotFinalized, ensureNotFinalized_fn).call(this);
+      if (!__privateGet(this, _options).video)
+        throw new Error("No video track declared.");
+      __privateMethod(this, _addSampleToTrack, addSampleToTrack_fn).call(this, __privateGet(this, _videoTrack), data, type, timestamp, duration, meta);
+    }
+    addAudioChunk(sample, meta, timestamp) {
+      let data = new Uint8Array(sample.byteLength);
+      sample.copyTo(data);
+      this.addAudioChunkRaw(data, sample.type, timestamp != null ? timestamp : sample.timestamp, sample.duration, meta);
+    }
+    addAudioChunkRaw(data, type, timestamp, duration, meta) {
+      __privateMethod(this, _ensureNotFinalized, ensureNotFinalized_fn).call(this);
+      if (!__privateGet(this, _options).audio)
+        throw new Error("No audio track declared.");
+      __privateMethod(this, _addSampleToTrack, addSampleToTrack_fn).call(this, __privateGet(this, _audioTrack), data, type, timestamp, duration, meta);
+    }
+    /** Finalizes the file, making it ready for use. Must be called after all video and audio chunks have been added. */
+    finalize() {
+      if (__privateGet(this, _videoTrack))
+        __privateMethod(this, _writeCurrentChunk, writeCurrentChunk_fn).call(this, __privateGet(this, _videoTrack));
+      if (__privateGet(this, _audioTrack))
+        __privateMethod(this, _writeCurrentChunk, writeCurrentChunk_fn).call(this, __privateGet(this, _audioTrack));
+      let mdatPos = __privateGet(this, _writer).offsets.get(__privateGet(this, _mdat));
+      let mdatSize = __privateGet(this, _writer).pos - mdatPos;
+      __privateGet(this, _mdat).size = mdatSize;
+      __privateGet(this, _writer).patchBox(__privateGet(this, _mdat));
+      let movieBox = moov([__privateGet(this, _videoTrack), __privateGet(this, _audioTrack)].filter(Boolean), __privateGet(this, _creationTime));
+      __privateGet(this, _writer).writeBox(movieBox);
+      __privateMethod(this, _maybeFlushStreamingTargetWriter, maybeFlushStreamingTargetWriter_fn).call(this);
+      __privateGet(this, _writer).finalize();
+      __privateSet(this, _finalized, true);
+    }
+  };
+  _options = /* @__PURE__ */ new WeakMap();
+  _writer = /* @__PURE__ */ new WeakMap();
+  _mdat = /* @__PURE__ */ new WeakMap();
+  _videoTrack = /* @__PURE__ */ new WeakMap();
+  _audioTrack = /* @__PURE__ */ new WeakMap();
+  _creationTime = /* @__PURE__ */ new WeakMap();
+  _finalized = /* @__PURE__ */ new WeakMap();
+  _validateOptions = /* @__PURE__ */ new WeakSet();
+  validateOptions_fn = function(options) {
+    if (options.video) {
+      if (!SUPPORTED_VIDEO_CODECS2.includes(options.video.codec)) {
+        throw new Error(`Unsupported video codec: ${options.video.codec}`);
+      }
+      if (options.video.rotation !== void 0 && ![0, 90, 180, 270].includes(options.video.rotation)) {
+        throw new Error(`Invalid video rotation: ${options.video.rotation}. Has to be 0, 90, 180 or 270.`);
+      }
+    }
+    if (options.audio && !SUPPORTED_AUDIO_CODECS2.includes(options.audio.codec)) {
+      throw new Error(`Unsupported audio codec: ${options.audio.codec}`);
+    }
+    if (options.firstTimestampBehavior && !FIRST_TIMESTAMP_BEHAVIORS.includes(options.firstTimestampBehavior)) {
+      throw new Error(`Invalid first timestamp behavior: ${options.firstTimestampBehavior}`);
+    }
+  };
+  _writeHeader = /* @__PURE__ */ new WeakSet();
+  writeHeader_fn = function() {
+    var _a;
+    let holdsHevc = ((_a = __privateGet(this, _options).video) == null ? void 0 : _a.codec) === "hevc";
+    __privateGet(this, _writer).writeBox(ftyp(holdsHevc));
+    __privateSet(this, _mdat, mdat());
+    __privateGet(this, _writer).writeBox(__privateGet(this, _mdat));
+    __privateMethod(this, _maybeFlushStreamingTargetWriter, maybeFlushStreamingTargetWriter_fn).call(this);
+  };
+  _prepareTracks = /* @__PURE__ */ new WeakSet();
+  prepareTracks_fn = function() {
+    var _a;
+    if (__privateGet(this, _options).video) {
+      __privateSet(this, _videoTrack, {
+        id: 1,
+        info: {
+          type: "video",
+          codec: __privateGet(this, _options).video.codec,
+          width: __privateGet(this, _options).video.width,
+          height: __privateGet(this, _options).video.height,
+          rotation: (_a = __privateGet(this, _options).video.rotation) != null ? _a : 0
+        },
+        timescale: 720,
+        // = lcm(24, 30, 60, 120, 144, 240, 360), so should fit with many framerates
+        codecPrivate: new Uint8Array(0),
+        samples: [],
+        writtenChunks: [],
+        currentChunk: null,
+        firstTimestamp: void 0,
+        lastTimestamp: -1,
+        timeToSampleTable: [],
+        lastTimescaleUnits: null,
+        compactlyCodedChunkTable: []
+      });
+    }
+    if (__privateGet(this, _options).audio) {
+      let guessedCodecPrivate = __privateMethod(this, _generateMpeg4AudioSpecificConfig, generateMpeg4AudioSpecificConfig_fn).call(
+        this,
+        2,
+        // Object type for AAC-LC, since it's the most common
+        __privateGet(this, _options).audio.sampleRate,
+        __privateGet(this, _options).audio.numberOfChannels
+      );
+      __privateSet(this, _audioTrack, {
+        id: __privateGet(this, _options).video ? 2 : 1,
+        info: {
+          type: "audio",
+          codec: __privateGet(this, _options).audio.codec,
+          numberOfChannels: __privateGet(this, _options).audio.numberOfChannels,
+          sampleRate: __privateGet(this, _options).audio.sampleRate
+        },
+        timescale: __privateGet(this, _options).audio.sampleRate,
+        codecPrivate: guessedCodecPrivate,
+        samples: [],
+        writtenChunks: [],
+        currentChunk: null,
+        firstTimestamp: void 0,
+        lastTimestamp: -1,
+        timeToSampleTable: [],
+        lastTimescaleUnits: null,
+        compactlyCodedChunkTable: []
+      });
+    }
+  };
+  _generateMpeg4AudioSpecificConfig = /* @__PURE__ */ new WeakSet();
+  generateMpeg4AudioSpecificConfig_fn = function(objectType, sampleRate, numberOfChannels) {
+    let frequencyIndices = [96e3, 88200, 64e3, 48e3, 44100, 32e3, 24e3, 22050, 16e3, 12e3, 11025, 8e3, 7350];
+    let frequencyIndex = frequencyIndices.indexOf(sampleRate);
+    let channelConfig = numberOfChannels;
+    let configBits = "";
+    configBits += objectType.toString(2).padStart(5, "0");
+    configBits += frequencyIndex.toString(2).padStart(4, "0");
+    if (frequencyIndex === 15)
+      configBits += sampleRate.toString(2).padStart(24, "0");
+    configBits += channelConfig.toString(2).padStart(4, "0");
+    let paddingLength = Math.ceil(configBits.length / 8) * 8;
+    configBits = configBits.padEnd(paddingLength, "0");
+    let configBytes = new Uint8Array(configBits.length / 8);
+    for (let i = 0; i < configBits.length; i += 8) {
+      configBytes[i / 8] = parseInt(configBits.slice(i, i + 8), 2);
+    }
+    return configBytes;
+  };
+  _addSampleToTrack = /* @__PURE__ */ new WeakSet();
+  addSampleToTrack_fn = function(track, data, type, timestamp, duration, meta) {
+    var _a;
+    let timestampInSeconds = timestamp / 1e6;
+    let durationInSeconds = duration / 1e6;
+    if (track.firstTimestamp === void 0)
+      track.firstTimestamp = timestampInSeconds;
+    timestampInSeconds = __privateMethod(this, _validateTimestamp, validateTimestamp_fn).call(this, timestampInSeconds, track);
+    track.lastTimestamp = timestampInSeconds;
+    if (!track.currentChunk || timestampInSeconds - track.currentChunk.startTimestamp >= MAX_CHUNK_DURATION) {
+      if (track.currentChunk)
+        __privateMethod(this, _writeCurrentChunk, writeCurrentChunk_fn).call(this, track);
+      track.currentChunk = {
+        startTimestamp: timestampInSeconds,
+        sampleData: [],
+        sampleCount: 0
+      };
+    }
+    track.currentChunk.sampleData.push(data);
+    track.currentChunk.sampleCount++;
+    if ((_a = meta == null ? void 0 : meta.decoderConfig) == null ? void 0 : _a.description) {
+      track.codecPrivate = new Uint8Array(meta.decoderConfig.description);
+    }
+    track.samples.push({
+      timestamp: timestampInSeconds,
+      duration: durationInSeconds,
+      size: data.byteLength,
+      type
+    });
+    if (track.lastTimescaleUnits !== null) {
+      let timescaleUnits = intoTimescale(timestampInSeconds, track.timescale, false);
+      let delta = Math.round(timescaleUnits - track.lastTimescaleUnits);
+      track.lastTimescaleUnits += delta;
+      let lastTableEntry = last(track.timeToSampleTable);
+      if (lastTableEntry.sampleCount === 1) {
+        lastTableEntry.sampleDelta = delta;
+        lastTableEntry.sampleCount++;
+      } else if (lastTableEntry.sampleDelta === delta) {
+        lastTableEntry.sampleCount++;
+      } else {
+        lastTableEntry.sampleCount--;
+        track.timeToSampleTable.push({
+          sampleCount: 2,
+          sampleDelta: delta
+        });
+      }
+    } else {
+      track.lastTimescaleUnits = 0;
+      track.timeToSampleTable.push({
+        sampleCount: 1,
+        sampleDelta: intoTimescale(durationInSeconds, track.timescale)
+      });
+    }
+  };
+  _validateTimestamp = /* @__PURE__ */ new WeakSet();
+  validateTimestamp_fn = function(timestamp, track) {
+    if (__privateGet(this, _options).firstTimestampBehavior === "strict" && track.lastTimestamp === -1 && timestamp !== 0) {
+      throw new Error(
+        `The first chunk for your media track must have a timestamp of 0 (received ${timestamp}). Non-zero first timestamps are often caused by directly piping frames or audio data from a MediaStreamTrack into the encoder. Their timestamps are typically relative to the age of the document, which is probably what you want.
+
+If you want to offset all timestamps of a track such that the first one is zero, set firstTimestampBehavior: 'offset' in the options.
+`
+      );
+    } else if (__privateGet(this, _options).firstTimestampBehavior === "offset") {
+      timestamp -= track.firstTimestamp;
+    }
+    if (timestamp < track.lastTimestamp) {
+      throw new Error(
+        `Timestamps must be monotonically increasing (went from ${track.lastTimestamp * 1e6} to ${timestamp * 1e6}).`
+      );
+    }
+    return timestamp;
+  };
+  _writeCurrentChunk = /* @__PURE__ */ new WeakSet();
+  writeCurrentChunk_fn = function(track) {
+    if (!track.currentChunk)
+      return;
+    track.currentChunk.offset = __privateGet(this, _writer).pos;
+    for (let bytes2 of track.currentChunk.sampleData)
+      __privateGet(this, _writer).write(bytes2);
+    track.currentChunk.sampleData = null;
+    if (track.compactlyCodedChunkTable.length === 0 || last(track.compactlyCodedChunkTable).samplesPerChunk !== track.currentChunk.sampleCount) {
+      track.compactlyCodedChunkTable.push({
+        firstChunk: track.writtenChunks.length + 1,
+        // 1-indexed
+        samplesPerChunk: track.currentChunk.sampleCount
+      });
+    }
+    track.writtenChunks.push(track.currentChunk);
+    __privateMethod(this, _maybeFlushStreamingTargetWriter, maybeFlushStreamingTargetWriter_fn).call(this);
+  };
+  _maybeFlushStreamingTargetWriter = /* @__PURE__ */ new WeakSet();
+  maybeFlushStreamingTargetWriter_fn = function() {
+    if (__privateGet(this, _writer) instanceof StreamTargetWriter) {
+      __privateGet(this, _writer).flush();
+    }
+  };
+  _ensureNotFinalized = /* @__PURE__ */ new WeakSet();
+  ensureNotFinalized_fn = function() {
+    if (__privateGet(this, _finalized)) {
+      throw new Error("Cannot add new video or audio chunks after the file has been finalized.");
+    }
+  };
+
+  // src/muxers/mp4muxer.ts
+  var Mp4MuxerWrapper = class {
+    constructor(config, postMessageCallback, options) {
+      this.videoConfigured = false;
+      this.audioConfigured = false;
+      this.firstAudioTimestamp = null;
+      this.firstVideoTimestamp = null;
+      this.firstTimestamp = null;
+      this.config = config;
+      this.postMessageToMain = postMessageCallback;
+      let disableAudio = options?.disableAudio ?? false;
+      const videoDisabled = config.width <= 0 || config.height <= 0 || config.videoBitrate <= 0;
+      const audioCodecOption = config.codec?.audio ?? "aac";
+      const supportedAudioCodecsForMp4 = /* @__PURE__ */ new Set(["aac", "mp3"]);
+      if (!supportedAudioCodecsForMp4.has(audioCodecOption)) {
+        console.warn(
+          `MP4 muxer: Audio codec ${audioCodecOption} is not supported. Disabling audio track.`
+        );
+        disableAudio = true;
+      }
+      const muxerAudioCodec = audioCodecOption;
+      const commonMuxerOptions = {};
+      if (!videoDisabled) {
+        const videoCodecOption = config.codec?.video ?? "avc";
+        let muxerVideoCodec;
+        switch (videoCodecOption) {
+          case "hevc":
+            muxerVideoCodec = "hevc";
+            break;
+          case "vp9":
+            muxerVideoCodec = "vp9";
+            break;
+          case "av1":
+            muxerVideoCodec = "av1";
+            break;
+          case "avc":
+          default:
+            muxerVideoCodec = "avc";
+            break;
+        }
+        commonMuxerOptions.video = {
+          codec: muxerVideoCodec,
+          width: config.width,
+          height: config.height
+          // framerate is not directly a muxer option here, but good to have in config
+        };
+      }
+      if (!disableAudio) {
+        commonMuxerOptions.audio = {
+          codec: muxerAudioCodec,
+          sampleRate: config.sampleRate,
+          numberOfChannels: config.channels
+        };
+      }
+      if (config.latencyMode === "realtime") {
+        this.target = new StreamTarget({
+          onData: (chunk, position) => {
+            const chunkCopy = new Uint8Array(chunk.slice(0));
+            const isHeader = position === 0;
+            const message = {
+              type: "dataChunk",
+              chunk: chunkCopy,
+              offset: position,
+              // Use position as offset
+              isHeader,
+              container: "mp4"
+            };
+            this.postMessageToMain(message, [chunkCopy.buffer]);
+          }
+        });
+        this.muxer = new Muxer({
+          target: this.target,
+          ...commonMuxerOptions,
+          fastStart: "fragmented"
+        });
+      } else {
+        this.target = new ArrayBufferTarget();
+        this.muxer = new Muxer({
+          target: this.target,
+          ...commonMuxerOptions,
+          fastStart: "in-memory"
+          // or false, depending on desired behavior for non-realtime
+        });
+      }
+      this.videoConfigured = !videoDisabled;
+      this.audioConfigured = !disableAudio;
+    }
+    addVideoChunk(chunk, meta) {
+      if (!this.videoConfigured) {
+        this.postMessageToMain({
+          type: "error",
+          errorDetail: {
+            message: "MP4: Video track not configured.",
+            type: "configuration-error" /* ConfigurationError */
+          }
+        });
+        return;
+      }
+      try {
+        let adjustedChunk = chunk;
+        const adjustedMeta = meta;
+        if (this.config.firstTimestampBehavior === "offset" && typeof chunk.timestamp === "number") {
+          if (this.firstVideoTimestamp === null) {
+            this.firstVideoTimestamp = chunk.timestamp;
+            if (this.firstTimestamp === null) {
+              this.firstTimestamp = chunk.timestamp;
+            } else {
+              this.firstTimestamp = Math.min(
+                this.firstTimestamp,
+                chunk.timestamp
+              );
+            }
+          }
+          const newTimestamp = Math.max(
+            0,
+            chunk.timestamp - (this.firstTimestamp || 0)
+          );
+          const data = new Uint8Array(chunk.byteLength);
+          chunk.copyTo(data.buffer);
+          chunk.close?.();
+          adjustedChunk = new EncodedVideoChunk({
+            type: chunk.type,
+            timestamp: newTimestamp,
+            duration: chunk.duration ?? void 0,
+            data: data.buffer
+          });
+        } else if (typeof chunk.timestamp !== "number" && this.config.firstTimestampBehavior === "offset") {
+        }
+        this.muxer.addVideoChunk(adjustedChunk, adjustedMeta);
+      } catch (e) {
+        this.postMessageToMain({
+          type: "error",
+          errorDetail: {
+            message: `MP4: Error adding video chunk: ${e.message}`,
+            type: "muxing-failed" /* MuxingFailed */,
+            stack: e.stack
+          }
+        });
+      }
+    }
+    addAudioChunk(chunk, meta) {
+      if (!this.audioConfigured) {
+        return;
+      }
+      try {
+        let adjustedChunk = chunk;
+        const adjustedMeta = meta;
+        if (this.config.firstTimestampBehavior === "offset" && typeof chunk.timestamp === "number") {
+          if (this.firstAudioTimestamp === null) {
+            this.firstAudioTimestamp = chunk.timestamp;
+            if (this.firstTimestamp === null) {
+              this.firstTimestamp = chunk.timestamp;
+            } else {
+              this.firstTimestamp = Math.min(
+                this.firstTimestamp,
+                chunk.timestamp
+              );
+            }
+          }
+          const newTimestamp = Math.max(
+            0,
+            chunk.timestamp - (this.firstTimestamp || 0)
+          );
+          const data = new Uint8Array(chunk.byteLength);
+          chunk.copyTo(data.buffer);
+          chunk.close?.();
+          adjustedChunk = new EncodedAudioChunk({
+            type: chunk.type,
+            timestamp: newTimestamp,
+            duration: chunk.duration ?? void 0,
+            data: data.buffer
+          });
+        } else if (typeof chunk.timestamp !== "number" && this.config.firstTimestampBehavior === "offset") {
+        }
+        this.muxer.addAudioChunk(adjustedChunk, adjustedMeta);
+      } catch (e) {
+        this.postMessageToMain({
+          type: "error",
+          errorDetail: {
+            message: `MP4: Error adding audio chunk: ${e.message}`,
+            type: "muxing-failed" /* MuxingFailed */,
+            stack: e.stack
+          }
+        });
+      }
+    }
+    finalize() {
+      if (this.config.latencyMode === "realtime") {
+        try {
+          this.muxer.finalize();
+        } catch (e) {
+          this.postMessageToMain({
+            type: "error",
+            errorDetail: {
+              message: `MP4: Error finalizing muxer (realtime): ${e.message}`,
+              type: "muxing-failed" /* MuxingFailed */,
+              stack: e.stack
+            }
+          });
+        }
+        return null;
+      }
+      if (!(this.target instanceof ArrayBufferTarget)) {
+        this.postMessageToMain({
+          type: "error",
+          errorDetail: {
+            message: "MP4: Muxer target is not ArrayBufferTarget in non-realtime mode.",
+            type: "unknown" /* Unknown */
+          }
+        });
+        return null;
+      }
+      try {
+        this.muxer.finalize();
+        const buffer = this.target.buffer;
+        this.target = new ArrayBufferTarget();
+        return new Uint8Array(buffer);
+      } catch (e) {
+        this.postMessageToMain({
+          type: "error",
+          errorDetail: {
+            message: `MP4: Error finalizing muxer (non-realtime): ${e.message}`,
+            type: "muxing-failed" /* MuxingFailed */,
+            stack: e.stack
+          }
+        });
+        return null;
+      }
+    }
+  };
+
+  // src/muxers/webmmuxer.ts
+  var import_webm_muxer = __toESM(require_webm_muxer(), 1);
+  var CallbackWritableStream = class {
+    constructor(onData) {
+      this.onData = onData;
+      this.position = 0;
+    }
+    write({ data, position }) {
+      this.onData(data, position);
+      this.position = position + data.byteLength;
+    }
+  };
+  var WebMMuxerWrapper = class {
+    constructor(config, postMessageCallback, options) {
+      this.videoConfigured = false;
+      this.audioConfigured = false;
+      this.firstAudioTimestamp = null;
+      this.firstVideoTimestamp = null;
+      this.firstTimestamp = null;
+      this.config = config;
+      this.postMessageToMain = postMessageCallback;
+      let disableAudio = options?.disableAudio ?? false;
+      const videoCodecOption = config.codec?.video ?? "vp9";
+      let muxerVideoCodec;
+      switch (videoCodecOption) {
+        case "vp8":
+          muxerVideoCodec = "V_VP8";
+          break;
+        case "vp9":
+          muxerVideoCodec = "V_VP9";
+          break;
+        case "av1":
+          muxerVideoCodec = "V_AV1";
+          break;
+        default:
+          muxerVideoCodec = "V_VP9";
+          break;
+      }
+      const requestedAudioCodec = config.codec?.audio ?? "opus";
+      let muxerAudioCodec = null;
+      switch (requestedAudioCodec) {
+        case "opus":
+          muxerAudioCodec = "A_OPUS";
+          break;
+        case "vorbis":
+          muxerAudioCodec = "A_VORBIS";
+          break;
+        case "flac":
+          muxerAudioCodec = "A_FLAC";
+          break;
+        default:
+          if (!disableAudio) {
+            console.warn(
+              `WebM muxer: Audio codec ${requestedAudioCodec} is not supported. Disabling audio track.`
+            );
+            disableAudio = true;
+          }
+          break;
+      }
+      const target = config.latencyMode === "realtime" ? new CallbackWritableStream((chunk, position) => {
+        const chunkCopy = new Uint8Array(chunk.slice(0));
+        const isHeader = position === 0;
+        const message = {
+          type: "dataChunk",
+          chunk: chunkCopy,
+          offset: position,
+          isHeader,
+          container: "webm"
+        };
+        this.postMessageToMain(message, [chunkCopy.buffer]);
+      }) : "buffer";
+      const videoDisabled = config.width === 0 || config.height === 0 || config.videoBitrate === 0;
+      const optionsForMuxer = {
+        target
+      };
+      if (!videoDisabled) {
+        optionsForMuxer.video = {
+          codec: muxerVideoCodec,
+          width: config.width,
+          height: config.height,
+          frameRate: config.frameRate
+        };
+      }
+      if (!disableAudio && muxerAudioCodec) {
+        optionsForMuxer.audio = {
+          codec: muxerAudioCodec,
+          numberOfChannels: config.channels,
+          sampleRate: config.sampleRate
+        };
+      }
+      this.muxer = new import_webm_muxer.default(optionsForMuxer);
+      this.videoConfigured = !videoDisabled;
+      this.audioConfigured = !disableAudio;
+    }
+    addVideoChunk(chunk, meta) {
+      if (!this.videoConfigured) {
+        this.postMessageToMain({
+          type: "error",
+          errorDetail: {
+            message: "WebM: Video track not configured.",
+            type: "configuration-error" /* ConfigurationError */
+          }
+        });
+        return;
+      }
+      try {
+        let adjustedChunk = chunk;
+        const adjustedMeta = meta;
+        if (this.config.firstTimestampBehavior === "offset" && typeof chunk.timestamp === "number") {
+          if (this.firstVideoTimestamp === null) {
+            this.firstVideoTimestamp = chunk.timestamp;
+            if (this.firstTimestamp === null) {
+              this.firstTimestamp = chunk.timestamp;
+            } else {
+              this.firstTimestamp = Math.min(
+                this.firstTimestamp,
+                chunk.timestamp
+              );
+            }
+          }
+          const newTimestamp = Math.max(
+            0,
+            chunk.timestamp - (this.firstTimestamp || 0)
+          );
+          const data = new Uint8Array(chunk.byteLength);
+          chunk.copyTo(data.buffer);
+          chunk.close?.();
+          adjustedChunk = new EncodedVideoChunk({
+            type: chunk.type,
+            timestamp: newTimestamp,
+            duration: chunk.duration ?? void 0,
+            data: data.buffer
+          });
+        }
+        this.muxer.addVideoChunk(adjustedChunk, adjustedMeta);
+      } catch (e) {
+        this.postMessageToMain({
+          type: "error",
+          errorDetail: {
+            message: `WebM: Error adding video chunk: ${e.message}`,
+            type: "muxing-failed" /* MuxingFailed */,
+            stack: e.stack
+          }
+        });
+      }
+    }
+    addAudioChunk(chunk, meta) {
+      if (!this.audioConfigured) return;
+      try {
+        let adjustedChunk = chunk;
+        const adjustedMeta = meta;
+        if (this.config.firstTimestampBehavior === "offset" && typeof chunk.timestamp === "number") {
+          if (this.firstAudioTimestamp === null) {
+            this.firstAudioTimestamp = chunk.timestamp;
+            if (this.firstTimestamp === null) {
+              this.firstTimestamp = chunk.timestamp;
+            } else {
+              this.firstTimestamp = Math.min(
+                this.firstTimestamp,
+                chunk.timestamp
+              );
+            }
+          }
+          const newTimestamp = Math.max(
+            0,
+            chunk.timestamp - (this.firstTimestamp || 0)
+          );
+          const data = new Uint8Array(chunk.byteLength);
+          chunk.copyTo(data.buffer);
+          chunk.close?.();
+          adjustedChunk = new EncodedAudioChunk({
+            type: chunk.type,
+            timestamp: newTimestamp,
+            duration: chunk.duration ?? void 0,
+            data: data.buffer
+          });
+        }
+        this.muxer.addAudioChunk(adjustedChunk, adjustedMeta);
+      } catch (e) {
+        this.postMessageToMain({
+          type: "error",
+          errorDetail: {
+            message: `WebM: Error adding audio chunk: ${e.message}`,
+            type: "muxing-failed" /* MuxingFailed */,
+            stack: e.stack
+          }
+        });
+      }
+    }
+    finalize() {
+      if (this.config.latencyMode === "realtime") {
+        try {
+          this.muxer.finalize();
+        } catch (e) {
+          this.postMessageToMain({
+            type: "error",
+            errorDetail: {
+              message: `WebM: Error finalizing muxer (realtime): ${e.message}`,
+              type: "muxing-failed" /* MuxingFailed */,
+              stack: e.stack
+            }
+          });
+        }
+        return null;
+      }
+      try {
+        const buffer = this.muxer.finalize();
+        if (buffer) return new Uint8Array(buffer);
+        this.postMessageToMain({
+          type: "error",
+          errorDetail: {
+            message: "WebM: Muxer finalized without output in non-realtime mode.",
+            type: "muxing-failed" /* MuxingFailed */
+          }
+        });
+        return null;
+      } catch (e) {
+        this.postMessageToMain({
+          type: "error",
+          errorDetail: {
+            message: `WebM: Error finalizing muxer (non-realtime): ${e.message}`,
+            type: "muxing-failed" /* MuxingFailed */,
+            stack: e.stack
+          }
+        });
+        return null;
+      }
+    }
+  };
+
+  // src/worker/encoder-worker.ts
+  if (typeof self !== "undefined" && typeof self.addEventListener === "function") {
+    self.addEventListener("error", (event) => {
+      console.error("Unhandled global error in worker. Event:", event);
+      const message = event.message || `Unhandled global error${event.filename ? ` at ${event.filename}` : ""}`;
+      self.postMessage({
+        type: "error",
+        errorDetail: {
+          message,
+          type: "worker-error" /* WorkerError */,
+          stack: event.error?.stack
+        }
+      });
+    });
+  }
+  if (typeof self !== "undefined" && typeof self.addEventListener === "function") {
+    self.addEventListener(
+      "unhandledrejection",
+      (event) => {
+        console.error(
+          "Unhandled promise rejection in worker. Reason:",
+          event.reason
+        );
+        const reason = event.reason;
+        const message = reason instanceof Error ? `Unhandled promise rejection: ${reason.message}` : `Unhandled promise rejection: ${String(reason)}`;
+        self.postMessage({
+          type: "error",
+          errorDetail: {
+            message,
+            type: "worker-error" /* WorkerError */,
+            stack: reason instanceof Error ? reason.stack : void 0
+          }
+        });
+      }
+    );
+  }
+  var getVideoEncoder = () => self.VideoEncoder ?? globalThis.VideoEncoder;
+  var getAudioEncoder = () => self.AudioEncoder ?? globalThis.AudioEncoder;
+  var getAudioData = () => self.AudioData ?? globalThis.AudioData;
+  var EncoderWorker = class {
+    constructor() {
+      this.videoEncoder = null;
+      this.audioEncoder = null;
+      this.muxer = null;
+      this.currentConfig = null;
+      this.processedFrames = 0;
+      this.videoFrameCount = 0;
+      this.isCancelled = false;
+    }
+    postMessageToMainThread(message, transfer) {
+      if (transfer && transfer.length > 0) {
+        self.postMessage(message, transfer);
+      } else {
+        self.postMessage(message);
+      }
+    }
+    defaultAvcCodecString(width, height, frameRate, profile) {
+      const mbPerSec = Math.ceil(width / 16) * Math.ceil(height / 16) * frameRate;
+      let level;
+      if (mbPerSec <= 108e3) level = 31;
+      else if (mbPerSec <= 216e3) level = 32;
+      else if (mbPerSec <= 245760) level = 40;
+      else if (mbPerSec <= 589824) level = 50;
+      else if (mbPerSec <= 983040) level = 51;
+      else level = 52;
+      const chosenProfile = profile ?? (width >= 1280 || height >= 720 ? "high" : "baseline");
+      const profileHex = chosenProfile === "high" ? "64" : chosenProfile === "main" ? "4d" : "42";
+      const levelHex = level.toString(16).padStart(2, "0");
+      return `avc1.${profileHex}00${levelHex}`;
+    }
+    getCodecString(codecType, width, height, frameRate) {
+      switch (codecType) {
+        case "avc":
+          return this.defaultAvcCodecString(width, height, frameRate);
+        case "vp9":
+          return "vp09.00.50.08";
+        case "vp8":
+          return "vp8";
+        case "hevc":
+          return "hvc1";
+        case "av1":
+          return "av01.0.04M.08";
+        default:
+          return codecType;
+      }
+    }
+    async isConfigSupportedWithHwFallback(Ctor, config, label) {
+      let support = await Ctor.isConfigSupported(config);
+      if (support?.supported && support.config) return support.config;
+      const pref = config.hardwareAcceleration;
+      if (pref) {
+        let altPref;
+        if (pref === "prefer-hardware") altPref = "prefer-software";
+        else if (pref === "prefer-software") altPref = "prefer-hardware";
+        if (altPref) {
+          const opposite = { ...config, hardwareAcceleration: altPref };
+          support = await Ctor.isConfigSupported(opposite);
+          if (support?.supported && support.config) {
+            console.warn(
+              `${label}: hardwareAcceleration preference '${pref}' not supported. Using '${altPref}'.`
+            );
+            return support.config;
+          }
+        }
+        const noPref = { ...config };
+        delete noPref.hardwareAcceleration;
+        support = await Ctor.isConfigSupported(noPref);
+        if (support?.supported && support.config) {
+          console.warn(
+            `${label}: hardwareAcceleration preference '${pref}' not supported. Using no preference.`
+          );
+          return support.config;
+        }
+        console.warn(
+          `${label}: Failed to find a supported hardware acceleration configuration for codec ${config.codec}.`
+        );
+      }
+      return null;
+    }
+    postQueueSize() {
+      this.postMessageToMainThread({
+        type: "queueSize",
+        videoQueueSize: this.videoEncoder?.encodeQueueSize ?? 0,
+        audioQueueSize: this.audioEncoder?.encodeQueueSize ?? 0
+      });
+    }
+    async prepareAudioCodec(container, audioDisabled) {
+      if (audioDisabled) {
+        return {
+          audioDisabled: true,
+          selectedCodec: null,
+          finalConfig: null,
+          encoderCtor: null
+        };
+      }
+      const AudioEncoderCtor = getAudioEncoder();
+      if (!AudioEncoderCtor) {
+        this.postMessageToMainThread({
+          type: "error",
+          errorDetail: {
+            message: "Worker: AudioEncoder not available",
+            type: "not-supported" /* NotSupported */
+          }
+        });
+        return {
+          audioDisabled: true,
+          selectedCodec: null,
+          finalConfig: null,
+          encoderCtor: null
+        };
+      }
+      const config = this.currentConfig;
+      if (!config) {
+        return {
+          audioDisabled: true,
+          selectedCodec: null,
+          finalConfig: null,
+          encoderCtor: AudioEncoderCtor
+        };
+      }
+      const requestedCodec = config.codec?.audio;
+      const requestedCodecString = config.codecString?.audio;
+      const preference = buildAudioCodecPreference(container, requestedCodec);
+      let attemptedDefaultCodec = false;
+      for (const candidate of preference) {
+        if (candidate === "aac") {
+          attemptedDefaultCodec = true;
+        }
+        const codecString = requestedCodec && requestedCodecString && candidate === requestedCodec ? requestedCodecString : getAudioEncoderCodecStringFromAudioCodec(candidate);
+        const baseConfig = {
+          codec: codecString,
+          sampleRate: config.sampleRate,
+          numberOfChannels: config.channels,
+          bitrate: config.audioBitrate,
+          ...config.audioBitrateMode && {
+            bitrateMode: config.audioBitrateMode
+          },
+          ...config.hardwareAcceleration && {
+            hardwareAcceleration: config.hardwareAcceleration
+          },
+          ...getAudioEncoderConfigOverridesForCodec(
+            candidate,
+            config.audioEncoderConfig
+          )
+        };
+        const support = await this.isConfigSupportedWithHwFallback(
+          AudioEncoderCtor,
+          baseConfig,
+          "AudioEncoder"
+        );
+        if (!support) {
+          if (candidate === "aac" && container === "mp4") {
+            console.warn(
+              "Worker: AAC audio codec is not supported. Falling back to MP3."
+            );
+            const mp3AttemptConfig = {
+              codec: getAudioEncoderCodecStringFromAudioCodec("mp3"),
+              sampleRate: config.sampleRate,
+              numberOfChannels: config.channels,
+              bitrate: config.audioBitrate,
+              ...config.audioBitrateMode && {
+                bitrateMode: config.audioBitrateMode
+              },
+              ...config.hardwareAcceleration && {
+                hardwareAcceleration: config.hardwareAcceleration
+              },
+              ...getAudioEncoderConfigOverridesForCodec(
+                "mp3",
+                config.audioEncoderConfig
+              )
+            };
+            const mp3Support = await this.isConfigSupportedWithHwFallback(
+              AudioEncoderCtor,
+              mp3AttemptConfig,
+              "AudioEncoder"
+            );
+            if (mp3Support) {
+              const resolvedMp3Config = mp3Support;
+              if (resolvedMp3Config.numberOfChannels !== void 0 && resolvedMp3Config.numberOfChannels !== config.channels) {
+                this.postMessageToMainThread({
+                  type: "error",
+                  errorDetail: {
+                    message: `AudioEncoder reported numberOfChannels (${resolvedMp3Config.numberOfChannels}) does not match configured channels (${config.channels}).`,
+                    type: "configuration-error" /* ConfigurationError */
+                  }
+                });
+                return {
+                  audioDisabled: true,
+                  selectedCodec: null,
+                  finalConfig: null,
+                  encoderCtor: AudioEncoderCtor
+                };
+              }
+              if (resolvedMp3Config.sampleRate !== void 0 && resolvedMp3Config.sampleRate !== config.sampleRate) {
+                this.postMessageToMainThread({
+                  type: "error",
+                  errorDetail: {
+                    message: `AudioEncoder reported sampleRate (${resolvedMp3Config.sampleRate}) does not match configured sampleRate (${config.sampleRate}).`,
+                    type: "configuration-error" /* ConfigurationError */
+                  }
+                });
+                return {
+                  audioDisabled: true,
+                  selectedCodec: null,
+                  finalConfig: null,
+                  encoderCtor: AudioEncoderCtor
+                };
+              }
+              if (!isAudioCodecMuxerCompatible(container, "mp3")) {
+                console.warn(
+                  "Worker: Audio codec mp3 is not compatible with MP4 muxer. Audio will be disabled."
+                );
+              } else {
+                console.warn("Worker: Falling back to MP3 for MP4 container.");
+                return {
+                  audioDisabled: false,
+                  selectedCodec: "mp3",
+                  finalConfig: resolvedMp3Config,
+                  encoderCtor: AudioEncoderCtor
+                };
+              }
+            }
+          } else {
+            console.warn(
+              `Worker: Audio codec ${candidate} not supported or config invalid.`
+            );
+          }
+          continue;
+        }
+        const resolvedConfig = support;
+        if (resolvedConfig.numberOfChannels !== void 0 && resolvedConfig.numberOfChannels !== config.channels) {
+          this.postMessageToMainThread({
+            type: "error",
+            errorDetail: {
+              message: `AudioEncoder reported numberOfChannels (${resolvedConfig.numberOfChannels}) does not match configured channels (${config.channels}).`,
+              type: "configuration-error" /* ConfigurationError */
+            }
+          });
+          return {
+            audioDisabled: true,
+            selectedCodec: null,
+            finalConfig: null,
+            encoderCtor: AudioEncoderCtor
+          };
+        }
+        if (resolvedConfig.sampleRate !== void 0 && resolvedConfig.sampleRate !== config.sampleRate) {
+          this.postMessageToMainThread({
+            type: "error",
+            errorDetail: {
+              message: `AudioEncoder reported sampleRate (${resolvedConfig.sampleRate}) does not match configured sampleRate (${config.sampleRate}).`,
+              type: "configuration-error" /* ConfigurationError */
+            }
+          });
+          return {
+            audioDisabled: true,
+            selectedCodec: null,
+            finalConfig: null,
+            encoderCtor: AudioEncoderCtor
+          };
+        }
+        if (!isAudioCodecMuxerCompatible(container, candidate)) {
+          console.warn(
+            `Worker: Audio codec ${candidate} is not compatible with ${container.toUpperCase()} muxer. Trying fallback codec.`
+          );
+          continue;
+        }
+        if (candidate === "aac") {
+          attemptedDefaultCodec = true;
+          if (container === "mp4" && requestedCodec && requestedCodec !== "aac") {
+            console.warn("Worker: Falling back to AAC for MP4 container.");
+          }
+        }
+        if (container === "mp4" && candidate === "mp3" && attemptedDefaultCodec) {
+          console.warn("Worker: Falling back to MP3 for MP4 container.");
+        }
+        return {
+          audioDisabled: false,
+          selectedCodec: candidate,
+          finalConfig: resolvedConfig,
+          encoderCtor: AudioEncoderCtor
+        };
+      }
+      const defaultCodec = DEFAULT_AUDIO_CODEC_BY_CONTAINER[container];
+      const noCodecMessage = container === "mp4" ? "Worker: No supported audio codec (AAC, MP3) found for MP4 container." : `Worker: No supported audio codec found. Requested: ${requestedCodec ?? "(auto)"}. Tried: ${preference.join(", ")}.`;
+      this.postMessageToMainThread({
+        type: "error",
+        errorDetail: {
+          message: noCodecMessage,
+          type: "not-supported" /* NotSupported */
+        }
+      });
+      console.warn(
+        `Worker: Disabling audio. Consider using ${defaultCodec} for container ${container}.`
+      );
+      return {
+        audioDisabled: true,
+        selectedCodec: null,
+        finalConfig: null,
+        encoderCtor: AudioEncoderCtor
+      };
+    }
+    async initializeEncoders(data) {
+      this.currentConfig = data.config;
+      this.totalFramesToProcess = data.totalFrames;
+      this.processedFrames = 0;
+      this.videoFrameCount = 0;
+      this.isCancelled = false;
+      if (!this.currentConfig) {
+        this.postMessageToMainThread({
+          type: "error",
+          errorDetail: {
+            message: "Worker: Configuration is missing.",
+            type: "initialization-failed" /* InitializationFailed */
+          }
+        });
+        return;
+      }
+      let audioDisabled = !this.currentConfig.audioBitrate || this.currentConfig.audioBitrate <= 0 || !this.currentConfig.channels || this.currentConfig.channels <= 0 || !this.currentConfig.sampleRate || this.currentConfig.sampleRate <= 0 || !this.currentConfig.codec?.audio;
+      const containerType = getContainerType(this.currentConfig.container);
+      const audioOriginallyDisabled = audioDisabled;
+      const audioPlan = await this.prepareAudioCodec(
+        containerType,
+        audioDisabled
+      );
+      audioDisabled = audioPlan.audioDisabled;
+      let selectedAudioCodec = audioPlan.selectedCodec;
+      let finalAudioEncoderConfig = audioPlan.finalConfig;
+      const preparedAudioEncoderCtor = audioPlan.encoderCtor;
+      if (audioDisabled) {
+        selectedAudioCodec = null;
+        finalAudioEncoderConfig = null;
+      }
+      if (!audioOriginallyDisabled && audioDisabled) {
+        this.cleanup();
+        return;
+      }
+      const videoDisabled = this.currentConfig.width === 0 || this.currentConfig.height === 0 || this.currentConfig.videoBitrate === 0;
+      let videoCodec = this.currentConfig.codec?.video ?? (this.currentConfig.container === "webm" ? "vp9" : "avc");
+      const requestedVideoCodec = videoCodec;
+      let finalVideoEncoderConfig = null;
+      let resolvedVideoCodecString = null;
+      let VideoEncoderCtor;
+      if (!videoDisabled) {
+        if (this.currentConfig.container === "webm" && (videoCodec === "avc" || videoCodec === "hevc")) {
+          console.warn(
+            `Worker: Video codec ${videoCodec} not compatible with WebM. Switching to VP9.`
+          );
+          videoCodec = "vp9";
+        }
+        resolvedVideoCodecString = (this.currentConfig.codecString?.video && videoCodec === requestedVideoCodec ? this.currentConfig.codecString.video : void 0) ?? (videoCodec === "avc" ? this.defaultAvcCodecString(
+          this.currentConfig.width,
+          this.currentConfig.height,
+          this.currentConfig.frameRate
+        ) : videoCodec === "vp9" ? "vp09.00.50.08" : videoCodec === "vp8" ? "vp8" : videoCodec === "hevc" ? "hvc1" : videoCodec === "av1" ? "av01.0.04M.08" : videoCodec);
+        const videoEncoderConfig = {
+          codec: resolvedVideoCodecString,
+          width: this.currentConfig.width,
+          height: this.currentConfig.height,
+          bitrate: this.currentConfig.videoBitrate,
+          framerate: this.currentConfig.frameRate,
+          ...this.currentConfig.container === "mp4" && videoCodec === "avc" ? { avc: { format: "avc" } } : {},
+          ...this.currentConfig.hardwareAcceleration ? { hardwareAcceleration: this.currentConfig.hardwareAcceleration } : {},
+          ...getVideoEncoderConfigOverridesForCodec(
+            videoCodec,
+            this.currentConfig.videoEncoderConfig
+          )
+        };
+        VideoEncoderCtor = getVideoEncoder();
+        if (!VideoEncoderCtor) {
+          this.postMessageToMainThread({
+            type: "error",
+            errorDetail: {
+              message: "Worker: VideoEncoder not available",
+              type: "not-supported" /* NotSupported */
+            }
+          });
+          this.cleanup();
+          return;
+        }
+        const initialSupport = await VideoEncoderCtor.isConfigSupported(videoEncoderConfig);
+        if (initialSupport?.supported && initialSupport.config) {
+          finalVideoEncoderConfig = initialSupport.config;
+        } else {
+          if (videoCodec === "vp9" || videoCodec === "vp8" || videoCodec === "av1") {
+            console.warn(
+              "Worker: Video codec " + videoCodec + " not supported or config invalid. Looking for fallback..."
+            );
+            let fallbackSuccessful = false;
+            if (this.currentConfig.container === "webm") {
+              const webmCodecs = ["vp9", "vp8"];
+              for (const fallbackCodec of webmCodecs) {
+                if (fallbackCodec === videoCodec) continue;
+                console.warn(
+                  `Worker: Trying fallback to ${fallbackCodec} for WebM container.`
+                );
+                const fallbackCodecString = this.getCodecString(
+                  fallbackCodec,
+                  this.currentConfig.width,
+                  this.currentConfig.height,
+                  this.currentConfig.frameRate
+                );
+                const fallbackConfig = {
+                  ...videoEncoderConfig,
+                  codec: fallbackCodecString,
+                  ...getVideoEncoderConfigOverridesForCodec(
+                    fallbackCodec,
+                    this.currentConfig.videoEncoderConfig
+                  )
+                };
+                const support = await this.isConfigSupportedWithHwFallback(
+                  VideoEncoderCtor,
+                  fallbackConfig,
+                  "VideoEncoder"
+                );
+                if (support) {
+                  console.warn(
+                    `Worker: Successfully fell back to ${fallbackCodec} for WebM.`
+                  );
+                  videoCodec = fallbackCodec;
+                  finalVideoEncoderConfig = support;
+                  fallbackSuccessful = true;
+                  break;
+                }
+              }
+              if (!fallbackSuccessful) {
+                this.postMessageToMainThread({
+                  type: "error",
+                  errorDetail: {
+                    message: "Worker: No compatible video codec (VP9, VP8) found for WebM container.",
+                    type: "not-supported" /* NotSupported */
+                  }
+                });
+                this.cleanup();
+                return;
+              }
+            } else {
+              console.warn("Worker: Falling back to AVC for MP4 container.");
+              videoCodec = "avc";
+              const avcCodecString = this.defaultAvcCodecString(
+                this.currentConfig.width,
+                this.currentConfig.height,
+                this.currentConfig.frameRate
+              );
+              const avcConfig = {
+                ...videoEncoderConfig,
+                codec: avcCodecString,
+                ...this.currentConfig.container === "mp4" ? { avc: { format: "avc" } } : {},
+                ...getVideoEncoderConfigOverridesForCodec(
+                  "avc",
+                  this.currentConfig.videoEncoderConfig
+                )
+              };
+              const support = await this.isConfigSupportedWithHwFallback(
+                VideoEncoderCtor,
+                avcConfig,
+                "VideoEncoder"
+              );
+              if (support) {
+                finalVideoEncoderConfig = support;
+                fallbackSuccessful = true;
+              } else {
+                this.postMessageToMainThread({
+                  type: "error",
+                  errorDetail: {
+                    message: "Worker: AVC (H.264) video codec is not supported after fallback.",
+                    type: "not-supported" /* NotSupported */
+                  }
+                });
+                this.cleanup();
+                return;
+              }
+            }
+          } else {
+            const result = await this.isConfigSupportedWithHwFallback(
+              VideoEncoderCtor,
+              videoEncoderConfig,
+              "VideoEncoder"
+            );
+            if (result) {
+              finalVideoEncoderConfig = result;
+            } else {
+              this.postMessageToMainThread({
+                type: "error",
+                errorDetail: {
+                  message: `Worker: Video codec ${videoCodec} config not supported.`,
+                  type: "not-supported" /* NotSupported */
+                }
+              });
+              this.cleanup();
+              return;
+            }
+          }
+        }
+      } else {
+        videoCodec = void 0;
+      }
+      const codecConfigForMuxer = {
+        ...this.currentConfig.codec ?? {},
+        video: videoDisabled ? void 0 : videoCodec,
+        audio: audioDisabled || !selectedAudioCodec ? void 0 : selectedAudioCodec
+      };
+      this.currentConfig.codec = codecConfigForMuxer;
+      try {
+        const MuxerCtor = containerType === "webm" ? WebMMuxerWrapper : Mp4MuxerWrapper;
+        this.muxer = new MuxerCtor(
+          this.currentConfig,
+          this.postMessageToMainThread.bind(this),
+          {
+            disableAudio: audioDisabled
+          }
+        );
+      } catch (e) {
+        this.postMessageToMainThread({
+          type: "error",
+          errorDetail: {
+            message: `Worker: Failed to initialize Muxer: ${e.message}`,
+            type: "initialization-failed" /* InitializationFailed */,
+            stack: e.stack
+          }
+        });
+        this.cleanup();
+        return;
+      }
+      if (!videoDisabled) {
+        try {
+          if (!VideoEncoderCtor) {
+            this.postMessageToMainThread({
+              type: "error",
+              errorDetail: {
+                message: "Worker: VideoEncoder not available",
+                type: "not-supported" /* NotSupported */
+              }
+            });
+            this.cleanup();
+            return;
+          }
+          this.videoEncoder = new VideoEncoderCtor({
+            output: (chunk, meta) => {
+              if (this.isCancelled || !this.muxer) return;
+              this.muxer.addVideoChunk(chunk, meta);
+            },
+            error: (error) => {
+              if (this.isCancelled) return;
+              this.postMessageToMainThread({
+                type: "error",
+                errorDetail: {
+                  message: `VideoEncoder error: ${error.message}`,
+                  type: "video-encoding-error" /* VideoEncodingError */,
+                  stack: error.stack
+                }
+              });
+              this.cleanup();
+            }
+          });
+          if (finalVideoEncoderConfig) {
+            if (this.videoEncoder) {
+              this.videoEncoder.configure(finalVideoEncoderConfig);
+            } else {
+              this.postMessageToMainThread({
+                type: "error",
+                errorDetail: {
+                  message: "Worker: VideoEncoder instance is null after creation.",
+                  type: "initialization-failed" /* InitializationFailed */
+                }
+              });
+              this.cleanup();
+              return;
+            }
+          } else {
+            this.postMessageToMainThread({
+              type: "error",
+              errorDetail: {
+                message: `Worker: VideoEncoder: Failed to find a supported hardware acceleration configuration for codec ${resolvedVideoCodecString ?? "(unknown)"}`,
+                type: "not-supported" /* NotSupported */
+              }
+            });
+            this.cleanup();
+            return;
+          }
+        } catch (e) {
+          this.postMessageToMainThread({
+            type: "error",
+            errorDetail: {
+              message: `Worker: Failed to initialize VideoEncoder: ${e.message}`,
+              type: "initialization-failed" /* InitializationFailed */,
+              stack: e.stack
+            }
+          });
+          this.cleanup();
+          return;
+        }
+      }
+      if (!audioDisabled) {
+        if (!selectedAudioCodec || !finalAudioEncoderConfig || !preparedAudioEncoderCtor) {
+          this.cleanup();
+          return;
+        }
+        try {
+          this.audioEncoder = new preparedAudioEncoderCtor({
+            output: (chunk, meta) => {
+              if (this.isCancelled || !this.muxer) return;
+              this.muxer.addAudioChunk(chunk, meta);
+            },
+            error: (error) => {
+              if (this.isCancelled) return;
+              this.postMessageToMainThread({
+                type: "error",
+                errorDetail: {
+                  message: `AudioEncoder error: ${error.message}`,
+                  type: "audio-encoding-error" /* AudioEncodingError */,
+                  stack: error.stack
+                }
+              });
+              this.cleanup();
+            }
+          });
+          if (this.audioEncoder) {
+            this.audioEncoder.configure(finalAudioEncoderConfig);
+          } else {
+            this.postMessageToMainThread({
+              type: "error",
+              errorDetail: {
+                message: "Worker: AudioEncoder instance is null after creation.",
+                type: "initialization-failed" /* InitializationFailed */
+              }
+            });
+            this.cleanup();
+            return;
+          }
+        } catch (e) {
+          this.postMessageToMainThread({
+            type: "error",
+            errorDetail: {
+              message: `Worker: Failed to initialize AudioEncoder: ${e.message}`,
+              type: "initialization-failed" /* InitializationFailed */,
+              stack: e.stack
+            }
+          });
+          this.cleanup();
+          return;
+        }
+      }
+      this.postMessageToMainThread({
+        type: "initialized",
+        actualVideoCodec: finalVideoEncoderConfig?.codec,
+        actualAudioCodec: audioDisabled ? null : finalAudioEncoderConfig?.codec
+      });
+      console.warn("Worker: Initialized successfully");
+    }
+    async handleAddVideoFrame(data) {
+      if (this.isCancelled || !this.videoEncoder || !this.currentConfig) return;
+      try {
+        const frame = data.frame;
+        const currentQueueSize = this.videoEncoder.encodeQueueSize;
+        const maxQueueSize = this.currentConfig.maxVideoQueueSize || 30;
+        const strategy = this.currentConfig.backpressureStrategy || "drop";
+        if (currentQueueSize >= maxQueueSize) {
+          if (strategy === "drop") {
+            console.warn(
+              `Video queue full (${currentQueueSize}/${maxQueueSize}), dropping frame`
+            );
+            try {
+              frame.close();
+            } catch (closeErr) {
+              console.warn(
+                "Worker: Ignored error closing dropped VideoFrame",
+                closeErr
+              );
+            }
+            return;
+          } else if (strategy === "wait") {
+            let waitTime = 10;
+            const maxWaitTime = 100;
+            const maxRetries = 5;
+            let attempts = 0;
+            while (this.videoEncoder.encodeQueueSize >= maxQueueSize && attempts < maxRetries) {
+              await new Promise((resolve) => setTimeout(resolve, waitTime));
+              waitTime = Math.min(waitTime * 1.5, maxWaitTime);
+              attempts++;
+            }
+            if (this.videoEncoder.encodeQueueSize >= maxQueueSize) {
+              console.warn(
+                `Video queue still full after waiting, dropping frame`
+              );
+              try {
+                frame.close();
+              } catch (closeErr) {
+                console.warn(
+                  "Worker: Ignored error closing waited VideoFrame",
+                  closeErr
+                );
+              }
+              return;
+            }
+          }
+        }
+        const interval = this.currentConfig.keyFrameInterval;
+        const opts = interval && this.videoFrameCount % interval === 0 ? { keyFrame: true } : void 0;
+        this.videoEncoder.encode(frame, opts);
+        try {
+          frame.close();
+        } catch (closeErr) {
+          console.warn("Worker: Ignored error closing VideoFrame", closeErr);
+        }
+        this.videoFrameCount++;
+        this.processedFrames++;
+        const progressMessage = {
+          type: "progress",
+          processedFrames: this.processedFrames
+        };
+        if (typeof this.totalFramesToProcess !== "undefined") {
+          progressMessage.totalFrames = this.totalFramesToProcess;
+        }
+        this.postMessageToMainThread(progressMessage);
+        this.postQueueSize();
+      } catch (error) {
+        this.postMessageToMainThread({
+          type: "error",
+          errorDetail: {
+            message: `Error encoding video frame: ${error.message}`,
+            type: "video-encoding-error" /* VideoEncodingError */,
+            stack: error.stack
+          }
+        });
+        this.cleanup();
+      }
+    }
+    async handleAddAudioData(data) {
+      if (this.isCancelled || !this.audioEncoder || !this.currentConfig) return;
+      if (data.audio) {
+        const audioData = data.audio;
+        let audioClosed = false;
+        const closeAudioData = (context) => {
+          if (audioClosed) return;
+          try {
+            audioData.close();
+            audioClosed = true;
+          } catch (closeErr) {
+            console.warn(`Worker: Ignored error closing ${context}`, closeErr);
+          }
+        };
+        try {
+          const currentQueueSize = this.audioEncoder.encodeQueueSize;
+          const maxQueueSize = this.currentConfig.maxAudioQueueSize || 30;
+          const strategy = this.currentConfig.backpressureStrategy || "drop";
+          if (currentQueueSize >= maxQueueSize) {
+            if (strategy === "drop") {
+              console.warn(
+                `Audio queue full (${currentQueueSize}/${maxQueueSize}), dropping audio data`
+              );
+              closeAudioData("dropped AudioData");
+              return;
+            } else if (strategy === "wait") {
+              let waitTime = 10;
+              const maxWaitTime = 100;
+              const maxRetries = 5;
+              let attempts = 0;
+              while (this.audioEncoder.encodeQueueSize >= maxQueueSize && attempts < maxRetries) {
+                await new Promise((resolve) => setTimeout(resolve, waitTime));
+                waitTime = Math.min(waitTime * 1.5, maxWaitTime);
+                attempts++;
+              }
+              if (this.audioEncoder.encodeQueueSize >= maxQueueSize) {
+                console.warn(
+                  `Audio queue still full after waiting, dropping audio data`
+                );
+                closeAudioData("waited AudioData");
+                return;
+              }
+            }
+          }
+          this.audioEncoder.encode(audioData);
+          this.postQueueSize();
+        } catch (error) {
+          this.postMessageToMainThread({
+            type: "error",
+            errorDetail: {
+              message: `Error encoding audio data: ${error.message}`,
+              type: "audio-encoding-error" /* AudioEncodingError */,
+              stack: error.stack
+            }
+          });
+          this.cleanup();
+        } finally {
+          closeAudioData("AudioData");
+        }
+        return;
+      }
+      if (!data.audioData || data.audioData.length === 0) return;
+      if (data.audioData.length !== this.currentConfig.channels) {
+        this.postMessageToMainThread({
+          type: "error",
+          errorDetail: {
+            message: `Audio data channel count (${data.audioData.length}) does not match configured channels (${this.currentConfig.channels}).`,
+            type: "configuration-error" /* ConfigurationError */
+          }
+        });
+        return;
+      }
+      const AudioDataCtor = getAudioData();
+      if (!AudioDataCtor) {
+        this.postMessageToMainThread({
+          type: "error",
+          errorDetail: {
+            message: "Worker: AudioData not available",
+            type: "not-supported" /* NotSupported */
+          }
+        });
+        this.cleanup();
+        return;
+      }
+      try {
+        const interleaveFloat32Arrays = (planarArrays) => {
+          if (!planarArrays || planarArrays.length === 0) {
+            return new Float32Array(0);
+          }
+          const numChannels = planarArrays.length;
+          const numFrames = Math.min(...planarArrays.map((arr) => arr.length));
+          const interleaved = new Float32Array(numFrames * numChannels);
+          for (let i = 0; i < numFrames; i++) {
+            for (let ch = 0; ch < numChannels; ch++) {
+              interleaved[i * numChannels + ch] = planarArrays[ch][i];
+            }
+          }
+          return interleaved;
+        };
+        const interleavedData = interleaveFloat32Arrays(data.audioData);
+        const audioData = new AudioDataCtor({
+          format: "f32",
+          sampleRate: data.sampleRate,
+          numberOfFrames: data.numberOfFrames,
+          numberOfChannels: data.numberOfChannels,
+          timestamp: data.timestamp,
+          data: interleavedData.buffer
+        });
+        try {
+          this.audioEncoder.encode(audioData);
+          this.postQueueSize();
+        } finally {
+          audioData.close();
+        }
+      } catch (error) {
+        this.postMessageToMainThread({
+          type: "error",
+          errorDetail: {
+            message: `Error encoding audio data: ${error.message}`,
+            type: "audio-encoding-error" /* AudioEncodingError */,
+            stack: error.stack
+          }
+        });
+        this.cleanup();
+      }
+    }
+    async handleFinalize(_message) {
+      if (this.isCancelled) return;
+      try {
+        if (this.videoEncoder) await this.videoEncoder.flush();
+        if (this.audioEncoder) await this.audioEncoder.flush();
+        if (this.muxer) {
+          const uint8ArrayOrNullOutput = this.muxer.finalize();
+          if (uint8ArrayOrNullOutput) {
+            this.postMessageToMainThread(
+              { type: "finalized", output: uint8ArrayOrNullOutput },
+              [uint8ArrayOrNullOutput.buffer]
+            );
+          } else if (this.currentConfig?.latencyMode === "realtime") {
+            this.postMessageToMainThread({ type: "finalized", output: null });
+          } else {
+            this.postMessageToMainThread({
+              type: "error",
+              errorDetail: {
+                message: "Muxer finalized without output in non-realtime mode.",
+                type: "muxing-failed" /* MuxingFailed */
+              }
+            });
+          }
+        } else {
+          this.postMessageToMainThread({
+            type: "error",
+            errorDetail: {
+              message: "Muxer not initialized during finalize.",
+              type: "muxing-failed" /* MuxingFailed */
+            }
+          });
+        }
+      } catch (error) {
+        this.postMessageToMainThread({
+          type: "error",
+          errorDetail: {
+            message: `Error during finalization: ${error.message}`,
+            type: "muxing-failed" /* MuxingFailed */,
+            stack: error.stack
+          }
+        });
+      } finally {
+        this.cleanup();
+      }
+    }
+    handleCancel(_message) {
+      if (this.isCancelled) return;
+      this.isCancelled = true;
+      console.warn("Worker: Received cancel signal.");
+      this.postMessageToMainThread({ type: "cancelled" });
+      this.videoEncoder?.close();
+      this.audioEncoder?.close();
+      this.cleanup();
+    }
+    cleanup() {
+      console.warn("Worker: Cleaning up resources.");
+      if (this.videoEncoder && this.videoEncoder.state !== "closed")
+        this.videoEncoder.close();
+      if (this.audioEncoder && this.audioEncoder.state !== "closed")
+        this.audioEncoder.close();
+      this.videoEncoder = null;
+      this.audioEncoder = null;
+      this.muxer = null;
+      this.currentConfig = null;
+      this.totalFramesToProcess = void 0;
+      this.processedFrames = 0;
+      this.videoFrameCount = 0;
+    }
+    async handleMessage(eventData) {
+      if (this.isCancelled && eventData.type !== "initialize" && eventData.type !== "cancel") {
+        console.warn(
+          `Worker: Ignoring message type '${eventData.type}' because worker is cancelled.`
+        );
+        return;
+      }
+      try {
+        switch (eventData.type) {
+          case "initialize":
+            this.isCancelled = false;
+            this.cleanup();
+            await this.initializeEncoders(eventData);
+            break;
+          case "addVideoFrame":
+            await this.handleAddVideoFrame(eventData);
+            break;
+          case "addAudioData":
+            await this.handleAddAudioData(eventData);
+            break;
+          case "finalize":
+            await this.handleFinalize(eventData);
+            break;
+          case "cancel":
+            this.handleCancel(eventData);
+            break;
+          default:
+            console.warn(
+              "Worker received unknown message type:",
+              eventData.type
+            );
+        }
+      } catch (error) {
+        this.postMessageToMainThread({
+          type: "error",
+          errorDetail: {
+            message: `Unhandled error in worker onmessage: ${error.message}`,
+            type: "unknown" /* Unknown */,
+            stack: error.stack
+          }
+        });
+        this.cleanup();
+      }
+    }
+  };
+  var encoder = new EncoderWorker();
+  self.onmessage = async (event) => {
+    await encoder.handleMessage(event.data);
+  };
+  var DEFAULT_AUDIO_CODEC_BY_CONTAINER = {
+    mp4: "aac",
+    webm: "opus"
+  };
+  var AUDIO_ENCODER_CODEC_MAP = {
+    aac: "mp4a.40.2",
+    opus: "opus",
+    flac: "flac",
+    mp3: "mp3",
+    vorbis: "vorbis",
+    pcm: "pcm",
+    ulaw: "ulaw",
+    alaw: "alaw"
+  };
+  var MUXER_COMPATIBLE_AUDIO = {
+    mp4: /* @__PURE__ */ new Set(["aac", "mp3"]),
+    webm: /* @__PURE__ */ new Set(["opus", "vorbis", "flac"])
+  };
+  function getAudioEncoderCodecStringFromAudioCodec(codec) {
+    return AUDIO_ENCODER_CODEC_MAP[codec] ?? codec;
+  }
+  function getVideoEncoderConfigOverridesForCodec(codec, overrides) {
+    if (!overrides) {
+      return {};
+    }
+    const sanitized = { ...overrides };
+    if (codec !== "avc") {
+      delete sanitized.avc;
+    }
+    if (codec !== "hevc") {
+      delete sanitized.hevc;
+    }
+    return sanitized;
+  }
+  function getAudioEncoderConfigOverridesForCodec(codec, overrides) {
+    if (!overrides) {
+      return {};
+    }
+    const sanitized = { ...overrides };
+    if (codec !== "aac") {
+      delete sanitized.aac;
+    }
+    return sanitized;
+  }
+  function getContainerType(container) {
+    return container === "webm" ? "webm" : "mp4";
+  }
+  function buildAudioCodecPreference(container, requested) {
+    const preference = [];
+    const addCodec = (codec) => {
+      if (!preference.includes(codec)) {
+        preference.push(codec);
+      }
+    };
+    if (requested) {
+      addCodec(requested);
+    }
+    addCodec(DEFAULT_AUDIO_CODEC_BY_CONTAINER[container]);
+    for (const codec of MUXER_COMPATIBLE_AUDIO[container]) {
+      addCodec(codec);
+    }
+    if (container === "mp4") {
+      addCodec("aac");
+      addCodec("mp3");
+    } else {
+      addCodec("opus");
+      addCodec("vorbis");
+      addCodec("flac");
+      addCodec("aac");
+    }
+    return preference;
+  }
+  function isAudioCodecMuxerCompatible(container, codec) {
+    return MUXER_COMPATIBLE_AUDIO[container].has(codec);
+  }
+})();
+//# sourceMappingURL=worker.js.map

+ 100 - 41
src/hooks/useImport.ts

@@ -11,6 +11,7 @@ import useSlideHandler from '@/hooks/useSlideHandler'
 import useHistorySnapshot from './useHistorySnapshot'
 import useHistorySnapshot from './useHistorySnapshot'
 import message from '@/utils/message'
 import message from '@/utils/message'
 import { getSvgPathRange } from '@/utils/svgPathParser'
 import { getSvgPathRange } from '@/utils/svgPathParser'
+import { encode, canEncode } from 'webcodecs-encoder';
 import type {
 import type {
   Slide,
   Slide,
   TableCellStyle,
   TableCellStyle,
@@ -28,7 +29,8 @@ import type {
   Gradient,
   Gradient,
 } from '@/types/slides'
 } from '@/types/slides'
 
 
-const convertFontSizePtToPx = (html: string, ratio: number) => {
+const convertFontSizePtToPx = (html: string, ratio: number, autoFit: any) => {
+  if (autoFit?.fontScale && autoFit?.type == "text") { ratio = ratio * autoFit.fontScale / 100; }
   // return html;
   // return html;
   return html.replace(/\s*([\d.]+)pt/g, (match, p1) => {
   return html.replace(/\s*([\d.]+)pt/g, (match, p1) => {
     return `${(parseFloat(p1) * ratio - 1) | 0}px `
     return `${(parseFloat(p1) * ratio - 1) | 0}px `
@@ -403,6 +405,7 @@ export default () => {
   /**
   /**
    * 将 base64 字符串或 Blob 转换为 File 对象
    * 将 base64 字符串或 Blob 转换为 File 对象
    */
    */
+  
   const dataToFile = async (data: string | Blob, filename: string, videoMimeType: string): File => {
   const dataToFile = async (data: string | Blob, filename: string, videoMimeType: string): File => {
     if (typeof data === 'string') {
     if (typeof data === 'string') {
       // 1. 通过 fetch 获取 Blob 数据
       // 1. 通过 fetch 获取 Blob 数据
@@ -422,6 +425,62 @@ export default () => {
     }
     }
     throw new Error('Unsupported data type')
     throw new Error('Unsupported data type')
   }
   }
+  
+/*
+  // 你原有的 dataToFile 函数保持不变
+  const dataToFile = async (data: string | Blob, filename: string, videoMimeType: string): Promise<File> => {
+    if (typeof data === 'string') {
+      const response = await fetch(data);
+      if (!response.ok) {
+        throw new Error(`Failed to fetch blob: ${response.statusText}`);
+      }
+      const blob = await response.blob();
+      const mime = videoMimeType || blob.type;
+      return new File([blob], filename, { type: mime });
+    } else if (data instanceof Blob) {
+      return new File([data], filename, { type: data.type });
+    }
+    throw new Error('Unsupported data type');
+  };
+
+
+  const convertVideoToMP4 = async (
+    videoSource: string | Blob,
+    outputFilename: string = `video_${Date.now()}.mp4`
+  ): Promise<File> => {
+    // 1. 检查浏览器支持
+    const supported = await canEncode();
+    if (!supported) {
+      throw new Error('当前浏览器不支持 WebCodecs,请使用最新 Chrome/Edge 并确保 HTTPS');
+    }
+
+    // 2. 转为 File
+    const inputFile = await dataToFile(videoSource, 'input.mp4', 'video/mp4');
+
+    // 3. 创建 VideoFile 对象(webcodecs-encoder 的输入包装)
+    //const videoFile = new VideoFile(inputFile);
+    const videoFile = {
+      file: inputFile,
+      type: 'video/mp4'
+    };
+    // 4. 执行编码
+    const encodedData = await encode(videoFile, {
+      quality: 'high',
+      video: {
+        codec: 'av1',
+        bitrate: 2_000_000,
+        hardwareAcceleration: 'prefer-hardware'
+      },
+      audio: false,           // 显式禁用音频编码
+      container: 'mp4',
+      onProgress: (progress) => console.log(progress)
+    });
+
+    // 5. 返回 File
+    return new File([encodedData], outputFilename, { type: 'video/mp4' });
+  };
+*/
+
 
 
   /*
   /*
   const makeWhiteTransparent = async (
   const makeWhiteTransparent = async (
@@ -514,14 +573,14 @@ export default () => {
     options?: { tolerance?: number }
     options?: { tolerance?: number }
   ): Promise<File> => {
   ): Promise<File> => {
     const tolerance = options?.tolerance ?? 15
     const tolerance = options?.tolerance ?? 15
-  
+
     // ----- 辅助函数:将输入统一转换为 { blob, mime } -----
     // ----- 辅助函数:将输入统一转换为 { blob, mime } -----
     async function getBlobAndMime(input: string | Blob): Promise<{ blob: Blob; mime: string }> {
     async function getBlobAndMime(input: string | Blob): Promise<{ blob: Blob; mime: string }> {
       // 1. 已经是 Blob
       // 1. 已经是 Blob
       if (input instanceof Blob) {
       if (input instanceof Blob) {
         return { blob: input, mime: input.type }
         return { blob: input, mime: input.type }
       }
       }
-  
+
       // 2. 处理字符串
       // 2. 处理字符串
       if (input.startsWith('data:')) {
       if (input.startsWith('data:')) {
         // data URL → 通过 fetch 获取 Blob(自动获得正确的 MIME 类型)
         // data URL → 通过 fetch 获取 Blob(自动获得正确的 MIME 类型)
@@ -540,20 +599,20 @@ export default () => {
         return { blob, mime: 'image/png' }
         return { blob, mime: 'image/png' }
       }
       }
     }
     }
-  
+
     // 获取统一的 blob 和实际 MIME 类型
     // 获取统一的 blob 和实际 MIME 类型
     const { blob, mime } = await getBlobAndMime(data)
     const { blob, mime } = await getBlobAndMime(data)
-  
+
     // ----- 非 PNG 格式:直接返回原始文件(不处理透明)-----
     // ----- 非 PNG 格式:直接返回原始文件(不处理透明)-----
     if (mime !== 'image/png') {
     if (mime !== 'image/png') {
       return new File([blob], filename, { type: mime })
       return new File([blob], filename, { type: mime })
     }
     }
-  
+
     // ----- PNG 格式:执行白色变透明处理 -----
     // ----- PNG 格式:执行白色变透明处理 -----
     // 1. 创建对象 URL 用于加载图片
     // 1. 创建对象 URL 用于加载图片
     const imageUrl = URL.createObjectURL(blob)
     const imageUrl = URL.createObjectURL(blob)
     let needRevoke = true
     let needRevoke = true
-  
+
     // 2. 加载图像
     // 2. 加载图像
     const img = await new Promise<HTMLImageElement>((resolve, reject) => {
     const img = await new Promise<HTMLImageElement>((resolve, reject) => {
       const image = new Image()
       const image = new Image()
@@ -562,40 +621,40 @@ export default () => {
       // Blob URL 不需要设置 crossOrigin
       // Blob URL 不需要设置 crossOrigin
       image.src = imageUrl
       image.src = imageUrl
     })
     })
-  
+
     const canvas = document.createElement('canvas')
     const canvas = document.createElement('canvas')
     try {
     try {
       canvas.width = img.width
       canvas.width = img.width
       canvas.height = img.height
       canvas.height = img.height
       const ctx = canvas.getContext('2d')!
       const ctx = canvas.getContext('2d')!
       ctx.drawImage(img, 0, 0)
       ctx.drawImage(img, 0, 0)
-  
+
       // 3. 获取像素数据,将接近白色的像素设为透明
       // 3. 获取像素数据,将接近白色的像素设为透明
       const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
       const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
       const dataArray = imageData.data
       const dataArray = imageData.data
-  
+
       for (let i = 0; i < dataArray.length; i += 4) {
       for (let i = 0; i < dataArray.length; i += 4) {
         const r = dataArray[i]
         const r = dataArray[i]
         const g = dataArray[i + 1]
         const g = dataArray[i + 1]
         const b = dataArray[i + 2]
         const b = dataArray[i + 2]
-  
+
         const dr = r - 255
         const dr = r - 255
         const dg = g - 255
         const dg = g - 255
         const db = b - 255
         const db = b - 255
         const dist = Math.sqrt(dr * dr + dg * dg + db * db)
         const dist = Math.sqrt(dr * dr + dg * dg + db * db)
-  
+
         if (dist <= tolerance) {
         if (dist <= tolerance) {
           dataArray[i + 3] = 0 // 完全透明
           dataArray[i + 3] = 0 // 完全透明
         }
         }
       }
       }
-  
+
       ctx.putImageData(imageData, 0, 0)
       ctx.putImageData(imageData, 0, 0)
     } finally {
     } finally {
       if (needRevoke) {
       if (needRevoke) {
         URL.revokeObjectURL(imageUrl)
         URL.revokeObjectURL(imageUrl)
       }
       }
     }
     }
-  
+
     // 4. 导出为 PNG Blob
     // 4. 导出为 PNG Blob
     const outputBlob = await new Promise<Blob>((resolve, reject) => {
     const outputBlob = await new Promise<Blob>((resolve, reject) => {
       canvas.toBlob((blob) => {
       canvas.toBlob((blob) => {
@@ -603,7 +662,7 @@ export default () => {
         else reject(new Error('Canvas toBlob failed'))
         else reject(new Error('Canvas toBlob failed'))
       }, 'image/png')
       }, 'image/png')
     })
     })
-  
+
     return new File([outputBlob], filename, { type: 'image/png' })
     return new File([outputBlob], filename, { type: 'image/png' })
   }
   }
 
 
@@ -1337,8 +1396,8 @@ export default () => {
                 rotate: el.rotate,
                 rotate: el.rotate,
                 defaultFontName: theme.value.fontName,
                 defaultFontName: theme.value.fontName,
                 defaultColor: theme.value.fontColor,
                 defaultColor: theme.value.fontColor,
-                content: convertFontSizePtToPx(el.content, ratio),
-                style: getStyle(convertFontSizePtToPx(el.content, ratio)),
+                content: convertFontSizePtToPx(el.content, ratio, el.autoFit),
+                style: getStyle(convertFontSizePtToPx(el.content, ratio, el.autoFit)),
                 lineHeight: 1.15,
                 lineHeight: 1.15,
                 outline: {
                 outline: {
                   color: el.borderColor,
                   color: el.borderColor,
@@ -1400,27 +1459,27 @@ export default () => {
                   range: [[0, 0], [100, 100]],
                   range: [[0, 0], [100, 100]],
                 }
                 }
               }
               }
-
-              // 如果 src 是 base64,触发上传
-              if (el.src && typeof el.src === 'string' && el.src.startsWith('data:')) {
-                const uploadTask = (async () => {
-                  try {
-                    const file = await makeWhiteTransparent(el.src, `image_${Date.now()}.png`)
-                    if (file) {
-                      const url = await uploadFileToS3(file)
-                      element.src = url // 替换为远程 URL
-                      const slidesStore = useSlidesStore()
-                      slidesStore.updateElement({ id: element.id, props: { src: url } })
-                    }
-                  }
-                  catch (error) {
-                    console.error('Image upload failed:', error)
-                    // 失败时保留原 base64(或可置空)
-                  }
-                })()
-                uploadTasks.push(uploadTask)
-              }
-
+              /*
+                            // 如果 src 是 base64,触发上传
+                            if (el.src && typeof el.src === 'string' && el.src.startsWith('data:')) {
+                              const uploadTask = (async () => {
+                                try {
+                                  const file = await makeWhiteTransparent(el.src, `image_${Date.now()}.png`)
+                                  if (file) {
+                                    const url = await uploadFileToS3(file)
+                                    element.src = url // 替换为远程 URL
+                                    const slidesStore = useSlidesStore()
+                                    slidesStore.updateElement({ id: element.id, props: { src: url } })
+                                  }
+                                }
+                                catch (error) {
+                                  console.error('Image upload failed:', error)
+                                  // 失败时保留原 base64(或可置空)
+                                }
+                              })()
+                              uploadTasks.push(uploadTask)
+                            }
+              */
               slide.elements.push(element)
               slide.elements.push(element)
             }
             }
             else if (el.type === 'math') {
             else if (el.type === 'math') {
@@ -1517,7 +1576,7 @@ export default () => {
               if (localData) {
               if (localData) {
                 const uploadTask = (async () => {
                 const uploadTask = (async () => {
                   try {
                   try {
-                    const file = await dataToFile(localData, `video_${Date.now()}.mp4`, 'video/mp4')
+                    const file = await convertVideoToMP4(localData, `video_${Date.now()}.mp4`)
                     if (file) {
                     if (file) {
                       const url = await uploadFileToS3(file)
                       const url = await uploadFileToS3(file)
                       element.src = url
                       element.src = url
@@ -1565,7 +1624,7 @@ export default () => {
 
 
                 const pattern: string | undefined = el.fill?.type === 'image' ? el.fill.value.picBase64 : undefined
                 const pattern: string | undefined = el.fill?.type === 'image' ? el.fill.value.picBase64 : undefined
                 const fill = el.fill?.type === 'color' ? el.fill.value : ''
                 const fill = el.fill?.type === 'color' ? el.fill.value : ''
-                const style = getStyle(convertFontSizePtToPx(el.content, ratio)) + (el.pathBBox.pWidth ? ';width:' + (el.pathBBox.pWidth * ratio) + 'px;height:' + (el.pathBBox.pHeight * ratio) + 'px;' : '') // 设置字体的样式等,这里由于不支持的样式在里面会过滤
+                const style = getStyle(convertFontSizePtToPx(el.content, ratio, el.autoFit)) + (el.pathBBox.pWidth ? ';width:' + (el.pathBBox.pWidth * ratio) + 'px;height:' + (el.pathBBox.pHeight * ratio) + 'px;' : '') // 设置字体的样式等,这里由于不支持的样式在里面会过滤
                 const element: PPTShapeElement = {
                 const element: PPTShapeElement = {
                   type: 'shape',
                   type: 'shape',
                   id: nanoid(10),
                   id: nanoid(10),
@@ -1587,7 +1646,7 @@ export default () => {
                     style: el.borderType,
                     style: el.borderType,
                   },
                   },
                   text: {
                   text: {
-                    content: convertFontSizePtToPx(el.content, ratio),
+                    content: convertFontSizePtToPx(el.content, ratio, el.autoFit),
                     style: style,
                     style: style,
                     defaultFontName: theme.value.fontName,
                     defaultFontName: theme.value.fontName,
                     defaultColor: theme.value.fontColor,
                     defaultColor: theme.value.fontColor,

Some files were not shown because too many files changed in this diff