@meframe/core 0.0.38-beta.3 → 0.0.39

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 (46) hide show
  1. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
  2. package/dist/orchestrator/ExportScheduler.js +11 -7
  3. package/dist/orchestrator/ExportScheduler.js.map +1 -1
  4. package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
  5. package/dist/orchestrator/GlobalAudioSession.js +19 -17
  6. package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
  7. package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
  8. package/dist/orchestrator/OnDemandVideoSession.js +1 -21
  9. package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
  10. package/dist/orchestrator/Orchestrator.d.ts +5 -1
  11. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  12. package/dist/orchestrator/Orchestrator.js +39 -26
  13. package/dist/orchestrator/Orchestrator.js.map +1 -1
  14. package/dist/stages/compose/FrameRateConverter.d.ts +6 -1
  15. package/dist/stages/compose/FrameRateConverter.d.ts.map +1 -1
  16. package/dist/stages/decode/BaseDecoder.d.ts.map +1 -1
  17. package/dist/stages/decode/BaseDecoder.js +0 -29
  18. package/dist/stages/decode/BaseDecoder.js.map +1 -1
  19. package/dist/stages/decode/VideoChunkDecoder.d.ts.map +1 -1
  20. package/dist/stages/encode/BaseEncoder.d.ts.map +1 -1
  21. package/dist/stages/encode/BaseEncoder.js +0 -24
  22. package/dist/stages/encode/BaseEncoder.js.map +1 -1
  23. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  24. package/dist/stages/load/ResourceLoader.js +36 -20
  25. package/dist/stages/load/ResourceLoader.js.map +1 -1
  26. package/dist/stages/load/TaskManager.d.ts +6 -1
  27. package/dist/stages/load/TaskManager.d.ts.map +1 -1
  28. package/dist/stages/load/TaskManager.js +11 -8
  29. package/dist/stages/load/TaskManager.js.map +1 -1
  30. package/dist/stages/load/types.d.ts +3 -3
  31. package/dist/stages/load/types.d.ts.map +1 -1
  32. package/dist/workers/{BaseDecoder.DWHTBMDB.js → BaseDecoder.CB5XmTpS.js} +1 -30
  33. package/dist/workers/BaseDecoder.CB5XmTpS.js.map +1 -0
  34. package/dist/workers/stages/compose/{video-compose.worker.BhpN-lxf.js → video-compose.worker.DM_bsOY8.js} +27 -4
  35. package/dist/workers/stages/compose/{video-compose.worker.BhpN-lxf.js.map → video-compose.worker.DM_bsOY8.js.map} +1 -1
  36. package/dist/workers/stages/decode/{audio-decode.worker.BsuBzFx5.js → audio-decode.worker.B__6tqsy.js} +2 -2
  37. package/dist/workers/stages/decode/{audio-decode.worker.BsuBzFx5.js.map → audio-decode.worker.B__6tqsy.js.map} +1 -1
  38. package/dist/workers/stages/decode/{video-decode.worker.CwOjEmre.js → video-decode.worker.tOv-QR2f.js} +2 -4
  39. package/dist/workers/stages/decode/video-decode.worker.tOv-QR2f.js.map +1 -0
  40. package/dist/workers/stages/encode/{video-encode.worker.CXgr5E16.js → video-encode.worker.D8pfFber.js} +1 -25
  41. package/dist/workers/stages/encode/video-encode.worker.D8pfFber.js.map +1 -0
  42. package/dist/workers/worker-manifest.json +4 -4
  43. package/package.json +1 -1
  44. package/dist/workers/BaseDecoder.DWHTBMDB.js.map +0 -1
  45. package/dist/workers/stages/decode/video-decode.worker.CwOjEmre.js.map +0 -1
  46. package/dist/workers/stages/encode/video-encode.worker.CXgr5E16.js.map +0 -1
@@ -147,11 +147,11 @@ class Orchestrator {
147
147
  if (!this.compositionModel) return null;
148
148
  const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];
149
149
  if (!clip || !hasResourceId(clip)) return null;
150
- const relativeTimeUs = timeUs - clip.startUs;
150
+ const resourceTimeUs = timeUs - clip.startUs + (clip.trimStartUs ?? 0);
151
151
  const resourceId = clip.resourceId;
152
152
  const keyframeSample = this.cacheManager.mp4IndexCache.getNearestKeyframe(
153
153
  resourceId,
154
- relativeTimeUs
154
+ resourceTimeUs
155
155
  );
156
156
  if (!keyframeSample) return null;
157
157
  const cachedKeyframe = this.cacheManager.getFrame(keyframeSample.timestamp, clip.id);
