@meframe/core 0.0.6 → 0.0.7

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 (41) hide show
  1. package/dist/config/defaults.d.ts.map +1 -1
  2. package/dist/config/defaults.js +1 -0
  3. package/dist/config/defaults.js.map +1 -1
  4. package/dist/config/types.d.ts +1 -0
  5. package/dist/config/types.d.ts.map +1 -1
  6. package/dist/orchestrator/Orchestrator.d.ts +0 -1
  7. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  8. package/dist/orchestrator/Orchestrator.js +24 -37
  9. package/dist/orchestrator/Orchestrator.js.map +1 -1
  10. package/dist/orchestrator/VideoClipSession.d.ts +1 -0
  11. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  12. package/dist/orchestrator/VideoClipSession.js +96 -82
  13. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  14. package/dist/stages/compose/GlobalAudioSession.d.ts.map +1 -1
  15. package/dist/stages/compose/GlobalAudioSession.js +11 -3
  16. package/dist/stages/compose/GlobalAudioSession.js.map +1 -1
  17. package/dist/stages/load/ResourceLoader.js +1 -1
  18. package/dist/stages/load/ResourceLoader.js.map +1 -1
  19. package/dist/worker/WorkerPool.d.ts.map +1 -1
  20. package/dist/worker/WorkerPool.js +6 -2
  21. package/dist/worker/WorkerPool.js.map +1 -1
  22. package/dist/worker/types.d.ts +1 -1
  23. package/dist/worker/types.d.ts.map +1 -1
  24. package/dist/worker/types.js.map +1 -1
  25. package/dist/worker/worker-event-whitelist.d.ts.map +1 -1
  26. package/dist/workers/BaseDecoder.js +130 -0
  27. package/dist/workers/BaseDecoder.js.map +1 -0
  28. package/dist/workers/WorkerChannel.js.map +1 -1
  29. package/dist/workers/stages/compose/video-compose.worker.js +13 -9
  30. package/dist/workers/stages/compose/video-compose.worker.js.map +1 -1
  31. package/dist/workers/stages/decode/audio-decode.worker.js +243 -0
  32. package/dist/workers/stages/decode/audio-decode.worker.js.map +1 -0
  33. package/dist/workers/stages/decode/video-decode.worker.js +346 -0
  34. package/dist/workers/stages/decode/video-decode.worker.js.map +1 -0
  35. package/dist/workers/stages/encode/video-encode.worker.js +340 -0
  36. package/dist/workers/stages/encode/video-encode.worker.js.map +1 -0
  37. package/package.json +1 -1
  38. package/dist/workers/stages/decode/decode.worker.js +0 -826
  39. package/dist/workers/stages/decode/decode.worker.js.map +0 -1
  40. package/dist/workers/stages/encode/encode.worker.js +0 -547
  41. package/dist/workers/stages/encode/encode.worker.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';\n\ninterface VideoClipSessionCallbacks {\n onComposedStreamReady(stream: ReadableStream<VideoFrame>, fps: number): void;\n onEncodedStreamReady(\n stream: ReadableStream<{ chunk: EncodedVideoChunk; metadata: EncodedVideoChunkMetadata }>,\n track: 'video' | 'audio'\n ): void;\n onStreamDisposed?(): void;\n onPipelineReady?(): Promise<void>;\n}\n\ninterface VideoClipSessionConfig {\n clipId: string;\n sessionId?: string;\n forL2Only?: boolean;\n planner: CompositionPlanner;\n workerPool: WorkerPool;\n cacheManager: CacheManager;\n compositionModel: CompositionModel;\n workerConfigs: Record<WorkerType, any>;\n callbacks: VideoClipSessionCallbacks;\n}\n\ninterface InstructionContext {\n revision: number;\n instructions: ClipInstructionSet;\n status: ClipInstructionSet['status'];\n}\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\n private instructionContext: InstructionContext | null = null;\n private visualWorker: BaseWorker | null = null;\n private decodeWorker: BaseWorker | null = null;\n private encodeWorker: BaseWorker | null = null;\n private videoDemuxWorker: BaseWorker | null = null;\n private visualStream: ReadableStream<VideoFrame> | null = null;\n private visualToEncodeChannel: MessageChannel | null = null;\n private decodeToVisualChannel: MessageChannel | null = null;\n private demuxToDecodeChannel: 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 }\n\n async activate(): Promise<void> {\n if (this.isActive || this.isDisposed) return;\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\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 await this.ensureInstructions(clip);\n\n this.isActive = true;\n\n // Notify that pipeline is ready - triggers resource loading\n if (this.callbacks.onPipelineReady) {\n await this.callbacks.onPipelineReady();\n }\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.restartPipeline(clip, resource, instructions);\n } else {\n await this.activate();\n }\n }\n\n get visualWorkerHandle(): BaseWorker | null {\n return this.visualWorker;\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) return null;\n return this.compositionModel.resources.get(clip.resourceId) || null;\n }\n\n private async ensureInstructions(_clip: Clip): Promise<void> {\n const instructions = this.planner.getInstructions(this.clipId);\n if (!instructions) {\n throw new Error(`No instructions for clip ${this.clipId}`);\n }\n await this.installInstructions(instructions);\n }\n\n private async setupImagePipeline(clip: Clip): Promise<void> {\n await this.acquireWorkers(clip, false);\n await this.connectImagePipeline();\n }\n\n private async connectImagePipeline(): Promise<void> {\n if (!this.visualWorker || !this.encodeWorker) {\n throw new Error('Pipeline workers not ready');\n }\n\n // Use sessionId for worker communication\n const sessionId = this.sessionId;\n\n this.visualToEncodeChannel = new MessageChannel();\n await this.visualWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.visualToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port1] }\n );\n await this.encodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.visualToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port2] }\n );\n\n this.visualWorker.receiveStream((stream) => {\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;\n this.callbacks.onComposedStreamReady(stream, fps);\n });\n\n // Receive encoded chunks from EncodeWorker and forward to callback\n // Note: encodeWorker is shared across sessions\n // Each session registers its own callback with sessionId filtering\n this.encodeWorker.receiveStream((stream, metadata) => {\n // Filter: only process streams for this session\n const streamSessionId = (metadata as any)?.sessionId;\n\n if (streamSessionId !== this.sessionId) {\n return;\n }\n\n if (metadata?.streamType === 'video') {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n 'video'\n );\n } else if (metadata?.streamType === 'audio') {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n 'audio'\n );\n }\n });\n }\n\n private async setupVideoPipeline(clip: Clip, resource: { id: string }): Promise<void> {\n await this.acquireWorkers(clip, true);\n await this.connectVideoPipeline(resource.id);\n }\n\n private async acquireWorkers(clip: Clip, isVideo: boolean): Promise<void> {\n if (isVideo) {\n this.decodeWorker = await this.workerPool.get('decode');\n this.videoDemuxWorker = await this.workerPool.get('videoDemux', this.sessionId, {\n lazy: true,\n });\n }\n\n this.encodeWorker = await this.workerPool.get('encode');\n this.visualWorker = await this.workerPool.get('videoCompose', this.sessionId, { 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 compositionFps: this.compositionModel.fps ?? visualConfig.fps ?? 30,\n };\n await this.visualWorker.send('configure', {\n initial: true,\n clipId: clip.id,\n config: { ...visualConfig, ...renderOverrides, timeline },\n });\n }\n\n private async connectVideoPipeline(resourceId: string): Promise<void> {\n if (!this.visualWorker || !this.decodeWorker || !this.encodeWorker || !this.videoDemuxWorker) {\n throw new Error('Pipeline workers not ready');\n }\n\n // Use sessionId for worker communication\n const sessionId = this.sessionId;\n\n this.visualToEncodeChannel = new MessageChannel();\n await this.visualWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.visualToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port1] }\n );\n await this.encodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.visualToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port2] }\n );\n\n this.decodeToVisualChannel = new MessageChannel();\n await this.decodeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.decodeToVisualChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.decodeToVisualChannel.port1] }\n );\n await this.visualWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.decodeToVisualChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.decodeToVisualChannel.port2] }\n );\n\n this.demuxToDecodeChannel = new MessageChannel();\n await this.videoDemuxWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.demuxToDecodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.demuxToDecodeChannel.port1] }\n );\n await this.decodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.demuxToDecodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.demuxToDecodeChannel.port2] }\n );\n\n const demuxConfig = this.workerConfigs.videoDemux ?? {};\n await this.videoDemuxWorker.send('configure', {\n initial: true,\n resourceId,\n sessionId,\n config: demuxConfig,\n });\n\n this.visualWorker.receiveStream((stream) => {\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;\n this.callbacks.onComposedStreamReady(stream, fps);\n });\n\n // Receive encoded chunks from EncodeWorker and forward to callback\n // Note: encodeWorker is shared across sessions\n // Each session registers its own callback with sessionId filtering\n this.encodeWorker.receiveStream((stream, metadata) => {\n // Filter: only process streams for this session\n const streamSessionId = (metadata as any)?.sessionId;\n\n if (streamSessionId !== this.sessionId) {\n return;\n }\n\n if (metadata?.streamType === 'video') {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n 'video'\n );\n } else if (metadata?.streamType === 'audio') {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n 'audio'\n );\n }\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 if (this.visualWorker) {\n await this.visualWorker.send('install_instructions', instructions);\n }\n }\n\n private async restartPipeline(\n clip: Clip,\n resource: { id: string; type: string },\n instructions: ClipInstructionSet\n ): Promise<void> {\n await this.releasePipeline();\n\n if (resource.type === 'video') {\n await this.setupVideoPipeline(clip, resource);\n } else if (resource.type === 'image') {\n await this.setupImagePipeline(clip);\n }\n\n await this.installInstructions(instructions);\n }\n\n private async releasePipeline(): Promise<void> {\n if (this.visualWorker && this.instructionContext) {\n await this.visualWorker.notify('dispose_clip', {\n sessionId: this.sessionId,\n revision: this.instructionContext.revision,\n });\n }\n\n this.visualToEncodeChannel?.port1.close();\n this.visualToEncodeChannel?.port2.close();\n this.decodeToVisualChannel?.port1.close();\n this.decodeToVisualChannel?.port2.close();\n this.demuxToDecodeChannel?.port1.close();\n this.demuxToDecodeChannel?.port2.close();\n\n this.visualToEncodeChannel = null;\n this.decodeToVisualChannel = null;\n this.demuxToDecodeChannel = null;\n\n if (this.visualStream && this.callbacks.onStreamDisposed) {\n this.callbacks.onStreamDisposed();\n }\n this.visualStream = null;\n\n if (this.videoDemuxWorker) {\n this.workerPool.terminate('videoDemux', this.sessionId);\n this.videoDemuxWorker = null;\n }\n\n if (this.visualWorker) {\n this.workerPool.terminate('videoCompose', this.sessionId);\n this.visualWorker = null;\n }\n\n // Shared workers are not terminated here (decode / encode)\n this.decodeWorker = null;\n this.encodeWorker = null;\n }\n\n async invalidateClipCache(): Promise<void> {\n await this.cacheManager.invalidateClip(this.clipId);\n }\n}\n"],"names":[],"mappings":"AAoCO,MAAM,iBAAiB;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,qBAAgD;AAAA,EAChD,eAAkC;AAAA,EAClC,eAAkC;AAAA,EAClC,eAAkC;AAAA,EAClC,mBAAsC;AAAA,EACtC,eAAkD;AAAA,EAClD,wBAA+C;AAAA,EAC/C,wBAA+C;AAAA,EAC/C,uBAA8C;AAAA,EAC9C,WAAW;AAAA,EACX,aAAa;AAAA,EAErB,aAAa,OAAO,QAA2D;AAC7E,WAAO,IAAI,iBAAiB,MAAM;AAAA,EACpC;AAAA,EAEQ,YAAY,QAAgC;AAClD,SAAK,SAAS,OAAO;AACrB,SAAK,YAAY,OAAO,aAAa,OAAO;AAC5C,SAAK,UAAU,OAAO;AACtB,SAAK,aAAa,OAAO;AACzB,SAAK,eAAe,OAAO;AAC3B,SAAK,mBAAmB,OAAO;AAC/B,SAAK,gBAAgB,OAAO;AAC5B,SAAK,YAAY,OAAO;AAAA,EAC1B;AAAA,EAEA,MAAM,WAA0B;AAC9B,QAAI,KAAK,YAAY,KAAK,WAAY;AAEtC,UAAM,OAAO,KAAK,QAAA;AAClB,UAAM,WAAW,KAAK,YAAA;AACtB,QAAI,CAAC,QAAQ,CAAC,UAAU;AACtB,cAAQ,KAAK,2DAA2D,KAAK,SAAS;AACtF;AAAA,IACF;AAEA,QAAI,SAAS,SAAS,SAAS;AAC7B,YAAM,KAAK,mBAAmB,MAAM,QAAQ;AAAA,IAC9C,WAAW,SAAS,SAAS,SAAS;AACpC,YAAM,KAAK,mBAAmB,IAAI;AAAA,IACpC,OAAO;AACL,cAAQ;AAAA,QACN;AAAA,QACA,SAAS;AAAA,QACT;AAAA,QACA,KAAK;AAAA,MAAA;AAAA,IAET;AAEA,UAAM,KAAK,mBAAmB,IAAI;AAElC,SAAK,WAAW;AAGhB,QAAI,KAAK,UAAU,iBAAiB;AAClC,YAAM,KAAK,UAAU,gBAAA;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAM,aAA4B;AAChC,QAAI,CAAC,KAAK,YAAY,KAAK,WAAY;AACvC,SAAK,WAAW;AAEhB,UAAM,KAAK,gBAAA;AAAA,EACb;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,WAAY;AACrB,UAAM,KAAK,WAAA;AACX,SAAK,QAAQ,YAAY,KAAK,MAAM;AACpC,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,MAAM,oBAAoB,QAAyC;AACjE,QAAI,KAAK,YAAY;AACnB;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,UAAU;AAC5B,YAAM,KAAK,QAAA;AACX;AAAA,IACF;AAEA,UAAM,eAAe,OAAO;AAC5B,QAAI,CAAC,cAAc;AACjB;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,QAAA;AAClB,UAAM,WAAW,KAAK,YAAA;AACtB,QAAI,CAAC,QAAQ,CAAC,YAAa,SAAS,SAAS,WAAW,SAAS,SAAS,SAAU;AAClF;AAAA,IACF;AAGA,QAAI,KAAK,UAAU;AACjB,YAAM,KAAK,gBAAgB,MAAM,UAAU,YAAY;AAAA,IACzD,OAAO;AACL,YAAM,KAAK,SAAA;AAAA,IACb;AAAA,EACF;AAAA,EAEA,IAAI,qBAAwC;AAC1C,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,UAAuB;AAC7B,WAAO,KAAK,kBAAkB,WAAW,KAAK,MAAM,KAAK;AAAA,EAC3D;AAAA,EAEQ,cAAc;AACpB,UAAM,OAAO,KAAK,QAAA;AAClB,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,KAAK,iBAAiB,UAAU,IAAI,KAAK,UAAU,KAAK;AAAA,EACjE;AAAA,EAEA,MAAc,mBAAmB,OAA4B;AAC3D,UAAM,eAAe,KAAK,QAAQ,gBAAgB,KAAK,MAAM;AAC7D,QAAI,CAAC,cAAc;AACjB,YAAM,IAAI,MAAM,4BAA4B,KAAK,MAAM,EAAE;AAAA,IAC3D;AACA,UAAM,KAAK,oBAAoB,YAAY;AAAA,EAC7C;AAAA,EAEA,MAAc,mBAAmB,MAA2B;AAC1D,UAAM,KAAK,eAAe,MAAM,KAAK;AACrC,UAAM,KAAK,qBAAA;AAAA,EACb;AAAA,EAEA,MAAc,uBAAsC;AAClD,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,cAAc;AAC5C,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAGA,UAAM,YAAY,KAAK;AAEvB,SAAK,wBAAwB,IAAI,eAAA;AACjC,UAAM,KAAK,aAAa;AAAA,MACtB;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,sBAAsB;AAAA,QACjC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,IAAE;AAEjD,UAAM,KAAK,aAAa;AAAA,MACtB;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,sBAAsB;AAAA,QACjC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,IAAE;AAGjD,SAAK,aAAa,cAAc,CAAC,WAAW;AAC1C,YAAM,eAAe,KAAK,cAAc,gBAAgB,CAAA;AACxD,YAAM,MAAM,KAAK,iBAAiB,OAAO,aAAa,OAAO;AAC7D,WAAK,UAAU,sBAAsB,QAAQ,GAAG;AAAA,IAClD,CAAC;AAKD,SAAK,aAAa,cAAc,CAAC,QAAQ,aAAa;AAEpD,YAAM,kBAAmB,UAAkB;AAE3C,UAAI,oBAAoB,KAAK,WAAW;AACtC;AAAA,MACF;AAEA,UAAI,UAAU,eAAe,SAAS;AACpC,aAAK,UAAU;AAAA,UACb;AAAA,UAIA;AAAA,QAAA;AAAA,MAEJ,WAAW,UAAU,eAAe,SAAS;AAC3C,aAAK,UAAU;AAAA,UACb;AAAA,UAIA;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,mBAAmB,MAAY,UAAyC;AACpF,UAAM,KAAK,eAAe,MAAM,IAAI;AACpC,UAAM,KAAK,qBAAqB,SAAS,EAAE;AAAA,EAC7C;AAAA,EAEA,MAAc,eAAe,MAAY,SAAiC;AACxE,QAAI,SAAS;AACX,WAAK,eAAe,MAAM,KAAK,WAAW,IAAI,QAAQ;AACtD,WAAK,mBAAmB,MAAM,KAAK,WAAW,IAAI,cAAc,KAAK,WAAW;AAAA,QAC9E,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAEA,SAAK,eAAe,MAAM,KAAK,WAAW,IAAI,QAAQ;AACtD,SAAK,eAAe,MAAM,KAAK,WAAW,IAAI,gBAAgB,KAAK,WAAW,EAAE,MAAM,KAAA,CAAM;AAE5F,UAAM,eAAe,KAAK,cAAc,gBAAgB,CAAA;AACxD,UAAM,kBAAkB,KAAK,iBAAiB,gBAAgB,CAAA;AAC9D,UAAM,WAAW;AAAA,MACf,QAAQ,KAAK;AAAA,MACb,aAAa,KAAK;AAAA,MAClB,WAAW,KAAK,UAAU,KAAK;AAAA,MAC/B,gBAAgB,KAAK,iBAAiB,OAAO,aAAa,OAAO;AAAA,IAAA;AAEnE,UAAM,KAAK,aAAa,KAAK,aAAa;AAAA,MACxC,SAAS;AAAA,MACT,QAAQ,KAAK;AAAA,MACb,QAAQ,EAAE,GAAG,cAAc,GAAG,iBAAiB,SAAA;AAAA,IAAS,CACzD;AAAA,EACH;AAAA,EAEA,MAAc,qBAAqB,YAAmC;AACpE,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,gBAAgB,CAAC,KAAK,gBAAgB,CAAC,KAAK,kBAAkB;AAC5F,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAGA,UAAM,YAAY,KAAK;AAEvB,SAAK,wBAAwB,IAAI,eAAA;AACjC,UAAM,KAAK,aAAa;AAAA,MACtB;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,sBAAsB;AAAA,QACjC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,IAAE;AAEjD,UAAM,KAAK,aAAa;AAAA,MACtB;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,sBAAsB;AAAA,QACjC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,IAAE;AAGjD,SAAK,wBAAwB,IAAI,eAAA;AACjC,UAAM,KAAK,aAAa;AAAA,MACtB;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,sBAAsB;AAAA,QACjC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,IAAE;AAEjD,UAAM,KAAK,aAAa;AAAA,MACtB;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,sBAAsB;AAAA,QACjC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,IAAE;AAGjD,SAAK,uBAAuB,IAAI,eAAA;AAChC,UAAM,KAAK,iBAAiB;AAAA,MAC1B;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,qBAAqB;AAAA,QAChC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,qBAAqB,KAAK,EAAA;AAAA,IAAE;AAEhD,UAAM,KAAK,aAAa;AAAA,MACtB;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,qBAAqB;AAAA,QAChC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,qBAAqB,KAAK,EAAA;AAAA,IAAE;AAGhD,UAAM,cAAc,KAAK,cAAc,cAAc,CAAA;AACrD,UAAM,KAAK,iBAAiB,KAAK,aAAa;AAAA,MAC5C,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IAAA,CACT;AAED,SAAK,aAAa,cAAc,CAAC,WAAW;AAC1C,YAAM,eAAe,KAAK,cAAc,gBAAgB,CAAA;AACxD,YAAM,MAAM,KAAK,iBAAiB,OAAO,aAAa,OAAO;AAC7D,WAAK,UAAU,sBAAsB,QAAQ,GAAG;AAAA,IAClD,CAAC;AAKD,SAAK,aAAa,cAAc,CAAC,QAAQ,aAAa;AAEpD,YAAM,kBAAmB,UAAkB;AAE3C,UAAI,oBAAoB,KAAK,WAAW;AACtC;AAAA,MACF;AAEA,UAAI,UAAU,eAAe,SAAS;AACpC,aAAK,UAAU;AAAA,UACb;AAAA,UAIA;AAAA,QAAA;AAAA,MAEJ,WAAW,UAAU,eAAe,SAAS;AAC3C,aAAK,UAAU;AAAA,UACb;AAAA,UAIA;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,oBAAoB,cAAiD;AACjF,SAAK,qBAAqB;AAAA,MACxB,UAAU,aAAa;AAAA,MACvB;AAAA,MACA,QAAQ,aAAa;AAAA,IAAA;AAGvB,QAAI,KAAK,cAAc;AACrB,YAAM,KAAK,aAAa,KAAK,wBAAwB,YAAY;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,MAAc,gBACZ,MACA,UACA,cACe;AACf,UAAM,KAAK,gBAAA;AAEX,QAAI,SAAS,SAAS,SAAS;AAC7B,YAAM,KAAK,mBAAmB,MAAM,QAAQ;AAAA,IAC9C,WAAW,SAAS,SAAS,SAAS;AACpC,YAAM,KAAK,mBAAmB,IAAI;AAAA,IACpC;AAEA,UAAM,KAAK,oBAAoB,YAAY;AAAA,EAC7C;AAAA,EAEA,MAAc,kBAAiC;AAC7C,QAAI,KAAK,gBAAgB,KAAK,oBAAoB;AAChD,YAAM,KAAK,aAAa,OAAO,gBAAgB;AAAA,QAC7C,WAAW,KAAK;AAAA,QAChB,UAAU,KAAK,mBAAmB;AAAA,MAAA,CACnC;AAAA,IACH;AAEA,SAAK,uBAAuB,MAAM,MAAA;AAClC,SAAK,uBAAuB,MAAM,MAAA;AAClC,SAAK,uBAAuB,MAAM,MAAA;AAClC,SAAK,uBAAuB,MAAM,MAAA;AAClC,SAAK,sBAAsB,MAAM,MAAA;AACjC,SAAK,sBAAsB,MAAM,MAAA;AAEjC,SAAK,wBAAwB;AAC7B,SAAK,wBAAwB;AAC7B,SAAK,uBAAuB;AAE5B,QAAI,KAAK,gBAAgB,KAAK,UAAU,kBAAkB;AACxD,WAAK,UAAU,iBAAA;AAAA,IACjB;AACA,SAAK,eAAe;AAEpB,QAAI,KAAK,kBAAkB;AACzB,WAAK,WAAW,UAAU,cAAc,KAAK,SAAS;AACtD,WAAK,mBAAmB;AAAA,IAC1B;AAEA,QAAI,KAAK,cAAc;AACrB,WAAK,WAAW,UAAU,gBAAgB,KAAK,SAAS;AACxD,WAAK,eAAe;AAAA,IACtB;AAGA,SAAK,eAAe;AACpB,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,MAAM,sBAAqC;AACzC,UAAM,KAAK,aAAa,eAAe,KAAK,MAAM;AAAA,EACpD;AACF;"}
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';\n\ninterface VideoClipSessionCallbacks {\n onComposedStreamReady(stream: ReadableStream<VideoFrame>, fps: number): void;\n onEncodedStreamReady(\n stream: ReadableStream<{ chunk: EncodedVideoChunk; metadata: EncodedVideoChunkMetadata }>,\n track: 'video' | 'audio'\n ): void;\n onStreamDisposed?(): void;\n onPipelineReady?(): Promise<void>;\n}\n\ninterface VideoClipSessionConfig {\n clipId: string;\n sessionId?: string;\n forL2Only?: boolean;\n planner: CompositionPlanner;\n workerPool: WorkerPool;\n cacheManager: CacheManager;\n compositionModel: CompositionModel;\n workerConfigs: Record<WorkerType, any>;\n callbacks: VideoClipSessionCallbacks;\n}\n\ninterface InstructionContext {\n revision: number;\n instructions: ClipInstructionSet;\n status: ClipInstructionSet['status'];\n}\n\nexport class VideoClipSession {\n private readonly clipId: string;\n private readonly sessionId: string;\n private readonly forL2Only: boolean;\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\n private instructionContext: InstructionContext | null = null;\n private visualWorker: BaseWorker | null = null;\n private decodeWorker: BaseWorker | null = null;\n private encodeWorker: BaseWorker | null = null;\n private videoDemuxWorker: BaseWorker | null = null;\n private visualStream: ReadableStream<VideoFrame> | null = null;\n private visualToEncodeChannel: MessageChannel | null = null;\n private decodeToVisualChannel: MessageChannel | null = null;\n private demuxToDecodeChannel: 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.forL2Only = config.forL2Only ?? false;\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 }\n\n async activate(): Promise<void> {\n if (this.isActive || this.isDisposed) return;\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\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 await this.ensureInstructions(clip);\n\n this.isActive = true;\n\n // Notify that pipeline is ready - triggers resource loading\n if (this.callbacks.onPipelineReady) {\n await this.callbacks.onPipelineReady();\n }\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.restartPipeline(clip, resource, instructions);\n } else {\n await this.activate();\n }\n }\n\n get visualWorkerHandle(): BaseWorker | null {\n return this.visualWorker;\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) return null;\n return this.compositionModel.resources.get(clip.resourceId) || null;\n }\n\n private async ensureInstructions(_clip: Clip): Promise<void> {\n const instructions = this.planner.getInstructions(this.clipId);\n if (!instructions) {\n throw new Error(`No instructions for clip ${this.clipId}`);\n }\n await this.installInstructions(instructions);\n }\n\n private async setupImagePipeline(clip: Clip): Promise<void> {\n await this.acquireWorkers(clip, false);\n await this.connectImagePipeline();\n }\n\n private async connectImagePipeline(): Promise<void> {\n if (!this.visualWorker) {\n throw new Error('Pipeline workers not ready');\n }\n\n // Use sessionId for worker communication\n const sessionId = this.sessionId;\n\n // Connect visualWorker to encodeWorker (only if L2 mode)\n if (this.encodeWorker) {\n this.visualToEncodeChannel = new MessageChannel();\n await this.visualWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.visualToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port1] }\n );\n await this.encodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.visualToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port2] }\n );\n }\n\n this.visualWorker.receiveStream((stream) => {\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;\n this.callbacks.onComposedStreamReady(stream, fps);\n });\n\n // Receive encoded chunks from EncodeWorker (only in L2 mode)\n if (this.encodeWorker) {\n this.encodeWorker.receiveStream((stream, metadata) => {\n if (metadata?.streamType === 'video') {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n 'video'\n );\n } else if (metadata?.streamType === 'audio') {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n 'audio'\n );\n }\n });\n }\n }\n\n private async setupVideoPipeline(clip: Clip, resource: { id: string }): Promise<void> {\n await this.acquireWorkers(clip, true);\n await this.connectVideoPipeline(resource.id);\n }\n\n private async acquireWorkers(clip: Clip, isVideo: boolean): Promise<void> {\n if (isVideo) {\n this.decodeWorker = await this.workerPool.get('videoDecode', this.sessionId, {\n lazy: true,\n });\n this.videoDemuxWorker = await this.workerPool.get('videoDemux', this.sessionId, {\n lazy: true,\n });\n }\n\n if (this.forL2Only) {\n this.encodeWorker = await this.workerPool.get('videoEncode', this.sessionId, {\n lazy: true,\n });\n }\n\n this.visualWorker = await this.workerPool.get('videoCompose', this.sessionId, { 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 compositionFps: this.compositionModel.fps ?? visualConfig.fps ?? 30,\n };\n await this.visualWorker.send('configure', {\n initial: true,\n clipId: clip.id,\n config: { ...visualConfig, ...renderOverrides, timeline },\n });\n }\n\n private async connectVideoPipeline(resourceId: string): Promise<void> {\n if (!this.visualWorker || !this.decodeWorker || !this.videoDemuxWorker) {\n throw new Error('Pipeline workers not ready');\n }\n\n // Use sessionId for worker communication\n const sessionId = this.sessionId;\n\n // Connect visualWorker to encodeWorker (only if L2 mode)\n if (this.encodeWorker) {\n this.visualToEncodeChannel = new MessageChannel();\n await this.visualWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.visualToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port1] }\n );\n await this.encodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.visualToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port2] }\n );\n }\n\n this.decodeToVisualChannel = new MessageChannel();\n await this.decodeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.decodeToVisualChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.decodeToVisualChannel.port1] }\n );\n await this.visualWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.decodeToVisualChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.decodeToVisualChannel.port2] }\n );\n\n this.demuxToDecodeChannel = new MessageChannel();\n await this.videoDemuxWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.demuxToDecodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.demuxToDecodeChannel.port1] }\n );\n await this.decodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.demuxToDecodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.demuxToDecodeChannel.port2] }\n );\n\n const demuxConfig = this.workerConfigs.videoDemux ?? {};\n await this.videoDemuxWorker.send('configure', {\n initial: true,\n resourceId,\n sessionId,\n config: demuxConfig,\n });\n\n this.visualWorker.receiveStream((stream) => {\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;\n this.callbacks.onComposedStreamReady(stream, fps);\n });\n\n // Receive encoded chunks from EncodeWorker (only in L2 mode)\n if (this.encodeWorker) {\n this.encodeWorker.receiveStream((stream, metadata) => {\n if (metadata?.streamType === 'video') {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n 'video'\n );\n } else if (metadata?.streamType === 'audio') {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n 'audio'\n );\n }\n });\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 if (this.visualWorker) {\n await this.visualWorker.send('install_instructions', instructions);\n }\n }\n\n private async restartPipeline(\n clip: Clip,\n resource: { id: string; type: string },\n instructions: ClipInstructionSet\n ): Promise<void> {\n await this.releasePipeline();\n\n if (resource.type === 'video') {\n await this.setupVideoPipeline(clip, resource);\n } else if (resource.type === 'image') {\n await this.setupImagePipeline(clip);\n }\n\n await this.installInstructions(instructions);\n }\n\n private async releasePipeline(): Promise<void> {\n if (this.visualWorker && this.instructionContext) {\n await this.visualWorker.notify('dispose_clip', {\n sessionId: this.sessionId,\n revision: this.instructionContext.revision,\n });\n }\n\n this.visualToEncodeChannel?.port1.close();\n this.visualToEncodeChannel?.port2.close();\n this.decodeToVisualChannel?.port1.close();\n this.decodeToVisualChannel?.port2.close();\n this.demuxToDecodeChannel?.port1.close();\n this.demuxToDecodeChannel?.port2.close();\n\n this.visualToEncodeChannel = null;\n this.decodeToVisualChannel = null;\n this.demuxToDecodeChannel = null;\n\n if (this.visualStream && this.callbacks.onStreamDisposed) {\n this.callbacks.onStreamDisposed();\n }\n this.visualStream = null;\n\n // Terminate clip-local workers\n if (this.videoDemuxWorker) {\n this.workerPool.terminate('videoDemux', this.sessionId);\n this.videoDemuxWorker = null;\n }\n\n if (this.decodeWorker) {\n this.workerPool.terminate('videoDecode', this.sessionId);\n this.decodeWorker = null;\n }\n\n if (this.visualWorker) {\n this.workerPool.terminate('videoCompose', this.sessionId);\n this.visualWorker = null;\n }\n\n if (this.encodeWorker) {\n this.workerPool.terminate('videoEncode', this.sessionId);\n this.encodeWorker = null;\n }\n }\n\n async invalidateClipCache(): Promise<void> {\n await this.cacheManager.invalidateClip(this.clipId);\n }\n}\n"],"names":[],"mappings":"AAoCO,MAAM,iBAAiB;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,qBAAgD;AAAA,EAChD,eAAkC;AAAA,EAClC,eAAkC;AAAA,EAClC,eAAkC;AAAA,EAClC,mBAAsC;AAAA,EACtC,eAAkD;AAAA,EAClD,wBAA+C;AAAA,EAC/C,wBAA+C;AAAA,EAC/C,uBAA8C;AAAA,EAC9C,WAAW;AAAA,EACX,aAAa;AAAA,EAErB,aAAa,OAAO,QAA2D;AAC7E,WAAO,IAAI,iBAAiB,MAAM;AAAA,EACpC;AAAA,EAEQ,YAAY,QAAgC;AAClD,SAAK,SAAS,OAAO;AACrB,SAAK,YAAY,OAAO,aAAa,OAAO;AAC5C,SAAK,YAAY,OAAO,aAAa;AACrC,SAAK,UAAU,OAAO;AACtB,SAAK,aAAa,OAAO;AACzB,SAAK,eAAe,OAAO;AAC3B,SAAK,mBAAmB,OAAO;AAC/B,SAAK,gBAAgB,OAAO;AAC5B,SAAK,YAAY,OAAO;AAAA,EAC1B;AAAA,EAEA,MAAM,WAA0B;AAC9B,QAAI,KAAK,YAAY,KAAK,WAAY;AAEtC,UAAM,OAAO,KAAK,QAAA;AAClB,UAAM,WAAW,KAAK,YAAA;AACtB,QAAI,CAAC,QAAQ,CAAC,UAAU;AACtB,cAAQ,KAAK,2DAA2D,KAAK,SAAS;AACtF;AAAA,IACF;AAEA,QAAI,SAAS,SAAS,SAAS;AAC7B,YAAM,KAAK,mBAAmB,MAAM,QAAQ;AAAA,IAC9C,WAAW,SAAS,SAAS,SAAS;AACpC,YAAM,KAAK,mBAAmB,IAAI;AAAA,IACpC,OAAO;AACL,cAAQ;AAAA,QACN;AAAA,QACA,SAAS;AAAA,QACT;AAAA,QACA,KAAK;AAAA,MAAA;AAAA,IAET;AAEA,UAAM,KAAK,mBAAmB,IAAI;AAElC,SAAK,WAAW;AAGhB,QAAI,KAAK,UAAU,iBAAiB;AAClC,YAAM,KAAK,UAAU,gBAAA;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,MAAM,aAA4B;AAChC,QAAI,CAAC,KAAK,YAAY,KAAK,WAAY;AACvC,SAAK,WAAW;AAEhB,UAAM,KAAK,gBAAA;AAAA,EACb;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,WAAY;AACrB,UAAM,KAAK,WAAA;AACX,SAAK,QAAQ,YAAY,KAAK,MAAM;AACpC,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,MAAM,oBAAoB,QAAyC;AACjE,QAAI,KAAK,YAAY;AACnB;AAAA,IACF;AAEA,QAAI,OAAO,SAAS,UAAU;AAC5B,YAAM,KAAK,QAAA;AACX;AAAA,IACF;AAEA,UAAM,eAAe,OAAO;AAC5B,QAAI,CAAC,cAAc;AACjB;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,QAAA;AAClB,UAAM,WAAW,KAAK,YAAA;AACtB,QAAI,CAAC,QAAQ,CAAC,YAAa,SAAS,SAAS,WAAW,SAAS,SAAS,SAAU;AAClF;AAAA,IACF;AAGA,QAAI,KAAK,UAAU;AACjB,YAAM,KAAK,gBAAgB,MAAM,UAAU,YAAY;AAAA,IACzD,OAAO;AACL,YAAM,KAAK,SAAA;AAAA,IACb;AAAA,EACF;AAAA,EAEA,IAAI,qBAAwC;AAC1C,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,UAAuB;AAC7B,WAAO,KAAK,kBAAkB,WAAW,KAAK,MAAM,KAAK;AAAA,EAC3D;AAAA,EAEQ,cAAc;AACpB,UAAM,OAAO,KAAK,QAAA;AAClB,QAAI,CAAC,KAAM,QAAO;AAClB,WAAO,KAAK,iBAAiB,UAAU,IAAI,KAAK,UAAU,KAAK;AAAA,EACjE;AAAA,EAEA,MAAc,mBAAmB,OAA4B;AAC3D,UAAM,eAAe,KAAK,QAAQ,gBAAgB,KAAK,MAAM;AAC7D,QAAI,CAAC,cAAc;AACjB,YAAM,IAAI,MAAM,4BAA4B,KAAK,MAAM,EAAE;AAAA,IAC3D;AACA,UAAM,KAAK,oBAAoB,YAAY;AAAA,EAC7C;AAAA,EAEA,MAAc,mBAAmB,MAA2B;AAC1D,UAAM,KAAK,eAAe,MAAM,KAAK;AACrC,UAAM,KAAK,qBAAA;AAAA,EACb;AAAA,EAEA,MAAc,uBAAsC;AAClD,QAAI,CAAC,KAAK,cAAc;AACtB,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAGA,UAAM,YAAY,KAAK;AAGvB,QAAI,KAAK,cAAc;AACrB,WAAK,wBAAwB,IAAI,eAAA;AACjC,YAAM,KAAK,aAAa;AAAA,QACtB;AAAA,QACA;AAAA,UACE,WAAW;AAAA,UACX,MAAM,KAAK,sBAAsB;AAAA,UACjC,YAAY;AAAA,UACZ;AAAA,QAAA;AAAA,QAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,MAAE;AAEjD,YAAM,KAAK,aAAa;AAAA,QACtB;AAAA,QACA;AAAA,UACE,WAAW;AAAA,UACX,MAAM,KAAK,sBAAsB;AAAA,UACjC,YAAY;AAAA,UACZ;AAAA,QAAA;AAAA,QAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,MAAE;AAAA,IAEnD;AAEA,SAAK,aAAa,cAAc,CAAC,WAAW;AAC1C,YAAM,eAAe,KAAK,cAAc,gBAAgB,CAAA;AACxD,YAAM,MAAM,KAAK,iBAAiB,OAAO,aAAa,OAAO;AAC7D,WAAK,UAAU,sBAAsB,QAAQ,GAAG;AAAA,IAClD,CAAC;AAGD,QAAI,KAAK,cAAc;AACrB,WAAK,aAAa,cAAc,CAAC,QAAQ,aAAa;AACpD,YAAI,UAAU,eAAe,SAAS;AACpC,eAAK,UAAU;AAAA,YACb;AAAA,YAIA;AAAA,UAAA;AAAA,QAEJ,WAAW,UAAU,eAAe,SAAS;AAC3C,eAAK,UAAU;AAAA,YACb;AAAA,YAIA;AAAA,UAAA;AAAA,QAEJ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,mBAAmB,MAAY,UAAyC;AACpF,UAAM,KAAK,eAAe,MAAM,IAAI;AACpC,UAAM,KAAK,qBAAqB,SAAS,EAAE;AAAA,EAC7C;AAAA,EAEA,MAAc,eAAe,MAAY,SAAiC;AACxE,QAAI,SAAS;AACX,WAAK,eAAe,MAAM,KAAK,WAAW,IAAI,eAAe,KAAK,WAAW;AAAA,QAC3E,MAAM;AAAA,MAAA,CACP;AACD,WAAK,mBAAmB,MAAM,KAAK,WAAW,IAAI,cAAc,KAAK,WAAW;AAAA,QAC9E,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAEA,QAAI,KAAK,WAAW;AAClB,WAAK,eAAe,MAAM,KAAK,WAAW,IAAI,eAAe,KAAK,WAAW;AAAA,QAC3E,MAAM;AAAA,MAAA,CACP;AAAA,IACH;AAEA,SAAK,eAAe,MAAM,KAAK,WAAW,IAAI,gBAAgB,KAAK,WAAW,EAAE,MAAM,KAAA,CAAM;AAE5F,UAAM,eAAe,KAAK,cAAc,gBAAgB,CAAA;AACxD,UAAM,kBAAkB,KAAK,iBAAiB,gBAAgB,CAAA;AAC9D,UAAM,WAAW;AAAA,MACf,QAAQ,KAAK;AAAA,MACb,aAAa,KAAK;AAAA,MAClB,WAAW,KAAK,UAAU,KAAK;AAAA,MAC/B,gBAAgB,KAAK,iBAAiB,OAAO,aAAa,OAAO;AAAA,IAAA;AAEnE,UAAM,KAAK,aAAa,KAAK,aAAa;AAAA,MACxC,SAAS;AAAA,MACT,QAAQ,KAAK;AAAA,MACb,QAAQ,EAAE,GAAG,cAAc,GAAG,iBAAiB,SAAA;AAAA,IAAS,CACzD;AAAA,EACH;AAAA,EAEA,MAAc,qBAAqB,YAAmC;AACpE,QAAI,CAAC,KAAK,gBAAgB,CAAC,KAAK,gBAAgB,CAAC,KAAK,kBAAkB;AACtE,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAGA,UAAM,YAAY,KAAK;AAGvB,QAAI,KAAK,cAAc;AACrB,WAAK,wBAAwB,IAAI,eAAA;AACjC,YAAM,KAAK,aAAa;AAAA,QACtB;AAAA,QACA;AAAA,UACE,WAAW;AAAA,UACX,MAAM,KAAK,sBAAsB;AAAA,UACjC,YAAY;AAAA,UACZ;AAAA,QAAA;AAAA,QAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,MAAE;AAEjD,YAAM,KAAK,aAAa;AAAA,QACtB;AAAA,QACA;AAAA,UACE,WAAW;AAAA,UACX,MAAM,KAAK,sBAAsB;AAAA,UACjC,YAAY;AAAA,UACZ;AAAA,QAAA;AAAA,QAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,MAAE;AAAA,IAEnD;AAEA,SAAK,wBAAwB,IAAI,eAAA;AACjC,UAAM,KAAK,aAAa;AAAA,MACtB;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,sBAAsB;AAAA,QACjC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,IAAE;AAEjD,UAAM,KAAK,aAAa;AAAA,MACtB;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,sBAAsB;AAAA,QACjC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,sBAAsB,KAAK,EAAA;AAAA,IAAE;AAGjD,SAAK,uBAAuB,IAAI,eAAA;AAChC,UAAM,KAAK,iBAAiB;AAAA,MAC1B;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,qBAAqB;AAAA,QAChC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,qBAAqB,KAAK,EAAA;AAAA,IAAE;AAEhD,UAAM,KAAK,aAAa;AAAA,MACtB;AAAA,MACA;AAAA,QACE,WAAW;AAAA,QACX,MAAM,KAAK,qBAAqB;AAAA,QAChC,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,EAAE,UAAU,CAAC,KAAK,qBAAqB,KAAK,EAAA;AAAA,IAAE;AAGhD,UAAM,cAAc,KAAK,cAAc,cAAc,CAAA;AACrD,UAAM,KAAK,iBAAiB,KAAK,aAAa;AAAA,MAC5C,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IAAA,CACT;AAED,SAAK,aAAa,cAAc,CAAC,WAAW;AAC1C,YAAM,eAAe,KAAK,cAAc,gBAAgB,CAAA;AACxD,YAAM,MAAM,KAAK,iBAAiB,OAAO,aAAa,OAAO;AAC7D,WAAK,UAAU,sBAAsB,QAAQ,GAAG;AAAA,IAClD,CAAC;AAGD,QAAI,KAAK,cAAc;AACrB,WAAK,aAAa,cAAc,CAAC,QAAQ,aAAa;AACpD,YAAI,UAAU,eAAe,SAAS;AACpC,eAAK,UAAU;AAAA,YACb;AAAA,YAIA;AAAA,UAAA;AAAA,QAEJ,WAAW,UAAU,eAAe,SAAS;AAC3C,eAAK,UAAU;AAAA,YACb;AAAA,YAIA;AAAA,UAAA;AAAA,QAEJ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAc,oBAAoB,cAAiD;AACjF,SAAK,qBAAqB;AAAA,MACxB,UAAU,aAAa;AAAA,MACvB;AAAA,MACA,QAAQ,aAAa;AAAA,IAAA;AAGvB,QAAI,KAAK,cAAc;AACrB,YAAM,KAAK,aAAa,KAAK,wBAAwB,YAAY;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,MAAc,gBACZ,MACA,UACA,cACe;AACf,UAAM,KAAK,gBAAA;AAEX,QAAI,SAAS,SAAS,SAAS;AAC7B,YAAM,KAAK,mBAAmB,MAAM,QAAQ;AAAA,IAC9C,WAAW,SAAS,SAAS,SAAS;AACpC,YAAM,KAAK,mBAAmB,IAAI;AAAA,IACpC;AAEA,UAAM,KAAK,oBAAoB,YAAY;AAAA,EAC7C;AAAA,EAEA,MAAc,kBAAiC;AAC7C,QAAI,KAAK,gBAAgB,KAAK,oBAAoB;AAChD,YAAM,KAAK,aAAa,OAAO,gBAAgB;AAAA,QAC7C,WAAW,KAAK;AAAA,QAChB,UAAU,KAAK,mBAAmB;AAAA,MAAA,CACnC;AAAA,IACH;AAEA,SAAK,uBAAuB,MAAM,MAAA;AAClC,SAAK,uBAAuB,MAAM,MAAA;AAClC,SAAK,uBAAuB,MAAM,MAAA;AAClC,SAAK,uBAAuB,MAAM,MAAA;AAClC,SAAK,sBAAsB,MAAM,MAAA;AACjC,SAAK,sBAAsB,MAAM,MAAA;AAEjC,SAAK,wBAAwB;AAC7B,SAAK,wBAAwB;AAC7B,SAAK,uBAAuB;AAE5B,QAAI,KAAK,gBAAgB,KAAK,UAAU,kBAAkB;AACxD,WAAK,UAAU,iBAAA;AAAA,IACjB;AACA,SAAK,eAAe;AAGpB,QAAI,KAAK,kBAAkB;AACzB,WAAK,WAAW,UAAU,cAAc,KAAK,SAAS;AACtD,WAAK,mBAAmB;AAAA,IAC1B;AAEA,QAAI,KAAK,cAAc;AACrB,WAAK,WAAW,UAAU,eAAe,KAAK,SAAS;AACvD,WAAK,eAAe;AAAA,IACtB;AAEA,QAAI,KAAK,cAAc;AACrB,WAAK,WAAW,UAAU,gBAAgB,KAAK,SAAS;AACxD,WAAK,eAAe;AAAA,IACtB;AAEA,QAAI,KAAK,cAAc;AACrB,WAAK,WAAW,UAAU,eAAe,KAAK,SAAS;AACvD,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,MAAM,sBAAqC;AACzC,UAAM,KAAK,aAAa,eAAe,KAAK,MAAM;AAAA,EACpD;AACF;"}
@@ -1 +1 @@
1
- {"version":3,"file":"GlobalAudioSession.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/GlobalAudioSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAE,gBAAgB,EAAQ,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,gBAAgB;IACxB,YAAY,EAAE,YAAY,CAAC;IAC3B,OAAO,EAAE,UAAU,CAAC;IACpB,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACpC,QAAQ,EAAE,MAAM,gBAAgB,GAAG,IAAI,CAAC;IACxC,kBAAkB,EAAE,MAAM,GAAG,CAAC;CAC/B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,IAAI,CAAmB;gBAEnB,IAAI,EAAE,gBAAgB;IAKlC,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAKtC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAwB5D,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAkB3E,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B5C,iBAAiB,CAAC,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IA+BzF,KAAK,IAAI,IAAI;IAKb;;;;OAIG;IACG,yBAAyB,CAC7B,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,EACpC,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,yBAAyB,KAAK,IAAI,GAC9D,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC;IA6BpD;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IAuC1E;;OAEG;YACW,sBAAsB;IAgBpC;;OAEG;IACH,OAAO,CAAC,cAAc;IAwBtB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAqB9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;YAgBd,kBAAkB;IAiChC,OAAO,CAAC,aAAa;CAGtB"}
1
+ {"version":3,"file":"GlobalAudioSession.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/GlobalAudioSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAE,gBAAgB,EAAQ,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAG7D,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,gBAAgB;IACxB,YAAY,EAAE,YAAY,CAAC;IAC3B,OAAO,EAAE,UAAU,CAAC;IACpB,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACpC,QAAQ,EAAE,MAAM,gBAAgB,GAAG,IAAI,CAAC;IACxC,kBAAkB,EAAE,MAAM,GAAG,CAAC;CAC/B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,KAAK,CAAoB;IACjC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,IAAI,CAAmB;gBAEnB,IAAI,EAAE,gBAAgB;IAKlC,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAKtC,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAwB5D,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAkB3E,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B5C,iBAAiB,CAAC,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IA+BzF,KAAK,IAAI,IAAI;IAKb;;;;OAIG;IACG,yBAAyB,CAC7B,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,EACpC,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,yBAAyB,KAAK,IAAI,GAC9D,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC;IA6BpD;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;IAuC1E;;OAEG;YACW,sBAAsB;IAgBpC;;OAEG;IACH,OAAO,CAAC,cAAc;IAwBtB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAqB9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;YAgBd,kBAAkB;IA0ChC,OAAO,CAAC,aAAa;CAGtB"}
@@ -230,25 +230,33 @@ class GlobalAudioSession {
230
230
  async setupAudioPipeline(clip) {
231
231
  const { id: clipId, resourceId, startUs, durationUs } = clip;
232
232
  const audioDemuxWorker = await this.deps.workers.get("audioDemux", clipId, { lazy: true });
233
- const decodeWorker = await this.deps.workers.get("decode");
233
+ const audioDecodeWorker = await this.deps.workers.get("audioDecode", clipId, { lazy: true });
234
234
  const demuxToDecodeChannel = new MessageChannel();
235
235
  await audioDemuxWorker.send(
236
236
  "connect",
237
237
  { direction: "downstream", port: demuxToDecodeChannel.port1, streamType: "audio", clipId },
238
238
  { transfer: [demuxToDecodeChannel.port1] }
239
239
  );
240
- await decodeWorker.send(
240
+ await audioDecodeWorker.send(
241
241
  "connect",
242
242
  {
243
243
  direction: "upstream",
244
244
  port: demuxToDecodeChannel.port2,
245
245
  streamType: "audio",
246
- clipId,
246
+ sessionId: clipId,
247
247
  clipStartUs: startUs || 0,
248
248
  clipDurationUs: durationUs || 0
249
249
  },
250
250
  { transfer: [demuxToDecodeChannel.port2] }
251
251
  );
252
+ audioDecodeWorker.receiveStream((stream, metadata) => {
253
+ this.handleAudioStream(stream, {
254
+ sessionId: clipId,
255
+ clipStartUs: startUs || 0,
256
+ clipDurationUs: durationUs || 0,
257
+ ...metadata
258
+ });
259
+ });
252
260
  const demuxConfig = this.deps.buildWorkerConfigs().audioDemux;
253
261
  await audioDemuxWorker.send("configure", {
254
262
  initial: true,
@@ -1 +1 @@
1
- {"version":3,"file":"GlobalAudioSession.js","sources":["../../../src/stages/compose/GlobalAudioSession.ts"],"sourcesContent":["import type { TimeUs } from '../../model/types';\nimport { OfflineAudioMixer } from './OfflineAudioMixer';\nimport type { CompositionModel, Clip } from '../../model';\nimport type { WorkerPool } from '../../worker/WorkerPool';\nimport type { ResourceLoader } from '../load/ResourceLoader';\nimport type { EventBus } from '../../event/EventBus';\nimport type { EventPayloadMap } from '../../event/events';\nimport { MeframeEvent } from '../../event/events';\nimport type { CacheManager } from '../../cache/CacheManager';\nimport { AudioChunkEncoder } from '../encode/AudioChunkEncoder';\n\ninterface AudioDataMessage {\n sessionId: string;\n audioData: AudioData;\n clipStartUs: TimeUs;\n clipDurationUs: TimeUs;\n}\n\nexport interface AudioPlaybackInfo {\n buffer: AudioBuffer;\n offsetSeconds: number;\n durationSeconds: number;\n}\n\ninterface AudioSessionDeps {\n cacheManager: CacheManager;\n workers: WorkerPool;\n resourceLoader: ResourceLoader;\n eventBus: EventBus<EventPayloadMap>;\n getModel: () => CompositionModel | null;\n buildWorkerConfigs: () => any;\n}\n\nexport class GlobalAudioSession {\n private mixWindowUs = 3_000_000;\n private mixer: OfflineAudioMixer;\n private activeClips = new Set<string>();\n private deps: AudioSessionDeps;\n\n constructor(deps: AudioSessionDeps) {\n this.deps = deps;\n this.mixer = new OfflineAudioMixer(deps.cacheManager, deps.getModel);\n }\n\n onAudioData(message: AudioDataMessage): void {\n const { sessionId, audioData, clipStartUs, clipDurationUs } = message;\n this.deps.cacheManager.putClipAudioData(sessionId, audioData, clipStartUs, clipDurationUs);\n }\n\n async ensureMixedPCM(startUs: TimeUs): Promise<AudioBuffer | null> {\n const model = this.deps.getModel();\n if (!model) {\n return null;\n }\n\n const windowStartUs = this.alignToWindow(startUs);\n const windowEndUs = windowStartUs + this.mixWindowUs;\n\n const cached = this.deps.cacheManager.getMixedAudio(windowStartUs, windowEndUs);\n if (cached) {\n return cached;\n }\n\n try {\n const mixedBuffer = await this.mixer.mix(windowStartUs, windowEndUs);\n this.deps.cacheManager.putMixedAudio(windowStartUs, windowEndUs, mixedBuffer);\n return mixedBuffer;\n } catch (error) {\n console.error('[GlobalAudioSession] Mix failed:', error);\n return null;\n }\n }\n\n async prepareAudioForPlayback(startUs: TimeUs): Promise<AudioPlaybackInfo | null> {\n const buffer = await this.ensureMixedPCM(startUs);\n if (!buffer) {\n return null;\n }\n\n const windowStartUs = this.alignToWindow(startUs);\n const offsetUs = startUs - windowStartUs;\n const offsetSeconds = offsetUs / 1_000_000;\n const durationSeconds = buffer.duration - offsetSeconds;\n\n return {\n buffer,\n offsetSeconds,\n durationSeconds,\n };\n }\n\n async activateAllAudioClips(): Promise<void> {\n const model = this.deps.getModel();\n if (!model) {\n return;\n }\n\n const audioTracks = model.tracks.filter((track) => track.kind === 'audio');\n\n for (const track of audioTracks) {\n for (const clip of track.clips) {\n if (!this.activeClips.has(clip.id)) {\n await this.setupAudioPipeline(clip);\n this.activeClips.add(clip.id);\n\n await this.deps.resourceLoader.fetch(clip.resourceId, {\n priority: 'high',\n sessionId: clip.id,\n trackId: track.id,\n });\n\n this.deps.eventBus.emit(MeframeEvent.ClipActivated, { clipId: clip.id });\n }\n }\n }\n }\n\n handleAudioStream(stream: ReadableStream<AudioData>, metadata: Record<string, any>): void {\n const sessionId = metadata.sessionId || 'unknown';\n const clipStartUs = metadata.clipStartUs ?? 0;\n const clipDurationUs = metadata.clipDurationUs ?? 0;\n\n const reader = stream.getReader();\n const pump = async (): Promise<void> => {\n try {\n const { done, value } = await reader.read();\n if (done) {\n reader.releaseLock();\n return;\n }\n\n this.onAudioData({\n sessionId,\n audioData: value,\n clipStartUs,\n clipDurationUs,\n });\n\n await pump();\n } catch (error) {\n console.error('[GlobalAudioSession] Audio stream error:', error);\n reader.releaseLock();\n }\n };\n\n pump();\n }\n\n reset(): void {\n this.deps.cacheManager.resetAudioCache();\n this.activeClips.clear();\n }\n\n /**\n * Create export encoded audio stream with metadata callback\n * Mixes all audio clips and encodes to EncodedAudioChunk for export\n * Extracts metadata from first chunk and invokes callback\n */\n async createExportEncodedStream(\n config?: Partial<AudioEncoderConfig>,\n onFirstMetadata?: (metadata: EncodedAudioChunkMetadata) => void\n ): Promise<ReadableStream<EncodedAudioChunk> | null> {\n const audioDataStream = await this.createExportAudioStream();\n if (!audioDataStream) {\n return null;\n }\n\n const encoder = new AudioChunkEncoder(config);\n await encoder.initialize();\n\n const encodingTransform = encoder.createStream();\n const encodedStream = audioDataStream.pipeThrough(encodingTransform);\n\n let firstMetadataExtracted = false;\n\n // Extract chunk from EncoderChunk and capture first metadata\n return encodedStream.pipeThrough(\n new TransformStream({\n transform(encoderChunk, controller) {\n // Extract metadata from first chunk\n if (!firstMetadataExtracted && onFirstMetadata) {\n onFirstMetadata(encoderChunk.metadata as EncodedAudioChunkMetadata);\n firstMetadataExtracted = true;\n }\n controller.enqueue(encoderChunk.chunk as EncodedAudioChunk);\n },\n })\n );\n }\n\n /**\n * Create export audio stream\n * Mixes all audio clips for the entire composition and outputs AudioData stream\n */\n async createExportAudioStream(): Promise<ReadableStream<AudioData> | null> {\n const model = this.deps.getModel();\n if (!model) {\n return null;\n }\n\n const totalDurationUs = model.durationUs;\n\n // Ensure all audio clips are active and cached\n await this.activateAllAudioClips();\n\n // Wait for PCM data to be available\n await this.waitForAudioClipsReady();\n\n return new ReadableStream<AudioData>({\n start: async (controller) => {\n const windowSize = 3_000_000; // 3 seconds per chunk\n let currentUs = 0;\n\n while (currentUs < totalDurationUs) {\n const windowEndUs = Math.min(currentUs + windowSize, totalDurationUs);\n\n // Mix this window\n const mixedBuffer = await this.mixer.mix(currentUs, windowEndUs);\n\n // Convert AudioBuffer to AudioData\n const audioData = this.audioBufferToAudioData(mixedBuffer, currentUs);\n if (audioData) {\n controller.enqueue(audioData);\n }\n\n currentUs = windowEndUs;\n }\n\n controller.close();\n },\n });\n }\n\n /**\n * Wait for audio clips to be decoded and cached\n */\n private async waitForAudioClipsReady(): Promise<void> {\n const model = this.deps.getModel();\n if (!model) return;\n\n const audioClips = model.tracks\n .filter((track) => track.kind === 'audio')\n .flatMap((track) => track.clips);\n\n // Wait for each clip to have PCM data\n const waitPromises = audioClips.map(\n (clip) => this.waitForClipPCM(clip.id, 1000) // Wait up to 1s per clip\n );\n\n await Promise.allSettled(waitPromises);\n }\n\n /**\n * Wait for clip PCM data to be available\n */\n private waitForClipPCM(clipId: string, timeoutMs: number): Promise<boolean> {\n return new Promise((resolve) => {\n const checkInterval = 50;\n let elapsed = 0;\n\n const check = () => {\n if (this.deps.cacheManager.hasClipPCM(clipId)) {\n resolve(true);\n return;\n }\n\n elapsed += checkInterval;\n if (elapsed >= timeoutMs) {\n resolve(false);\n return;\n }\n\n setTimeout(check, checkInterval);\n };\n\n check();\n });\n }\n\n /**\n * Convert AudioBuffer to AudioData for encoding\n */\n private audioBufferToAudioData(buffer: AudioBuffer, timestampUs: TimeUs): AudioData | null {\n const sampleRate = buffer.sampleRate;\n const numberOfChannels = buffer.numberOfChannels;\n const numberOfFrames = buffer.length;\n\n // Interleave channels into planar format\n const planes: Float32Array[] = [];\n for (let channel = 0; channel < numberOfChannels; channel++) {\n planes.push(buffer.getChannelData(channel));\n }\n\n return new AudioData({\n format: 'f32', // interleaved format\n sampleRate,\n numberOfFrames,\n numberOfChannels,\n timestamp: timestampUs,\n data: this.interleavePlanarData(planes),\n });\n }\n\n /**\n * Interleave planar audio data for AudioData constructor\n */\n private interleavePlanarData(planes: Float32Array[]): ArrayBuffer {\n const numberOfChannels = planes.length;\n const numberOfFrames = planes[0]?.length ?? 0;\n const totalSamples = numberOfChannels * numberOfFrames;\n\n const interleaved = new Float32Array(totalSamples);\n\n for (let frame = 0; frame < numberOfFrames; frame++) {\n for (let channel = 0; channel < numberOfChannels; channel++) {\n interleaved[frame * numberOfChannels + channel] = planes[channel]![frame]!;\n }\n }\n\n return interleaved.buffer;\n }\n\n private async setupAudioPipeline(clip: Clip): Promise<void> {\n const { id: clipId, resourceId, startUs, durationUs } = clip;\n const audioDemuxWorker = await this.deps.workers.get('audioDemux', clipId, { lazy: true });\n const decodeWorker = await this.deps.workers.get('decode');\n\n const demuxToDecodeChannel = new MessageChannel();\n await audioDemuxWorker.send(\n 'connect',\n { direction: 'downstream', port: demuxToDecodeChannel.port1, streamType: 'audio', clipId },\n { transfer: [demuxToDecodeChannel.port1] }\n );\n await decodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: demuxToDecodeChannel.port2,\n streamType: 'audio',\n clipId,\n clipStartUs: startUs || 0,\n clipDurationUs: durationUs || 0,\n },\n { transfer: [demuxToDecodeChannel.port2] }\n );\n\n const demuxConfig = this.deps.buildWorkerConfigs().audioDemux;\n await audioDemuxWorker.send('configure', {\n initial: true,\n resourceId,\n clipId,\n config: demuxConfig,\n });\n }\n\n private alignToWindow(timeUs: TimeUs): TimeUs {\n return Math.floor(timeUs / this.mixWindowUs) * this.mixWindowUs;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GlobalAudioSession.js","sources":["../../../src/stages/compose/GlobalAudioSession.ts"],"sourcesContent":["import type { TimeUs } from '../../model/types';\nimport { OfflineAudioMixer } from './OfflineAudioMixer';\nimport type { CompositionModel, Clip } from '../../model';\nimport type { WorkerPool } from '../../worker/WorkerPool';\nimport type { ResourceLoader } from '../load/ResourceLoader';\nimport type { EventBus } from '../../event/EventBus';\nimport type { EventPayloadMap } from '../../event/events';\nimport { MeframeEvent } from '../../event/events';\nimport type { CacheManager } from '../../cache/CacheManager';\nimport { AudioChunkEncoder } from '../encode/AudioChunkEncoder';\n\ninterface AudioDataMessage {\n sessionId: string;\n audioData: AudioData;\n clipStartUs: TimeUs;\n clipDurationUs: TimeUs;\n}\n\nexport interface AudioPlaybackInfo {\n buffer: AudioBuffer;\n offsetSeconds: number;\n durationSeconds: number;\n}\n\ninterface AudioSessionDeps {\n cacheManager: CacheManager;\n workers: WorkerPool;\n resourceLoader: ResourceLoader;\n eventBus: EventBus<EventPayloadMap>;\n getModel: () => CompositionModel | null;\n buildWorkerConfigs: () => any;\n}\n\nexport class GlobalAudioSession {\n private mixWindowUs = 3_000_000;\n private mixer: OfflineAudioMixer;\n private activeClips = new Set<string>();\n private deps: AudioSessionDeps;\n\n constructor(deps: AudioSessionDeps) {\n this.deps = deps;\n this.mixer = new OfflineAudioMixer(deps.cacheManager, deps.getModel);\n }\n\n onAudioData(message: AudioDataMessage): void {\n const { sessionId, audioData, clipStartUs, clipDurationUs } = message;\n this.deps.cacheManager.putClipAudioData(sessionId, audioData, clipStartUs, clipDurationUs);\n }\n\n async ensureMixedPCM(startUs: TimeUs): Promise<AudioBuffer | null> {\n const model = this.deps.getModel();\n if (!model) {\n return null;\n }\n\n const windowStartUs = this.alignToWindow(startUs);\n const windowEndUs = windowStartUs + this.mixWindowUs;\n\n const cached = this.deps.cacheManager.getMixedAudio(windowStartUs, windowEndUs);\n if (cached) {\n return cached;\n }\n\n try {\n const mixedBuffer = await this.mixer.mix(windowStartUs, windowEndUs);\n this.deps.cacheManager.putMixedAudio(windowStartUs, windowEndUs, mixedBuffer);\n return mixedBuffer;\n } catch (error) {\n console.error('[GlobalAudioSession] Mix failed:', error);\n return null;\n }\n }\n\n async prepareAudioForPlayback(startUs: TimeUs): Promise<AudioPlaybackInfo | null> {\n const buffer = await this.ensureMixedPCM(startUs);\n if (!buffer) {\n return null;\n }\n\n const windowStartUs = this.alignToWindow(startUs);\n const offsetUs = startUs - windowStartUs;\n const offsetSeconds = offsetUs / 1_000_000;\n const durationSeconds = buffer.duration - offsetSeconds;\n\n return {\n buffer,\n offsetSeconds,\n durationSeconds,\n };\n }\n\n async activateAllAudioClips(): Promise<void> {\n const model = this.deps.getModel();\n if (!model) {\n return;\n }\n\n const audioTracks = model.tracks.filter((track) => track.kind === 'audio');\n\n for (const track of audioTracks) {\n for (const clip of track.clips) {\n if (!this.activeClips.has(clip.id)) {\n await this.setupAudioPipeline(clip);\n this.activeClips.add(clip.id);\n\n await this.deps.resourceLoader.fetch(clip.resourceId, {\n priority: 'high',\n sessionId: clip.id,\n trackId: track.id,\n });\n\n this.deps.eventBus.emit(MeframeEvent.ClipActivated, { clipId: clip.id });\n }\n }\n }\n }\n\n handleAudioStream(stream: ReadableStream<AudioData>, metadata: Record<string, any>): void {\n const sessionId = metadata.sessionId || 'unknown';\n const clipStartUs = metadata.clipStartUs ?? 0;\n const clipDurationUs = metadata.clipDurationUs ?? 0;\n\n const reader = stream.getReader();\n const pump = async (): Promise<void> => {\n try {\n const { done, value } = await reader.read();\n if (done) {\n reader.releaseLock();\n return;\n }\n\n this.onAudioData({\n sessionId,\n audioData: value,\n clipStartUs,\n clipDurationUs,\n });\n\n await pump();\n } catch (error) {\n console.error('[GlobalAudioSession] Audio stream error:', error);\n reader.releaseLock();\n }\n };\n\n pump();\n }\n\n reset(): void {\n this.deps.cacheManager.resetAudioCache();\n this.activeClips.clear();\n }\n\n /**\n * Create export encoded audio stream with metadata callback\n * Mixes all audio clips and encodes to EncodedAudioChunk for export\n * Extracts metadata from first chunk and invokes callback\n */\n async createExportEncodedStream(\n config?: Partial<AudioEncoderConfig>,\n onFirstMetadata?: (metadata: EncodedAudioChunkMetadata) => void\n ): Promise<ReadableStream<EncodedAudioChunk> | null> {\n const audioDataStream = await this.createExportAudioStream();\n if (!audioDataStream) {\n return null;\n }\n\n const encoder = new AudioChunkEncoder(config);\n await encoder.initialize();\n\n const encodingTransform = encoder.createStream();\n const encodedStream = audioDataStream.pipeThrough(encodingTransform);\n\n let firstMetadataExtracted = false;\n\n // Extract chunk from EncoderChunk and capture first metadata\n return encodedStream.pipeThrough(\n new TransformStream({\n transform(encoderChunk, controller) {\n // Extract metadata from first chunk\n if (!firstMetadataExtracted && onFirstMetadata) {\n onFirstMetadata(encoderChunk.metadata as EncodedAudioChunkMetadata);\n firstMetadataExtracted = true;\n }\n controller.enqueue(encoderChunk.chunk as EncodedAudioChunk);\n },\n })\n );\n }\n\n /**\n * Create export audio stream\n * Mixes all audio clips for the entire composition and outputs AudioData stream\n */\n async createExportAudioStream(): Promise<ReadableStream<AudioData> | null> {\n const model = this.deps.getModel();\n if (!model) {\n return null;\n }\n\n const totalDurationUs = model.durationUs;\n\n // Ensure all audio clips are active and cached\n await this.activateAllAudioClips();\n\n // Wait for PCM data to be available\n await this.waitForAudioClipsReady();\n\n return new ReadableStream<AudioData>({\n start: async (controller) => {\n const windowSize = 3_000_000; // 3 seconds per chunk\n let currentUs = 0;\n\n while (currentUs < totalDurationUs) {\n const windowEndUs = Math.min(currentUs + windowSize, totalDurationUs);\n\n // Mix this window\n const mixedBuffer = await this.mixer.mix(currentUs, windowEndUs);\n\n // Convert AudioBuffer to AudioData\n const audioData = this.audioBufferToAudioData(mixedBuffer, currentUs);\n if (audioData) {\n controller.enqueue(audioData);\n }\n\n currentUs = windowEndUs;\n }\n\n controller.close();\n },\n });\n }\n\n /**\n * Wait for audio clips to be decoded and cached\n */\n private async waitForAudioClipsReady(): Promise<void> {\n const model = this.deps.getModel();\n if (!model) return;\n\n const audioClips = model.tracks\n .filter((track) => track.kind === 'audio')\n .flatMap((track) => track.clips);\n\n // Wait for each clip to have PCM data\n const waitPromises = audioClips.map(\n (clip) => this.waitForClipPCM(clip.id, 1000) // Wait up to 1s per clip\n );\n\n await Promise.allSettled(waitPromises);\n }\n\n /**\n * Wait for clip PCM data to be available\n */\n private waitForClipPCM(clipId: string, timeoutMs: number): Promise<boolean> {\n return new Promise((resolve) => {\n const checkInterval = 50;\n let elapsed = 0;\n\n const check = () => {\n if (this.deps.cacheManager.hasClipPCM(clipId)) {\n resolve(true);\n return;\n }\n\n elapsed += checkInterval;\n if (elapsed >= timeoutMs) {\n resolve(false);\n return;\n }\n\n setTimeout(check, checkInterval);\n };\n\n check();\n });\n }\n\n /**\n * Convert AudioBuffer to AudioData for encoding\n */\n private audioBufferToAudioData(buffer: AudioBuffer, timestampUs: TimeUs): AudioData | null {\n const sampleRate = buffer.sampleRate;\n const numberOfChannels = buffer.numberOfChannels;\n const numberOfFrames = buffer.length;\n\n // Interleave channels into planar format\n const planes: Float32Array[] = [];\n for (let channel = 0; channel < numberOfChannels; channel++) {\n planes.push(buffer.getChannelData(channel));\n }\n\n return new AudioData({\n format: 'f32', // interleaved format\n sampleRate,\n numberOfFrames,\n numberOfChannels,\n timestamp: timestampUs,\n data: this.interleavePlanarData(planes),\n });\n }\n\n /**\n * Interleave planar audio data for AudioData constructor\n */\n private interleavePlanarData(planes: Float32Array[]): ArrayBuffer {\n const numberOfChannels = planes.length;\n const numberOfFrames = planes[0]?.length ?? 0;\n const totalSamples = numberOfChannels * numberOfFrames;\n\n const interleaved = new Float32Array(totalSamples);\n\n for (let frame = 0; frame < numberOfFrames; frame++) {\n for (let channel = 0; channel < numberOfChannels; channel++) {\n interleaved[frame * numberOfChannels + channel] = planes[channel]![frame]!;\n }\n }\n\n return interleaved.buffer;\n }\n\n private async setupAudioPipeline(clip: Clip): Promise<void> {\n const { id: clipId, resourceId, startUs, durationUs } = clip;\n const audioDemuxWorker = await this.deps.workers.get('audioDemux', clipId, { lazy: true });\n const audioDecodeWorker = await this.deps.workers.get('audioDecode', clipId, { lazy: true });\n\n const demuxToDecodeChannel = new MessageChannel();\n await audioDemuxWorker.send(\n 'connect',\n { direction: 'downstream', port: demuxToDecodeChannel.port1, streamType: 'audio', clipId },\n { transfer: [demuxToDecodeChannel.port1] }\n );\n await audioDecodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: demuxToDecodeChannel.port2,\n streamType: 'audio',\n sessionId: clipId,\n clipStartUs: startUs || 0,\n clipDurationUs: durationUs || 0,\n },\n { transfer: [demuxToDecodeChannel.port2] }\n );\n\n audioDecodeWorker.receiveStream((stream, metadata) => {\n this.handleAudioStream(stream as ReadableStream<AudioData>, {\n sessionId: clipId,\n clipStartUs: startUs || 0,\n clipDurationUs: durationUs || 0,\n ...metadata,\n });\n });\n\n const demuxConfig = this.deps.buildWorkerConfigs().audioDemux;\n await audioDemuxWorker.send('configure', {\n initial: true,\n resourceId,\n clipId,\n config: demuxConfig,\n });\n }\n\n private alignToWindow(timeUs: TimeUs): TimeUs {\n return Math.floor(timeUs / this.mixWindowUs) * this.mixWindowUs;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@ class ResourceLoader {
13
13
  onStateChange;
14
14
  byteRangeResolver;
15
15
  constructor(options) {
16
- const maxConcurrent = options?.config?.maxConcurrent ?? 4;
16
+ const maxConcurrent = options?.config?.maxConcurrent ?? 2;
17
17
  this.taskManager = new TaskManager(maxConcurrent);
18
18
  this.streamFactory = new StreamFactory(options?.onProgress, options?.config);
19
19
  this.eventBus = options?.eventBus;
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceLoader.js","sources":["../../../src/stages/load/ResourceLoader.ts"],"sourcesContent":["import type { Resource, CompositionModel } from '../../model';\nimport type { Orchestrator, ResourceLoadOptions, LoadTask, ResourceLoaderOptions } from './types';\nimport { TaskManager } from './TaskManager';\nimport { StreamFactory } from './StreamFactory';\nimport { EventHandlers } from './EventHandlers';\nimport { EventPayloadMap, MeframeEvent } from '../../event/events';\nimport { EventBus } from '../../event/EventBus';\nimport { WindowByteRangeResolver } from './WindowByteRangeResolver';\n\nexport class ResourceLoader {\n private orchestrator?: Orchestrator;\n private model?: CompositionModel;\n private taskManager: TaskManager;\n private streamFactory: StreamFactory;\n private eventHandlers?: EventHandlers;\n private eventBus?: EventBus<EventPayloadMap>;\n private onStateChange?: (resourceId: string, state: Resource['state']) => void;\n private byteRangeResolver: WindowByteRangeResolver;\n\n constructor(options?: ResourceLoaderOptions) {\n const maxConcurrent = options?.config?.maxConcurrent ?? 4;\n this.taskManager = new TaskManager(maxConcurrent);\n this.streamFactory = new StreamFactory(options?.onProgress, options?.config);\n this.eventBus = options?.eventBus;\n this.onStateChange = options?.onStateChange;\n this.byteRangeResolver = new WindowByteRangeResolver();\n\n if (options?.orchestrator) {\n this.bind(options.orchestrator);\n }\n }\n\n /**\n * Bind to Orchestrator event system\n */\n bind(orchestrator: Orchestrator): void {\n this.unbind();\n this.orchestrator = orchestrator;\n\n this.eventHandlers = new EventHandlers(\n orchestrator,\n (resourceId) => this.cancel(resourceId),\n (model) => this.handleModelSet(model)\n );\n }\n\n /**\n * Unbind from Orchestrator\n */\n unbind(): void {\n this.eventHandlers?.dispose();\n this.eventHandlers = undefined;\n this.orchestrator = undefined;\n }\n\n private handleModelSet(model: CompositionModel): void {\n this.model = model;\n }\n\n private enqueueLoad(\n resource: Resource,\n priority: 'high' | 'normal' | 'low' = 'normal',\n sessionId?: string,\n trackId?: string\n ): void {\n if (this.taskManager.hasActiveTask(resource.id)) {\n return;\n }\n\n this.taskManager.enqueue(resource, priority, sessionId, trackId);\n this.processQueue();\n }\n\n private processQueue(): void {\n while (this.taskManager.canProcess) {\n const task = this.taskManager.getNextTask();\n if (!task) break;\n this.startLoad(task);\n }\n }\n\n /**\n * Start loading a resource\n * Handles state management (loading → ready/error) for all resource types\n */\n private async startLoad(task: LoadTask): Promise<void> {\n this.taskManager.activateTask(task);\n\n try {\n this.updateResourceState(task.resourceId, 'loading');\n task.controller = new AbortController();\n\n // Route to different handlers based on resource type\n // Note: Each handler only deals with data, state is managed here\n if (task.resource.type === 'image') {\n await this.loadImageBitmap(task);\n } else if (task.resource.type === 'video' || task.resource.type === 'audio') {\n const stream = await this.streamFactory.createRegularStream(task);\n if (!stream) {\n throw new Error(`Failed to create stream for ${task.resourceId}`);\n }\n task.stream = stream;\n await this.transferToDemuxWorker(task);\n } else if (task.resource.type === 'json' || task.resource.type === 'text') {\n await this.loadTextResource(task);\n }\n\n // Unified state update for all resource types\n this.updateResourceState(task.resourceId, 'ready');\n } catch (error) {\n task.error = error as Error;\n this.updateResourceState(task.resourceId, 'error');\n } finally {\n this.taskManager.completeTask(task.resourceId);\n this.processQueue();\n }\n }\n\n /**\n * Load text-based resources (json, text)\n * Just download the content - state management is handled by startLoad()\n */\n private async loadTextResource(task: LoadTask): Promise<void> {\n await this.fetchBlob(task.resource.uri, task.controller!.signal);\n // For json/text, just verify we can download it\n // Future: could parse and validate json here if needed\n }\n\n /**\n * Load image resource: fetch blob → create ImageBitmap → transfer to worker\n * Note: Images don't need streaming (typically < 5MB)\n */\n private async loadImageBitmap(task: LoadTask): Promise<void> {\n const blob = await this.fetchBlob(task.resource.uri, task.controller!.signal);\n\n // Create ImageBitmap in main thread, then transfer to worker\n // Note: In Worker context, ImageBitmap is the only way to create VideoFrame from image data\n const imageBitmap = await createImageBitmap(blob, {\n premultiplyAlpha: 'premultiply',\n colorSpaceConversion: 'default',\n imageOrientation: 'from-image',\n });\n\n await this.transferImageToWorker(task, imageBitmap);\n }\n\n /**\n * Fetch resource as blob (for images, json, etc.)\n */\n private async fetchBlob(uri: string, signal: AbortSignal): Promise<Blob> {\n const response = await fetch(uri, { signal });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return response.blob();\n }\n\n /**\n * Transfer ImageBitmap to VideoComposeWorker\n */\n private async transferImageToWorker(task: LoadTask, imageBitmap: ImageBitmap): Promise<void> {\n if (!this.orchestrator) return;\n\n if (!task.sessionId) {\n throw new Error(\n `[ResourceLoader] sessionId required for resource ${task.resourceId}. ` +\n `In Clip-based architecture, use fetch(resourceId, { sessionId })`\n );\n }\n\n const composeWorker = await this.orchestrator.workers.get('videoCompose', task.sessionId, {\n lazy: true,\n });\n\n await composeWorker.send(\n 'receive_image',\n {\n resourceId: task.resourceId,\n sessionId: task.sessionId,\n imageBitmap,\n },\n { transfer: [imageBitmap] }\n );\n }\n\n private async transferToDemuxWorker(task: LoadTask): Promise<void> {\n if (!task.stream || !this.orchestrator) return;\n\n if (!task.sessionId) {\n throw new Error(\n `[ResourceLoader] sessionId required for resource ${task.resourceId}. ` +\n `In Clip-based architecture, use fetch(resourceId, { sessionId })`\n );\n }\n\n const workerType = task.resource.type === 'video' ? 'videoDemux' : 'audioDemux';\n const demuxWorker = await this.orchestrator.workers.get(workerType, task.sessionId, {\n lazy: true,\n });\n\n await demuxWorker.sendStream(task.stream, {\n sessionId: task.sessionId,\n ...task.metadata,\n ...(task.range && { range: task.range }),\n ...(task.trackId && { trackId: task.trackId }),\n });\n }\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 if (this.orchestrator) {\n this.eventBus?.emit(MeframeEvent.ResourceStageChange, {\n type: MeframeEvent.ResourceStageChange,\n resourceId,\n oldState,\n newState: state,\n });\n }\n }\n\n this.onStateChange?.(resourceId, state);\n }\n\n async fetch(resourceId?: string, options?: ResourceLoadOptions): Promise<void> {\n if (!resourceId) {\n return;\n }\n\n const resource = this.model?.resources.get(resourceId);\n if (!resource) {\n console.warn(`Resource ${resourceId} not found in model`);\n return;\n }\n\n this.enqueueLoad(resource, options?.priority || 'normal', options?.sessionId, options?.trackId);\n }\n\n cancel(resourceId: string): void {\n this.taskManager.cancelTask(resourceId);\n this.processQueue();\n }\n\n pause(resourceId: string): void {\n const task = this.taskManager.getActiveTask(resourceId);\n if (task) {\n task.controller?.abort();\n }\n }\n\n async resume(resourceId: string, options?: ResourceLoadOptions): Promise<void> {\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n throw new Error(`Resource ${resourceId} not found`);\n }\n\n const pausedTask = this.taskManager.getActiveTask(resourceId);\n\n if (pausedTask?.pausedAt !== undefined) {\n this.enqueueLoad(\n resource,\n options?.priority || 'normal',\n options?.sessionId,\n options?.trackId\n );\n } else {\n await this.fetch(resourceId, options);\n }\n }\n\n get activeTasks(): Map<string, LoadTask> {\n return this.taskManager.activeTasks;\n }\n\n get taskQueue(): LoadTask[] {\n return this.taskManager.taskQueue;\n }\n\n dispose(): void {\n this.taskManager.clear();\n this.byteRangeResolver.dispose();\n this.unbind();\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ResourceLoader.js","sources":["../../../src/stages/load/ResourceLoader.ts"],"sourcesContent":["import type { Resource, CompositionModel } from '../../model';\nimport type { Orchestrator, ResourceLoadOptions, LoadTask, ResourceLoaderOptions } from './types';\nimport { TaskManager } from './TaskManager';\nimport { StreamFactory } from './StreamFactory';\nimport { EventHandlers } from './EventHandlers';\nimport { EventPayloadMap, MeframeEvent } from '../../event/events';\nimport { EventBus } from '../../event/EventBus';\nimport { WindowByteRangeResolver } from './WindowByteRangeResolver';\n\nexport class ResourceLoader {\n private orchestrator?: Orchestrator;\n private model?: CompositionModel;\n private taskManager: TaskManager;\n private streamFactory: StreamFactory;\n private eventHandlers?: EventHandlers;\n private eventBus?: EventBus<EventPayloadMap>;\n private onStateChange?: (resourceId: string, state: Resource['state']) => void;\n private byteRangeResolver: WindowByteRangeResolver;\n\n constructor(options?: ResourceLoaderOptions) {\n const maxConcurrent = options?.config?.maxConcurrent ?? 2;\n this.taskManager = new TaskManager(maxConcurrent);\n this.streamFactory = new StreamFactory(options?.onProgress, options?.config);\n this.eventBus = options?.eventBus;\n this.onStateChange = options?.onStateChange;\n this.byteRangeResolver = new WindowByteRangeResolver();\n\n if (options?.orchestrator) {\n this.bind(options.orchestrator);\n }\n }\n\n /**\n * Bind to Orchestrator event system\n */\n bind(orchestrator: Orchestrator): void {\n this.unbind();\n this.orchestrator = orchestrator;\n\n this.eventHandlers = new EventHandlers(\n orchestrator,\n (resourceId) => this.cancel(resourceId),\n (model) => this.handleModelSet(model)\n );\n }\n\n /**\n * Unbind from Orchestrator\n */\n unbind(): void {\n this.eventHandlers?.dispose();\n this.eventHandlers = undefined;\n this.orchestrator = undefined;\n }\n\n private handleModelSet(model: CompositionModel): void {\n this.model = model;\n }\n\n private enqueueLoad(\n resource: Resource,\n priority: 'high' | 'normal' | 'low' = 'normal',\n sessionId?: string,\n trackId?: string\n ): void {\n if (this.taskManager.hasActiveTask(resource.id)) {\n return;\n }\n\n this.taskManager.enqueue(resource, priority, sessionId, trackId);\n this.processQueue();\n }\n\n private processQueue(): void {\n while (this.taskManager.canProcess) {\n const task = this.taskManager.getNextTask();\n if (!task) break;\n this.startLoad(task);\n }\n }\n\n /**\n * Start loading a resource\n * Handles state management (loading → ready/error) for all resource types\n */\n private async startLoad(task: LoadTask): Promise<void> {\n this.taskManager.activateTask(task);\n\n try {\n this.updateResourceState(task.resourceId, 'loading');\n task.controller = new AbortController();\n\n // Route to different handlers based on resource type\n // Note: Each handler only deals with data, state is managed here\n if (task.resource.type === 'image') {\n await this.loadImageBitmap(task);\n } else if (task.resource.type === 'video' || task.resource.type === 'audio') {\n const stream = await this.streamFactory.createRegularStream(task);\n if (!stream) {\n throw new Error(`Failed to create stream for ${task.resourceId}`);\n }\n task.stream = stream;\n await this.transferToDemuxWorker(task);\n } else if (task.resource.type === 'json' || task.resource.type === 'text') {\n await this.loadTextResource(task);\n }\n\n // Unified state update for all resource types\n this.updateResourceState(task.resourceId, 'ready');\n } catch (error) {\n task.error = error as Error;\n this.updateResourceState(task.resourceId, 'error');\n } finally {\n this.taskManager.completeTask(task.resourceId);\n this.processQueue();\n }\n }\n\n /**\n * Load text-based resources (json, text)\n * Just download the content - state management is handled by startLoad()\n */\n private async loadTextResource(task: LoadTask): Promise<void> {\n await this.fetchBlob(task.resource.uri, task.controller!.signal);\n // For json/text, just verify we can download it\n // Future: could parse and validate json here if needed\n }\n\n /**\n * Load image resource: fetch blob → create ImageBitmap → transfer to worker\n * Note: Images don't need streaming (typically < 5MB)\n */\n private async loadImageBitmap(task: LoadTask): Promise<void> {\n const blob = await this.fetchBlob(task.resource.uri, task.controller!.signal);\n\n // Create ImageBitmap in main thread, then transfer to worker\n // Note: In Worker context, ImageBitmap is the only way to create VideoFrame from image data\n const imageBitmap = await createImageBitmap(blob, {\n premultiplyAlpha: 'premultiply',\n colorSpaceConversion: 'default',\n imageOrientation: 'from-image',\n });\n\n await this.transferImageToWorker(task, imageBitmap);\n }\n\n /**\n * Fetch resource as blob (for images, json, etc.)\n */\n private async fetchBlob(uri: string, signal: AbortSignal): Promise<Blob> {\n const response = await fetch(uri, { signal });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return response.blob();\n }\n\n /**\n * Transfer ImageBitmap to VideoComposeWorker\n */\n private async transferImageToWorker(task: LoadTask, imageBitmap: ImageBitmap): Promise<void> {\n if (!this.orchestrator) return;\n\n if (!task.sessionId) {\n throw new Error(\n `[ResourceLoader] sessionId required for resource ${task.resourceId}. ` +\n `In Clip-based architecture, use fetch(resourceId, { sessionId })`\n );\n }\n\n const composeWorker = await this.orchestrator.workers.get('videoCompose', task.sessionId, {\n lazy: true,\n });\n\n await composeWorker.send(\n 'receive_image',\n {\n resourceId: task.resourceId,\n sessionId: task.sessionId,\n imageBitmap,\n },\n { transfer: [imageBitmap] }\n );\n }\n\n private async transferToDemuxWorker(task: LoadTask): Promise<void> {\n if (!task.stream || !this.orchestrator) return;\n\n if (!task.sessionId) {\n throw new Error(\n `[ResourceLoader] sessionId required for resource ${task.resourceId}. ` +\n `In Clip-based architecture, use fetch(resourceId, { sessionId })`\n );\n }\n\n const workerType = task.resource.type === 'video' ? 'videoDemux' : 'audioDemux';\n const demuxWorker = await this.orchestrator.workers.get(workerType, task.sessionId, {\n lazy: true,\n });\n\n await demuxWorker.sendStream(task.stream, {\n sessionId: task.sessionId,\n ...task.metadata,\n ...(task.range && { range: task.range }),\n ...(task.trackId && { trackId: task.trackId }),\n });\n }\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 if (this.orchestrator) {\n this.eventBus?.emit(MeframeEvent.ResourceStageChange, {\n type: MeframeEvent.ResourceStageChange,\n resourceId,\n oldState,\n newState: state,\n });\n }\n }\n\n this.onStateChange?.(resourceId, state);\n }\n\n async fetch(resourceId?: string, options?: ResourceLoadOptions): Promise<void> {\n if (!resourceId) {\n return;\n }\n\n const resource = this.model?.resources.get(resourceId);\n if (!resource) {\n console.warn(`Resource ${resourceId} not found in model`);\n return;\n }\n\n this.enqueueLoad(resource, options?.priority || 'normal', options?.sessionId, options?.trackId);\n }\n\n cancel(resourceId: string): void {\n this.taskManager.cancelTask(resourceId);\n this.processQueue();\n }\n\n pause(resourceId: string): void {\n const task = this.taskManager.getActiveTask(resourceId);\n if (task) {\n task.controller?.abort();\n }\n }\n\n async resume(resourceId: string, options?: ResourceLoadOptions): Promise<void> {\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n throw new Error(`Resource ${resourceId} not found`);\n }\n\n const pausedTask = this.taskManager.getActiveTask(resourceId);\n\n if (pausedTask?.pausedAt !== undefined) {\n this.enqueueLoad(\n resource,\n options?.priority || 'normal',\n options?.sessionId,\n options?.trackId\n );\n } else {\n await this.fetch(resourceId, options);\n }\n }\n\n get activeTasks(): Map<string, LoadTask> {\n return this.taskManager.activeTasks;\n }\n\n get taskQueue(): LoadTask[] {\n return this.taskManager.taskQueue;\n }\n\n dispose(): void {\n this.taskManager.clear();\n this.byteRangeResolver.dispose();\n this.unbind();\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"WorkerPool.d.ts","sourceRoot":"","sources":["../../src/worker/WorkerPool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAA0B,MAAM,SAAS,CAAC;AAEhF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACxC,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAcD,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAiC;IAC7C,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAS;gBAEpB,MAAM,EAAE,gBAAgB;IAOpC;;OAEG;IACH,OAAO,CAAC,YAAY;IAmBpB;;;;;;OAMG;IACG,GAAG,CACP,IAAI,EAAE,UAAU,EAChB,EAAE,CAAC,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GACrD,OAAO,CAAC,UAAU,CAAC;IAsChB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB7D;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAU9C;;OAEG;IACH,YAAY,IAAI,IAAI;IAOpB;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC,CAQrD;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,MAAM,EAAE,CAE5B;IAED;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;CAI5C"}
1
+ {"version":3,"file":"WorkerPool.d.ts","sourceRoot":"","sources":["../../src/worker/WorkerPool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAA0B,MAAM,SAAS,CAAC;AAEhF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACxC,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAeD,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAiC;IAC7C,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAS;gBAEpB,MAAM,EAAE,gBAAgB;IAOpC;;OAEG;IACH,OAAO,CAAC,YAAY;IAsBpB;;;;;;OAMG;IACG,GAAG,CACP,IAAI,EAAE,UAAU,EAChB,EAAE,CAAC,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,GACrD,OAAO,CAAC,UAAU,CAAC;IAsChB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB7D;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAU9C;;OAEG;IACH,YAAY,IAAI,IAAI;IAOpB;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC,CAQrD;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,MAAM,EAAE,CAE5B;IAED;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;CAI5C"}
@@ -3,10 +3,11 @@ import { WorkerMessageType } from "./types.js";
3
3
  const WORKER_FILE_NAMES = {
4
4
  videoDemux: "video-demux",
5
5
  audioDemux: "audio-demux",
6
- decode: "decode",
6
+ videoDecode: "video-decode",
7
+ audioDecode: "audio-decode",
7
8
  videoCompose: "video-compose",
8
9
  audioCompose: "audio-compose",
9
- encode: "encode"
10
+ videoEncode: "video-encode"
10
11
  };
11
12
  class WorkerPool {
12
13
  pool = /* @__PURE__ */ new Map();
@@ -28,9 +29,12 @@ class WorkerPool {
28
29
  const stageMap = {
29
30
  "video-demux": "demux",
30
31
  "audio-demux": "demux",
32
+ "video-decode": "decode",
33
+ "audio-decode": "decode",
31
34
  decode: "decode",
32
35
  "video-compose": "compose",
33
36
  "audio-compose": "compose",
37
+ "video-encode": "encode",
34
38
  encode: "encode"
35
39
  };
36
40
  const stage = stageMap[fileName];
@@ -1 +1 @@
1
- {"version":3,"file":"WorkerPool.js","sources":["../../src/worker/WorkerPool.ts"],"sourcesContent":["/**\n * WorkerPool: Manages worker instances with key-based access\n * Provides lazy creation and unified management of workers\n */\n\nimport { BaseWorker } from './BaseWorker';\nimport type { WorkerType, WorkerStatus, WorkerConfigurePayload } from './types';\nimport { WorkerMessageType } from './types';\nimport type { EventBus } from '../event/EventBus';\nimport type { EventPayloadMap } from '../event/events';\n\nexport interface WorkerPoolConfig {\n eventBus: EventBus<EventPayloadMap>;\n workerConfigs?: Record<WorkerType, any>;\n /** Worker files base path (default: '/meframe-workers') */\n workerPath?: string;\n workerExtension?: string;\n}\n\n/**\n * Worker name mapping (worker type -> file name)\n */\nconst WORKER_FILE_NAMES: Record<WorkerType, string> = {\n videoDemux: 'video-demux',\n audioDemux: 'audio-demux',\n decode: 'decode',\n videoCompose: 'video-compose',\n audioCompose: 'audio-compose',\n encode: 'encode',\n};\n\nexport class WorkerPool {\n private pool = new Map<string, BaseWorker>();\n private eventBus: EventBus<EventPayloadMap>;\n private workerConfigs: Record<string, any>;\n private workerPath: string;\n private workerExtension: string;\n\n constructor(config: WorkerPoolConfig) {\n this.eventBus = config.eventBus;\n this.workerConfigs = config.workerConfigs || {};\n this.workerPath = config.workerPath || '/meframe-workers';\n this.workerExtension = config.workerExtension || '.js';\n }\n\n /**\n * Get worker URL for a specific worker type\n */\n private getWorkerUrl(type: WorkerType): string {\n const fileName = WORKER_FILE_NAMES[type];\n\n // Map worker type to its stage directory\n const stageMap: Record<string, string> = {\n 'video-demux': 'demux',\n 'audio-demux': 'demux',\n decode: 'decode',\n 'video-compose': 'compose',\n 'audio-compose': 'compose',\n encode: 'encode',\n };\n const stage = stageMap[fileName];\n\n // Workers are in stages subdirectory\n const url = `${this.workerPath}/stages/${stage}/${fileName}.worker${this.workerExtension}`;\n return url;\n }\n\n /**\n * Get or create a worker instance\n * @param type - Worker type\n * @param id - Optional ID for per-resource or per-clip workers\n * @param options - Optional configuration\n * - lazy: If true, skip initial configure (default: false)\n */\n async get(\n type: WorkerType,\n id?: string,\n options?: { lazy?: boolean; skipInitialize?: boolean }\n ): Promise<BaseWorker> {\n const key = id ? `${type}#${id}` : type;\n\n const existing = this.pool.get(key);\n if (!existing) {\n // Generate worker URL based on worker path and type\n const url = this.getWorkerUrl(type);\n\n const worker = new BaseWorker({\n type,\n url,\n eventBus: this.eventBus,\n clipId: id,\n });\n\n // Only initialize if not in lazy mode\n if (!options?.lazy && !options?.skipInitialize) {\n const config = this.workerConfigs[type];\n await worker.initialize(config);\n }\n\n this.pool.set(key, worker);\n return worker;\n }\n\n if (!options?.lazy && !options?.skipInitialize) {\n const config = this.workerConfigs[type];\n if (config) {\n await existing.send(WorkerMessageType.Configure, {\n config,\n initial: false,\n } as WorkerConfigurePayload);\n }\n }\n\n return existing;\n }\n\n async setConfig(type: WorkerType, config: any): Promise<void> {\n const existing = this.workerConfigs[type] || {};\n const mergedConfig = { ...existing, ...config };\n\n this.workerConfigs[type] = mergedConfig;\n\n for (const [key, worker] of this.pool.entries()) {\n if (key === type || key.startsWith(`${type}#`)) {\n await worker.send(WorkerMessageType.Configure, {\n config: mergedConfig,\n initial: false,\n } as WorkerConfigurePayload);\n }\n }\n }\n\n /**\n * Terminate a specific worker\n */\n terminate(type: WorkerType, id?: string): void {\n const key = id ? `${type}#${id}` : type;\n const worker = this.pool.get(key);\n\n if (worker) {\n worker.terminate();\n this.pool.delete(key);\n }\n }\n\n /**\n * Terminate all workers\n */\n terminateAll(): void {\n for (const worker of this.pool.values()) {\n worker.terminate();\n }\n this.pool.clear();\n }\n\n /**\n * Get status of all workers\n */\n get status(): Record<string, WorkerStatus[WorkerType]> {\n const result: Record<string, WorkerStatus[WorkerType]> = {};\n\n for (const [key, worker] of this.pool.entries()) {\n result[key] = worker.status;\n }\n\n return result;\n }\n\n /**\n * Get list of active worker keys\n */\n get activeWorkers(): string[] {\n return Array.from(this.pool.keys());\n }\n\n /**\n * Check if a worker exists\n */\n has(type: WorkerType, id?: string): boolean {\n const key = id ? `${type}#${id}` : type;\n return this.pool.has(key);\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WorkerPool.js","sources":["../../src/worker/WorkerPool.ts"],"sourcesContent":["/**\n * WorkerPool: Manages worker instances with key-based access\n * Provides lazy creation and unified management of workers\n */\n\nimport { BaseWorker } from './BaseWorker';\nimport type { WorkerType, WorkerStatus, WorkerConfigurePayload } from './types';\nimport { WorkerMessageType } from './types';\nimport type { EventBus } from '../event/EventBus';\nimport type { EventPayloadMap } from '../event/events';\n\nexport interface WorkerPoolConfig {\n eventBus: EventBus<EventPayloadMap>;\n workerConfigs?: Record<WorkerType, any>;\n /** Worker files base path (default: '/meframe-workers') */\n workerPath?: string;\n workerExtension?: string;\n}\n\n/**\n * Worker name mapping (worker type -> file name)\n */\nconst WORKER_FILE_NAMES: Record<WorkerType, string> = {\n videoDemux: 'video-demux',\n audioDemux: 'audio-demux',\n videoDecode: 'video-decode',\n audioDecode: 'audio-decode',\n videoCompose: 'video-compose',\n audioCompose: 'audio-compose',\n videoEncode: 'video-encode',\n};\n\nexport class WorkerPool {\n private pool = new Map<string, BaseWorker>();\n private eventBus: EventBus<EventPayloadMap>;\n private workerConfigs: Record<string, any>;\n private workerPath: string;\n private workerExtension: string;\n\n constructor(config: WorkerPoolConfig) {\n this.eventBus = config.eventBus;\n this.workerConfigs = config.workerConfigs || {};\n this.workerPath = config.workerPath || '/meframe-workers';\n this.workerExtension = config.workerExtension || '.js';\n }\n\n /**\n * Get worker URL for a specific worker type\n */\n private getWorkerUrl(type: WorkerType): string {\n const fileName = WORKER_FILE_NAMES[type];\n\n // Map worker type to its stage directory\n const stageMap: Record<string, string> = {\n 'video-demux': 'demux',\n 'audio-demux': 'demux',\n 'video-decode': 'decode',\n 'audio-decode': 'decode',\n decode: 'decode',\n 'video-compose': 'compose',\n 'audio-compose': 'compose',\n 'video-encode': 'encode',\n encode: 'encode',\n };\n const stage = stageMap[fileName];\n\n // Workers are in stages subdirectory\n const url = `${this.workerPath}/stages/${stage}/${fileName}.worker${this.workerExtension}`;\n return url;\n }\n\n /**\n * Get or create a worker instance\n * @param type - Worker type\n * @param id - Optional ID for per-resource or per-clip workers\n * @param options - Optional configuration\n * - lazy: If true, skip initial configure (default: false)\n */\n async get(\n type: WorkerType,\n id?: string,\n options?: { lazy?: boolean; skipInitialize?: boolean }\n ): Promise<BaseWorker> {\n const key = id ? `${type}#${id}` : type;\n\n const existing = this.pool.get(key);\n if (!existing) {\n // Generate worker URL based on worker path and type\n const url = this.getWorkerUrl(type);\n\n const worker = new BaseWorker({\n type,\n url,\n eventBus: this.eventBus,\n clipId: id,\n });\n\n // Only initialize if not in lazy mode\n if (!options?.lazy && !options?.skipInitialize) {\n const config = this.workerConfigs[type];\n await worker.initialize(config);\n }\n\n this.pool.set(key, worker);\n return worker;\n }\n\n if (!options?.lazy && !options?.skipInitialize) {\n const config = this.workerConfigs[type];\n if (config) {\n await existing.send(WorkerMessageType.Configure, {\n config,\n initial: false,\n } as WorkerConfigurePayload);\n }\n }\n\n return existing;\n }\n\n async setConfig(type: WorkerType, config: any): Promise<void> {\n const existing = this.workerConfigs[type] || {};\n const mergedConfig = { ...existing, ...config };\n\n this.workerConfigs[type] = mergedConfig;\n\n for (const [key, worker] of this.pool.entries()) {\n if (key === type || key.startsWith(`${type}#`)) {\n await worker.send(WorkerMessageType.Configure, {\n config: mergedConfig,\n initial: false,\n } as WorkerConfigurePayload);\n }\n }\n }\n\n /**\n * Terminate a specific worker\n */\n terminate(type: WorkerType, id?: string): void {\n const key = id ? `${type}#${id}` : type;\n const worker = this.pool.get(key);\n\n if (worker) {\n worker.terminate();\n this.pool.delete(key);\n }\n }\n\n /**\n * Terminate all workers\n */\n terminateAll(): void {\n for (const worker of this.pool.values()) {\n worker.terminate();\n }\n this.pool.clear();\n }\n\n /**\n * Get status of all workers\n */\n get status(): Record<string, WorkerStatus[WorkerType]> {\n const result: Record<string, WorkerStatus[WorkerType]> = {};\n\n for (const [key, worker] of this.pool.entries()) {\n result[key] = worker.status;\n }\n\n return result;\n }\n\n /**\n * Get list of active worker keys\n */\n get activeWorkers(): string[] {\n return Array.from(this.pool.keys());\n }\n\n /**\n * Check if a worker exists\n */\n has(type: WorkerType, id?: string): boolean {\n const key = id ? `${type}#${id}` : type;\n return this.pool.has(key);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@ export type PortLike = MessagePort | DedicatedWorkerGlobalScope | Worker;
6
6
  /**
7
7
  * Worker types in the pipeline
8
8
  */
9
- export type WorkerType = 'videoDemux' | 'audioDemux' | 'decode' | 'videoCompose' | 'audioCompose' | 'encode';
9
+ export type WorkerType = 'videoDemux' | 'audioDemux' | 'videoDecode' | 'audioDecode' | 'videoCompose' | 'audioCompose' | 'videoEncode';
10
10
  export interface WorkerConfigurePayload<T = any> {
11
11
  config: T;
12
12
  initial: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/worker/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/worker/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,0BAA0B,GAAG,MAAM,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,UAAU,GAClB,YAAY,GACZ,YAAY,GACZ,aAAa,GACb,aAAa,GACb,cAAc,GACd,cAAc,GACd,aAAa,CAAC;AAElB,MAAM,WAAW,sBAAsB,CAAC,CAAC,GAAG,GAAG;IAC7C,MAAM,EAAE,CAAC,CAAC;IACV,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,YAAY,CAAC;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAEhC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAC3D,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC;IACtD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC;CACxD;AAGD,oBAAY,iBAAiB;IAE3B,KAAK,UAAU;IACf,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,YAAY,kBAAkB;IAC9B,cAAc,oBAAoB;IAClC,gBAAgB,sBAAsB;IACtC,cAAc,oBAAoB;IAClC,YAAY,kBAAkB;IAC9B,YAAY,kBAAkB;IAC9B,UAAU,gBAAgB;IAC1B,eAAe,qBAAqB;IACpC,WAAW,iBAAiB;IAC5B,YAAY,kBAAkB;IAC9B,OAAO,aAAa;IACpB,cAAc,oBAAoB;IAClC,UAAU,gBAAgB;IAC1B,WAAW,iBAAiB;IAC5B,iBAAiB,wBAAwB;IACzC,eAAe,qBAAqB;IACpC,WAAW,iBAAiB;IAC5B,WAAW,iBAAiB;IAC5B,YAAY,kBAAkB;IAC9B,WAAW,iBAAiB;IAC5B,QAAQ,cAAc;IACtB,gBAAgB,sBAAsB;IACtC,YAAY,iBAAiB;IAC7B,aAAa,oBAAoB;IACjC,gBAAgB,uBAAuB;IACvC,gBAAgB,uBAAuB;CACxC;AAGD,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,GAAG;IACpC,IAAI,EAAE,iBAAiB,CAAC;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,CAAC,CAAC;IACZ,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,GAAG;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,CAAC;IACX,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAGD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,MAAM,cAAc,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,IAAI,CAC7C,OAAO,EAAE,CAAC,EACV,QAAQ,CAAC,EAAE,YAAY,EAAE,KACtB,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAGpB,MAAM,WAAW,eAAe;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,UAAU,CAAC;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,UAAU,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,CAAC;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAGD,oBAAY,WAAW;IACrB,IAAI,SAAS;IACb,YAAY,iBAAiB;IAC7B,KAAK,UAAU;IACf,UAAU,eAAe;IACzB,KAAK,UAAU;IACf,QAAQ,aAAa;CACtB;AAGD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAGD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,iBAAiB,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC9B,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,MAAM,WAAW,YAAY;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAGD,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAGD,oBAAY,eAAe;IACzB,GAAG,IAAI;IACP,MAAM,IAAI;IACV,IAAI,IAAI;IACR,QAAQ,IAAI;CACb;AAGD,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;IAChE,QAAQ,EAAE,eAAe,CAAC;CAC3B"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sources":["../../src/worker/types.ts"],"sourcesContent":["/**\n * Worker Communication Protocol Types\n * Define standard message types and protocols for worker communication\n */\n\n// A port-like object that supports postMessage/onmessage used by WorkerChannel\nexport type PortLike = MessagePort | DedicatedWorkerGlobalScope | Worker;\n\n/**\n * Worker types in the pipeline\n */\nexport type WorkerType =\n | 'videoDemux'\n | 'audioDemux'\n | 'decode'\n | 'videoCompose'\n | 'audioCompose'\n | 'encode';\n\nexport interface WorkerConfigurePayload<T = any> {\n config: T;\n initial: boolean;\n}\n\n/**\n * Worker state definition\n */\nexport interface WorkerStateInfo {\n state: 'idle' | 'busy' | 'error' | 'terminated';\n taskCount: number;\n lastError?: string;\n}\n\n/**\n * Worker status for all workers\n */\nexport type WorkerStatus = Record<WorkerType, WorkerStateInfo>;\n\n/**\n * Worker wrapper interface\n */\nexport interface IWorker {\n readonly type: WorkerType;\n readonly state: WorkerStateInfo;\n\n initialize(): Promise<void>;\n terminate(): Promise<void>;\n restart(): Promise<void>;\n\n postMessage(message: any, transfer?: Transferable[]): void;\n on(event: string, handler: (data: any) => void): void;\n off(event: string, handler: (data: any) => void): void;\n}\n\n// Message types for different workers\nexport enum WorkerMessageType {\n // Init = 'init', // configure, { initial: true }\n Ready = 'ready',\n Error = 'error',\n Dispose = 'dispose',\n Configure = 'configure',\n LoadResource = 'load_resource',\n ResourceLoaded = 'resource_loaded',\n ResourceProgress = 'resource_progress',\n ConfigureDemux = 'configure_demux',\n AppendBuffer = 'append_buffer',\n DemuxSamples = 'demux_samples',\n FlushDemux = 'flush_demux',\n ConfigureDecode = 'configure_decode',\n DecodeChunk = 'decode_chunk',\n DecodedFrame = 'decoded_frame',\n SeekGop = 'seek_gop',\n SetComposition = 'set_composition',\n ApplyPatch = 'apply_patch',\n RenderFrame = 'render_frame',\n ComposeFrameReady = 'compose_frame_ready',\n ConfigureEncode = 'configure_encode',\n EncodeFrame = 'encode_frame',\n EncodeAudio = 'encode_audio',\n EncodedChunk = 'encoded_chunk',\n FlushEncode = 'flush_encode',\n AddChunk = 'add_chunk',\n PerformanceStats = 'performance_stats',\n RenderWindow = 'renderWindow',\n AudioTrackAdd = 'audio_track:add',\n AudioTrackRemove = 'audio_track:remove',\n AudioTrackUpdate = 'audio_track:update',\n}\n\n// Base message structure\nexport interface WorkerMessage<T = any> {\n type: WorkerMessageType;\n id: string; // Unique message ID for request-response pairing\n payload?: T;\n transfer?: Transferable[]; // Objects to transfer ownership\n timestamp: number;\n}\n\n// Response message structure\nexport interface WorkerResponse<T = any> {\n id: string; // Matches request ID\n success: boolean;\n result?: T;\n error?: WorkerError;\n timestamp: number;\n}\n\n// Error structure\nexport interface WorkerError {\n code: string;\n message: string;\n details?: any;\n stack?: string;\n}\n\n// Worker configuration\nexport interface WorkerConfig {\n name: string;\n workerUrl?: string;\n capabilities?: string[];\n maxRetries?: number;\n timeout?: number;\n}\n\n// Message handler type\nexport type MessageHandler<T = any, R = any> = (\n payload: T,\n transfer?: Transferable[]\n) => Promise<R> | R;\n\n// Message handler registry\nexport interface MessageHandlers {\n [key: string]: MessageHandler;\n}\n\n/**\n * Unified stream connection message for the data plane\n * Used by the stream-pipeline refactor to reduce message surface\n */\nexport interface ConnectMsg {\n direction: 'upstream';\n port: MessagePort;\n streamType: 'video' | 'audio' | 'frame' | 'chunk';\n clipId?: string;\n trackId?: string;\n}\n\n// Transferable types used in workers\nexport interface TransferableFrame {\n frame: VideoFrame;\n timestamp: number;\n duration?: number;\n}\n\nexport interface TransferableChunk {\n chunk: EncodedVideoChunk | EncodedAudioChunk;\n timestamp: number;\n duration?: number;\n trackId: string;\n}\n\nexport interface TransferableBuffer {\n buffer: ArrayBuffer;\n offset?: number;\n length?: number;\n}\n\n// Worker state\nexport enum WorkerState {\n Idle = 'idle',\n Initializing = 'initializing',\n Ready = 'ready',\n Processing = 'processing',\n Error = 'error',\n Disposed = 'disposed',\n}\n\n// Performance metrics\nexport interface WorkerPerformanceStats {\n workerId: string;\n workerName: string;\n messagesProcessed: number;\n averageProcessingTime: number;\n maxProcessingTime: number;\n errorCount: number;\n queueSize: number;\n memoryUsage?: number;\n}\n\n// Request tracking\nexport interface PendingRequest {\n id: string;\n type: WorkerMessageType;\n timestamp: number;\n timeout?: number;\n resolve: (value: any) => void;\n reject: (error: any) => void;\n retryCount?: number;\n maxRetries?: number;\n}\n\n// Stream support\nexport interface StreamConfig {\n highWaterMark?: number;\n strategy?: QueuingStrategy;\n}\n\n// Worker pool settings\nexport interface WorkerPoolSettings {\n maxWorkers?: number;\n workerIdleTimeout?: number;\n enableSharedArrayBuffer?: boolean;\n enableOffscreenCanvas?: boolean;\n}\n\n// Message priority levels\nexport enum MessagePriority {\n Low = 0,\n Normal = 1,\n High = 2,\n Critical = 3,\n}\n\n// Priority message\nexport interface PriorityMessage<T = any> extends WorkerMessage<T> {\n priority: MessagePriority;\n}\n"],"names":["WorkerMessageType","WorkerState"],"mappings":"AAuDO,IAAK,sCAAAA,uBAAL;AAELA,qBAAA,OAAA,IAAQ;AACRA,qBAAA,OAAA,IAAQ;AACRA,qBAAA,SAAA,IAAU;AACVA,qBAAA,WAAA,IAAY;AACZA,qBAAA,cAAA,IAAe;AACfA,qBAAA,gBAAA,IAAiB;AACjBA,qBAAA,kBAAA,IAAmB;AACnBA,qBAAA,gBAAA,IAAiB;AACjBA,qBAAA,cAAA,IAAe;AACfA,qBAAA,cAAA,IAAe;AACfA,qBAAA,YAAA,IAAa;AACbA,qBAAA,iBAAA,IAAkB;AAClBA,qBAAA,aAAA,IAAc;AACdA,qBAAA,cAAA,IAAe;AACfA,qBAAA,SAAA,IAAU;AACVA,qBAAA,gBAAA,IAAiB;AACjBA,qBAAA,YAAA,IAAa;AACbA,qBAAA,aAAA,IAAc;AACdA,qBAAA,mBAAA,IAAoB;AACpBA,qBAAA,iBAAA,IAAkB;AAClBA,qBAAA,aAAA,IAAc;AACdA,qBAAA,aAAA,IAAc;AACdA,qBAAA,cAAA,IAAe;AACfA,qBAAA,aAAA,IAAc;AACdA,qBAAA,UAAA,IAAW;AACXA,qBAAA,kBAAA,IAAmB;AACnBA,qBAAA,cAAA,IAAe;AACfA,qBAAA,eAAA,IAAgB;AAChBA,qBAAA,kBAAA,IAAmB;AACnBA,qBAAA,kBAAA,IAAmB;AA/BT,SAAAA;AAAA,GAAA,qBAAA,CAAA,CAAA;AAiHL,IAAK,gCAAAC,iBAAL;AACLA,eAAA,MAAA,IAAO;AACPA,eAAA,cAAA,IAAe;AACfA,eAAA,OAAA,IAAQ;AACRA,eAAA,YAAA,IAAa;AACbA,eAAA,OAAA,IAAQ;AACRA,eAAA,UAAA,IAAW;AAND,SAAAA;AAAA,GAAA,eAAA,CAAA,CAAA;"}
1
+ {"version":3,"file":"types.js","sources":["../../src/worker/types.ts"],"sourcesContent":["/**\n * Worker Communication Protocol Types\n * Define standard message types and protocols for worker communication\n */\n\n// A port-like object that supports postMessage/onmessage used by WorkerChannel\nexport type PortLike = MessagePort | DedicatedWorkerGlobalScope | Worker;\n\n/**\n * Worker types in the pipeline\n */\nexport type WorkerType =\n | 'videoDemux'\n | 'audioDemux'\n | 'videoDecode' // Clip-local video decoder\n | 'audioDecode' // Clip-local audio decoder\n | 'videoCompose'\n | 'audioCompose'\n | 'videoEncode'; // Clip-local video encoder (L2 only)\n\nexport interface WorkerConfigurePayload<T = any> {\n config: T;\n initial: boolean;\n}\n\n/**\n * Worker state definition\n */\nexport interface WorkerStateInfo {\n state: 'idle' | 'busy' | 'error' | 'terminated';\n taskCount: number;\n lastError?: string;\n}\n\n/**\n * Worker status for all workers\n */\nexport type WorkerStatus = Record<WorkerType, WorkerStateInfo>;\n\n/**\n * Worker wrapper interface\n */\nexport interface IWorker {\n readonly type: WorkerType;\n readonly state: WorkerStateInfo;\n\n initialize(): Promise<void>;\n terminate(): Promise<void>;\n restart(): Promise<void>;\n\n postMessage(message: any, transfer?: Transferable[]): void;\n on(event: string, handler: (data: any) => void): void;\n off(event: string, handler: (data: any) => void): void;\n}\n\n// Message types for different workers\nexport enum WorkerMessageType {\n // Init = 'init', // configure, { initial: true }\n Ready = 'ready',\n Error = 'error',\n Dispose = 'dispose',\n Configure = 'configure',\n LoadResource = 'load_resource',\n ResourceLoaded = 'resource_loaded',\n ResourceProgress = 'resource_progress',\n ConfigureDemux = 'configure_demux',\n AppendBuffer = 'append_buffer',\n DemuxSamples = 'demux_samples',\n FlushDemux = 'flush_demux',\n ConfigureDecode = 'configure_decode',\n DecodeChunk = 'decode_chunk',\n DecodedFrame = 'decoded_frame',\n SeekGop = 'seek_gop',\n SetComposition = 'set_composition',\n ApplyPatch = 'apply_patch',\n RenderFrame = 'render_frame',\n ComposeFrameReady = 'compose_frame_ready',\n ConfigureEncode = 'configure_encode',\n EncodeFrame = 'encode_frame',\n EncodeAudio = 'encode_audio',\n EncodedChunk = 'encoded_chunk',\n FlushEncode = 'flush_encode',\n AddChunk = 'add_chunk',\n PerformanceStats = 'performance_stats',\n RenderWindow = 'renderWindow',\n AudioTrackAdd = 'audio_track:add',\n AudioTrackRemove = 'audio_track:remove',\n AudioTrackUpdate = 'audio_track:update',\n}\n\n// Base message structure\nexport interface WorkerMessage<T = any> {\n type: WorkerMessageType;\n id: string; // Unique message ID for request-response pairing\n payload?: T;\n transfer?: Transferable[]; // Objects to transfer ownership\n timestamp: number;\n}\n\n// Response message structure\nexport interface WorkerResponse<T = any> {\n id: string; // Matches request ID\n success: boolean;\n result?: T;\n error?: WorkerError;\n timestamp: number;\n}\n\n// Error structure\nexport interface WorkerError {\n code: string;\n message: string;\n details?: any;\n stack?: string;\n}\n\n// Worker configuration\nexport interface WorkerConfig {\n name: string;\n workerUrl?: string;\n capabilities?: string[];\n maxRetries?: number;\n timeout?: number;\n}\n\n// Message handler type\nexport type MessageHandler<T = any, R = any> = (\n payload: T,\n transfer?: Transferable[]\n) => Promise<R> | R;\n\n// Message handler registry\nexport interface MessageHandlers {\n [key: string]: MessageHandler;\n}\n\n/**\n * Unified stream connection message for the data plane\n * Used by the stream-pipeline refactor to reduce message surface\n */\nexport interface ConnectMsg {\n direction: 'upstream';\n port: MessagePort;\n streamType: 'video' | 'audio' | 'frame' | 'chunk';\n clipId?: string;\n trackId?: string;\n}\n\n// Transferable types used in workers\nexport interface TransferableFrame {\n frame: VideoFrame;\n timestamp: number;\n duration?: number;\n}\n\nexport interface TransferableChunk {\n chunk: EncodedVideoChunk | EncodedAudioChunk;\n timestamp: number;\n duration?: number;\n trackId: string;\n}\n\nexport interface TransferableBuffer {\n buffer: ArrayBuffer;\n offset?: number;\n length?: number;\n}\n\n// Worker state\nexport enum WorkerState {\n Idle = 'idle',\n Initializing = 'initializing',\n Ready = 'ready',\n Processing = 'processing',\n Error = 'error',\n Disposed = 'disposed',\n}\n\n// Performance metrics\nexport interface WorkerPerformanceStats {\n workerId: string;\n workerName: string;\n messagesProcessed: number;\n averageProcessingTime: number;\n maxProcessingTime: number;\n errorCount: number;\n queueSize: number;\n memoryUsage?: number;\n}\n\n// Request tracking\nexport interface PendingRequest {\n id: string;\n type: WorkerMessageType;\n timestamp: number;\n timeout?: number;\n resolve: (value: any) => void;\n reject: (error: any) => void;\n retryCount?: number;\n maxRetries?: number;\n}\n\n// Stream support\nexport interface StreamConfig {\n highWaterMark?: number;\n strategy?: QueuingStrategy;\n}\n\n// Worker pool settings\nexport interface WorkerPoolSettings {\n maxWorkers?: number;\n workerIdleTimeout?: number;\n enableSharedArrayBuffer?: boolean;\n enableOffscreenCanvas?: boolean;\n}\n\n// Message priority levels\nexport enum MessagePriority {\n Low = 0,\n Normal = 1,\n High = 2,\n Critical = 3,\n}\n\n// Priority message\nexport interface PriorityMessage<T = any> extends WorkerMessage<T> {\n priority: MessagePriority;\n}\n"],"names":["WorkerMessageType","WorkerState"],"mappings":"AAwDO,IAAK,sCAAAA,uBAAL;AAELA,qBAAA,OAAA,IAAQ;AACRA,qBAAA,OAAA,IAAQ;AACRA,qBAAA,SAAA,IAAU;AACVA,qBAAA,WAAA,IAAY;AACZA,qBAAA,cAAA,IAAe;AACfA,qBAAA,gBAAA,IAAiB;AACjBA,qBAAA,kBAAA,IAAmB;AACnBA,qBAAA,gBAAA,IAAiB;AACjBA,qBAAA,cAAA,IAAe;AACfA,qBAAA,cAAA,IAAe;AACfA,qBAAA,YAAA,IAAa;AACbA,qBAAA,iBAAA,IAAkB;AAClBA,qBAAA,aAAA,IAAc;AACdA,qBAAA,cAAA,IAAe;AACfA,qBAAA,SAAA,IAAU;AACVA,qBAAA,gBAAA,IAAiB;AACjBA,qBAAA,YAAA,IAAa;AACbA,qBAAA,aAAA,IAAc;AACdA,qBAAA,mBAAA,IAAoB;AACpBA,qBAAA,iBAAA,IAAkB;AAClBA,qBAAA,aAAA,IAAc;AACdA,qBAAA,aAAA,IAAc;AACdA,qBAAA,cAAA,IAAe;AACfA,qBAAA,aAAA,IAAc;AACdA,qBAAA,UAAA,IAAW;AACXA,qBAAA,kBAAA,IAAmB;AACnBA,qBAAA,cAAA,IAAe;AACfA,qBAAA,eAAA,IAAgB;AAChBA,qBAAA,kBAAA,IAAmB;AACnBA,qBAAA,kBAAA,IAAmB;AA/BT,SAAAA;AAAA,GAAA,qBAAA,CAAA,CAAA;AAiHL,IAAK,gCAAAC,iBAAL;AACLA,eAAA,MAAA,IAAO;AACPA,eAAA,cAAA,IAAe;AACfA,eAAA,OAAA,IAAQ;AACRA,eAAA,YAAA,IAAa;AACbA,eAAA,OAAA,IAAQ;AACRA,eAAA,UAAA,IAAW;AAND,SAAAA;AAAA,GAAA,eAAA,CAAA,CAAA;"}
@@ -1 +1 @@
1
- {"version":3,"file":"worker-event-whitelist.d.ts","sourceRoot":"","sources":["../../src/worker/worker-event-whitelist.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,UAAU,EAAE,YAAY,EAAE,CAkDrE,CAAC;AAEF;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAGvF;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAKlE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAGhE,CAAC"}
1
+ {"version":3,"file":"worker-event-whitelist.d.ts","sourceRoot":"","sources":["../../src/worker/worker-event-whitelist.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,UAAU,EAAE,YAAY,EAAE,CA0DrE,CAAC;AAEF;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAGvF;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAKlE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAGhE,CAAC"}
@@ -0,0 +1,130 @@
1
+ class BaseDecoder {
2
+ decoder;
3
+ config;
4
+ controller = null;
5
+ constructor(config) {
6
+ this.config = config;
7
+ }
8
+ async initialize() {
9
+ if (this.decoder?.state === "configured") {
10
+ return;
11
+ }
12
+ const isSupported = await this.isConfigSupported(this.config);
13
+ if (!isSupported.supported) {
14
+ throw new Error(
15
+ `Codec not supported: ${this.config.codecString || this.config.codec}`
16
+ );
17
+ }
18
+ this.decoder = this.createDecoder({
19
+ output: this.handleOutput.bind(this),
20
+ error: this.handleError.bind(this)
21
+ });
22
+ await this.configureDecoder(this.config);
23
+ }
24
+ async reconfigure(config) {
25
+ this.config = { ...this.config, ...config };
26
+ if (!this.decoder) {
27
+ await this.initialize();
28
+ return;
29
+ }
30
+ if (this.decoder.state === "configured") {
31
+ await this.decoder.flush();
32
+ }
33
+ const isSupported = await this.isConfigSupported(this.config);
34
+ if (!isSupported.supported) {
35
+ throw new Error(
36
+ `New configuration not supported: ${this.config.codecString || this.config.codec}`
37
+ );
38
+ }
39
+ await this.configureDecoder(this.config);
40
+ }
41
+ async flush() {
42
+ if (!this.decoder) return;
43
+ await this.decoder.flush();
44
+ }
45
+ async reset() {
46
+ if (!this.decoder) return;
47
+ this.decoder.reset();
48
+ this.onReset();
49
+ }
50
+ async close() {
51
+ if (!this.decoder) return;
52
+ if (this.decoder.state === "configured") {
53
+ await this.decoder.flush();
54
+ }
55
+ this.decoder.close();
56
+ this.decoder = void 0;
57
+ }
58
+ get isReady() {
59
+ return this.decoder?.state === "configured";
60
+ }
61
+ get queueSize() {
62
+ return this.decoder?.decodeQueueSize ?? 0;
63
+ }
64
+ handleOutput(data) {
65
+ if (!this.controller) {
66
+ this.closeIfPossible(data);
67
+ return;
68
+ }
69
+ try {
70
+ this.controller.enqueue(data);
71
+ } catch (error) {
72
+ if (error instanceof TypeError && /Cannot enqueue a chunk into a readable stream that is closed/.test(error.message)) {
73
+ this.closeIfPossible(data);
74
+ return;
75
+ }
76
+ throw error;
77
+ }
78
+ }
79
+ handleError(error) {
80
+ console.error(`${this.getDecoderType()} decoder error:`, error);
81
+ this.controller?.error(error);
82
+ }
83
+ closeIfPossible(data) {
84
+ data?.close?.();
85
+ data?.frame?.close?.();
86
+ }
87
+ onReset() {
88
+ }
89
+ createStream() {
90
+ return new TransformStream(
91
+ {
92
+ start: async (controller) => {
93
+ this.controller = controller;
94
+ if (!this.isReady) {
95
+ await this.initialize();
96
+ }
97
+ },
98
+ transform: async (input) => {
99
+ if (!this.decoder || this.decoder.state !== "configured") {
100
+ throw new Error("Decoder not configured");
101
+ }
102
+ if (this.decoder.decodeQueueSize >= this.decodeQueueThreshold) {
103
+ await new Promise((resolve) => {
104
+ const check = () => {
105
+ if (!this.decoder || this.decoder.decodeQueueSize < this.decodeQueueThreshold - 1) {
106
+ resolve();
107
+ } else {
108
+ setTimeout(check, 10);
109
+ }
110
+ };
111
+ check();
112
+ });
113
+ }
114
+ this.decode(input);
115
+ },
116
+ flush: async () => {
117
+ await this.flush();
118
+ }
119
+ },
120
+ {
121
+ highWaterMark: this.highWaterMark,
122
+ size: () => 1
123
+ }
124
+ );
125
+ }
126
+ }
127
+ export {
128
+ BaseDecoder as B
129
+ };
130
+ //# sourceMappingURL=BaseDecoder.js.map