@meframe/core 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/Meframe.d.ts +7 -1
  2. package/dist/Meframe.d.ts.map +1 -1
  3. package/dist/Meframe.js +7 -27
  4. package/dist/Meframe.js.map +1 -1
  5. package/dist/cache/CacheManager.d.ts.map +1 -1
  6. package/dist/cache/CacheManager.js +9 -2
  7. package/dist/cache/CacheManager.js.map +1 -1
  8. package/dist/cache/storage/opfs/OPFSManager.d.ts.map +1 -1
  9. package/dist/cache/storage/opfs/OPFSManager.js +11 -7
  10. package/dist/cache/storage/opfs/OPFSManager.js.map +1 -1
  11. package/dist/controllers/ExportController.d.ts +7 -1
  12. package/dist/controllers/ExportController.d.ts.map +1 -1
  13. package/dist/controllers/ExportController.js.map +1 -1
  14. package/dist/controllers/PlaybackController.d.ts.map +1 -1
  15. package/dist/controllers/PlaybackController.js +6 -0
  16. package/dist/controllers/PlaybackController.js.map +1 -1
  17. package/dist/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +1 -0
  18. package/dist/{medeo-fe/node_modules → node_modules}/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js +2 -2
  19. package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +1 -0
  20. package/dist/orchestrator/AudioExportSession.d.ts +3 -1
  21. package/dist/orchestrator/AudioExportSession.d.ts.map +1 -1
  22. package/dist/orchestrator/AudioExportSession.js +28 -20
  23. package/dist/orchestrator/AudioExportSession.js.map +1 -1
  24. package/dist/orchestrator/ExportScheduler.d.ts +5 -1
  25. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
  26. package/dist/orchestrator/ExportScheduler.js +20 -2
  27. package/dist/orchestrator/ExportScheduler.js.map +1 -1
  28. package/dist/orchestrator/OnDemandVideoSession.d.ts +1 -0
  29. package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
  30. package/dist/orchestrator/OnDemandVideoSession.js +28 -17
  31. package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
  32. package/dist/orchestrator/Orchestrator.d.ts +1 -1
  33. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  34. package/dist/orchestrator/Orchestrator.js.map +1 -1
  35. package/dist/orchestrator/types.d.ts +1 -1
  36. package/dist/orchestrator/types.d.ts.map +1 -1
  37. package/dist/stages/compose/FrameRateConverter.d.ts +6 -2
  38. package/dist/stages/compose/FrameRateConverter.d.ts.map +1 -1
  39. package/dist/stages/compose/OfflineAudioMixer.d.ts.map +1 -1
  40. package/dist/stages/compose/OfflineAudioMixer.js +8 -0
  41. package/dist/stages/compose/OfflineAudioMixer.js.map +1 -1
  42. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  43. package/dist/stages/load/ResourceLoader.js +34 -8
  44. package/dist/stages/load/ResourceLoader.js.map +1 -1
  45. package/dist/stages/mux/MP4Muxer.d.ts +11 -2
  46. package/dist/stages/mux/MP4Muxer.d.ts.map +1 -1
  47. package/dist/stages/mux/MP4Muxer.js +32 -6
  48. package/dist/stages/mux/MP4Muxer.js.map +1 -1
  49. package/dist/stages/mux/MuxManager.d.ts +5 -1
  50. package/dist/stages/mux/MuxManager.d.ts.map +1 -1
  51. package/dist/stages/mux/MuxManager.js +15 -7
  52. package/dist/stages/mux/MuxManager.js.map +1 -1
  53. package/dist/types.d.ts +8 -0
  54. package/dist/types.d.ts.map +1 -1
  55. package/dist/utils/errors.d.ts +1 -0
  56. package/dist/utils/errors.d.ts.map +1 -1
  57. package/dist/utils/errors.js +5 -1
  58. package/dist/utils/errors.js.map +1 -1
  59. package/dist/utils/mp4box.js +1 -1
  60. package/dist/workers/MP4Demuxer.DfWiwyjB.js.map +1 -1
  61. package/dist/workers/stages/compose/{video-compose.worker.CA2_Kpg-.js → video-compose.worker.KMZjuJuY.js} +47 -4
  62. package/dist/workers/stages/compose/video-compose.worker.KMZjuJuY.js.map +1 -0
  63. package/dist/workers/worker-manifest.json +1 -1
  64. package/package.json +1 -1
  65. package/dist/medeo-fe/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +0 -1
  66. package/dist/medeo-fe/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +0 -1
  67. package/dist/workers/stages/compose/video-compose.worker.CA2_Kpg-.js.map +0 -1
  68. /package/dist/{medeo-fe/node_modules → node_modules}/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js +0 -0