@@ -163,7 +163,7 @@ class Orchestrator {
163
163
  const session = await OnDemandVideoSession.create({
164
164
  clipId: clip.id,
165
165
  resourceId,
166
- targetTimeUs: relativeTimeUs,
166
+ targetTimeUs: resourceTimeUs,
167
167
  globalTimeUs: timeUs,
168
168
  resourceLoader: this.resourceLoader,
169
169
  mp4IndexCache: this.cacheManager.mp4IndexCache,
@@ -172,7 +172,7 @@ class Orchestrator {
172
172
  fps: this.compositionModel.fps ?? 30
173
173
  });
174
174
  try {
175
- const keyframeTimeUs = await session.decodeKeyframe(relativeTimeUs);
175
+ const keyframeTimeUs = await session.decodeKeyframe(resourceTimeUs);
176
176
  return keyframeTimeUs;
177
177
  } catch (error) {
178
178
  console.warn("[Orchestrator] Fast keyframe decode failed:", error);
@@ -234,41 +234,47 @@ class Orchestrator {
234
234
  if (!clip) {
235
235
  return null;
236
236
  }
237
- let relativeTimeUs = options?.relativeTimeUs ?? timeUs - clip.startUs;
238
- relativeTimeUs = Math.min(relativeTimeUs, clip.durationUs - 1);
237
+ const trimStartUs = clip.trimStartUs ?? 0;
238
+ let resourceTimeUs = options?.relativeTimeUs ? options.relativeTimeUs + trimStartUs : timeUs - clip.startUs + trimStartUs;
239
+ resourceTimeUs = Math.min(resourceTimeUs, trimStartUs + clip.durationUs - 1);
239
240
  if (!preheat) {
240
- const cachedFrame = this.cacheManager.getFrame(relativeTimeUs, clip.id);
241
+ const cachedFrame = this.cacheManager.getFrame(resourceTimeUs, clip.id);
241
242
  if (cachedFrame) {
242
243
  this.eventBus.emit(MeframeEvent.CacheHit, {
243
244
  timeUs,
244
245
  level: "L1",
245
- key: `${clip.id}-${relativeTimeUs}`
246
+ key: `${clip.id}-${resourceTimeUs}`
246
247
  });
247
248
  return cachedFrame;
248
249
  }
249
250
  this.eventBus.emit(MeframeEvent.CacheMiss, {
250
251
  timeUs,
251
252
  level: "L1",
252
- key: `${clip.id}-${relativeTimeUs}`
253
+ key: `${clip.id}-${resourceTimeUs}`
253
254
  });
254
255
  }
255
256
  if (signal?.aborted) {
256
257
  return null;
257
258
  }
258
- const resourceFrame = await this.decodeFromResource(clip, relativeTimeUs, timeUs, options);
259
+ const resourceFrame = await this.decodeFromResource(clip, resourceTimeUs, timeUs, options);
259
260
  return resourceFrame;
260
261
  }
261
262
  /**
262
263
  * Compose frame from OPFS resource (on-demand decoding)
263
264
  * This is the new path for long clips with window caching
265
+ *
266
+ * @param clip - The clip to decode
267
+ * @param resourceTimeUs - Time in resource-relative coordinates (same as frame.timestamp)
268
+ * @param globalTimeUs - Time in composition timeline
264
269
  */
265
- async decodeFromResource(clip, relativeTimeUs, globalTimeUs, options) {
270
+ async decodeFromResource(clip, resourceTimeUs, globalTimeUs, options) {
266
271
  if (!hasResourceId(clip)) return null;
267
272
  const resourceId = clip.resourceId;
273
+ const trimStartUs = clip.trimStartUs ?? 0;
268
274
  const resource = this.compositionModel?.getResource(resourceId);
269
275
  const isReady = resource?.state === "ready";
270
276
  const fetchOptions = {
271
- priority: "high",
277
+ isPreload: false,
272
278
  clipId: clip.id,
273
279
  trackId: clip.trackId
274
280
  };
@@ -281,7 +287,7 @@ class Orchestrator {
281
287
  const session = await OnDemandVideoSession.create({
282
288
  clipId: clip.id,
283
289
  resourceId,
284
- targetTimeUs: relativeTimeUs,
290
+ targetTimeUs: resourceTimeUs,
285
291
  globalTimeUs,
286
292
  resourceLoader: this.resourceLoader,
287
293
  mp4IndexCache: this.cacheManager.mp4IndexCache,
@@ -292,10 +298,13 @@ class Orchestrator {
292
298
  this.activeOnDemandSession = session;
293
299
  try {
294
300
  const DECODE_WINDOW_SIZE = 3e6;
295
- const windowStart = relativeTimeUs;
296
- const windowEnd = Math.min(clip.durationUs, relativeTimeUs + DECODE_WINDOW_SIZE);
301
+ const windowStart = resourceTimeUs;
302
+ const windowEnd = Math.min(
303
+ trimStartUs + clip.durationUs,
304
+ resourceTimeUs + DECODE_WINDOW_SIZE
305
+ );
297
306
  await session.decodeWindow(windowStart, windowEnd);
298
- return this.cacheManager.getFrame(relativeTimeUs, clip.id);
307
+ return this.cacheManager.getFrame(resourceTimeUs, clip.id);
299
308
  } catch (error) {
300
309
  if (session.isDisposed) {
301
310
  return null;
@@ -407,7 +416,7 @@ class Orchestrator {
407
416
  * Used by PlaybackController for cross-clip window preheating
408
417
  *
409
418
  * @param clipId - Clip identifier
410
- * @param clipRelativeStart - Start time relative to clip (microseconds)
419
+ * @param clipRelativeStart - Start time relative to clip (microseconds), 0 = clip start
411
420
  * @param clipRelativeEnd - End time relative to clip (microseconds)
412
421
  * @param globalTimeUs - Global timeline position (for globalTimeUs in cache)
413
422
  */
@@ -416,15 +425,18 @@ class Orchestrator {
416
425
  const clip = this.compositionModel.findClip(clipId);
417
426
  if (!clip || !hasResourceId(clip)) return;
418
427
  const resourceId = clip.resourceId;
428
+ const trimStartUs = clip.trimStartUs ?? 0;
429
+ const resourceStart = clipRelativeStart + trimStartUs;
430
+ const resourceEnd = clipRelativeEnd + trimStartUs;
419
431
  await this.resourceLoader.load(resourceId, {
420
- priority: "normal",
432
+ isPreload: false,
421
433
  clipId: clip.id,
422
434
  trackId: clip.trackId
423
435
  });
424
436
  const session = await OnDemandVideoSession.create({
425
437
  clipId: clip.id,
426
438
  resourceId,
427
- targetTimeUs: clipRelativeStart,
439
+ targetTimeUs: resourceStart,
428
440
  globalTimeUs,
429
441
  resourceLoader: this.resourceLoader,
430
442
  mp4IndexCache: this.cacheManager.mp4IndexCache,
@@ -433,7 +445,7 @@ class Orchestrator {
433
445
  fps: this.compositionModel.fps ?? 30
434
446
  });
435
447
  try {
436
- await session.decodeWindow(clipRelativeStart, clipRelativeEnd);
448
+ await session.decodeWindow(resourceStart, resourceEnd);
437
449
  } catch (error) {
438
450
  console.warn(`[Orchestrator] Preheat clip ${clipId} window failed:`, error);
439
451
  } finally {
@@ -456,7 +468,8 @@ class Orchestrator {
456
468
  if (!clip) {
457
469
  return null;
458
470
  }
459
- const relativeTimeUs = timeUs - clip.startUs;
471
+ const clipRelativeTimeUs = timeUs - clip.startUs;
472
+ const resourceTimeUs = clipRelativeTimeUs + (clip.trimStartUs ?? 0);
460
473
  const instructions = this.planner.getInstructions(clip.id);
461
474
  if (!instructions) {
462
475
  return null;
@@ -470,11 +483,11 @@ class Orchestrator {
470
483
  return false;
471
484
  }
472
485
  return layer.activeRanges.some(
473
- (range) => relativeTimeUs >= range.startUs && relativeTimeUs < range.endUs
486
+ (range) => clipRelativeTimeUs >= range.startUs && clipRelativeTimeUs < range.endUs
474
487
  );
475
488
  });
476
489
  for (const layerPlan of activeLayers) {
477
- const layer = await this.materializeLayer(layerPlan, clip, relativeTimeUs, timeUs);
490
+ const layer = await this.materializeLayer(layerPlan, clip, resourceTimeUs, timeUs);
478
491
  if (layer) {
479
492
  layers.push(layer);
480
493
  }
@@ -484,7 +497,7 @@ class Orchestrator {
484
497
  /**
485
498
  * Materialize a serialized layer plan into concrete Layer
486
499
  */
487
- async materializeLayer(layerPlan, clip, clipRelativeTimeUs, globalTimeUs) {
500
+ async materializeLayer(layerPlan, clip, resourceTimeUs, globalTimeUs) {
488
501
  const baseLayer = {
489
502
  id: layerPlan.layerId,
490
503
  type: layerPlan.type,
@@ -493,9 +506,9 @@ class Orchestrator {
493
506
  opacity: layerPlan.opacity ?? 1
494
507
  };
495
508
  if (layerPlan.type === "video" && !layerPlan.payload.attachmentId) {
496
- const rcFrame = this.cacheManager.getFrame(clipRelativeTimeUs, clip.id);
509
+ const rcFrame = this.cacheManager.getFrame(resourceTimeUs, clip.id);
497
510
  if (!rcFrame) {
498
- console.warn("[Orchestrator] Video frame not found in L1:", clip.id, clipRelativeTimeUs);
511
+ console.warn("[Orchestrator] Video frame not found in L1:", clip.id, resourceTimeUs);
499
512
  return null;
500
513
  }
501
514
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"Orchestrator.js","sources":["../../src/orchestrator/Orchestrator.ts"],"sourcesContent":["import { EventBus } from '../event/EventBus';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { applyPatch as applyModelPatch } from '../model/patch';\nimport { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, RenderFrameOptions } from './types';\nimport { WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame, Clip } from '../model';\nimport { hasResourceId } from '../model/types';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { GlobalAudioSession } from './GlobalAudioSession';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { ExportOptions } from '../types';\nimport { OnDemandVideoSession } from './OnDemandVideoSession';\nimport { ExportScheduler } from './ExportScheduler';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n muxManager: MuxManager;\n exportScheduler: ExportScheduler;\n\n private isInitialized = false;\n private config = ConfigLoader.getInstance().getConfig();\n private ensureCacheDebounceTimer: number | null = null;\n private activeOnDemandSession: OnDemandVideoSession | null = null;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n // Initialize WorkerPool with worker path from config\n this.workers = new WorkerPool({\n eventBus: this.eventBus,\n workerConfigs,\n workerPath: config.workerPath,\n workerExtension: config.workerExtension,\n });\n\n const maxMemoryMB = config.cacheConfig?.l1Size || this.config.cache?.l1?.maxMemoryMB || 1024;\n const maxGOPs = this.config.decode?.video?.maxGOPs || 4;\n\n this.cacheManager = new CacheManager(\n {\n l1: {\n maxMemoryMB,\n maxGOPs,\n },\n resource: {\n projectId: config.projectId || this.config.global.projectId || 'default',\n },\n },\n this.eventBus\n );\n\n this.resourceLoader = new ResourceLoader({\n cacheManager: this.cacheManager,\n workerPool: this.workers,\n eventBus: this.eventBus,\n config: {\n maxConcurrent: this.config.load.maxConcurrent,\n preloadConcurrency: 2, // Fixed preload concurrency for idle background loading\n },\n onStateChange: (resourceId, state) => this.handleResourceStateChange(resourceId, state),\n });\n\n this.planner = new CompositionPlanner();\n\n this.audioSession = new GlobalAudioSession({\n cacheManager: this.cacheManager,\n workerPool: this.workers,\n resourceLoader: this.resourceLoader,\n eventBus: this.eventBus,\n buildWorkerConfigs: () => this.buildWorkerConfigs(),\n });\n\n this.muxManager = new MuxManager(\n this.cacheManager,\n this.audioSession,\n this.config.encode.audio as AudioEncoderConfig\n );\n\n this.exportScheduler = new ExportScheduler({\n workerPool: this.workers,\n planner: this.planner,\n cacheManager: this.cacheManager,\n resourceLoader: this.resourceLoader,\n muxManager: this.muxManager,\n audioSession: this.audioSession,\n workerConfigsProvider: () => this.buildWorkerConfigs(),\n eventBus: this.eventBus,\n });\n\n this.setupResourceFirstFrameHandler();\n this.setupPreloadHandlers();\n }\n\n private setupResourceFirstFrameHandler(): void {\n this.eventBus.on(MeframeEvent.ResourceFirstFrameReady, async (payload) => {\n const { resourceId, clipId, index, chunks } = payload;\n\n if (!this.compositionModel) return;\n\n // Find the specific clip\n const clip = this.compositionModel.findClip(clipId);\n if (!clip || !clip.trackId) return;\n\n // Only decode first frame for clips that start at composition time 0\n // (these clips need the resource's first frame as cover)\n if (clip.startUs === 0) {\n const fps = this.compositionModel.fps ?? 30;\n await OnDemandVideoSession.decodeAndCacheFirstFrame(\n resourceId,\n chunks,\n index,\n clip,\n this.cacheManager,\n fps\n );\n }\n });\n }\n\n private setupPreloadHandlers(): void {\n // Stop preloading when playback starts\n this.eventBus.on(MeframeEvent.PlaybackPlay, () => {\n this.resourceLoader.setPreloadingEnabled(false);\n });\n\n // Enable preloading when playback pauses/stops\n this.eventBus.on(MeframeEvent.PlaybackPause, () => {\n this.resourceLoader.setPreloadingEnabled(true);\n });\n\n this.eventBus.on(MeframeEvent.PlaybackStop, () => {\n this.resourceLoader.setPreloadingEnabled(true);\n });\n\n // Note: ModelSet and PatchApplied are handled internally in ResourceLoader via handleModelSet\n // and direct calls isn't needed as ResourceLoader listens to updateResourceState via onStateChange?\n // Wait, ResourceLoader handles ModelSet via eventHandlers.\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n await this.cacheManager.init();\n\n this.isInitialized = true;\n }\n\n // Event methods - forward to eventBus\n on<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.on(event, handler);\n }\n\n off<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.off(event, handler);\n }\n\n once<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.once(event, handler);\n }\n\n cancelActiveDecoding(): void {\n if (this.activeOnDemandSession) {\n void this.activeOnDemandSession.dispose();\n this.activeOnDemandSession = null;\n }\n }\n\n /**\n * Decode and render nearest keyframe for fast seek preview\n * Returns the keyframe timestamp if successfully decoded, null otherwise\n */\n async tryRenderKeyframe(timeUs: TimeUs): Promise<TimeUs | null> {\n if (!this.compositionModel) return null;\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip || !hasResourceId(clip)) return null;\n\n const relativeTimeUs = timeUs - clip.startUs;\n const resourceId = clip.resourceId;\n\n // Check if keyframe is already in L1 cache\n const keyframeSample = this.cacheManager.mp4IndexCache.getNearestKeyframe(\n resourceId,\n relativeTimeUs\n );\n\n if (!keyframeSample) return null;\n\n const cachedKeyframe = this.cacheManager.getFrame(keyframeSample.timestamp, clip.id);\n if (cachedKeyframe) {\n return keyframeSample.timestamp;\n }\n\n // Resource must be ready to decode keyframe\n const resource = this.compositionModel.getResource(resourceId);\n if (resource?.state !== 'ready') return null;\n\n // Create temporary session to decode keyframe\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: relativeTimeUs,\n globalTimeUs: timeUs,\n resourceLoader: this.resourceLoader,\n mp4IndexCache: this.cacheManager.mp4IndexCache,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel,\n fps: this.compositionModel.fps ?? 30,\n });\n\n try {\n const keyframeTimeUs = await session.decodeKeyframe(relativeTimeUs);\n return keyframeTimeUs;\n } catch (error) {\n console.warn('[Orchestrator] Fast keyframe decode failed:', error);\n return null;\n } finally {\n await session.dispose();\n }\n }\n\n async setCompositionModel(model: CompositionModel): Promise<void> {\n this.compositionModel = model;\n this.cacheManager.clear();\n this.planner.setModel(model);\n // ensure the cover resource is preloaded before audio is activated\n await this.resourceLoader.setModel(model);\n this.audioSession.setModel(model);\n\n this.eventBus.emit(MeframeEvent.ModelSet, model);\n\n this.eventBus.emit(MeframeEvent.CompositionUpdated, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce((acc: number, track: any) => acc + track.clips.length, 0),\n durationUs: model.durationUs,\n });\n }\n\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n // Apply patch and get affected clip IDs (simplified for 2-Clip strategy)\n // Note: addTrack/removeTrack already call buildIndexes() in patch.ts\n const affectedClipIds = applyModelPatch(this.compositionModel, patch);\n this.planner.applyPatch(patch, affectedClipIds);\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n affectedClips: Array.from(affectedClipIds),\n });\n\n // Process clip updates\n for (const clipId of affectedClipIds) {\n this.cacheManager.invalidateClip(clipId);\n }\n\n // Reactivate updated audio clips\n const reactivatedAudioClips: string[] = [];\n const reactivatedVideoClips: string[] = [];\n for (const clipId of affectedClipIds) {\n const clip = this.compositionModel.findClip(clipId);\n if (clip?.trackKind === 'audio') {\n await this.audioSession.deactivateClip(clipId);\n reactivatedAudioClips.push(clipId);\n } else if (clip?.trackKind === 'video') {\n reactivatedVideoClips.push(clipId);\n }\n }\n\n // Activate all audio clips (including reactivated ones)\n await this.audioSession.activateAllAudioClips();\n\n // Note: No need to restart per-clip playback in new architecture\n // scheduleAudio() uses OfflineAudioMixer which automatically includes all active clips\n }\n\n private handleResourceStateChange(resourceId: string, state: Resource['state']): void {\n if (!this.compositionModel) {\n return;\n }\n\n this.compositionModel.updateResourceState(resourceId, state ?? 'pending');\n\n if (state !== 'ready') {\n return;\n }\n\n // For preview, simple cache invalidation or triggering re-render might be enough\n // if we were caching instructions. But PlaybackController pulls instructions every frame.\n // So just updating Model state is enough.\n }\n\n async getFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n const preheat = options?.preheat ?? false;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n // Calculate clip-relative time for cache lookup (global time - clip start time)\n let relativeTimeUs = options?.relativeTimeUs ?? timeUs - clip.startUs;\n\n // Clamp to clip duration to handle edge cases at clip boundaries\n relativeTimeUs = Math.min(relativeTimeUs, clip.durationUs - 1);\n\n // 1. Check L1 window cache\n // Note: preheat mode skips cache check to force decoding the entire window\n // Why: Video cache is frame-level (point query), not window-level (range query)\n // A single cached frame doesn't guarantee the entire window is cached\n // Without preheat flag, preheating would return early on first frame hit\n if (!preheat) {\n const cachedFrame = this.cacheManager.getFrame(relativeTimeUs, clip.id);\n if (cachedFrame) {\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n return cachedFrame;\n }\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n }\n\n if (signal?.aborted) {\n return null;\n }\n\n // 2. Try decode from OPFS resource (on-demand path)\n const resourceFrame = await this.decodeFromResource(clip, relativeTimeUs, timeUs, options);\n return resourceFrame;\n }\n\n /**\n * Compose frame from OPFS resource (on-demand decoding)\n * This is the new path for long clips with window caching\n */\n private async decodeFromResource(\n clip: Clip,\n relativeTimeUs: TimeUs,\n globalTimeUs: TimeUs,\n options?: RenderFrameOptions\n ): Promise<RcFrame | null> {\n if (!hasResourceId(clip)) return null;\n\n const resourceId = clip.resourceId;\n\n // Check resource state\n const resource = this.compositionModel?.getResource(resourceId);\n const isReady = resource?.state === 'ready';\n\n const fetchOptions = {\n priority: 'high' as const,\n clipId: clip.id,\n trackId: clip.trackId,\n };\n\n // In immediate mode, if not ready, trigger fetch in background and return null\n if (options?.immediate && !isReady) {\n // Fire and forget fetch to start loading\n this.resourceLoader.load(resourceId, fetchOptions);\n return null;\n }\n\n // Normal mode: wait for download\n await this.resourceLoader.load(resourceId, fetchOptions);\n\n this.cancelActiveDecoding();\n\n // Create temporary on-demand video session\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: relativeTimeUs,\n globalTimeUs,\n resourceLoader: this.resourceLoader,\n mp4IndexCache: this.cacheManager.mp4IndexCache,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n fps: this.compositionModel?.fps ?? 30,\n });\n\n this.activeOnDemandSession = session;\n\n try {\n // Decode window: from GOP start frame to target position + 3s\n const DECODE_WINDOW_SIZE = 3_000_000;\n\n const windowStart = relativeTimeUs;\n const windowEnd = Math.min(clip.durationUs, relativeTimeUs + DECODE_WINDOW_SIZE);\n\n await session.decodeWindow(windowStart, windowEnd);\n // Return target frame from L1 cache (now composed)\n return this.cacheManager.getFrame(relativeTimeUs, clip.id);\n } catch (error) {\n if (session.isDisposed) {\n return null;\n }\n console.error('[Orchestrator] Error composing from resource:', error);\n return null;\n } finally {\n if (this.activeOnDemandSession === session) {\n this.activeOnDemandSession = null;\n }\n await session.dispose();\n }\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId);\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 5,\n timeoutMs: options?.timeoutMs ?? 5_000,\n });\n }\n\n async dispose(): Promise<void> {\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n this.resourceLoader.dispose();\n await this.cacheManager.clear();\n\n this.workers.terminateAll();\n this.compositionModel = null;\n this.eventBus.dispose();\n }\n\n private buildWorkerConfigs(): Record<WorkerType, any> {\n const config = this.config;\n const defaultCanvasWidth = config.global.defaultCanvasWidth;\n const defaultCanvasHeight = config.global.defaultCanvasHeight;\n const defaultFps = config.global.defaultFps;\n\n const targetFps = this.compositionModel?.fps ?? defaultFps;\n\n return {\n videoDemux: {\n highWaterMark: config.demux.backpressure.highWaterMark,\n },\n audioDemux: {\n highWaterMark: config.demux.backpressure.highWaterMark,\n },\n videoDecode: config.decode.video,\n audioDecode: config.decode.audio,\n videoCompose: {\n width: defaultCanvasWidth,\n height: defaultCanvasHeight,\n fps: targetFps,\n backgroundColor: '#000000',\n enableSmoothing: true,\n enableHardwareAcceleration: true,\n },\n audioCompose: {\n enableDucking: config.compose.audio.enableDucking,\n },\n videoEncode: {\n // Main Profile Level 4.1 - better compression efficiency for social media\n codec: 'avc1.4D0029',\n width: defaultCanvasWidth,\n height: defaultCanvasHeight,\n bitrate: config.encode.video.bitrateKbps\n ? config.encode.video.bitrateKbps * 1000\n : this.calculateDefaultBitrate(defaultCanvasWidth, defaultCanvasHeight),\n framerate: targetFps,\n latencyMode: 'quality',\n bitrateMode: 'variable',\n hardwareAcceleration: 'no-preference',\n // Default 1 second keyframe interval for better social media compatibility\n keyFrameInterval: config.encode.video.keyIntervalS\n ? Math.round(config.encode.video.keyIntervalS * targetFps)\n : targetFps,\n ...config.encode.video,\n },\n };\n }\n\n /**\n * Calculate default video bitrate based on resolution\n * Optimized for social media platforms (YouTube, TikTok, WeChat, etc.)\n */\n private calculateDefaultBitrate(width: number, height: number): number {\n const pixels = width * height;\n\n // Bitrate recommendations for H.264 Main Profile VBR:\n // - 720p (921,600 px): 5 Mbps\n // - 1080p (2,073,600 px): 8 Mbps\n // - 4K (8,294,400 px): 25 Mbps\n if (pixels <= 921_600) {\n // 720p and below\n return 5_000_000;\n } else if (pixels <= 2_073_600) {\n // 1080p\n return 8_000_000;\n } else if (pixels <= 3_686_400) {\n // 1440p (2K)\n return 16_000_000;\n } else {\n // 4K and above\n return 25_000_000;\n }\n }\n\n async export(model: CompositionModel, options: ExportOptions): Promise<Blob> {\n return this.exportScheduler.execute(model, options);\n }\n\n /**\n * Preheat a specific clip's window range\n * Used by PlaybackController for cross-clip window preheating\n *\n * @param clipId - Clip identifier\n * @param clipRelativeStart - Start time relative to clip (microseconds)\n * @param clipRelativeEnd - End time relative to clip (microseconds)\n * @param globalTimeUs - Global timeline position (for globalTimeUs in cache)\n */\n async preheatClipWindow(\n clipId: string,\n clipRelativeStart: TimeUs,\n clipRelativeEnd: TimeUs,\n globalTimeUs: TimeUs\n ): Promise<void> {\n if (!this.compositionModel) return;\n\n const clip = this.compositionModel.findClip(clipId);\n if (!clip || !hasResourceId(clip)) return;\n\n const resourceId = clip.resourceId;\n\n // Ensure resource is downloaded\n await this.resourceLoader.load(resourceId, {\n priority: 'normal',\n clipId: clip.id,\n trackId: clip.trackId,\n });\n\n // Create temporary on-demand session for this window\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: clipRelativeStart,\n globalTimeUs,\n resourceLoader: this.resourceLoader,\n mp4IndexCache: this.cacheManager.mp4IndexCache,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel,\n fps: this.compositionModel.fps ?? 30,\n });\n\n try {\n // Decode the entire window range for this clip\n await session.decodeWindow(clipRelativeStart, clipRelativeEnd);\n } catch (error) {\n console.warn(`[Orchestrator] Preheat clip ${clipId} window failed:`, error);\n // Non-critical, don't throw\n } finally {\n await session.dispose();\n }\n }\n\n /**\n * Get render state for real-time composition\n * Returns layers ready for VideoComposer\n */\n async getRenderState(\n timeUs: TimeUs,\n options?: RenderFrameOptions\n ): Promise<{ layers: any[]; transition?: any } | null> {\n if (!this.compositionModel) {\n return null;\n }\n\n // Ensure frame/resource is ready (this populates L1 if needed)\n const frame = await this.getFrame(timeUs, options);\n\n // If immediate mode and no frame, return null to trigger buffering\n if (options?.immediate && !frame) {\n return null;\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n const relativeTimeUs = timeUs - clip.startUs;\n\n // Get instructions from planner\n const instructions = this.planner.getInstructions(clip.id);\n if (!instructions) {\n return null;\n }\n\n // Build layers array\n const layers: any[] = [];\n\n // 1. Filter active layers at this timestamp\n const activeLayers = instructions.layers.filter((layer: any) => {\n if (!layer.payload.attachmentId) {\n // Main track layer is always active\n return true;\n }\n if (layer.status !== 'ready') {\n return false;\n }\n // Check if layer is active at current timestamp\n return layer.activeRanges.some(\n (range: any) => relativeTimeUs >= range.startUs && relativeTimeUs < range.endUs\n );\n });\n\n // 2. Materialize layers\n for (const layerPlan of activeLayers) {\n const layer = await this.materializeLayer(layerPlan, clip, relativeTimeUs, timeUs);\n if (layer) {\n layers.push(layer);\n }\n }\n\n return { layers };\n }\n\n /**\n * Materialize a serialized layer plan into concrete Layer\n */\n private async materializeLayer(\n layerPlan: any,\n clip: Clip,\n clipRelativeTimeUs: TimeUs,\n globalTimeUs: TimeUs\n ): Promise<any | null> {\n const baseLayer: any = {\n id: layerPlan.layerId,\n type: layerPlan.type,\n zIndex: layerPlan.zIndex ?? 0,\n visible: true,\n opacity: layerPlan.opacity ?? 1,\n };\n\n // Video layer - fetch raw VideoFrame from L1 (RcFrame wrapper)\n if (layerPlan.type === 'video' && !layerPlan.payload.attachmentId) {\n const rcFrame = this.cacheManager.getFrame(clipRelativeTimeUs, clip.id);\n if (!rcFrame) {\n console.warn('[Orchestrator] Video frame not found in L1:', clip.id, clipRelativeTimeUs);\n return null;\n }\n\n return {\n ...baseLayer,\n type: 'video',\n rcFrame: rcFrame,\n };\n }\n\n // Text layer\n if (layerPlan.type === 'text') {\n const payload = layerPlan.payload;\n return {\n ...baseLayer,\n type: 'text',\n text: payload.text,\n localeCode: payload.localeCode,\n fontConfig: payload.fontConfig,\n animation: payload.animation,\n wordTimings: payload.wordTimings,\n letterCase: payload.letterCase,\n };\n }\n\n // Image layer\n if (layerPlan.type === 'image') {\n const payload = layerPlan.payload;\n const resource = this.compositionModel?.getResource(payload.resourceId);\n if (!resource) {\n return null;\n }\n\n const source = await this.resourceLoader.loadImage(resource);\n const imageLayer: any = {\n ...baseLayer,\n type: 'image',\n source,\n attachmentId: payload.attachmentId,\n };\n\n if (payload.targetWidth !== undefined) {\n imageLayer.targetWidth = payload.targetWidth;\n }\n if (payload.targetHeight !== undefined) {\n imageLayer.targetHeight = payload.targetHeight;\n }\n\n // Handle animation (overlays)\n if (payload.animation) {\n const { position, keyframes, overlayClipStartUs } = payload.animation;\n\n // Calculate time relative to overlay clip start\n const relativeTimeUs = globalTimeUs - overlayClipStartUs;\n\n // If outside keyframe range, hide\n if (relativeTimeUs < 0 || relativeTimeUs > keyframes[keyframes.length - 1].time) {\n return null; // Not visible at this time\n }\n\n const rotationRad = 0; // TODO: interpolate from keyframes\n\n imageLayer.transform = {\n x: position.x,\n y: position.y,\n scaleX: 1,\n scaleY: 1,\n rotation: rotationRad,\n anchorX: 0.5,\n anchorY: 0.5,\n };\n }\n\n return imageLayer;\n }\n\n return baseLayer;\n }\n}\n"],"names":["applyModelPatch"],"mappings":"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"}
1
+ {"version":3,"file":"Orchestrator.js","sources":["../../src/orchestrator/Orchestrator.ts"],"sourcesContent":["import { EventBus } from '../event/EventBus';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { applyPatch as applyModelPatch } from '../model/patch';\nimport { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, RenderFrameOptions } from './types';\nimport { WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame, Clip } from '../model';\nimport { hasResourceId } from '../model/types';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { GlobalAudioSession } from './GlobalAudioSession';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { ExportOptions } from '../types';\nimport { OnDemandVideoSession } from './OnDemandVideoSession';\nimport { ExportScheduler } from './ExportScheduler';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n muxManager: MuxManager;\n exportScheduler: ExportScheduler;\n\n private isInitialized = false;\n private config = ConfigLoader.getInstance().getConfig();\n private ensureCacheDebounceTimer: number | null = null;\n private activeOnDemandSession: OnDemandVideoSession | null = null;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n // Initialize WorkerPool with worker path from config\n this.workers = new WorkerPool({\n eventBus: this.eventBus,\n workerConfigs,\n workerPath: config.workerPath,\n workerExtension: config.workerExtension,\n });\n\n const maxMemoryMB = config.cacheConfig?.l1Size || this.config.cache?.l1?.maxMemoryMB || 1024;\n const maxGOPs = this.config.decode?.video?.maxGOPs || 4;\n\n this.cacheManager = new CacheManager(\n {\n l1: {\n maxMemoryMB,\n maxGOPs,\n },\n resource: {\n projectId: config.projectId || this.config.global.projectId || 'default',\n },\n },\n this.eventBus\n );\n\n this.resourceLoader = new ResourceLoader({\n cacheManager: this.cacheManager,\n workerPool: this.workers,\n eventBus: this.eventBus,\n config: {\n maxConcurrent: this.config.load.maxConcurrent,\n preloadConcurrency: 2, // Fixed preload concurrency for idle background loading\n },\n onStateChange: (resourceId, state) => this.handleResourceStateChange(resourceId, state),\n });\n\n this.planner = new CompositionPlanner();\n\n this.audioSession = new GlobalAudioSession({\n cacheManager: this.cacheManager,\n workerPool: this.workers,\n resourceLoader: this.resourceLoader,\n eventBus: this.eventBus,\n buildWorkerConfigs: () => this.buildWorkerConfigs(),\n });\n\n this.muxManager = new MuxManager(\n this.cacheManager,\n this.audioSession,\n this.config.encode.audio as AudioEncoderConfig\n );\n\n this.exportScheduler = new ExportScheduler({\n workerPool: this.workers,\n planner: this.planner,\n cacheManager: this.cacheManager,\n resourceLoader: this.resourceLoader,\n muxManager: this.muxManager,\n audioSession: this.audioSession,\n workerConfigsProvider: () => this.buildWorkerConfigs(),\n eventBus: this.eventBus,\n });\n\n this.setupResourceFirstFrameHandler();\n this.setupPreloadHandlers();\n }\n\n private setupResourceFirstFrameHandler(): void {\n this.eventBus.on(MeframeEvent.ResourceFirstFrameReady, async (payload) => {\n const { resourceId, clipId, index, chunks } = payload;\n\n if (!this.compositionModel) return;\n\n // Find the specific clip\n const clip = this.compositionModel.findClip(clipId);\n if (!clip || !clip.trackId) return;\n\n // Only decode first frame for clips that start at composition time 0\n // (these clips need the resource's first frame as cover)\n if (clip.startUs === 0) {\n const fps = this.compositionModel.fps ?? 30;\n await OnDemandVideoSession.decodeAndCacheFirstFrame(\n resourceId,\n chunks,\n index,\n clip,\n this.cacheManager,\n fps\n );\n }\n });\n }\n\n private setupPreloadHandlers(): void {\n // Stop preloading when playback starts\n this.eventBus.on(MeframeEvent.PlaybackPlay, () => {\n this.resourceLoader.setPreloadingEnabled(false);\n });\n\n // Enable preloading when playback pauses/stops\n this.eventBus.on(MeframeEvent.PlaybackPause, () => {\n this.resourceLoader.setPreloadingEnabled(true);\n });\n\n this.eventBus.on(MeframeEvent.PlaybackStop, () => {\n this.resourceLoader.setPreloadingEnabled(true);\n });\n\n // Note: ModelSet and PatchApplied are handled internally in ResourceLoader via handleModelSet\n // and direct calls isn't needed as ResourceLoader listens to updateResourceState via onStateChange?\n // Wait, ResourceLoader handles ModelSet via eventHandlers.\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n await this.cacheManager.init();\n\n this.isInitialized = true;\n }\n\n // Event methods - forward to eventBus\n on<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.on(event, handler);\n }\n\n off<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.off(event, handler);\n }\n\n once<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.once(event, handler);\n }\n\n cancelActiveDecoding(): void {\n if (this.activeOnDemandSession) {\n void this.activeOnDemandSession.dispose();\n this.activeOnDemandSession = null;\n }\n }\n\n /**\n * Decode and render nearest keyframe for fast seek preview\n * Returns the keyframe timestamp if successfully decoded, null otherwise\n */\n async tryRenderKeyframe(timeUs: TimeUs): Promise<TimeUs | null> {\n if (!this.compositionModel) return null;\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip || !hasResourceId(clip)) return null;\n\n // Use resource-relative time (same as frame.timestamp from decoder)\n const resourceTimeUs = timeUs - clip.startUs + (clip.trimStartUs ?? 0);\n const resourceId = clip.resourceId;\n\n // Check if keyframe is already in L1 cache\n const keyframeSample = this.cacheManager.mp4IndexCache.getNearestKeyframe(\n resourceId,\n resourceTimeUs\n );\n\n if (!keyframeSample) return null;\n\n const cachedKeyframe = this.cacheManager.getFrame(keyframeSample.timestamp, clip.id);\n if (cachedKeyframe) {\n return keyframeSample.timestamp;\n }\n\n // Resource must be ready to decode keyframe\n const resource = this.compositionModel.getResource(resourceId);\n if (resource?.state !== 'ready') return null;\n\n // Create temporary session to decode keyframe\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: resourceTimeUs,\n globalTimeUs: timeUs,\n resourceLoader: this.resourceLoader,\n mp4IndexCache: this.cacheManager.mp4IndexCache,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel,\n fps: this.compositionModel.fps ?? 30,\n });\n\n try {\n const keyframeTimeUs = await session.decodeKeyframe(resourceTimeUs);\n return keyframeTimeUs;\n } catch (error) {\n console.warn('[Orchestrator] Fast keyframe decode failed:', error);\n return null;\n } finally {\n await session.dispose();\n }\n }\n\n async setCompositionModel(model: CompositionModel): Promise<void> {\n this.compositionModel = model;\n this.cacheManager.clear();\n this.planner.setModel(model);\n // ensure the cover resource is preloaded before audio is activated\n await this.resourceLoader.setModel(model);\n this.audioSession.setModel(model);\n\n this.eventBus.emit(MeframeEvent.ModelSet, model);\n\n this.eventBus.emit(MeframeEvent.CompositionUpdated, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce((acc: number, track: any) => acc + track.clips.length, 0),\n durationUs: model.durationUs,\n });\n }\n\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n // Apply patch and get affected clip IDs (simplified for 2-Clip strategy)\n // Note: addTrack/removeTrack already call buildIndexes() in patch.ts\n const affectedClipIds = applyModelPatch(this.compositionModel, patch);\n this.planner.applyPatch(patch, affectedClipIds);\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n affectedClips: Array.from(affectedClipIds),\n });\n\n // Process clip updates\n for (const clipId of affectedClipIds) {\n this.cacheManager.invalidateClip(clipId);\n }\n\n // Reactivate updated audio clips\n const reactivatedAudioClips: string[] = [];\n const reactivatedVideoClips: string[] = [];\n for (const clipId of affectedClipIds) {\n const clip = this.compositionModel.findClip(clipId);\n if (clip?.trackKind === 'audio') {\n await this.audioSession.deactivateClip(clipId);\n reactivatedAudioClips.push(clipId);\n } else if (clip?.trackKind === 'video') {\n reactivatedVideoClips.push(clipId);\n }\n }\n\n // Activate all audio clips (including reactivated ones)\n await this.audioSession.activateAllAudioClips();\n\n // Note: No need to restart per-clip playback in new architecture\n // scheduleAudio() uses OfflineAudioMixer which automatically includes all active clips\n }\n\n private handleResourceStateChange(resourceId: string, state: Resource['state']): void {\n if (!this.compositionModel) {\n return;\n }\n\n this.compositionModel.updateResourceState(resourceId, state ?? 'pending');\n\n if (state !== 'ready') {\n return;\n }\n\n // For preview, simple cache invalidation or triggering re-render might be enough\n // if we were caching instructions. But PlaybackController pulls instructions every frame.\n // So just updating Model state is enough.\n }\n\n async getFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n const preheat = options?.preheat ?? false;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n const trimStartUs = clip.trimStartUs ?? 0;\n\n // Calculate resource-relative time (same as frame.timestamp from decoder)\n // resourceTimeUs = clipRelativeTime + trimStartUs\n let resourceTimeUs = options?.relativeTimeUs\n ? options.relativeTimeUs + trimStartUs\n : timeUs - clip.startUs + trimStartUs;\n\n // Clamp to valid range: [trimStartUs, trimStartUs + durationUs)\n resourceTimeUs = Math.min(resourceTimeUs, trimStartUs + clip.durationUs - 1);\n\n // 1. Check L1 window cache\n // Note: preheat mode skips cache check to force decoding the entire window\n // Why: Video cache is frame-level (point query), not window-level (range query)\n // A single cached frame doesn't guarantee the entire window is cached\n // Without preheat flag, preheating would return early on first frame hit\n if (!preheat) {\n const cachedFrame = this.cacheManager.getFrame(resourceTimeUs, clip.id);\n if (cachedFrame) {\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${resourceTimeUs}`,\n });\n return cachedFrame;\n }\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${resourceTimeUs}`,\n });\n }\n\n if (signal?.aborted) {\n return null;\n }\n\n // 2. Try decode from OPFS resource (on-demand path)\n const resourceFrame = await this.decodeFromResource(clip, resourceTimeUs, timeUs, options);\n return resourceFrame;\n }\n\n /**\n * Compose frame from OPFS resource (on-demand decoding)\n * This is the new path for long clips with window caching\n *\n * @param clip - The clip to decode\n * @param resourceTimeUs - Time in resource-relative coordinates (same as frame.timestamp)\n * @param globalTimeUs - Time in composition timeline\n */\n private async decodeFromResource(\n clip: Clip,\n resourceTimeUs: TimeUs,\n globalTimeUs: TimeUs,\n options?: RenderFrameOptions\n ): Promise<RcFrame | null> {\n if (!hasResourceId(clip)) return null;\n\n const resourceId = clip.resourceId;\n const trimStartUs = clip.trimStartUs ?? 0;\n\n // Check resource state\n const resource = this.compositionModel?.getResource(resourceId);\n const isReady = resource?.state === 'ready';\n\n const fetchOptions = {\n isPreload: false,\n clipId: clip.id,\n trackId: clip.trackId,\n };\n\n // In immediate mode, if not ready, trigger fetch in background and return null\n if (options?.immediate && !isReady) {\n // Fire and forget fetch to start loading\n this.resourceLoader.load(resourceId, fetchOptions);\n return null;\n }\n\n // Normal mode: wait for download\n await this.resourceLoader.load(resourceId, fetchOptions);\n\n this.cancelActiveDecoding();\n\n // Create temporary on-demand video session\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: resourceTimeUs,\n globalTimeUs,\n resourceLoader: this.resourceLoader,\n mp4IndexCache: this.cacheManager.mp4IndexCache,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n fps: this.compositionModel?.fps ?? 30,\n });\n\n this.activeOnDemandSession = session;\n\n try {\n // Decode window: from target position to target + 3s\n // Window bounds are in resource time: [trimStartUs, trimStartUs + durationUs)\n const DECODE_WINDOW_SIZE = 3_000_000;\n\n const windowStart = resourceTimeUs;\n const windowEnd = Math.min(\n trimStartUs + clip.durationUs,\n resourceTimeUs + DECODE_WINDOW_SIZE\n );\n\n await session.decodeWindow(windowStart, windowEnd);\n // Return target frame from L1 cache\n return this.cacheManager.getFrame(resourceTimeUs, clip.id);\n } catch (error) {\n if (session.isDisposed) {\n return null;\n }\n console.error('[Orchestrator] Error composing from resource:', error);\n return null;\n } finally {\n if (this.activeOnDemandSession === session) {\n this.activeOnDemandSession = null;\n }\n await session.dispose();\n }\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId);\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 5,\n timeoutMs: options?.timeoutMs ?? 5_000,\n });\n }\n\n async dispose(): Promise<void> {\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n this.resourceLoader.dispose();\n await this.cacheManager.clear();\n\n this.workers.terminateAll();\n this.compositionModel = null;\n this.eventBus.dispose();\n }\n\n private buildWorkerConfigs(): Record<WorkerType, any> {\n const config = this.config;\n const defaultCanvasWidth = config.global.defaultCanvasWidth;\n const defaultCanvasHeight = config.global.defaultCanvasHeight;\n const defaultFps = config.global.defaultFps;\n\n const targetFps = this.compositionModel?.fps ?? defaultFps;\n\n return {\n videoDemux: {\n highWaterMark: config.demux.backpressure.highWaterMark,\n },\n audioDemux: {\n highWaterMark: config.demux.backpressure.highWaterMark,\n },\n videoDecode: config.decode.video,\n audioDecode: config.decode.audio,\n videoCompose: {\n width: defaultCanvasWidth,\n height: defaultCanvasHeight,\n fps: targetFps,\n backgroundColor: '#000000',\n enableSmoothing: true,\n enableHardwareAcceleration: true,\n },\n audioCompose: {\n enableDucking: config.compose.audio.enableDucking,\n },\n videoEncode: {\n // Main Profile Level 4.1 - better compression efficiency for social media\n codec: 'avc1.4D0029',\n width: defaultCanvasWidth,\n height: defaultCanvasHeight,\n bitrate: config.encode.video.bitrateKbps\n ? config.encode.video.bitrateKbps * 1000\n : this.calculateDefaultBitrate(defaultCanvasWidth, defaultCanvasHeight),\n framerate: targetFps,\n latencyMode: 'quality',\n bitrateMode: 'variable',\n hardwareAcceleration: 'no-preference',\n // Default 1 second keyframe interval for better social media compatibility\n keyFrameInterval: config.encode.video.keyIntervalS\n ? Math.round(config.encode.video.keyIntervalS * targetFps)\n : targetFps,\n ...config.encode.video,\n },\n };\n }\n\n /**\n * Calculate default video bitrate based on resolution\n * Optimized for social media platforms (YouTube, TikTok, WeChat, etc.)\n */\n private calculateDefaultBitrate(width: number, height: number): number {\n const pixels = width * height;\n\n // Bitrate recommendations for H.264 Main Profile VBR:\n // - 720p (921,600 px): 5 Mbps\n // - 1080p (2,073,600 px): 8 Mbps\n // - 4K (8,294,400 px): 25 Mbps\n if (pixels <= 921_600) {\n // 720p and below\n return 5_000_000;\n } else if (pixels <= 2_073_600) {\n // 1080p\n return 8_000_000;\n } else if (pixels <= 3_686_400) {\n // 1440p (2K)\n return 16_000_000;\n } else {\n // 4K and above\n return 25_000_000;\n }\n }\n\n async export(model: CompositionModel, options: ExportOptions): Promise<Blob> {\n return this.exportScheduler.execute(model, options);\n }\n\n /**\n * Preheat a specific clip's window range\n * Used by PlaybackController for cross-clip window preheating\n *\n * @param clipId - Clip identifier\n * @param clipRelativeStart - Start time relative to clip (microseconds), 0 = clip start\n * @param clipRelativeEnd - End time relative to clip (microseconds)\n * @param globalTimeUs - Global timeline position (for globalTimeUs in cache)\n */\n async preheatClipWindow(\n clipId: string,\n clipRelativeStart: TimeUs,\n clipRelativeEnd: TimeUs,\n globalTimeUs: TimeUs\n ): Promise<void> {\n if (!this.compositionModel) return;\n\n const clip = this.compositionModel.findClip(clipId);\n if (!clip || !hasResourceId(clip)) return;\n\n const resourceId = clip.resourceId;\n const trimStartUs = clip.trimStartUs ?? 0;\n\n // Convert clip-relative time to resource time\n const resourceStart = clipRelativeStart + trimStartUs;\n const resourceEnd = clipRelativeEnd + trimStartUs;\n\n // Ensure resource is downloaded\n await this.resourceLoader.load(resourceId, {\n isPreload: false,\n clipId: clip.id,\n trackId: clip.trackId,\n });\n\n // Create temporary on-demand session for this window\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: resourceStart,\n globalTimeUs,\n resourceLoader: this.resourceLoader,\n mp4IndexCache: this.cacheManager.mp4IndexCache,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel,\n fps: this.compositionModel.fps ?? 30,\n });\n\n try {\n // Decode the entire window range for this clip (using resource time)\n await session.decodeWindow(resourceStart, resourceEnd);\n } catch (error) {\n console.warn(`[Orchestrator] Preheat clip ${clipId} window failed:`, error);\n // Non-critical, don't throw\n } finally {\n await session.dispose();\n }\n }\n\n /**\n * Get render state for real-time composition\n * Returns layers ready for VideoComposer\n */\n async getRenderState(\n timeUs: TimeUs,\n options?: RenderFrameOptions\n ): Promise<{ layers: any[]; transition?: any } | null> {\n if (!this.compositionModel) {\n return null;\n }\n\n // Ensure frame/resource is ready (this populates L1 if needed)\n const frame = await this.getFrame(timeUs, options);\n\n // If immediate mode and no frame, return null to trigger buffering\n if (options?.immediate && !frame) {\n return null;\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n const clipRelativeTimeUs = timeUs - clip.startUs;\n const resourceTimeUs = clipRelativeTimeUs + (clip.trimStartUs ?? 0);\n\n // Get instructions from planner\n const instructions = this.planner.getInstructions(clip.id);\n if (!instructions) {\n return null;\n }\n\n // Build layers array\n const layers: any[] = [];\n\n // 1. Filter active layers at this timestamp (uses clip-relative time for activeRanges)\n const activeLayers = instructions.layers.filter((layer: any) => {\n if (!layer.payload.attachmentId) {\n // Main track layer is always active\n return true;\n }\n if (layer.status !== 'ready') {\n return false;\n }\n // Check if layer is active at current timestamp\n return layer.activeRanges.some(\n (range: any) => clipRelativeTimeUs >= range.startUs && clipRelativeTimeUs < range.endUs\n );\n });\n\n // 2. Materialize layers\n for (const layerPlan of activeLayers) {\n const layer = await this.materializeLayer(layerPlan, clip, resourceTimeUs, timeUs);\n if (layer) {\n layers.push(layer);\n }\n }\n\n return { layers };\n }\n\n /**\n * Materialize a serialized layer plan into concrete Layer\n */\n private async materializeLayer(\n layerPlan: any,\n clip: Clip,\n resourceTimeUs: TimeUs,\n globalTimeUs: TimeUs\n ): Promise<any | null> {\n const baseLayer: any = {\n id: layerPlan.layerId,\n type: layerPlan.type,\n zIndex: layerPlan.zIndex ?? 0,\n visible: true,\n opacity: layerPlan.opacity ?? 1,\n };\n\n // Video layer - fetch raw VideoFrame from L1 (RcFrame wrapper)\n if (layerPlan.type === 'video' && !layerPlan.payload.attachmentId) {\n const rcFrame = this.cacheManager.getFrame(resourceTimeUs, clip.id);\n if (!rcFrame) {\n console.warn('[Orchestrator] Video frame not found in L1:', clip.id, resourceTimeUs);\n return null;\n }\n\n return {\n ...baseLayer,\n type: 'video',\n rcFrame: rcFrame,\n };\n }\n\n // Text layer\n if (layerPlan.type === 'text') {\n const payload = layerPlan.payload;\n return {\n ...baseLayer,\n type: 'text',\n text: payload.text,\n localeCode: payload.localeCode,\n fontConfig: payload.fontConfig,\n animation: payload.animation,\n wordTimings: payload.wordTimings,\n letterCase: payload.letterCase,\n };\n }\n\n // Image layer\n if (layerPlan.type === 'image') {\n const payload = layerPlan.payload;\n const resource = this.compositionModel?.getResource(payload.resourceId);\n if (!resource) {\n return null;\n }\n\n const source = await this.resourceLoader.loadImage(resource);\n const imageLayer: any = {\n ...baseLayer,\n type: 'image',\n source,\n attachmentId: payload.attachmentId,\n };\n\n if (payload.targetWidth !== undefined) {\n imageLayer.targetWidth = payload.targetWidth;\n }\n if (payload.targetHeight !== undefined) {\n imageLayer.targetHeight = payload.targetHeight;\n }\n\n // Handle animation (overlays)\n if (payload.animation) {\n const { position, keyframes, overlayClipStartUs } = payload.animation;\n\n // Calculate time relative to overlay clip start\n const relativeTimeUs = globalTimeUs - overlayClipStartUs;\n\n // If outside keyframe range, hide\n if (relativeTimeUs < 0 || relativeTimeUs > keyframes[keyframes.length - 1].time) {\n return null; // Not visible at this time\n }\n\n const rotationRad = 0; // TODO: interpolate from keyframes\n\n imageLayer.transform = {\n x: position.x,\n y: position.y,\n scaleX: 1,\n scaleY: 1,\n rotation: rotationRad,\n anchorX: 0.5,\n anchorY: 0.5,\n };\n }\n\n return imageLayer;\n }\n\n return baseLayer;\n }\n}\n"],"names":["applyModelPatch"],"mappings":"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"}
@@ -12,6 +12,10 @@ import { TimeUs } from '../../model/types';
12
12
  * - Output new frame with standardized timestamp t
13
13
  * - Close source frames after use to prevent memory leaks
14
14
  *
15
+ * Trim Support:
16
+ * - Frames with timestamp < trimStartUs are filtered out (closed and skipped)
17
+ * - Remaining frame timestamps are adjusted: adjustedTimestamp = timestamp - trimStartUs
18
+ *
15
19
  * Scenarios:
16
20
  * - Downsampling (60fps→30fps): Skip every other frame
17
21
  * - Upsampling (24fps→30fps): Duplicate some frames
@@ -20,10 +24,11 @@ import { TimeUs } from '../../model/types';
20
24
  export declare class FrameRateConverter {
21
25
  private readonly clipDurationUs;
22
26
  private readonly frameDurationUs;
27
+ private readonly trimStartUs;
23
28
  private targetFrameIndex;
24
29
  private targetFrameTimeUs;
25
30
  private sourceFrameBuffer;
26
- constructor(targetFps: number, clipDurationUs: TimeUs);
31
+ constructor(targetFps: number, clipDurationUs: TimeUs, trimStartUs?: TimeUs);
27
32
  /**
28
33
  * Create a TransformStream that converts VFR frames to CFR frames
29
34
  */
@@ -1 +1 @@
1
- {"version":3,"file":"FrameRateConverter.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/FrameRateConverter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IAGzC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,iBAAiB,CAAoB;gBAEjC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM;IAYrD;;OAEG;IACH,YAAY,IAAI,eAAe,CAAC,UAAU,EAAE,UAAU,CAAC;IAqBvD,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,gBAAgB,CAAK;IAE7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAgD1B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA6C5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;;;;;OAMG;IACH,OAAO,CAAC,sBAAsB;IAa9B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAyCxB;;OAEG;IACH,QAAQ,IAAI;QACV,gBAAgB,EAAE,MAAM,CAAC;QACzB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,CAAC;KACzB;CAQF"}
1
+ {"version":3,"file":"FrameRateConverter.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/FrameRateConverter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAGrC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,iBAAiB,CAAoB;gBAEjC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,GAAE,MAAU;IAa9E;;OAEG;IACH,YAAY,IAAI,eAAe,CAAC,UAAU,EAAE,UAAU,CAAC;IAqBvD,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,gBAAgB,CAAK;IAE7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAuE1B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA6C5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;;;;;OAMG;IACH,OAAO,CAAC,sBAAsB;IAa9B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAyCxB;;OAEG;IACH,QAAQ,IAAI;QACV,gBAAgB,EAAE,MAAM,CAAC;QACzB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,UAAU,EAAE,MAAM,CAAC;QACnB,eAAe,EAAE,MAAM,CAAC;KACzB;CAQF"}
@@ -1 +1 @@
1
- {"version":3,"file":"BaseDecoder.d.ts","sourceRoot":"","sources":["../../../src/stages/decode/BaseDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEpF,8BAAsB,WAAW,CAC/B,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,kBAAkB,GAAG,kBAAkB,EACvD,MAAM,SAAS,iBAAiB,GAAG,iBAAiB,EACpD,OAAO,SAAS,UAAU,GAAG,SAAS,GAAG,iBAAiB;IAE1D,SAAS,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,UAAU,EAAE,gCAAgC,CAAC,OAAO,CAAC,GAAG,IAAI,CAAQ;gBAElE,MAAM,EAAE,OAAO;IAIrB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgD3B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAyB3C,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAWhD,OAAO,CAAC,eAAe;IAMvB,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ;IAC7D,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IACnE,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,MAAM;IAC3C,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAC9C,SAAS,CAAC,OAAO,IAAI,IAAI;IAGzB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEzD,YAAY,IAAI,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC;CAyCjD;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACtC"}
1
+ {"version":3,"file":"BaseDecoder.d.ts","sourceRoot":"","sources":["../../../src/stages/decode/BaseDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEpF,8BAAsB,WAAW,CAC/B,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,kBAAkB,GAAG,kBAAkB,EACvD,MAAM,SAAS,iBAAiB,GAAG,iBAAiB,EACpD,OAAO,SAAS,UAAU,GAAG,SAAS,GAAG,iBAAiB;IAE1D,SAAS,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,UAAU,EAAE,gCAAgC,CAAC,OAAO,CAAC,GAAG,IAAI,CAAQ;gBAElE,MAAM,EAAE,OAAO;IAIrB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB3B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAyB3C,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAIhD,OAAO,CAAC,eAAe;IAMvB,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ;IAC7D,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IACnE,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,MAAM;IAC3C,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAC9C,SAAS,CAAC,OAAO,IAAI,IAAI;IAGzB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEzD,YAAY,IAAI,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC;CAyCjD;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACtC"}
@@ -9,24 +9,7 @@ class BaseDecoder {
9
9
  if (this.decoder?.state === "configured") {
10
10
  return;
11
11
  }
12
- const configInfo = {
13
- codec: this.config.codec,
14
- width: this.config.width,
15
- height: this.config.height,
16
- sampleRate: this.config.sampleRate,
17
- numberOfChannels: this.config.numberOfChannels,
18
- hardwareAcceleration: this.config.hardwareAcceleration
19
- };
20
- console.log(`[${this.getDecoderType()}Decoder] Checking config support:`, {
21
- platform: typeof navigator !== "undefined" ? navigator.platform : "unknown",
22
- userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "unknown",
23
- config: configInfo
24
- });
25
12
  const isSupported = await this.isConfigSupported(this.config);
26
- console.log(`[${this.getDecoderType()}Decoder] Config support result:`, {
27
- supported: isSupported.supported,
28
- config: configInfo
29
- });
30
13
  if (!isSupported.supported) {
31
14
  throw new Error(
32
15
  `Codec not supported: ${this.config.codecString || this.config.codec}`
@@ -36,12 +19,7 @@ class BaseDecoder {
36
19
  output: this.handleOutput.bind(this),
37
20
  error: this.handleError.bind(this)
38
21
  });
39
- console.log(`[${this.getDecoderType()}Decoder] Decoder created, configuring...`);
40
22
  await this.configureDecoder(this.config);
41
- console.log(
42
- `[${this.getDecoderType()}Decoder] Configured successfully, state:`,
43
- this.decoder?.state
44
- );
45
23
  }
46
24
  async reconfigure(config) {
47
25
  this.config = { ...this.config, ...config };
@@ -107,13 +85,6 @@ class BaseDecoder {
107
85
  }
108
86
  }
109
87
  handleError(error) {
110
- console.error(`[${this.getDecoderType()}Decoder] Decode error:`, {
111
- name: error.name,
112
- message: error.message,
113
- decoderState: this.decoder?.state,
114
- queueSize: this.queueSize,
115
- platform: typeof navigator !== "undefined" ? navigator.platform : "unknown"
116
- });
117
88
  this.controller?.error(error);
118
89
  }
119
90
  closeIfPossible(data) {
@@ -1 +1 @@
1
- {"version":3,"file":"BaseDecoder.js","sources":["../../../src/stages/decode/BaseDecoder.ts"],"sourcesContent":["/**\n * Base decoder class abstracting common WebCodecs decoder workflow.\n * Mirrors BaseEncoder but for decoding path.\n */\nimport { AudioDecoderConfig, DecodedVideoFrame, VideoDecoderConfig } from './types';\n\nexport abstract class BaseDecoder<\n TDecoder extends VideoDecoder | AudioDecoder,\n TConfig extends VideoDecoderConfig | AudioDecoderConfig,\n TInput extends EncodedVideoChunk | EncodedAudioChunk,\n TOutput extends VideoFrame | AudioData | DecodedVideoFrame,\n> {\n protected decoder?: TDecoder;\n protected config: TConfig;\n protected controller: TransformStreamDefaultController<TOutput> | null = null;\n\n constructor(config: TConfig) {\n this.config = config;\n }\n\n async initialize(): Promise<void> {\n if (this.decoder?.state === 'configured') {\n return;\n }\n\n const configInfo = {\n codec: (this.config as any).codec,\n width: (this.config as any).width,\n height: (this.config as any).height,\n sampleRate: (this.config as any).sampleRate,\n numberOfChannels: (this.config as any).numberOfChannels,\n hardwareAcceleration: (this.config as any).hardwareAcceleration,\n };\n\n console.log(`[${this.getDecoderType()}Decoder] Checking config support:`, {\n platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown',\n config: configInfo,\n });\n\n const isSupported = await this.isConfigSupported(this.config);\n\n console.log(`[${this.getDecoderType()}Decoder] Config support result:`, {\n supported: isSupported.supported,\n config: configInfo,\n });\n\n if (!isSupported.supported) {\n throw new Error(\n `Codec not supported: ${(this.config as any).codecString || (this.config as any).codec}`\n );\n }\n\n this.decoder = this.createDecoder({\n output: this.handleOutput.bind(this),\n error: this.handleError.bind(this),\n });\n\n console.log(`[${this.getDecoderType()}Decoder] Decoder created, configuring...`);\n\n await this.configureDecoder(this.config);\n\n console.log(\n `[${this.getDecoderType()}Decoder] Configured successfully, state:`,\n this.decoder?.state\n );\n }\n\n async reconfigure(config: Partial<TConfig>): Promise<void> {\n this.config = { ...this.config, ...config } as TConfig;\n\n if (!this.decoder) {\n await this.initialize();\n return;\n }\n\n // Flush before reconfiguring\n if (this.decoder.state === 'configured') {\n await this.decoder.flush();\n }\n\n const isSupported = await this.isConfigSupported(this.config);\n if (!isSupported.supported) {\n throw new Error(\n `New configuration not supported: ${(this.config as any).codecString || (this.config as any).codec}`\n );\n }\n\n await this.configureDecoder(this.config);\n }\n\n async flush(): Promise<void> {\n if (!this.decoder) return;\n await this.decoder.flush();\n }\n\n async reset(): Promise<void> {\n if (!this.decoder) return;\n\n // Windows Media Foundation requires flush before reset to avoid decoder error state\n if (this.decoder.state === 'configured') {\n try {\n await this.decoder.flush();\n } catch {\n // Ignore flush errors during reset\n }\n }\n\n this.decoder.reset();\n this.onReset();\n }\n\n async close(): Promise<void> {\n if (!this.decoder) return;\n\n if (this.decoder.state === 'configured') {\n await this.decoder.flush();\n }\n\n this.decoder.close();\n this.decoder = undefined;\n }\n\n get isReady(): boolean {\n return this.decoder?.state === 'configured';\n }\n\n get queueSize(): number {\n return (this.decoder as any)?.decodeQueueSize ?? 0;\n }\n\n protected handleOutput(data: TOutput): void {\n if (!this.controller) {\n this.closeIfPossible(data);\n return;\n }\n\n try {\n this.controller.enqueue(data);\n if (data instanceof VideoFrame && (this.config as VideoDecoderConfig).thread !== 'main') {\n // ugly hack to avoid memory leak\n // setTimeout(() => {\n // data.close();\n // }, 500);\n }\n } catch (error) {\n // Stream closed or errored - silently drop frame\n if (error instanceof TypeError) {\n this.closeIfPossible(data);\n return;\n }\n\n throw error;\n }\n }\n\n protected handleError(error: DOMException): void {\n console.error(`[${this.getDecoderType()}Decoder] Decode error:`, {\n name: error.name,\n message: error.message,\n decoderState: this.decoder?.state,\n queueSize: this.queueSize,\n platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',\n });\n this.controller?.error(error);\n }\n\n private closeIfPossible(data: TOutput): void {\n (data as any)?.close?.();\n (data as DecodedVideoFrame)?.frame?.close?.();\n }\n\n // Abstracts\n protected abstract isConfigSupported(config: TConfig): Promise<{ supported: boolean }>;\n protected abstract createDecoder(init: DecoderInit): TDecoder;\n protected abstract configureDecoder(config: TConfig): Promise<void>;\n protected abstract getDecoderType(): string;\n protected abstract decode(input: TInput): void;\n protected onReset(): void {}\n\n // Backpressure\n protected abstract readonly highWaterMark: number;\n protected abstract readonly decodeQueueThreshold: number;\n\n createStream(): TransformStream<TInput, TOutput> {\n return new TransformStream<TInput, TOutput>(\n {\n start: async (controller) => {\n this.controller = controller;\n if (!this.isReady) {\n await this.initialize();\n }\n },\n transform: async (input) => {\n if (!this.decoder || this.decoder.state !== 'configured') {\n throw new Error('Decoder not configured');\n }\n // backpressure\n if ((this.decoder as any).decodeQueueSize >= this.decodeQueueThreshold) {\n await new Promise<void>((resolve) => {\n const check = () => {\n if (\n !this.decoder ||\n (this.decoder as any).decodeQueueSize < this.decodeQueueThreshold - 1\n ) {\n resolve();\n } else {\n setTimeout(check, 10);\n }\n };\n check();\n });\n }\n this.decode(input);\n },\n flush: async () => {\n await this.flush();\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: () => 1,\n }\n );\n }\n}\n\ninterface DecoderInit {\n output: (data: any) => void;\n error: (error: DOMException) => void;\n}\n"],"names":[],"mappings":"AAMO,MAAe,YAKpB;AAAA,EACU;AAAA,EACA;AAAA,EACA,aAA+D;AAAA,EAEzE,YAAY,QAAiB;AAC3B,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,aAA4B;AAChC,QAAI,KAAK,SAAS,UAAU,cAAc;AACxC;AAAA,IACF;AAEA,UAAM,aAAa;AAAA,MACjB,OAAQ,KAAK,OAAe;AAAA,MAC5B,OAAQ,KAAK,OAAe;AAAA,MAC5B,QAAS,KAAK,OAAe;AAAA,MAC7B,YAAa,KAAK,OAAe;AAAA,MACjC,kBAAmB,KAAK,OAAe;AAAA,MACvC,sBAAuB,KAAK,OAAe;AAAA,IAAA;AAG7C,YAAQ,IAAI,IAAI,KAAK,eAAA,CAAgB,qCAAqC;AAAA,MACxE,UAAU,OAAO,cAAc,cAAc,UAAU,WAAW;AAAA,MAClE,WAAW,OAAO,cAAc,cAAc,UAAU,YAAY;AAAA,MACpE,QAAQ;AAAA,IAAA,CACT;AAED,UAAM,cAAc,MAAM,KAAK,kBAAkB,KAAK,MAAM;AAE5D,YAAQ,IAAI,IAAI,KAAK,eAAA,CAAgB,mCAAmC;AAAA,MACtE,WAAW,YAAY;AAAA,MACvB,QAAQ;AAAA,IAAA,CACT;AAED,QAAI,CAAC,YAAY,WAAW;AAC1B,YAAM,IAAI;AAAA,QACR,wBAAyB,KAAK,OAAe,eAAgB,KAAK,OAAe,KAAK;AAAA,MAAA;AAAA,IAE1F;AAEA,SAAK,UAAU,KAAK,cAAc;AAAA,MAChC,QAAQ,KAAK,aAAa,KAAK,IAAI;AAAA,MACnC,OAAO,KAAK,YAAY,KAAK,IAAI;AAAA,IAAA,CAClC;AAED,YAAQ,IAAI,IAAI,KAAK,eAAA,CAAgB,0CAA0C;AAE/E,UAAM,KAAK,iBAAiB,KAAK,MAAM;AAEvC,YAAQ;AAAA,MACN,IAAI,KAAK,eAAA,CAAgB;AAAA,MACzB,KAAK,SAAS;AAAA,IAAA;AAAA,EAElB;AAAA,EAEA,MAAM,YAAY,QAAyC;AACzD,SAAK,SAAS,EAAE,GAAG,KAAK,QAAQ,GAAG,OAAA;AAEnC,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,KAAK,WAAA;AACX;AAAA,IACF;AAGA,QAAI,KAAK,QAAQ,UAAU,cAAc;AACvC,YAAM,KAAK,QAAQ,MAAA;AAAA,IACrB;AAEA,UAAM,cAAc,MAAM,KAAK,kBAAkB,KAAK,MAAM;AAC5D,QAAI,CAAC,YAAY,WAAW;AAC1B,YAAM,IAAI;AAAA,QACR,oCAAqC,KAAK,OAAe,eAAgB,KAAK,OAAe,KAAK;AAAA,MAAA;AAAA,IAEtG;AAEA,UAAM,KAAK,iBAAiB,KAAK,MAAM;AAAA,EACzC;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,QAAS;AACnB,UAAM,KAAK,QAAQ,MAAA;AAAA,EACrB;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,QAAS;AAGnB,QAAI,KAAK,QAAQ,UAAU,cAAc;AACvC,UAAI;AACF,cAAM,KAAK,QAAQ,MAAA;AAAA,MACrB,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,SAAK,QAAQ,MAAA;AACb,SAAK,QAAA;AAAA,EACP;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,QAAS;AAEnB,QAAI,KAAK,QAAQ,UAAU,cAAc;AACvC,YAAM,KAAK,QAAQ,MAAA;AAAA,IACrB;AAEA,SAAK,QAAQ,MAAA;AACb,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,IAAI,UAAmB;AACrB,WAAO,KAAK,SAAS,UAAU;AAAA,EACjC;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAQ,KAAK,SAAiB,mBAAmB;AAAA,EACnD;AAAA,EAEU,aAAa,MAAqB;AAC1C,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,gBAAgB,IAAI;AACzB;AAAA,IACF;AAEA,QAAI;AACF,WAAK,WAAW,QAAQ,IAAI;AAC5B,UAAI,gBAAgB,cAAe,KAAK,OAA8B,WAAW,QAAQ;AAAA,MAKzF;AAAA,IACF,SAAS,OAAO;AAEd,UAAI,iBAAiB,WAAW;AAC9B,aAAK,gBAAgB,IAAI;AACzB;AAAA,MACF;AAEA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEU,YAAY,OAA2B;AAC/C,YAAQ,MAAM,IAAI,KAAK,eAAA,CAAgB,0BAA0B;AAAA,MAC/D,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,MACf,cAAc,KAAK,SAAS;AAAA,MAC5B,WAAW,KAAK;AAAA,MAChB,UAAU,OAAO,cAAc,cAAc,UAAU,WAAW;AAAA,IAAA,CACnE;AACD,SAAK,YAAY,MAAM,KAAK;AAAA,EAC9B;AAAA,EAEQ,gBAAgB,MAAqB;AAC1C,UAAc,QAAA;AACd,UAA4B,OAAO,QAAA;AAAA,EACtC;AAAA,EAQU,UAAgB;AAAA,EAAC;AAAA,EAM3B,eAAiD;AAC/C,WAAO,IAAI;AAAA,MACT;AAAA,QACE,OAAO,OAAO,eAAe;AAC3B,eAAK,aAAa;AAClB,cAAI,CAAC,KAAK,SAAS;AACjB,kBAAM,KAAK,WAAA;AAAA,UACb;AAAA,QACF;AAAA,QACA,WAAW,OAAO,UAAU;AAC1B,cAAI,CAAC,KAAK,WAAW,KAAK,QAAQ,UAAU,cAAc;AACxD,kBAAM,IAAI,MAAM,wBAAwB;AAAA,UAC1C;AAEA,cAAK,KAAK,QAAgB,mBAAmB,KAAK,sBAAsB;AACtE,kBAAM,IAAI,QAAc,CAAC,YAAY;AACnC,oBAAM,QAAQ,MAAM;AAClB,oBACE,CAAC,KAAK,WACL,KAAK,QAAgB,kBAAkB,KAAK,uBAAuB,GACpE;AACA,0BAAA;AAAA,gBACF,OAAO;AACL,6BAAW,OAAO,EAAE;AAAA,gBACtB;AAAA,cACF;AACA,oBAAA;AAAA,YACF,CAAC;AAAA,UACH;AACA,eAAK,OAAO,KAAK;AAAA,QACnB;AAAA,QACA,OAAO,YAAY;AACjB,gBAAM,KAAK,MAAA;AAAA,QACb;AAAA,MAAA;AAAA,MAEF;AAAA,QACE,eAAe,KAAK;AAAA,QACpB,MAAM,MAAM;AAAA,MAAA;AAAA,IACd;AAAA,EAEJ;AACF;"}
1
+ {"version":3,"file":"BaseDecoder.js","sources":["../../../src/stages/decode/BaseDecoder.ts"],"sourcesContent":["/**\n * Base decoder class abstracting common WebCodecs decoder workflow.\n * Mirrors BaseEncoder but for decoding path.\n */\nimport { AudioDecoderConfig, DecodedVideoFrame, VideoDecoderConfig } from './types';\n\nexport abstract class BaseDecoder<\n TDecoder extends VideoDecoder | AudioDecoder,\n TConfig extends VideoDecoderConfig | AudioDecoderConfig,\n TInput extends EncodedVideoChunk | EncodedAudioChunk,\n TOutput extends VideoFrame | AudioData | DecodedVideoFrame,\n> {\n protected decoder?: TDecoder;\n protected config: TConfig;\n protected controller: TransformStreamDefaultController<TOutput> | null = null;\n\n constructor(config: TConfig) {\n this.config = config;\n }\n\n async initialize(): Promise<void> {\n if (this.decoder?.state === 'configured') {\n return;\n }\n\n const isSupported = await this.isConfigSupported(this.config);\n\n if (!isSupported.supported) {\n throw new Error(\n `Codec not supported: ${(this.config as any).codecString || (this.config as any).codec}`\n );\n }\n\n this.decoder = this.createDecoder({\n output: this.handleOutput.bind(this),\n error: this.handleError.bind(this),\n });\n\n await this.configureDecoder(this.config);\n }\n\n async reconfigure(config: Partial<TConfig>): Promise<void> {\n this.config = { ...this.config, ...config } as TConfig;\n\n if (!this.decoder) {\n await this.initialize();\n return;\n }\n\n // Flush before reconfiguring\n if (this.decoder.state === 'configured') {\n await this.decoder.flush();\n }\n\n const isSupported = await this.isConfigSupported(this.config);\n if (!isSupported.supported) {\n throw new Error(\n `New configuration not supported: ${(this.config as any).codecString || (this.config as any).codec}`\n );\n }\n\n await this.configureDecoder(this.config);\n }\n\n async flush(): Promise<void> {\n if (!this.decoder) return;\n await this.decoder.flush();\n }\n\n async reset(): Promise<void> {\n if (!this.decoder) return;\n\n // Windows Media Foundation requires flush before reset to avoid decoder error state\n if (this.decoder.state === 'configured') {\n try {\n await this.decoder.flush();\n } catch {\n // Ignore flush errors during reset\n }\n }\n\n this.decoder.reset();\n this.onReset();\n }\n\n async close(): Promise<void> {\n if (!this.decoder) return;\n\n if (this.decoder.state === 'configured') {\n await this.decoder.flush();\n }\n\n this.decoder.close();\n this.decoder = undefined;\n }\n\n get isReady(): boolean {\n return this.decoder?.state === 'configured';\n }\n\n get queueSize(): number {\n return (this.decoder as any)?.decodeQueueSize ?? 0;\n }\n\n protected handleOutput(data: TOutput): void {\n if (!this.controller) {\n this.closeIfPossible(data);\n return;\n }\n\n try {\n this.controller.enqueue(data);\n if (data instanceof VideoFrame && (this.config as VideoDecoderConfig).thread !== 'main') {\n // ugly hack to avoid memory leak\n // setTimeout(() => {\n // data.close();\n // }, 500);\n }\n } catch (error) {\n // Stream closed or errored - silently drop frame\n if (error instanceof TypeError) {\n this.closeIfPossible(data);\n return;\n }\n\n throw error;\n }\n }\n\n protected handleError(error: DOMException): void {\n this.controller?.error(error);\n }\n\n private closeIfPossible(data: TOutput): void {\n (data as any)?.close?.();\n (data as DecodedVideoFrame)?.frame?.close?.();\n }\n\n // Abstracts\n protected abstract isConfigSupported(config: TConfig): Promise<{ supported: boolean }>;\n protected abstract createDecoder(init: DecoderInit): TDecoder;\n protected abstract configureDecoder(config: TConfig): Promise<void>;\n protected abstract getDecoderType(): string;\n protected abstract decode(input: TInput): void;\n protected onReset(): void {}\n\n // Backpressure\n protected abstract readonly highWaterMark: number;\n protected abstract readonly decodeQueueThreshold: number;\n\n createStream(): TransformStream<TInput, TOutput> {\n return new TransformStream<TInput, TOutput>(\n {\n start: async (controller) => {\n this.controller = controller;\n if (!this.isReady) {\n await this.initialize();\n }\n },\n transform: async (input) => {\n if (!this.decoder || this.decoder.state !== 'configured') {\n throw new Error('Decoder not configured');\n }\n // backpressure\n if ((this.decoder as any).decodeQueueSize >= this.decodeQueueThreshold) {\n await new Promise<void>((resolve) => {\n const check = () => {\n if (\n !this.decoder ||\n (this.decoder as any).decodeQueueSize < this.decodeQueueThreshold - 1\n ) {\n resolve();\n } else {\n setTimeout(check, 10);\n }\n };\n check();\n });\n }\n this.decode(input);\n },\n flush: async () => {\n await this.flush();\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: () => 1,\n }\n );\n }\n}\n\ninterface DecoderInit {\n output: (data: any) => void;\n error: (error: DOMException) => void;\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VideoChunkDecoder.d.ts","sourceRoot":"","sources":["../../../src/stages/decode/VideoChunkDecoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,WAAW,CAChD,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,CACX;IACC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAK;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,8BAA8B,CAAM;IAE5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IACzC,SAAS,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAGhD,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,kBAAkB,CAAkB;gBAEhC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC;IAYjE,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAS7D,YAAY,IAAI,eAAe,CAAC,iBAAiB,EAAE,UAAU,CAAC;IA2CvE;;OAEG;YACW,YAAY;cAcP,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;cAKxC,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAS9F,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE;QAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;QACpC,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;KACtC,GAAG,YAAY;IAIhB,SAAS,CAAC,cAAc,IAAI,MAAM;cAIlB,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB3E,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAIhD;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAe1D;;OAEG;IACG,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAOtC"}
1
+ {"version":3,"file":"VideoChunkDecoder.d.ts","sourceRoot":"","sources":["../../../src/stages/decode/VideoChunkDecoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,WAAW,CAChD,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,CACX;IACC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAK;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,8BAA8B,CAAM;IAE5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IACzC,SAAS,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAGhD,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,kBAAkB,CAAkB;gBAEhC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC;IAYjE,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAS7D,YAAY,IAAI,eAAe,CAAC,iBAAiB,EAAE,UAAU,CAAC;IAyCvE;;OAEG;YACW,YAAY;cAcP,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;cAKxC,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAS9F,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE;QAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;QACpC,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;KACtC,GAAG,YAAY;IAIhB,SAAS,CAAC,cAAc,IAAI,MAAM;cAIlB,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB3E,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAIhD;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAe1D;;OAEG;IACG,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAOtC"}
@@ -1 +1 @@
1
- {"version":3,"file":"BaseEncoder.d.ts","sourceRoot":"","sources":["../../../src/stages/encode/BaseEncoder.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,yBAAyB,CAAC;CACrC;AAED,8BAAsB,WAAW,CAC/B,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,kBAAkB,GAAG,kBAAkB,EACvD,MAAM,SAAS,UAAU,GAAG,SAAS,EACrC,MAAM,SAAS,iBAAiB,GAAG,iBAAiB,EACpD,SAAS,SAAS,yBAAyB,GAAG,yBAAyB;IAEvE,SAAS,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,UAAU,EAAE,gCAAgC,CAAC,YAAY,CAAC,GAAG,IAAI,CAAQ;gBAEvE,MAAM,EAAE,OAAO;IAI3B,SAAS,IAAI,OAAO;IAIpB,SAAS,KAAK,aAAa,IAAI,OAAO,CAErC;IAED,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO;IAW/D,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAiBlD,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAIjD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgD3B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,IAAI;IAchE,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAYhD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ;IAC7D,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,MAAM;IAG3C,SAAS,CAAC,OAAO,IAAI,IAAI;IAKzB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEzD;;;OAGG;IACH,YAAY,IAAI,eAAe,CAAC,MAAM,EAAE,YAAY,CAAC;IAkDrD,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CACrC;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,CAAC;IAC5C,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACtC"}
1
+ {"version":3,"file":"BaseEncoder.d.ts","sourceRoot":"","sources":["../../../src/stages/encode/BaseEncoder.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,yBAAyB,CAAC;CACrC;AAED,8BAAsB,WAAW,CAC/B,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,kBAAkB,GAAG,kBAAkB,EACvD,MAAM,SAAS,UAAU,GAAG,SAAS,EACrC,MAAM,SAAS,iBAAiB,GAAG,iBAAiB,EACpD,SAAS,SAAS,yBAAyB,GAAG,yBAAyB;IAEvE,SAAS,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,UAAU,EAAE,gCAAgC,CAAC,YAAY,CAAC,GAAG,IAAI,CAAQ;gBAEvE,MAAM,EAAE,OAAO;IAI3B,SAAS,IAAI,OAAO;IAIpB,SAAS,KAAK,aAAa,IAAI,OAAO,CAErC;IAED,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO;IAW/D,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAiBlD,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAIjD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,IAAI;IAchE,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAYhD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ;IAC7D,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,MAAM;IAG3C,SAAS,CAAC,OAAO,IAAI,IAAI;IAKzB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEzD;;;OAGG;IACH,YAAY,IAAI,eAAe,CAAC,MAAM,EAAE,YAAY,CAAC;IAkDrD,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CACrC;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,CAAC;IAC5C,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACtC"}
@@ -42,26 +42,7 @@ class BaseEncoder {
42
42
  if (this.encoder?.state === "configured") {
43
43
  return;
44
44
  }
45
- const configInfo = {
46
- codec: this.config.codec,
47
- width: this.config.width,
48
- height: this.config.height,
49
- bitrate: this.config.bitrate,
50
- framerate: this.config.framerate,
51
- sampleRate: this.config.sampleRate,
52
- numberOfChannels: this.config.numberOfChannels,
53
- hardwareAcceleration: this.config.hardwareAcceleration
54
- };
55
- console.log(`[${this.getEncoderType()}Encoder] Checking config support:`, {
56
- platform: typeof navigator !== "undefined" ? navigator.platform : "unknown",
57
- userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "unknown",
58
- config: configInfo
59
- });
60
45
  const isSupported = await this.isConfigSupported(this.config);
61
- console.log(`[${this.getEncoderType()}Encoder] Config support result:`, {
62
- supported: isSupported.supported,
63
- config: configInfo
64
- });
65
46
  if (!isSupported.supported) {
66
47
  throw new Error(`Codec not supported: ${JSON.stringify(this.config)}`);
67
48
  }
@@ -69,12 +50,7 @@ class BaseEncoder {
69
50
  output: this.handleOutput.bind(this),
70
51
  error: this.handleError.bind(this)
71
52
  });
72
- console.log(`[${this.getEncoderType()}Encoder] Encoder created, configuring...`);
73
53
  this.encoder.configure(this.config);
74
- console.log(
75
- `[${this.getEncoderType()}Encoder] Configured successfully, state:`,
76
- this.encoder?.state
77
- );
78
54
  }
79
55
  async reconfigure(config) {
80
56
  if (!config || Object.keys(config).length === 0) {