@meframe/core 0.1.7 → 0.1.9

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 (82) hide show
  1. package/dist/cache/CacheManager.d.ts +1 -1
  2. package/dist/cache/CacheManager.d.ts.map +1 -1
  3. package/dist/cache/CacheManager.js +4 -3
  4. package/dist/cache/CacheManager.js.map +1 -1
  5. package/dist/cache/l1/AudioL1Cache.d.ts +3 -8
  6. package/dist/cache/l1/AudioL1Cache.d.ts.map +1 -1
  7. package/dist/cache/l1/AudioL1Cache.js +41 -56
  8. package/dist/cache/l1/AudioL1Cache.js.map +1 -1
  9. package/dist/controllers/PlaybackController.d.ts +2 -0
  10. package/dist/controllers/PlaybackController.d.ts.map +1 -1
  11. package/dist/controllers/PlaybackController.js +75 -14
  12. package/dist/controllers/PlaybackController.js.map +1 -1
  13. package/dist/controllers/PlaybackStateMachine.d.ts.map +1 -1
  14. package/dist/controllers/PlaybackStateMachine.js +33 -10
  15. package/dist/controllers/PlaybackStateMachine.js.map +1 -1
  16. package/dist/controllers/types.d.ts +13 -3
  17. package/dist/controllers/types.d.ts.map +1 -1
  18. package/dist/controllers/types.js +2 -0
  19. package/dist/controllers/types.js.map +1 -1
  20. package/dist/model/types.d.ts +0 -1
  21. package/dist/model/types.d.ts.map +1 -1
  22. package/dist/model/types.js.map +1 -1
  23. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
  24. package/dist/orchestrator/ExportScheduler.js +22 -19
  25. package/dist/orchestrator/ExportScheduler.js.map +1 -1
  26. package/dist/orchestrator/GlobalAudioSession.d.ts +15 -2
  27. package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
  28. package/dist/orchestrator/GlobalAudioSession.js +48 -20
  29. package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
  30. package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
  31. package/dist/orchestrator/OnDemandVideoSession.js +13 -0
  32. package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
  33. package/dist/orchestrator/Orchestrator.d.ts +1 -1
  34. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  35. package/dist/orchestrator/Orchestrator.js +52 -20
  36. package/dist/orchestrator/Orchestrator.js.map +1 -1
  37. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  38. package/dist/orchestrator/VideoClipSession.js +4 -2
  39. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  40. package/dist/orchestrator/types.d.ts +7 -1
  41. package/dist/orchestrator/types.d.ts.map +1 -1
  42. package/dist/stages/load/ResourceLoader.d.ts +6 -5
  43. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  44. package/dist/stages/load/ResourceLoader.js +62 -41
  45. package/dist/stages/load/ResourceLoader.js.map +1 -1
  46. package/dist/stages/load/TaskManager.d.ts +5 -12
  47. package/dist/stages/load/TaskManager.d.ts.map +1 -1
  48. package/dist/stages/load/TaskManager.js +60 -46
  49. package/dist/stages/load/TaskManager.js.map +1 -1
  50. package/dist/stages/mux/MP4Muxer.d.ts.map +1 -1
  51. package/dist/stages/mux/MP4Muxer.js +3 -0
  52. package/dist/stages/mux/MP4Muxer.js.map +1 -1
  53. package/dist/utils/platform-utils.d.ts +1 -0
  54. package/dist/utils/platform-utils.d.ts.map +1 -1
  55. package/dist/utils/platform-utils.js +19 -6
  56. package/dist/utils/platform-utils.js.map +1 -1
  57. package/dist/worker/BaseWorker.d.ts +2 -0
  58. package/dist/worker/BaseWorker.d.ts.map +1 -1
  59. package/dist/worker/BaseWorker.js.map +1 -1
  60. package/dist/worker/WorkerChannel.d.ts +2 -0
  61. package/dist/worker/WorkerChannel.d.ts.map +1 -1
  62. package/dist/worker/WorkerChannel.js +17 -1
  63. package/dist/worker/WorkerChannel.js.map +1 -1
  64. package/dist/workers/{WorkerChannel.DjBEVvEA.js → WorkerChannel.DQK8rAab.js} +18 -2
  65. package/dist/workers/{WorkerChannel.DjBEVvEA.js.map → WorkerChannel.DQK8rAab.js.map} +1 -1
  66. package/dist/workers/stages/compose/{audio-compose.worker.CiM_KP27.js → audio-compose.worker.B4Io5w9i.js} +2 -2
  67. package/dist/workers/stages/compose/{audio-compose.worker.CiM_KP27.js.map → audio-compose.worker.B4Io5w9i.js.map} +1 -1
  68. package/dist/workers/stages/compose/{video-compose.worker.CQwmNfXT.js → video-compose.worker.CA2_Kpg-.js} +2 -2
  69. package/dist/workers/stages/compose/{video-compose.worker.CQwmNfXT.js.map → video-compose.worker.CA2_Kpg-.js.map} +1 -1
  70. package/dist/workers/stages/decode/{audio-decode.worker.CpjkrZtT.js → audio-decode.worker.-DGlQrJD.js} +2 -2
  71. package/dist/workers/stages/decode/{audio-decode.worker.CpjkrZtT.js.map → audio-decode.worker.-DGlQrJD.js.map} +1 -1
  72. package/dist/workers/stages/decode/{video-decode.worker.BQtw6eWn.js → video-decode.worker.BnWVUkng.js} +2 -2
  73. package/dist/workers/stages/decode/video-decode.worker.BnWVUkng.js.map +1 -0
  74. package/dist/workers/stages/demux/{audio-demux.worker.C4V11GQi.js → audio-demux.worker.D-_LoVqW.js} +2 -2
  75. package/dist/workers/stages/demux/{audio-demux.worker.C4V11GQi.js.map → audio-demux.worker.D-_LoVqW.js.map} +1 -1
  76. package/dist/workers/stages/demux/{video-demux.worker.5pJr0Ij-.js → video-demux.worker.BWDrLGni.js} +2 -2
  77. package/dist/workers/stages/demux/{video-demux.worker.5pJr0Ij-.js.map → video-demux.worker.BWDrLGni.js.map} +1 -1
  78. package/dist/workers/stages/encode/{video-encode.worker.CX2_3YhQ.js → video-encode.worker.D6aB_rF9.js} +2 -2
  79. package/dist/workers/stages/encode/{video-encode.worker.CX2_3YhQ.js.map → video-encode.worker.D6aB_rF9.js.map} +1 -1
  80. package/dist/workers/worker-manifest.json +7 -7
  81. package/package.json +1 -1
  82. package/dist/workers/stages/decode/video-decode.worker.BQtw6eWn.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"VideoClipSession.js","sources":["../../src/orchestrator/VideoClipSession.ts"],"sourcesContent":["import type { WorkerPool } from '../worker/WorkerPool';\nimport type { CacheManager } from '../cache/CacheManager';\nimport type { CompositionModel, Clip } from '../model';\nimport type { CompositionPlanner, ClipUpdateResult } from './CompositionPlanner';\nimport type { ClipInstructionSet } from '../stages/compose/instructions';\nimport type { BaseWorker } from '../worker/BaseWorker';\nimport type { WorkerType } from '../worker/types';\nimport type { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { hasResourceId } from '../model/types';\nimport { OnDemandVideoSession } from './OnDemandVideoSession';\n\ninterface VideoClipSessionCallbacks {\n onEncodedStreamReady(\n stream: ReadableStream<{ chunk: EncodedVideoChunk; metadata: EncodedVideoChunkMetadata }>,\n track: 'video' | 'audio'\n ): Promise<void>;\n onAudioStreamReady?(\n stream: ReadableStream<AudioData>,\n metadata: {\n sessionId: string;\n clipStartUs: number;\n clipDurationUs: number;\n }\n ): void;\n onStreamDisposed?(): void;\n}\n\ninterface VideoClipSessionConfig {\n clipId: string;\n sessionId?: string;\n planner: CompositionPlanner;\n workerPool: WorkerPool;\n cacheManager: CacheManager;\n compositionModel: CompositionModel;\n workerConfigs: Record<WorkerType, any>;\n callbacks: VideoClipSessionCallbacks;\n resourceLoader?: ResourceLoader;\n}\n\ninterface InstructionContext {\n revision: number;\n instructions: ClipInstructionSet;\n status: ClipInstructionSet['status'];\n}\n\n/**\n * VideoClipSession - Export-only session for rendering video clips\n *\n * Pipeline: OnDemandVideoSession (decode) -> ComposeWorker -> EncodeWorker\n * All sessions now use this architecture for export processing\n */\nexport class VideoClipSession {\n private readonly clipId: string;\n private readonly sessionId: string;\n private readonly planner: CompositionPlanner;\n private readonly workerPool: WorkerPool;\n private readonly cacheManager: CacheManager;\n private readonly compositionModel: CompositionModel;\n private readonly workerConfigs: Record<WorkerType, any>;\n private readonly callbacks: VideoClipSessionCallbacks;\n private readonly resourceLoader?: ResourceLoader;\n\n private instructionContext: InstructionContext | null = null;\n private composeWorker: BaseWorker | null = null;\n private videoEncodeWorker: BaseWorker | null = null;\n private onDemandSession: OnDemandVideoSession | null = null;\n private visualStream: ReadableStream<VideoFrame> | null = null;\n private composeToEncodeChannel: MessageChannel | null = null;\n private isActive = false;\n private isDisposed = false;\n\n static async create(config: VideoClipSessionConfig): Promise<VideoClipSession> {\n return new VideoClipSession(config);\n }\n\n private constructor(config: VideoClipSessionConfig) {\n this.clipId = config.clipId;\n this.sessionId = config.sessionId ?? config.clipId;\n this.planner = config.planner;\n this.workerPool = config.workerPool;\n this.cacheManager = config.cacheManager;\n this.compositionModel = config.compositionModel;\n this.workerConfigs = config.workerConfigs;\n this.callbacks = config.callbacks;\n this.resourceLoader = config.resourceLoader;\n }\n\n async activate(): Promise<void> {\n if (this.isActive || this.isDisposed) return;\n\n // Prepare instructions (but don't send yet - will send with stream)\n await this.ensureInstructions();\n\n const clip = this.getClip();\n const resource = this.getResource();\n if (!clip || !resource) {\n console.warn('[VideoClipSession] activate: clip or resource not found', this.sessionId);\n return;\n }\n if (resource.type === 'video') {\n await this.setupVideoPipeline(clip, resource);\n } else if (resource.type === 'image') {\n await this.setupImagePipeline(clip);\n } else {\n console.warn(\n '[VideoClipSession] Unknown resource type:',\n resource.type,\n 'for',\n this.sessionId\n );\n }\n\n this.isActive = true;\n\n // Note: Attachment resources are loaded in connectVideoPipeline/connectImagePipeline\n // before sending video stream, ensuring watermarks appear from the first frame\n }\n\n async deactivate(): Promise<void> {\n if (!this.isActive || this.isDisposed) return;\n this.isActive = false;\n\n await this.releasePipeline();\n }\n\n async dispose(): Promise<void> {\n if (this.isDisposed) return;\n await this.deactivate();\n this.planner.releaseClip(this.clipId);\n this.isDisposed = true;\n }\n\n async handlePlannerUpdate(update: ClipUpdateResult): Promise<void> {\n if (this.isDisposed) {\n return;\n }\n\n if (update.type === 'remove') {\n await this.dispose();\n return;\n }\n\n const instructions = update.instructions;\n if (!instructions) {\n return;\n }\n\n const clip = this.getClip();\n const resource = this.getResource();\n if (!clip || !resource || (resource.type !== 'video' && resource.type !== 'image')) {\n return;\n }\n\n // Any update requires pipeline restart (stream is closed after cache eviction)\n if (this.isActive) {\n await this.releasePipeline();\n this.isActive = false;\n }\n await this.activate();\n }\n\n private getClip(): Clip | null {\n return this.compositionModel?.findClip?.(this.clipId) ?? null;\n }\n\n private getResource() {\n const clip = this.getClip();\n if (!clip || !hasResourceId(clip)) return null;\n return this.compositionModel.getResource(clip.resourceId) ?? null;\n }\n\n private extractAttachmentImageResources(): string[] {\n if (!this.instructionContext?.instructions) return [];\n\n const resourceIdSet = new Set<string>();\n for (const layer of this.instructionContext.instructions.layers) {\n // Only process attachment layers (with attachmentId)\n if (!layer.payload.attachmentId) continue;\n\n if (layer.type === 'image') {\n const imagePayload = layer.payload;\n if (imagePayload.oldResourceId) {\n resourceIdSet.add(imagePayload.oldResourceId);\n }\n const resourceId = imagePayload.resourceId;\n if (resourceId) {\n resourceIdSet.add(resourceId);\n }\n }\n }\n return Array.from(resourceIdSet);\n }\n\n /**\n * Load and transfer attachment images to ComposeWorker\n * Must be called after workers are created and before sending video stream\n */\n private async loadAndTransferAttachments(sessionId: string, clipId: string): Promise<void> {\n const attachmentResources = this.extractAttachmentImageResources();\n if (attachmentResources.length === 0 || !this.resourceLoader) {\n return;\n }\n\n // Parallel load all attachments\n await Promise.all(\n attachmentResources.map(async (resourceId) => {\n const resource = this.compositionModel.getResource(resourceId);\n if (!resource) {\n console.warn(`[VideoClipSession] Resource not found: ${resourceId}`);\n return;\n }\n const imageBitmap = await this.resourceLoader!.loadImage(resource);\n if (!imageBitmap) {\n console.warn(`[VideoClipSession] Failed to load attachment: ${resourceId}`);\n return;\n }\n\n await this.composeWorker!.send(\n 'receive_image',\n { clipId, resourceId, sessionId, imageBitmap },\n { transfer: [imageBitmap] }\n );\n })\n );\n }\n\n private async ensureInstructions(): Promise<void> {\n // Always get fresh clip from model to ensure latest attachments\n const clip = this.getClip();\n if (!clip) {\n throw new Error(`Clip ${this.clipId} not found`);\n }\n\n // For export, always build fresh instructions (no cache) to ensure latest attachments\n const plan = this.planner.buildClipPlan(clip, { cache: false });\n await this.installInstructions(plan.instructions);\n }\n\n private async setupImagePipeline(clip: Clip): Promise<void> {\n await this.acquireWorkers(clip);\n await this.connectImagePipeline();\n }\n\n private async connectImagePipeline(): Promise<void> {\n if (!this.composeWorker || !this.videoEncodeWorker) {\n throw new Error('Pipeline workers not ready');\n }\n\n if (!this.resourceLoader) {\n throw new Error('[VideoClipSession] ResourceLoader not available for image pipeline');\n }\n\n const sessionId = this.sessionId;\n const clip = this.getClip();\n if (!clip) {\n throw new Error('[VideoClipSession] Clip not found for pipeline connection');\n }\n\n if (!this.instructionContext) {\n throw new Error('[VideoClipSession] Instructions not installed before connecting pipeline');\n }\n\n // STEP 1: Connect ComposeWorker -> EncodeWorker\n this.composeToEncodeChannel = new MessageChannel();\n await this.composeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.composeToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port1] }\n );\n await this.videoEncodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.composeToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port2] }\n );\n\n // STEP 2: Setup EncodeWorker stream receiver\n this.videoEncodeWorker.receiveStream((stream, metadata) => {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n metadata?.streamType ?? 'video'\n );\n });\n\n // STEP 3: Load and transfer attachment images (before sending main track image)\n await this.loadAndTransferAttachments(sessionId, clip.id);\n\n // STEP 4: Load main track image and send to ComposeWorker with instructions\n if (hasResourceId(clip)) {\n const resource = this.getResource();\n if (!resource) {\n throw new Error('[VideoClipSession] Resource not found for image pipeline');\n }\n\n // Load image directly\n const imageBitmap = await this.resourceLoader.loadImage(resource);\n\n // Send to ComposeWorker with instructions (same as video pipeline metadata)\n await this.composeWorker.send(\n 'receive_image',\n {\n resourceId: resource.id,\n sessionId,\n imageBitmap,\n instructions: this.instructionContext.instructions,\n },\n { transfer: [imageBitmap] }\n );\n }\n }\n\n private async setupVideoPipeline(clip: Clip, resource: { id: string }): Promise<void> {\n await this.acquireWorkers(clip);\n\n // Create OnDemandVideoSession for main-thread decoding\n this.onDemandSession = await OnDemandVideoSession.create({\n clipId: this.clipId,\n resourceId: resource.id,\n targetTimeUs: clip.trimStartUs ?? 0,\n globalTimeUs: clip.startUs,\n mp4IndexCache: this.cacheManager.mp4IndexCache,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel,\n resourceLoader: this.resourceLoader!,\n fps: this.compositionModel.fps ?? 30,\n });\n\n await this.connectVideoPipeline();\n }\n\n private async acquireWorkers(clip: Clip): Promise<void> {\n // VideoComposeWorker\n this.composeWorker = await this.workerPool.getOrCreate('videoCompose', this.sessionId, {\n lazy: true,\n });\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const renderOverrides = this.compositionModel.renderConfig ?? {};\n const timeline = {\n clipId: clip.id,\n clipStartUs: clip.startUs,\n clipEndUs: clip.startUs + clip.durationUs,\n clipDurationUs: clip.durationUs,\n compositionFps: this.compositionModel.fps ?? visualConfig.fps ?? 30,\n };\n await this.composeWorker.send('configure', {\n initial: true,\n clipId: clip.id,\n config: { ...visualConfig, ...renderOverrides, timeline },\n });\n\n // VideoEncodeWorker (always needed for export)\n this.videoEncodeWorker = await this.workerPool.getOrCreate('videoEncode', this.sessionId, {\n lazy: true,\n });\n const encodeConfig = this.workerConfigs.videoEncode ?? {};\n const encoderConfig = { ...encodeConfig };\n if (this.compositionModel.renderConfig?.width) {\n encoderConfig.width = this.compositionModel.renderConfig.width;\n }\n if (this.compositionModel.renderConfig?.height) {\n encoderConfig.height = this.compositionModel.renderConfig.height;\n }\n await this.videoEncodeWorker.send('configure', {\n initial: true,\n config: encoderConfig,\n });\n }\n\n private async connectVideoPipeline(): Promise<void> {\n if (!this.composeWorker || !this.videoEncodeWorker || !this.onDemandSession) {\n throw new Error('Pipeline workers not ready');\n }\n\n const sessionId = this.sessionId;\n const clip = this.getClip();\n if (!clip) {\n throw new Error('[VideoClipSession] Clip not found for pipeline connection');\n }\n\n if (!this.instructionContext) {\n throw new Error('[VideoClipSession] Instructions not installed before connecting pipeline');\n }\n\n // STEP 1: Connect ComposeWorker -> EncodeWorker\n this.composeToEncodeChannel = new MessageChannel();\n await this.composeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.composeToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port1] }\n );\n await this.videoEncodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.composeToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port2] }\n );\n\n // STEP 2: Setup EncodeWorker stream receiver\n this.videoEncodeWorker.receiveStream((stream, metadata) => {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n metadata?.streamType ?? 'video'\n );\n });\n\n // STEP 3: Load and transfer attachment images (before sending video stream)\n await this.loadAndTransferAttachments(sessionId, clip.id);\n\n // STEP 4: Start decoding and send frames to ComposeWorker\n const trimStartUs = clip.trimStartUs ?? 0;\n const trimEndUs = clip.trimEndUs ?? trimStartUs + clip.durationUs;\n const frameStream = await this.onDemandSession.decodeRangeToStream(trimStartUs, trimEndUs);\n\n // Send VideoFrame stream to ComposeWorker (main thread → worker)\n // Include instructions in metadata to avoid race conditions\n await this.composeWorker.sendStream(frameStream, {\n sessionId,\n streamType: 'video',\n instructions: this.instructionContext.instructions,\n });\n }\n\n private async installInstructions(instructions: ClipInstructionSet): Promise<void> {\n this.instructionContext = {\n revision: instructions.revision,\n instructions,\n status: instructions.status,\n };\n }\n\n private async releasePipeline(): Promise<void> {\n if (this.composeWorker && this.instructionContext) {\n await this.composeWorker.notify('dispose_clip', {\n sessionId: this.sessionId,\n revision: this.instructionContext.revision,\n });\n }\n\n if (this.visualStream && this.callbacks.onStreamDisposed) {\n this.callbacks.onStreamDisposed();\n }\n this.visualStream = null;\n\n // Cleanup OnDemandVideoSession\n if (this.onDemandSession) {\n await this.onDemandSession.dispose();\n this.onDemandSession = null;\n }\n\n // Terminate workers\n if (this.composeWorker) {\n this.workerPool.terminate('videoCompose', this.sessionId);\n this.composeWorker = null;\n }\n\n if (this.videoEncodeWorker) {\n this.workerPool.terminate('videoEncode', this.sessionId);\n this.videoEncodeWorker = null;\n }\n\n this.composeToEncodeChannel?.port1.close();\n this.composeToEncodeChannel?.port2.close();\n this.composeToEncodeChannel = null;\n }\n\n async invalidateClipCache(): Promise<void> {\n await this.cacheManager.invalidateClip(this.clipId);\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"VideoClipSession.js","sources":["../../src/orchestrator/VideoClipSession.ts"],"sourcesContent":["import type { WorkerPool } from '../worker/WorkerPool';\nimport type { CacheManager } from '../cache/CacheManager';\nimport type { CompositionModel, Clip } from '../model';\nimport type { CompositionPlanner, ClipUpdateResult } from './CompositionPlanner';\nimport type { ClipInstructionSet } from '../stages/compose/instructions';\nimport type { BaseWorker } from '../worker/BaseWorker';\nimport type { WorkerType } from '../worker/types';\nimport type { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { hasResourceId } from '../model/types';\nimport { OnDemandVideoSession } from './OnDemandVideoSession';\n\ninterface VideoClipSessionCallbacks {\n onEncodedStreamReady(\n stream: ReadableStream<{ chunk: EncodedVideoChunk; metadata: EncodedVideoChunkMetadata }>,\n track: 'video' | 'audio'\n ): Promise<void>;\n onAudioStreamReady?(\n stream: ReadableStream<AudioData>,\n metadata: {\n sessionId: string;\n clipStartUs: number;\n clipDurationUs: number;\n }\n ): void;\n onStreamDisposed?(): void;\n}\n\ninterface VideoClipSessionConfig {\n clipId: string;\n sessionId?: string;\n planner: CompositionPlanner;\n workerPool: WorkerPool;\n cacheManager: CacheManager;\n compositionModel: CompositionModel;\n workerConfigs: Record<WorkerType, any>;\n callbacks: VideoClipSessionCallbacks;\n resourceLoader?: ResourceLoader;\n}\n\ninterface InstructionContext {\n revision: number;\n instructions: ClipInstructionSet;\n status: ClipInstructionSet['status'];\n}\n\n/**\n * VideoClipSession - Export-only session for rendering video clips\n *\n * Pipeline: OnDemandVideoSession (decode) -> ComposeWorker -> EncodeWorker\n * All sessions now use this architecture for export processing\n */\nexport class VideoClipSession {\n private readonly clipId: string;\n private readonly sessionId: string;\n private readonly planner: CompositionPlanner;\n private readonly workerPool: WorkerPool;\n private readonly cacheManager: CacheManager;\n private readonly compositionModel: CompositionModel;\n private readonly workerConfigs: Record<WorkerType, any>;\n private readonly callbacks: VideoClipSessionCallbacks;\n private readonly resourceLoader?: ResourceLoader;\n\n private instructionContext: InstructionContext | null = null;\n private composeWorker: BaseWorker | null = null;\n private videoEncodeWorker: BaseWorker | null = null;\n private onDemandSession: OnDemandVideoSession | null = null;\n private visualStream: ReadableStream<VideoFrame> | null = null;\n private composeToEncodeChannel: MessageChannel | null = null;\n private isActive = false;\n private isDisposed = false;\n\n static async create(config: VideoClipSessionConfig): Promise<VideoClipSession> {\n return new VideoClipSession(config);\n }\n\n private constructor(config: VideoClipSessionConfig) {\n this.clipId = config.clipId;\n this.sessionId = config.sessionId ?? config.clipId;\n this.planner = config.planner;\n this.workerPool = config.workerPool;\n this.cacheManager = config.cacheManager;\n this.compositionModel = config.compositionModel;\n this.workerConfigs = config.workerConfigs;\n this.callbacks = config.callbacks;\n this.resourceLoader = config.resourceLoader;\n }\n\n async activate(): Promise<void> {\n if (this.isActive || this.isDisposed) return;\n\n // Prepare instructions (but don't send yet - will send with stream)\n await this.ensureInstructions();\n\n const clip = this.getClip();\n const resource = this.getResource();\n if (!clip || !resource) {\n console.warn('[VideoClipSession] activate: clip or resource not found', this.sessionId);\n return;\n }\n if (resource.type === 'video') {\n await this.setupVideoPipeline(clip, resource);\n } else if (resource.type === 'image') {\n await this.setupImagePipeline(clip);\n } else {\n console.warn(\n '[VideoClipSession] Unknown resource type:',\n resource.type,\n 'for',\n this.sessionId\n );\n }\n\n this.isActive = true;\n\n // Note: Attachment resources are loaded in connectVideoPipeline/connectImagePipeline\n // before sending video stream, ensuring watermarks appear from the first frame\n }\n\n async deactivate(): Promise<void> {\n if (!this.isActive || this.isDisposed) return;\n this.isActive = false;\n\n await this.releasePipeline();\n }\n\n async dispose(): Promise<void> {\n if (this.isDisposed) return;\n await this.deactivate();\n this.planner.releaseClip(this.clipId);\n this.isDisposed = true;\n }\n\n async handlePlannerUpdate(update: ClipUpdateResult): Promise<void> {\n if (this.isDisposed) {\n return;\n }\n\n if (update.type === 'remove') {\n await this.dispose();\n return;\n }\n\n const instructions = update.instructions;\n if (!instructions) {\n return;\n }\n\n const clip = this.getClip();\n const resource = this.getResource();\n if (!clip || !resource || (resource.type !== 'video' && resource.type !== 'image')) {\n return;\n }\n\n // Any update requires pipeline restart (stream is closed after cache eviction)\n if (this.isActive) {\n await this.releasePipeline();\n this.isActive = false;\n }\n await this.activate();\n }\n\n private getClip(): Clip | null {\n return this.compositionModel?.findClip?.(this.clipId) ?? null;\n }\n\n private getResource() {\n const clip = this.getClip();\n if (!clip || !hasResourceId(clip)) return null;\n return this.compositionModel.getResource(clip.resourceId) ?? null;\n }\n\n private extractAttachmentImageResources(): string[] {\n if (!this.instructionContext?.instructions) return [];\n\n const resourceIdSet = new Set<string>();\n for (const layer of this.instructionContext.instructions.layers) {\n // Only process attachment layers (with attachmentId)\n if (!layer.payload.attachmentId) continue;\n\n if (layer.type === 'image') {\n const imagePayload = layer.payload;\n if (imagePayload.oldResourceId) {\n resourceIdSet.add(imagePayload.oldResourceId);\n }\n const resourceId = imagePayload.resourceId;\n if (resourceId) {\n resourceIdSet.add(resourceId);\n }\n }\n }\n return Array.from(resourceIdSet);\n }\n\n /**\n * Load and transfer attachment images to ComposeWorker\n * Must be called after workers are created and before sending video stream\n */\n private async loadAndTransferAttachments(sessionId: string, clipId: string): Promise<void> {\n const attachmentResources = this.extractAttachmentImageResources();\n if (attachmentResources.length === 0 || !this.resourceLoader) {\n return;\n }\n\n // Parallel load all attachments\n await Promise.all(\n attachmentResources.map(async (resourceId) => {\n const resource = this.compositionModel.getResource(resourceId);\n if (!resource) {\n console.warn(`[VideoClipSession] Resource not found: ${resourceId}`);\n return;\n }\n const imageBitmap = await this.resourceLoader!.loadImage(resource);\n if (!imageBitmap) {\n console.warn(`[VideoClipSession] Failed to load attachment: ${resourceId}`);\n return;\n }\n\n await this.composeWorker!.send(\n 'receive_image',\n { clipId, resourceId, sessionId, imageBitmap },\n { transfer: [imageBitmap] }\n );\n })\n );\n }\n\n private async ensureInstructions(): Promise<void> {\n // Always get fresh clip from model to ensure latest attachments\n const clip = this.getClip();\n if (!clip) {\n throw new Error(`Clip ${this.clipId} not found`);\n }\n\n // For export, always build fresh instructions (no cache) to ensure latest attachments\n const plan = this.planner.buildClipPlan(clip, { cache: false });\n await this.installInstructions(plan.instructions);\n }\n\n private async setupImagePipeline(clip: Clip): Promise<void> {\n await this.acquireWorkers(clip);\n await this.connectImagePipeline();\n }\n\n private async connectImagePipeline(): Promise<void> {\n if (!this.composeWorker || !this.videoEncodeWorker) {\n throw new Error('Pipeline workers not ready');\n }\n\n if (!this.resourceLoader) {\n throw new Error('[VideoClipSession] ResourceLoader not available for image pipeline');\n }\n\n const sessionId = this.sessionId;\n const clip = this.getClip();\n if (!clip) {\n throw new Error('[VideoClipSession] Clip not found for pipeline connection');\n }\n\n if (!this.instructionContext) {\n throw new Error('[VideoClipSession] Instructions not installed before connecting pipeline');\n }\n\n // STEP 1: Connect ComposeWorker -> EncodeWorker\n this.composeToEncodeChannel = new MessageChannel();\n await this.composeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.composeToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port1] }\n );\n await this.videoEncodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.composeToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port2] }\n );\n\n // STEP 2: Setup EncodeWorker stream receiver\n this.videoEncodeWorker.receiveStream((stream, metadata) => {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n metadata?.streamType ?? 'video'\n );\n });\n\n // STEP 3: Load and transfer attachment images (before sending main track image)\n await this.loadAndTransferAttachments(sessionId, clip.id);\n\n // STEP 4: Load main track image and send to ComposeWorker with instructions\n if (hasResourceId(clip)) {\n const resource = this.getResource();\n if (!resource) {\n throw new Error('[VideoClipSession] Resource not found for image pipeline');\n }\n\n // Load image directly\n const imageBitmap = await this.resourceLoader.loadImage(resource);\n\n // Send to ComposeWorker with instructions (same as video pipeline metadata)\n await this.composeWorker.send(\n 'receive_image',\n {\n resourceId: resource.id,\n sessionId,\n imageBitmap,\n instructions: this.instructionContext.instructions,\n },\n { transfer: [imageBitmap] }\n );\n }\n }\n\n private async setupVideoPipeline(clip: Clip, resource: { id: string }): Promise<void> {\n await this.acquireWorkers(clip);\n\n // Create OnDemandVideoSession for main-thread decoding\n this.onDemandSession = await OnDemandVideoSession.create({\n clipId: this.clipId,\n resourceId: resource.id,\n targetTimeUs: clip.trimStartUs ?? 0,\n globalTimeUs: clip.startUs,\n mp4IndexCache: this.cacheManager.mp4IndexCache,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel,\n resourceLoader: this.resourceLoader!,\n fps: this.compositionModel.fps ?? 30,\n });\n\n await this.connectVideoPipeline();\n }\n\n private async acquireWorkers(clip: Clip): Promise<void> {\n // VideoComposeWorker\n this.composeWorker = await this.workerPool.getOrCreate('videoCompose', this.sessionId, {\n lazy: true,\n });\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const renderOverrides = this.compositionModel.renderConfig ?? {};\n const timeline = {\n clipId: clip.id,\n clipStartUs: clip.startUs,\n clipEndUs: clip.startUs + clip.durationUs,\n clipDurationUs: clip.durationUs,\n compositionFps: this.compositionModel.fps ?? visualConfig.fps ?? 30,\n };\n await this.composeWorker.send('configure', {\n initial: true,\n clipId: clip.id,\n config: { ...visualConfig, ...renderOverrides, timeline },\n });\n\n // VideoEncodeWorker (always needed for export)\n this.videoEncodeWorker = await this.workerPool.getOrCreate('videoEncode', this.sessionId, {\n lazy: true,\n });\n const encodeConfig = this.workerConfigs.videoEncode ?? {};\n const encoderConfig = { ...encodeConfig };\n if (this.compositionModel.renderConfig?.width) {\n encoderConfig.width = this.compositionModel.renderConfig.width;\n }\n if (this.compositionModel.renderConfig?.height) {\n encoderConfig.height = this.compositionModel.renderConfig.height;\n }\n await this.videoEncodeWorker.send('configure', {\n initial: true,\n config: encoderConfig,\n });\n }\n\n private async connectVideoPipeline(): Promise<void> {\n if (!this.composeWorker || !this.videoEncodeWorker || !this.onDemandSession) {\n throw new Error('Pipeline workers not ready');\n }\n\n const sessionId = this.sessionId;\n const clip = this.getClip();\n if (!clip) {\n throw new Error('[VideoClipSession] Clip not found for pipeline connection');\n }\n\n if (!this.instructionContext) {\n throw new Error('[VideoClipSession] Instructions not installed before connecting pipeline');\n }\n\n // STEP 1: Connect ComposeWorker -> EncodeWorker\n this.composeToEncodeChannel = new MessageChannel();\n await this.composeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.composeToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port1] }\n );\n await this.videoEncodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.composeToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port2] }\n );\n\n // STEP 2: Setup EncodeWorker stream receiver\n this.videoEncodeWorker.receiveStream((stream, metadata) => {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n metadata?.streamType ?? 'video'\n );\n });\n\n // STEP 3: Load and transfer attachment images (before sending video stream)\n await this.loadAndTransferAttachments(sessionId, clip.id);\n\n // STEP 4: Start decoding and send frames to ComposeWorker\n const trimStartUs = clip.trimStartUs ?? 0;\n const frameStream = await this.onDemandSession.decodeRangeToStream(\n trimStartUs,\n trimStartUs + clip.durationUs\n );\n\n // Send VideoFrame stream to ComposeWorker (main thread → worker)\n // Include instructions in metadata to avoid race conditions\n await this.composeWorker.sendStream(frameStream, {\n sessionId,\n streamType: 'video',\n instructions: this.instructionContext.instructions,\n });\n }\n\n private async installInstructions(instructions: ClipInstructionSet): Promise<void> {\n this.instructionContext = {\n revision: instructions.revision,\n instructions,\n status: instructions.status,\n };\n }\n\n private async releasePipeline(): Promise<void> {\n if (this.composeWorker && this.instructionContext) {\n await this.composeWorker.notify('dispose_clip', {\n sessionId: this.sessionId,\n revision: this.instructionContext.revision,\n });\n }\n\n if (this.visualStream && this.callbacks.onStreamDisposed) {\n this.callbacks.onStreamDisposed();\n }\n this.visualStream = null;\n\n // Cleanup OnDemandVideoSession\n if (this.onDemandSession) {\n await this.onDemandSession.dispose();\n this.onDemandSession = null;\n }\n\n // Terminate workers\n if (this.composeWorker) {\n this.workerPool.terminate('videoCompose', this.sessionId);\n this.composeWorker = null;\n }\n\n if (this.videoEncodeWorker) {\n this.workerPool.terminate('videoEncode', this.sessionId);\n this.videoEncodeWorker = null;\n }\n\n this.composeToEncodeChannel?.port1.close();\n this.composeToEncodeChannel?.port2.close();\n this.composeToEncodeChannel = null;\n }\n\n async invalidateClipCache(): Promise<void> {\n await this.cacheManager.invalidateClip(this.clipId);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -20,13 +20,19 @@ export interface OrchestratorConfig {
20
20
  /**
21
21
  * Frame render options
22
22
  */
23
+ export type RequestMode = 'probe' | 'blocking';
23
24
  export interface RenderFrameOptions {
24
25
  priority?: 'high' | 'normal' | 'low';
25
26
  clipId?: string;
26
27
  relativeTimeUs?: TimeUs;
27
28
  signal?: AbortSignal;
28
29
  preheat?: boolean;
29
- immediate?: boolean;
30
+ /**
31
+ * Data request mode:
32
+ * - 'probe': do not block on resource loading. If required data isn't ready, return null and kick background loading.
33
+ * - 'blocking': wait until required data is ready (may trigger resource load + decode).
34
+ */
35
+ mode?: RequestMode;
30
36
  onBuffering?: () => Promise<void>;
31
37
  }
32
38
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/orchestrator/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/orchestrator/types.ts"],"names":[],"mappings":"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"}
@@ -15,12 +15,10 @@ export declare class ResourceLoader {
15
15
  private parsingIndexes;
16
16
  private writingResources;
17
17
  private isPreloadingEnabled;
18
- private preloadQueue;
19
18
  constructor(options: ResourceLoaderOptions);
20
19
  setModel(model: CompositionModel): Promise<void>;
21
20
  setPreloadingEnabled(enabled: boolean): void;
22
21
  startPreloading(): void;
23
- private processPreloadQueue;
24
22
  private enqueueLoad;
25
23
  private processQueue;
26
24
  /**
@@ -32,10 +30,13 @@ export declare class ResourceLoader {
32
30
  */
33
31
  private loadAudioResource;
34
32
  /**
35
- * Load image resource (download + cache or read from cache)
36
- * Returns ImageBitmap for caller to use (no worker transfer)
33
+ * Ensure image blob is cached (download + cache or reuse cache).
34
+ *
35
+ * Important: preload should NOT decode ImageBitmap.
36
+ * createImageBitmap can be slow / flaky under heavy main-thread load (playback + export),
37
+ * and decoding isn't required for caching or for later loadImage()/getImageBitmap() calls.
37
38
  */
38
- private loadImageResource;
39
+ private ensureImageBlob;
39
40
  /**
40
41
  * Get cached ImageBitmap (for already loaded resources)
41
42
  * Used by VideoClipSession to batch transfer attachments
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceLoader.d.ts","sourceRoot":"","sources":["../../../src/stages/load/ResourceLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,gBAAgB,EAAiB,MAAM,aAAa,CAAC;AAClF,OAAO,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAepF,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,KAAK,CAAC,CAAmB;IACjC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,QAAQ,CAAC,CAA4B;IAC7C,OAAO,CAAC,aAAa,CAAC,CAAyD;IAC/E,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,gBAAgB,CAAqB;IAG7C,OAAO,CAAC,mBAAmB,CAAQ;IACnC,OAAO,CAAC,YAAY,CAAgB;gBAExB,OAAO,EAAE,qBAAqB;IAWpC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAatD,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAO5C,eAAe,IAAI,IAAI;IAmCvB,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,WAAW;IAuBnB,OAAO,CAAC,YAAY;IAQpB;;OAEG;YACW,iBAAiB;IAe/B;;OAEG;YACW,iBAAiB;IAQ/B;;;OAGG;YACW,iBAAiB;IAoB/B;;;OAGG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAMrE;;OAEG;YACW,gBAAgB;IAM9B;;;OAGG;YACW,SAAS;IA4CvB;;OAEG;IACG,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4D1D;;;OAGG;YACW,oBAAoB;IAclC;;;;;;;OAOG;YACW,iBAAiB;IAiB/B;;;OAGG;YACW,WAAW;IA8BzB;;;;OAIG;YACW,qBAAqB;IA8CnC;;OAEG;YACW,oBAAoB;IAwD5B,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC;IAoBzD;;OAEG;YACW,SAAS;IAUvB,OAAO,CAAC,mBAAmB;IAgB3B;;;;;;;;;;;OAWG;IACG,IAAI,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgE7E,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAKhC;;OAEG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI9C,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAOzB,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB9E,IAAI,WAAW,IAAI,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAEvC;IAED,IAAI,SAAS,IAAI,QAAQ,EAAE,CAE1B;IAED,OAAO,IAAI,IAAI;IAKf;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CAM3B"}
1
+ {"version":3,"file":"ResourceLoader.d.ts","sourceRoot":"","sources":["../../../src/stages/load/ResourceLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,gBAAgB,EAAiB,MAAM,aAAa,CAAC;AAClF,OAAO,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAepF,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,KAAK,CAAC,CAAmB;IACjC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,QAAQ,CAAC,CAA4B;IAC7C,OAAO,CAAC,aAAa,CAAC,CAAyD;IAC/E,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,gBAAgB,CAAqB;IAG7C,OAAO,CAAC,mBAAmB,CAAQ;gBAEvB,OAAO,EAAE,qBAAqB;IAYpC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAetD,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAO5C,eAAe,IAAI,IAAI;IAmCvB,OAAO,CAAC,WAAW;IAqBnB,OAAO,CAAC,YAAY;IAQpB;;OAEG;YACW,iBAAiB;IAe/B;;OAEG;YACW,iBAAiB;IAQ/B;;;;;;OAMG;YACW,eAAe;IAe7B;;;OAGG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAMrE;;OAEG;YACW,gBAAgB;IAM9B;;;OAGG;YACW,SAAS;IA0CvB;;OAEG;IACG,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4D1D;;;OAGG;YACW,oBAAoB;IAclC;;;;;;;OAOG;YACW,iBAAiB;IAiB/B;;;OAGG;YACW,WAAW;IA8BzB;;;;OAIG;YACW,qBAAqB;IA8CnC;;OAEG;YACW,oBAAoB;IAwD5B,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC;IAsCzD;;OAEG;YACW,SAAS;IAUvB,OAAO,CAAC,mBAAmB;IAgB3B;;;;;;;;;;;OAWG;IACG,IAAI,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwF7E,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAKhC;;OAEG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI9C,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAOzB,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB9E,IAAI,WAAW,IAAI,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAEvC;IAED,IAAI,SAAS,IAAI,QAAQ,EAAE,CAE1B;IAED,OAAO,IAAI,IAAI;IAKf;;;OAGG;IACH,OAAO,CAAC,kBAAkB;CAM3B"}
@@ -20,11 +20,11 @@ class ResourceLoader {
20
20
  // Track in-progress OPFS writes
21
21
  // Preloading state
22
22
  isPreloadingEnabled = true;
23
- preloadQueue = [];
24
23
  constructor(options) {
25
24
  const config = options.config || {};
26
25
  const maxConcurrent = config.maxConcurrent ?? 3;
27
- this.taskManager = new TaskManager(maxConcurrent);
26
+ const preloadConcurrency = config.preloadConcurrency ?? 2;
27
+ this.taskManager = new TaskManager(maxConcurrent, preloadConcurrency);
28
28
  this.streamFactory = new StreamFactory(options.onProgress, config);
29
29
  this.eventBus = options.eventBus;
30
30
  this.onStateChange = options.onStateChange;
@@ -68,22 +68,10 @@ class ResourceLoader {
68
68
  }
69
69
  }
70
70
  }
71
- processPreloadQueue() {
72
- if (!this.isPreloadingEnabled || this.preloadQueue.length === 0) return;
73
- while (this.preloadQueue.length > 0) {
74
- const resourceId = this.preloadQueue.shift();
75
- if (!resourceId) break;
76
- this.load(resourceId, { isPreload: true }).finally(() => {
77
- this.processPreloadQueue();
78
- });
79
- }
80
- }
81
71
  enqueueLoad(resource, isPreload = false, sessionId, clipId, trackId) {
82
72
  const existingTask = this.taskManager.getActiveTask(resource.id);
83
73
  if (existingTask) {
84
- if (existingTask.isPreload && !isPreload) {
85
- existingTask.isPreload = false;
86
- }
74
+ this.taskManager.enqueue(resource, isPreload, sessionId, clipId, trackId);
87
75
  return existingTask;
88
76
  }
89
77
  const task = this.taskManager.enqueue(resource, isPreload, sessionId, clipId, trackId);
@@ -117,22 +105,22 @@ class ResourceLoader {
117
105
  }
118
106
  }
119
107
  /**
120
- * Load image resource (download + cache or read from cache)
121
- * Returns ImageBitmap for caller to use (no worker transfer)
108
+ * Ensure image blob is cached (download + cache or reuse cache).
109
+ *
110
+ * Important: preload should NOT decode ImageBitmap.
111
+ * createImageBitmap can be slow / flaky under heavy main-thread load (playback + export),
112
+ * and decoding isn't required for caching or for later loadImage()/getImageBitmap() calls.
122
113
  */
123
- async loadImageResource(task) {
114
+ async ensureImageBlob(task) {
124
115
  let blob = this.blobCache.get(task.resourceId);
125
116
  if (!blob) {
126
117
  if (task.controller) {
127
- this.updateResourceState(task.resourceId, "loading");
128
118
  blob = await this.fetchBlob(task.resource.uri, task.controller.signal);
129
- this.updateResourceState(task.resourceId, "ready");
130
119
  this.blobCache.set(task.resourceId, blob);
131
120
  } else {
132
- return null;
121
+ return;
133
122
  }
134
123
  }
135
- return await createImageBitmapFromBlob(blob);
136
124
  }
137
125
  /**
138
126
  * Get cached ImageBitmap (for already loaded resources)
@@ -163,10 +151,7 @@ class ResourceLoader {
163
151
  task.controller = new AbortController();
164
152
  switch (task.resource.type) {
165
153
  case "image": {
166
- const image = await this.loadImageResource(task);
167
- if (image) {
168
- image.close();
169
- }
154
+ await this.ensureImageBlob(task);
170
155
  break;
171
156
  }
172
157
  case "video":
@@ -370,6 +355,11 @@ class ResourceLoader {
370
355
  }
371
356
  }
372
357
  async loadImage(resource) {
358
+ const existingBlob = this.blobCache.get(resource.id);
359
+ if (existingBlob) {
360
+ const imageBitmap = await createImageBitmapFromBlob(existingBlob);
361
+ return imageBitmap;
362
+ }
373
363
  const task = {
374
364
  resourceId: resource.id,
375
365
  resource,
@@ -379,11 +369,20 @@ class ResourceLoader {
379
369
  isPreload: false,
380
370
  controller: new AbortController()
381
371
  };
382
- const imageBitmap = await this.loadImageResource(task);
383
- if (!imageBitmap) {
384
- throw new Error(`Failed to load image ${resource.id}`);
372
+ let loadError;
373
+ try {
374
+ this.updateResourceState(resource.id, "loading");
375
+ await this.ensureImageBlob(task);
376
+ const blob = this.blobCache.get(resource.id);
377
+ if (!blob) throw new Error(`Failed to load image ${resource.id}`);
378
+ const imageBitmap = await createImageBitmapFromBlob(blob);
379
+ this.updateResourceState(resource.id, "ready");
380
+ return imageBitmap;
381
+ } catch (error) {
382
+ loadError = error;
383
+ this.updateResourceState(resource.id, "error");
384
+ throw loadError;
385
385
  }
386
- return imageBitmap;
387
386
  }
388
387
  /**
389
388
  * Fetch resource as blob (for images, json, etc.)
@@ -425,24 +424,42 @@ class ResourceLoader {
425
424
  if (!resourceId) {
426
425
  return;
427
426
  }
428
- const resource = this.model?.resources.get(resourceId);
427
+ const resource = this.model?.getResource(resourceId);
429
428
  if (!resource) {
430
- console.warn(`Resource ${resourceId} not found in model`);
429
+ const m = this.model;
430
+ console.warn("[ResourceLoader] Resource not found in model:", {
431
+ resourceId,
432
+ options: {
433
+ isPreload: options?.isPreload ?? false,
434
+ sessionId: options?.sessionId,
435
+ clipId: options?.clipId,
436
+ trackId: options?.trackId,
437
+ isMainTrack: options?.isMainTrack
438
+ },
439
+ model: m ? {
440
+ fps: m.fps,
441
+ durationUs: m.durationUs,
442
+ mainTrackId: m.mainTrackId,
443
+ trackCount: m.tracks.length,
444
+ resourcesSize: m.resources.size
445
+ } : null
446
+ });
431
447
  return;
432
448
  }
433
449
  const isPreload = options?.isPreload ?? false;
434
- if (!isPreload) {
435
- this.taskManager.pausePreloadTasks();
436
- }
437
450
  if (resource.state === "ready") {
438
451
  return;
439
452
  }
440
453
  if (resource.state === "loading") {
441
454
  const existingTask2 = this.taskManager.getActiveTask(resourceId);
442
455
  if (existingTask2) {
443
- if (existingTask2.isPreload && !isPreload) {
444
- existingTask2.isPreload = false;
445
- }
456
+ this.taskManager.enqueue(
457
+ resource,
458
+ isPreload,
459
+ options?.sessionId,
460
+ options?.clipId,
461
+ options?.trackId
462
+ );
446
463
  if (!options?.sessionId || existingTask2.sessionId === options.sessionId) {
447
464
  return existingTask2.promise;
448
465
  }
@@ -450,9 +467,13 @@ class ResourceLoader {
450
467
  }
451
468
  const existingTask = this.taskManager.getActiveTask(resourceId);
452
469
  if (existingTask) {
453
- if (existingTask.isPreload && !isPreload) {
454
- existingTask.isPreload = false;
455
- }
470
+ this.taskManager.enqueue(
471
+ resource,
472
+ isPreload,
473
+ options?.sessionId,
474
+ options?.clipId,
475
+ options?.trackId
476
+ );
456
477
  return existingTask.promise;
457
478
  }
458
479
  const task = this.enqueueLoad(
@@ -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 { MP3FrameParser } from '../demux/MP3FrameParser';\nimport type { CacheManager } from '../../cache/CacheManager';\nimport {\n EmptyStreamError,\n ResourceCorruptedError,\n OPFSQuotaExceededError,\n} from '../../utils/errors';\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 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 private writingResources = new Set<string>(); // Track in-progress OPFS writes\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 ?? 3;\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 }\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.load(mainTrack.clips[0].resourceId, {\n isPreload: false,\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 // Collect all video/audio tracks for horizontal loading\n const tracks = this.model.tracks.filter(\n (track) => track.kind === 'video' || track.kind === 'audio'\n );\n if (tracks.length === 0) return;\n\n // Find maximum clip count across all tracks\n const maxClipCount = Math.max(...tracks.map((track) => track.clips.length));\n\n // Horizontal loading: load clip[0] from all tracks, then clip[1], etc.\n for (let clipIndex = 0; clipIndex < maxClipCount; clipIndex++) {\n for (const track of tracks) {\n const clip = track.clips[clipIndex];\n if (!clip || !hasResourceId(clip)) continue;\n\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.state === 'ready' ||\n resource.state === 'loading' ||\n resource.state === 'error'\n ) {\n continue;\n }\n\n this.load(resource.id, { isPreload: true });\n }\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 isPreload flag for background preloading\n this.load(resourceId, { isPreload: true }).finally(() => {\n // Continue processing queue\n this.processPreloadQueue();\n });\n }\n }\n\n private enqueueLoad(\n resource: Resource,\n isPreload: boolean = false,\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 // Upgrade preload task to active task if needed\n if (existingTask.isPreload && !isPreload) {\n existingTask.isPreload = false;\n }\n return existingTask;\n }\n\n // Create new task and enqueue\n const task = this.taskManager.enqueue(resource, isPreload, 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 * Load video resource (download + cache or read from cache)\n */\n private async loadVideoResource(task: LoadTask): Promise<void> {\n const cached = await this.cacheManager.hasResourceInCache(task.resourceId);\n\n if (cached) {\n // Resource already in OPFS - ensure index is parsed\n await this.ensureIndexParsed(task.resourceId);\n\n // Note: Export now uses IndexedVideoSource directly from OPFS\n // No need to transfer stream to worker anymore\n } else {\n // Not cached - download and cache to OPFS\n await this.loadWithOPFSCache(task);\n }\n }\n\n /**\n * Load audio resource (download + parse or reuse cache)\n */\n private async loadAudioResource(task: LoadTask): Promise<void> {\n if (!this.cacheManager.audioSampleCache.has(task.resourceId)) {\n // Not cached - download and parse\n await this.loadAndParseAudioFile(task);\n }\n // If already cached, do nothing (reuse existing cache)\n }\n\n /**\n * Load image resource (download + cache or read from cache)\n * Returns ImageBitmap for caller to use (no worker transfer)\n */\n private async loadImageResource(task: LoadTask): Promise<ImageBitmap | null> {\n // Check cache first\n let blob = this.blobCache.get(task.resourceId);\n\n if (!blob) {\n // Not cached: download and cache\n if (task.controller) {\n this.updateResourceState(task.resourceId, 'loading');\n blob = await this.fetchBlob(task.resource.uri, task.controller.signal);\n this.updateResourceState(task.resourceId, 'ready');\n this.blobCache.set(task.resourceId, blob);\n } else {\n return null;\n }\n }\n\n // Create ImageBitmap and return (caller decides what to do with it)\n return await createImageBitmapFromBlob(blob);\n }\n\n /**\n * Get cached ImageBitmap (for already loaded resources)\n * Used by VideoClipSession to batch transfer attachments\n */\n async getImageBitmap(resourceId: string): Promise<ImageBitmap | null> {\n const blob = this.blobCache.get(resourceId);\n if (!blob) return null;\n return await createImageBitmapFromBlob(blob);\n }\n\n /**\n * Load text resource (json/text)\n */\n private async loadTextResource(task: LoadTask): Promise<void> {\n if (task.controller) {\n await this.fetchBlob(task.resource.uri, task.controller.signal);\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 specific handlers based on resource type\n switch (task.resource.type) {\n case 'image': {\n const image = await this.loadImageResource(task);\n if (image) {\n image.close(); // Close if not transferred\n }\n break;\n }\n\n case 'video':\n await this.loadVideoResource(task);\n break;\n\n case 'audio':\n await this.loadAudioResource(task);\n break;\n\n case 'json':\n case 'text':\n await this.loadTextResource(task);\n break;\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 // Wait for OPFS write to complete\n while (this.writingResources.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\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 // Validate file exists and has content\n const cached = await this.cacheManager.hasResourceInCache(resourceId);\n if (!cached) {\n throw new ResourceCorruptedError(resourceId, 'File not found in OPFS or is empty');\n }\n\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 isPreload: false,\n };\n await this.parseIndexFromStream(parseTask, stream);\n } catch (error) {\n // Handle empty stream error by clearing corrupted cache\n if (error instanceof EmptyStreamError || error instanceof ResourceCorruptedError) {\n console.warn(`[ResourceLoader] Corrupted cache detected for ${resourceId}, clearing...`);\n try {\n await this.cacheManager.resourceCache.deleteResource(resourceId);\n this.cacheManager.mp4IndexCache.delete(resourceId);\n } catch (deleteError) {\n console.error(`[ResourceLoader] Failed to clear corrupted cache:`, deleteError);\n }\n }\n throw error;\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 // Export mode: only 2-way split (OPFS + parsing)\n // IndexedVideoSource will read directly from OPFS later\n const [opfsStream, parseStream] = stream.tee();\n\n // Parallel execution: write to OPFS and parse index\n await Promise.all([\n this.writeToOPFS(task.resourceId, opfsStream, task),\n this.parseIndexFromStream(task, parseStream),\n ]);\n }\n\n /**\n * Write resource stream to OPFS with retry on quota exceeded\n * If quota is exceeded and old projects are evicted, fetches a fresh stream and retries\n */\n private async writeToOPFS(\n resourceId: string,\n stream: ReadableStream<Uint8Array>,\n task?: LoadTask\n ): Promise<void> {\n this.writingResources.add(resourceId);\n try {\n await this.cacheManager.resourceCache.writeResource(resourceId, stream);\n } catch (error) {\n if (error instanceof OPFSQuotaExceededError && error.retryable && task) {\n console.log(\n `[ResourceLoader] OPFS quota exceeded for ${resourceId}, retrying with fresh stream...`\n );\n\n // Create fresh stream for retry\n const retryStream = await this.streamFactory.createRegularStream(task);\n if (!retryStream) {\n throw new Error(`Failed to create retry stream for ${resourceId}`);\n }\n\n // Retry write with fresh stream\n await this.cacheManager.resourceCache.writeResource(resourceId, retryStream);\n } else {\n throw error;\n }\n } finally {\n this.writingResources.delete(resourceId);\n }\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 // Only enable first GOP extraction for clips that start at time 0 (cover clips)\n const shouldExtractFirstGOP = clipId ? this.shouldExtractCover(clipId) : false;\n\n const result: MP4ParseResult = await parser.parseFromStream(stream, {\n resourceId: resourceId,\n onFirstFrameReady: shouldExtractFirstGOP\n ? 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 this.eventBus?.emit(MeframeEvent.ResourceFirstFrameReady, {\n resourceId,\n clipId: clipId!,\n index,\n chunks,\n });\n }\n : undefined,\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 }\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 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 isPreload: false,\n controller: new AbortController(),\n };\n\n // Load image (without sessionId, so it won't transfer to worker)\n const imageBitmap = await this.loadImageResource(task);\n if (!imageBitmap) {\n throw new Error(`Failed to load image ${resource.id}`);\n }\n\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 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 load(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 isPreload = options?.isPreload ?? false;\n\n // If this is an active load (not preload), pause all preload tasks\n if (!isPreload) {\n this.taskManager.pausePreloadTasks();\n }\n\n // First check: if resource is already ready, nothing to do\n if (resource.state === 'ready') {\n return;\n }\n\n // Second check: if resource is being loaded\n if (resource.state === 'loading') {\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload task to active task\n if (existingTask.isPreload && !isPreload) {\n existingTask.isPreload = false;\n }\n\n // If sessionId matches or no sessionId required, reuse existing task\n if (!options?.sessionId || existingTask.sessionId === options.sessionId) {\n return existingTask.promise;\n }\n }\n }\n\n // Third path: check if already has active task\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload task to active task\n if (existingTask.isPreload && !isPreload) {\n existingTask.isPreload = false;\n }\n\n // Reuse existing task\n return existingTask.promise;\n }\n\n // Create new task\n const task = this.enqueueLoad(\n resource,\n isPreload,\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?.isPreload ?? false,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n } else {\n await this.load(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 /**\n * Check if a clip needs cover extraction (first GOP decode)\n * Only clips starting at time 0 need fast cover rendering\n */\n private shouldExtractCover(clipId: string): boolean {\n if (!this.model) return false;\n\n const clip = this.model.findClip(clipId);\n return clip?.startUs === 0;\n }\n}\n"],"names":["existingTask"],"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 {\n EmptyStreamError,\n ResourceCorruptedError,\n OPFSQuotaExceededError,\n} from '../../utils/errors';\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 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 private writingResources = new Set<string>(); // Track in-progress OPFS writes\n\n // Preloading state\n private isPreloadingEnabled = true;\n\n constructor(options: ResourceLoaderOptions) {\n const config = options.config || {};\n const maxConcurrent = config.maxConcurrent ?? 3;\n const preloadConcurrency = config.preloadConcurrency ?? 2;\n\n this.taskManager = new TaskManager(maxConcurrent, preloadConcurrency);\n this.streamFactory = new StreamFactory(options.onProgress, config);\n this.eventBus = options.eventBus;\n this.onStateChange = options.onStateChange;\n this.cacheManager = options.cacheManager;\n }\n\n async setModel(model: CompositionModel): Promise<void> {\n // Resource IDs are globally stable across models (same id implies same uri/content),\n // so in-flight load tasks remain valid across model switching.\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.load(mainTrack.clips[0].resourceId, {\n isPreload: false,\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 // Collect all video/audio tracks for horizontal loading\n const tracks = this.model.tracks.filter(\n (track) => track.kind === 'video' || track.kind === 'audio'\n );\n if (tracks.length === 0) return;\n\n // Find maximum clip count across all tracks\n const maxClipCount = Math.max(...tracks.map((track) => track.clips.length));\n\n // Horizontal loading: load clip[0] from all tracks, then clip[1], etc.\n for (let clipIndex = 0; clipIndex < maxClipCount; clipIndex++) {\n for (const track of tracks) {\n const clip = track.clips[clipIndex];\n if (!clip || !hasResourceId(clip)) continue;\n\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.state === 'ready' ||\n resource.state === 'loading' ||\n resource.state === 'error'\n ) {\n continue;\n }\n\n this.load(resource.id, { isPreload: true });\n }\n }\n }\n\n private enqueueLoad(\n resource: Resource,\n isPreload: boolean = false,\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 // Upgrade preload task to foreground if needed (go through TaskManager to keep counters correct)\n this.taskManager.enqueue(resource, isPreload, sessionId, clipId, trackId);\n return existingTask;\n }\n\n // Create new task and enqueue\n const task = this.taskManager.enqueue(resource, isPreload, 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 * Load video resource (download + cache or read from cache)\n */\n private async loadVideoResource(task: LoadTask): Promise<void> {\n const cached = await this.cacheManager.hasResourceInCache(task.resourceId);\n\n if (cached) {\n // Resource already in OPFS - ensure index is parsed\n await this.ensureIndexParsed(task.resourceId);\n\n // Note: Export now uses IndexedVideoSource directly from OPFS\n // No need to transfer stream to worker anymore\n } else {\n // Not cached - download and cache to OPFS\n await this.loadWithOPFSCache(task);\n }\n }\n\n /**\n * Load audio resource (download + parse or reuse cache)\n */\n private async loadAudioResource(task: LoadTask): Promise<void> {\n if (!this.cacheManager.audioSampleCache.has(task.resourceId)) {\n // Not cached - download and parse\n await this.loadAndParseAudioFile(task);\n }\n // If already cached, do nothing (reuse existing cache)\n }\n\n /**\n * Ensure image blob is cached (download + cache or reuse cache).\n *\n * Important: preload should NOT decode ImageBitmap.\n * createImageBitmap can be slow / flaky under heavy main-thread load (playback + export),\n * and decoding isn't required for caching or for later loadImage()/getImageBitmap() calls.\n */\n private async ensureImageBlob(task: LoadTask): Promise<void> {\n // Check cache first\n let blob = this.blobCache.get(task.resourceId);\n\n if (!blob) {\n // Not cached: download and cache\n if (task.controller) {\n blob = await this.fetchBlob(task.resource.uri, task.controller.signal);\n this.blobCache.set(task.resourceId, blob);\n } else {\n return;\n }\n }\n }\n\n /**\n * Get cached ImageBitmap (for already loaded resources)\n * Used by VideoClipSession to batch transfer attachments\n */\n async getImageBitmap(resourceId: string): Promise<ImageBitmap | null> {\n const blob = this.blobCache.get(resourceId);\n if (!blob) return null;\n return await createImageBitmapFromBlob(blob);\n }\n\n /**\n * Load text resource (json/text)\n */\n private async loadTextResource(task: LoadTask): Promise<void> {\n if (task.controller) {\n await this.fetchBlob(task.resource.uri, task.controller.signal);\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 specific handlers based on resource type\n switch (task.resource.type) {\n case 'image': {\n // Preload path: only cache blob, avoid createImageBitmap during export/playback concurrency.\n await this.ensureImageBlob(task);\n break;\n }\n\n case 'video':\n await this.loadVideoResource(task);\n break;\n\n case 'audio':\n await this.loadAudioResource(task);\n break;\n\n case 'json':\n case 'text':\n await this.loadTextResource(task);\n break;\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 // Wait for OPFS write to complete\n while (this.writingResources.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\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 // Validate file exists and has content\n const cached = await this.cacheManager.hasResourceInCache(resourceId);\n if (!cached) {\n throw new ResourceCorruptedError(resourceId, 'File not found in OPFS or is empty');\n }\n\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 isPreload: false,\n };\n await this.parseIndexFromStream(parseTask, stream);\n } catch (error) {\n // Handle empty stream error by clearing corrupted cache\n if (error instanceof EmptyStreamError || error instanceof ResourceCorruptedError) {\n console.warn(`[ResourceLoader] Corrupted cache detected for ${resourceId}, clearing...`);\n try {\n await this.cacheManager.resourceCache.deleteResource(resourceId);\n this.cacheManager.mp4IndexCache.delete(resourceId);\n } catch (deleteError) {\n console.error(`[ResourceLoader] Failed to clear corrupted cache:`, deleteError);\n }\n }\n throw error;\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 // Export mode: only 2-way split (OPFS + parsing)\n // IndexedVideoSource will read directly from OPFS later\n const [opfsStream, parseStream] = stream.tee();\n\n // Parallel execution: write to OPFS and parse index\n await Promise.all([\n this.writeToOPFS(task.resourceId, opfsStream, task),\n this.parseIndexFromStream(task, parseStream),\n ]);\n }\n\n /**\n * Write resource stream to OPFS with retry on quota exceeded\n * If quota is exceeded and old projects are evicted, fetches a fresh stream and retries\n */\n private async writeToOPFS(\n resourceId: string,\n stream: ReadableStream<Uint8Array>,\n task?: LoadTask\n ): Promise<void> {\n this.writingResources.add(resourceId);\n try {\n await this.cacheManager.resourceCache.writeResource(resourceId, stream);\n } catch (error) {\n if (error instanceof OPFSQuotaExceededError && error.retryable && task) {\n console.log(\n `[ResourceLoader] OPFS quota exceeded for ${resourceId}, retrying with fresh stream...`\n );\n\n // Create fresh stream for retry\n const retryStream = await this.streamFactory.createRegularStream(task);\n if (!retryStream) {\n throw new Error(`Failed to create retry stream for ${resourceId}`);\n }\n\n // Retry write with fresh stream\n await this.cacheManager.resourceCache.writeResource(resourceId, retryStream);\n } else {\n throw error;\n }\n } finally {\n this.writingResources.delete(resourceId);\n }\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 // Only enable first GOP extraction for clips that start at time 0 (cover clips)\n const shouldExtractFirstGOP = clipId ? this.shouldExtractCover(clipId) : false;\n\n const result: MP4ParseResult = await parser.parseFromStream(stream, {\n resourceId: resourceId,\n onFirstFrameReady: shouldExtractFirstGOP\n ? 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 this.eventBus?.emit(MeframeEvent.ResourceFirstFrameReady, {\n resourceId,\n clipId: clipId!,\n index,\n chunks,\n });\n }\n : undefined,\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 }\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 async loadImage(resource: Resource): Promise<ImageBitmap> {\n // If we already have the blob in memory, decoding to ImageBitmap is NOT resource loading.\n // Do not flip resource.state to 'loading' during playback; it will cause buffering oscillation\n // because PlaybackController gates on resource.state === 'ready'.\n const existingBlob = this.blobCache.get(resource.id);\n if (existingBlob) {\n const imageBitmap = await createImageBitmapFromBlob(existingBlob);\n return imageBitmap;\n }\n\n const task: LoadTask = {\n resourceId: resource.id,\n resource: resource,\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n isPreload: false,\n controller: new AbortController(),\n };\n\n // loadImage() bypasses startLoad(), so it must manage resource state by itself.\n let loadError: Error | undefined;\n try {\n this.updateResourceState(resource.id, 'loading');\n // Ensure blob exists, then decode to ImageBitmap.\n await this.ensureImageBlob(task);\n const blob = this.blobCache.get(resource.id);\n if (!blob) throw new Error(`Failed to load image ${resource.id}`);\n const imageBitmap = await createImageBitmapFromBlob(blob);\n this.updateResourceState(resource.id, 'ready');\n return imageBitmap;\n } catch (error) {\n loadError = error as Error;\n this.updateResourceState(resource.id, 'error');\n throw loadError;\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 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 load(resourceId?: string, options?: ResourceLoadOptions): Promise<void> {\n if (!resourceId) {\n return;\n }\n\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n const m = this.model;\n console.warn('[ResourceLoader] Resource not found in model:', {\n resourceId,\n options: {\n isPreload: options?.isPreload ?? false,\n sessionId: options?.sessionId,\n clipId: options?.clipId,\n trackId: options?.trackId,\n isMainTrack: options?.isMainTrack,\n },\n model: m\n ? {\n fps: m.fps,\n durationUs: m.durationUs,\n mainTrackId: m.mainTrackId,\n trackCount: m.tracks.length,\n resourcesSize: m.resources.size,\n }\n : null,\n });\n return;\n }\n\n const isPreload = options?.isPreload ?? false;\n\n // First check: if resource is already ready, nothing to do\n if (resource.state === 'ready') {\n return;\n }\n\n // Second check: if resource is being loaded\n if (resource.state === 'loading') {\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload -> foreground through TaskManager to keep counters correct\n // (do NOT mutate existingTask.isPreload here).\n this.taskManager.enqueue(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // If sessionId matches or no sessionId required, reuse existing task\n if (!options?.sessionId || existingTask.sessionId === options.sessionId) {\n return existingTask.promise;\n }\n }\n }\n\n // Third path: check if already has active task\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload -> foreground through TaskManager to keep counters correct\n // (do NOT mutate existingTask.isPreload here).\n this.taskManager.enqueue(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // Reuse existing task\n return existingTask.promise;\n }\n\n // Create new task\n const task = this.enqueueLoad(\n resource,\n isPreload,\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?.isPreload ?? false,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n } else {\n await this.load(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 /**\n * Check if a clip needs cover extraction (first GOP decode)\n * Only clips starting at time 0 need fast cover rendering\n */\n private shouldExtractCover(clipId: string): boolean {\n if (!this.model) return false;\n\n const clip = this.model.findClip(clipId);\n return clip?.startUs === 0;\n }\n}\n"],"names":["existingTask"],"mappings":"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"}
@@ -7,10 +7,12 @@ import { Resource } from '../../model';
7
7
  export declare class TaskManager {
8
8
  activeTasks: Map<string, LoadTask>;
9
9
  taskQueue: LoadTask[];
10
- private pausedPreloadTasks;
10
+ private queuedTasks;
11
11
  private concurrentCount;
12
+ private concurrentPreloadCount;
12
13
  private maxConcurrent;
13
- constructor(maxConcurrent?: number);
14
+ private maxPreloadConcurrent;
15
+ constructor(maxConcurrent?: number, preloadConcurrency?: number);
14
16
  /**
15
17
  * Check if a resource is already being loaded
16
18
  */
@@ -51,16 +53,7 @@ export declare class TaskManager {
51
53
  * Check if can process more tasks
52
54
  */
53
55
  get canProcess(): boolean;
54
- /**
55
- * Pause all preload tasks (when active load starts)
56
- * Moves preload tasks to paused queue instead of discarding them
57
- */
58
- pausePreloadTasks(): void;
59
- /**
60
- * Resume preload tasks when no active normal tasks
61
- * Called automatically after task completion
62
- */
63
- resumePreloadTasks(): void;
56
+ private mergeTaskMeta;
64
57
  /**
65
58
  * Clear all tasks
66
59
  */
@@ -1 +1 @@
1
- {"version":3,"file":"TaskManager.d.ts","sourceRoot":"","sources":["../../../src/stages/load/TaskManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;GAEG;AACH,qBAAa,WAAW;IACtB,WAAW,wBAA+B;IAC1C,SAAS,EAAE,QAAQ,EAAE,CAAM;IAC3B,OAAO,CAAC,kBAAkB,CAAkB;IAC5C,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,aAAa,CAAS;gBAElB,aAAa,GAAE,MAAU;IAIrC;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C;;OAEG;IACH,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO;IAYxE;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS;IAM7D;;OAEG;IACH,OAAO,CACL,QAAQ,EAAE,QAAQ,EAClB,SAAS,GAAE,OAAe,EAC1B,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,QAAQ;IA8BX;;OAEG;IACH,WAAW,IAAI,QAAQ,GAAG,IAAI;IAQ9B;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAKlC;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI;IAkBrD;;OAEG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAkBvC;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIvD;;OAEG;IACH,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;;OAGG;IACH,iBAAiB,IAAI,IAAI;IAmBzB;;;OAGG;IACH,kBAAkB,IAAI,IAAI;IA2B1B;;OAEG;IACH,KAAK,IAAI,IAAI;CAWd"}
1
+ {"version":3,"file":"TaskManager.d.ts","sourceRoot":"","sources":["../../../src/stages/load/TaskManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;GAEG;AACH,qBAAa,WAAW;IACtB,WAAW,wBAA+B;IAC1C,SAAS,EAAE,QAAQ,EAAE,CAAM;IAC3B,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,oBAAoB,CAAS;gBAEzB,aAAa,GAAE,MAAU,EAAE,kBAAkB,GAAE,MAAU;IAKrE;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C;;OAEG;IACH,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO;IAYxE;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS;IAM7D;;OAEG;IACH,OAAO,CACL,QAAQ,EAAE,QAAQ,EAClB,SAAS,GAAE,OAAe,EAC1B,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,GACf,QAAQ;IAuDX;;OAEG;IACH,WAAW,IAAI,QAAQ,GAAG,IAAI;IAyB9B;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAQlC;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI;IAkBrD;;OAEG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAqBvC;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIvD;;OAEG;IACH,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,OAAO,CAAC,aAAa;IAoBrB;;OAEG;IACH,KAAK,IAAI,IAAI;CAYd"}