@meframe/core 0.0.31 → 0.0.32

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 (61) hide show
  1. package/dist/Meframe.d.ts +2 -2
  2. package/dist/Meframe.d.ts.map +1 -1
  3. package/dist/Meframe.js +3 -2
  4. package/dist/Meframe.js.map +1 -1
  5. package/dist/cache/CacheManager.d.ts +12 -17
  6. package/dist/cache/CacheManager.d.ts.map +1 -1
  7. package/dist/cache/CacheManager.js +18 -280
  8. package/dist/cache/CacheManager.js.map +1 -1
  9. package/dist/cache/l1/AudioL1Cache.d.ts +36 -19
  10. package/dist/cache/l1/AudioL1Cache.d.ts.map +1 -1
  11. package/dist/cache/l1/AudioL1Cache.js +182 -282
  12. package/dist/cache/l1/AudioL1Cache.js.map +1 -1
  13. package/dist/controllers/PlaybackController.d.ts +4 -2
  14. package/dist/controllers/PlaybackController.d.ts.map +1 -1
  15. package/dist/controllers/PlaybackController.js +43 -13
  16. package/dist/controllers/PlaybackController.js.map +1 -1
  17. package/dist/model/types.d.ts +0 -4
  18. package/dist/model/types.d.ts.map +1 -1
  19. package/dist/model/types.js.map +1 -1
  20. package/dist/orchestrator/ExportScheduler.d.ts +6 -0
  21. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
  22. package/dist/orchestrator/ExportScheduler.js +45 -66
  23. package/dist/orchestrator/ExportScheduler.js.map +1 -1
  24. package/dist/orchestrator/GlobalAudioSession.d.ts +35 -28
  25. package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
  26. package/dist/orchestrator/GlobalAudioSession.js +212 -421
  27. package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
  28. package/dist/orchestrator/OnDemandVideoSession.d.ts +3 -3
  29. package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
  30. package/dist/orchestrator/OnDemandVideoSession.js +4 -4
  31. package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
  32. package/dist/orchestrator/Orchestrator.d.ts +1 -2
  33. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  34. package/dist/orchestrator/Orchestrator.js +34 -48
  35. package/dist/orchestrator/Orchestrator.js.map +1 -1
  36. package/dist/orchestrator/VideoClipSession.d.ts +0 -2
  37. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  38. package/dist/orchestrator/VideoClipSession.js +0 -49
  39. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  40. package/dist/stages/compose/OfflineAudioMixer.d.ts.map +1 -1
  41. package/dist/stages/compose/OfflineAudioMixer.js +13 -18
  42. package/dist/stages/compose/OfflineAudioMixer.js.map +1 -1
  43. package/dist/stages/decode/AudioChunkDecoder.js +169 -0
  44. package/dist/stages/decode/AudioChunkDecoder.js.map +1 -0
  45. package/dist/stages/demux/MP3FrameParser.js +186 -0
  46. package/dist/stages/demux/MP3FrameParser.js.map +1 -0
  47. package/dist/stages/load/ResourceLoader.d.ts +20 -9
  48. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  49. package/dist/stages/load/ResourceLoader.js +92 -135
  50. package/dist/stages/load/ResourceLoader.js.map +1 -1
  51. package/dist/stages/load/types.d.ts +1 -0
  52. package/dist/stages/load/types.d.ts.map +1 -1
  53. package/dist/utils/audio-data.d.ts +16 -0
  54. package/dist/utils/audio-data.d.ts.map +1 -0
  55. package/dist/utils/audio-data.js +111 -0
  56. package/dist/utils/audio-data.js.map +1 -0
  57. package/package.json +1 -1
  58. package/dist/cache/resource/ImageBitmapCache.d.ts +0 -65
  59. package/dist/cache/resource/ImageBitmapCache.d.ts.map +0 -1
  60. package/dist/cache/resource/ImageBitmapCache.js +0 -101
  61. package/dist/cache/resource/ImageBitmapCache.js.map +0 -1
@@ -4,12 +4,7 @@ import { StreamFactory } from "./StreamFactory.js";
4
4
  import { MeframeEvent } from "../../event/events.js";
5
5
  import { createImageBitmapFromBlob } from "../../utils/image-utils.js";
6
6
  import { MP4IndexParser } from "../demux/MP4IndexParser.js";
