@meframe/core 0.0.1 → 0.0.2
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.
- package/README.md +17 -4
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +0 -3
- package/dist/Meframe.js.map +1 -1
- package/dist/assets/audio-compose.worker-nGVvHD5Q.js +1537 -0
- package/dist/assets/audio-compose.worker-nGVvHD5Q.js.map +1 -0
- package/dist/assets/audio-demux.worker-xwWBtbAe.js +8299 -0
- package/dist/assets/audio-demux.worker-xwWBtbAe.js.map +1 -0
- package/dist/assets/decode.worker-DpWHsc7R.js +1291 -0
- package/dist/assets/decode.worker-DpWHsc7R.js.map +1 -0
- package/dist/assets/encode.worker-nfOb3kw6.js +1026 -0
- package/dist/assets/encode.worker-nfOb3kw6.js.map +1 -0
- package/dist/assets/mux.worker-uEMQY066.js +8019 -0
- package/dist/assets/mux.worker-uEMQY066.js.map +1 -0
- package/dist/assets/video-compose.worker-DPzsC21d.js +1683 -0
- package/dist/assets/video-compose.worker-DPzsC21d.js.map +1 -0
- package/dist/assets/video-demux.worker-D019I7GQ.js +7957 -0
- package/dist/assets/video-demux.worker-D019I7GQ.js.map +1 -0
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +8 -1
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +0 -8
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/types.d.ts +0 -4
- package/dist/config/types.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.d.ts +4 -2
- package/dist/controllers/PlaybackController.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.js +7 -13
- package/dist/controllers/PlaybackController.js.map +1 -1
- package/dist/controllers/PreRenderService.d.ts +3 -2
- package/dist/controllers/PreRenderService.d.ts.map +1 -1
- package/dist/controllers/PreRenderService.js.map +1 -1
- package/dist/controllers/PreviewHandle.d.ts +2 -0
- package/dist/controllers/PreviewHandle.d.ts.map +1 -1
- package/dist/controllers/PreviewHandle.js +6 -0
- package/dist/controllers/PreviewHandle.js.map +1 -1
- package/dist/controllers/index.d.ts +1 -1
- package/dist/controllers/index.d.ts.map +1 -1
- package/dist/controllers/types.d.ts +2 -12
- package/dist/controllers/types.d.ts.map +1 -1
- package/dist/event/events.d.ts +5 -59
- package/dist/event/events.d.ts.map +1 -1
- package/dist/event/events.js +1 -6
- package/dist/event/events.js.map +1 -1
- package/dist/model/CompositionModel.js +1 -2
- package/dist/model/CompositionModel.js.map +1 -1
- package/dist/orchestrator/CompositionPlanner.d.ts.map +1 -1
- package/dist/orchestrator/CompositionPlanner.js +1 -0
- package/dist/orchestrator/CompositionPlanner.js.map +1 -1
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +1 -12
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
- package/dist/orchestrator/VideoClipSession.js +4 -5
- package/dist/orchestrator/VideoClipSession.js.map +1 -1
- package/dist/orchestrator/types.d.ts +0 -1
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/dist/stages/compose/GlobalAudioSession.d.ts.map +1 -1
- package/dist/stages/compose/GlobalAudioSession.js +3 -2
- package/dist/stages/compose/GlobalAudioSession.js.map +1 -1
- package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
- package/dist/stages/compose/VideoComposer.js +2 -2
- package/dist/stages/compose/VideoComposer.js.map +1 -1
- package/dist/stages/compose/audio-compose.worker.d.ts.map +1 -1
- package/dist/stages/compose/audio-compose.worker.js +0 -1
- package/dist/stages/compose/audio-compose.worker.js.map +1 -1
- package/dist/stages/compose/audio-compose.worker2.js +5 -0
- package/dist/stages/compose/audio-compose.worker2.js.map +1 -0
- package/dist/stages/compose/types.d.ts +1 -0
- package/dist/stages/compose/types.d.ts.map +1 -1
- package/dist/stages/compose/video-compose.worker.d.ts.map +1 -1
- package/dist/stages/compose/video-compose.worker.js +18 -8
- package/dist/stages/compose/video-compose.worker.js.map +1 -1
- package/dist/stages/compose/video-compose.worker2.js +5 -0
- package/dist/stages/compose/video-compose.worker2.js.map +1 -0
- package/dist/stages/decode/AudioChunkDecoder.d.ts.map +1 -1
- package/dist/stages/decode/AudioChunkDecoder.js +0 -1
- package/dist/stages/decode/AudioChunkDecoder.js.map +1 -1
- package/dist/stages/decode/VideoChunkDecoder.d.ts +0 -1
- package/dist/stages/decode/VideoChunkDecoder.d.ts.map +1 -1
- package/dist/stages/decode/VideoChunkDecoder.js +1 -11
- package/dist/stages/decode/VideoChunkDecoder.js.map +1 -1
- package/dist/stages/decode/decode.worker.d.ts.map +1 -1
- package/dist/stages/decode/decode.worker.js +3 -16
- package/dist/stages/decode/decode.worker.js.map +1 -1
- package/dist/stages/decode/decode.worker2.js +5 -0
- package/dist/stages/decode/decode.worker2.js.map +1 -0
- package/dist/stages/demux/MP4Demuxer.d.ts +2 -0
- package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
- package/dist/stages/demux/MP4Demuxer.js +13 -2
- package/dist/stages/demux/MP4Demuxer.js.map +1 -1
- package/dist/stages/demux/audio-demux.worker2.js +5 -0
- package/dist/stages/demux/audio-demux.worker2.js.map +1 -0
- package/dist/stages/demux/video-demux.worker.d.ts +6 -3
- package/dist/stages/demux/video-demux.worker.d.ts.map +1 -1
- package/dist/stages/demux/video-demux.worker.js +5 -27
- package/dist/stages/demux/video-demux.worker.js.map +1 -1
- package/dist/stages/demux/video-demux.worker2.js +5 -0
- package/dist/stages/demux/video-demux.worker2.js.map +1 -0
- package/dist/stages/encode/encode.worker.d.ts.map +1 -1
- package/dist/stages/encode/encode.worker.js +0 -1
- package/dist/stages/encode/encode.worker.js.map +1 -1
- package/dist/stages/encode/encode.worker2.js +5 -0
- package/dist/stages/encode/encode.worker2.js.map +1 -0
- package/dist/stages/load/EventHandlers.d.ts +2 -11
- package/dist/stages/load/EventHandlers.d.ts.map +1 -1
- package/dist/stages/load/EventHandlers.js +1 -24
- package/dist/stages/load/EventHandlers.js.map +1 -1
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +11 -13
- package/dist/stages/load/ResourceLoader.js.map +1 -1
- package/dist/stages/load/TaskManager.d.ts +1 -1
- package/dist/stages/load/TaskManager.d.ts.map +1 -1
- package/dist/stages/load/TaskManager.js +3 -2
- package/dist/stages/load/TaskManager.js.map +1 -1
- package/dist/stages/load/types.d.ts +2 -0
- package/dist/stages/load/types.d.ts.map +1 -1
- package/dist/stages/mux/mux.worker2.js +5 -0
- package/dist/stages/mux/mux.worker2.js.map +1 -0
- package/dist/vite-plugin.d.ts +17 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +88 -0
- package/dist/vite-plugin.js.map +1 -0
- package/dist/worker/WorkerPool.d.ts +0 -4
- package/dist/worker/WorkerPool.d.ts.map +1 -1
- package/dist/worker/WorkerPool.js +4 -17
- package/dist/worker/WorkerPool.js.map +1 -1
- package/dist/worker/worker-registry.d.ts +12 -0
- package/dist/worker/worker-registry.d.ts.map +1 -0
- package/dist/worker/worker-registry.js +20 -0
- package/dist/worker/worker-registry.js.map +1 -0
- package/package.json +7 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Orchestrator.js","sources":["../../src/orchestrator/Orchestrator.ts"],"sourcesContent":["import { EventBus } from '../event/EventBus';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { applyPatch as applyModelPatch } from '../model/patch';\nimport { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, DirtyRange, RenderFrameOptions } from './types';\nimport { WorkerStatus, WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame } from '../model';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { VideoClipSession } from './VideoClipSession';\nimport { ClipSessionManager } from './ClipSessionManager';\nimport { GlobalAudioSession } from '../stages/compose/GlobalAudioSession';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n\n private activeClips = new Set<string>();\n private isInitialized = false;\n private config = ConfigLoader.getInstance().getConfig();\n private clipSessionManager: ClipSessionManager;\n private currentClipId: string | null = null;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n const globalConfig = (this.config as any).global;\n const workerBaseUrl = config.workerBaseUrl || globalConfig?.workerBaseUrl || '/dist/stages';\n const workerExtension = globalConfig?.workerExtension || '.js';\n this.workers = new WorkerPool({\n baseUrl: workerBaseUrl,\n workerExtension,\n eventBus: this.eventBus,\n workerConfigs,\n });\n\n this.resourceLoader = new ResourceLoader({\n orchestrator: this as any,\n eventBus: this.eventBus,\n config: {\n maxConcurrent: config.maxWorkers || (this.config.load as any)?.retry?.maxAttempts || 4,\n },\n onStateChange: (resourceId, state) => this.handleResourceStateChange(resourceId, state),\n });\n\n this.planner = new CompositionPlanner();\n\n const cacheConfig = config.cacheConfig || this.config.cache;\n this.cacheManager = new CacheManager(\n {\n l1: {\n maxMemoryMB:\n (cacheConfig as any)?.l1Size || (cacheConfig as any)?.l1?.maxMemoryMB || 1024,\n maxGOPs: (this.config.decode as any)?.video?.maxGOPs || 4,\n },\n l2: {\n maxSizeMB: (cacheConfig as any)?.l2Size || (cacheConfig as any)?.l2?.maxSizeMB || 2048,\n projectId: 'default',\n },\n },\n this.eventBus\n );\n\n this.clipSessionManager = new ClipSessionManager({\n maxConcurrent: 2,\n factory: {\n createSession: (clipId) => this.createSession(clipId),\n },\n model: () => this.compositionModel,\n cacheManager: this.cacheManager,\n });\n\n this.audioSession = new GlobalAudioSession({\n cacheManager: this.cacheManager,\n workers: this.workers,\n resourceLoader: this.resourceLoader,\n eventBus: this.eventBus,\n getModel: () => this.compositionModel,\n buildWorkerConfigs: () => this.buildWorkerConfigs(),\n });\n }\n\n get workerStatus(): WorkerStatus {\n const status = this.workers.status;\n const result: WorkerStatus = {} as WorkerStatus;\n\n const workerTypes: WorkerType[] = [\n 'videoDemux',\n 'audioDemux',\n 'decode',\n 'videoCompose',\n 'audioCompose',\n 'encode',\n 'mux',\n ];\n\n for (const type of workerTypes) {\n result[type] = status[type] || {\n state: 'idle',\n taskCount: 0,\n };\n }\n\n return result;\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n await this.cacheManager.init();\n // Use unified stream pipeline wiring\n await this.setupSharedConnections();\n\n this.isInitialized = true;\n }\n\n // Event methods - forward to eventBus\n on<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.on(event, handler);\n }\n\n off<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.off(event, handler);\n }\n\n once<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.once(event, handler);\n }\n\n async setCompositionModel(model: CompositionModel): Promise<void> {\n this.compositionModel = model;\n this.planner.setModel(model);\n this.cacheManager.configure(model);\n this.currentClipId = null;\n\n this.eventBus.emit(MeframeEvent.ModelSet, model);\n\n this.eventBus.emit(MeframeEvent.CompositionUpdated, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce((acc: number, track: any) => acc + track.clips.length, 0),\n durationUs: model.durationUs,\n });\n\n await this.audioSession.activateAllAudioClips();\n\n this.ensureClipCache(0);\n }\n\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const dirtyRanges = applyModelPatch(this.compositionModel, patch);\n const clipUpdates = this.planner.applyPatch(patch, dirtyRanges);\n\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n dirtyRanges: dirtyRanges.map((r) => ({ startUs: r.startUs, endUs: r.endUs })),\n });\n\n for (const range of dirtyRanges) {\n await this.handleDirtyRange(range);\n }\n\n for (const update of clipUpdates) {\n // In 3-Clip strategy, session lifecycle is managed by ClipSessionManager\n // Clip updates handled through cache invalidation in dirtyRanges\n if (update.type === 'remove') {\n this.activeClips.delete(update.clipId);\n this.cacheManager.evictClip(update.clipId);\n }\n }\n }\n\n private handleResourceStateChange(resourceId: string, state: Resource['state']): void {\n if (!this.compositionModel) {\n return;\n }\n\n this.compositionModel.updateResourceState(resourceId, state ?? 'pending');\n\n if (state !== 'ready') {\n return;\n }\n\n const clipIds = this.compositionModel.getClipIdsByResourceId(resourceId);\n for (const clipId of clipIds) {\n const clip = this.compositionModel.findClip(clipId);\n if (!clip) {\n continue;\n }\n\n const update = this.planner.refreshClip(clipId, 'resource-ready');\n if (update) {\n this.clipSessionManager.handlePlannerUpdate(clipId, update);\n }\n // Clip will be activated when ensureClipCache is called\n }\n }\n\n async restartWorker(type: WorkerType, clipId?: string): Promise<void> {\n if ((type === 'videoDemux' || type === 'audioDemux' || type === 'videoCompose') && !clipId) {\n throw new Error(`clipId required for restarting ${type} worker`);\n }\n\n this.workers.terminate(type, clipId);\n const worker = await this.workers.get(type, clipId);\n\n this.eventBus.emit(MeframeEvent.WorkerRestarted, {\n type,\n workerId: worker.getWorkerId(),\n reason: 'Manual restart',\n });\n\n if (clipId) {\n const session = this.clipSessionManager.getSession(clipId);\n if (session) {\n await session.activate();\n }\n } else if (\n type === 'decode' ||\n type === 'audioCompose' ||\n type === 'encode' ||\n type === 'mux'\n ) {\n await this.setupSharedConnections();\n }\n }\n\n async renderFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, 'main')[0];\n if (!clip) {\n return null;\n }\n\n // Detect clip change and proactively ensure cache for prev/current/next\n if (this.currentClipId !== clip.id) {\n this.currentClipId = clip.id;\n void this.ensureClipCache(timeUs);\n }\n\n const cachedFrame = await this.cacheManager.getFrame(timeUs, clip.id);\n if (cachedFrame) {\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${timeUs}`,\n });\n return cachedFrame;\n }\n console.log('>>>>>>>>>[Orchestrator] renderFrame', timeUs, clip.id, 'cache miss');\n // Cache miss - also ensure cache (defensive, already called on clip change)\n void this.ensureClipCache(timeUs);\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${timeUs}`,\n });\n\n if (signal?.aborted) {\n throw new DOMException('Render aborted', 'AbortError');\n }\n\n // Return null immediately instead of waiting\n // This allows PlaybackController to detect miss and trigger buffering\n return null;\n }\n\n /**\n * Ensure clips are cached using 3-Clip strategy\n * Called by PlaybackController\n */\n async ensureClipCache(timeUs: TimeUs): Promise<void> {\n if (!this.compositionModel) {\n return;\n }\n\n await this.clipSessionManager.ensureClips(timeUs);\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, 'main');\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 30,\n timeoutMs: options?.timeoutMs ?? 3_000,\n });\n }\n\n /**\n * Create a new session for a clip\n */\n private async createSession(clipId: string): Promise<VideoClipSession> {\n const clip = this.compositionModel?.findClip(clipId);\n if (!clip) {\n throw new Error(`Clip ${clipId} not found`);\n }\n\n const session = await VideoClipSession.create({\n clipId,\n planner: this.planner,\n workerPool: this.workers,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n workerConfigs: this.buildWorkerConfigs(),\n callbacks: {\n onStreamReady: (id, stream, fps) => {\n this.cacheManager.acceptComposedFrames(stream, {\n clipId: id,\n trackId: 'main',\n fps,\n onFrame: () => {\n // Frame arrived\n },\n });\n },\n onPipelineReady: async (clipId) => {\n // Pipeline is connected, now trigger resource loading\n const clip = this.compositionModel?.findClip(clipId);\n if (clip?.resourceId) {\n // Range is based on source video timeline (considering trim)\n const trimStart = clip.trimStartUs || 0;\n const trimEnd = clip.trimEndUs || trimStart + clip.durationUs;\n\n await this.resourceLoader.fetch(clip.resourceId, {\n priority: 'high',\n range: {\n start: trimStart,\n end: trimEnd,\n },\n });\n }\n },\n },\n });\n\n this.activeClips.add(clipId);\n return session;\n }\n\n async dispose(): Promise<void> {\n this.resourceLoader.dispose();\n await this.clipSessionManager.dispose();\n await this.cacheManager.clear();\n\n this.currentClipId = null;\n this.activeClips.clear();\n\n this.workers.terminateAll();\n this.compositionModel = null;\n this.eventBus.dispose();\n }\n\n private buildWorkerConfigs(): Record<WorkerType, any> {\n const config = this.config as any;\n const defaultCanvasWidth = config.global?.defaultCanvasWidth ?? 1280;\n const defaultCanvasHeight = config.global?.defaultCanvasHeight ?? 720;\n const defaultFps = config.global?.defaultFps ?? 30;\n return {\n videoDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n audioDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n decode: {\n video: config.decode?.video,\n audio: config.decode?.audio,\n },\n videoCompose: {\n width: config.compose?.canvas?.width ?? defaultCanvasWidth,\n height: config.compose?.canvas?.height ?? defaultCanvasHeight,\n fps: config.global?.defaultFps ?? defaultFps,\n backgroundColor: config.compose?.canvas?.backgroundColor ?? '#000000',\n enableSmoothing: config.compose?.visual?.enableSmoothing ?? true,\n enableHardwareAcceleration: config.compose?.visual?.enableHardwareAcceleration ?? true,\n },\n audioCompose: {\n ducking: config.compose?.audio?.ducking,\n mixing: config.compose?.audio?.mixing,\n },\n encode: {\n video: {\n codec: 'avc1.42002A',\n width: config.compose?.canvas?.width || defaultCanvasWidth,\n height: config.compose?.canvas?.height || defaultCanvasHeight,\n bitrate: config.encode?.video?.bitrateKbps\n ? config.encode.video.bitrateKbps * 1000\n : 12_000_000,\n framerate: config.encode?.video?.framerate || defaultFps,\n latencyMode: 'quality',\n bitrateMode: 'variable',\n hardwareAcceleration: 'prefer-hardware',\n ...(config.encode?.video as any),\n },\n audio: {\n codec: 'mp4a.40.2',\n sampleRate: 48000,\n numberOfChannels: 2,\n bitrate: config.encode?.audio?.bitrateKbps\n ? config.encode.audio.bitrateKbps * 1000\n : 128000,\n ...(config.encode?.audio as any),\n },\n },\n mux: {\n container: config.export?.container || 'mp4',\n },\n };\n }\n\n private async setupSharedConnections(): Promise<void> {\n const decodeWorker = await this.workers.get('decode');\n\n decodeWorker.receiveStream((stream, metadata) => {\n if (metadata?.streamType === 'audio') {\n this.audioSession.handleAudioStream(stream as ReadableStream<AudioData>, metadata);\n }\n });\n\n const encodeWorker = await this.workers.get('encode');\n const muxWorker = await this.workers.get('mux');\n const videoChannel = new MessageChannel();\n await encodeWorker.send(\n 'connect',\n { direction: 'downstream', port: videoChannel.port1, streamType: 'video' },\n { transfer: [videoChannel.port1] }\n );\n await muxWorker.send(\n 'connect',\n { direction: 'upstream', port: videoChannel.port2, streamType: 'video' },\n { transfer: [videoChannel.port2] }\n );\n }\n\n private async handleDirtyRange(range: DirtyRange): Promise<void> {\n this.cacheManager.invalidateRange(range.startUs, range.endUs, range.trackId);\n }\n}\n"],"names":["applyModelPatch","clipId","clip"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Orchestrator.js","sources":["../../src/orchestrator/Orchestrator.ts"],"sourcesContent":["import { EventBus } from '../event/EventBus';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { applyPatch as applyModelPatch } from '../model/patch';\nimport { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, DirtyRange, RenderFrameOptions } from './types';\nimport { WorkerStatus, WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame } from '../model';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { VideoClipSession } from './VideoClipSession';\nimport { ClipSessionManager } from './ClipSessionManager';\nimport { GlobalAudioSession } from '../stages/compose/GlobalAudioSession';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n\n private activeClips = new Set<string>();\n private isInitialized = false;\n private config = ConfigLoader.getInstance().getConfig();\n private clipSessionManager: ClipSessionManager;\n private currentClipId: string | null = null;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n // Worker URLs are automatically resolved via worker-registry\n this.workers = new WorkerPool({\n eventBus: this.eventBus,\n workerConfigs,\n });\n\n this.resourceLoader = new ResourceLoader({\n orchestrator: this as any,\n eventBus: this.eventBus,\n config: {\n maxConcurrent: config.maxWorkers || (this.config.load as any)?.retry?.maxAttempts || 4,\n },\n onStateChange: (resourceId, state) => this.handleResourceStateChange(resourceId, state),\n });\n\n this.planner = new CompositionPlanner();\n\n const cacheConfig = config.cacheConfig || this.config.cache;\n this.cacheManager = new CacheManager(\n {\n l1: {\n maxMemoryMB:\n (cacheConfig as any)?.l1Size || (cacheConfig as any)?.l1?.maxMemoryMB || 1024,\n maxGOPs: (this.config.decode as any)?.video?.maxGOPs || 4,\n },\n l2: {\n maxSizeMB: (cacheConfig as any)?.l2Size || (cacheConfig as any)?.l2?.maxSizeMB || 2048,\n projectId: 'default',\n },\n },\n this.eventBus\n );\n\n this.clipSessionManager = new ClipSessionManager({\n maxConcurrent: 2,\n factory: {\n createSession: (clipId) => this.createSession(clipId),\n },\n model: () => this.compositionModel,\n cacheManager: this.cacheManager,\n });\n\n this.audioSession = new GlobalAudioSession({\n cacheManager: this.cacheManager,\n workers: this.workers,\n resourceLoader: this.resourceLoader,\n eventBus: this.eventBus,\n getModel: () => this.compositionModel,\n buildWorkerConfigs: () => this.buildWorkerConfigs(),\n });\n }\n\n get workerStatus(): WorkerStatus {\n const status = this.workers.status;\n const result: WorkerStatus = {} as WorkerStatus;\n\n const workerTypes: WorkerType[] = [\n 'videoDemux',\n 'audioDemux',\n 'decode',\n 'videoCompose',\n 'audioCompose',\n 'encode',\n 'mux',\n ];\n\n for (const type of workerTypes) {\n result[type] = status[type] || {\n state: 'idle',\n taskCount: 0,\n };\n }\n\n return result;\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n await this.cacheManager.init();\n // Use unified stream pipeline wiring\n await this.setupSharedConnections();\n\n this.isInitialized = true;\n }\n\n // Event methods - forward to eventBus\n on<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.on(event, handler);\n }\n\n off<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.off(event, handler);\n }\n\n once<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.once(event, handler);\n }\n\n async setCompositionModel(model: CompositionModel): Promise<void> {\n this.compositionModel = model;\n this.planner.setModel(model);\n this.cacheManager.configure(model);\n this.currentClipId = null;\n\n this.eventBus.emit(MeframeEvent.ModelSet, model);\n\n this.eventBus.emit(MeframeEvent.CompositionUpdated, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce((acc: number, track: any) => acc + track.clips.length, 0),\n durationUs: model.durationUs,\n });\n\n await this.audioSession.activateAllAudioClips();\n\n this.ensureClipCache(0);\n }\n\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const dirtyRanges = applyModelPatch(this.compositionModel, patch);\n const clipUpdates = this.planner.applyPatch(patch, dirtyRanges);\n\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n dirtyRanges: dirtyRanges.map((r) => ({ startUs: r.startUs, endUs: r.endUs })),\n });\n\n for (const range of dirtyRanges) {\n await this.handleDirtyRange(range);\n }\n\n for (const update of clipUpdates) {\n // In 3-Clip strategy, session lifecycle is managed by ClipSessionManager\n // Clip updates handled through cache invalidation in dirtyRanges\n if (update.type === 'remove') {\n this.activeClips.delete(update.clipId);\n this.cacheManager.evictClip(update.clipId);\n }\n }\n }\n\n private handleResourceStateChange(resourceId: string, state: Resource['state']): void {\n if (!this.compositionModel) {\n return;\n }\n\n this.compositionModel.updateResourceState(resourceId, state ?? 'pending');\n\n if (state !== 'ready') {\n return;\n }\n\n const clipIds = this.compositionModel.getClipIdsByResourceId(resourceId);\n for (const clipId of clipIds) {\n const clip = this.compositionModel.findClip(clipId);\n if (!clip) {\n continue;\n }\n\n const update = this.planner.refreshClip(clipId, 'resource-ready');\n if (update) {\n this.clipSessionManager.handlePlannerUpdate(clipId, update);\n }\n // Clip will be activated when ensureClipCache is called\n }\n }\n\n async restartWorker(type: WorkerType, clipId?: string): Promise<void> {\n if ((type === 'videoDemux' || type === 'audioDemux' || type === 'videoCompose') && !clipId) {\n throw new Error(`clipId required for restarting ${type} worker`);\n }\n\n this.workers.terminate(type, clipId);\n const worker = await this.workers.get(type, clipId);\n\n this.eventBus.emit(MeframeEvent.WorkerRestarted, {\n type,\n workerId: worker.getWorkerId(),\n reason: 'Manual restart',\n });\n\n if (clipId) {\n const session = this.clipSessionManager.getSession(clipId);\n if (session) {\n await session.activate();\n }\n } else if (\n type === 'decode' ||\n type === 'audioCompose' ||\n type === 'encode' ||\n type === 'mux'\n ) {\n await this.setupSharedConnections();\n }\n }\n\n async renderFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, 'main')[0];\n if (!clip) {\n return null;\n }\n\n // Detect clip change and proactively ensure cache for prev/current/next\n if (this.currentClipId !== clip.id) {\n this.currentClipId = clip.id;\n void this.ensureClipCache(timeUs);\n }\n\n const cachedFrame = await this.cacheManager.getFrame(timeUs, clip.id);\n if (cachedFrame) {\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${timeUs}`,\n });\n return cachedFrame;\n }\n // Cache miss - also ensure cache (defensive, already called on clip change)\n void this.ensureClipCache(timeUs);\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${timeUs}`,\n });\n\n if (signal?.aborted) {\n throw new DOMException('Render aborted', 'AbortError');\n }\n\n // Return null immediately instead of waiting\n // This allows PlaybackController to detect miss and trigger buffering\n return null;\n }\n\n /**\n * Ensure clips are cached using 3-Clip strategy\n * Called by PlaybackController\n */\n async ensureClipCache(timeUs: TimeUs): Promise<void> {\n if (!this.compositionModel) {\n return;\n }\n\n await this.clipSessionManager.ensureClips(timeUs);\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, 'main');\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 30,\n timeoutMs: options?.timeoutMs ?? 3_000,\n });\n }\n\n /**\n * Create a new session for a clip\n */\n private async createSession(clipId: string): Promise<VideoClipSession> {\n const clip = this.compositionModel?.findClip(clipId);\n if (!clip) {\n throw new Error(`Clip ${clipId} not found`);\n }\n\n const session = await VideoClipSession.create({\n clipId,\n planner: this.planner,\n workerPool: this.workers,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n workerConfigs: this.buildWorkerConfigs(),\n callbacks: {\n onStreamReady: (id, stream, fps) => {\n this.cacheManager.acceptComposedFrames(stream, {\n clipId: id,\n trackId: 'main',\n fps,\n onFrame: () => {\n // Frame arrived\n },\n });\n },\n onPipelineReady: async (clipId) => {\n // Pipeline is connected, now trigger resource loading\n const clip = this.compositionModel?.findClip(clipId);\n if (clip?.resourceId) {\n await this.resourceLoader.fetch(clip.resourceId, {\n priority: 'high',\n clipId,\n });\n }\n },\n },\n });\n\n this.activeClips.add(clipId);\n return session;\n }\n\n async dispose(): Promise<void> {\n this.resourceLoader.dispose();\n await this.clipSessionManager.dispose();\n await this.cacheManager.clear();\n\n this.currentClipId = null;\n this.activeClips.clear();\n\n this.workers.terminateAll();\n this.compositionModel = null;\n this.eventBus.dispose();\n }\n\n private buildWorkerConfigs(): Record<WorkerType, any> {\n const config = this.config as any;\n const defaultCanvasWidth = config.global?.defaultCanvasWidth ?? 1280;\n const defaultCanvasHeight = config.global?.defaultCanvasHeight ?? 720;\n const defaultFps = config.global?.defaultFps ?? 30;\n return {\n videoDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n audioDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n decode: {\n video: config.decode?.video,\n audio: config.decode?.audio,\n },\n videoCompose: {\n width: config.compose?.canvas?.width ?? defaultCanvasWidth,\n height: config.compose?.canvas?.height ?? defaultCanvasHeight,\n fps: config.global?.defaultFps ?? defaultFps,\n backgroundColor: config.compose?.canvas?.backgroundColor ?? '#000000',\n enableSmoothing: config.compose?.visual?.enableSmoothing ?? true,\n enableHardwareAcceleration: config.compose?.visual?.enableHardwareAcceleration ?? true,\n },\n audioCompose: {\n ducking: config.compose?.audio?.ducking,\n mixing: config.compose?.audio?.mixing,\n },\n encode: {\n video: {\n codec: 'avc1.42002A',\n width: config.compose?.canvas?.width || defaultCanvasWidth,\n height: config.compose?.canvas?.height || defaultCanvasHeight,\n bitrate: config.encode?.video?.bitrateKbps\n ? config.encode.video.bitrateKbps * 1000\n : 12_000_000,\n framerate: config.encode?.video?.framerate || defaultFps,\n latencyMode: 'quality',\n bitrateMode: 'variable',\n hardwareAcceleration: 'prefer-hardware',\n ...(config.encode?.video as any),\n },\n audio: {\n codec: 'mp4a.40.2',\n sampleRate: 48000,\n numberOfChannels: 2,\n bitrate: config.encode?.audio?.bitrateKbps\n ? config.encode.audio.bitrateKbps * 1000\n : 128000,\n ...(config.encode?.audio as any),\n },\n },\n mux: {\n container: config.export?.container || 'mp4',\n },\n };\n }\n\n private async setupSharedConnections(): Promise<void> {\n const decodeWorker = await this.workers.get('decode');\n\n decodeWorker.receiveStream((stream, metadata) => {\n if (metadata?.streamType === 'audio') {\n this.audioSession.handleAudioStream(stream as ReadableStream<AudioData>, metadata);\n }\n });\n\n const encodeWorker = await this.workers.get('encode');\n const muxWorker = await this.workers.get('mux');\n const videoChannel = new MessageChannel();\n await encodeWorker.send(\n 'connect',\n { direction: 'downstream', port: videoChannel.port1, streamType: 'video' },\n { transfer: [videoChannel.port1] }\n );\n await muxWorker.send(\n 'connect',\n { direction: 'upstream', port: videoChannel.port2, streamType: 'video' },\n { transfer: [videoChannel.port2] }\n );\n }\n\n private async handleDirtyRange(range: DirtyRange): Promise<void> {\n this.cacheManager.invalidateRange(range.startUs, range.endUs, range.trackId);\n }\n}\n"],"names":["applyModelPatch","clipId","clip"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoClipSession.d.ts","sourceRoot":"","sources":["../../src/orchestrator/VideoClipSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAQ,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,UAAU,yBAAyB;IACjC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACrF,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,eAAe,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAED,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,kBAAkB,CAAC;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACvC,SAAS,EAAE,yBAAyB,CAAC;CACtC;AAQD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0B;IACxD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IAEtD,OAAO,CAAC,kBAAkB,CAAmC;IAC7D,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,oBAAoB,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAS;WAEd,MAAM,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI9E,OAAO;IAUD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"VideoClipSession.d.ts","sourceRoot":"","sources":["../../src/orchestrator/VideoClipSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAQ,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,UAAU,yBAAyB;IACjC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACrF,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,eAAe,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAED,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,kBAAkB,CAAC;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACvC,SAAS,EAAE,yBAAyB,CAAC;CACtC;AAQD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0B;IACxD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IAEtD,OAAO,CAAC,kBAAkB,CAAmC;IAC7D,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,oBAAoB,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAS;WAEd,MAAM,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI9E,OAAO;IAUD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOxB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoC5D,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAarD,IAAI,kBAAkB,IAAI,UAAU,GAAG,IAAI,CAE1C;IAED,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,WAAW;YAML,kBAAkB;YAiBlB,aAAa;YAmBb,cAAc;YAsBd,oBAAoB;YAwFpB,mBAAmB;YAwBnB,eAAe;YAcf,eAAe;YAuCf,mBAAmB;YAInB,gBAAgB;YAMhB,eAAe;CAW9B"}
|
|
@@ -41,7 +41,6 @@ class VideoClipSession {
|
|
|
41
41
|
this.isActive = true;
|
|
42
42
|
if (this.callbacks.onPipelineReady) {
|
|
43
43
|
await this.callbacks.onPipelineReady(this.clipId);
|
|
44
|
-
console.log("[VideoClipSession] activate - onPipelineReady done", this.clipId);
|
|
45
44
|
}
|
|
46
45
|
}
|
|
47
46
|
async deactivate() {
|
|
@@ -123,17 +122,17 @@ class VideoClipSession {
|
|
|
123
122
|
]);
|
|
124
123
|
}
|
|
125
124
|
async setupPipeline(clip, resource) {
|
|
126
|
-
await this.acquireWorkers(clip
|
|
125
|
+
await this.acquireWorkers(clip);
|
|
127
126
|
await this.connectVideoPipeline(clip, resource.id);
|
|
128
127
|
if (!this.visualWorker) {
|
|
129
128
|
throw new Error("Visual worker unavailable");
|
|
130
129
|
}
|
|
131
130
|
}
|
|
132
|
-
async acquireWorkers(clip
|
|
131
|
+
async acquireWorkers(clip) {
|
|
133
132
|
this.visualWorker = await this.workerPool.get("videoCompose", clip.id, { lazy: true });
|
|
134
133
|
this.decodeWorker = await this.workerPool.get("decode");
|
|
135
134
|
this.encodeWorker = await this.workerPool.get("encode");
|
|
136
|
-
this.videoDemuxWorker = await this.workerPool.get("videoDemux",
|
|
135
|
+
this.videoDemuxWorker = await this.workerPool.get("videoDemux", clip.id, { lazy: true });
|
|
137
136
|
const visualConfig = this.workerConfigs.videoCompose ?? {};
|
|
138
137
|
const renderOverrides = this.compositionModel.renderConfig ?? {};
|
|
139
138
|
const timeline = {
|
|
@@ -274,7 +273,7 @@ class VideoClipSession {
|
|
|
274
273
|
}
|
|
275
274
|
this.visualStream = null;
|
|
276
275
|
if (this.videoDemuxWorker) {
|
|
277
|
-
this.workerPool.terminate("videoDemux", this.
|
|
276
|
+
this.workerPool.terminate("videoDemux", this.clipId);
|
|
278
277
|
this.videoDemuxWorker = null;
|
|
279
278
|
}
|
|
280
279
|
if (this.visualWorker) {
|
|
@@ -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 { DirtyRange } from './types';\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 onStreamReady(clipId: string, stream: ReadableStream<VideoFrame>, fps: number): void;\n onStreamDisposed?(clipId: string): void;\n onPipelineReady?(clipId: string): Promise<void>;\n}\n\ninterface VideoClipSessionConfig {\n clipId: string;\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 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.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 || resource.type !== 'video') {\n return;\n }\n\n await this.setupPipeline(clip, resource);\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(this.clipId);\n console.log('[VideoClipSession] activate - onPipelineReady done', this.clipId);\n }\n }\n\n async deactivate(): Promise<void> {\n if (!this.isActive || this.isDisposed) return;\n this.isActive = false;\n\n await this.invalidateClipCache();\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') {\n return;\n }\n\n if (!this.isActive) {\n await this.activate();\n }\n\n if (update.type === 'pipelineChange') {\n await this.restartPipeline(clip, resource, instructions, update.dirtyRanges);\n return;\n }\n\n await this.installInstructions(instructions, update.dirtyRanges, {\n expectCacheInvalidation: false,\n triggerSync: update.type !== 'instructionOnly',\n });\n }\n\n async requestReplay(range: DirtyRange): Promise<void> {\n if (this.isDisposed || !this.isActive || !this.visualWorker || !this.instructionContext) {\n return;\n }\n\n await this.cacheManager.invalidateRange(range.startUs, range.endUs, this.clipId);\n await this.visualWorker.send('sync_clip', {\n clipId: this.clipId,\n revision: this.instructionContext.revision,\n range,\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\n await this.installInstructions(instructions, [\n {\n clipId: clip.id,\n trackId: clip.trackId || 'main',\n startUs: clip.startUs,\n endUs: clip.startUs + clip.durationUs,\n reason: 'initial-install',\n },\n ]);\n }\n\n private async setupPipeline(clip: Clip, resource: { id: string }): Promise<void> {\n await this.acquireWorkers(clip, resource.id);\n await this.connectVideoPipeline(clip, resource.id);\n\n if (!this.visualWorker) {\n throw new Error('Visual worker unavailable');\n }\n\n // this.visualStream = await this.visualWorker.send<never, ReadableStream<VideoFrame>>(\n // 'get_stream'\n // );\n\n // if (this.visualStream) {\n // const visualConfig = this.workerConfigs.videoCompose ?? {};\n // const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;\n // this.callbacks.onStreamReady(this.clipId, this.visualStream, clip.trackId || 'main', fps);\n // }\n }\n\n private async acquireWorkers(clip: Clip, resourceId: string): Promise<void> {\n this.visualWorker = await this.workerPool.get('videoCompose', clip.id, { lazy: true });\n this.decodeWorker = await this.workerPool.get('decode');\n this.encodeWorker = await this.workerPool.get('encode');\n this.videoDemuxWorker = await this.workerPool.get('videoDemux', resourceId, { 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\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(clip: Clip, 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 const clipId = clip.id;\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 clipId,\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 clipId,\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 clipId,\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 clipId,\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 clipId,\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 clipId,\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 clipId,\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.onStreamReady(this.clipId, stream, fps);\n });\n }\n\n private async installInstructions(\n instructions: ClipInstructionSet,\n dirtyRanges: DirtyRange[],\n options?: { expectCacheInvalidation?: boolean; triggerSync?: boolean }\n ): Promise<void> {\n this.instructionContext = {\n revision: instructions.revision,\n instructions,\n status: instructions.status,\n };\n\n if (options?.expectCacheInvalidation) {\n await this.invalidateRanges(dirtyRanges);\n }\n\n if (this.visualWorker) {\n await this.visualWorker.send('install_instructions', instructions);\n }\n\n if (options?.triggerSync) {\n await this.syncDirtyRanges(dirtyRanges, instructions.revision);\n }\n }\n\n private async restartPipeline(\n clip: Clip,\n resource: { id: string },\n instructions: ClipInstructionSet,\n dirtyRanges: DirtyRange[]\n ): Promise<void> {\n await this.releasePipeline();\n await this.setupPipeline(clip, resource);\n await this.installInstructions(instructions, dirtyRanges, {\n expectCacheInvalidation: true,\n triggerSync: true,\n });\n }\n\n private async releasePipeline(): Promise<void> {\n if (this.visualWorker && this.instructionContext) {\n await this.visualWorker.notify('dispose_clip', {\n clipId: this.clipId,\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(this.clipId);\n }\n this.visualStream = null;\n\n if (this.videoDemuxWorker) {\n this.workerPool.terminate('videoDemux', this.getResource()?.id);\n this.videoDemuxWorker = null;\n }\n\n if (this.visualWorker) {\n this.workerPool.terminate('videoCompose', this.clipId);\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 private async invalidateClipCache(): Promise<void> {\n await this.cacheManager.invalidateClip(this.clipId);\n }\n\n private async invalidateRanges(ranges: DirtyRange[]): Promise<void> {\n for (const range of ranges) {\n await this.cacheManager.invalidateRange(range.startUs, range.endUs, this.clipId);\n }\n }\n\n private async syncDirtyRanges(ranges: DirtyRange[], revision: number): Promise<void> {\n if (!this.visualWorker) return;\n\n for (const range of ranges) {\n await this.visualWorker.send('sync_clip', {\n clipId: this.clipId,\n revision,\n range,\n });\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VideoClipSession.js","sources":["../../src/orchestrator/VideoClipSession.ts"],"sourcesContent":["import type { WorkerPool } from '../worker/WorkerPool';\nimport type { CacheManager } from '../cache/CacheManager';\nimport type { CompositionModel, Clip } from '../model';\nimport type { DirtyRange } from './types';\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 onStreamReady(clipId: string, stream: ReadableStream<VideoFrame>, fps: number): void;\n onStreamDisposed?(clipId: string): void;\n onPipelineReady?(clipId: string): Promise<void>;\n}\n\ninterface VideoClipSessionConfig {\n clipId: string;\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 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.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 || resource.type !== 'video') {\n return;\n }\n\n await this.setupPipeline(clip, resource);\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(this.clipId);\n }\n }\n\n async deactivate(): Promise<void> {\n if (!this.isActive || this.isDisposed) return;\n this.isActive = false;\n\n await this.invalidateClipCache();\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') {\n return;\n }\n\n if (!this.isActive) {\n await this.activate();\n }\n\n if (update.type === 'pipelineChange') {\n await this.restartPipeline(clip, resource, instructions, update.dirtyRanges);\n return;\n }\n\n await this.installInstructions(instructions, update.dirtyRanges, {\n expectCacheInvalidation: false,\n triggerSync: update.type !== 'instructionOnly',\n });\n }\n\n async requestReplay(range: DirtyRange): Promise<void> {\n if (this.isDisposed || !this.isActive || !this.visualWorker || !this.instructionContext) {\n return;\n }\n\n await this.cacheManager.invalidateRange(range.startUs, range.endUs, this.clipId);\n await this.visualWorker.send('sync_clip', {\n clipId: this.clipId,\n revision: this.instructionContext.revision,\n range,\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\n await this.installInstructions(instructions, [\n {\n clipId: clip.id,\n trackId: clip.trackId || 'main',\n startUs: clip.startUs,\n endUs: clip.startUs + clip.durationUs,\n reason: 'initial-install',\n },\n ]);\n }\n\n private async setupPipeline(clip: Clip, resource: { id: string }): Promise<void> {\n await this.acquireWorkers(clip);\n await this.connectVideoPipeline(clip, resource.id);\n\n if (!this.visualWorker) {\n throw new Error('Visual worker unavailable');\n }\n\n // this.visualStream = await this.visualWorker.send<never, ReadableStream<VideoFrame>>(\n // 'get_stream'\n // );\n\n // if (this.visualStream) {\n // const visualConfig = this.workerConfigs.videoCompose ?? {};\n // const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;\n // this.callbacks.onStreamReady(this.clipId, this.visualStream, clip.trackId || 'main', fps);\n // }\n }\n\n private async acquireWorkers(clip: Clip): Promise<void> {\n this.visualWorker = await this.workerPool.get('videoCompose', clip.id, { lazy: true });\n this.decodeWorker = await this.workerPool.get('decode');\n this.encodeWorker = await this.workerPool.get('encode');\n this.videoDemuxWorker = await this.workerPool.get('videoDemux', clip.id, { 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\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(clip: Clip, 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 const clipId = clip.id;\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 clipId,\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 clipId,\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 clipId,\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 clipId,\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 clipId,\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 clipId,\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 clipId,\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.onStreamReady(this.clipId, stream, fps);\n });\n }\n\n private async installInstructions(\n instructions: ClipInstructionSet,\n dirtyRanges: DirtyRange[],\n options?: { expectCacheInvalidation?: boolean; triggerSync?: boolean }\n ): Promise<void> {\n this.instructionContext = {\n revision: instructions.revision,\n instructions,\n status: instructions.status,\n };\n\n if (options?.expectCacheInvalidation) {\n await this.invalidateRanges(dirtyRanges);\n }\n\n if (this.visualWorker) {\n await this.visualWorker.send('install_instructions', instructions);\n }\n\n if (options?.triggerSync) {\n await this.syncDirtyRanges(dirtyRanges, instructions.revision);\n }\n }\n\n private async restartPipeline(\n clip: Clip,\n resource: { id: string },\n instructions: ClipInstructionSet,\n dirtyRanges: DirtyRange[]\n ): Promise<void> {\n await this.releasePipeline();\n await this.setupPipeline(clip, resource);\n await this.installInstructions(instructions, dirtyRanges, {\n expectCacheInvalidation: true,\n triggerSync: true,\n });\n }\n\n private async releasePipeline(): Promise<void> {\n if (this.visualWorker && this.instructionContext) {\n await this.visualWorker.notify('dispose_clip', {\n clipId: this.clipId,\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(this.clipId);\n }\n this.visualStream = null;\n\n if (this.videoDemuxWorker) {\n this.workerPool.terminate('videoDemux', this.clipId);\n this.videoDemuxWorker = null;\n }\n\n if (this.visualWorker) {\n this.workerPool.terminate('videoCompose', this.clipId);\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 private async invalidateClipCache(): Promise<void> {\n await this.cacheManager.invalidateClip(this.clipId);\n }\n\n private async invalidateRanges(ranges: DirtyRange[]): Promise<void> {\n for (const range of ranges) {\n await this.cacheManager.invalidateRange(range.startUs, range.endUs, this.clipId);\n }\n }\n\n private async syncDirtyRanges(ranges: DirtyRange[], revision: number): Promise<void> {\n if (!this.visualWorker) return;\n\n for (const range of ranges) {\n await this.visualWorker.send('sync_clip', {\n clipId: this.clipId,\n revision,\n range,\n });\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/orchestrator/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,MAAM,EACN,OAAO,EACR,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/orchestrator/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,MAAM,EACN,OAAO,EACR,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;IACF,QAAQ,CAAC,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC,CAAC;IACxE,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACnD,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IAEpC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnF,UAAU,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACnF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;IACf,IAAI,IAAI,IAAI,CAAC;IACb,gBAAgB,IAAI,MAAM,CAAC;IAC3B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC3C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC7C;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB"}
|
|
@@ -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;AAE7D,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;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,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
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;AAE7D,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB;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,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB5C,iBAAiB,CAAC,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IA+BzF,KAAK,IAAI,IAAI;YAKC,kBAAkB;IAiChC,OAAO,CAAC,aAAa;CAGtB"}
|
|
@@ -45,7 +45,8 @@ class GlobalAudioSession {
|
|
|
45
45
|
await this.setupAudioPipeline(clip);
|
|
46
46
|
this.activeClips.add(clip.id);
|
|
47
47
|
await this.deps.resourceLoader.fetch(clip.resourceId, {
|
|
48
|
-
priority: "high"
|
|
48
|
+
priority: "high",
|
|
49
|
+
clipId: clip.id
|
|
49
50
|
});
|
|
50
51
|
this.deps.eventBus.emit(MeframeEvent.ClipActivated, { clipId: clip.id });
|
|
51
52
|
}
|
|
@@ -84,7 +85,7 @@ class GlobalAudioSession {
|
|
|
84
85
|
}
|
|
85
86
|
async setupAudioPipeline(clip) {
|
|
86
87
|
const { id: clipId, resourceId, startUs, durationUs } = clip;
|
|
87
|
-
const audioDemuxWorker = await this.deps.workers.get("audioDemux",
|
|
88
|
+
const audioDemuxWorker = await this.deps.workers.get("audioDemux", clipId, { lazy: true });
|
|
88
89
|
const decodeWorker = await this.deps.workers.get("decode");
|
|
89
90
|
const demuxToDecodeChannel = new MessageChannel();
|
|
90
91
|
await audioDemuxWorker.send(
|
|
@@ -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';\n\ninterface AudioDataMessage {\n clipId: string;\n audioData: AudioData;\n clipStartUs: TimeUs;\n clipDurationUs: TimeUs;\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 { clipId, audioData, clipStartUs, clipDurationUs } = message;\n this.deps.cacheManager.putClipAudioData(clipId, 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 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 });\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 clipId = metadata.clipId || '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 clipId,\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 private async setupAudioPipeline(clip: Clip): Promise<void> {\n const { id: clipId, resourceId, startUs, durationUs } = clip;\n const audioDemuxWorker = await this.deps.workers.get('audioDemux',
|
|
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';\n\ninterface AudioDataMessage {\n clipId: string;\n audioData: AudioData;\n clipStartUs: TimeUs;\n clipDurationUs: TimeUs;\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 { clipId, audioData, clipStartUs, clipDurationUs } = message;\n this.deps.cacheManager.putClipAudioData(clipId, 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 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 clipId: clip.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 clipId = metadata.clipId || '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 clipId,\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 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoComposer.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/VideoComposer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,EAGjB,MAAM,SAAS,CAAC;AAKjB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,UAAU,cAAc;IACtB,aAAa,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC;IAC9C,WAAW,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IAEjC,OAAO,CAAC,GAAG,CAAoC;IAC/C,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,eAAe,CAAyB;gBAEpC,MAAM,EAAE,kBAAkB;IAyBtC,OAAO,CAAC,aAAa;
|
|
1
|
+
{"version":3,"file":"VideoComposer.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/VideoComposer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,gBAAgB,EAGjB,MAAM,SAAS,CAAC;AAKjB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,UAAU,cAAc;IACtB,aAAa,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC;IAC9C,WAAW,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IAEjC,OAAO,CAAC,GAAG,CAAoC;IAC/C,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,eAAe,CAAyB;gBAEpC,MAAM,EAAE,kBAAkB;IAyBtC,OAAO,CAAC,aAAa;IAuBrB,aAAa,CAAC,YAAY,CAAC,EAAE,kBAAkB,GAAG,cAAc;IAoC1D,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAkE7D,iBAAiB,CACrB,WAAW,EAAE,cAAc,EAC3B,SAAS,EAAE,cAAc,EACzB,UAAU,EAAE,gBAAgB,GAC3B,OAAO,CAAC,aAAa,CAAC;IAWzB,OAAO,CAAC,WAAW;YAaL,iBAAiB;IAW/B,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI;IAmBvD,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -47,6 +47,7 @@ class VideoComposer {
|
|
|
47
47
|
clipId: "default",
|
|
48
48
|
trackId: "main",
|
|
49
49
|
clipStartUs: 0,
|
|
50
|
+
clipDurationUs: Infinity,
|
|
50
51
|
compositionFps: 30
|
|
51
52
|
}
|
|
52
53
|
};
|
|
@@ -64,8 +65,7 @@ class VideoComposer {
|
|
|
64
65
|
metadata: result.metadata
|
|
65
66
|
});
|
|
66
67
|
},
|
|
67
|
-
flush: async (
|
|
68
|
-
console.log("[VideoComposer] flush", controller.desiredSize);
|
|
68
|
+
flush: async () => {
|
|
69
69
|
this.filterProcessor.clearCache();
|
|
70
70
|
}
|
|
71
71
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoComposer.js","sources":["../../../src/stages/compose/VideoComposer.ts"],"sourcesContent":["import type {\n VideoComposeConfig,\n ComposeRequest,\n ComposeResult,\n TransitionEffect,\n VideoLayer,\n ComposeTimelineContext,\n} from './types';\nimport { frameDurationFromFps } from '../../utils/time-utils';\nimport { LayerRenderer } from './LayerRenderer';\nimport { TransitionProcessor } from './TransitionProcessor';\nimport { FilterProcessor } from './FilterProcessor';\nimport { ClipInstructionSet } from './instructions';\n\ninterface ComposeStreams {\n composeStream: WritableStream<ComposeRequest>;\n cacheStream: ReadableStream<VideoFrame>;\n}\n\n/**\n * VideoComposer - Main visual composition orchestrator\n */\nexport class VideoComposer {\n readonly config: Required<VideoComposeConfig>;\n readonly canvas: OffscreenCanvas;\n\n private ctx: OffscreenCanvasRenderingContext2D;\n private layerRenderer: LayerRenderer;\n private transitionProcessor: TransitionProcessor;\n private filterProcessor: FilterProcessor;\n private timelineContext: ComposeTimelineContext;\n\n constructor(config: VideoComposeConfig) {\n this.config = this.applyDefaults(config);\n this.canvas = new OffscreenCanvas(this.config.width, this.config.height);\n\n const ctx = this.canvas.getContext('2d', {\n alpha: true,\n desynchronized: true,\n willReadFrequently: false,\n colorSpace: 'srgb',\n });\n\n if (!ctx) {\n throw new Error('Failed to create 2D rendering context');\n }\n\n this.ctx = ctx;\n this.ctx.imageSmoothingEnabled = this.config.enableSmoothing;\n this.ctx.imageSmoothingQuality = 'high';\n\n this.layerRenderer = new LayerRenderer(ctx, this.config.width, this.config.height);\n this.transitionProcessor = new TransitionProcessor(this.config.width, this.config.height);\n this.filterProcessor = new FilterProcessor();\n this.timelineContext = this.config.timeline;\n }\n\n private applyDefaults(config: VideoComposeConfig): Required<VideoComposeConfig> {\n return {\n width: config.width || 720,\n height: config.height || 1280,\n fps: config.fps || 30,\n backgroundColor: config.backgroundColor ?? '#000',\n renderer: config.renderer ?? 'canvas2d',\n enableSmoothing: config.enableSmoothing ?? true,\n enableHardwareAcceleration: config.enableHardwareAcceleration ?? true,\n revision: config.revision ?? 0,\n inputHighWaterMark: config.inputHighWaterMark ?? 3,\n outputHighWaterMark: config.outputHighWaterMark ?? 1,\n maxLayers: config.maxLayers ?? 100,\n timeline: config.timeline ?? {\n clipId: 'default',\n trackId: 'main',\n clipStartUs: 0,\n compositionFps: 30,\n },\n };\n }\n\n createStreams(_instruction?: ClipInstructionSet): ComposeStreams {\n if (_instruction?.baseConfig.timeline) {\n this.timelineContext = _instruction.baseConfig.timeline;\n }\n\n // Always create new streams for each clip\n // ReadableStreams can only be consumed once\n const stream = new TransformStream<ComposeRequest, any>(\n {\n transform: async (request, controller) => {\n // console.log('[VideoComposer] transform', request, controller.desiredSize);\n const result = await this.composeFrame(request);\n controller.enqueue({\n frame: result.frame,\n metadata: result.metadata,\n });\n },\n\n flush: async (controller) => {\n console.log('[VideoComposer] flush', controller.desiredSize);\n this.filterProcessor.clearCache();\n },\n },\n {\n highWaterMark: this.config.inputHighWaterMark,\n },\n {\n highWaterMark: this.config.outputHighWaterMark,\n }\n );\n // const [encodeStream, cacheStream] = stream.readable.tee();\n return {\n composeStream: stream.writable,\n cacheStream: stream.readable,\n };\n }\n\n async composeFrame(request: ComposeRequest): Promise<ComposeResult> {\n if (request.layers.length > this.config.maxLayers) {\n throw new Error(`Too many layers: ${request.layers.length} > ${this.config.maxLayers}`);\n }\n\n this.clearCanvas();\n // const sortedLayers = this.sortLayers(request.layers);\n\n if (request.transition) {\n this.ctx.save();\n this.transitionProcessor.applyTransition(this.ctx, request.transition);\n }\n\n for (const layer of request.layers) {\n if (!layer.visible || layer.opacity <= 0) {\n // Close video frame for invisible layers\n if ((layer as any).type === 'video') {\n const vf = (layer as VideoLayer).videoFrame;\n vf?.close?.();\n }\n continue;\n }\n\n try {\n if (layer.filters && layer.filters.length > 0) {\n this.ctx.save();\n this.filterProcessor.applyFilters(this.ctx, layer.filters);\n }\n\n await this.layerRenderer.renderLayer(layer);\n\n if (layer.filters && layer.filters.length > 0) {\n this.ctx.restore();\n }\n } finally {\n // Always close video frame after rendering (or on error)\n if ((layer as any).type === 'video') {\n const vf = (layer as VideoLayer).videoFrame;\n vf?.close?.();\n }\n }\n }\n\n if (request.transition) {\n this.ctx.restore();\n }\n\n const frame = await this.createOutputFrame(request.timeUs);\n\n const gopSerial = (request as any).gopSerial;\n const isKeyframe = (request as any).isKeyframe;\n\n return {\n frame,\n timeUs: request.timeUs,\n metadata: {\n layerCount: request.layers.length,\n renderTime: 0,\n gpuAccelerated:\n this.config.enableHardwareAcceleration && this.config.renderer !== 'canvas2d',\n ...(typeof gopSerial === 'number' && { gopSerial }),\n ...(typeof isKeyframe === 'boolean' && { isKeyframe }),\n },\n };\n }\n\n async composeTransition(\n fromRequest: ComposeRequest,\n toRequest: ComposeRequest,\n transition: TransitionEffect\n ): Promise<ComposeResult> {\n await this.composeFrame(fromRequest);\n\n const toFrameRequest = {\n ...toRequest,\n transition,\n };\n\n return this.composeFrame(toFrameRequest);\n }\n\n private clearCanvas(): void {\n if (this.config.backgroundColor) {\n this.ctx.fillStyle = this.config.backgroundColor;\n this.ctx.fillRect(0, 0, this.config.width, this.config.height);\n } else {\n this.ctx.clearRect(0, 0, this.config.width, this.config.height);\n }\n }\n\n // private sortLayers(layers: Layer[]): Layer[] {\n // return [...layers].sort((a, b) => a.zIndex - b.zIndex);\n // }\n\n private async createOutputFrame(timeUs: number): Promise<VideoFrame> {\n const duration = frameDurationFromFps(this.timelineContext.compositionFps);\n const frame = new VideoFrame(this.canvas, {\n timestamp: timeUs,\n duration,\n alpha: 'discard',\n visibleRect: { x: 0, y: 0, width: this.canvas.width, height: this.canvas.height },\n });\n return frame;\n }\n\n updateConfig(config: Partial<VideoComposeConfig>): void {\n Object.assign(this.config, this.applyDefaults({ ...this.config, ...config }));\n\n if (config.width || config.height) {\n this.canvas.width = this.config.width;\n this.canvas.height = this.config.height;\n this.layerRenderer.updateDimensions(this.config.width, this.config.height);\n this.transitionProcessor.updateDimensions(this.config.width, this.config.height);\n }\n\n if (config.enableSmoothing !== undefined) {\n this.ctx.imageSmoothingEnabled = this.config.enableSmoothing;\n }\n\n if (config.timeline) {\n this.timelineContext = config.timeline;\n }\n }\n\n dispose(): void {\n this.filterProcessor.clearCache();\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VideoComposer.js","sources":["../../../src/stages/compose/VideoComposer.ts"],"sourcesContent":["import type {\n VideoComposeConfig,\n ComposeRequest,\n ComposeResult,\n TransitionEffect,\n VideoLayer,\n ComposeTimelineContext,\n} from './types';\nimport { frameDurationFromFps } from '../../utils/time-utils';\nimport { LayerRenderer } from './LayerRenderer';\nimport { TransitionProcessor } from './TransitionProcessor';\nimport { FilterProcessor } from './FilterProcessor';\nimport { ClipInstructionSet } from './instructions';\n\ninterface ComposeStreams {\n composeStream: WritableStream<ComposeRequest>;\n cacheStream: ReadableStream<VideoFrame>;\n}\n\n/**\n * VideoComposer - Main visual composition orchestrator\n */\nexport class VideoComposer {\n readonly config: Required<VideoComposeConfig>;\n readonly canvas: OffscreenCanvas;\n\n private ctx: OffscreenCanvasRenderingContext2D;\n private layerRenderer: LayerRenderer;\n private transitionProcessor: TransitionProcessor;\n private filterProcessor: FilterProcessor;\n private timelineContext: ComposeTimelineContext;\n\n constructor(config: VideoComposeConfig) {\n this.config = this.applyDefaults(config);\n this.canvas = new OffscreenCanvas(this.config.width, this.config.height);\n\n const ctx = this.canvas.getContext('2d', {\n alpha: true,\n desynchronized: true,\n willReadFrequently: false,\n colorSpace: 'srgb',\n });\n\n if (!ctx) {\n throw new Error('Failed to create 2D rendering context');\n }\n\n this.ctx = ctx;\n this.ctx.imageSmoothingEnabled = this.config.enableSmoothing;\n this.ctx.imageSmoothingQuality = 'high';\n\n this.layerRenderer = new LayerRenderer(ctx, this.config.width, this.config.height);\n this.transitionProcessor = new TransitionProcessor(this.config.width, this.config.height);\n this.filterProcessor = new FilterProcessor();\n this.timelineContext = this.config.timeline;\n }\n\n private applyDefaults(config: VideoComposeConfig): Required<VideoComposeConfig> {\n return {\n width: config.width || 720,\n height: config.height || 1280,\n fps: config.fps || 30,\n backgroundColor: config.backgroundColor ?? '#000',\n renderer: config.renderer ?? 'canvas2d',\n enableSmoothing: config.enableSmoothing ?? true,\n enableHardwareAcceleration: config.enableHardwareAcceleration ?? true,\n revision: config.revision ?? 0,\n inputHighWaterMark: config.inputHighWaterMark ?? 3,\n outputHighWaterMark: config.outputHighWaterMark ?? 1,\n maxLayers: config.maxLayers ?? 100,\n timeline: config.timeline ?? {\n clipId: 'default',\n trackId: 'main',\n clipStartUs: 0,\n clipDurationUs: Infinity,\n compositionFps: 30,\n },\n };\n }\n\n createStreams(_instruction?: ClipInstructionSet): ComposeStreams {\n if (_instruction?.baseConfig.timeline) {\n this.timelineContext = _instruction.baseConfig.timeline;\n }\n\n // Always create new streams for each clip\n // ReadableStreams can only be consumed once\n const stream = new TransformStream<ComposeRequest, any>(\n {\n transform: async (request, controller) => {\n // console.log('[VideoComposer] transform', request, controller.desiredSize);\n const result = await this.composeFrame(request);\n controller.enqueue({\n frame: result.frame,\n metadata: result.metadata,\n });\n },\n\n flush: async () => {\n this.filterProcessor.clearCache();\n },\n },\n {\n highWaterMark: this.config.inputHighWaterMark,\n },\n {\n highWaterMark: this.config.outputHighWaterMark,\n }\n );\n // const [encodeStream, cacheStream] = stream.readable.tee();\n return {\n composeStream: stream.writable,\n cacheStream: stream.readable,\n };\n }\n\n async composeFrame(request: ComposeRequest): Promise<ComposeResult> {\n if (request.layers.length > this.config.maxLayers) {\n throw new Error(`Too many layers: ${request.layers.length} > ${this.config.maxLayers}`);\n }\n\n this.clearCanvas();\n // const sortedLayers = this.sortLayers(request.layers);\n\n if (request.transition) {\n this.ctx.save();\n this.transitionProcessor.applyTransition(this.ctx, request.transition);\n }\n\n for (const layer of request.layers) {\n if (!layer.visible || layer.opacity <= 0) {\n // Close video frame for invisible layers\n if ((layer as any).type === 'video') {\n const vf = (layer as VideoLayer).videoFrame;\n vf?.close?.();\n }\n continue;\n }\n\n try {\n if (layer.filters && layer.filters.length > 0) {\n this.ctx.save();\n this.filterProcessor.applyFilters(this.ctx, layer.filters);\n }\n\n await this.layerRenderer.renderLayer(layer);\n\n if (layer.filters && layer.filters.length > 0) {\n this.ctx.restore();\n }\n } finally {\n // Always close video frame after rendering (or on error)\n if ((layer as any).type === 'video') {\n const vf = (layer as VideoLayer).videoFrame;\n vf?.close?.();\n }\n }\n }\n\n if (request.transition) {\n this.ctx.restore();\n }\n\n const frame = await this.createOutputFrame(request.timeUs);\n\n const gopSerial = (request as any).gopSerial;\n const isKeyframe = (request as any).isKeyframe;\n\n return {\n frame,\n timeUs: request.timeUs,\n metadata: {\n layerCount: request.layers.length,\n renderTime: 0,\n gpuAccelerated:\n this.config.enableHardwareAcceleration && this.config.renderer !== 'canvas2d',\n ...(typeof gopSerial === 'number' && { gopSerial }),\n ...(typeof isKeyframe === 'boolean' && { isKeyframe }),\n },\n };\n }\n\n async composeTransition(\n fromRequest: ComposeRequest,\n toRequest: ComposeRequest,\n transition: TransitionEffect\n ): Promise<ComposeResult> {\n await this.composeFrame(fromRequest);\n\n const toFrameRequest = {\n ...toRequest,\n transition,\n };\n\n return this.composeFrame(toFrameRequest);\n }\n\n private clearCanvas(): void {\n if (this.config.backgroundColor) {\n this.ctx.fillStyle = this.config.backgroundColor;\n this.ctx.fillRect(0, 0, this.config.width, this.config.height);\n } else {\n this.ctx.clearRect(0, 0, this.config.width, this.config.height);\n }\n }\n\n // private sortLayers(layers: Layer[]): Layer[] {\n // return [...layers].sort((a, b) => a.zIndex - b.zIndex);\n // }\n\n private async createOutputFrame(timeUs: number): Promise<VideoFrame> {\n const duration = frameDurationFromFps(this.timelineContext.compositionFps);\n const frame = new VideoFrame(this.canvas, {\n timestamp: timeUs,\n duration,\n alpha: 'discard',\n visibleRect: { x: 0, y: 0, width: this.canvas.width, height: this.canvas.height },\n });\n return frame;\n }\n\n updateConfig(config: Partial<VideoComposeConfig>): void {\n Object.assign(this.config, this.applyDefaults({ ...this.config, ...config }));\n\n if (config.width || config.height) {\n this.canvas.width = this.config.width;\n this.canvas.height = this.config.height;\n this.layerRenderer.updateDimensions(this.config.width, this.config.height);\n this.transitionProcessor.updateDimensions(this.config.width, this.config.height);\n }\n\n if (config.enableSmoothing !== undefined) {\n this.ctx.imageSmoothingEnabled = this.config.enableSmoothing;\n }\n\n if (config.timeline) {\n this.timelineContext = config.timeline;\n }\n }\n\n dispose(): void {\n this.filterProcessor.clearCache();\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-compose.worker.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/audio-compose.worker.ts"],"names":[],"mappings":"AA4BA;;;;;;;;;;;GAWG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,SAAS,CAAuD;IAGxE,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,WAAW,CAA4B;IAE/C,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,iBAAiB,CAAwC;IACjE,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAK;IACvC,OAAO,CAAC,MAAM,CAAS;;IAYvB,OAAO,CAAC,aAAa;IAarB,0DAA0D;YAC5C,aAAa;YAsCb,mBAAmB;IAmDjC;;;;OAIG;YACW,eAAe;
|
|
1
|
+
{"version":3,"file":"audio-compose.worker.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/audio-compose.worker.ts"],"names":[],"mappings":"AA4BA;;;;;;;;;;;GAWG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,SAAS,CAAuD;IAGxE,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,WAAW,CAA4B;IAE/C,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,iBAAiB,CAAwC;IACjE,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAK;IACvC,OAAO,CAAC,MAAM,CAAS;;IAYvB,OAAO,CAAC,aAAa;IAarB,0DAA0D;YAC5C,aAAa;YAsCb,mBAAmB;IAmDjC;;;;OAIG;YACW,eAAe;IA+C7B;;OAEG;IACH;;OAEG;IACH,OAAO,CAAC,cAAc;IAsCtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkBzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA6BzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAiB9B;;OAEG;YACW,cAAc;IAoB5B;;OAEG;YACW,aAAa;YAiBb,kBAAkB;YAkElB,iBAAiB;YA6CjB,WAAW;IAmCzB,OAAO,CAAC,iBAAiB;IA2BzB,OAAO,CAAC,eAAe;IAsCvB,OAAO,CAAC,cAAc;IAmCtB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,qBAAqB;YAqCf,iBAAiB;IAgB/B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,kBAAkB;CAsB3B;;AAUD,wBAAoB"}
|
|
@@ -102,7 +102,6 @@ class AudioComposeWorker {
|
|
|
102
102
|
if (initial) {
|
|
103
103
|
this.channel.state = WorkerState.Ready;
|
|
104
104
|
this.mixer = new AudioMixer(config);
|
|
105
|
-
console.log(">>>>>>>>>>>>>> handleConfigure", config);
|
|
106
105
|
this.ducker = new AudioDucker(config.sampleRate);
|
|
107
106
|
this.mixStream = this.mixer.createMixStream(this.ducker);
|
|
108
107
|
this.channel.notify("configured", {
|