@meframe/core 0.0.29 → 0.0.30-beta

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 (212) hide show
  1. package/dist/Meframe.d.ts +2 -13
  2. package/dist/Meframe.d.ts.map +1 -1
  3. package/dist/Meframe.js +6 -100
  4. package/dist/Meframe.js.map +1 -1
  5. package/dist/cache/CacheManager.d.ts +35 -19
  6. package/dist/cache/CacheManager.d.ts.map +1 -1
  7. package/dist/cache/CacheManager.js +223 -134
  8. package/dist/cache/CacheManager.js.map +1 -1
  9. package/dist/cache/l1/VideoL1Cache.d.ts +15 -2
  10. package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -1
  11. package/dist/cache/l1/VideoL1Cache.js +58 -38
  12. package/dist/cache/l1/VideoL1Cache.js.map +1 -1
  13. package/dist/cache/l2/L2Cache.d.ts.map +1 -1
  14. package/dist/cache/l2/L2Cache.js +5 -5
  15. package/dist/cache/l2/L2Cache.js.map +1 -1
  16. package/dist/cache/l2/L2OPFSStore.d.ts +37 -0
  17. package/dist/cache/l2/L2OPFSStore.d.ts.map +1 -0
  18. package/dist/cache/l2/L2OPFSStore.js +89 -0
  19. package/dist/cache/l2/L2OPFSStore.js.map +1 -0
  20. package/dist/cache/resource/AudioSampleCache.d.ts +52 -0
  21. package/dist/cache/resource/AudioSampleCache.d.ts.map +1 -0
  22. package/dist/cache/resource/AudioSampleCache.js +69 -0
  23. package/dist/cache/resource/AudioSampleCache.js.map +1 -0
  24. package/dist/cache/resource/ImageBitmapCache.d.ts +65 -0
  25. package/dist/cache/resource/ImageBitmapCache.d.ts.map +1 -0
  26. package/dist/cache/resource/ImageBitmapCache.js +101 -0
  27. package/dist/cache/resource/ImageBitmapCache.js.map +1 -0
  28. package/dist/cache/resource/MP4IndexCache.d.ts +48 -0
  29. package/dist/cache/resource/MP4IndexCache.d.ts.map +1 -0
  30. package/dist/cache/resource/MP4IndexCache.js +104 -0
  31. package/dist/cache/resource/MP4IndexCache.js.map +1 -0
  32. package/dist/cache/resource/ResourceCache.d.ts +46 -0
  33. package/dist/cache/resource/ResourceCache.d.ts.map +1 -0
  34. package/dist/cache/resource/ResourceCache.js +92 -0
  35. package/dist/cache/resource/ResourceCache.js.map +1 -0
  36. package/dist/cache/storage/indexeddb/ChunkRecordStore.d.ts +75 -0
  37. package/dist/cache/storage/indexeddb/ChunkRecordStore.d.ts.map +1 -0
  38. package/dist/cache/{l2/IndexedDBStore.js → storage/indexeddb/ChunkRecordStore.js} +3 -3
  39. package/dist/cache/storage/indexeddb/ChunkRecordStore.js.map +1 -0
  40. package/dist/cache/storage/opfs/OPFSManager.d.ts +54 -0
  41. package/dist/cache/storage/opfs/OPFSManager.d.ts.map +1 -0
  42. package/dist/cache/storage/opfs/OPFSManager.js +133 -0
  43. package/dist/cache/storage/opfs/OPFSManager.js.map +1 -0
  44. package/dist/cache/storage/opfs/types.d.ts +16 -0
  45. package/dist/cache/storage/opfs/types.d.ts.map +1 -0
  46. package/dist/config/defaults.d.ts.map +1 -1
  47. package/dist/config/defaults.js +21 -2
  48. package/dist/config/defaults.js.map +1 -1
  49. package/dist/config/types.d.ts +28 -0
  50. package/dist/config/types.d.ts.map +1 -1
  51. package/dist/controllers/ExportController.d.ts +16 -0
  52. package/dist/controllers/ExportController.d.ts.map +1 -0
  53. package/dist/controllers/ExportController.js +44 -0
  54. package/dist/controllers/ExportController.js.map +1 -0
  55. package/dist/controllers/PlaybackController.d.ts +28 -4
  56. package/dist/controllers/PlaybackController.d.ts.map +1 -1
  57. package/dist/controllers/PlaybackController.js +116 -51
  58. package/dist/controllers/PlaybackController.js.map +1 -1
  59. package/dist/controllers/index.d.ts +2 -3
  60. package/dist/controllers/index.d.ts.map +1 -1
  61. package/dist/controllers/types.d.ts +0 -28
  62. package/dist/controllers/types.d.ts.map +1 -1
  63. package/dist/event/events.d.ts +8 -0
  64. package/dist/event/events.d.ts.map +1 -1
  65. package/dist/event/events.js +1 -0
  66. package/dist/event/events.js.map +1 -1
  67. package/dist/model/CompositionModel.d.ts.map +1 -1
  68. package/dist/model/CompositionModel.js +11 -6
  69. package/dist/model/CompositionModel.js.map +1 -1
  70. package/dist/model/RcFrame.d.ts +2 -0
  71. package/dist/model/RcFrame.d.ts.map +1 -1
  72. package/dist/model/RcFrame.js +3 -0
  73. package/dist/model/RcFrame.js.map +1 -1
  74. package/dist/orchestrator/ExportScheduler.d.ts +35 -0
  75. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -0
  76. package/dist/orchestrator/ExportScheduler.js +241 -0
  77. package/dist/orchestrator/ExportScheduler.js.map +1 -0
  78. package/dist/orchestrator/GlobalAudioSession.d.ts +21 -7
  79. package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
  80. package/dist/orchestrator/GlobalAudioSession.js +132 -140
  81. package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
  82. package/dist/orchestrator/OnDemandVideoSession.d.ts +73 -0
  83. package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -0
  84. package/dist/orchestrator/OnDemandVideoSession.js +281 -0
  85. package/dist/orchestrator/OnDemandVideoSession.js.map +1 -0
  86. package/dist/orchestrator/Orchestrator.d.ts +22 -17
  87. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  88. package/dist/orchestrator/Orchestrator.js +231 -297
  89. package/dist/orchestrator/Orchestrator.js.map +1 -1
  90. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  91. package/dist/orchestrator/VideoClipSession.js +3 -15
  92. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  93. package/dist/orchestrator/index.d.ts +0 -1
  94. package/dist/orchestrator/index.d.ts.map +1 -1
  95. package/dist/orchestrator/types.d.ts +4 -4
  96. package/dist/orchestrator/types.d.ts.map +1 -1
  97. package/dist/stages/compose/FilterProcessor.d.ts +1 -1
  98. package/dist/stages/compose/FilterProcessor.d.ts.map +1 -1
  99. package/dist/stages/compose/FilterProcessor.js +226 -0
  100. package/dist/stages/compose/FilterProcessor.js.map +1 -0
  101. package/dist/stages/compose/LayerRenderer.d.ts +1 -1
  102. package/dist/stages/compose/LayerRenderer.d.ts.map +1 -1
  103. package/dist/stages/compose/LayerRenderer.js +270 -0
  104. package/dist/stages/compose/LayerRenderer.js.map +1 -0
  105. package/dist/stages/compose/TransitionProcessor.d.ts +1 -1
  106. package/dist/stages/compose/TransitionProcessor.d.ts.map +1 -1
  107. package/dist/stages/compose/TransitionProcessor.js +189 -0
  108. package/dist/stages/compose/TransitionProcessor.js.map +1 -0
  109. package/dist/stages/compose/VideoComposer.d.ts +4 -2
  110. package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
  111. package/dist/stages/compose/VideoComposer.js +229 -0
  112. package/dist/stages/compose/VideoComposer.js.map +1 -0
  113. package/dist/stages/compose/text-renderers/animation-utils.js +76 -0
  114. package/dist/stages/compose/text-renderers/animation-utils.js.map +1 -0
  115. package/dist/stages/compose/text-renderers/basic-text-renderer.d.ts +2 -2
  116. package/dist/stages/compose/text-renderers/basic-text-renderer.d.ts.map +1 -1
  117. package/dist/stages/compose/text-renderers/basic-text-renderer.js +93 -0
  118. package/dist/stages/compose/text-renderers/basic-text-renderer.js.map +1 -0
  119. package/dist/stages/compose/text-renderers/character-ktv-renderer.d.ts +1 -1
  120. package/dist/stages/compose/text-renderers/character-ktv-renderer.d.ts.map +1 -1
  121. package/dist/stages/compose/text-renderers/character-ktv-renderer.js +132 -0
  122. package/dist/stages/compose/text-renderers/character-ktv-renderer.js.map +1 -0
  123. package/dist/stages/compose/text-renderers/word-by-word-renderer.d.ts +1 -1
  124. package/dist/stages/compose/text-renderers/word-by-word-renderer.d.ts.map +1 -1
  125. package/dist/stages/compose/text-renderers/word-by-word-renderer.js +128 -0
  126. package/dist/stages/compose/text-renderers/word-by-word-renderer.js.map +1 -0
  127. package/dist/stages/compose/text-renderers/word-fancy-renderer.d.ts +1 -1
  128. package/dist/stages/compose/text-renderers/word-fancy-renderer.d.ts.map +1 -1
  129. package/dist/stages/compose/text-renderers/word-fancy-renderer.js +135 -0
  130. package/dist/stages/compose/text-renderers/word-fancy-renderer.js.map +1 -0
  131. package/dist/stages/compose/text-utils/locale-detector.js +16 -0
  132. package/dist/stages/compose/text-utils/locale-detector.js.map +1 -0
  133. package/dist/stages/compose/text-utils/text-metrics.js +21 -0
  134. package/dist/stages/compose/text-utils/text-metrics.js.map +1 -0
  135. package/dist/stages/compose/text-utils/text-wrapper.js +225 -0
  136. package/dist/stages/compose/text-utils/text-wrapper.js.map +1 -0
  137. package/dist/stages/compose/types.d.ts +2 -1
  138. package/dist/stages/compose/types.d.ts.map +1 -1
  139. package/dist/stages/decode/BaseDecoder.js +0 -3
  140. package/dist/stages/decode/BaseDecoder.js.map +1 -1
  141. package/dist/stages/demux/MP4Demuxer.d.ts +5 -0
  142. package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
  143. package/dist/stages/demux/MP4Demuxer.js +281 -0
  144. package/dist/stages/demux/MP4Demuxer.js.map +1 -0
  145. package/dist/stages/demux/MP4IndexParser.d.ts +71 -0
  146. package/dist/stages/demux/MP4IndexParser.d.ts.map +1 -0
  147. package/dist/stages/demux/MP4IndexParser.js +416 -0
  148. package/dist/stages/demux/MP4IndexParser.js.map +1 -0
  149. package/dist/stages/demux/types.d.ts +48 -0
  150. package/dist/stages/demux/types.d.ts.map +1 -1
  151. package/dist/stages/load/ResourceLoader.d.ts +44 -2
  152. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  153. package/dist/stages/load/ResourceLoader.js +281 -37
  154. package/dist/stages/load/ResourceLoader.js.map +1 -1
  155. package/dist/stages/load/TaskManager.d.ts +6 -2
  156. package/dist/stages/load/TaskManager.d.ts.map +1 -1
  157. package/dist/stages/load/TaskManager.js +27 -4
  158. package/dist/stages/load/TaskManager.js.map +1 -1
  159. package/dist/stages/load/types.d.ts +7 -0
  160. package/dist/stages/load/types.d.ts.map +1 -1
  161. package/dist/stages/mux/MP4Muxer.d.ts +2 -2
  162. package/dist/stages/mux/MP4Muxer.d.ts.map +1 -1
  163. package/dist/stages/mux/MP4Muxer.js +24 -13
  164. package/dist/stages/mux/MP4Muxer.js.map +1 -1
  165. package/dist/stages/mux/MuxManager.d.ts +10 -21
  166. package/dist/stages/mux/MuxManager.d.ts.map +1 -1
  167. package/dist/stages/mux/MuxManager.js +21 -162
  168. package/dist/stages/mux/MuxManager.js.map +1 -1
  169. package/dist/stages/mux/index.d.ts +0 -1
  170. package/dist/stages/mux/index.d.ts.map +1 -1
  171. package/dist/utils/binary-search.d.ts +12 -4
  172. package/dist/utils/binary-search.d.ts.map +1 -1
  173. package/dist/utils/binary-search.js +52 -6
  174. package/dist/utils/binary-search.js.map +1 -1
  175. package/dist/workers/{BaseDecoder.BWYu1W0B.js → BaseDecoder.CTW-vr29.js} +1 -4
  176. package/dist/workers/BaseDecoder.CTW-vr29.js.map +1 -0
  177. package/dist/workers/{MP4Demuxer.lMOUMWFh.js → MP4Demuxer.BEa6PLJm.js} +9 -2
  178. package/dist/workers/{MP4Demuxer.lMOUMWFh.js.map → MP4Demuxer.BEa6PLJm.js.map} +1 -1
  179. package/dist/workers/stages/compose/{video-compose.worker.CIeEIJO7.js → video-compose.worker.DHQ8B105.js} +59 -31
  180. package/dist/workers/stages/compose/video-compose.worker.DHQ8B105.js.map +1 -0
  181. package/dist/workers/stages/decode/{audio-decode.worker.DnS17GD9.js → audio-decode.worker.CP8bXXa4.js} +2 -2
  182. package/dist/workers/stages/decode/{audio-decode.worker.DnS17GD9.js.map → audio-decode.worker.CP8bXXa4.js.map} +1 -1
  183. package/dist/workers/stages/decode/{video-decode.worker.BEYsjOXp.js → video-decode.worker.BIspTxgV.js} +2 -2
  184. package/dist/workers/stages/decode/{video-decode.worker.BEYsjOXp.js.map → video-decode.worker.BIspTxgV.js.map} +1 -1
  185. package/dist/workers/stages/demux/{audio-demux.worker.DcurGC8i.js → audio-demux.worker._VRQdLdv.js} +2 -2
  186. package/dist/workers/stages/demux/{audio-demux.worker.DcurGC8i.js.map → audio-demux.worker._VRQdLdv.js.map} +1 -1
  187. package/dist/workers/stages/demux/{video-demux.worker.B1_wntU4.js → video-demux.worker.CSkxGtmx.js} +3 -19
  188. package/dist/workers/stages/demux/video-demux.worker.CSkxGtmx.js.map +1 -0
  189. package/dist/workers/worker-manifest.json +5 -5
  190. package/package.json +1 -1
  191. package/dist/cache/l2/IndexedDBStore.js.map +0 -1
  192. package/dist/cache/l2/OPFSStore.js +0 -131
  193. package/dist/cache/l2/OPFSStore.js.map +0 -1
  194. package/dist/controllers/PreRenderService.d.ts +0 -59
  195. package/dist/controllers/PreRenderService.d.ts.map +0 -1
  196. package/dist/controllers/PreRenderService.js +0 -185
  197. package/dist/controllers/PreRenderService.js.map +0 -1
  198. package/dist/controllers/PreRenderTaskQueue.d.ts +0 -21
  199. package/dist/controllers/PreRenderTaskQueue.d.ts.map +0 -1
  200. package/dist/orchestrator/ClipSessionManager.d.ts +0 -70
  201. package/dist/orchestrator/ClipSessionManager.d.ts.map +0 -1
  202. package/dist/orchestrator/ClipSessionManager.js +0 -158
  203. package/dist/orchestrator/ClipSessionManager.js.map +0 -1
  204. package/dist/stages/decode/AudioChunkDecoder.js +0 -169
  205. package/dist/stages/decode/AudioChunkDecoder.js.map +0 -1
  206. package/dist/stages/mux/OPFSWriter.d.ts +0 -46
  207. package/dist/stages/mux/OPFSWriter.d.ts.map +0 -1
  208. package/dist/utils/BackpressureAdapter.d.ts +0 -26
  209. package/dist/utils/BackpressureAdapter.d.ts.map +0 -1
  210. package/dist/workers/BaseDecoder.BWYu1W0B.js.map +0 -1
  211. package/dist/workers/stages/compose/video-compose.worker.CIeEIJO7.js.map +0 -1
  212. package/dist/workers/stages/demux/video-demux.worker.B1_wntU4.js.map +0 -1