7
- class ResourceConflictError extends Error {
8
- constructor(message) {
9
- super(message);
10
- this.name = "ResourceConflictError";
11
- }
12
- }
7
+ import { MP3FrameParser } from "../demux/MP3FrameParser.js";
13
8
  class ResourceLoader {
14
9
  cacheManager;
15
10
  workerPool;
@@ -19,19 +14,16 @@ class ResourceLoader {
19
14
  eventBus;
20
15
  onStateChange;
21
16
  blobCache = /* @__PURE__ */ new Map();
22
- pendingTransfers = /* @__PURE__ */ new Map();
23
17
  parsingIndexes = /* @__PURE__ */ new Set();
24
18
  // Track in-progress index parsing
25
19
  // Preloading state
26
20
  isPreloadingEnabled = true;
27
21
  preloadQueue = [];
28
- activePreloads = /* @__PURE__ */ new Set();
29
- // TODO: make this configurable
30
- IDLE_PRELOAD_CONCURRENCY = 2;
31
22
  constructor(options) {
32
- const maxConcurrent = options.config?.maxConcurrent ?? 4;
23
+ const config = options.config || {};
24
+ const maxConcurrent = config.maxConcurrent ?? 2;
33
25
  this.taskManager = new TaskManager(maxConcurrent);
34
- this.streamFactory = new StreamFactory(options.onProgress, options.config);
26
+ this.streamFactory = new StreamFactory(options.onProgress, config);
35
27
  this.eventBus = options.eventBus;
36
28
  this.onStateChange = options.onStateChange;
37
29
  this.cacheManager = options.cacheManager;
@@ -53,8 +45,6 @@ class ResourceLoader {
53
45
  this.isPreloadingEnabled = enabled;
54
46
  if (enabled) {
55
47
  this.startPreloading();
56
- } else {
57
- this.preloadQueue = [];
58
48
  }
59
49
  }
60
50
  startPreloading() {
@@ -63,7 +53,6 @@ class ResourceLoader {
63
53
  (track) => track.id === (this.model?.mainTrackId || "main")
64
54
  );
65
55
  if (!mainTrack) return;
66
- const newQueue = [];
67
56
  for (const clip of mainTrack.clips) {
68
57
  if (!hasResourceId(clip)) continue;
69
58
  const resource = this.model.getResource(clip.resourceId);
@@ -71,56 +60,27 @@ class ResourceLoader {
71
60
  if (!resource || resource.state === "ready" || resource.state === "loading" || resource.state === "error") {
72
61
  continue;
73
62
  }
74
- if (this.activePreloads.has(resource.id) || this.taskManager.hasActiveTask(resource.id)) {
75
- continue;
76
- }
77
- newQueue.push(resource.id);
63
+ this.fetch(resource.id, { priority: "low" });
78
64
  }
79
- this.preloadQueue = newQueue;
80
- this.processPreloadQueue();
81
65
  }
82
66
  processPreloadQueue() {
83
67
  if (!this.isPreloadingEnabled || this.preloadQueue.length === 0) return;
84
- while (this.activePreloads.size < this.IDLE_PRELOAD_CONCURRENCY && this.preloadQueue.length > 0) {
68
+ while (this.preloadQueue.length > 0) {
85
69
  const resourceId = this.preloadQueue.shift();
86
70
  if (!resourceId) break;
87
- this.activePreloads.add(resourceId);
88
71
  this.fetch(resourceId, { priority: "low" }).finally(() => {
89
- this.activePreloads.delete(resourceId);
90
72
  this.processPreloadQueue();
91
73
  });
92
74
  }
93
75
  }
94
- enqueueLoad(resource, priority = "normal", sessionId, clipId, trackId, isMainTrack = false) {
95
- if (this.taskManager.hasActiveTask(resource.id)) {
96
- if (priority === "high") ;
97
- else if (isMainTrack) {
98
- throw new ResourceConflictError(
99
- `Resource ${resource.id} is being loaded by another session. Preview channel has priority.`
100
- );
101
- } else {
102
- if (this.blobCache.has(resource.id)) {
103
- void this.transferCachedImage(resource.id, sessionId);
104
- return;
105
- } else {
106
- this.registerPendingTransfer(resource.id, sessionId);
107
- console.debug(
108
- `[ResourceLoader] Attachment resource ${resource.id} loading, registered for pending transfer`
109
- );
110
- return;
111
- }
112
- }
76
+ enqueueLoad(resource, priority = "normal", sessionId, clipId, trackId) {
77
+ const existingTask = this.taskManager.getActiveTask(resource.id);
78
+ if (existingTask) {
79
+ return existingTask;
113
80
  }
114
- this.taskManager.enqueue(resource, priority, sessionId, clipId, trackId);
81
+ const task = this.taskManager.enqueue(resource, priority, sessionId, clipId, trackId);
115
82
  this.processQueue();
116
- }
117
- registerPendingTransfer(resourceId, sessionId) {
118
- if (!sessionId) return;
119
- const pending = this.pendingTransfers.get(resourceId) || [];
120
- if (!pending.includes(sessionId)) {
121
- pending.push(sessionId);
122
- this.pendingTransfers.set(resourceId, pending);
123
- }
83
+ return task;
124
84
  }
125
85
  processQueue() {
126
86
  while (this.taskManager.canProcess) {
@@ -141,6 +101,9 @@ class ResourceLoader {
141
101
  task.controller = new AbortController();
142
102
  if (task.resource.type === "image") {
143
103
  await this.loadImageBitmap(task);
104
+ if (task.sessionId) {
105
+ this.transferCachedImage(task);
106
+ }
144
107
  } else if (task.resource.type === "video") {
145
108
  const cached = await this.cacheManager.hasResourceInCache(task.resourceId);
146
109
  if (cached) {
@@ -154,12 +117,9 @@ class ResourceLoader {
154
117
  await this.loadWithOPFSCache(task);
155
118
  }
156
119
  } else if (task.resource.type === "audio") {
157
- const stream = await this.streamFactory.createRegularStream(task);
158
- if (!stream) {
159
- throw new Error(`Failed to create stream for ${task.resourceId}`);
120
+ if (!this.cacheManager.audioSampleCache.has(task.resourceId)) {
121
+ await this.loadAndParseAudioFile(task);
160
122
  }
161
- task.stream = stream;
162
- await this.transferToDemuxWorker(task);
163
123
  } else if (task.resource.type === "json" || task.resource.type === "text") {
164
124
  await this.loadTextResource(task);
165
125
  }
@@ -258,6 +218,44 @@ class ResourceLoader {
258
218
  async writeToOPFS(resourceId, stream) {
259
219
  await this.cacheManager.resourceCache.writeResource(resourceId, stream);
260
220
  }
221
+ /**
222
+ * Load and parse audio file (MP3/WAV) in main thread
223
+ * Extract EncodedAudioChunk and cache to AudioSampleCache
224
+ * Aligned with video audio track extraction (unified architecture)
225
+ */
226
+ async loadAndParseAudioFile(task) {
227
+ const { resourceId } = task;
228
+ try {
229
+ const blob = await this.fetchBlob(task.resource.uri, task.controller.signal);
230
+ const arrayBuffer = await blob.arrayBuffer();
231
+ const uint8Array = new Uint8Array(arrayBuffer);
232
+ const parser = new MP3FrameParser();
233
+ const { frames, config } = parser.push(uint8Array);
234
+ const remainingFrames = parser.flush();
235
+ const allFrames = [...frames, ...remainingFrames];
236
+ if (!config) {
237
+ throw new Error(`Failed to parse audio config for ${resourceId}`);
238
+ }
239
+ const audioChunks = allFrames.map((frame) => {
240
+ return new EncodedAudioChunk({
241
+ type: "key",
242
+ // MP3 frames are all key frames
243
+ timestamp: frame.timestampUs,
244
+ duration: frame.durationUs,
245
+ data: frame.data
246
+ });
247
+ });
248
+ const audioConfig = {
249
+ codec: "mp3",
250
+ sampleRate: config.sampleRate,
251
+ numberOfChannels: config.channels
252
+ };
253
+ this.cacheManager.audioSampleCache.set(resourceId, audioChunks, audioConfig);
254
+ } catch (error) {
255
+ console.error(`[ResourceLoader] Failed to parse audio file ${resourceId}:`, error);
256
+ throw error;
257
+ }
258
+ }
261
259
  /**
262
260
  * Parse moov from stream and cache index + audio samples + decode first frame
263
261
  */
@@ -303,6 +301,18 @@ class ResourceLoader {
303
301
  async loadTextResource(task) {
304
302
  await this.fetchBlob(task.resource.uri, task.controller.signal);
305
303
  }
304
+ async loadImage(resource) {
305
+ const task = {
306
+ resourceId: resource.id,
307
+ resource,
308
+ bytesLoaded: 0,
309
+ totalBytes: 0,
310
+ startTime: Date.now(),
311
+ priority: "normal",
312
+ controller: new AbortController()
313
+ };
314
+ return this.loadImageBitmap(task);
315
+ }
306
316
  /**
307
317
  * Load image resource: fetch blob → create ImageBitmap → cache in CacheManager
308
318
  * Note: Images don't need streaming (typically < 5MB)
@@ -319,7 +329,7 @@ class ResourceLoader {
319
329
  this.blobCache.set(task.resourceId, blob);
320
330
  }
321
331
  const imageBitmap = await createImageBitmapFromBlob(blob);
322
- this.cacheManager.imageBitmapCache.set(task.resourceId, imageBitmap);
332
+ return imageBitmap;
323
333
  }
324
334
  /**
325
335
  * Fetch resource as blob (for images, json, etc.)
@@ -331,34 +341,12 @@ class ResourceLoader {
331
341
  }
332
342
  return response.blob();
333
343
  }
334
- /**
335
- * Transfer ImageBitmap to VideoComposeWorker
336
- * Legacy: Not used in window cache architecture (images accessed via CacheManager)
337
- */
338
- // private async transferImageToWorker(task: LoadTask, imageBitmap: ImageBitmap): Promise<void> {
339
- // if (!this.orchestrator) return;
340
- // if (!task.sessionId) {
341
- // throw new Error(
342
- // `[ResourceLoader] sessionId required for resource ${task.resourceId}. ` +
343
- // `In Clip-based architecture, use fetch(resourceId, { sessionId })`
344
- // );
345
- // }
346
- // const composeWorker = await this.orchestrator.workers.get('videoCompose', task.sessionId);
347
- // await composeWorker?.send?.(
348
- // 'receive_image',
349
- // {
350
- // resourceId: task.resourceId,
351
- // sessionId: task.sessionId,
352
- // imageBitmap,
353
- // },
354
- // { transfer: [imageBitmap] }
355
- // );
356
- // }
357
344
  /**
358
345
  * Transfer cached image to a session
359
346
  * Creates new ImageBitmap from cached Blob and transfers to worker
360
347
  */
361
- async transferCachedImage(resourceId, sessionId) {
348
+ async transferCachedImage(task) {
349
+ const { resourceId, sessionId } = task;
362
350
  const blob = this.blobCache.get(resourceId);
363
351
  if (!blob || !sessionId) return;
364
352
  const imageBitmap = await createImageBitmapFromBlob(blob);
@@ -379,9 +367,8 @@ class ResourceLoader {
379
367
  async transferToDemuxWorker(task) {
380
368
  if (!task.stream) return;
381
369
  if (!task.sessionId) {
382
- throw new Error(
383
- `[ResourceLoader] sessionId required for resource ${task.resourceId}. In Clip-based architecture, use fetch(resourceId, { sessionId })`
384
- );
370
+ task.stream.cancel();
371
+ return;
385
372
  }
386
373
  const workerType = task.resource.type === "video" ? "videoDemux" : "audioDemux";
387
374
  const demuxWorker = await this.workerPool.get(workerType, task.sessionId);
@@ -410,6 +397,18 @@ class ResourceLoader {
410
397
  }
411
398
  this.onStateChange?.(resourceId, state);
412
399
  }
400
+ /**
401
+ * Fetch a resource and wait for loading + parsing to complete
402
+ *
403
+ * Returns a Promise that resolves when:
404
+ * - Resource is fully loaded, parsed, and cached (state='ready')
405
+ * - Or rejects if loading/parsing fails
406
+ *
407
+ * Promise lifecycle:
408
+ * 1. enqueueLoad() creates LoadTask with promise/resolve/reject (or reuses existing)
409
+ * 2. processQueue() → startLoad() executes async in background
410
+ * 3. startLoad() completes → finally → completeTask() → task.resolve()/reject()
411
+ */
413
412
  async fetch(resourceId, options) {
414
413
  if (!resourceId) {
415
414
  return;
@@ -419,54 +418,14 @@ class ResourceLoader {
419
418
  console.warn(`Resource ${resourceId} not found in model`);
420
419
  return;
421
420
  }
422
- const transferResourceToWorker = async (rId, sId, type) => {
423
- if (type === "video") {
424
- const stream = await this.createOPFSReadStream(rId);
425
- const task = {
426
- resourceId: rId,
427
- sessionId: sId,
428
- resource: this.model?.resources.get(rId),
429
- stream,
430
- bytesLoaded: 0,
431
- totalBytes: 0,
432
- startTime: Date.now(),
433
- priority: "normal"
434
- };
435
- await this.transferToDemuxWorker(task);
436
- } else if (type === "image") {
437
- await this.transferCachedImage(rId, sId);
438
- }
439
- };
440
- if (resource.state === "ready") {
441
- if (options?.sessionId) {
442
- await transferResourceToWorker(resourceId, options.sessionId, resource.type);
443
- }
444
- return;
445
- }
446
- let taskPromise = this.taskManager.getTaskPromise(resourceId);
447
- let isCoveredByTask = false;
448
- if (taskPromise) {
449
- const activeTask = this.taskManager.activeTasks.get(resourceId) || this.taskManager.taskQueue.find((t) => t.resourceId === resourceId);
450
- if (activeTask && activeTask.sessionId === options?.sessionId) {
451
- isCoveredByTask = true;
452
- }
453
- } else {
454
- this.enqueueLoad(
455
- resource,
456
- options?.priority || "normal",
457
- options?.sessionId,
458
- options?.clipId,
459
- options?.trackId,
460
- options?.isMainTrack ?? false
461
- );
462
- taskPromise = this.taskManager.getTaskPromise(resourceId);
463
- isCoveredByTask = true;
464
- }
465
- await taskPromise;
466
- const updatedResource = this.model?.resources.get(resourceId);
467
- if (!isCoveredByTask && updatedResource?.state === "ready" && options?.sessionId) {
468
- await transferResourceToWorker(resourceId, options.sessionId, resource.type);
469
- }
421
+ const task = this.enqueueLoad(
422
+ resource,
423
+ options?.priority || "normal",
424
+ options?.sessionId,
425
+ options?.clipId,
426
+ options?.trackId
427
+ );
428
+ return task.promise;
470
429
  }
471
430
  cancel(resourceId) {
472
431
  this.taskManager.cancelTask(resourceId);
@@ -511,11 +470,9 @@ class ResourceLoader {
511
470
  dispose() {
512
471
  this.taskManager.clear();
513
472
  this.blobCache.clear();
514
- this.pendingTransfers.clear();
515
473
  }
516
474
  }
517
475
  export {
518
- ResourceConflictError,
519
476
  ResourceLoader
520
477
  };
521
478
  //# sourceMappingURL=ResourceLoader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceLoader.js","sources":["../../../src/stages/load/ResourceLoader.ts"],"sourcesContent":["import { type Resource, type CompositionModel, hasResourceId } from '../../model';\nimport type { ResourceLoadOptions, LoadTask, ResourceLoaderOptions } from './types';\nimport { TaskManager } from './TaskManager';\nimport { StreamFactory } from './StreamFactory';\nimport { EventPayloadMap, MeframeEvent } from '../../event/events';\nimport { EventBus } from '../../event/EventBus';\nimport { createImageBitmapFromBlob } from '../../utils/image-utils';\nimport { MP4IndexParser, type MP4ParseResult } from '../demux/MP4IndexParser';\nimport type { CacheManager } from '../../cache/CacheManager';\nimport type { WorkerPool } from '../../worker/WorkerPool';\n\nexport class ResourceConflictError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ResourceConflictError';\n }\n}\n\nexport class ResourceLoader {\n private cacheManager: CacheManager;\n private workerPool: WorkerPool;\n private model?: CompositionModel;\n private taskManager: TaskManager;\n private streamFactory: StreamFactory;\n private eventBus?: EventBus<EventPayloadMap>;\n private onStateChange?: (resourceId: string, state: Resource['state']) => void;\n private blobCache = new Map<string, Blob>();\n private pendingTransfers = new Map<string, string[]>();\n private parsingIndexes = new Set<string>(); // Track in-progress index parsing\n\n // Preloading state\n private isPreloadingEnabled = true;\n private preloadQueue: string[] = [];\n private activePreloads = new Set<string>();\n // TODO: make this configurable\n private readonly IDLE_PRELOAD_CONCURRENCY = 2;\n\n constructor(options: ResourceLoaderOptions) {\n const maxConcurrent = options.config?.maxConcurrent ?? 4;\n this.taskManager = new TaskManager(maxConcurrent);\n this.streamFactory = new StreamFactory(options.onProgress, options.config);\n this.eventBus = options.eventBus;\n this.onStateChange = options.onStateChange;\n this.cacheManager = options.cacheManager;\n this.workerPool = options.workerPool;\n }\n\n async setModel(model: CompositionModel): Promise<void> {\n this.model = model;\n const mainTrack = model.tracks.find((track) => track.id === (model.mainTrackId || 'main'));\n if (mainTrack?.clips?.[0] && hasResourceId(mainTrack.clips[0])) {\n await this.fetch(mainTrack.clips[0].resourceId, {\n priority: 'high',\n clipId: mainTrack.clips[0].id,\n trackId: mainTrack.id,\n });\n }\n this.startPreloading();\n }\n\n setPreloadingEnabled(enabled: boolean): void {\n this.isPreloadingEnabled = enabled;\n if (enabled) {\n this.startPreloading();\n } else {\n // Clear queue if disabled, but let active preloads finish (they are low priority)\n this.preloadQueue = [];\n }\n }\n\n startPreloading(): void {\n if (!this.model || !this.isPreloadingEnabled) return;\n\n const mainTrack = this.model.tracks.find(\n (track) => track.id === (this.model?.mainTrackId || 'main')\n );\n if (!mainTrack) return;\n const newQueue: string[] = [];\n for (const clip of mainTrack.clips) {\n if (!hasResourceId(clip)) continue;\n const resource = this.model.getResource(clip.resourceId);\n if (!resource) continue;\n\n // Skip if already ready, loading, or error\n if (\n !resource ||\n resource.state === 'ready' ||\n resource.state === 'loading' ||\n resource.state === 'error'\n ) {\n continue;\n }\n\n // Also skip if currently in activePreloads or activeTasks\n if (this.activePreloads.has(resource.id) || this.taskManager.hasActiveTask(resource.id)) {\n continue;\n }\n\n newQueue.push(resource.id);\n }\n\n this.preloadQueue = newQueue;\n this.processPreloadQueue();\n }\n\n private processPreloadQueue(): void {\n if (!this.isPreloadingEnabled || this.preloadQueue.length === 0) return;\n\n while (\n this.activePreloads.size < this.IDLE_PRELOAD_CONCURRENCY &&\n this.preloadQueue.length > 0\n ) {\n const resourceId = this.preloadQueue.shift();\n if (!resourceId) break;\n\n this.activePreloads.add(resourceId);\n\n // Use low priority for preloading\n this.fetch(resourceId, { priority: 'low' }).finally(() => {\n this.activePreloads.delete(resourceId);\n // Continue processing queue\n this.processPreloadQueue();\n });\n }\n }\n\n private enqueueLoad(\n resource: Resource,\n priority: 'high' | 'normal' | 'low' = 'normal',\n sessionId?: string,\n clipId?: string,\n trackId?: string,\n isMainTrack = false\n ): void {\n if (this.taskManager.hasActiveTask(resource.id)) {\n if (priority === 'high') {\n // Preview channel: high priority can preempt (existing logic preserved)\n } else if (isMainTrack) {\n // Main track resource conflict: throw error for PreRenderService to detect\n throw new ResourceConflictError(\n `Resource ${resource.id} is being loaded by another session. Preview channel has priority.`\n );\n } else {\n // Attachment resource conflict: check cache\n if (this.blobCache.has(resource.id)) {\n // Already cached: immediately create ImageBitmap and transfer\n void this.transferCachedImage(resource.id, sessionId);\n return;\n } else {\n // Loading in progress: register to pending queue, will transfer after load complete\n this.registerPendingTransfer(resource.id, sessionId);\n console.debug(\n `[ResourceLoader] Attachment resource ${resource.id} loading, registered for pending transfer`\n );\n return;\n }\n }\n }\n\n this.taskManager.enqueue(resource, priority, sessionId, clipId, trackId);\n this.processQueue();\n }\n\n private registerPendingTransfer(resourceId: string, sessionId?: string): void {\n if (!sessionId) return;\n\n const pending = this.pendingTransfers.get(resourceId) || [];\n if (!pending.includes(sessionId)) {\n pending.push(sessionId);\n this.pendingTransfers.set(resourceId, pending);\n }\n }\n\n private processQueue(): void {\n while (this.taskManager.canProcess) {\n const task = this.taskManager.getNextTask();\n if (!task) break;\n this.startLoad(task);\n }\n }\n\n /**\n * Start loading a resource\n * Handles state management (loading → ready/error) for all resource types\n */\n private async startLoad(task: LoadTask): Promise<void> {\n this.taskManager.activateTask(task);\n let loadError: Error | undefined;\n\n try {\n this.updateResourceState(task.resourceId, 'loading');\n task.controller = new AbortController();\n\n // Route to different handlers based on resource type\n if (task.resource.type === 'image') {\n await this.loadImageBitmap(task);\n } else if (task.resource.type === 'video') {\n // Video: OPFS cache + MP4 index parsing (for window cache architecture)\n const cached = await this.cacheManager.hasResourceInCache(task.resourceId);\n if (cached) {\n // Resource already in OPFS - ensure index is parsed\n await this.ensureIndexParsed(task.resourceId);\n\n // If sessionId is present (Worker Pipeline active), transfer OPFS stream to worker\n if (task.sessionId) {\n const stream = await this.createOPFSReadStream(task.resourceId);\n task.stream = stream;\n await this.transferToDemuxWorker(task);\n }\n } else {\n // Not cached - download and cache to OPFS\n await this.loadWithOPFSCache(task);\n }\n } else if (task.resource.type === 'audio') {\n // Audio (MP3/WAV): Traditional pipeline (demux worker)\n // No OPFS cache or index parsing needed\n const stream = await this.streamFactory.createRegularStream(task);\n if (!stream) {\n throw new Error(`Failed to create stream for ${task.resourceId}`);\n }\n task.stream = stream;\n await this.transferToDemuxWorker(task);\n } else if (task.resource.type === 'json' || task.resource.type === 'text') {\n await this.loadTextResource(task);\n }\n\n // Unified state update for all resource types\n this.updateResourceState(task.resourceId, 'ready');\n } catch (error) {\n task.error = error as Error;\n loadError = error as Error;\n this.updateResourceState(task.resourceId, 'error');\n } finally {\n this.taskManager.completeTask(task.resourceId, loadError);\n this.processQueue();\n }\n }\n\n /**\n * Ensure MP4 index is parsed for a cached resource\n */\n async ensureIndexParsed(resourceId: string): Promise<void> {\n // Check if index already exists\n if (this.cacheManager.mp4IndexCache.has(resourceId)) {\n return;\n }\n\n // Check if already parsing (avoid duplicate parsing for same resource)\n if (this.parsingIndexes.has(resourceId)) {\n // Wait for the in-progress parsing to complete\n while (this.parsingIndexes.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n return;\n }\n\n // Mark as parsing\n this.parsingIndexes.add(resourceId);\n\n try {\n // Parse from OPFS file\n const stream = await this.createOPFSReadStream(resourceId);\n // Create minimal task for parsing (no clipId since this is a background index parse)\n const parseTask: LoadTask = {\n resourceId,\n resource: { id: resourceId, type: 'video', uri: '' },\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n priority: 'normal',\n };\n await this.parseIndexFromStream(parseTask, stream);\n } finally {\n // Remove from parsing set\n this.parsingIndexes.delete(resourceId);\n }\n }\n\n /**\n * Create ReadableStream from OPFS file\n * Reuses OPFSManager's underlying file access\n */\n private async createOPFSReadStream(resourceId: string): Promise<ReadableStream<Uint8Array>> {\n const opfsManager = this.cacheManager.resourceCache.opfsManager;\n const projectId = this.cacheManager.resourceCache.projectId;\n\n // Get file handle from OPFS\n const dir = await opfsManager.getProjectDir(projectId, 'resource');\n const fileName = `${resourceId}.mp4`;\n const fileHandle = await dir.getFileHandle(fileName);\n const file = await fileHandle.getFile();\n\n // Return native stream\n return file.stream();\n }\n\n /**\n * Load resource and cache to OPFS + parse moov + extract first frame\n *\n * Strategy: Parallel tee() approach for fast first frame\n * - One stream writes to OPFS (background)\n * - Another stream parses moov and extracts first GOP\n * - First frame is decoded and cached immediately\n */\n private async loadWithOPFSCache(task: LoadTask): Promise<void> {\n const stream = await this.streamFactory.createRegularStream(task);\n if (!stream) {\n throw new Error(`Failed to create stream for ${task.resourceId}`);\n }\n\n // Prepare streams: 1 for OPFS, 1 for parsing, optional 1 for Worker\n let opfsStream: ReadableStream<Uint8Array>;\n let parseStream: ReadableStream<Uint8Array>;\n let workerStream: ReadableStream<Uint8Array> | undefined;\n\n if (task.sessionId) {\n // If worker needs it, split into 3 ways\n const [branch1, branch2] = stream.tee();\n const [branch2a, branch2b] = branch2.tee();\n\n opfsStream = branch1;\n parseStream = branch2a;\n workerStream = branch2b;\n } else {\n // Just 2 ways\n const [s1, s2] = stream.tee();\n opfsStream = s1;\n parseStream = s2;\n }\n\n const promises: Promise<void>[] = [\n this.writeToOPFS(task.resourceId, opfsStream),\n this.parseIndexFromStream(task, parseStream),\n ];\n\n if (workerStream && task.sessionId) {\n // Assign stream to task so transferToDemuxWorker uses it\n // Note: we need to clone the task or modify it carefully, but here it's safe\n // We create a temp task object or just modify current (since stream is consumed)\n // Actually transferToDemuxWorker uses task.stream.\n // We can't modify task.stream in place easily if type is readonly-ish, but it's not.\n const workerTask = { ...task, stream: workerStream };\n promises.push(this.transferToDemuxWorker(workerTask));\n }\n\n // Parallel execution\n await Promise.all(promises);\n }\n\n /**\n * Write resource stream to OPFS\n */\n private async writeToOPFS(resourceId: string, stream: ReadableStream<Uint8Array>): Promise<void> {\n await this.cacheManager.resourceCache.writeResource(resourceId, stream);\n }\n\n /**\n * Parse moov from stream and cache index + audio samples + decode first frame\n */\n private async parseIndexFromStream(\n task: LoadTask,\n stream: ReadableStream<Uint8Array>\n ): Promise<void> {\n const { resourceId, clipId } = task;\n\n try {\n const parser = new MP4IndexParser();\n\n const result: MP4ParseResult = await parser.parseFromStream(stream, {\n onFirstFrameReady: async (index, chunks) => {\n // Set resourceId on index\n index.resourceId = resourceId;\n\n // Cache index immediately\n this.cacheManager.mp4IndexCache.set(resourceId, index);\n\n // Emit event with chunks for Orchestrator to handle\n // Only if clipId is provided (indicates this is a preview/cover request)\n if (clipId) {\n this.eventBus?.emit(MeframeEvent.ResourceFirstFrameReady, {\n resourceId,\n clipId,\n index,\n chunks,\n });\n }\n },\n });\n\n result.index.resourceId = resourceId;\n\n // Cache index (if not already cached by onFirstFrameReady)\n if (!this.cacheManager.mp4IndexCache.has(resourceId)) {\n this.cacheManager.mp4IndexCache.set(resourceId, result.index);\n }\n\n // Cache audio samples if present\n if (result.audioSamples && result.audioMetadata) {\n this.cacheManager.audioSampleCache.set(\n resourceId,\n result.audioSamples,\n result.audioMetadata\n );\n } else {\n // Ensure cache knows this resource has NO audio\n // This prevents GlobalAudioSession from waiting for it\n // AudioSampleCache should ideally support storing \"empty\" state or we just rely on has() returning false\n // But has() returning false triggers fetch.\n // We need a way to say \"fetched, but no audio\".\n // Currently AudioSampleCache.set requires samples.\n // We might need to update AudioSampleCache to support explicit \"no audio\" record\n // Or we rely on MP4Index having audio track info.\n }\n } catch (error) {\n console.error(`[ResourceLoader] Failed to parse MP4 index for ${resourceId}:`, error);\n // Rethrow error to ensure resource is marked as failed\n // In the new architecture (Window Cache + AudioSampleCache), index parsing is critical.\n throw error;\n }\n }\n\n /**\n * Load text-based resources (json, text)\n * Just download the content - state management is handled by startLoad()\n */\n private async loadTextResource(task: LoadTask): Promise<void> {\n await this.fetchBlob(task.resource.uri, task.controller!.signal);\n // For json/text, just verify we can download it\n // Future: could parse and validate json here if needed\n }\n\n /**\n * Load image resource: fetch blob → create ImageBitmap → cache in CacheManager\n * Note: Images don't need streaming (typically < 5MB)\n *\n * Strategy for window cache architecture:\n * - Store ImageBitmap in CacheManager.imageBitmapCache (main thread accessible)\n * - OnDemandComposeSession retrieves from cache for composition\n * - No longer transfer to Worker (composition is in main thread)\n */\n private async loadImageBitmap(task: LoadTask): Promise<void> {\n // Check cache\n let blob = this.blobCache.get(task.resourceId);\n\n if (!blob) {\n // Not cached: download and cache\n blob = await this.fetchBlob(task.resource.uri, task.controller!.signal);\n this.blobCache.set(task.resourceId, blob);\n }\n\n // Create ImageBitmap from Blob (with special handling for SVG)\n const imageBitmap = await createImageBitmapFromBlob(blob);\n\n // Store in CacheManager for main-thread access (OnDemandComposeSession)\n this.cacheManager.imageBitmapCache.set(task.resourceId, imageBitmap);\n\n // Legacy: Transfer to Worker if sessionId is provided (for old pipeline compatibility)\n // if (task.sessionId) {\n // await this.transferImageToWorker(task, imageBitmap);\n\n // // Process pending transfer queue\n // const pending = this.pendingTransfers.get(task.resourceId);\n // if (pending && pending.length > 0) {\n // for (const sessionId of pending) {\n // await this.transferCachedImage(task.resourceId, sessionId);\n // }\n // this.pendingTransfers.delete(task.resourceId);\n // }\n // }\n }\n\n /**\n * Fetch resource as blob (for images, json, etc.)\n */\n private async fetchBlob(uri: string, signal: AbortSignal): Promise<Blob> {\n const response = await fetch(uri, { signal });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return response.blob();\n }\n\n /**\n * Transfer ImageBitmap to VideoComposeWorker\n * Legacy: Not used in window cache architecture (images accessed via CacheManager)\n */\n // private async transferImageToWorker(task: LoadTask, imageBitmap: ImageBitmap): Promise<void> {\n // if (!this.orchestrator) return;\n\n // if (!task.sessionId) {\n // throw new Error(\n // `[ResourceLoader] sessionId required for resource ${task.resourceId}. ` +\n // `In Clip-based architecture, use fetch(resourceId, { sessionId })`\n // );\n // }\n\n // const composeWorker = await this.orchestrator.workers.get('videoCompose', task.sessionId);\n // await composeWorker?.send?.(\n // 'receive_image',\n // {\n // resourceId: task.resourceId,\n // sessionId: task.sessionId,\n // imageBitmap,\n // },\n // { transfer: [imageBitmap] }\n // );\n // }\n\n /**\n * Transfer cached image to a session\n * Creates new ImageBitmap from cached Blob and transfers to worker\n */\n private async transferCachedImage(resourceId: string, sessionId?: string): Promise<void> {\n const blob = this.blobCache.get(resourceId);\n if (!blob || !sessionId) return;\n\n // Create new ImageBitmap from cached Blob (with SVG support)\n const imageBitmap = await createImageBitmapFromBlob(blob);\n\n // Transfer to Worker\n const composeWorker = await this.workerPool.get('videoCompose', sessionId);\n\n await composeWorker?.send?.(\n 'receive_image',\n {\n resourceId,\n sessionId,\n imageBitmap,\n },\n { transfer: [imageBitmap] }\n );\n }\n\n /**\n * Transfer stream to demux worker (for audio files)\n */\n private async transferToDemuxWorker(task: LoadTask): Promise<void> {\n if (!task.stream) return;\n\n if (!task.sessionId) {\n throw new Error(\n `[ResourceLoader] sessionId required for resource ${task.resourceId}. ` +\n `In Clip-based architecture, use fetch(resourceId, { sessionId })`\n );\n }\n\n const workerType = task.resource.type === 'video' ? 'videoDemux' : 'audioDemux';\n const demuxWorker = await this.workerPool.get(workerType, task.sessionId);\n if (demuxWorker) {\n await demuxWorker.sendStream(task.stream, {\n sessionId: task.sessionId,\n ...task.metadata,\n ...(task.range && { range: task.range }),\n ...(task.trackId && { trackId: task.trackId }),\n });\n } else {\n // Demux worker not ready - cancel stream\n task.stream.cancel();\n }\n }\n\n private updateResourceState(resourceId: string, state: Resource['state']): void {\n const resource = this.model?.resources.get(resourceId);\n if (resource) {\n const oldState = resource.state;\n resource.state = state;\n this.eventBus?.emit(MeframeEvent.ResourceStageChange, {\n type: MeframeEvent.ResourceStageChange,\n resourceId,\n oldState,\n newState: state,\n });\n }\n\n this.onStateChange?.(resourceId, state);\n }\n\n async fetch(resourceId?: string, options?: ResourceLoadOptions): Promise<void> {\n if (!resourceId) {\n return;\n }\n\n const resource = this.model?.resources.get(resourceId);\n if (!resource) {\n console.warn(`Resource ${resourceId} not found in model`);\n return;\n }\n\n const transferResourceToWorker = async (\n rId: string,\n sId: string,\n type: string\n ): Promise<void> => {\n if (type === 'video') {\n const stream = await this.createOPFSReadStream(rId);\n // Mock a task to reuse transfer logic\n const task: LoadTask = {\n resourceId: rId,\n sessionId: sId,\n resource: this.model?.resources.get(rId)!,\n stream,\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n priority: 'normal',\n };\n await this.transferToDemuxWorker(task);\n } else if (type === 'image') {\n await this.transferCachedImage(rId, sId);\n }\n };\n\n // If resource is already ready, check if we need to transfer stream (Worker Pipeline)\n if (resource.state === 'ready') {\n if (options?.sessionId) {\n await transferResourceToWorker(resourceId, options.sessionId, resource.type);\n }\n return;\n }\n\n // Check if task already exists\n let taskPromise = this.taskManager.getTaskPromise(resourceId);\n let isCoveredByTask = false;\n\n if (taskPromise) {\n // Check if existing task covers this session\n // Use activeTasks map + taskQueue search instead of getTaskFromQueue\n const activeTask =\n this.taskManager.activeTasks.get(resourceId) ||\n this.taskManager.taskQueue.find((t) => t.resourceId === resourceId);\n\n if (activeTask && activeTask.sessionId === options?.sessionId) {\n isCoveredByTask = true;\n }\n } else {\n // No task, we create it.\n // enqueue() will create the task with promise\n this.enqueueLoad(\n resource,\n options?.priority || 'normal',\n options?.sessionId,\n options?.clipId,\n options?.trackId,\n options?.isMainTrack ?? false\n );\n // Get the newly created promise\n taskPromise = this.taskManager.getTaskPromise(resourceId);\n isCoveredByTask = true;\n }\n\n // Wait for task to complete\n await taskPromise;\n\n // If we weren't the primary session for the task, transfer now from cache/OPFS\n // We must re-check resource.state here because it might have failed\n const updatedResource = this.model?.resources.get(resourceId);\n if (!isCoveredByTask && updatedResource?.state === 'ready' && options?.sessionId) {\n await transferResourceToWorker(resourceId, options.sessionId, resource.type);\n }\n }\n\n cancel(resourceId: string): void {\n this.taskManager.cancelTask(resourceId);\n this.processQueue();\n }\n\n /**\n * Check if a resource is currently being loaded\n */\n isResourceLoading(resourceId: string): boolean {\n return this.taskManager.hasActiveTask(resourceId);\n }\n\n pause(resourceId: string): void {\n const task = this.taskManager.getActiveTask(resourceId);\n if (task) {\n task.controller?.abort();\n }\n }\n\n async resume(resourceId: string, options?: ResourceLoadOptions): Promise<void> {\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n throw new Error(`Resource ${resourceId} not found`);\n }\n\n const pausedTask = this.taskManager.getActiveTask(resourceId);\n\n if (pausedTask?.pausedAt !== undefined) {\n this.enqueueLoad(\n resource,\n options?.priority || 'normal',\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n } else {\n await this.fetch(resourceId, options);\n }\n }\n\n get activeTasks(): Map<string, LoadTask> {\n return this.taskManager.activeTasks;\n }\n\n get taskQueue(): LoadTask[] {\n return this.taskManager.taskQueue;\n }\n\n dispose(): void {\n this.taskManager.clear();\n this.blobCache.clear();\n this.pendingTransfers.clear();\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ResourceLoader.js","sources":["../../../src/stages/load/ResourceLoader.ts"],"sourcesContent":["import { type Resource, type CompositionModel, hasResourceId } from '../../model';\nimport type { ResourceLoadOptions, LoadTask, ResourceLoaderOptions } from './types';\nimport { TaskManager } from './TaskManager';\nimport { StreamFactory } from './StreamFactory';\nimport { EventPayloadMap, MeframeEvent } from '../../event/events';\nimport { EventBus } from '../../event/EventBus';\nimport { createImageBitmapFromBlob } from '../../utils/image-utils';\nimport { MP4IndexParser, type MP4ParseResult } from '../demux/MP4IndexParser';\nimport { MP3FrameParser } from '../demux/MP3FrameParser';\nimport type { CacheManager } from '../../cache/CacheManager';\nimport type { WorkerPool } from '../../worker/WorkerPool';\n\nexport class ResourceConflictError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ResourceConflictError';\n }\n}\n\nexport class ResourceLoader {\n private cacheManager: CacheManager;\n private workerPool: WorkerPool;\n private model?: CompositionModel;\n private taskManager: TaskManager;\n private streamFactory: StreamFactory;\n private eventBus?: EventBus<EventPayloadMap>;\n private onStateChange?: (resourceId: string, state: Resource['state']) => void;\n private blobCache = new Map<string, Blob>();\n private parsingIndexes = new Set<string>(); // Track in-progress index parsing\n\n // Preloading state\n private isPreloadingEnabled = true;\n private preloadQueue: string[] = [];\n\n constructor(options: ResourceLoaderOptions) {\n const config = options.config || {};\n const maxConcurrent = config.maxConcurrent ?? 2;\n\n this.taskManager = new TaskManager(maxConcurrent);\n this.streamFactory = new StreamFactory(options.onProgress, config);\n this.eventBus = options.eventBus;\n this.onStateChange = options.onStateChange;\n this.cacheManager = options.cacheManager;\n this.workerPool = options.workerPool;\n }\n\n async setModel(model: CompositionModel): Promise<void> {\n this.model = model;\n const mainTrack = model.tracks.find((track) => track.id === (model.mainTrackId || 'main'));\n if (mainTrack?.clips?.[0] && hasResourceId(mainTrack.clips[0])) {\n await this.fetch(mainTrack.clips[0].resourceId, {\n priority: 'high',\n clipId: mainTrack.clips[0].id,\n trackId: mainTrack.id,\n });\n }\n this.startPreloading();\n }\n\n setPreloadingEnabled(enabled: boolean): void {\n this.isPreloadingEnabled = enabled;\n if (enabled) {\n this.startPreloading();\n }\n }\n\n startPreloading(): void {\n if (!this.model || !this.isPreloadingEnabled) return;\n\n const mainTrack = this.model.tracks.find(\n (track) => track.id === (this.model?.mainTrackId || 'main')\n );\n if (!mainTrack) return;\n for (const clip of mainTrack.clips) {\n if (!hasResourceId(clip)) continue;\n const resource = this.model.getResource(clip.resourceId);\n if (!resource) continue;\n\n // Skip if already ready, loading, or error\n if (\n !resource ||\n resource.state === 'ready' ||\n resource.state === 'loading' ||\n resource.state === 'error'\n ) {\n continue;\n }\n\n this.fetch(resource.id, { priority: 'low' });\n }\n }\n\n private processPreloadQueue(): void {\n if (!this.isPreloadingEnabled || this.preloadQueue.length === 0) return;\n\n while (this.preloadQueue.length > 0) {\n const resourceId = this.preloadQueue.shift();\n if (!resourceId) break;\n\n // Use low priority for preloading\n this.fetch(resourceId, { priority: 'low' }).finally(() => {\n // Continue processing queue\n this.processPreloadQueue();\n });\n }\n }\n\n private enqueueLoad(\n resource: Resource,\n priority: 'high' | 'normal' | 'low' = 'normal',\n sessionId?: string,\n clipId?: string,\n trackId?: string\n ): LoadTask {\n // Check if task is already active\n const existingTask = this.taskManager.getActiveTask(resource.id);\n if (existingTask) {\n return existingTask;\n }\n\n // Create new task and enqueue\n const task = this.taskManager.enqueue(resource, priority, sessionId, clipId, trackId);\n this.processQueue();\n return task;\n }\n\n private processQueue(): void {\n while (this.taskManager.canProcess) {\n const task = this.taskManager.getNextTask();\n if (!task) break;\n this.startLoad(task);\n }\n }\n\n /**\n * Start loading a resource\n * Handles state management (loading → ready/error) for all resource types\n */\n private async startLoad(task: LoadTask): Promise<void> {\n this.taskManager.activateTask(task);\n let loadError: Error | undefined;\n\n try {\n this.updateResourceState(task.resourceId, 'loading');\n task.controller = new AbortController();\n\n // Route to different handlers based on resource type\n if (task.resource.type === 'image') {\n await this.loadImageBitmap(task);\n if (task.sessionId) {\n this.transferCachedImage(task);\n }\n } else if (task.resource.type === 'video') {\n // Video: OPFS cache + MP4 index parsing (for window cache architecture)\n const cached = await this.cacheManager.hasResourceInCache(task.resourceId);\n if (cached) {\n // Resource already in OPFS - ensure index is parsed\n await this.ensureIndexParsed(task.resourceId);\n\n // If sessionId is present (Worker Pipeline active), transfer OPFS stream to worker\n if (task.sessionId) {\n const stream = await this.createOPFSReadStream(task.resourceId);\n task.stream = stream;\n await this.transferToDemuxWorker(task);\n }\n } else {\n // Not cached - download and cache to OPFS\n await this.loadWithOPFSCache(task);\n }\n } else if (task.resource.type === 'audio') {\n // Check if already cached in AudioSampleCache\n if (!this.cacheManager.audioSampleCache.has(task.resourceId)) {\n // Not cached - load and parse\n await this.loadAndParseAudioFile(task);\n }\n // If already cached, skip loading (reuse existing cache)\n } else if (task.resource.type === 'json' || task.resource.type === 'text') {\n await this.loadTextResource(task);\n }\n\n // Unified state update for all resource types\n this.updateResourceState(task.resourceId, 'ready');\n } catch (error) {\n task.error = error as Error;\n loadError = error as Error;\n this.updateResourceState(task.resourceId, 'error');\n } finally {\n this.taskManager.completeTask(task.resourceId, loadError);\n this.processQueue();\n }\n }\n\n /**\n * Ensure MP4 index is parsed for a cached resource\n */\n async ensureIndexParsed(resourceId: string): Promise<void> {\n // Check if index already exists\n if (this.cacheManager.mp4IndexCache.has(resourceId)) {\n return;\n }\n\n // Check if already parsing (avoid duplicate parsing for same resource)\n if (this.parsingIndexes.has(resourceId)) {\n // Wait for the in-progress parsing to complete\n while (this.parsingIndexes.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n return;\n }\n\n // Mark as parsing\n this.parsingIndexes.add(resourceId);\n\n try {\n // Parse from OPFS file\n const stream = await this.createOPFSReadStream(resourceId);\n // Create minimal task for parsing (no clipId since this is a background index parse)\n const parseTask: LoadTask = {\n resourceId,\n resource: { id: resourceId, type: 'video', uri: '' },\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n priority: 'normal',\n };\n await this.parseIndexFromStream(parseTask, stream);\n } finally {\n // Remove from parsing set\n this.parsingIndexes.delete(resourceId);\n }\n }\n\n /**\n * Create ReadableStream from OPFS file\n * Reuses OPFSManager's underlying file access\n */\n private async createOPFSReadStream(resourceId: string): Promise<ReadableStream<Uint8Array>> {\n const opfsManager = this.cacheManager.resourceCache.opfsManager;\n const projectId = this.cacheManager.resourceCache.projectId;\n\n // Get file handle from OPFS\n const dir = await opfsManager.getProjectDir(projectId, 'resource');\n const fileName = `${resourceId}.mp4`;\n const fileHandle = await dir.getFileHandle(fileName);\n const file = await fileHandle.getFile();\n\n // Return native stream\n return file.stream();\n }\n\n /**\n * Load resource and cache to OPFS + parse moov + extract first frame\n *\n * Strategy: Parallel tee() approach for fast first frame\n * - One stream writes to OPFS (background)\n * - Another stream parses moov and extracts first GOP\n * - First frame is decoded and cached immediately\n */\n private async loadWithOPFSCache(task: LoadTask): Promise<void> {\n const stream = await this.streamFactory.createRegularStream(task);\n if (!stream) {\n throw new Error(`Failed to create stream for ${task.resourceId}`);\n }\n\n // Prepare streams: 1 for OPFS, 1 for parsing, optional 1 for Worker\n let opfsStream: ReadableStream<Uint8Array>;\n let parseStream: ReadableStream<Uint8Array>;\n let workerStream: ReadableStream<Uint8Array> | undefined;\n\n if (task.sessionId) {\n // If worker needs it, split into 3 ways\n const [branch1, branch2] = stream.tee();\n const [branch2a, branch2b] = branch2.tee();\n\n opfsStream = branch1;\n parseStream = branch2a;\n workerStream = branch2b;\n } else {\n // Just 2 ways\n const [s1, s2] = stream.tee();\n opfsStream = s1;\n parseStream = s2;\n }\n\n const promises: Promise<void>[] = [\n this.writeToOPFS(task.resourceId, opfsStream),\n this.parseIndexFromStream(task, parseStream),\n ];\n\n if (workerStream && task.sessionId) {\n // Assign stream to task so transferToDemuxWorker uses it\n // Note: we need to clone the task or modify it carefully, but here it's safe\n // We create a temp task object or just modify current (since stream is consumed)\n // Actually transferToDemuxWorker uses task.stream.\n // We can't modify task.stream in place easily if type is readonly-ish, but it's not.\n const workerTask = { ...task, stream: workerStream };\n promises.push(this.transferToDemuxWorker(workerTask));\n }\n\n // Parallel execution\n await Promise.all(promises);\n }\n\n /**\n * Write resource stream to OPFS\n */\n private async writeToOPFS(resourceId: string, stream: ReadableStream<Uint8Array>): Promise<void> {\n await this.cacheManager.resourceCache.writeResource(resourceId, stream);\n }\n\n /**\n * Load and parse audio file (MP3/WAV) in main thread\n * Extract EncodedAudioChunk and cache to AudioSampleCache\n * Aligned with video audio track extraction (unified architecture)\n */\n private async loadAndParseAudioFile(task: LoadTask): Promise<void> {\n const { resourceId } = task;\n\n try {\n // TODO: Streaming download and parse?\n const blob = await this.fetchBlob(task.resource.uri, task.controller!.signal);\n\n // Convert blob to ArrayBuffer\n const arrayBuffer = await blob.arrayBuffer();\n const uint8Array = new Uint8Array(arrayBuffer);\n\n // Parse MP3 frames using MP3FrameParser\n const parser = new MP3FrameParser();\n const { frames, config } = parser.push(uint8Array);\n const remainingFrames = parser.flush();\n const allFrames = [...frames, ...remainingFrames];\n\n if (!config) {\n throw new Error(`Failed to parse audio config for ${resourceId}`);\n }\n\n // Convert MP3Frame to EncodedAudioChunk\n const audioChunks: EncodedAudioChunk[] = allFrames.map((frame) => {\n return new EncodedAudioChunk({\n type: 'key', // MP3 frames are all key frames\n timestamp: frame.timestampUs,\n duration: frame.durationUs,\n data: frame.data,\n });\n });\n\n // Build AudioDecoderConfig from MP3Config\n const audioConfig: AudioDecoderConfig = {\n codec: 'mp3',\n sampleRate: config.sampleRate,\n numberOfChannels: config.channels,\n };\n\n // Cache to AudioSampleCache\n this.cacheManager.audioSampleCache.set(resourceId, audioChunks, audioConfig);\n } catch (error) {\n console.error(`[ResourceLoader] Failed to parse audio file ${resourceId}:`, error);\n throw error;\n }\n }\n\n /**\n * Parse moov from stream and cache index + audio samples + decode first frame\n */\n private async parseIndexFromStream(\n task: LoadTask,\n stream: ReadableStream<Uint8Array>\n ): Promise<void> {\n const { resourceId, clipId } = task;\n\n try {\n const parser = new MP4IndexParser();\n\n const result: MP4ParseResult = await parser.parseFromStream(stream, {\n onFirstFrameReady: async (index, chunks) => {\n // Set resourceId on index\n index.resourceId = resourceId;\n\n // Cache index immediately\n this.cacheManager.mp4IndexCache.set(resourceId, index);\n\n // Emit event with chunks for Orchestrator to handle\n // Only if clipId is provided (indicates this is a preview/cover request)\n if (clipId) {\n this.eventBus?.emit(MeframeEvent.ResourceFirstFrameReady, {\n resourceId,\n clipId,\n index,\n chunks,\n });\n }\n },\n });\n\n result.index.resourceId = resourceId;\n\n // Cache index (if not already cached by onFirstFrameReady)\n if (!this.cacheManager.mp4IndexCache.has(resourceId)) {\n this.cacheManager.mp4IndexCache.set(resourceId, result.index);\n }\n\n // Cache audio samples if present\n if (result.audioSamples && result.audioMetadata) {\n this.cacheManager.audioSampleCache.set(\n resourceId,\n result.audioSamples,\n result.audioMetadata\n );\n } else {\n // Ensure cache knows this resource has NO audio\n // This prevents GlobalAudioSession from waiting for it\n // AudioSampleCache should ideally support storing \"empty\" state or we just rely on has() returning false\n // But has() returning false triggers fetch.\n // We need a way to say \"fetched, but no audio\".\n // Currently AudioSampleCache.set requires samples.\n // We might need to update AudioSampleCache to support explicit \"no audio\" record\n // Or we rely on MP4Index having audio track info.\n }\n } catch (error) {\n console.error(`[ResourceLoader] Failed to parse MP4 index for ${resourceId}:`, error);\n // Rethrow error to ensure resource is marked as failed\n // In the new architecture (Window Cache + AudioSampleCache), index parsing is critical.\n throw error;\n }\n }\n\n /**\n * Load text-based resources (json, text)\n * Just download the content - state management is handled by startLoad()\n */\n private async loadTextResource(task: LoadTask): Promise<void> {\n await this.fetchBlob(task.resource.uri, task.controller!.signal);\n // For json/text, just verify we can download it\n // Future: could parse and validate json here if needed\n }\n\n async loadImage(resource: Resource): Promise<ImageBitmap> {\n const task: LoadTask = {\n resourceId: resource.id,\n resource: resource,\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n priority: 'normal',\n controller: new AbortController(),\n };\n return this.loadImageBitmap(task);\n }\n\n /**\n * Load image resource: fetch blob → create ImageBitmap → cache in CacheManager\n * Note: Images don't need streaming (typically < 5MB)\n *\n * Strategy for window cache architecture:\n * - Store ImageBitmap in CacheManager.imageBitmapCache (main thread accessible)\n * - OnDemandComposeSession retrieves from cache for composition\n * - No longer transfer to Worker (composition is in main thread)\n */\n private async loadImageBitmap(task: LoadTask): Promise<ImageBitmap> {\n // Check cache\n let blob = this.blobCache.get(task.resourceId);\n\n if (!blob) {\n // Not cached: download and cache\n blob = await this.fetchBlob(task.resource.uri, task.controller!.signal);\n this.blobCache.set(task.resourceId, blob);\n }\n\n // Create ImageBitmap from Blob (with special handling for SVG)\n const imageBitmap = await createImageBitmapFromBlob(blob);\n return imageBitmap;\n }\n\n /**\n * Fetch resource as blob (for images, json, etc.)\n */\n private async fetchBlob(uri: string, signal: AbortSignal): Promise<Blob> {\n const response = await fetch(uri, { signal });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return response.blob();\n }\n\n /**\n * Transfer cached image to a session\n * Creates new ImageBitmap from cached Blob and transfers to worker\n */\n private async transferCachedImage(task: LoadTask): Promise<void> {\n const { resourceId, sessionId } = task;\n const blob = this.blobCache.get(resourceId);\n if (!blob || !sessionId) return;\n\n // Create new ImageBitmap from cached Blob (with SVG support)\n const imageBitmap = await createImageBitmapFromBlob(blob);\n\n // Transfer to Worker\n const composeWorker = await this.workerPool.get('videoCompose', sessionId);\n\n await composeWorker?.send?.(\n 'receive_image',\n {\n resourceId,\n sessionId,\n imageBitmap,\n },\n { transfer: [imageBitmap] }\n );\n }\n\n /**\n * Transfer stream to demux worker (for audio files)\n */\n private async transferToDemuxWorker(task: LoadTask): Promise<void> {\n if (!task.stream) return;\n\n if (!task.sessionId) {\n // Skip demux worker transfer if no sessionId (e.g., during preload)\n // Resource is already downloaded to OPFS, demux will happen later when needed\n task.stream.cancel();\n return;\n }\n\n const workerType = task.resource.type === 'video' ? 'videoDemux' : 'audioDemux';\n const demuxWorker = await this.workerPool.get(workerType, task.sessionId);\n if (demuxWorker) {\n await demuxWorker.sendStream(task.stream, {\n sessionId: task.sessionId,\n ...task.metadata,\n ...(task.range && { range: task.range }),\n ...(task.trackId && { trackId: task.trackId }),\n });\n } else {\n // Demux worker not ready - cancel stream\n task.stream.cancel();\n }\n }\n\n private updateResourceState(resourceId: string, state: Resource['state']): void {\n const resource = this.model?.resources.get(resourceId);\n if (resource) {\n const oldState = resource.state;\n resource.state = state;\n this.eventBus?.emit(MeframeEvent.ResourceStageChange, {\n type: MeframeEvent.ResourceStageChange,\n resourceId,\n oldState,\n newState: state,\n });\n }\n\n this.onStateChange?.(resourceId, state);\n }\n\n /**\n * Fetch a resource and wait for loading + parsing to complete\n *\n * Returns a Promise that resolves when:\n * - Resource is fully loaded, parsed, and cached (state='ready')\n * - Or rejects if loading/parsing fails\n *\n * Promise lifecycle:\n * 1. enqueueLoad() creates LoadTask with promise/resolve/reject (or reuses existing)\n * 2. processQueue() → startLoad() executes async in background\n * 3. startLoad() completes → finally → completeTask() → task.resolve()/reject()\n */\n async fetch(resourceId?: string, options?: ResourceLoadOptions): Promise<void> {\n if (!resourceId) {\n return;\n }\n\n const resource = this.model?.resources.get(resourceId);\n if (!resource) {\n console.warn(`Resource ${resourceId} not found in model`);\n return;\n }\n\n // Enqueue task (returns existing task if already queued/active)\n const task = this.enqueueLoad(\n resource,\n options?.priority || 'normal',\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // Wait for task completion\n return task.promise;\n }\n\n cancel(resourceId: string): void {\n this.taskManager.cancelTask(resourceId);\n this.processQueue();\n }\n\n /**\n * Check if a resource is currently being loaded\n */\n isResourceLoading(resourceId: string): boolean {\n return this.taskManager.hasActiveTask(resourceId);\n }\n\n pause(resourceId: string): void {\n const task = this.taskManager.getActiveTask(resourceId);\n if (task) {\n task.controller?.abort();\n }\n }\n\n async resume(resourceId: string, options?: ResourceLoadOptions): Promise<void> {\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n throw new Error(`Resource ${resourceId} not found`);\n }\n\n const pausedTask = this.taskManager.getActiveTask(resourceId);\n\n if (pausedTask?.pausedAt !== undefined) {\n this.enqueueLoad(\n resource,\n options?.priority || 'normal',\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n } else {\n await this.fetch(resourceId, options);\n }\n }\n\n get activeTasks(): Map<string, LoadTask> {\n return this.taskManager.activeTasks;\n }\n\n get taskQueue(): LoadTask[] {\n return this.taskManager.taskQueue;\n }\n\n dispose(): void {\n this.taskManager.clear();\n this.blobCache.clear();\n }\n}\n"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ import { WorkerPool } from '../../worker/WorkerPool';
8
8
  export interface LoaderConfig {
9
9
  highWaterMark?: number;
10
10
  maxConcurrent?: number;
11
+ preloadConcurrency?: number;
11
12
  chunkSize?: number;
12
13
  retryCount?: number;
13
14
  retryDelay?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/stages/load/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/stages/load/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG1D,MAAM,WAAW,YAAY;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACrC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAGD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;CACtC;AAGD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAGD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAGD,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,OAAO,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC;AAGD,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,CAAC,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAC9C,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;CACxE;AAGD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,CAAC,EAAE,mBAAmB,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,KAAK,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;CAC3B;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,YAAY,CAAC;AAEpF,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,MAAM,GAAG,WAAW,GAAG,YAAY,CAAC;AAElF,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,mBAAmB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,iBAAiB,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,gBAAgB,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,gBAAgB,CAAC;IACvB,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;CAC3D;AAED,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE9F,MAAM,WAAW,kBAAkB;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE;QACL,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,2BAA4B,SAAQ,mBAAmB;IACtE,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,2BAA2B,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Utility functions for processing AudioData and PCM buffers
3
+ */
4
+ export interface AudioMetadata {
5
+ sampleRate: number;
6
+ numberOfChannels: number;
7
+ }
8
+ /**
9
+ * Extract PCM data from AudioData object into Float32Array planes
10
+ */
11
+ export declare function extractPlanesFromAudioData(audioData: AudioData, numberOfChannels: number, numberOfFrames: number): Float32Array[];
12
+ /**
13
+ * Apply volume/gain to PCM data
14
+ */
15
+ export declare function applyGain(planes: Float32Array[], volume: number, muted: boolean): Float32Array[];
16
+ //# sourceMappingURL=audio-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-data.d.ts","sourceRoot":"","sources":["../../src/utils/audio-data.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,SAAS,EACpB,gBAAgB,EAAE,MAAM,EACxB,cAAc,EAAE,MAAM,GACrB,YAAY,EAAE,CAiIhB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,EAAE,CAgBhG"}