@@ -1,3 +1,11 @@
1
+ export type MuxOutputConfig = {
2
+ kind: 'blob';
3
+ } | {
4
+ kind: 'stream';
5
+ onData: (data: Uint8Array, position: number) => void;
6
+ chunked?: boolean;
7
+ chunkSize?: number;
8
+ };
1
9
  /**
2
10
  * MP4Muxer - MP4 container multiplexer using mp4-muxer library
3
11
  * Supports video and audio track export
@@ -8,6 +16,7 @@ export declare class MP4Muxer {
8
16
  private firstAudioChunk;
9
17
  private videoChunkMeta;
10
18
  private audioChunkMeta;
19
+ private lastAudioDurationUs;
11
20
  constructor(config: {
12
21
  width: number;
13
22
  height: number;
@@ -15,11 +24,11 @@ export declare class MP4Muxer {
15
24
  fastStart?: false | 'in-memory' | 'fragmented';
16
25
  videoChunkMeta?: any;
17
26
  audioChunkMeta?: any;
18
- });
27
+ }, output?: MuxOutputConfig);
19
28
  private videoChunkCount;
20
29
  writeVideoChunk(chunk: EncodedVideoChunk, metadata?: EncodedVideoChunkMetadata): void;
21
30
  private audioChunkCount;
22
31
  writeAudioChunk(chunk: EncodedAudioChunk, metadata?: EncodedAudioChunkMetadata): void;
23
- finalize(): Blob;
32
+ finalize(): Blob | null;
24
33
  }
25
34
  //# sourceMappingURL=MP4Muxer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MP4Muxer.d.ts","sourceRoot":"","sources":["../../../src/stages/mux/MP4Muxer.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,cAAc,CAAa;gBAEvB,MAAM,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,KAAK,GAAG,WAAW,GAAG,YAAY,CAAC;QAC/C,cAAc,CAAC,EAAE,GAAG,CAAC;QACrB,cAAc,CAAC,EAAE,GAAG,CAAC;KACtB;IA2CD,OAAO,CAAC,eAAe,CAAK;IAE5B,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,yBAAyB,GAAG,IAAI;IAwBrF,OAAO,CAAC,eAAe,CAAK;IAE5B,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,yBAAyB,GAAG,IAAI;IA0BrF,QAAQ,IAAI,IAAI;CAKjB"}
1
+ {"version":3,"file":"MP4Muxer.d.ts","sourceRoot":"","sources":["../../../src/stages/mux/MP4Muxer.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEN;;;GAGG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,mBAAmB,CAAuB;gBAGhD,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,KAAK,GAAG,WAAW,GAAG,YAAY,CAAC;QAC/C,cAAc,CAAC,EAAE,GAAG,CAAC;QACrB,cAAc,CAAC,EAAE,GAAG,CAAC;KACtB,EACD,MAAM,GAAE,eAAkC;IAqD5C,OAAO,CAAC,eAAe,CAAK;IAE5B,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,yBAAyB,GAAG,IAAI;IAwBrF,OAAO,CAAC,eAAe,CAAK;IAE5B,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,yBAAyB,GAAG,IAAI;IAmDrF,QAAQ,IAAI,IAAI,GAAG,IAAI;CASxB"}
@@ -1,15 +1,22 @@
1
- import { ArrayBufferTarget, Muxer } from "../../medeo-fe/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js";
1
+ import { StreamTarget, ArrayBufferTarget, Muxer } from "../../node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js";
2
2
  class MP4Muxer {
3
3
  muxer;
4
4
  firstVideoChunk = true;
5
5
  firstAudioChunk = true;
6
6
  videoChunkMeta = null;
7
7
  audioChunkMeta = null;
8
- constructor(config) {
8
+ lastAudioDurationUs = null;
9
+ constructor(config, output = { kind: "blob" }) {
9
10
  this.videoChunkMeta = config.videoChunkMeta;
10
11
  this.audioChunkMeta = config.audioChunkMeta;
12
+ const target = output.kind === "stream" ? new StreamTarget({
13
+ onData: output.onData,
14
+ chunked: output.chunked ?? true,
15
+ chunkSize: output.chunkSize ?? 16 * 1024 * 1024
16
+ // 16 MiB default to reduce writes
17
+ }) : new ArrayBufferTarget();
11
18
  const muxerConfig = {
12
- target: new ArrayBufferTarget(),
19
+ target,
13
20
  video: {
14
21
  codec: "avc",
15
22
  width: config.width,
@@ -46,7 +53,7 @@ class MP4Muxer {
46
53
  }
47
54
  audioChunkCount = 0;
48
55
  writeAudioChunk(chunk, metadata) {
49
- if (chunk.byteLength <= 16) {
56
+ if (chunk.byteLength <= 16 && (!chunk.duration || chunk.duration <= 0)) {
50
57
  return;
51
58
  }
52
59
  let meta;
@@ -59,13 +66,32 @@ class MP4Muxer {
59
66
  }
60
67
  this.firstAudioChunk = false;
61
68
  }
69
+ const hasValidDuration = !!chunk.duration && chunk.duration > 0;
70
+ if (!hasValidDuration) {
71
+ const sampleRate = metadata?.decoderConfig?.sampleRate ?? this.audioChunkMeta?.sampleRate ?? 48e3;
72
+ const inferredDurationUs = this.lastAudioDurationUs ?? Math.max(1, Math.round(1024 / sampleRate * 1e6));
73
+ const buffer = new ArrayBuffer(chunk.byteLength);
74
+ chunk.copyTo(buffer);
75
+ chunk = new EncodedAudioChunk({
76
+ type: chunk.type,
77
+ timestamp: chunk.timestamp,
78
+ duration: inferredDurationUs,
79
+ data: buffer
80
+ });
81
+ this.lastAudioDurationUs = inferredDurationUs;
82
+ } else {
83
+ this.lastAudioDurationUs = chunk.duration;
84
+ }
62
85
  this.audioChunkCount++;
63
86
  this.muxer.addAudioChunk(chunk, meta);
64
87
  }
65
88
  finalize() {
66
89
  this.muxer.finalize();
67
- const buffer = this.muxer.target.buffer;
68
- return new Blob([buffer], { type: "video/mp4" });
90
+ if (this.muxer.target instanceof ArrayBufferTarget) {
91
+ const buffer = this.muxer.target.buffer;
92
+ return new Blob([buffer], { type: "video/mp4" });
93
+ }
94
+ return null;
69
95
  }
70
96
  }
71
97
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"MP4Muxer.js","sources":["../../../src/stages/mux/MP4Muxer.ts"],"sourcesContent":["import { Muxer, ArrayBufferTarget } from 'mp4-muxer';\n\n/**\n * MP4Muxer - MP4 container multiplexer using mp4-muxer library\n * Supports video and audio track export\n */\nexport class MP4Muxer {\n private muxer: Muxer<ArrayBufferTarget>;\n private firstVideoChunk = true;\n private firstAudioChunk = true;\n private videoChunkMeta: any = null;\n private audioChunkMeta: any = null;\n\n constructor(config: {\n width: number;\n height: number;\n fps: number;\n fastStart?: false | 'in-memory' | 'fragmented';\n videoChunkMeta?: any;\n audioChunkMeta?: any;\n }) {\n this.videoChunkMeta = config.videoChunkMeta;\n this.audioChunkMeta = config.audioChunkMeta;\n\n const muxerConfig: any = {\n target: new ArrayBufferTarget(),\n video: {\n codec: 'avc',\n width: config.width,\n height: config.height,\n frameRate: config.fps,\n },\n fastStart: config.fastStart ?? 'in-memory',\n firstTimestampBehavior: 'offset',\n };\n\n // Add audio configuration if provided\n // If not provided initially, we assume AAC (standard for web)\n // but mp4-muxer might need it.\n // Actually mp4-muxer allows adding track configuration later?\n // No, it requires it in constructor or inferred?\n // If audioChunkMeta is missing, we can't configure audio fully here.\n // But mp4-muxer docs say: \"If you don't provide options.audio, no audio track will be created.\"\n // So we MUST provide options.audio if we want audio.\n // If we don't have meta yet, we guess?\n // Or we rely on audio export always providing AAC 48k?\n // Let's assume AAC 48k 2ch as default if we want audio support.\n // Or better: if audioChunkMeta is null, we enable audio with defaults.\n\n // However, for robustness, we should probably wait for first chunk to configure muxer?\n // But MuxManager calls `start` then `writeVideoChunk` then `writeAudioChunk`.\n // Audio might come later.\n //\n // Let's assume we always want audio track capability.\n muxerConfig.audio = {\n codec: 'aac',\n sampleRate: this.audioChunkMeta?.sampleRate || 48000,\n numberOfChannels: this.audioChunkMeta?.numberOfChannels || 2,\n };\n\n this.muxer = new Muxer(muxerConfig);\n }\n\n private videoChunkCount = 0;\n\n writeVideoChunk(chunk: EncodedVideoChunk, metadata?: EncodedVideoChunkMetadata): void {\n let meta: EncodedVideoChunkMetadata | undefined;\n\n if (this.firstVideoChunk) {\n if (metadata && metadata.decoderConfig) {\n this.videoChunkMeta = metadata.decoderConfig;\n }\n\n if (this.videoChunkMeta) {\n meta = { decoderConfig: this.videoChunkMeta };\n }\n\n // Ensure we have metadata for first chunk if it's a keyframe\n if (chunk.type === 'key' && !meta) {\n console.warn('[MP4Muxer] First video chunk is keyframe but missing decoderConfig');\n }\n\n this.firstVideoChunk = false;\n }\n\n this.videoChunkCount++;\n this.muxer.addVideoChunk(chunk, meta);\n }\n\n private audioChunkCount = 0;\n\n writeAudioChunk(chunk: EncodedAudioChunk, metadata?: EncodedAudioChunkMetadata): void {\n // Safari (and sometimes Chromium) may emit tiny (e.g. 6 bytes) \"audio\" chunks that are not valid AAC frames.\n // Keeping them may cause players to treat the whole audio track as undecodable/silent.\n // Drop tiny chunks and DO NOT trust their metadata.\n if (chunk.byteLength <= 16) {\n return;\n }\n\n let meta: EncodedAudioChunkMetadata | undefined;\n\n if (this.firstAudioChunk) {\n if (metadata && metadata.decoderConfig) {\n this.audioChunkMeta = metadata.decoderConfig;\n }\n\n if (this.audioChunkMeta) {\n meta = { decoderConfig: this.audioChunkMeta };\n }\n\n this.firstAudioChunk = false;\n }\n\n this.audioChunkCount++;\n this.muxer.addAudioChunk(chunk, meta);\n }\n\n finalize(): Blob {\n this.muxer.finalize();\n const buffer = (this.muxer.target as ArrayBufferTarget).buffer;\n return new Blob([buffer], { type: 'video/mp4' });\n }\n}\n"],"names":[],"mappings":";AAMO,MAAM,SAAS;AAAA,EACZ;AAAA,EACA,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,iBAAsB;AAAA,EACtB,iBAAsB;AAAA,EAE9B,YAAY,QAOT;AACD,SAAK,iBAAiB,OAAO;AAC7B,SAAK,iBAAiB,OAAO;AAE7B,UAAM,cAAmB;AAAA,MACvB,QAAQ,IAAI,kBAAA;AAAA,MACZ,OAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,QACf,WAAW,OAAO;AAAA,MAAA;AAAA,MAEpB,WAAW,OAAO,aAAa;AAAA,MAC/B,wBAAwB;AAAA,IAAA;AAqB1B,gBAAY,QAAQ;AAAA,MAClB,OAAO;AAAA,MACP,YAAY,KAAK,gBAAgB,cAAc;AAAA,MAC/C,kBAAkB,KAAK,gBAAgB,oBAAoB;AAAA,IAAA;AAG7D,SAAK,QAAQ,IAAI,MAAM,WAAW;AAAA,EACpC;AAAA,EAEQ,kBAAkB;AAAA,EAE1B,gBAAgB,OAA0B,UAA4C;AACpF,QAAI;AAEJ,QAAI,KAAK,iBAAiB;AACxB,UAAI,YAAY,SAAS,eAAe;AACtC,aAAK,iBAAiB,SAAS;AAAA,MACjC;AAEA,UAAI,KAAK,gBAAgB;AACvB,eAAO,EAAE,eAAe,KAAK,eAAA;AAAA,MAC/B;AAGA,UAAI,MAAM,SAAS,SAAS,CAAC,MAAM;AACjC,gBAAQ,KAAK,oEAAoE;AAAA,MACnF;AAEA,WAAK,kBAAkB;AAAA,IACzB;AAEA,SAAK;AACL,SAAK,MAAM,cAAc,OAAO,IAAI;AAAA,EACtC;AAAA,EAEQ,kBAAkB;AAAA,EAE1B,gBAAgB,OAA0B,UAA4C;AAIpF,QAAI,MAAM,cAAc,IAAI;AAC1B;AAAA,IACF;AAEA,QAAI;AAEJ,QAAI,KAAK,iBAAiB;AACxB,UAAI,YAAY,SAAS,eAAe;AACtC,aAAK,iBAAiB,SAAS;AAAA,MACjC;AAEA,UAAI,KAAK,gBAAgB;AACvB,eAAO,EAAE,eAAe,KAAK,eAAA;AAAA,MAC/B;AAEA,WAAK,kBAAkB;AAAA,IACzB;AAEA,SAAK;AACL,SAAK,MAAM,cAAc,OAAO,IAAI;AAAA,EACtC;AAAA,EAEA,WAAiB;AACf,SAAK,MAAM,SAAA;AACX,UAAM,SAAU,KAAK,MAAM,OAA6B;AACxD,WAAO,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,MAAM,aAAa;AAAA,EACjD;AACF;"}
1
+ {"version":3,"file":"MP4Muxer.js","sources":["../../../src/stages/mux/MP4Muxer.ts"],"sourcesContent":["import { Muxer, ArrayBufferTarget, StreamTarget } from 'mp4-muxer';\n\nexport type MuxOutputConfig =\n | { kind: 'blob' }\n | {\n kind: 'stream';\n onData: (data: Uint8Array, position: number) => void;\n chunked?: boolean;\n chunkSize?: number;\n };\n\n/**\n * MP4Muxer - MP4 container multiplexer using mp4-muxer library\n * Supports video and audio track export\n */\nexport class MP4Muxer {\n private muxer: Muxer<ArrayBufferTarget | StreamTarget>;\n private firstVideoChunk = true;\n private firstAudioChunk = true;\n private videoChunkMeta: any = null;\n private audioChunkMeta: any = null;\n private lastAudioDurationUs: number | null = null;\n\n constructor(\n config: {\n width: number;\n height: number;\n fps: number;\n fastStart?: false | 'in-memory' | 'fragmented';\n videoChunkMeta?: any;\n audioChunkMeta?: any;\n },\n output: MuxOutputConfig = { kind: 'blob' }\n ) {\n this.videoChunkMeta = config.videoChunkMeta;\n this.audioChunkMeta = config.audioChunkMeta;\n\n const target =\n output.kind === 'stream'\n ? new StreamTarget({\n onData: output.onData,\n chunked: output.chunked ?? true,\n chunkSize: output.chunkSize ?? 16 * 1024 * 1024, // 16 MiB default to reduce writes\n })\n : new ArrayBufferTarget();\n\n const muxerConfig: any = {\n target,\n video: {\n codec: 'avc',\n width: config.width,\n height: config.height,\n frameRate: config.fps,\n },\n fastStart: config.fastStart ?? 'in-memory',\n firstTimestampBehavior: 'offset',\n };\n\n // Add audio configuration if provided\n // If not provided initially, we assume AAC (standard for web)\n // but mp4-muxer might need it.\n // Actually mp4-muxer allows adding track configuration later?\n // No, it requires it in constructor or inferred?\n // If audioChunkMeta is missing, we can't configure audio fully here.\n // But mp4-muxer docs say: \"If you don't provide options.audio, no audio track will be created.\"\n // So we MUST provide options.audio if we want audio.\n // If we don't have meta yet, we guess?\n // Or we rely on audio export always providing AAC 48k?\n // Let's assume AAC 48k 2ch as default if we want audio support.\n // Or better: if audioChunkMeta is null, we enable audio with defaults.\n\n // However, for robustness, we should probably wait for first chunk to configure muxer?\n // But MuxManager calls `start` then `writeVideoChunk` then `writeAudioChunk`.\n // Audio might come later.\n //\n // Let's assume we always want audio track capability.\n muxerConfig.audio = {\n codec: 'aac',\n sampleRate: this.audioChunkMeta?.sampleRate || 48000,\n numberOfChannels: this.audioChunkMeta?.numberOfChannels || 2,\n };\n\n this.muxer = new Muxer(muxerConfig);\n }\n\n private videoChunkCount = 0;\n\n writeVideoChunk(chunk: EncodedVideoChunk, metadata?: EncodedVideoChunkMetadata): void {\n let meta: EncodedVideoChunkMetadata | undefined;\n\n if (this.firstVideoChunk) {\n if (metadata && metadata.decoderConfig) {\n this.videoChunkMeta = metadata.decoderConfig;\n }\n\n if (this.videoChunkMeta) {\n meta = { decoderConfig: this.videoChunkMeta };\n }\n\n // Ensure we have metadata for first chunk if it's a keyframe\n if (chunk.type === 'key' && !meta) {\n console.warn('[MP4Muxer] First video chunk is keyframe but missing decoderConfig');\n }\n\n this.firstVideoChunk = false;\n }\n\n this.videoChunkCount++;\n this.muxer.addVideoChunk(chunk, meta);\n }\n\n private audioChunkCount = 0;\n\n writeAudioChunk(chunk: EncodedAudioChunk, metadata?: EncodedAudioChunkMetadata): void {\n // Safari (and sometimes Chromium) may emit tiny (e.g. 6 bytes) \"audio\" chunks that are not valid AAC frames.\n // mp4-muxer treats durations as authoritative; dropping *valid* small chunks can collapse silence gaps and cause stutter.\n // So only drop tiny chunks when they are clearly invalid (duration missing/<=0).\n if (chunk.byteLength <= 16 && (!chunk.duration || chunk.duration <= 0)) {\n return;\n }\n\n let meta: EncodedAudioChunkMetadata | undefined;\n\n if (this.firstAudioChunk) {\n if (metadata && metadata.decoderConfig) {\n this.audioChunkMeta = metadata.decoderConfig;\n }\n\n if (this.audioChunkMeta) {\n meta = { decoderConfig: this.audioChunkMeta };\n }\n\n this.firstAudioChunk = false;\n }\n\n // Some platforms output duration=0/undefined for audio chunks.\n // mp4-muxer uses duration to build the timeline; 0 duration can collapse time and remove intended gaps.\n // Try to synthesize a sane duration:\n // - Prefer the chunk's own duration when >0\n // - Otherwise use AAC-LC frame duration (1024 samples) inferred from decoderConfig sampleRate if known\n const hasValidDuration = !!chunk.duration && chunk.duration > 0;\n if (!hasValidDuration) {\n const sampleRate =\n (metadata as any)?.decoderConfig?.sampleRate ?? this.audioChunkMeta?.sampleRate ?? 48_000;\n const inferredDurationUs =\n this.lastAudioDurationUs ?? Math.max(1, Math.round((1024 / sampleRate) * 1_000_000));\n\n const buffer = new ArrayBuffer(chunk.byteLength);\n chunk.copyTo(buffer);\n chunk = new EncodedAudioChunk({\n type: chunk.type,\n timestamp: chunk.timestamp,\n duration: inferredDurationUs,\n data: buffer,\n });\n this.lastAudioDurationUs = inferredDurationUs;\n } else {\n this.lastAudioDurationUs = chunk.duration!;\n }\n\n this.audioChunkCount++;\n this.muxer.addAudioChunk(chunk, meta);\n }\n\n finalize(): Blob | null {\n this.muxer.finalize();\n if (this.muxer.target instanceof ArrayBufferTarget) {\n const buffer = this.muxer.target.buffer;\n return new Blob([buffer], { type: 'video/mp4' });\n }\n // Stream target has already delivered data via onData callback.\n return null;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,13 +1,17 @@
1
+ import { MuxOutputConfig } from './MP4Muxer';
2
+
1
3
  export declare class MuxManager {
2
4
  private muxer;
5
+ private output;
3
6
  constructor();
4
7
  start(config: {
5
8
  width: number;
6
9
  height: number;
7
10
  fps: number;
11
+ output?: MuxOutputConfig;
8
12
  }): void;
9
13
  writeVideoChunk(chunk: EncodedVideoChunk, metadata?: EncodedVideoChunkMetadata): void;
10
14
  writeAudioChunk(chunk: EncodedAudioChunk, metadata?: EncodedAudioChunkMetadata): void;
11
- finalize(): Blob;
15
+ finalize(): Blob | null;
12
16
  }
13
17
  //# sourceMappingURL=MuxManager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MuxManager.d.ts","sourceRoot":"","sources":["../../../src/stages/mux/MuxManager.ts"],"names":[],"mappings":"AAEA,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAyB;;IAItC,KAAK,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;IAU5D,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,yBAAyB;IAK9E,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,yBAAyB;IAS9E,QAAQ,IAAI,IAAI;CAMjB"}
1
+ {"version":3,"file":"MuxManager.d.ts","sourceRoot":"","sources":["../../../src/stages/mux/MuxManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAE5D,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,MAAM,CAAgC;;IAI9C,KAAK,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,eAAe,CAAA;KAAE;IAgBtF,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,yBAAyB;IAK9E,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,yBAAyB;IAS9E,QAAQ,IAAI,IAAI,GAAG,IAAI;CAOxB"}
@@ -1,16 +1,23 @@
1
1
  import { MP4Muxer } from "./MP4Muxer.js";
2
2
  class MuxManager {
3
3
  muxer = null;
4
+ output = null;
4
5
  constructor() {
5
6
  }
6
7
  start(config) {
7
- this.muxer = new MP4Muxer({
8
- width: config.width,
9
- height: config.height,
10
- fps: config.fps,
11
- fastStart: "in-memory"
12
- // Metadata will be handled by first chunks
13
- });
8
+ this.output = config.output ?? { kind: "blob" };
9
+ this.muxer = new MP4Muxer(
10
+ {
11
+ width: config.width,
12
+ height: config.height,
13
+ fps: config.fps,
14
+ // For blob output, prefer fast start by keeping chunks in memory.
15
+ // For stream output, prefer minimal memory usage: metadata at end (non-fMP4).
16
+ fastStart: this.output.kind === "stream" ? false : "in-memory"
17
+ // Metadata will be handled by first chunks
18
+ },
19
+ this.output
20
+ );
14
21
  }
15
22
  writeVideoChunk(chunk, metadata) {
16
23
  if (!this.muxer) throw new Error("Muxer not started");
@@ -27,6 +34,7 @@ class MuxManager {
27
34
  if (!this.muxer) throw new Error("Muxer not started");
28
35
  const blob = this.muxer.finalize();
29
36
  this.muxer = null;
37
+ this.output = null;
30
38
  return blob;
31
39
  }
32
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MuxManager.js","sources":["../../../src/stages/mux/MuxManager.ts"],"sourcesContent":["import { MP4Muxer } from './MP4Muxer';\n\nexport class MuxManager {\n private muxer: MP4Muxer | null = null;\n\n constructor() {}\n\n start(config: { width: number; height: number; fps: number }) {\n this.muxer = new MP4Muxer({\n width: config.width,\n height: config.height,\n fps: config.fps,\n fastStart: 'in-memory',\n // Metadata will be handled by first chunks\n });\n }\n\n writeVideoChunk(chunk: EncodedVideoChunk, metadata?: EncodedVideoChunkMetadata) {\n if (!this.muxer) throw new Error('Muxer not started');\n this.muxer.writeVideoChunk(chunk, metadata);\n }\n\n writeAudioChunk(chunk: EncodedAudioChunk, metadata?: EncodedAudioChunkMetadata) {\n // Check if muxer is available (it might have been finalized already if audio is late)\n if (!this.muxer) {\n console.warn('[MuxManager] writeAudioChunk called after finalization, dropping chunk');\n return;\n }\n this.muxer.writeAudioChunk(chunk, metadata);\n }\n\n finalize(): Blob {\n if (!this.muxer) throw new Error('Muxer not started');\n const blob = this.muxer.finalize();\n this.muxer = null;\n return blob;\n }\n}\n"],"names":[],"mappings":";AAEO,MAAM,WAAW;AAAA,EACd,QAAyB;AAAA,EAEjC,cAAc;AAAA,EAAC;AAAA,EAEf,MAAM,QAAwD;AAC5D,SAAK,QAAQ,IAAI,SAAS;AAAA,MACxB,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,KAAK,OAAO;AAAA,MACZ,WAAW;AAAA;AAAA,IAAA,CAEZ;AAAA,EACH;AAAA,EAEA,gBAAgB,OAA0B,UAAsC;AAC9E,QAAI,CAAC,KAAK,MAAO,OAAM,IAAI,MAAM,mBAAmB;AACpD,SAAK,MAAM,gBAAgB,OAAO,QAAQ;AAAA,EAC5C;AAAA,EAEA,gBAAgB,OAA0B,UAAsC;AAE9E,QAAI,CAAC,KAAK,OAAO;AACf,cAAQ,KAAK,wEAAwE;AACrF;AAAA,IACF;AACA,SAAK,MAAM,gBAAgB,OAAO,QAAQ;AAAA,EAC5C;AAAA,EAEA,WAAiB;AACf,QAAI,CAAC,KAAK,MAAO,OAAM,IAAI,MAAM,mBAAmB;AACpD,UAAM,OAAO,KAAK,MAAM,SAAA;AACxB,SAAK,QAAQ;AACb,WAAO;AAAA,EACT;AACF;"}
1
+ {"version":3,"file":"MuxManager.js","sources":["../../../src/stages/mux/MuxManager.ts"],"sourcesContent":["import { MP4Muxer, type MuxOutputConfig } from './MP4Muxer';\n\nexport class MuxManager {\n private muxer: MP4Muxer | null = null;\n private output: MuxOutputConfig | null = null;\n\n constructor() {}\n\n start(config: { width: number; height: number; fps: number; output?: MuxOutputConfig }) {\n this.output = config.output ?? { kind: 'blob' };\n this.muxer = new MP4Muxer(\n {\n width: config.width,\n height: config.height,\n fps: config.fps,\n // For blob output, prefer fast start by keeping chunks in memory.\n // For stream output, prefer minimal memory usage: metadata at end (non-fMP4).\n fastStart: this.output.kind === 'stream' ? false : 'in-memory',\n // Metadata will be handled by first chunks\n },\n this.output\n );\n }\n\n writeVideoChunk(chunk: EncodedVideoChunk, metadata?: EncodedVideoChunkMetadata) {\n if (!this.muxer) throw new Error('Muxer not started');\n this.muxer.writeVideoChunk(chunk, metadata);\n }\n\n writeAudioChunk(chunk: EncodedAudioChunk, metadata?: EncodedAudioChunkMetadata) {\n // Check if muxer is available (it might have been finalized already if audio is late)\n if (!this.muxer) {\n console.warn('[MuxManager] writeAudioChunk called after finalization, dropping chunk');\n return;\n }\n this.muxer.writeAudioChunk(chunk, metadata);\n }\n\n finalize(): Blob | null {\n if (!this.muxer) throw new Error('Muxer not started');\n const blob = this.muxer.finalize();\n this.muxer = null;\n this.output = null;\n return blob;\n }\n}\n"],"names":[],"mappings":";AAEO,MAAM,WAAW;AAAA,EACd,QAAyB;AAAA,EACzB,SAAiC;AAAA,EAEzC,cAAc;AAAA,EAAC;AAAA,EAEf,MAAM,QAAkF;AACtF,SAAK,SAAS,OAAO,UAAU,EAAE,MAAM,OAAA;AACvC,SAAK,QAAQ,IAAI;AAAA,MACf;AAAA,QACE,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,QACf,KAAK,OAAO;AAAA;AAAA;AAAA,QAGZ,WAAW,KAAK,OAAO,SAAS,WAAW,QAAQ;AAAA;AAAA,MAAA;AAAA,MAGrD,KAAK;AAAA,IAAA;AAAA,EAET;AAAA,EAEA,gBAAgB,OAA0B,UAAsC;AAC9E,QAAI,CAAC,KAAK,MAAO,OAAM,IAAI,MAAM,mBAAmB;AACpD,SAAK,MAAM,gBAAgB,OAAO,QAAQ;AAAA,EAC5C;AAAA,EAEA,gBAAgB,OAA0B,UAAsC;AAE9E,QAAI,CAAC,KAAK,OAAO;AACf,cAAQ,KAAK,wEAAwE;AACrF;AAAA,IACF;AACA,SAAK,MAAM,gBAAgB,OAAO,QAAQ;AAAA,EAC5C;AAAA,EAEA,WAAwB;AACtB,QAAI,CAAC,KAAK,MAAO,OAAM,IAAI,MAAM,mBAAmB;AACpD,UAAM,OAAO,KAAK,MAAM,SAAA;AACxB,SAAK,QAAQ;AACb,SAAK,SAAS;AACd,WAAO;AAAA,EACT;AACF;"}
package/dist/types.d.ts CHANGED
@@ -21,5 +21,13 @@ export interface ExportOptions {
21
21
  fps?: number;
22
22
  /** Quality preset */
23
23
  quality?: 'low' | 'medium' | 'high' | 'highest';
24
+ /** Export output mode (default blob). */
25
+ exportMode?: 'blob' | 'stream';
26
+ /** Streaming mux callback, required when exportMode is 'stream'. */
27
+ onMuxData?: (data: Uint8Array, position: number) => void;
28
+ /** Optional chunk sizing hint for streaming mux output. */
29
+ muxChunkSizeBytes?: number;
30
+ /** Whether to enable mp4-muxer chunked StreamTarget (default true). */
31
+ muxChunked?: boolean;
24
32
  }
25
33
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEpE,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,SAAS,GACT,OAAO,GACP,SAAS,GACT,QAAQ,GACR,WAAW,GACX,OAAO,GACP,WAAW,CAAC;AAEhB,MAAM,WAAW,aAAa;IAC5B,oBAAoB;IACpB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAExB,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IAE7C,kBAAkB;IAClB,UAAU,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAE5B,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,iBAAiB;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,qBAAqB;IACrB,OAAO,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;CACjD"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEpE,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,SAAS,GACT,OAAO,GACP,SAAS,GACT,QAAQ,GACR,WAAW,GACX,OAAO,GACP,WAAW,CAAC;AAEhB,MAAM,WAAW,aAAa;IAC5B,oBAAoB;IACpB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAExB,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;IAE7C,kBAAkB;IAClB,UAAU,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAE5B,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,iBAAiB;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,qBAAqB;IACrB,OAAO,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IAEhD,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B,oEAAoE;IACpE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uEAAuE;IACvE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB"}
@@ -61,4 +61,5 @@ export declare class BrowserCompatibilityError extends Error {
61
61
  };
62
62
  });
63
63
  }
64
+ export declare function isDOMException(error: unknown, name: string): boolean;
64
65
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;aAChB,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CAI3C;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,UAAU,EAAE,MAAM;aAClB,UAAU,EAAE,MAAM;gBADlB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM;CAQrC;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM;gBADd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM;CAKjC;AAED;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,SAAS,EAAE,MAAM;aACjB,MAAM,EAAE,MAAM;aACd,SAAS,EAAE,OAAO;gBAFlB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO;CAQrC;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;aAEhC,aAAa,EAAE;QAC7B,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE;gBALe,aAAa,EAAE;QAC7B,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE;CAYJ"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;aAChB,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CAI3C;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,UAAU,EAAE,MAAM;aAClB,UAAU,EAAE,MAAM;gBADlB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM;CAQrC;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM;gBADd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM;CAKjC;AAED;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,SAAS,EAAE,MAAM;aACjB,MAAM,EAAE,MAAM;aACd,SAAS,EAAE,OAAO;gBAFlB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO;CAQrC;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;aAEhC,aAAa,EAAE;QAC7B,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE;gBALe,aAAa,EAAE;QAC7B,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE;CAYJ;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpE"}
@@ -44,11 +44,15 @@ class BrowserCompatibilityError extends Error {
44
44
  this.name = "BrowserCompatibilityError";
45
45
  }
46
46
  }
47
+ function isDOMException(error, name) {
48
+ return error instanceof Error && "name" in error && error.name === name;
49
+ }
47
50
  export {
48
51
  BrowserCompatibilityError,
49
52
  EmptyStreamError,
50
53
  OPFSQuotaExceededError,
51
54
  ResourceCorruptedError,
52
- WaiterReplacedError
55
+ WaiterReplacedError,
56
+ isDOMException
53
57
  };
54
58
  //# sourceMappingURL=errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sources":["../../src/utils/errors.ts"],"sourcesContent":["/**\n * Custom error types for Meframe\n */\n\n/**\n * Error thrown when a clip ready waiter is replaced by a newer one\n * This happens during rapid seeks and should be ignored by callers\n */\nexport class WaiterReplacedError extends Error {\n constructor(public readonly clipId: string) {\n super(`Waiter for clip ${clipId} was replaced by a newer waiter`);\n this.name = 'WaiterReplacedError';\n }\n}\n\n/**\n * Error thrown when reading an empty or incomplete OPFS file\n * Usually indicates a race condition between write and read operations\n */\nexport class EmptyStreamError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly fileOffset: number\n ) {\n super(\n `Empty stream received for resource ${resourceId}. ` +\n `File offset: ${fileOffset}. This indicates the file is being written or is corrupted.`\n );\n this.name = 'EmptyStreamError';\n }\n}\n\n/**\n * Error thrown when a cached resource file is corrupted or incomplete\n */\nexport class ResourceCorruptedError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly reason: string\n ) {\n super(`Resource ${resourceId} is corrupted: ${reason}`);\n this.name = 'ResourceCorruptedError';\n }\n}\n\n/**\n * Error thrown when OPFS quota is exceeded\n * Used for project-level LRU eviction coordination\n */\nexport class OPFSQuotaExceededError extends Error {\n constructor(\n public readonly projectId: string,\n public readonly prefix: string,\n public readonly retryable: boolean\n ) {\n super(\n `OPFS quota exceeded for ${prefix}-${projectId}. ` +\n (retryable ? 'Old projects evicted, please retry.' : 'No space available.')\n );\n this.name = 'OPFSQuotaExceededError';\n }\n}\n\n/**\n * Error thrown when browser doesn't meet minimum requirements\n */\nexport class BrowserCompatibilityError extends Error {\n constructor(\n public readonly compatibility: {\n webCodecsAvailable: boolean;\n opfsAvailable: boolean;\n missingFeatures: string[];\n browserInfo: { name: string; version: string; recommended: string };\n }\n ) {\n const { browserInfo, missingFeatures } = compatibility;\n\n super(\n `Browser not supported: ${browserInfo.name} ${browserInfo.version}. ` +\n `Required: ${browserInfo.recommended}. ` +\n `Missing: ${missingFeatures.join(', ')}`\n );\n\n this.name = 'BrowserCompatibilityError';\n }\n}\n"],"names":[],"mappings":"AAQO,MAAM,4BAA4B,MAAM;AAAA,EAC7C,YAA4B,QAAgB;AAC1C,UAAM,mBAAmB,MAAM,iCAAiC;AADtC,SAAA,SAAA;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,yBAAyB,MAAM;AAAA,EAC1C,YACkB,YACA,YAChB;AACA;AAAA,MACE,sCAAsC,UAAU,kBAC9B,UAAU;AAAA,IAAA;AALd,SAAA,aAAA;AACA,SAAA,aAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,YACA,QAChB;AACA,UAAM,YAAY,UAAU,kBAAkB,MAAM,EAAE;AAHtC,SAAA,aAAA;AACA,SAAA,SAAA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,WACA,QACA,WAChB;AACA;AAAA,MACE,2BAA2B,MAAM,IAAI,SAAS,QAC3C,YAAY,wCAAwC;AAAA,IAAA;AANzC,SAAA,YAAA;AACA,SAAA,SAAA;AACA,SAAA,YAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,MAAM,kCAAkC,MAAM;AAAA,EACnD,YACkB,eAMhB;AACA,UAAM,EAAE,aAAa,gBAAA,IAAoB;AAEzC;AAAA,MACE,0BAA0B,YAAY,IAAI,IAAI,YAAY,OAAO,eAClD,YAAY,WAAW,cACxB,gBAAgB,KAAK,IAAI,CAAC;AAAA,IAAA;AAZ1B,SAAA,gBAAA;AAehB,SAAK,OAAO;AAAA,EACd;AACF;"}
1
+ {"version":3,"file":"errors.js","sources":["../../src/utils/errors.ts"],"sourcesContent":["/**\n * Custom error types for Meframe\n */\n\n/**\n * Error thrown when a clip ready waiter is replaced by a newer one\n * This happens during rapid seeks and should be ignored by callers\n */\nexport class WaiterReplacedError extends Error {\n constructor(public readonly clipId: string) {\n super(`Waiter for clip ${clipId} was replaced by a newer waiter`);\n this.name = 'WaiterReplacedError';\n }\n}\n\n/**\n * Error thrown when reading an empty or incomplete OPFS file\n * Usually indicates a race condition between write and read operations\n */\nexport class EmptyStreamError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly fileOffset: number\n ) {\n super(\n `Empty stream received for resource ${resourceId}. ` +\n `File offset: ${fileOffset}. This indicates the file is being written or is corrupted.`\n );\n this.name = 'EmptyStreamError';\n }\n}\n\n/**\n * Error thrown when a cached resource file is corrupted or incomplete\n */\nexport class ResourceCorruptedError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly reason: string\n ) {\n super(`Resource ${resourceId} is corrupted: ${reason}`);\n this.name = 'ResourceCorruptedError';\n }\n}\n\n/**\n * Error thrown when OPFS quota is exceeded\n * Used for project-level LRU eviction coordination\n */\nexport class OPFSQuotaExceededError extends Error {\n constructor(\n public readonly projectId: string,\n public readonly prefix: string,\n public readonly retryable: boolean\n ) {\n super(\n `OPFS quota exceeded for ${prefix}-${projectId}. ` +\n (retryable ? 'Old projects evicted, please retry.' : 'No space available.')\n );\n this.name = 'OPFSQuotaExceededError';\n }\n}\n\n/**\n * Error thrown when browser doesn't meet minimum requirements\n */\nexport class BrowserCompatibilityError extends Error {\n constructor(\n public readonly compatibility: {\n webCodecsAvailable: boolean;\n opfsAvailable: boolean;\n missingFeatures: string[];\n browserInfo: { name: string; version: string; recommended: string };\n }\n ) {\n const { browserInfo, missingFeatures } = compatibility;\n\n super(\n `Browser not supported: ${browserInfo.name} ${browserInfo.version}. ` +\n `Required: ${browserInfo.recommended}. ` +\n `Missing: ${missingFeatures.join(', ')}`\n );\n\n this.name = 'BrowserCompatibilityError';\n }\n}\n\nexport function isDOMException(error: unknown, name: string): boolean {\n return error instanceof Error && 'name' in error && error.name === name;\n}\n"],"names":[],"mappings":"AAQO,MAAM,4BAA4B,MAAM;AAAA,EAC7C,YAA4B,QAAgB;AAC1C,UAAM,mBAAmB,MAAM,iCAAiC;AADtC,SAAA,SAAA;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,yBAAyB,MAAM;AAAA,EAC1C,YACkB,YACA,YAChB;AACA;AAAA,MACE,sCAAsC,UAAU,kBAC9B,UAAU;AAAA,IAAA;AALd,SAAA,aAAA;AACA,SAAA,aAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,YACA,QAChB;AACA,UAAM,YAAY,UAAU,kBAAkB,MAAM,EAAE;AAHtC,SAAA,aAAA;AACA,SAAA,SAAA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,WACA,QACA,WAChB;AACA;AAAA,MACE,2BAA2B,MAAM,IAAI,SAAS,QAC3C,YAAY,wCAAwC;AAAA,IAAA;AANzC,SAAA,YAAA;AACA,SAAA,SAAA;AACA,SAAA,YAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,MAAM,kCAAkC,MAAM;AAAA,EACnD,YACkB,eAMhB;AACA,UAAM,EAAE,aAAa,gBAAA,IAAoB;AAEzC;AAAA,MACE,0BAA0B,YAAY,IAAI,IAAI,YAAY,OAAO,eAClD,YAAY,WAAW,cACxB,gBAAgB,KAAK,IAAI,CAAC;AAAA,IAAA;AAZ1B,SAAA,gBAAA;AAehB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,eAAe,OAAgB,MAAuB;AACpE,SAAO,iBAAiB,SAAS,UAAU,SAAS,MAAM,SAAS;AACrE;"}
@@ -1,4 +1,4 @@
1
- import * as mp4box_all from "../medeo-fe/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js";
1
+ import * as mp4box_all from "../node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js";
2
2
  const lib = mp4box_all;
3
3
  const MP4Box = lib.default && typeof lib.default.createFile === "function" ? lib.default : lib;
4
4
  if (typeof MP4Box.createFile !== "function") {