@meframe/core 0.0.29 → 0.0.30

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 (218) hide show
  1. package/dist/Meframe.d.ts +2 -30
  2. package/dist/Meframe.d.ts.map +1 -1
  3. package/dist/Meframe.js +7 -118
  4. package/dist/Meframe.js.map +1 -1
  5. package/dist/cache/CacheManager.d.ts +42 -68
  6. package/dist/cache/CacheManager.d.ts.map +1 -1
  7. package/dist/cache/CacheManager.js +224 -188
  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/L2OPFSStore.d.ts +37 -0
  15. package/dist/cache/l2/L2OPFSStore.d.ts.map +1 -0
  16. package/dist/cache/resource/AudioSampleCache.d.ts +52 -0
  17. package/dist/cache/resource/AudioSampleCache.d.ts.map +1 -0
  18. package/dist/cache/resource/AudioSampleCache.js +69 -0
  19. package/dist/cache/resource/AudioSampleCache.js.map +1 -0
  20. package/dist/cache/resource/ImageBitmapCache.d.ts +65 -0
  21. package/dist/cache/resource/ImageBitmapCache.d.ts.map +1 -0
  22. package/dist/cache/resource/ImageBitmapCache.js +101 -0
  23. package/dist/cache/resource/ImageBitmapCache.js.map +1 -0
  24. package/dist/cache/resource/MP4IndexCache.d.ts +48 -0
  25. package/dist/cache/resource/MP4IndexCache.d.ts.map +1 -0
  26. package/dist/cache/resource/MP4IndexCache.js +104 -0
  27. package/dist/cache/resource/MP4IndexCache.js.map +1 -0
  28. package/dist/cache/resource/ResourceCache.d.ts +46 -0
  29. package/dist/cache/resource/ResourceCache.d.ts.map +1 -0
  30. package/dist/cache/resource/ResourceCache.js +92 -0
  31. package/dist/cache/resource/ResourceCache.js.map +1 -0
  32. package/dist/cache/storage/indexeddb/ChunkRecordStore.d.ts +75 -0
  33. package/dist/cache/storage/indexeddb/ChunkRecordStore.d.ts.map +1 -0
  34. package/dist/cache/storage/opfs/OPFSManager.d.ts +54 -0
  35. package/dist/cache/storage/opfs/OPFSManager.d.ts.map +1 -0
  36. package/dist/cache/storage/opfs/OPFSManager.js +133 -0
  37. package/dist/cache/storage/opfs/OPFSManager.js.map +1 -0
  38. package/dist/cache/storage/opfs/types.d.ts +16 -0
  39. package/dist/cache/storage/opfs/types.d.ts.map +1 -0
  40. package/dist/config/defaults.d.ts.map +1 -1
  41. package/dist/config/defaults.js +21 -2
  42. package/dist/config/defaults.js.map +1 -1
  43. package/dist/config/types.d.ts +28 -0
  44. package/dist/config/types.d.ts.map +1 -1
  45. package/dist/controllers/ExportController.d.ts +16 -0
  46. package/dist/controllers/ExportController.d.ts.map +1 -0
  47. package/dist/controllers/ExportController.js +44 -0
  48. package/dist/controllers/ExportController.js.map +1 -0
  49. package/dist/controllers/PlaybackController.d.ts +28 -4
  50. package/dist/controllers/PlaybackController.d.ts.map +1 -1
  51. package/dist/controllers/PlaybackController.js +115 -51
  52. package/dist/controllers/PlaybackController.js.map +1 -1
  53. package/dist/controllers/index.d.ts +2 -3
  54. package/dist/controllers/index.d.ts.map +1 -1
  55. package/dist/controllers/types.d.ts +0 -28
  56. package/dist/controllers/types.d.ts.map +1 -1
  57. package/dist/event/events.d.ts +8 -0
  58. package/dist/event/events.d.ts.map +1 -1
  59. package/dist/event/events.js +1 -0
  60. package/dist/event/events.js.map +1 -1
  61. package/dist/model/CompositionModel.d.ts.map +1 -1
  62. package/dist/model/CompositionModel.js +11 -6
  63. package/dist/model/CompositionModel.js.map +1 -1
  64. package/dist/model/RcFrame.d.ts +2 -0
  65. package/dist/model/RcFrame.d.ts.map +1 -1
  66. package/dist/model/RcFrame.js +3 -0
  67. package/dist/model/RcFrame.js.map +1 -1
  68. package/dist/model/types.d.ts +0 -1
  69. package/dist/model/types.d.ts.map +1 -1
  70. package/dist/model/types.js.map +1 -1
  71. package/dist/orchestrator/ExportScheduler.d.ts +35 -0
  72. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -0
  73. package/dist/orchestrator/ExportScheduler.js +241 -0
  74. package/dist/orchestrator/ExportScheduler.js.map +1 -0
  75. package/dist/orchestrator/GlobalAudioSession.d.ts +24 -9
  76. package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
  77. package/dist/orchestrator/GlobalAudioSession.js +149 -152
  78. package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
  79. package/dist/orchestrator/OnDemandVideoSession.d.ts +73 -0
  80. package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -0
  81. package/dist/orchestrator/OnDemandVideoSession.js +281 -0
  82. package/dist/orchestrator/OnDemandVideoSession.js.map +1 -0
  83. package/dist/orchestrator/Orchestrator.d.ts +22 -17
  84. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  85. package/dist/orchestrator/Orchestrator.js +244 -312
  86. package/dist/orchestrator/Orchestrator.js.map +1 -1
  87. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  88. package/dist/orchestrator/VideoClipSession.js +3 -15
  89. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  90. package/dist/orchestrator/index.d.ts +0 -1
  91. package/dist/orchestrator/index.d.ts.map +1 -1
  92. package/dist/orchestrator/types.d.ts +4 -4
  93. package/dist/orchestrator/types.d.ts.map +1 -1
  94. package/dist/stages/compose/FilterProcessor.d.ts +1 -1
  95. package/dist/stages/compose/FilterProcessor.d.ts.map +1 -1
  96. package/dist/stages/compose/FilterProcessor.js +226 -0
  97. package/dist/stages/compose/FilterProcessor.js.map +1 -0
  98. package/dist/stages/compose/LayerRenderer.d.ts +1 -1
  99. package/dist/stages/compose/LayerRenderer.d.ts.map +1 -1
  100. package/dist/stages/compose/LayerRenderer.js +270 -0
  101. package/dist/stages/compose/LayerRenderer.js.map +1 -0
  102. package/dist/stages/compose/TransitionProcessor.d.ts +1 -1
  103. package/dist/stages/compose/TransitionProcessor.d.ts.map +1 -1
  104. package/dist/stages/compose/TransitionProcessor.js +189 -0
  105. package/dist/stages/compose/TransitionProcessor.js.map +1 -0
  106. package/dist/stages/compose/VideoComposer.d.ts +4 -2
  107. package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
  108. package/dist/stages/compose/VideoComposer.js +229 -0
  109. package/dist/stages/compose/VideoComposer.js.map +1 -0
  110. package/dist/stages/compose/text-renderers/animation-utils.js +76 -0
  111. package/dist/stages/compose/text-renderers/animation-utils.js.map +1 -0
  112. package/dist/stages/compose/text-renderers/basic-text-renderer.d.ts +2 -2
  113. package/dist/stages/compose/text-renderers/basic-text-renderer.d.ts.map +1 -1
  114. package/dist/stages/compose/text-renderers/basic-text-renderer.js +93 -0
  115. package/dist/stages/compose/text-renderers/basic-text-renderer.js.map +1 -0
  116. package/dist/stages/compose/text-renderers/character-ktv-renderer.d.ts +1 -1
  117. package/dist/stages/compose/text-renderers/character-ktv-renderer.d.ts.map +1 -1
  118. package/dist/stages/compose/text-renderers/character-ktv-renderer.js +132 -0
  119. package/dist/stages/compose/text-renderers/character-ktv-renderer.js.map +1 -0
  120. package/dist/stages/compose/text-renderers/word-by-word-renderer.d.ts +1 -1
  121. package/dist/stages/compose/text-renderers/word-by-word-renderer.d.ts.map +1 -1
  122. package/dist/stages/compose/text-renderers/word-by-word-renderer.js +128 -0
  123. package/dist/stages/compose/text-renderers/word-by-word-renderer.js.map +1 -0
  124. package/dist/stages/compose/text-renderers/word-fancy-renderer.d.ts +1 -1
  125. package/dist/stages/compose/text-renderers/word-fancy-renderer.d.ts.map +1 -1
  126. package/dist/stages/compose/text-renderers/word-fancy-renderer.js +135 -0
  127. package/dist/stages/compose/text-renderers/word-fancy-renderer.js.map +1 -0
  128. package/dist/stages/compose/text-utils/locale-detector.js +16 -0
  129. package/dist/stages/compose/text-utils/locale-detector.js.map +1 -0
  130. package/dist/stages/compose/text-utils/text-metrics.js +21 -0
  131. package/dist/stages/compose/text-utils/text-metrics.js.map +1 -0
  132. package/dist/stages/compose/text-utils/text-wrapper.js +225 -0
  133. package/dist/stages/compose/text-utils/text-wrapper.js.map +1 -0
  134. package/dist/stages/compose/types.d.ts +2 -1
  135. package/dist/stages/compose/types.d.ts.map +1 -1
  136. package/dist/stages/decode/BaseDecoder.js +0 -3
  137. package/dist/stages/decode/BaseDecoder.js.map +1 -1
  138. package/dist/stages/demux/MP4Demuxer.d.ts +5 -0
  139. package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
  140. package/dist/stages/demux/MP4Demuxer.js +281 -0
  141. package/dist/stages/demux/MP4Demuxer.js.map +1 -0
  142. package/dist/stages/demux/MP4IndexParser.d.ts +71 -0
  143. package/dist/stages/demux/MP4IndexParser.d.ts.map +1 -0
  144. package/dist/stages/demux/MP4IndexParser.js +416 -0
  145. package/dist/stages/demux/MP4IndexParser.js.map +1 -0
  146. package/dist/stages/demux/types.d.ts +48 -0
  147. package/dist/stages/demux/types.d.ts.map +1 -1
  148. package/dist/stages/load/ResourceLoader.d.ts +50 -15
  149. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  150. package/dist/stages/load/ResourceLoader.js +297 -80
  151. package/dist/stages/load/ResourceLoader.js.map +1 -1
  152. package/dist/stages/load/TaskManager.d.ts +6 -2
  153. package/dist/stages/load/TaskManager.d.ts.map +1 -1
  154. package/dist/stages/load/TaskManager.js +27 -4
  155. package/dist/stages/load/TaskManager.js.map +1 -1
  156. package/dist/stages/load/index.d.ts +0 -1
  157. package/dist/stages/load/index.d.ts.map +1 -1
  158. package/dist/stages/load/types.d.ts +9 -9
  159. package/dist/stages/load/types.d.ts.map +1 -1
  160. package/dist/stages/mux/MP4Muxer.d.ts +2 -2
  161. package/dist/stages/mux/MP4Muxer.d.ts.map +1 -1
  162. package/dist/stages/mux/MP4Muxer.js +24 -13
  163. package/dist/stages/mux/MP4Muxer.js.map +1 -1
  164. package/dist/stages/mux/MuxManager.d.ts +10 -21
  165. package/dist/stages/mux/MuxManager.d.ts.map +1 -1
  166. package/dist/stages/mux/MuxManager.js +21 -162
  167. package/dist/stages/mux/MuxManager.js.map +1 -1
  168. package/dist/stages/mux/index.d.ts +0 -1
  169. package/dist/stages/mux/index.d.ts.map +1 -1
  170. package/dist/utils/binary-search.d.ts +12 -4
  171. package/dist/utils/binary-search.d.ts.map +1 -1
  172. package/dist/utils/binary-search.js +52 -6
  173. package/dist/utils/binary-search.js.map +1 -1
  174. package/dist/workers/{BaseDecoder.BWYu1W0B.js → BaseDecoder.CTW-vr29.js} +1 -4
  175. package/dist/workers/BaseDecoder.CTW-vr29.js.map +1 -0
  176. package/dist/workers/{MP4Demuxer.lMOUMWFh.js → MP4Demuxer.BEa6PLJm.js} +9 -2
  177. package/dist/workers/{MP4Demuxer.lMOUMWFh.js.map → MP4Demuxer.BEa6PLJm.js.map} +1 -1
  178. package/dist/workers/stages/compose/{video-compose.worker.CIeEIJO7.js → video-compose.worker.DHQ8B105.js} +59 -31
  179. package/dist/workers/stages/compose/video-compose.worker.DHQ8B105.js.map +1 -0
  180. package/dist/workers/stages/decode/{audio-decode.worker.DnS17GD9.js → audio-decode.worker.CP8bXXa4.js} +2 -2
  181. package/dist/workers/stages/decode/{audio-decode.worker.DnS17GD9.js.map → audio-decode.worker.CP8bXXa4.js.map} +1 -1
  182. package/dist/workers/stages/decode/{video-decode.worker.BEYsjOXp.js → video-decode.worker.BIspTxgV.js} +2 -2
  183. package/dist/workers/stages/decode/{video-decode.worker.BEYsjOXp.js.map → video-decode.worker.BIspTxgV.js.map} +1 -1
  184. package/dist/workers/stages/demux/{audio-demux.worker.DcurGC8i.js → audio-demux.worker._VRQdLdv.js} +2 -2
  185. package/dist/workers/stages/demux/{audio-demux.worker.DcurGC8i.js.map → audio-demux.worker._VRQdLdv.js.map} +1 -1
  186. package/dist/workers/stages/demux/{video-demux.worker.B1_wntU4.js → video-demux.worker.CSkxGtmx.js} +3 -19
  187. package/dist/workers/stages/demux/video-demux.worker.CSkxGtmx.js.map +1 -0
  188. package/dist/workers/worker-manifest.json +5 -5
  189. package/package.json +1 -1
  190. package/dist/cache/l2/IndexedDBStore.js +0 -180
  191. package/dist/cache/l2/IndexedDBStore.js.map +0 -1
  192. package/dist/cache/l2/L2Cache.js +0 -329
  193. package/dist/cache/l2/L2Cache.js.map +0 -1
  194. package/dist/cache/l2/OPFSStore.js +0 -131
  195. package/dist/cache/l2/OPFSStore.js.map +0 -1
  196. package/dist/controllers/PreRenderService.d.ts +0 -59
  197. package/dist/controllers/PreRenderService.d.ts.map +0 -1
  198. package/dist/controllers/PreRenderService.js +0 -185
  199. package/dist/controllers/PreRenderService.js.map +0 -1
  200. package/dist/controllers/PreRenderTaskQueue.d.ts +0 -21
  201. package/dist/controllers/PreRenderTaskQueue.d.ts.map +0 -1
  202. package/dist/orchestrator/ClipSessionManager.d.ts +0 -70
  203. package/dist/orchestrator/ClipSessionManager.d.ts.map +0 -1
  204. package/dist/orchestrator/ClipSessionManager.js +0 -158
  205. package/dist/orchestrator/ClipSessionManager.js.map +0 -1
  206. package/dist/stages/decode/AudioChunkDecoder.js +0 -169
  207. package/dist/stages/decode/AudioChunkDecoder.js.map +0 -1
  208. package/dist/stages/load/EventHandlers.d.ts +0 -26
  209. package/dist/stages/load/EventHandlers.d.ts.map +0 -1
  210. package/dist/stages/load/EventHandlers.js +0 -42
  211. package/dist/stages/load/EventHandlers.js.map +0 -1
  212. package/dist/stages/mux/OPFSWriter.d.ts +0 -46
  213. package/dist/stages/mux/OPFSWriter.d.ts.map +0 -1
  214. package/dist/utils/BackpressureAdapter.d.ts +0 -26
  215. package/dist/utils/BackpressureAdapter.d.ts.map +0 -1
  216. package/dist/workers/BaseDecoder.BWYu1W0B.js.map +0 -1
  217. package/dist/workers/stages/compose/video-compose.worker.CIeEIJO7.js.map +0 -1
  218. package/dist/workers/stages/demux/video-demux.worker.B1_wntU4.js.map +0 -1