@@ -0,0 +1,281 @@
1
+ import "../../node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js";
2
+ import { __exports as mp4box_all } from "../../_virtual/mp4box.all.js";
3
+ function normalizeAudioCodec(codec) {
4
+ if (!codec) return "mp4a.40.2";
5
+ if (codec === "mp4a") {
6
+ return "mp4a.40.2";
7
+ }
8
+ return codec;
9
+ }
10
+ class MP4Demuxer {
11
+ mp4boxFile;
12
+ tracks = /* @__PURE__ */ new Map();
13
+ isReady = false;
14
+ videoController;
15
+ audioController;
16
+ demuxHighWaterMark;
17
+ onReadyCallback;
18
+ fileOffset = 0;
19
+ videoTimestampOffset = null;
20
+ audioTimestampOffset = null;
21
+ skipAudio = false;
22
+ constructor(config = {}) {
23
+ this.mp4boxFile = mp4box_all.createFile();
24
+ this.onReadyCallback = config.onReady;
25
+ this.skipAudio = config.skipAudio ?? false;
26
+ const DEFAULT_HIGH_WATER_MARK = 10;
27
+ this.demuxHighWaterMark = config.highWaterMark ?? DEFAULT_HIGH_WATER_MARK;
28
+ this.setupHandlers();
29
+ }
30
+ updateConfig(config) {
31
+ this.demuxHighWaterMark = config.highWaterMark ?? this.demuxHighWaterMark;
32
+ }
33
+ setupHandlers() {
34
+ this.mp4boxFile.onError = (error) => {
35
+ console.error("[MP4Demuxer] MP4Box error:", error);
36
+ const err = new Error(error);
37
+ this.videoController?.error(err);
38
+ this.audioController?.error(err);
39
+ };
40
+ this.mp4boxFile.onReady = (info) => {
41
+ this.processTracks(info.tracks);
42
+ this.isReady = true;
43
+ if (this.onReadyCallback) {
44
+ this.onReadyCallback();
45
+ }
46
+ this.mp4boxFile.start();
47
+ };
48
+ this.mp4boxFile.onSamples = (trackId, _user, samples) => {
49
+ this.processSamples(trackId, samples);
50
+ };
51
+ }
52
+ processTracks(tracks) {
53
+ for (const track of tracks) {
54
+ const trackInfo = {
55
+ id: track.id,
56
+ type: track.type === "video" ? "video" : "audio",
57
+ codec: track.type === "audio" ? normalizeAudioCodec(track.codec) : track.codec,
58
+ timescale: track.timescale
59
+ };
60
+ if (track.type === "video") {
61
+ trackInfo.width = track.video?.width;
62
+ trackInfo.height = track.video?.height;
63
+ trackInfo.description = this.getVideoDescription(track);
64
+ } else if (track.type === "audio") {
65
+ trackInfo.sampleRate = track.audio?.sample_rate || track.timescale;
66
+ trackInfo.numberOfChannels = track.audio?.channel_count;
67
+ trackInfo.description = this.getAudioDescription(track);
68
+ }
69
+ this.tracks.set(track.id, trackInfo);
70
+ this.mp4boxFile.setExtractionOptions(track.id, track, {
71
+ nbSamples: Infinity
72
+ });
73
+ }
74
+ }
75
+ processSamples(trackId, samples) {
76
+ const track = this.tracks.get(trackId);
77
+ if (!track) return;
78
+ const timescale = track.timescale || 9e4;
79
+ for (const sample of samples) {
80
+ const rawTimestamp = sample.cts * 1e6 / timescale;
81
+ const duration = sample.duration * 1e6 / timescale;
82
+ if (track.type === "video") {
83
+ if (!this.videoController) {
84
+ console.error("[MP4Demuxer] videoController is null when trying to output chunk!");
85
+ return;
86
+ }
87
+ if (this.videoTimestampOffset === null) {
88
+ this.videoTimestampOffset = rawTimestamp;
89
+ }
90
+ const timestamp = rawTimestamp - this.videoTimestampOffset;
91
+ const chunk = new EncodedVideoChunk({
92
+ type: sample.is_sync ? "key" : "delta",
93
+ timestamp,
94
+ duration,
95
+ data: sample.data
96
+ });
97
+ this.videoController.enqueue(chunk);
98
+ } else if (track.type === "audio") {
99
+ if (!this.audioController) {
100
+ const last2 = samples[samples.length - 1].number;
101
+ this.mp4boxFile.releaseUsedSamples(trackId, last2 + 1);
102
+ return;
103
+ }
104
+ if (this.audioTimestampOffset === null) {
105
+ this.audioTimestampOffset = rawTimestamp;
106
+ }
107
+ const timestamp = rawTimestamp - this.audioTimestampOffset;
108
+ const chunk = new EncodedAudioChunk({
109
+ type: "key",
110
+ timestamp,
111
+ duration,
112
+ data: sample.data
113
+ });
114
+ this.audioController.enqueue(chunk);
115
+ }
116
+ }
117
+ const last = samples[samples.length - 1].number;
118
+ this.mp4boxFile.releaseUsedSamples(trackId, last + 1);
119
+ }
120
+ getVideoDescription(track) {
121
+ return MP4Demuxer.extractVideoDescription(this.mp4boxFile, track.id);
122
+ }
123
+ /**
124
+ * Extract video description (avcC/hvcC/etc) for VideoDecoder configuration
125
+ * Static method for reuse in other components
126
+ */
127
+ static extractVideoDescription(mp4boxFile, trackId) {
128
+ try {
129
+ const fullTrack = mp4boxFile.getTrackById(trackId);
130
+ for (const entry of fullTrack.mdia.minf.stbl.stsd.entries) {
131
+ const box = entry.avcC ?? entry.hvcC ?? entry.av1C ?? entry.vpcC;
132
+ if (box) {
133
+ const stream = new mp4box_all.DataStream(
134
+ void 0,
135
+ 0,
136
+ mp4box_all.DataStream.BIG_ENDIAN
137
+ // IMPORTANT: must be BIG_ENDIAN
138
+ );
139
+ box.write(stream);
140
+ return new Uint8Array(stream.buffer.slice(8)).buffer;
141
+ }
142
+ }
143
+ } catch (error) {
144
+ console.error("Failed to get video description:", error);
145
+ }
146
+ return void 0;
147
+ }
148
+ // private getVideoDescription(track: any): ArrayBuffer | undefined {
149
+ // if (!this.mp4boxFile) return undefined;
150
+ // return getVideoDescription(this.mp4boxFile, track);
151
+ // }
152
+ getAudioDescription(track) {
153
+ try {
154
+ const fullTrack = this.mp4boxFile.getTrackById(track.id);
155
+ for (const entry of fullTrack.mdia.minf.stbl.stsd.entries) {
156
+ if (entry.esds) {
157
+ const decConfigDesc = entry.esds.esd?.descs?.[0];
158
+ const decSpecInfo = decConfigDesc?.descs?.[0];
159
+ if (decSpecInfo?.data) {
160
+ const data = decSpecInfo.data;
161
+ if (data instanceof Uint8Array) {
162
+ const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
163
+ return buffer instanceof ArrayBuffer ? buffer : void 0;
164
+ } else if (Array.isArray(data)) {
165
+ return new Uint8Array(data).buffer;
166
+ }
167
+ }
168
+ console.warn("[MP4Demuxer] Could not extract AudioSpecificConfig from esds structure");
169
+ return void 0;
170
+ } else if (entry.dOps) {
171
+ const stream = new mp4box_all.DataStream(
172
+ void 0,
173
+ 0,
174
+ mp4box_all.DataStream.BIG_ENDIAN
175
+ );
176
+ entry.dOps.write(stream);
177
+ return new Uint8Array(stream.buffer.slice(8)).buffer;
178
+ }
179
+ }
180
+ } catch (error) {
181
+ console.error("Failed to get audio description:", error);
182
+ }
183
+ return void 0;
184
+ }
185
+ /**
186
+ * Create readable stream for video chunks (output only)
187
+ */
188
+ createVideoStream() {
189
+ return new ReadableStream(
190
+ {
191
+ start: (ctrl) => {
192
+ this.videoController = ctrl;
193
+ },
194
+ cancel: () => {
195
+ this.videoController = void 0;
196
+ }
197
+ },
198
+ {
199
+ highWaterMark: this.demuxHighWaterMark,
200
+ size: () => 1
201
+ }
202
+ );
203
+ }
204
+ /**
205
+ * Create readable stream for audio chunks (output only)
206
+ */
207
+ createAudioStream() {
208
+ if (this.skipAudio) {
209
+ return null;
210
+ }
211
+ return new ReadableStream(
212
+ {
213
+ start: (ctrl) => {
214
+ this.audioController = ctrl;
215
+ },
216
+ cancel: () => {
217
+ this.audioController = void 0;
218
+ }
219
+ },
220
+ {
221
+ highWaterMark: this.demuxHighWaterMark,
222
+ size: () => 1
223
+ }
224
+ );
225
+ }
226
+ /**
227
+ * Create writable stream for input data (single source)
228
+ * This is the only stream that should receive the input data
229
+ */
230
+ createInputStream() {
231
+ return new WritableStream(
232
+ {
233
+ write: (chunk) => {
234
+ const chunkData = new Uint8Array(chunk);
235
+ this.appendBuffer(chunkData);
236
+ this.mp4boxFile.flush();
237
+ },
238
+ close: async () => {
239
+ this.mp4boxFile.flush();
240
+ await new Promise((resolve) => setTimeout(resolve, 100));
241
+ this.videoController?.close();
242
+ this.audioController?.close();
243
+ }
244
+ },
245
+ {
246
+ highWaterMark: this.demuxHighWaterMark
247
+ }
248
+ );
249
+ }
250
+ appendBuffer(chunk) {
251
+ const buffer = chunk.buffer;
252
+ buffer.fileStart = this.fileOffset;
253
+ this.mp4boxFile.appendBuffer(buffer);
254
+ this.fileOffset += chunk.byteLength;
255
+ }
256
+ /**
257
+ * Get video track info if available
258
+ */
259
+ get videoTrackInfo() {
260
+ return Array.from(this.tracks.values()).find((track) => track.type === "video");
261
+ }
262
+ /**
263
+ * Get audio track info if available
264
+ */
265
+ get audioTrackInfo() {
266
+ return Array.from(this.tracks.values()).find((track) => track.type === "audio");
267
+ }
268
+ destroy() {
269
+ this.mp4boxFile?.stop();
270
+ this.mp4boxFile = null;
271
+ this.tracks.clear();
272
+ this.isReady = false;
273
+ this.videoTimestampOffset = null;
274
+ this.audioTimestampOffset = null;
275
+ }
276
+ }
277
+ export {
278
+ MP4Demuxer,
279
+ normalizeAudioCodec
280
+ };
281
+ //# sourceMappingURL=MP4Demuxer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MP4Demuxer.js","sources":["../../../src/stages/demux/MP4Demuxer.ts"],"sourcesContent":["import * as MP4Box from 'mp4box';\nimport type { DemuxConfig, TrackInfo } from './types';\n\n/**\n * Normalize audio codec string for WebCodecs compatibility\n * Fixes incomplete AAC codec strings from mp4box.js\n */\nexport function normalizeAudioCodec(codec?: string): string {\n if (!codec) return 'mp4a.40.2'; // Default AAC-LC\n\n // Incomplete mp4a → complete as AAC-LC\n if (codec === 'mp4a') {\n return 'mp4a.40.2';\n }\n\n // Already complete or other format, return as-is\n return codec;\n}\n\n/**\n * MP4 Demuxer - Extract encoded chunks from MP4 container\n * Simplified implementation following Stream API pattern\n */\nexport class MP4Demuxer {\n private mp4boxFile: any;\n tracks = new Map<number, TrackInfo>();\n isReady = false;\n private videoController?: ReadableStreamDefaultController<EncodedVideoChunk>;\n private audioController?: ReadableStreamDefaultController<EncodedAudioChunk>;\n private demuxHighWaterMark: number;\n private onReadyCallback?: () => void;\n private fileOffset = 0;\n private videoTimestampOffset: number | null = null;\n private audioTimestampOffset: number | null = null;\n private skipAudio: boolean = false;\n\n constructor(config: DemuxConfig & { onReady?: () => void } = {}) {\n this.mp4boxFile = MP4Box.createFile();\n this.onReadyCallback = config.onReady;\n this.skipAudio = config.skipAudio ?? false;\n\n // Use provided config with local default as fallback\n const DEFAULT_HIGH_WATER_MARK = 10; // Default for video chunks\n this.demuxHighWaterMark = config.highWaterMark ?? DEFAULT_HIGH_WATER_MARK;\n\n this.setupHandlers();\n }\n\n updateConfig(config: DemuxConfig): void {\n this.demuxHighWaterMark = config.highWaterMark ?? this.demuxHighWaterMark;\n }\n\n private setupHandlers(): void {\n this.mp4boxFile.onError = (error: string) => {\n console.error('[MP4Demuxer] MP4Box error:', error);\n const err = new Error(error);\n this.videoController?.error(err);\n this.audioController?.error(err);\n };\n\n this.mp4boxFile.onReady = (info: any) => {\n this.processTracks(info.tracks);\n this.isReady = true;\n\n // Call the ready callback before starting\n if (this.onReadyCallback) {\n this.onReadyCallback();\n }\n\n // Start processing samples after callback\n // Note: start() enables extraction, actual samples will be output\n // when flush() is called (by TransformStream's flush callback)\n this.mp4boxFile.start();\n };\n\n this.mp4boxFile.onSamples = (trackId: number, _user: any, samples: any[]) => {\n this.processSamples(trackId, samples);\n };\n }\n\n private processTracks(tracks: any[]): void {\n for (const track of tracks) {\n const trackInfo: TrackInfo = {\n id: track.id,\n type: track.type === 'video' ? 'video' : 'audio',\n codec: track.type === 'audio' ? normalizeAudioCodec(track.codec) : track.codec,\n timescale: track.timescale,\n };\n\n if (track.type === 'video') {\n trackInfo.width = track.video?.width;\n trackInfo.height = track.video?.height;\n trackInfo.description = this.getVideoDescription(track);\n } else if (track.type === 'audio') {\n trackInfo.sampleRate = track.audio?.sample_rate || track.timescale;\n trackInfo.numberOfChannels = track.audio?.channel_count;\n trackInfo.description = this.getAudioDescription(track);\n }\n\n this.tracks.set(track.id, trackInfo);\n\n // Configure extraction - use Infinity to extract all samples\n // Note: onSamples will be called multiple times as data is appended,\n // but we don't need to manually request more batches\n this.mp4boxFile.setExtractionOptions(track.id, track, {\n nbSamples: Infinity,\n });\n }\n }\n\n private processSamples(trackId: number, samples: any[]): void {\n const track = this.tracks.get(trackId);\n if (!track) return;\n\n const timescale = track.timescale || 90000;\n for (const sample of samples) {\n const rawTimestamp = (sample.cts * 1000000) / timescale;\n const duration = (sample.duration * 1000000) / timescale;\n\n if (track.type === 'video') {\n if (!this.videoController) {\n console.error('[MP4Demuxer] videoController is null when trying to output chunk!');\n // Should not happen - stream should be created before appendBuffer\n return;\n }\n\n // Normalize timestamp: first frame starts at 0\n if (this.videoTimestampOffset === null) {\n this.videoTimestampOffset = rawTimestamp;\n }\n const timestamp = rawTimestamp - this.videoTimestampOffset;\n\n const chunk = new EncodedVideoChunk({\n type: sample.is_sync ? 'key' : 'delta',\n timestamp,\n duration,\n data: sample.data,\n });\n this.videoController.enqueue(chunk);\n } else if (track.type === 'audio') {\n // Skip audio processing if skipAudio enabled or no controller\n if (!this.audioController) {\n // Still release samples immediately to avoid memory accumulation in mp4box.js\n const last = samples[samples.length - 1].number;\n this.mp4boxFile.releaseUsedSamples(trackId, last + 1);\n return;\n }\n\n // Normalize timestamp: first frame starts at 0\n if (this.audioTimestampOffset === null) {\n this.audioTimestampOffset = rawTimestamp;\n }\n const timestamp = rawTimestamp - this.audioTimestampOffset;\n\n const chunk = new EncodedAudioChunk({\n type: 'key',\n timestamp,\n duration,\n data: sample.data,\n });\n this.audioController.enqueue(chunk);\n }\n }\n\n const last = samples[samples.length - 1].number;\n // Release memory immediately\n this.mp4boxFile.releaseUsedSamples(trackId, last + 1);\n }\n\n private getVideoDescription(track: any): ArrayBuffer | undefined {\n return MP4Demuxer.extractVideoDescription(this.mp4boxFile, track.id);\n }\n\n /**\n * Extract video description (avcC/hvcC/etc) for VideoDecoder configuration\n * Static method for reuse in other components\n */\n static extractVideoDescription(mp4boxFile: any, trackId: number): ArrayBuffer | undefined {\n try {\n const fullTrack = mp4boxFile.getTrackById(trackId);\n for (const entry of fullTrack.mdia.minf.stbl.stsd.entries) {\n const box = entry.avcC ?? entry.hvcC ?? entry.av1C ?? entry.vpcC;\n if (box) {\n const stream = new (MP4Box as any).DataStream(\n undefined,\n 0,\n (MP4Box as any).DataStream.BIG_ENDIAN // IMPORTANT: must be BIG_ENDIAN\n );\n box.write(stream);\n return new Uint8Array(stream.buffer.slice(8)).buffer;\n }\n }\n } catch (error) {\n console.error('Failed to get video description:', error);\n }\n return undefined;\n }\n\n // private getVideoDescription(track: any): ArrayBuffer | undefined {\n // if (!this.mp4boxFile) return undefined;\n // return getVideoDescription(this.mp4boxFile, track);\n // }\n\n private getAudioDescription(track: any): ArrayBuffer | undefined {\n try {\n const fullTrack = this.mp4boxFile.getTrackById(track.id);\n for (const entry of fullTrack.mdia.minf.stbl.stsd.entries) {\n if (entry.esds) {\n // Use mp4box.js parsed structure to get AudioSpecificConfig\n // esds.esd.descs[0] = DecoderConfigDescriptor\n // esds.esd.descs[0].descs[0].data = DecoderSpecificInfo (AudioSpecificConfig)\n const decConfigDesc = entry.esds.esd?.descs?.[0];\n const decSpecInfo = decConfigDesc?.descs?.[0];\n\n if (decSpecInfo?.data) {\n // Convert Uint8Array to ArrayBuffer\n const data = decSpecInfo.data;\n if (data instanceof Uint8Array) {\n const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);\n return buffer instanceof ArrayBuffer ? buffer : undefined;\n } else if (Array.isArray(data)) {\n // Some versions of mp4box.js return Array instead of Uint8Array\n return new Uint8Array(data).buffer;\n }\n }\n\n console.warn('[MP4Demuxer] Could not extract AudioSpecificConfig from esds structure');\n return undefined;\n } else if (entry.dOps) {\n // For Opus, use the full dOps box\n const stream = new (MP4Box as any).DataStream(\n undefined,\n 0,\n (MP4Box as any).DataStream.BIG_ENDIAN\n );\n entry.dOps.write(stream);\n return new Uint8Array(stream.buffer.slice(8)).buffer;\n }\n }\n } catch (error) {\n console.error('Failed to get audio description:', error);\n }\n return undefined;\n }\n\n /**\n * Create readable stream for video chunks (output only)\n */\n createVideoStream(): ReadableStream<EncodedVideoChunk> {\n return new ReadableStream<EncodedVideoChunk>(\n {\n start: (ctrl) => {\n this.videoController = ctrl as any;\n },\n cancel: () => {\n this.videoController = undefined;\n },\n },\n {\n highWaterMark: this.demuxHighWaterMark,\n size: () => 1,\n }\n );\n }\n\n /**\n * Create readable stream for audio chunks (output only)\n */\n createAudioStream(): ReadableStream<EncodedAudioChunk> | null {\n if (this.skipAudio) {\n return null;\n }\n\n return new ReadableStream<EncodedAudioChunk>(\n {\n start: (ctrl) => {\n this.audioController = ctrl as any;\n },\n cancel: () => {\n this.audioController = undefined;\n },\n },\n {\n highWaterMark: this.demuxHighWaterMark,\n size: () => 1,\n }\n );\n }\n\n /**\n * Create writable stream for input data (single source)\n * This is the only stream that should receive the input data\n */\n createInputStream(): WritableStream<Uint8Array | ArrayBuffer> {\n return new WritableStream<Uint8Array | ArrayBuffer>(\n {\n write: (chunk) => {\n // Create a copy to avoid buffer reuse issues (required for mp4box 0.5.x)\n const chunkData = new Uint8Array(chunk);\n this.appendBuffer(chunkData);\n\n // Flush after each append to trigger sample extraction immediately\n this.mp4boxFile.flush();\n },\n close: async () => {\n // Trigger final MP4Box flush\n this.mp4boxFile.flush();\n\n // Wait for MP4Box to complete sample extraction (onSamples callbacks)\n await new Promise((resolve) => setTimeout(resolve, 100));\n this.videoController?.close();\n this.audioController?.close();\n },\n },\n {\n highWaterMark: this.demuxHighWaterMark,\n }\n );\n }\n\n appendBuffer(chunk: Uint8Array): void {\n const buffer = chunk.buffer as ArrayBuffer & { fileStart: number };\n buffer.fileStart = this.fileOffset;\n this.mp4boxFile.appendBuffer(buffer);\n this.fileOffset += chunk.byteLength;\n }\n\n /**\n * Get video track info if available\n */\n get videoTrackInfo(): TrackInfo | undefined {\n return Array.from(this.tracks.values()).find((track) => track.type === 'video');\n }\n\n /**\n * Get audio track info if available\n */\n get audioTrackInfo(): TrackInfo | undefined {\n return Array.from(this.tracks.values()).find((track) => track.type === 'audio');\n }\n\n destroy(): void {\n this.mp4boxFile?.stop();\n this.mp4boxFile = null;\n this.tracks.clear();\n this.isReady = false;\n this.videoTimestampOffset = null;\n this.audioTimestampOffset = null;\n }\n}\n"],"names":["MP4Box.createFile","last","MP4Box.DataStream"],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ import { MP4Index } from './types';
2
+
3
+ export interface MP4ParseResult {
4
+ index: MP4Index;
5
+ audioSamples?: EncodedAudioChunk[];
6
+ audioMetadata?: AudioDecoderConfig;
7
+ }
8
+ export interface ParseStreamOptions {
9
+ onFirstFrameReady?: (index: MP4Index, firstGOPChunks: EncodedVideoChunk[]) => void;
10
+ }
11
+ /**
12
+ * MP4IndexParser - Parse MP4 moov box and build time→byte index
13
+ *
14
+ * Features:
15
+ * - Stream-based moov parsing (stop after moov found)
16
+ * - Build sample table with byte offsets
17
+ * - Build GOP index for keyframe positions
18
+ * - Extract all audio samples (encoded) for memory caching
19
+ * - Extract first GOP for fast cover rendering
20
+ */
21
+ export declare class MP4IndexParser {
22
+ /**
23
+ * Parse from streaming download
24
+ * Returns video index + all audio samples
25
+ * Can optionally extract first GOP for fast cover rendering
26
+ */
27
+ parseFromStream(stream: ReadableStream<Uint8Array>, options?: ParseStreamOptions): Promise<MP4ParseResult>;
28
+ private processStreamData;
29
+ private prepareBuffer;
30
+ private handleFirstGOPExtraction;
31
+ private setupAudioExtraction;
32
+ /**
33
+ * Parse from file/ArrayBuffer (for already cached resources)
34
+ */
35
+ parseFromFile(data: File | ArrayBuffer): Promise<MP4Index>;
36
+ /**
37
+ * Build MP4Index from mp4box.js parsed data
38
+ */
39
+ private buildIndex;
40
+ /**
41
+ * Build video track index with sample table and GOP boundaries
42
+ */
43
+ private buildVideoTrackIndex;
44
+ /**
45
+ * Build audio track index
46
+ */
47
+ private buildAudioTrackIndex;
48
+ /**
49
+ * Build sample table from mp4box track samples
50
+ *
51
+ * IMPORTANT: Keep samples in DTS (decode) order, not PTS (presentation) order!
52
+ * VideoDecoder requires chunks in decode order. It will output frames in PTS order.
53
+ */
54
+ private buildSampleTable;
55
+ /**
56
+ * Extract video description (avcC/hvcC/etc) for VideoDecoder
57
+ * Reuses MP4Demuxer.extractVideoDescription for consistency
58
+ */
59
+ private getVideoDescription;
60
+ /**
61
+ * Build GOP index from samples
62
+ * GOP = Group of Pictures, starts with a keyframe
63
+ */
64
+ private buildGOPIndex;
65
+ /**
66
+ * Extract first GOP chunks from accumulated buffer
67
+ * Used for fast cover rendering during streaming download
68
+ */
69
+ private extractFirstGOP;
70
+ }
71
+ //# sourceMappingURL=MP4IndexParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MP4IndexParser.d.ts","sourceRoot":"","sources":["../../../src/stages/demux/MP4IndexParser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAiD,MAAM,SAAS,CAAC;AAIvF,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,QAAQ,CAAC;IAChB,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACnC,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,iBAAiB,EAAE,KAAK,IAAI,CAAC;CACpF;AAED;;;;;;;;;GASG;AACH,qBAAa,cAAc;IACzB;;;;OAIG;IACG,eAAe,CACnB,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,cAAc,CAAC;YAuKZ,iBAAiB;IA0C/B,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,wBAAwB;IA6ChC,OAAO,CAAC,oBAAoB;IAmC5B;;OAEG;IACG,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAiChE;;OAEG;IACH,OAAO,CAAC,UAAU;IAoBlB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAiB5B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAwCxB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAsCrB;;;OAGG;IACH,OAAO,CAAC,eAAe;CAkDxB"}