@@ -1,24 +1,76 @@
1
+ import { binarySearchNearest, binarySearchFirst } from "../../utils/binary-search.js";
1
2
  import { RcFrame } from "../../model/RcFrame.js";
2
3
  const BYTES_PER_MB = 1024 * 1024;
3
4
  class VideoL1Cache {
4
5
  framesByClip = /* @__PURE__ */ new Map();
5
6
  maxMemoryBytes;
6
7
  currentBytes = 0;
8
+ windowCenter = 0;
9
+ windowSize = 35e5;
10
+ // ±3.5s
11
+ lastEvictTime = 0;
12
+ EVICT_THROTTLE_MS = 500;
7
13
  constructor(config) {
8
14
  this.maxMemoryBytes = config.maxMemoryMB * BYTES_PER_MB;
9
15
  }
16
+ /**
17
+ * Set window center and trigger eviction
18
+ * Window management helps limit memory usage for long clips
19
+ */
20
+ setWindow(centerTimeUs) {
21
+ this.windowCenter = centerTimeUs;
22
+ const now = Date.now();
23
+ if (now - this.lastEvictTime > this.EVICT_THROTTLE_MS) {
24
+ this.evictOutOfWindow();
25
+ this.lastEvictTime = now;
26
+ }
27
+ }
28
+ /**
29
+ * Evict frames outside the window
30
+ * Essential for long clips to limit memory usage to window size (±3s)
31
+ */
32
+ evictOutOfWindow() {
33
+ const minTime = Math.max(0, this.windowCenter - this.windowSize);
34
+ const maxTime = this.windowCenter + this.windowSize;
35
+ for (const [clipId, frames] of this.framesByClip) {
36
+ const toEvict = [];
37
+ const toKeep = [];
38
+ for (const frame of frames) {
39
+ const globalTime = frame.globalTimeUs;
40
+ if (globalTime === void 0) {
41
+ toKeep.push(frame);
42
+ continue;
43
+ }
44
+ if (globalTime < minTime || globalTime > maxTime) {
45
+ toEvict.push(frame);
46
+ } else {
47
+ toKeep.push(frame);
48
+ }
49
+ }
50
+ if (toEvict.length > 0) {
51
+ for (const frame of toEvict) {
52
+ this.currentBytes -= frame.sizeEstimate;
53
+ frame.close();
54
+ }
55
+ this.framesByClip.set(clipId, toKeep);
56
+ }
57
+ }
58
+ }
10
59
  get(timeUs, clipId) {
11
60
  const frames = this.framesByClip.get(clipId);
12
- if (!frames || frames.length === 0) return null;
13
- const index = this.binarySearchFrame(frames, timeUs);
14
- return index !== -1 ? frames[index] ?? null : null;
61
+ if (!frames || frames.length === 0) {
62
+ return null;
63
+ }
64
+ return binarySearchNearest(frames, timeUs, (frame) => frame.timestampUs ?? 0);
15
65
  }
16
- addFrame(frame, clipId, frameDuration, trackId) {
66
+ addFrame(frame, clipId, frameDuration, trackId, globalTimeUs) {
17
67
  const rcFrame = RcFrame.wrap(frame, {
18
68
  trackId,
19
69
  clipId,
20
70
  timestampUs: frame.timestamp ?? 0,
21
- durationUs: frame.duration ?? frameDuration
71
+ durationUs: frame.duration ?? frameDuration,
72
+ globalTimeUs
73
+ // Store global timestamp for window eviction
22
74
  });
23
75
  let frames = this.framesByClip.get(clipId);
24
76
  if (!frames) {
@@ -86,40 +138,8 @@ class VideoL1Cache {
86
138
  clipCount: this.framesByClip.size
87
139
  };
88
140
  }
89
- binarySearchFrame(frames, timeUs) {
90
- let low = 0;
91
- let high = frames.length - 1;
92
- let result = -1;
93
- while (low <= high) {
94
- const mid = Math.floor((low + high) / 2);
95
- const frame = frames[mid];
96
- if (!frame) break;
97
- const frameTime = frame.timestampUs ?? 0;
98
- const frameDuration = frame.durationUs ?? 0;
99
- if (timeUs < frameTime) {
100
- high = mid - 1;
101
- } else if (timeUs >= frameTime + frameDuration) {
102
- low = mid + 1;
103
- } else {
104
- result = mid;
105
- break;
106
- }
107
- }
108
- return result;
109
- }
110
141
  findInsertIndex(frames, timestamp) {
111
- let low = 0;
112
- let high = frames.length;
113
- while (low < high) {
114
- const mid = Math.floor((low + high) / 2);
115
- const midTs = frames[mid]?.timestampUs ?? 0;
116
- if (midTs < timestamp) {
117
- low = mid + 1;
118
- } else {
119
- high = mid;
120
- }
121
- }
122
- return low;
142
+ return binarySearchFirst(frames, (frame) => (frame.timestampUs ?? 0) >= timestamp);
123
143
  }
124
144
  }
125
145
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"VideoL1Cache.js","sources":["../../../src/cache/l1/VideoL1Cache.ts"],"sourcesContent":["import type { TimeUs } from '../../model/types';\nimport { RcFrame } from '../../model';\n\nconst BYTES_PER_MB = 1024 * 1024;\n\ninterface L1Config {\n maxMemoryMB: number;\n}\n\nexport interface L1CacheMetadata {\n size: number;\n maxSize: number;\n entries: number;\n clipCount: number;\n}\n\n/**\n * Simplified VideoL1Cache - Single-layer frame storage per clip\n *\n * Each clip stores frames in a sorted array by timestamp.\n * No GOP management - frames are directly stored and retrieved via binary search.\n */\nexport class VideoL1Cache {\n private readonly framesByClip = new Map<string, RcFrame[]>();\n private readonly maxMemoryBytes: number;\n private currentBytes = 0;\n\n constructor(config: L1Config) {\n this.maxMemoryBytes = config.maxMemoryMB * BYTES_PER_MB;\n }\n\n get(timeUs: TimeUs, clipId: string): RcFrame | null {\n const frames = this.framesByClip.get(clipId);\n if (!frames || frames.length === 0) return null;\n\n // Binary search for frame at timeUs\n const index = this.binarySearchFrame(frames, timeUs);\n return index !== -1 ? (frames[index] ?? null) : null;\n }\n\n addFrame(frame: VideoFrame, clipId: string, frameDuration: TimeUs, trackId: string): RcFrame {\n const rcFrame = RcFrame.wrap(frame, {\n trackId,\n clipId,\n timestampUs: frame.timestamp ?? 0,\n durationUs: frame.duration ?? frameDuration,\n });\n\n let frames = this.framesByClip.get(clipId);\n if (!frames) {\n frames = [];\n this.framesByClip.set(clipId, frames);\n }\n\n const timestamp = rcFrame.timestampUs ?? 0;\n const insertIndex = this.findInsertIndex(frames, timestamp);\n\n // Check for duplicate timestamp\n if (insertIndex < frames.length && (frames[insertIndex]?.timestampUs ?? 0) === timestamp) {\n const oldFrame = frames[insertIndex];\n frames[insertIndex] = rcFrame;\n this.currentBytes -= oldFrame?.sizeEstimate ?? 0;\n oldFrame?.close?.();\n } else {\n frames.splice(insertIndex, 0, rcFrame);\n }\n\n this.currentBytes += rcFrame.sizeEstimate;\n return rcFrame;\n }\n\n invalidateClip(clipId: string): void {\n const frames = this.framesByClip.get(clipId);\n if (!frames) return;\n\n for (const frame of frames) {\n this.currentBytes -= frame.sizeEstimate;\n frame?.close?.();\n }\n\n this.framesByClip.delete(clipId);\n }\n\n hasClip(clipId: string): boolean {\n const frames = this.framesByClip.get(clipId);\n return !!frames && frames.length > 0;\n }\n\n getClipFrameCount(clipId: string, startTimeUs?: TimeUs): number {\n const frames = this.framesByClip.get(clipId);\n if (!frames) return 0;\n\n if (startTimeUs === undefined) {\n return frames.length;\n }\n\n // Count frames >= startTimeUs\n let count = 0;\n for (const frame of frames) {\n if ((frame.timestampUs ?? 0) >= startTimeUs) {\n count++;\n }\n }\n return count;\n }\n\n clear(): void {\n for (const frames of this.framesByClip.values()) {\n for (const frame of frames) {\n frame?.close?.();\n }\n }\n this.framesByClip.clear();\n this.currentBytes = 0;\n }\n\n getMetadata(): L1CacheMetadata {\n let totalFrames = 0;\n for (const frames of this.framesByClip.values()) {\n totalFrames += frames.length;\n }\n\n return {\n size: this.currentBytes,\n maxSize: this.maxMemoryBytes,\n entries: totalFrames,\n clipCount: this.framesByClip.size,\n };\n }\n\n private binarySearchFrame(frames: RcFrame[], timeUs: TimeUs): number {\n let low = 0;\n let high = frames.length - 1;\n let result = -1;\n\n while (low <= high) {\n const mid = Math.floor((low + high) / 2);\n const frame = frames[mid];\n if (!frame) break;\n\n const frameTime = frame.timestampUs ?? 0;\n const frameDuration = frame.durationUs ?? 0;\n\n if (timeUs < frameTime) {\n high = mid - 1;\n } else if (timeUs >= frameTime + frameDuration) {\n low = mid + 1;\n } else {\n result = mid;\n break;\n }\n }\n\n return result;\n }\n\n private findInsertIndex(frames: RcFrame[], timestamp: TimeUs): number {\n let low = 0;\n let high = frames.length;\n\n while (low < high) {\n const mid = Math.floor((low + high) / 2);\n const midTs = frames[mid]?.timestampUs ?? 0;\n\n if (midTs < timestamp) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n\n return low;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"VideoL1Cache.js","sources":["../../../src/cache/l1/VideoL1Cache.ts"],"sourcesContent":["import type { TimeUs } from '../../model/types';\nimport { RcFrame } from '../../model';\nimport { binarySearchNearest, binarySearchFirst } from '../../utils/binary-search';\n\nconst BYTES_PER_MB = 1024 * 1024;\n\ninterface L1Config {\n maxMemoryMB: number;\n}\n\nexport interface L1CacheMetadata {\n size: number;\n maxSize: number;\n entries: number;\n clipCount: number;\n}\n\n/**\n * Simplified VideoL1Cache - Single-layer frame storage per clip\n *\n * Each clip stores frames in a sorted array by timestamp.\n * No GOP management - frames are directly stored and retrieved via binary search.\n */\nexport class VideoL1Cache {\n private readonly framesByClip = new Map<string, RcFrame[]>();\n private readonly maxMemoryBytes: number;\n private currentBytes = 0;\n private windowCenter: TimeUs = 0;\n private windowSize: TimeUs = 3_500_000; // ±3.5s\n private lastEvictTime = 0;\n private readonly EVICT_THROTTLE_MS = 500;\n\n constructor(config: L1Config) {\n this.maxMemoryBytes = config.maxMemoryMB * BYTES_PER_MB;\n }\n\n /**\n * Set window center and trigger eviction\n * Window management helps limit memory usage for long clips\n */\n setWindow(centerTimeUs: TimeUs): void {\n this.windowCenter = centerTimeUs;\n\n // Throttle eviction to avoid excessive processing\n const now = Date.now();\n if (now - this.lastEvictTime > this.EVICT_THROTTLE_MS) {\n this.evictOutOfWindow();\n this.lastEvictTime = now;\n }\n }\n\n /**\n * Evict frames outside the window\n * Essential for long clips to limit memory usage to window size (±3s)\n */\n private evictOutOfWindow(): void {\n const minTime = Math.max(0, this.windowCenter - this.windowSize);\n const maxTime = this.windowCenter + this.windowSize;\n\n for (const [clipId, frames] of this.framesByClip) {\n const toEvict: RcFrame[] = [];\n const toKeep: RcFrame[] = [];\n\n for (const frame of frames) {\n const globalTime = frame.globalTimeUs;\n\n // Frames without globalTimeUs (legacy) are kept (will be evicted by clip-level logic)\n if (globalTime === undefined) {\n toKeep.push(frame);\n continue;\n }\n\n // Evict frames outside window\n if (globalTime < minTime || globalTime > maxTime) {\n // console.log('[VideoL1Cache] Evicting frame outside window:', {\n // globalTime,\n // windowCenter: this.windowCenter,\n // windowSize: this.windowSize,\n // minTime,\n // maxTime,\n // });\n toEvict.push(frame);\n } else {\n toKeep.push(frame);\n }\n }\n\n // Release evicted frames\n if (toEvict.length > 0) {\n for (const frame of toEvict) {\n this.currentBytes -= frame.sizeEstimate;\n frame.close();\n }\n\n this.framesByClip.set(clipId, toKeep);\n }\n }\n }\n\n get(timeUs: TimeUs, clipId: string): RcFrame | null {\n const frames = this.framesByClip.get(clipId);\n if (!frames || frames.length === 0) {\n return null;\n }\n\n // Use nearest frame search (instead of exact range match)\n return binarySearchNearest(frames, timeUs, (frame) => frame.timestampUs ?? 0);\n }\n\n addFrame(\n frame: VideoFrame,\n clipId: string,\n frameDuration: TimeUs,\n trackId: string,\n globalTimeUs?: TimeUs\n ): RcFrame {\n const rcFrame = RcFrame.wrap(frame, {\n trackId,\n clipId,\n timestampUs: frame.timestamp ?? 0,\n durationUs: frame.duration ?? frameDuration,\n globalTimeUs, // Store global timestamp for window eviction\n });\n\n let frames = this.framesByClip.get(clipId);\n if (!frames) {\n frames = [];\n this.framesByClip.set(clipId, frames);\n }\n\n const timestamp = rcFrame.timestampUs ?? 0;\n const insertIndex = this.findInsertIndex(frames, timestamp);\n\n // Check for duplicate timestamp\n if (insertIndex < frames.length && (frames[insertIndex]?.timestampUs ?? 0) === timestamp) {\n const oldFrame = frames[insertIndex];\n frames[insertIndex] = rcFrame;\n this.currentBytes -= oldFrame?.sizeEstimate ?? 0;\n oldFrame?.close?.();\n } else {\n frames.splice(insertIndex, 0, rcFrame);\n }\n\n this.currentBytes += rcFrame.sizeEstimate;\n return rcFrame;\n }\n\n invalidateClip(clipId: string): void {\n const frames = this.framesByClip.get(clipId);\n if (!frames) return;\n\n for (const frame of frames) {\n this.currentBytes -= frame.sizeEstimate;\n frame?.close?.();\n }\n\n this.framesByClip.delete(clipId);\n }\n\n hasClip(clipId: string): boolean {\n const frames = this.framesByClip.get(clipId);\n return !!frames && frames.length > 0;\n }\n\n getClipFrameCount(clipId: string, startTimeUs?: TimeUs): number {\n const frames = this.framesByClip.get(clipId);\n if (!frames) return 0;\n\n if (startTimeUs === undefined) {\n return frames.length;\n }\n\n // Count frames >= startTimeUs\n let count = 0;\n for (const frame of frames) {\n if ((frame.timestampUs ?? 0) >= startTimeUs) {\n count++;\n }\n }\n return count;\n }\n\n clear(): void {\n for (const frames of this.framesByClip.values()) {\n for (const frame of frames) {\n frame?.close?.();\n }\n }\n this.framesByClip.clear();\n this.currentBytes = 0;\n }\n\n getMetadata(): L1CacheMetadata {\n let totalFrames = 0;\n for (const frames of this.framesByClip.values()) {\n totalFrames += frames.length;\n }\n\n return {\n size: this.currentBytes,\n maxSize: this.maxMemoryBytes,\n entries: totalFrames,\n clipCount: this.framesByClip.size,\n };\n }\n\n private findInsertIndex(frames: RcFrame[], timestamp: TimeUs): number {\n return binarySearchFirst(frames, (frame) => (frame.timestampUs ?? 0) >= timestamp);\n }\n}\n"],"names":[],"mappings":";;AAIA,MAAM,eAAe,OAAO;AAmBrB,MAAM,aAAa;AAAA,EACP,mCAAmB,IAAA;AAAA,EACnB;AAAA,EACT,eAAe;AAAA,EACf,eAAuB;AAAA,EACvB,aAAqB;AAAA;AAAA,EACrB,gBAAgB;AAAA,EACP,oBAAoB;AAAA,EAErC,YAAY,QAAkB;AAC5B,SAAK,iBAAiB,OAAO,cAAc;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,cAA4B;AACpC,SAAK,eAAe;AAGpB,UAAM,MAAM,KAAK,IAAA;AACjB,QAAI,MAAM,KAAK,gBAAgB,KAAK,mBAAmB;AACrD,WAAK,iBAAA;AACL,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,mBAAyB;AAC/B,UAAM,UAAU,KAAK,IAAI,GAAG,KAAK,eAAe,KAAK,UAAU;AAC/D,UAAM,UAAU,KAAK,eAAe,KAAK;AAEzC,eAAW,CAAC,QAAQ,MAAM,KAAK,KAAK,cAAc;AAChD,YAAM,UAAqB,CAAA;AAC3B,YAAM,SAAoB,CAAA;AAE1B,iBAAW,SAAS,QAAQ;AAC1B,cAAM,aAAa,MAAM;AAGzB,YAAI,eAAe,QAAW;AAC5B,iBAAO,KAAK,KAAK;AACjB;AAAA,QACF;AAGA,YAAI,aAAa,WAAW,aAAa,SAAS;AAQhD,kBAAQ,KAAK,KAAK;AAAA,QACpB,OAAO;AACL,iBAAO,KAAK,KAAK;AAAA,QACnB;AAAA,MACF;AAGA,UAAI,QAAQ,SAAS,GAAG;AACtB,mBAAW,SAAS,SAAS;AAC3B,eAAK,gBAAgB,MAAM;AAC3B,gBAAM,MAAA;AAAA,QACR;AAEA,aAAK,aAAa,IAAI,QAAQ,MAAM;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI,QAAgB,QAAgC;AAClD,UAAM,SAAS,KAAK,aAAa,IAAI,MAAM;AAC3C,QAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,aAAO;AAAA,IACT;AAGA,WAAO,oBAAoB,QAAQ,QAAQ,CAAC,UAAU,MAAM,eAAe,CAAC;AAAA,EAC9E;AAAA,EAEA,SACE,OACA,QACA,eACA,SACA,cACS;AACT,UAAM,UAAU,QAAQ,KAAK,OAAO;AAAA,MAClC;AAAA,MACA;AAAA,MACA,aAAa,MAAM,aAAa;AAAA,MAChC,YAAY,MAAM,YAAY;AAAA,MAC9B;AAAA;AAAA,IAAA,CACD;AAED,QAAI,SAAS,KAAK,aAAa,IAAI,MAAM;AACzC,QAAI,CAAC,QAAQ;AACX,eAAS,CAAA;AACT,WAAK,aAAa,IAAI,QAAQ,MAAM;AAAA,IACtC;AAEA,UAAM,YAAY,QAAQ,eAAe;AACzC,UAAM,cAAc,KAAK,gBAAgB,QAAQ,SAAS;AAG1D,QAAI,cAAc,OAAO,WAAW,OAAO,WAAW,GAAG,eAAe,OAAO,WAAW;AACxF,YAAM,WAAW,OAAO,WAAW;AACnC,aAAO,WAAW,IAAI;AACtB,WAAK,gBAAgB,UAAU,gBAAgB;AAC/C,gBAAU,QAAA;AAAA,IACZ,OAAO;AACL,aAAO,OAAO,aAAa,GAAG,OAAO;AAAA,IACvC;AAEA,SAAK,gBAAgB,QAAQ;AAC7B,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,QAAsB;AACnC,UAAM,SAAS,KAAK,aAAa,IAAI,MAAM;AAC3C,QAAI,CAAC,OAAQ;AAEb,eAAW,SAAS,QAAQ;AAC1B,WAAK,gBAAgB,MAAM;AAC3B,aAAO,QAAA;AAAA,IACT;AAEA,SAAK,aAAa,OAAO,MAAM;AAAA,EACjC;AAAA,EAEA,QAAQ,QAAyB;AAC/B,UAAM,SAAS,KAAK,aAAa,IAAI,MAAM;AAC3C,WAAO,CAAC,CAAC,UAAU,OAAO,SAAS;AAAA,EACrC;AAAA,EAEA,kBAAkB,QAAgB,aAA8B;AAC9D,UAAM,SAAS,KAAK,aAAa,IAAI,MAAM;AAC3C,QAAI,CAAC,OAAQ,QAAO;AAEpB,QAAI,gBAAgB,QAAW;AAC7B,aAAO,OAAO;AAAA,IAChB;AAGA,QAAI,QAAQ;AACZ,eAAW,SAAS,QAAQ;AAC1B,WAAK,MAAM,eAAe,MAAM,aAAa;AAC3C;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,QAAc;AACZ,eAAW,UAAU,KAAK,aAAa,OAAA,GAAU;AAC/C,iBAAW,SAAS,QAAQ;AAC1B,eAAO,QAAA;AAAA,MACT;AAAA,IACF;AACA,SAAK,aAAa,MAAA;AAClB,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,cAA+B;AAC7B,QAAI,cAAc;AAClB,eAAW,UAAU,KAAK,aAAa,OAAA,GAAU;AAC/C,qBAAe,OAAO;AAAA,IACxB;AAEA,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,SAAS;AAAA,MACT,WAAW,KAAK,aAAa;AAAA,IAAA;AAAA,EAEjC;AAAA,EAEQ,gBAAgB,QAAmB,WAA2B;AACpE,WAAO,kBAAkB,QAAQ,CAAC,WAAW,MAAM,eAAe,MAAM,SAAS;AAAA,EACnF;AACF;"}
@@ -1 +1 @@
1
- {"version":3,"file":"L2Cache.d.ts","sourceRoot":"","sources":["../../../src/cache/l2/L2Cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"L2Cache.d.ts","sourceRoot":"","sources":["../../../src/cache/l2/L2Cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { ChunkBatch } from '../storage/opfs/types';
2
+
3
+ /**
4
+ * L2-specific OPFS wrapper
5
+ * Adapts OPFSManager for L2 cache chunk operations
6
+ */
7
+ export declare class L2OPFSStore {
8
+ private opfsManager;
9
+ constructor();
10
+ init(): Promise<void>;
11
+ /**
12
+ * Read chunk data from OPFS file
13
+ */
14
+ read(fileName: string, batch: ChunkBatch, projectId: string): Promise<ArrayBuffer | null>;
15
+ /**
16
+ * Append chunks to OPFS file (or create new file)
17
+ */
18
+ append(fileName: string, chunks: Array<EncodedVideoChunk | EncodedAudioChunk>, existingBatches: ChunkBatch[] | undefined, projectId: string): Promise<ChunkBatch[]>;
19
+ /**
20
+ * Delete a file from OPFS
21
+ */
22
+ deleteFile(fileName: string, projectId: string): Promise<void>;
23
+ /**
24
+ * Delete entire project directory
25
+ */
26
+ deleteProjectDirectory(projectId: string): Promise<void>;
27
+ /**
28
+ * Check if a file exists in OPFS
29
+ */
30
+ fileExists(fileName: string, projectId: string): Promise<boolean>;
31
+ /**
32
+ * Clear all OPFS data for current project
33
+ */
34
+ clear(projectId: string): Promise<void>;
35
+ private chunkToArrayBuffer;
36
+ }
37
+ //# sourceMappingURL=L2OPFSStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"L2OPFSStore.d.ts","sourceRoot":"","sources":["../../../src/cache/l2/L2OPFSStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,WAAW,CAAc;;IAM3B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAe/F;;OAEG;IACG,MAAM,CACV,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,KAAK,CAAC,iBAAiB,GAAG,iBAAiB,CAAC,EACpD,eAAe,EAAE,UAAU,EAAE,GAAG,SAAS,EACzC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,UAAU,EAAE,CAAC;IAkCxB;;OAEG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE;;OAEG;IACG,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9D;;OAEG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIvE;;OAEG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAI/B,kBAAkB;CAOjC"}
@@ -0,0 +1,52 @@
1
+ import { TimeUs } from '../../model/types';
2
+
3
+ export interface AudioSampleRecord {
4
+ resourceId: string;
5
+ samples: EncodedAudioChunk[];
6
+ metadata: AudioDecoderConfig;
7
+ totalBytes: number;
8
+ durationUs: TimeUs;
9
+ }
10
+ /**
11
+ * AudioSampleCache - Resource-level audio sample cache
12
+ *
13
+ * Caches all encoded audio samples from MP4 in memory
14
+ * Audio data is small (~100KB for 30s AAC, ~6MB for 30min)
15
+ *
16
+ * Strategy:
17
+ * - Extract all audio samples during moov parsing (one-time cost)
18
+ * - Keep encoded chunks in memory (compressed)
19
+ * - Decode to PCM on-demand when clip is played
20
+ */
21
+ export declare class AudioSampleCache {
22
+ private cache;
23
+ /**
24
+ * Set audio samples for a resource
25
+ */
26
+ set(resourceId: string, samples: EncodedAudioChunk[], metadata: AudioDecoderConfig): void;
27
+ /**
28
+ * Get audio record for a resource
29
+ */
30
+ get(resourceId: string): AudioSampleRecord | null;
31
+ /**
32
+ * Check if resource has audio samples
33
+ */
34
+ has(resourceId: string): boolean;
35
+ /**
36
+ * Get samples in time range (for clip trimming)
37
+ */
38
+ getSamplesInRange(resourceId: string, startUs: TimeUs, endUs: TimeUs): EncodedAudioChunk[];
39
+ /**
40
+ * Delete audio samples for a resource
41
+ */
42
+ delete(resourceId: string): void;
43
+ /**
44
+ * Clear all cached audio samples
45
+ */
46
+ clear(): void;
47
+ /**
48
+ * Get total memory usage
49
+ */
50
+ getTotalBytes(): number;
51
+ }
52
+ //# sourceMappingURL=AudioSampleCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioSampleCache.d.ts","sourceRoot":"","sources":["../../../src/cache/resource/AudioSampleCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAwC;IAErD;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,EAAE,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IAgBzF;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAIjD;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAW1F;;OAEG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAShC;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,aAAa,IAAI,MAAM;CAOxB"}
@@ -0,0 +1,69 @@
1
+ class AudioSampleCache {
2
+ cache = /* @__PURE__ */ new Map();
3
+ /**
4
+ * Set audio samples for a resource
5
+ */
6
+ set(resourceId, samples, metadata) {
7
+ const totalBytes = samples.reduce((sum, s) => sum + s.byteLength, 0);
8
+ const durationUs = samples.length > 0 ? samples[samples.length - 1].timestamp + (samples[samples.length - 1].duration ?? 0) : 0;
9
+ this.cache.set(resourceId, {
10
+ resourceId,
11
+ samples,
12
+ metadata,
13
+ totalBytes,
14
+ durationUs
15
+ });
16
+ }
17
+ /**
18
+ * Get audio record for a resource
19
+ */
20
+ get(resourceId) {
21
+ return this.cache.get(resourceId) || null;
22
+ }
23
+ /**
24
+ * Check if resource has audio samples
25
+ */
26
+ has(resourceId) {
27
+ return this.cache.has(resourceId);
28
+ }
29
+ /**
30
+ * Get samples in time range (for clip trimming)
31
+ */
32
+ getSamplesInRange(resourceId, startUs, endUs) {
33
+ const record = this.cache.get(resourceId);
34
+ if (!record) return [];
35
+ return record.samples.filter((s) => {
36
+ const sampleEndUs = s.timestamp + (s.duration ?? 0);
37
+ return s.timestamp < endUs && sampleEndUs > startUs;
38
+ });
39
+ }
40
+ /**
41
+ * Delete audio samples for a resource
42
+ */
43
+ delete(resourceId) {
44
+ const record = this.cache.get(resourceId);
45
+ if (record) {
46
+ this.cache.delete(resourceId);
47
+ }
48
+ }
49
+ /**
50
+ * Clear all cached audio samples
51
+ */
52
+ clear() {
53
+ this.cache.clear();
54
+ }
55
+ /**
56
+ * Get total memory usage
57
+ */
58
+ getTotalBytes() {
59
+ let total = 0;
60
+ for (const record of this.cache.values()) {
61
+ total += record.totalBytes;
62
+ }
63
+ return total;
64
+ }
65
+ }
66
+ export {
67
+ AudioSampleCache
68
+ };
69
+ //# sourceMappingURL=AudioSampleCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioSampleCache.js","sources":["../../../src/cache/resource/AudioSampleCache.ts"],"sourcesContent":["import type { TimeUs } from '../../model/types';\n\nexport interface AudioSampleRecord {\n resourceId: string;\n samples: EncodedAudioChunk[];\n metadata: AudioDecoderConfig;\n totalBytes: number;\n durationUs: TimeUs;\n}\n\n/**\n * AudioSampleCache - Resource-level audio sample cache\n *\n * Caches all encoded audio samples from MP4 in memory\n * Audio data is small (~100KB for 30s AAC, ~6MB for 30min)\n *\n * Strategy:\n * - Extract all audio samples during moov parsing (one-time cost)\n * - Keep encoded chunks in memory (compressed)\n * - Decode to PCM on-demand when clip is played\n */\nexport class AudioSampleCache {\n private cache = new Map<string, AudioSampleRecord>();\n\n /**\n * Set audio samples for a resource\n */\n set(resourceId: string, samples: EncodedAudioChunk[], metadata: AudioDecoderConfig): void {\n const totalBytes = samples.reduce((sum, s) => sum + s.byteLength, 0);\n const durationUs =\n samples.length > 0\n ? samples[samples.length - 1]!.timestamp + (samples[samples.length - 1]!.duration ?? 0)\n : 0;\n\n this.cache.set(resourceId, {\n resourceId,\n samples,\n metadata,\n totalBytes,\n durationUs,\n });\n }\n\n /**\n * Get audio record for a resource\n */\n get(resourceId: string): AudioSampleRecord | null {\n return this.cache.get(resourceId) || null;\n }\n\n /**\n * Check if resource has audio samples\n */\n has(resourceId: string): boolean {\n return this.cache.has(resourceId);\n }\n\n /**\n * Get samples in time range (for clip trimming)\n */\n getSamplesInRange(resourceId: string, startUs: TimeUs, endUs: TimeUs): EncodedAudioChunk[] {\n const record = this.cache.get(resourceId);\n if (!record) return [];\n\n return record.samples.filter((s) => {\n const sampleEndUs = s.timestamp + (s.duration ?? 0);\n // Include sample if it overlaps with range\n return s.timestamp < endUs && sampleEndUs > startUs;\n });\n }\n\n /**\n * Delete audio samples for a resource\n */\n delete(resourceId: string): void {\n const record = this.cache.get(resourceId);\n if (record) {\n // Note: EncodedAudioChunk doesn't have close() method\n // Chunks will be garbage collected automatically\n this.cache.delete(resourceId);\n }\n }\n\n /**\n * Clear all cached audio samples\n */\n clear(): void {\n // Note: EncodedAudioChunk doesn't have close() method\n // Chunks will be garbage collected automatically\n this.cache.clear();\n }\n\n /**\n * Get total memory usage\n */\n getTotalBytes(): number {\n let total = 0;\n for (const record of this.cache.values()) {\n total += record.totalBytes;\n }\n return total;\n }\n}\n"],"names":[],"mappings":"AAqBO,MAAM,iBAAiB;AAAA,EACpB,4BAAY,IAAA;AAAA;AAAA;AAAA;AAAA,EAKpB,IAAI,YAAoB,SAA8B,UAAoC;AACxF,UAAM,aAAa,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,YAAY,CAAC;AACnE,UAAM,aACJ,QAAQ,SAAS,IACb,QAAQ,QAAQ,SAAS,CAAC,EAAG,aAAa,QAAQ,QAAQ,SAAS,CAAC,EAAG,YAAY,KACnF;AAEN,SAAK,MAAM,IAAI,YAAY;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAA8C;AAChD,WAAO,KAAK,MAAM,IAAI,UAAU,KAAK;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAA6B;AAC/B,WAAO,KAAK,MAAM,IAAI,UAAU;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAkB,YAAoB,SAAiB,OAAoC;AACzF,UAAM,SAAS,KAAK,MAAM,IAAI,UAAU;AACxC,QAAI,CAAC,OAAQ,QAAO,CAAA;AAEpB,WAAO,OAAO,QAAQ,OAAO,CAAC,MAAM;AAClC,YAAM,cAAc,EAAE,aAAa,EAAE,YAAY;AAEjD,aAAO,EAAE,YAAY,SAAS,cAAc;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,YAA0B;AAC/B,UAAM,SAAS,KAAK,MAAM,IAAI,UAAU;AACxC,QAAI,QAAQ;AAGV,WAAK,MAAM,OAAO,UAAU;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AAGZ,SAAK,MAAM,MAAA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAwB;AACtB,QAAI,QAAQ;AACZ,eAAW,UAAU,KAAK,MAAM,OAAA,GAAU;AACxC,eAAS,OAAO;AAAA,IAClB;AACA,WAAO;AAAA,EACT;AACF;"}
@@ -0,0 +1,65 @@
1
+ export interface ImageBitmapRecord {
2
+ resourceId: string;
3
+ bitmap: ImageBitmap;
4
+ width: number;
5
+ height: number;
6
+ sizeBytes: number;
7
+ }
8
+ /**
9
+ * ImageBitmapCache - Resource-level image bitmap cache
10
+ *
11
+ * Caches decoded ImageBitmap for image resources in main thread
12
+ * Used by OnDemandComposeSession for image clip composition
13
+ *
14
+ * Strategy:
15
+ * - Store ImageBitmap after ResourceLoader downloads image
16
+ * - Keep in main thread memory for direct access
17
+ * - Close and evict when memory pressure is high
18
+ *
19
+ * Memory usage:
20
+ * - Typical image (1080p): width * height * 4 bytes = ~8MB
21
+ * - Cache ~10 images max to stay under ~100MB
22
+ */
23
+ export declare class ImageBitmapCache {
24
+ private cache;
25
+ private maxCacheSizeMB;
26
+ private currentSizeBytes;
27
+ constructor(maxCacheSizeMB?: number);
28
+ /**
29
+ * Set image bitmap for a resource
30
+ */
31
+ set(resourceId: string, bitmap: ImageBitmap): void;
32
+ /**
33
+ * Get image bitmap for a resource
34
+ */
35
+ get(resourceId: string): ImageBitmap | null;
36
+ /**
37
+ * Get full record with metadata
38
+ */
39
+ getRecord(resourceId: string): ImageBitmapRecord | null;
40
+ /**
41
+ * Check if resource has bitmap cached
42
+ */
43
+ has(resourceId: string): boolean;
44
+ /**
45
+ * Delete bitmap for a resource
46
+ */
47
+ delete(resourceId: string): void;
48
+ /**
49
+ * Clear all cached bitmaps
50
+ */
51
+ clear(): void;
52
+ /**
53
+ * Get total memory usage in bytes
54
+ */
55
+ getTotalBytes(): number;
56
+ /**
57
+ * Get total memory usage in MB
58
+ */
59
+ getTotalMB(): number;
60
+ /**
61
+ * Evict oldest entries if over size limit
62
+ */
63
+ private evictIfNeeded;
64
+ }
65
+ //# sourceMappingURL=ImageBitmapCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageBitmapCache.d.ts","sourceRoot":"","sources":["../../../src/cache/resource/ImageBitmapCache.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,cAAc,CAAO;IAC7B,OAAO,CAAC,gBAAgB,CAAK;gBAEjB,cAAc,SAAM;IAIhC;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;IA0BlD;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAK3C;;OAEG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAIvD;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIhC;;OAEG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAShC;;OAEG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,UAAU,IAAI,MAAM;IAIpB;;OAEG;IACH,OAAO,CAAC,aAAa;CAetB"}
@@ -0,0 +1,101 @@
1
+ class ImageBitmapCache {
2
+ cache = /* @__PURE__ */ new Map();
3
+ maxCacheSizeMB = 100;
4
+ // Default 100MB
5
+ currentSizeBytes = 0;
6
+ constructor(maxCacheSizeMB = 100) {
7
+ this.maxCacheSizeMB = maxCacheSizeMB;
8
+ }
9
+ /**
10
+ * Set image bitmap for a resource
11
+ */
12
+ set(resourceId, bitmap) {
13
+ const existing = this.cache.get(resourceId);
14
+ if (existing) {
15
+ existing.bitmap.close();
16
+ this.currentSizeBytes -= existing.sizeBytes;
17
+ }
18
+ const sizeBytes = bitmap.width * bitmap.height * 4;
19
+ const record = {
20
+ resourceId,
21
+ bitmap,
22
+ width: bitmap.width,
23
+ height: bitmap.height,
24
+ sizeBytes
25
+ };
26
+ this.cache.set(resourceId, record);
27
+ this.currentSizeBytes += sizeBytes;
28
+ this.evictIfNeeded();
29
+ }
30
+ /**
31
+ * Get image bitmap for a resource
32
+ */
33
+ get(resourceId) {
34
+ const record = this.cache.get(resourceId);
35
+ return record ? record.bitmap : null;
36
+ }
37
+ /**
38
+ * Get full record with metadata
39
+ */
40
+ getRecord(resourceId) {
41
+ return this.cache.get(resourceId) || null;
42
+ }
43
+ /**
44
+ * Check if resource has bitmap cached
45
+ */
46
+ has(resourceId) {
47
+ return this.cache.has(resourceId);
48
+ }
49
+ /**
50
+ * Delete bitmap for a resource
51
+ */
52
+ delete(resourceId) {
53
+ const record = this.cache.get(resourceId);
54
+ if (record) {
55
+ record.bitmap.close();
56
+ this.currentSizeBytes -= record.sizeBytes;
57
+ this.cache.delete(resourceId);
58
+ }
59
+ }
60
+ /**
61
+ * Clear all cached bitmaps
62
+ */
63
+ clear() {
64
+ for (const record of this.cache.values()) {
65
+ record.bitmap.close();
66
+ }
67
+ this.cache.clear();
68
+ this.currentSizeBytes = 0;
69
+ }
70
+ /**
71
+ * Get total memory usage in bytes
72
+ */
73
+ getTotalBytes() {
74
+ return this.currentSizeBytes;
75
+ }
76
+ /**
77
+ * Get total memory usage in MB
78
+ */
79
+ getTotalMB() {
80
+ return this.currentSizeBytes / (1024 * 1024);
81
+ }
82
+ /**
83
+ * Evict oldest entries if over size limit
84
+ */
85
+ evictIfNeeded() {
86
+ const maxBytes = this.maxCacheSizeMB * 1024 * 1024;
87
+ if (this.currentSizeBytes > maxBytes) {
88
+ const entries = Array.from(this.cache.entries());
89
+ let evictIndex = 0;
90
+ while (this.currentSizeBytes > maxBytes && evictIndex < entries.length) {
91
+ const [resourceId] = entries[evictIndex];
92
+ this.delete(resourceId);
93
+ evictIndex++;
94
+ }
95
+ }
96
+ }
97
+ }
98
+ export {
99
+ ImageBitmapCache
100
+ };
101
+ //# sourceMappingURL=ImageBitmapCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageBitmapCache.js","sources":["../../../src/cache/resource/ImageBitmapCache.ts"],"sourcesContent":["export interface ImageBitmapRecord {\n resourceId: string;\n bitmap: ImageBitmap;\n width: number;\n height: number;\n sizeBytes: number; // Estimated memory usage\n}\n\n/**\n * ImageBitmapCache - Resource-level image bitmap cache\n *\n * Caches decoded ImageBitmap for image resources in main thread\n * Used by OnDemandComposeSession for image clip composition\n *\n * Strategy:\n * - Store ImageBitmap after ResourceLoader downloads image\n * - Keep in main thread memory for direct access\n * - Close and evict when memory pressure is high\n *\n * Memory usage:\n * - Typical image (1080p): width * height * 4 bytes = ~8MB\n * - Cache ~10 images max to stay under ~100MB\n */\nexport class ImageBitmapCache {\n private cache = new Map<string, ImageBitmapRecord>();\n private maxCacheSizeMB = 100; // Default 100MB\n private currentSizeBytes = 0;\n\n constructor(maxCacheSizeMB = 100) {\n this.maxCacheSizeMB = maxCacheSizeMB;\n }\n\n /**\n * Set image bitmap for a resource\n */\n set(resourceId: string, bitmap: ImageBitmap): void {\n // Close existing bitmap if present\n const existing = this.cache.get(resourceId);\n if (existing) {\n existing.bitmap.close();\n this.currentSizeBytes -= existing.sizeBytes;\n }\n\n // Estimate memory usage (RGBA format)\n const sizeBytes = bitmap.width * bitmap.height * 4;\n\n const record: ImageBitmapRecord = {\n resourceId,\n bitmap,\n width: bitmap.width,\n height: bitmap.height,\n sizeBytes,\n };\n\n this.cache.set(resourceId, record);\n this.currentSizeBytes += sizeBytes;\n\n // Evict if over limit (simple FIFO for now)\n this.evictIfNeeded();\n }\n\n /**\n * Get image bitmap for a resource\n */\n get(resourceId: string): ImageBitmap | null {\n const record = this.cache.get(resourceId);\n return record ? record.bitmap : null;\n }\n\n /**\n * Get full record with metadata\n */\n getRecord(resourceId: string): ImageBitmapRecord | null {\n return this.cache.get(resourceId) || null;\n }\n\n /**\n * Check if resource has bitmap cached\n */\n has(resourceId: string): boolean {\n return this.cache.has(resourceId);\n }\n\n /**\n * Delete bitmap for a resource\n */\n delete(resourceId: string): void {\n const record = this.cache.get(resourceId);\n if (record) {\n record.bitmap.close();\n this.currentSizeBytes -= record.sizeBytes;\n this.cache.delete(resourceId);\n }\n }\n\n /**\n * Clear all cached bitmaps\n */\n clear(): void {\n for (const record of this.cache.values()) {\n record.bitmap.close();\n }\n this.cache.clear();\n this.currentSizeBytes = 0;\n }\n\n /**\n * Get total memory usage in bytes\n */\n getTotalBytes(): number {\n return this.currentSizeBytes;\n }\n\n /**\n * Get total memory usage in MB\n */\n getTotalMB(): number {\n return this.currentSizeBytes / (1024 * 1024);\n }\n\n /**\n * Evict oldest entries if over size limit\n */\n private evictIfNeeded(): void {\n const maxBytes = this.maxCacheSizeMB * 1024 * 1024;\n\n // Simple FIFO eviction: remove oldest entries until under limit\n if (this.currentSizeBytes > maxBytes) {\n const entries = Array.from(this.cache.entries());\n let evictIndex = 0;\n\n while (this.currentSizeBytes > maxBytes && evictIndex < entries.length) {\n const [resourceId] = entries[evictIndex]!;\n this.delete(resourceId);\n evictIndex++;\n }\n }\n }\n}\n"],"names":[],"mappings":"AAuBO,MAAM,iBAAiB;AAAA,EACpB,4BAAY,IAAA;AAAA,EACZ,iBAAiB;AAAA;AAAA,EACjB,mBAAmB;AAAA,EAE3B,YAAY,iBAAiB,KAAK;AAChC,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAoB,QAA2B;AAEjD,UAAM,WAAW,KAAK,MAAM,IAAI,UAAU;AAC1C,QAAI,UAAU;AACZ,eAAS,OAAO,MAAA;AAChB,WAAK,oBAAoB,SAAS;AAAA,IACpC;AAGA,UAAM,YAAY,OAAO,QAAQ,OAAO,SAAS;AAEjD,UAAM,SAA4B;AAAA,MAChC;AAAA,MACA;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf;AAAA,IAAA;AAGF,SAAK,MAAM,IAAI,YAAY,MAAM;AACjC,SAAK,oBAAoB;AAGzB,SAAK,cAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAAwC;AAC1C,UAAM,SAAS,KAAK,MAAM,IAAI,UAAU;AACxC,WAAO,SAAS,OAAO,SAAS;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,YAA8C;AACtD,WAAO,KAAK,MAAM,IAAI,UAAU,KAAK;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,YAA6B;AAC/B,WAAO,KAAK,MAAM,IAAI,UAAU;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,YAA0B;AAC/B,UAAM,SAAS,KAAK,MAAM,IAAI,UAAU;AACxC,QAAI,QAAQ;AACV,aAAO,OAAO,MAAA;AACd,WAAK,oBAAoB,OAAO;AAChC,WAAK,MAAM,OAAO,UAAU;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,eAAW,UAAU,KAAK,MAAM,OAAA,GAAU;AACxC,aAAO,OAAO,MAAA;AAAA,IAChB;AACA,SAAK,MAAM,MAAA;AACX,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAwB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,aAAqB;AACnB,WAAO,KAAK,oBAAoB,OAAO;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,UAAM,WAAW,KAAK,iBAAiB,OAAO;AAG9C,QAAI,KAAK,mBAAmB,UAAU;AACpC,YAAM,UAAU,MAAM,KAAK,KAAK,MAAM,SAAS;AAC/C,UAAI,aAAa;AAEjB,aAAO,KAAK,mBAAmB,YAAY,aAAa,QAAQ,QAAQ;AACtE,cAAM,CAAC,UAAU,IAAI,QAAQ,UAAU;AACvC,aAAK,OAAO,UAAU;AACtB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;"}
@@ -0,0 +1,48 @@
1
+ import { MP4Index, Sample, GOPRange } from '../../stages/demux/types';
2
+ import { TimeUs } from '../../model/types';
3
+
4
+ /**
5
+ * MP4IndexCache - In-memory cache for MP4 indexes
6
+ *
7
+ * Provides fast lookup for:
8
+ * - Sample at specific timestamp
9
+ * - GOP range for time range
10
+ * - Nearest keyframe
11
+ */
12
+ export declare class MP4IndexCache {
13
+ private indexes;
14
+ /**
15
+ * Set index for a resource
16
+ */
17
+ set(resourceId: string, index: MP4Index): void;
18
+ /**
19
+ * Get index for a resource
20
+ */
21
+ get(resourceId: string): MP4Index | null;
22
+ /**
23
+ * Check if index exists for a resource
24
+ */
25
+ has(resourceId: string): boolean;
26
+ /**
27
+ * Get GOP range (byte range) for a specific time
28
+ * Returns the GOP that contains the target time
29
+ */
30
+ getGOPRangeForTime(resourceId: string, timeUs: TimeUs): GOPRange | null;
31
+ /**
32
+ * Get sample at specific timestamp
33
+ */
34
+ getSampleAtTime(resourceId: string, timeUs: TimeUs): Sample | null;
35
+ /**
36
+ * Get nearest keyframe before or at the target time
37
+ */
38
+ getNearestKeyframe(resourceId: string, timeUs: TimeUs): Sample | null;
39
+ /**
40
+ * Clear all cached indexes
41
+ */
42
+ clear(): void;
43
+ /**
44
+ * Remove index for a specific resource
45
+ */
46
+ remove(resourceId: string): void;
47
+ }
48
+ //# sourceMappingURL=MP4IndexCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MP4IndexCache.d.ts","sourceRoot":"","sources":["../../../src/cache/resource/MP4IndexCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAC3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhD;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA+B;IAE9C;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI;IAI9C;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAIxC;;OAEG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAIhC;;;OAGG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IA0CvE;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAclE;;OAEG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAwBrE;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;CAGjC"}