@meframe/core 0.0.25 → 0.0.27
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/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +1 -1
- package/dist/Meframe.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +1 -4
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +3 -13
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/index.d.ts +1 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/l1/VideoL1Cache.d.ts +7 -40
- package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -1
- package/dist/cache/l1/VideoL1Cache.js +63 -251
- package/dist/cache/l1/VideoL1Cache.js.map +1 -1
- package/dist/cache/l1/types.d.ts +0 -2
- package/dist/cache/l1/types.d.ts.map +1 -1
- package/dist/cache/types.d.ts +0 -15
- package/dist/cache/types.d.ts.map +1 -1
- package/dist/model/CompositionModel.d.ts +7 -3
- package/dist/model/CompositionModel.d.ts.map +1 -1
- package/dist/model/CompositionModel.js +13 -6
- package/dist/model/CompositionModel.js.map +1 -1
- package/dist/model/RcFrame.d.ts +0 -4
- package/dist/model/RcFrame.d.ts.map +1 -1
- package/dist/model/RcFrame.js +0 -6
- package/dist/model/RcFrame.js.map +1 -1
- package/dist/model/patch.js +22 -59
- package/dist/model/patch.js.map +1 -1
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +29 -39
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/VideoClipSession.d.ts +4 -5
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
- package/dist/orchestrator/VideoClipSession.js +32 -35
- package/dist/orchestrator/VideoClipSession.js.map +1 -1
- package/dist/stages/compose/LayerRenderer.d.ts.map +1 -1
- package/dist/stages/compose/VideoComposer.d.ts +1 -7
- package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
- package/dist/stages/compose/types.d.ts +1 -9
- package/dist/stages/compose/types.d.ts.map +1 -1
- package/dist/stages/decode/BaseDecoder.d.ts.map +1 -1
- package/dist/stages/decode/BaseDecoder.js +5 -0
- package/dist/stages/decode/BaseDecoder.js.map +1 -1
- package/dist/stages/decode/VideoChunkDecoder.d.ts +0 -4
- package/dist/stages/decode/VideoChunkDecoder.d.ts.map +1 -1
- package/dist/stages/decode/VideoChunkDecoder.js +3 -27
- package/dist/stages/decode/VideoChunkDecoder.js.map +1 -1
- package/dist/stages/decode/types.d.ts +1 -0
- package/dist/stages/decode/types.d.ts.map +1 -1
- package/dist/utils/time-utils.js +0 -16
- package/dist/utils/time-utils.js.map +1 -1
- package/dist/workers/{BaseDecoder.Bk26nCBk.js → BaseDecoder.BWYu1W0B.js} +6 -1
- package/dist/workers/BaseDecoder.BWYu1W0B.js.map +1 -0
- package/dist/workers/stages/compose/{video-compose.worker.B7xEVmN3.js → video-compose.worker.M5uomNVr.js} +50 -64
- package/dist/workers/stages/compose/video-compose.worker.M5uomNVr.js.map +1 -0
- package/dist/workers/stages/decode/{audio-decode.worker.Czb1P66a.js → audio-decode.worker.DnS17GD9.js} +2 -2
- package/dist/workers/stages/decode/{audio-decode.worker.Czb1P66a.js.map → audio-decode.worker.DnS17GD9.js.map} +1 -1
- package/dist/workers/stages/decode/{video-decode.worker.DLX8FRVc.js → video-decode.worker.BEYsjOXp.js} +5 -29
- package/dist/workers/stages/decode/video-decode.worker.BEYsjOXp.js.map +1 -0
- package/dist/workers/worker-manifest.json +3 -3
- package/package.json +1 -1
- package/dist/cache/l1/gop-utils.d.ts +0 -10
- package/dist/cache/l1/gop-utils.d.ts.map +0 -1
- package/dist/cache/l1/gop-utils.js +0 -78
- package/dist/cache/l1/gop-utils.js.map +0 -1
- package/dist/workers/BaseDecoder.Bk26nCBk.js.map +0 -1
- package/dist/workers/stages/compose/video-compose.worker.B7xEVmN3.js.map +0 -1
- package/dist/workers/stages/decode/video-decode.worker.DLX8FRVc.js.map +0 -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 { ResourceConflictError, ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, RenderFrameOptions } from './types';\nimport { WorkerStatus, WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame, Clip } from '../model';\nimport { hasResourceId } from '../model/types';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { VideoClipSession } from './VideoClipSession';\nimport { ClipSessionManager } from './ClipSessionManager';\nimport { GlobalAudioSession } from './GlobalAudioSession';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { ExportOptions } from '@/types';\nimport { VideoChunkDecoder } from '../stages/decode/VideoChunkDecoder';\nimport { quantizeTimestampToFrame } from '../utils/time-utils';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n muxManager: MuxManager;\n\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 private ensureCacheDebounceTimer: number | null = null;\n private readonly ensureCacheDebounceDelay = 150;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n // Initialize WorkerPool with worker path from config\n this.workers = new WorkerPool({\n eventBus: this.eventBus,\n workerConfigs,\n workerPath: config.workerPath,\n workerExtension: config.workerExtension,\n });\n\n 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: config.projectId || this.config.global.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 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 this.muxManager = new MuxManager(\n this.cacheManager,\n this.audioSession,\n this.config.encode.audio as AudioEncoderConfig\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 'videoDecode',\n 'audioDecode',\n 'videoCompose',\n 'audioCompose',\n 'videoEncode',\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\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.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\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n // Apply patch and get affected clip IDs (simplified for 2-Clip strategy)\n // Note: addTrack/removeTrack already call buildIndexes() in patch.ts\n const affectedClipIds = applyModelPatch(this.compositionModel, patch);\n const clipUpdates = this.planner.applyPatch(patch, affectedClipIds);\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n affectedClips: Array.from(affectedClipIds),\n });\n\n // Process clip updates\n for (const update of clipUpdates) {\n if (update.type === 'remove') {\n this.activeClips.delete(update.clipId);\n }\n\n this.cacheManager.invalidateClip(update.clipId);\n await this.clipSessionManager.handlePlannerUpdate(update.clipId, update);\n }\n\n // Reactivate updated audio clips\n const reactivatedAudioClips: string[] = [];\n const reactivatedVideoClips: string[] = [];\n for (const clipId of affectedClipIds) {\n const clip = this.compositionModel.findClip(clipId);\n if (clip?.trackKind === 'audio') {\n await this.audioSession.deactivateClip(clipId);\n reactivatedAudioClips.push(clipId);\n } else if (clip?.trackKind === 'video') {\n // Video clip audio will be restarted via VideoClipSession pipeline restart\n reactivatedVideoClips.push(clipId);\n }\n }\n\n // Activate all audio clips (including reactivated ones)\n await this.audioSession.activateAllAudioClips();\n\n // Restart playback for affected clips (give pipeline time to decode first frame)\n const allReactivatedClips = [...reactivatedAudioClips, ...reactivatedVideoClips];\n if (allReactivatedClips.length > 0) {\n setTimeout(() => {\n for (const clipId of allReactivatedClips) {\n this.audioSession.restartPlayingClip(clipId);\n }\n }, 150);\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 resource = this.compositionModel.getResource(resourceId);\n if (!resource) {\n return;\n }\n\n // Main video/audio resources: data will flow naturally into pipeline\n if (resource.type === 'video' || resource.type === 'audio') {\n return;\n }\n\n // Attachment resources (fonts, images): update instructions for active clips\n const clipIds = this.compositionModel.getClipIdsByResourceId(resourceId);\n for (const clipId of clipIds) {\n // Only update active clips (in 2-Clip window)\n if (!this.clipSessionManager.isClipActive(clipId)) {\n continue;\n }\n\n const clip = this.compositionModel.findClip(clipId);\n if (!clip) {\n continue;\n }\n\n // Rebuild instructions with updated resource status\n const instructions = this.planner.getInstructions(clipId);\n if (!instructions) {\n continue;\n }\n\n // Send updated instructions to worker (no pipeline restart needed)\n const session = this.clipSessionManager.getSession(clipId);\n const visualWorker = session?.visualWorkerHandle;\n if (visualWorker) {\n visualWorker.send('install_instructions', instructions);\n }\n }\n }\n\n async restartWorker(type: WorkerType, clipId?: string): Promise<void> {\n const clipLocalTypes: WorkerType[] = [\n 'videoDemux',\n 'audioDemux',\n 'videoDecode',\n 'audioDecode',\n 'videoCompose',\n 'videoEncode',\n ];\n\n if (clipLocalTypes.includes(type) && !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.getOrCreate(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 }\n }\n\n async renderFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n const immediate = options?.immediate ?? true;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n // Detect clip change and proactively ensure cache for current/next\n if (this.currentClipId !== clip.id) {\n this.currentClipId = clip.id;\n void this.ensureClipCache(timeUs, immediate);\n }\n\n // Calculate clip-relative time for cache lookup (global time - clip start time)\n let relativeTimeUs = options?.relativeTimeUs ?? timeUs - clip.startUs;\n\n // Quantize to frame boundary to handle timestamp precision issues\n relativeTimeUs = quantizeTimestampToFrame(relativeTimeUs, 0, this.compositionModel.fps);\n\n // Clamp to clip duration to handle edge cases where quantization pushes beyond clip end\n // This can happen when clip duration is not exactly aligned to frame boundaries\n relativeTimeUs = Math.min(relativeTimeUs, clip.durationUs - 1);\n\n const cachedFrame = this.cacheManager.getFrame(relativeTimeUs, clip.id);\n if (cachedFrame) {\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n return cachedFrame;\n }\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n\n if (signal?.aborted) {\n throw new DOMException('Render aborted', 'AbortError');\n }\n\n // L1 miss - try decode from L2\n const l2Frame = await this.decodeFromL2(relativeTimeUs, clip);\n if (l2Frame) {\n // If we are serving video from L2, opportunistically ensure audio from L2 as well\n void this.ensureAudioFromL2(clip.id);\n return l2Frame;\n }\n\n return null;\n }\n\n private async ensureAudioFromL2(clipId: string): Promise<void> {\n try {\n // Skip if PCM already present\n if (this.cacheManager.hasClipPCM(clipId)) {\n return;\n }\n // Check if L2 has encoded audio for this clip\n const hasAudio = await this.cacheManager.hasClipInL2(clipId, 'audio');\n if (!hasAudio) return;\n // Decode from L2 into PCM cache (non-blocking for render)\n await this.audioSession.ensureClipAudioFromL2(clipId);\n } catch (error) {\n console.warn('[Orchestrator] ensureAudioFromL2IfNeeded error:', error);\n }\n }\n\n // TODO: move to @ClipSessionManager\n private async decodeFromL2(timeUs: TimeUs, clip: Clip): Promise<RcFrame | null> {\n const { id, trackId, startUs } = clip;\n const [chunkStream, metadata] = await Promise.all([\n this.cacheManager.l2Cache.createReadStream(id, 'video'),\n this.cacheManager.l2Cache.getClipMetadata(id, 'video'),\n ]);\n\n if (!chunkStream || !metadata?.codec) {\n return null;\n }\n\n const decoder = new VideoChunkDecoder(`l2-temp-${id}`, {\n codec: metadata.codec,\n width: metadata.codedWidth,\n height: metadata.codedHeight,\n description: metadata.description,\n hardwareAcceleration: metadata.hardwareAcceleration || 'no-preference',\n });\n\n try {\n const decodeStream = chunkStream.pipeThrough(decoder.createStream());\n\n let targetFrame: RcFrame | null = null;\n\n await this.cacheManager.receiveComposedFrames(decodeStream, {\n clipId: id,\n trackId: trackId || this.compositionModel?.mainTrackId || 'main',\n fps: this.compositionModel?.fps ?? 30,\n clipStartUs: startUs,\n onFrame: (info) => {\n if (info.timeUs === timeUs) {\n targetFrame = this.cacheManager.getFrame(timeUs, id);\n }\n },\n });\n\n return targetFrame;\n } finally {\n await decoder.close();\n }\n }\n\n /**\n * Ensure clips are cached using 2-Clip strategy\n * Debounced to avoid excessive session activation during fast seek\n * @param timeUs - Target time for cache window\n * @param immediate - Skip debounce if true (used for initial load)\n */\n async ensureClipCache(timeUs: TimeUs, immediate = false): Promise<void> {\n const executeCache = async (): Promise<void> => {\n if (!this.compositionModel) return;\n\n const clipIds = this.compositionModel.getClipsToCacheAtTime(timeUs);\n if (clipIds.size === 0) return;\n await this.clipSessionManager.ensureClips(clipIds);\n };\n\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n if (immediate) {\n return executeCache();\n }\n\n return new Promise((resolve) => {\n this.ensureCacheDebounceTimer = setTimeout(async () => {\n this.ensureCacheDebounceTimer = null;\n await executeCache();\n resolve();\n }, this.ensureCacheDebounceDelay) as unknown as number;\n });\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId);\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n // For buffering scenario, we just need ANY frames in the clip\n // Don't restrict to frames after a specific startTimeUs, as the pipeline\n // might be rendering from the beginning while playback is in the middle\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 5,\n timeoutMs: options?.timeoutMs ?? 5_000,\n // Don't pass startTimeUs - count all frames in the clip\n });\n }\n\n // TODO: move to @ClipSessionManager\n async renderClipForL2(clipId: string): Promise<boolean> {\n const sessionId = `${clipId}#l2`;\n let session: VideoClipSession | null = null;\n\n return new Promise<boolean>((resolve, reject) => {\n this.createSession(sessionId, {\n forL2Only: true,\n clipId: clipId,\n onL2Complete: () => {\n resolve(true);\n },\n onL2Error: (error) => {\n console.error('[Orchestrator] L2 rendering failed for', clipId, error);\n reject(error);\n },\n })\n .then((s) => {\n session = s;\n return session.activate();\n })\n .catch(async (error) => {\n // Clean up partial session on any error to avoid worker state pollution\n if (session) {\n await session.dispose();\n }\n\n if (error instanceof ResourceConflictError) {\n resolve(false);\n } else {\n reject(error);\n }\n });\n });\n }\n\n // TODO: move to @ClipSessionManager\n private async createSession(\n sessionId: string,\n options?: {\n forL2Only?: boolean;\n clipId?: string;\n onL2Complete?: () => void;\n onL2Error?: (error: Error) => void;\n }\n ): Promise<VideoClipSession> {\n const clipId = options?.clipId ?? sessionId;\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 sessionId,\n forL2Only: options?.forL2Only ?? false,\n planner: this.planner,\n workerPool: this.workers,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n workerConfigs: this.buildWorkerConfigs(),\n resourceLoader: this.resourceLoader,\n callbacks: {\n onComposedStreamReady: (stream, fps) => {\n if (options?.forL2Only) {\n // L2 channel: don't need L1, cancel stream\n stream.cancel();\n } else {\n // Preview channel: fill L1\n this.cacheManager.receiveComposedFrames(stream, {\n clipId: clipId,\n trackId: this.compositionModel!.mainTrackId,\n fps,\n clipStartUs: clip.startUs,\n onFrame: () => {},\n });\n }\n },\n onEncodedStreamReady: (stream, track) => {\n if (options?.forL2Only) {\n // L2 channel: write to L2 using clipId, notify on complete\n this.cacheManager.receiveEncodedChunks(stream, clipId, track, {\n onComplete: () => {\n session.dispose();\n // Only notify completion for video track (audio is optional)\n if (track === 'video' && options.onL2Complete) {\n options.onL2Complete();\n }\n },\n onError: (error) => {\n console.error(\n `[Orchestrator] L2 encode stream error for ${clipId} ${track}:`,\n error\n );\n session.dispose();\n if (options.onL2Error) {\n options.onL2Error(error);\n }\n },\n });\n } else {\n // Preview channel: don't write to L2, cancel stream\n stream.cancel();\n }\n },\n onAudioStreamReady: (stream, metadata) => {\n if (options?.forL2Only) {\n stream.cancel();\n } else {\n this.audioSession.handleAudioStream(stream, metadata);\n }\n },\n onPipelineReady: async (attachmentResourceIds?: string[]) => {\n // 1. Load main track resource\n const clip = this.compositionModel?.findClip(clipId);\n if (clip && hasResourceId(clip)) {\n await this.resourceLoader.fetch(clip.resourceId, {\n priority: options?.forL2Only ? 'low' : 'high',\n sessionId: sessionId,\n trackId: clip.trackId,\n isMainTrack: true,\n });\n }\n\n // 2. Load attachment resources (global shared Blob cache)\n if (attachmentResourceIds && attachmentResourceIds.length > 0) {\n for (const resourceId of attachmentResourceIds) {\n await this.resourceLoader.fetch(resourceId, {\n priority: 'normal',\n sessionId: sessionId,\n isMainTrack: false,\n });\n }\n }\n },\n },\n });\n\n this.activeClips.add(sessionId);\n return session;\n }\n\n async dispose(): Promise<void> {\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n this.resourceLoader.dispose();\n await this.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 ?? 720;\n const defaultCanvasHeight = config.global?.defaultCanvasHeight ?? 1280;\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 videoDecode: config.decode?.video,\n audioDecode: config.decode?.audio,\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 fonts: config.global?.fonts,\n },\n audioCompose: {\n ducking: config.compose?.audio?.ducking,\n mixing: config.compose?.audio?.mixing,\n },\n videoEncode: {\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: 'no-preference',\n ...(config.encode?.video as any),\n },\n };\n }\n\n async export(model: CompositionModel, options: ExportOptions): Promise<Blob> {\n return this.muxManager.export(model, options);\n }\n}\n"],"names":["applyModelPatch","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 { ResourceConflictError, ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, RenderFrameOptions } from './types';\nimport { WorkerStatus, WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame, Clip } from '../model';\nimport { hasResourceId } from '../model/types';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { VideoClipSession } from './VideoClipSession';\nimport { ClipSessionManager } from './ClipSessionManager';\nimport { GlobalAudioSession } from './GlobalAudioSession';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { ExportOptions } from '@/types';\nimport { VideoChunkDecoder } from '../stages/decode/VideoChunkDecoder';\nimport { quantizeTimestampToFrame } from '../utils/time-utils';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n muxManager: MuxManager;\n\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 private ensureCacheDebounceTimer: number | null = null;\n private readonly ensureCacheDebounceDelay = 150;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n // Initialize WorkerPool with worker path from config\n this.workers = new WorkerPool({\n eventBus: this.eventBus,\n workerConfigs,\n workerPath: config.workerPath,\n workerExtension: config.workerExtension,\n });\n\n 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: config.projectId || this.config.global.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 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 this.muxManager = new MuxManager(\n this.cacheManager,\n this.audioSession,\n this.config.encode.audio as AudioEncoderConfig\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 'videoDecode',\n 'audioDecode',\n 'videoCompose',\n 'audioCompose',\n 'videoEncode',\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\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.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\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n // Apply patch and get affected clip IDs (simplified for 2-Clip strategy)\n // Note: addTrack/removeTrack already call buildIndexes() in patch.ts\n const affectedClipIds = applyModelPatch(this.compositionModel, patch);\n const clipUpdates = this.planner.applyPatch(patch, affectedClipIds);\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n affectedClips: Array.from(affectedClipIds),\n });\n\n // Process clip updates\n for (const update of clipUpdates) {\n if (update.type === 'remove') {\n this.activeClips.delete(update.clipId);\n }\n\n this.cacheManager.invalidateClip(update.clipId);\n await this.clipSessionManager.handlePlannerUpdate(update.clipId, update);\n }\n\n // Reactivate updated audio clips\n const reactivatedAudioClips: string[] = [];\n const reactivatedVideoClips: string[] = [];\n for (const clipId of affectedClipIds) {\n const clip = this.compositionModel.findClip(clipId);\n if (clip?.trackKind === 'audio') {\n await this.audioSession.deactivateClip(clipId);\n reactivatedAudioClips.push(clipId);\n } else if (clip?.trackKind === 'video') {\n // Video clip audio will be restarted via VideoClipSession pipeline restart\n reactivatedVideoClips.push(clipId);\n }\n }\n\n // Activate all audio clips (including reactivated ones)\n await this.audioSession.activateAllAudioClips();\n\n // Restart playback for affected clips (give pipeline time to decode first frame)\n const allReactivatedClips = [...reactivatedAudioClips, ...reactivatedVideoClips];\n if (allReactivatedClips.length > 0) {\n setTimeout(() => {\n for (const clipId of allReactivatedClips) {\n this.audioSession.restartPlayingClip(clipId);\n }\n }, 150);\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 resource = this.compositionModel.getResource(resourceId);\n if (!resource) {\n return;\n }\n\n // Main video/audio resources: data will flow naturally into pipeline\n if (resource.type === 'video' || resource.type === 'audio') {\n return;\n }\n\n // Attachment resources (fonts, images): update instructions for active clips\n const clipIds = this.compositionModel.getClipIdsByResourceId(resourceId);\n for (const clipId of clipIds) {\n // Only update active clips (in 2-Clip window)\n if (!this.clipSessionManager.isClipActive(clipId)) {\n continue;\n }\n\n const clip = this.compositionModel.findClip(clipId);\n if (!clip) {\n continue;\n }\n\n // Rebuild instructions with updated resource status\n const instructions = this.planner.getInstructions(clipId);\n if (!instructions) {\n continue;\n }\n\n // Send updated instructions to worker (no pipeline restart needed)\n const session = this.clipSessionManager.getSession(clipId);\n const composeWorker = session?.composeWorker;\n if (composeWorker) {\n composeWorker.send('install_instructions', instructions);\n }\n }\n }\n\n async restartWorker(type: WorkerType, clipId?: string): Promise<void> {\n const clipLocalTypes: WorkerType[] = [\n 'videoDemux',\n 'audioDemux',\n 'videoDecode',\n 'audioDecode',\n 'videoCompose',\n 'videoEncode',\n ];\n\n if (clipLocalTypes.includes(type) && !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.getOrCreate(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 }\n }\n\n async renderFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n const immediate = options?.immediate ?? true;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n // Detect clip change and proactively ensure cache for current/next\n if (this.currentClipId !== clip.id) {\n this.currentClipId = clip.id;\n void this.ensureClipCache(timeUs, immediate);\n }\n\n // Calculate clip-relative time for cache lookup (global time - clip start time)\n let relativeTimeUs = options?.relativeTimeUs ?? timeUs - clip.startUs;\n\n // Quantize to frame boundary to handle timestamp precision issues\n relativeTimeUs = quantizeTimestampToFrame(relativeTimeUs, 0, this.compositionModel.fps);\n\n // Clamp to clip duration to handle edge cases where quantization pushes beyond clip end\n // This can happen when clip duration is not exactly aligned to frame boundaries\n relativeTimeUs = Math.min(relativeTimeUs, clip.durationUs - 1);\n\n const cachedFrame = this.cacheManager.getFrame(relativeTimeUs, clip.id);\n if (cachedFrame) {\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n return cachedFrame;\n }\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n\n if (signal?.aborted) {\n throw new DOMException('Render aborted', 'AbortError');\n }\n\n // L1 miss - try decode from L2\n const l2Frame = await this.decodeFromL2(relativeTimeUs, clip);\n if (l2Frame) {\n // If we are serving video from L2, opportunistically ensure audio from L2 as well\n void this.ensureAudioFromL2(clip.id);\n return l2Frame;\n }\n\n return null;\n }\n\n private async ensureAudioFromL2(clipId: string): Promise<void> {\n try {\n // Skip if PCM already present\n if (this.cacheManager.hasClipPCM(clipId)) {\n return;\n }\n // Check if L2 has encoded audio for this clip\n const hasAudio = await this.cacheManager.hasClipInL2(clipId, 'audio');\n if (!hasAudio) return;\n // Decode from L2 into PCM cache (non-blocking for render)\n await this.audioSession.ensureClipAudioFromL2(clipId);\n } catch (error) {\n console.warn('[Orchestrator] ensureAudioFromL2IfNeeded error:', error);\n }\n }\n\n // TODO: move to @ClipSessionManager\n private async decodeFromL2(timeUs: TimeUs, clip: Clip): Promise<RcFrame | null> {\n const { id, trackId, startUs } = clip;\n const [chunkStream, metadata] = await Promise.all([\n this.cacheManager.l2Cache.createReadStream(id, 'video'),\n this.cacheManager.l2Cache.getClipMetadata(id, 'video'),\n ]);\n\n if (!chunkStream || !metadata?.codec) {\n return null;\n }\n\n const decoder = new VideoChunkDecoder(`l2-temp-${id}`, {\n codec: metadata.codec,\n width: metadata.codedWidth,\n height: metadata.codedHeight,\n description: metadata.description,\n hardwareAcceleration: metadata.hardwareAcceleration || 'no-preference',\n thread: 'main',\n });\n\n try {\n const decodeStream = chunkStream.pipeThrough(decoder.createStream());\n\n let targetFrame: RcFrame | null = null;\n\n await this.cacheManager.receiveComposedFrames(decodeStream, {\n clipId: id,\n trackId: trackId || this.compositionModel?.mainTrackId || 'main',\n fps: this.compositionModel?.fps ?? 30,\n clipStartUs: startUs,\n onFrame: (info) => {\n if (info.timeUs === timeUs) {\n targetFrame = this.cacheManager.getFrame(timeUs, id);\n }\n },\n });\n\n return targetFrame;\n } finally {\n await decoder.close();\n }\n }\n\n /**\n * Ensure clips are cached using 2-Clip strategy\n * Debounced to avoid excessive session activation during fast seek\n * @param timeUs - Target time for cache window\n * @param immediate - Skip debounce if true (used for initial load)\n */\n async ensureClipCache(timeUs: TimeUs, immediate = false): Promise<void> {\n const executeCache = async (): Promise<void> => {\n if (!this.compositionModel) return;\n\n const clipIds = this.compositionModel.getClipsToCacheAtTime(timeUs);\n if (clipIds.size === 0) return;\n await this.clipSessionManager.ensureClips(clipIds);\n };\n\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n if (immediate) {\n return executeCache();\n }\n\n return new Promise((resolve) => {\n this.ensureCacheDebounceTimer = setTimeout(async () => {\n this.ensureCacheDebounceTimer = null;\n await executeCache();\n resolve();\n }, this.ensureCacheDebounceDelay) as unknown as number;\n });\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId);\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n // For buffering scenario, we just need ANY frames in the clip\n // Don't restrict to frames after a specific startTimeUs, as the pipeline\n // might be rendering from the beginning while playback is in the middle\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 5,\n timeoutMs: options?.timeoutMs ?? 5_000,\n // Don't pass startTimeUs - count all frames in the clip\n });\n }\n\n // TODO: move to @ClipSessionManager\n async renderClipForL2(clipId: string): Promise<boolean> {\n const sessionId = `${clipId}#l2`;\n let session: VideoClipSession | null = null;\n\n return new Promise<boolean>((resolve, reject) => {\n this.createSession(sessionId, {\n forL2Only: true,\n clipId: clipId,\n onL2Complete: () => {\n resolve(true);\n },\n onL2Error: (error) => {\n console.error('[Orchestrator] L2 rendering failed for', clipId, error);\n reject(error);\n },\n })\n .then((s) => {\n session = s;\n return session.activate();\n })\n .catch(async (error) => {\n // Clean up partial session on any error to avoid worker state pollution\n if (session) {\n await session.dispose();\n }\n\n if (error instanceof ResourceConflictError) {\n resolve(false);\n } else {\n reject(error);\n }\n });\n });\n }\n\n // TODO: move to @ClipSessionManager\n private async createSession(\n sessionId: string,\n options?: {\n forL2Only?: boolean;\n clipId?: string;\n onL2Complete?: () => void;\n onL2Error?: (error: Error) => void;\n }\n ): Promise<VideoClipSession> {\n const clipId = options?.clipId ?? sessionId;\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 sessionId,\n forL2Only: options?.forL2Only ?? false,\n planner: this.planner,\n workerPool: this.workers,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n workerConfigs: this.buildWorkerConfigs(),\n resourceLoader: this.resourceLoader,\n callbacks: {\n onComposedStreamReady: async (stream, fps) => {\n // Only preview channel calls this (L2 sends directly to encode worker)\n await this.cacheManager.receiveComposedFrames(stream, {\n clipId: clipId,\n trackId: this.compositionModel!.mainTrackId,\n fps,\n clipStartUs: clip.startUs,\n onFrame: () => {},\n });\n },\n onEncodedStreamReady: async (stream, track) => {\n // Only L2 channel calls this (preview has no encode worker)\n await this.cacheManager.receiveEncodedChunks(stream, clipId, track, {\n onComplete: () => {\n session.dispose();\n // Only notify completion for video track (audio is optional)\n if (track === 'video' && options?.onL2Complete) {\n options.onL2Complete();\n }\n },\n onError: (error) => {\n console.error(`[Orchestrator] L2 encode stream error for ${clipId} ${track}:`, error);\n session.dispose();\n if (options?.onL2Error) {\n options.onL2Error(error);\n }\n },\n });\n },\n onAudioStreamReady: (stream, metadata) => {\n if (options?.forL2Only) {\n stream.cancel();\n } else {\n this.audioSession.handleAudioStream(stream, metadata);\n }\n },\n onPipelineReady: async (attachmentResourceIds?: string[]) => {\n // 1. Load main track resource\n const clip = this.compositionModel?.findClip(clipId);\n if (clip && hasResourceId(clip)) {\n await this.resourceLoader.fetch(clip.resourceId, {\n priority: options?.forL2Only ? 'low' : 'high',\n sessionId: sessionId,\n trackId: clip.trackId,\n isMainTrack: true,\n });\n }\n\n // 2. Load attachment resources (global shared Blob cache)\n if (attachmentResourceIds && attachmentResourceIds.length > 0) {\n for (const resourceId of attachmentResourceIds) {\n await this.resourceLoader.fetch(resourceId, {\n priority: 'normal',\n sessionId: sessionId,\n isMainTrack: false,\n });\n }\n }\n },\n },\n });\n\n this.activeClips.add(sessionId);\n return session;\n }\n\n async dispose(): Promise<void> {\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n this.resourceLoader.dispose();\n await this.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 ?? 720;\n const defaultCanvasHeight = config.global?.defaultCanvasHeight ?? 1280;\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 videoDecode: config.decode?.video,\n audioDecode: config.decode?.audio,\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 fonts: config.global?.fonts,\n },\n audioCompose: {\n ducking: config.compose?.audio?.ducking,\n mixing: config.compose?.audio?.mixing,\n },\n videoEncode: {\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: 'no-preference',\n ...(config.encode?.video as any),\n },\n };\n }\n\n async export(model: CompositionModel, options: ExportOptions): Promise<Blob> {\n return this.muxManager.export(model, options);\n }\n}\n"],"names":["applyModelPatch","clip"],"mappings":";;;;;;;;;;;;;;;AAoBO,MAAM,aAAsC;AAAA,EACjD;AAAA,EACA;AAAA,EACA,mBAA4C;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEQ,kCAAkB,IAAA;AAAA,EAClB,gBAAgB;AAAA,EAChB,SAAS,aAAa,YAAA,EAAc,UAAA;AAAA,EACpC;AAAA,EACA,gBAA+B;AAAA,EAC/B,2BAA0C;AAAA,EACjC,2BAA2B;AAAA,EACnC;AAAA,EAET,YAAY,QAA4B;AAEtC,SAAK,WAAW,OAAO,YAAY,IAAI,SAAA;AACvC,SAAK,SAAS,KAAK,SAAS,WAAA;AAG5B,SAAK,SAAS,aAAa,YAAA,EAAc,UAAA;AAEzC,UAAM,gBAAgB,KAAK,mBAAA;AAG3B,SAAK,UAAU,IAAI,WAAW;AAAA,MAC5B,UAAU,KAAK;AAAA,MACf;AAAA,MACA,YAAY,OAAO;AAAA,MACnB,iBAAiB,OAAO;AAAA,IAAA,CACzB;AAED,SAAK,iBAAiB,IAAI,eAAe;AAAA,MACvC,cAAc;AAAA,MACd,UAAU,KAAK;AAAA,MACf,QAAQ;AAAA,QACN,eAAe,OAAO,cAAe,KAAK,OAAO,MAAc,OAAO,eAAe;AAAA,MAAA;AAAA,MAEvF,eAAe,CAAC,YAAY,UAAU,KAAK,0BAA0B,YAAY,KAAK;AAAA,IAAA,CACvF;AAED,SAAK,UAAU,IAAI,mBAAA;AAEnB,UAAM,cAAc,OAAO,eAAe,KAAK,OAAO;AACtD,SAAK,eAAe,IAAI;AAAA,MACtB;AAAA,QACE,IAAI;AAAA,UACF,aACG,aAAqB,UAAW,aAAqB,IAAI,eAAe;AAAA,UAC3E,SAAU,KAAK,OAAO,QAAgB,OAAO,WAAW;AAAA,QAAA;AAAA,QAE1D,IAAI;AAAA,UACF,WAAY,aAAqB,UAAW,aAAqB,IAAI,aAAa;AAAA,UAClF,WAAW,OAAO,aAAa,KAAK,OAAO,OAAO,aAAa;AAAA,QAAA;AAAA,MACjE;AAAA,MAEF,KAAK;AAAA,IAAA;AAGP,SAAK,qBAAqB,IAAI,mBAAmB;AAAA,MAC/C,eAAe;AAAA,MACf,SAAS;AAAA,QACP,eAAe,CAAC,WAAW,KAAK,cAAc,MAAM;AAAA,MAAA;AAAA,MAEtD,cAAc,KAAK;AAAA,IAAA,CACpB;AAED,SAAK,eAAe,IAAI,mBAAmB;AAAA,MACzC,cAAc,KAAK;AAAA,MACnB,SAAS,KAAK;AAAA,MACd,gBAAgB,KAAK;AAAA,MACrB,UAAU,KAAK;AAAA,MACf,UAAU,MAAM,KAAK;AAAA,MACrB,oBAAoB,MAAM,KAAK,mBAAA;AAAA,IAAmB,CACnD;AAED,SAAK,aAAa,IAAI;AAAA,MACpB,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,OAAO,OAAO;AAAA,IAAA;AAAA,EAEvB;AAAA,EAEA,IAAI,eAA6B;AAC/B,UAAM,SAAS,KAAK,QAAQ;AAC5B,UAAM,SAAuB,CAAA;AAE7B,UAAM,cAA4B;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAGF,eAAW,QAAQ,aAAa;AAC9B,aAAO,IAAI,IAAI,OAAO,IAAI,KAAK;AAAA,QAC7B,OAAO;AAAA,QACP,WAAW;AAAA,MAAA;AAAA,IAEf;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,aAA4B;AAChC,QAAI,KAAK,cAAe;AAExB,UAAM,KAAK,aAAa,KAAA;AAExB,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA,EAGA,GACE,OACA,SACM;AACN,SAAK,SAAS,GAAG,OAAO,OAAO;AAAA,EACjC;AAAA,EAEA,IACE,OACA,SACM;AACN,SAAK,SAAS,IAAI,OAAO,OAAO;AAAA,EAClC;AAAA,EAEA,KACE,OACA,SACM;AACN,SAAK,SAAS,KAAK,OAAO,OAAO;AAAA,EACnC;AAAA,EAEA,MAAM,oBAAoB,OAAwC;AAChE,SAAK,mBAAmB;AACxB,SAAK,QAAQ,SAAS,KAAK;AAC3B,SAAK,gBAAgB;AAErB,SAAK,SAAS,KAAK,aAAa,UAAU,KAAK;AAE/C,SAAK,SAAS,KAAK,aAAa,oBAAoB;AAAA,MAClD,YAAY,MAAM,OAAO;AAAA,MACzB,WAAW,MAAM,OAAO,OAAO,CAAC,KAAa,UAAe,MAAM,MAAM,MAAM,QAAQ,CAAC;AAAA,MACvF,YAAY,MAAM;AAAA,IAAA,CACnB;AAED,UAAM,KAAK,aAAa,sBAAA;AAAA,EAC1B;AAAA,EAEA,MAAM,WAAW,OAAwC;AACvD,QAAI,CAAC,KAAK,kBAAkB;AAC1B,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAIA,UAAM,kBAAkBA,WAAgB,KAAK,kBAAkB,KAAK;AACpE,UAAM,cAAc,KAAK,QAAQ,WAAW,OAAO,eAAe;AAClE,SAAK,SAAS,KAAK,aAAa,cAAc;AAAA,MAC5C,YAAY,MAAM,WAAW;AAAA,MAC7B,eAAe,MAAM,KAAK,eAAe;AAAA,IAAA,CAC1C;AAGD,eAAW,UAAU,aAAa;AAChC,UAAI,OAAO,SAAS,UAAU;AAC5B,aAAK,YAAY,OAAO,OAAO,MAAM;AAAA,MACvC;AAEA,WAAK,aAAa,eAAe,OAAO,MAAM;AAC9C,YAAM,KAAK,mBAAmB,oBAAoB,OAAO,QAAQ,MAAM;AAAA,IACzE;AAGA,UAAM,wBAAkC,CAAA;AACxC,UAAM,wBAAkC,CAAA;AACxC,eAAW,UAAU,iBAAiB;AACpC,YAAM,OAAO,KAAK,iBAAiB,SAAS,MAAM;AAClD,UAAI,MAAM,cAAc,SAAS;AAC/B,cAAM,KAAK,aAAa,eAAe,MAAM;AAC7C,8BAAsB,KAAK,MAAM;AAAA,MACnC,WAAW,MAAM,cAAc,SAAS;AAEtC,8BAAsB,KAAK,MAAM;AAAA,MACnC;AAAA,IACF;AAGA,UAAM,KAAK,aAAa,sBAAA;AAGxB,UAAM,sBAAsB,CAAC,GAAG,uBAAuB,GAAG,qBAAqB;AAC/E,QAAI,oBAAoB,SAAS,GAAG;AAClC,iBAAW,MAAM;AACf,mBAAW,UAAU,qBAAqB;AACxC,eAAK,aAAa,mBAAmB,MAAM;AAAA,QAC7C;AAAA,MACF,GAAG,GAAG;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,0BAA0B,YAAoB,OAAgC;AACpF,QAAI,CAAC,KAAK,kBAAkB;AAC1B;AAAA,IACF;AAEA,SAAK,iBAAiB,oBAAoB,YAAY,SAAS,SAAS;AAExE,QAAI,UAAU,SAAS;AACrB;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,iBAAiB,YAAY,UAAU;AAC7D,QAAI,CAAC,UAAU;AACb;AAAA,IACF;AAGA,QAAI,SAAS,SAAS,WAAW,SAAS,SAAS,SAAS;AAC1D;AAAA,IACF;AAGA,UAAM,UAAU,KAAK,iBAAiB,uBAAuB,UAAU;AACvE,eAAW,UAAU,SAAS;AAE5B,UAAI,CAAC,KAAK,mBAAmB,aAAa,MAAM,GAAG;AACjD;AAAA,MACF;AAEA,YAAM,OAAO,KAAK,iBAAiB,SAAS,MAAM;AAClD,UAAI,CAAC,MAAM;AACT;AAAA,MACF;AAGA,YAAM,eAAe,KAAK,QAAQ,gBAAgB,MAAM;AACxD,UAAI,CAAC,cAAc;AACjB;AAAA,MACF;AAGA,YAAM,UAAU,KAAK,mBAAmB,WAAW,MAAM;AACzD,YAAM,gBAAgB,SAAS;AAC/B,UAAI,eAAe;AACjB,sBAAc,KAAK,wBAAwB,YAAY;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,MAAkB,QAAgC;AACpE,UAAM,iBAA+B;AAAA,MACnC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAGF,QAAI,eAAe,SAAS,IAAI,KAAK,CAAC,QAAQ;AAC5C,YAAM,IAAI,MAAM,kCAAkC,IAAI,SAAS;AAAA,IACjE;AAEA,SAAK,QAAQ,UAAU,MAAM,MAAM;AACnC,UAAM,SAAS,MAAM,KAAK,QAAQ,YAAY,MAAM,MAAM;AAE1D,SAAK,SAAS,KAAK,aAAa,iBAAiB;AAAA,MAC/C;AAAA,MACA,UAAU,OAAO,YAAA;AAAA,MACjB,QAAQ;AAAA,IAAA,CACT;AAED,QAAI,QAAQ;AACV,YAAM,UAAU,KAAK,mBAAmB,WAAW,MAAM;AACzD,UAAI,SAAS;AACX,cAAM,QAAQ,SAAA;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,QAAgB,SAAuD;AACvF,UAAM,SAAS,SAAS;AACxB,UAAM,YAAY,SAAS,aAAa;AAExC,QAAI,CAAC,KAAK,kBAAkB;AAC1B,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,UAAM,OAAO,KAAK,iBAAiB,eAAe,QAAQ,KAAK,iBAAiB,WAAW,EAAE,CAAC;AAC9F,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,IACT;AAGA,QAAI,KAAK,kBAAkB,KAAK,IAAI;AAClC,WAAK,gBAAgB,KAAK;AAC1B,WAAK,KAAK,gBAAgB,QAAQ,SAAS;AAAA,IAC7C;AAGA,QAAI,iBAAiB,SAAS,kBAAkB,SAAS,KAAK;AAG9D,qBAAiB,yBAAyB,gBAAgB,GAAG,KAAK,iBAAiB,GAAG;AAItF,qBAAiB,KAAK,IAAI,gBAAgB,KAAK,aAAa,CAAC;AAE7D,UAAM,cAAc,KAAK,aAAa,SAAS,gBAAgB,KAAK,EAAE;AACtE,QAAI,aAAa;AACf,WAAK,SAAS,KAAK,aAAa,UAAU;AAAA,QACxC;AAAA,QACA,OAAO;AAAA,QACP,KAAK,GAAG,KAAK,EAAE,IAAI,cAAc;AAAA,MAAA,CAClC;AACD,aAAO;AAAA,IACT;AAEA,SAAK,SAAS,KAAK,aAAa,WAAW;AAAA,MACzC;AAAA,MACA,OAAO;AAAA,MACP,KAAK,GAAG,KAAK,EAAE,IAAI,cAAc;AAAA,IAAA,CAClC;AAED,QAAI,QAAQ,SAAS;AACnB,YAAM,IAAI,aAAa,kBAAkB,YAAY;AAAA,IACvD;AAGA,UAAM,UAAU,MAAM,KAAK,aAAa,gBAAgB,IAAI;AAC5D,QAAI,SAAS;AAEX,WAAK,KAAK,kBAAkB,KAAK,EAAE;AACnC,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,kBAAkB,QAA+B;AAC7D,QAAI;AAEF,UAAI,KAAK,aAAa,WAAW,MAAM,GAAG;AACxC;AAAA,MACF;AAEA,YAAM,WAAW,MAAM,KAAK,aAAa,YAAY,QAAQ,OAAO;AACpE,UAAI,CAAC,SAAU;AAEf,YAAM,KAAK,aAAa,sBAAsB,MAAM;AAAA,IACtD,SAAS,OAAO;AACd,cAAQ,KAAK,mDAAmD,KAAK;AAAA,IACvE;AAAA,EACF;AAAA;AAAA,EAGA,MAAc,aAAa,QAAgB,MAAqC;AAC9E,UAAM,EAAE,IAAI,SAAS,QAAA,IAAY;AACjC,UAAM,CAAC,aAAa,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,MAChD,KAAK,aAAa,QAAQ,iBAAiB,IAAI,OAAO;AAAA,MACtD,KAAK,aAAa,QAAQ,gBAAgB,IAAI,OAAO;AAAA,IAAA,CACtD;AAED,QAAI,CAAC,eAAe,CAAC,UAAU,OAAO;AACpC,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,IAAI,kBAAkB,WAAW,EAAE,IAAI;AAAA,MACrD,OAAO,SAAS;AAAA,MAChB,OAAO,SAAS;AAAA,MAChB,QAAQ,SAAS;AAAA,MACjB,aAAa,SAAS;AAAA,MACtB,sBAAsB,SAAS,wBAAwB;AAAA,MACvD,QAAQ;AAAA,IAAA,CACT;AAED,QAAI;AACF,YAAM,eAAe,YAAY,YAAY,QAAQ,cAAc;AAEnE,UAAI,cAA8B;AAElC,YAAM,KAAK,aAAa,sBAAsB,cAAc;AAAA,QAC1D,QAAQ;AAAA,QACR,SAAS,WAAW,KAAK,kBAAkB,eAAe;AAAA,QAC1D,KAAK,KAAK,kBAAkB,OAAO;AAAA,QACnC,aAAa;AAAA,QACb,SAAS,CAAC,SAAS;AACjB,cAAI,KAAK,WAAW,QAAQ;AAC1B,0BAAc,KAAK,aAAa,SAAS,QAAQ,EAAE;AAAA,UACrD;AAAA,QACF;AAAA,MAAA,CACD;AAED,aAAO;AAAA,IACT,UAAA;AACE,YAAM,QAAQ,MAAA;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,gBAAgB,QAAgB,YAAY,OAAsB;AACtE,UAAM,eAAe,YAA2B;AAC9C,UAAI,CAAC,KAAK,iBAAkB;AAE5B,YAAM,UAAU,KAAK,iBAAiB,sBAAsB,MAAM;AAClE,UAAI,QAAQ,SAAS,EAAG;AACxB,YAAM,KAAK,mBAAmB,YAAY,OAAO;AAAA,IACnD;AAEA,QAAI,KAAK,6BAA6B,MAAM;AAC1C,mBAAa,KAAK,wBAAwB;AAC1C,WAAK,2BAA2B;AAAA,IAClC;AAEA,QAAI,WAAW;AACb,aAAO,aAAA;AAAA,IACT;AAEA,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,WAAK,2BAA2B,WAAW,YAAY;AACrD,aAAK,2BAA2B;AAChC,cAAM,aAAA;AACN,gBAAA;AAAA,MACF,GAAG,KAAK,wBAAwB;AAAA,IAClC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,iBACJ,QACA,SACkB;AAClB,QAAI,CAAC,KAAK,kBAAkB;AAC1B,aAAO;AAAA,IACT;AAEA,UAAM,QAAQ,KAAK,iBAAiB,eAAe,QAAQ,KAAK,iBAAiB,WAAW;AAC5F,QAAI,MAAM,WAAW,GAAG;AACtB,aAAO;AAAA,IACT;AAEA,UAAM,cAAc,MAAM,CAAC;AAC3B,QAAI,CAAC,aAAa;AAChB,aAAO;AAAA,IACT;AAKA,WAAO,KAAK,aAAa,iBAAiB,YAAY,IAAI;AAAA,MACxD,eAAe,SAAS,iBAAiB;AAAA,MACzC,WAAW,SAAS,aAAa;AAAA;AAAA,IAAA,CAElC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,gBAAgB,QAAkC;AACtD,UAAM,YAAY,GAAG,MAAM;AAC3B,QAAI,UAAmC;AAEvC,WAAO,IAAI,QAAiB,CAAC,SAAS,WAAW;AAC/C,WAAK,cAAc,WAAW;AAAA,QAC5B,WAAW;AAAA,QACX;AAAA,QACA,cAAc,MAAM;AAClB,kBAAQ,IAAI;AAAA,QACd;AAAA,QACA,WAAW,CAAC,UAAU;AACpB,kBAAQ,MAAM,0CAA0C,QAAQ,KAAK;AACrE,iBAAO,KAAK;AAAA,QACd;AAAA,MAAA,CACD,EACE,KAAK,CAAC,MAAM;AACX,kBAAU;AACV,eAAO,QAAQ,SAAA;AAAA,MACjB,CAAC,EACA,MAAM,OAAO,UAAU;AAEtB,YAAI,SAAS;AACX,gBAAM,QAAQ,QAAA;AAAA,QAChB;AAEA,YAAI,iBAAiB,uBAAuB;AAC1C,kBAAQ,KAAK;AAAA,QACf,OAAO;AACL,iBAAO,KAAK;AAAA,QACd;AAAA,MACF,CAAC;AAAA,IACL,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAc,cACZ,WACA,SAM2B;AAC3B,UAAM,SAAS,SAAS,UAAU;AAClC,UAAM,OAAO,KAAK,kBAAkB,SAAS,MAAM;AACnD,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,MAAM,QAAQ,MAAM,YAAY;AAAA,IAC5C;AAEA,UAAM,UAAU,MAAM,iBAAiB,OAAO;AAAA,MAC5C;AAAA,MACA;AAAA,MACA,WAAW,SAAS,aAAa;AAAA,MACjC,SAAS,KAAK;AAAA,MACd,YAAY,KAAK;AAAA,MACjB,cAAc,KAAK;AAAA,MACnB,kBAAkB,KAAK;AAAA,MACvB,eAAe,KAAK,mBAAA;AAAA,MACpB,gBAAgB,KAAK;AAAA,MACrB,WAAW;AAAA,QACT,uBAAuB,OAAO,QAAQ,QAAQ;AAE5C,gBAAM,KAAK,aAAa,sBAAsB,QAAQ;AAAA,YACpD;AAAA,YACA,SAAS,KAAK,iBAAkB;AAAA,YAChC;AAAA,YACA,aAAa,KAAK;AAAA,YAClB,SAAS,MAAM;AAAA,YAAC;AAAA,UAAA,CACjB;AAAA,QACH;AAAA,QACA,sBAAsB,OAAO,QAAQ,UAAU;AAE7C,gBAAM,KAAK,aAAa,qBAAqB,QAAQ,QAAQ,OAAO;AAAA,YAClE,YAAY,MAAM;AAChB,sBAAQ,QAAA;AAER,kBAAI,UAAU,WAAW,SAAS,cAAc;AAC9C,wBAAQ,aAAA;AAAA,cACV;AAAA,YACF;AAAA,YACA,SAAS,CAAC,UAAU;AAClB,sBAAQ,MAAM,6CAA6C,MAAM,IAAI,KAAK,KAAK,KAAK;AACpF,sBAAQ,QAAA;AACR,kBAAI,SAAS,WAAW;AACtB,wBAAQ,UAAU,KAAK;AAAA,cACzB;AAAA,YACF;AAAA,UAAA,CACD;AAAA,QACH;AAAA,QACA,oBAAoB,CAAC,QAAQ,aAAa;AACxC,cAAI,SAAS,WAAW;AACtB,mBAAO,OAAA;AAAA,UACT,OAAO;AACL,iBAAK,aAAa,kBAAkB,QAAQ,QAAQ;AAAA,UACtD;AAAA,QACF;AAAA,QACA,iBAAiB,OAAO,0BAAqC;AAE3D,gBAAMC,QAAO,KAAK,kBAAkB,SAAS,MAAM;AACnD,cAAIA,SAAQ,cAAcA,KAAI,GAAG;AAC/B,kBAAM,KAAK,eAAe,MAAMA,MAAK,YAAY;AAAA,cAC/C,UAAU,SAAS,YAAY,QAAQ;AAAA,cACvC;AAAA,cACA,SAASA,MAAK;AAAA,cACd,aAAa;AAAA,YAAA,CACd;AAAA,UACH;AAGA,cAAI,yBAAyB,sBAAsB,SAAS,GAAG;AAC7D,uBAAW,cAAc,uBAAuB;AAC9C,oBAAM,KAAK,eAAe,MAAM,YAAY;AAAA,gBAC1C,UAAU;AAAA,gBACV;AAAA,gBACA,aAAa;AAAA,cAAA,CACd;AAAA,YACH;AAAA,UACF;AAAA,QACF;AAAA,MAAA;AAAA,IACF,CACD;AAED,SAAK,YAAY,IAAI,SAAS;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,UAAyB;AAC7B,QAAI,KAAK,6BAA6B,MAAM;AAC1C,mBAAa,KAAK,wBAAwB;AAC1C,WAAK,2BAA2B;AAAA,IAClC;AAEA,SAAK,eAAe,QAAA;AACpB,UAAM,KAAK,mBAAmB,QAAA;AAC9B,UAAM,KAAK,aAAa,MAAA;AAExB,SAAK,gBAAgB;AACrB,SAAK,YAAY,MAAA;AAEjB,SAAK,QAAQ,aAAA;AACb,SAAK,mBAAmB;AACxB,SAAK,SAAS,QAAA;AAAA,EAChB;AAAA,EAEQ,qBAA8C;AACpD,UAAM,SAAS,KAAK;AACpB,UAAM,qBAAqB,OAAO,QAAQ,sBAAsB;AAChE,UAAM,sBAAsB,OAAO,QAAQ,uBAAuB;AAClE,UAAM,aAAa,OAAO,QAAQ,cAAc;AAChD,WAAO;AAAA,MACL,YAAY;AAAA,QACV,eAAe,OAAO,OAAO,cAAc,iBAAiB;AAAA,MAAA;AAAA,MAE9D,YAAY;AAAA,QACV,eAAe,OAAO,OAAO,cAAc,iBAAiB;AAAA,MAAA;AAAA,MAE9D,aAAa,OAAO,QAAQ;AAAA,MAC5B,aAAa,OAAO,QAAQ;AAAA,MAC5B,cAAc;AAAA,QACZ,OAAO,OAAO,SAAS,QAAQ,SAAS;AAAA,QACxC,QAAQ,OAAO,SAAS,QAAQ,UAAU;AAAA,QAC1C,KAAK,OAAO,QAAQ,cAAc;AAAA,QAClC,iBAAiB,OAAO,SAAS,QAAQ,mBAAmB;AAAA,QAC5D,iBAAiB,OAAO,SAAS,QAAQ,mBAAmB;AAAA,QAC5D,4BAA4B,OAAO,SAAS,QAAQ,8BAA8B;AAAA,QAClF,OAAO,OAAO,QAAQ;AAAA,MAAA;AAAA,MAExB,cAAc;AAAA,QACZ,SAAS,OAAO,SAAS,OAAO;AAAA,QAChC,QAAQ,OAAO,SAAS,OAAO;AAAA,MAAA;AAAA,MAEjC,aAAa;AAAA,QACX,OAAO;AAAA,QACP,OAAO,OAAO,SAAS,QAAQ,SAAS;AAAA,QACxC,QAAQ,OAAO,SAAS,QAAQ,UAAU;AAAA,QAC1C,SAAS,OAAO,QAAQ,OAAO,cAC3B,OAAO,OAAO,MAAM,cAAc,MAClC;AAAA,QACJ,WAAW,OAAO,QAAQ,OAAO,aAAa;AAAA,QAC9C,aAAa;AAAA,QACb,aAAa;AAAA,QACb,sBAAsB;AAAA,QACtB,GAAI,OAAO,QAAQ;AAAA,MAAA;AAAA,IACrB;AAAA,EAEJ;AAAA,EAEA,MAAM,OAAO,OAAyB,SAAuC;AAC3E,WAAO,KAAK,WAAW,OAAO,OAAO,OAAO;AAAA,EAC9C;AACF;"}
|
|
@@ -7,11 +7,11 @@ import { WorkerType } from '../worker/types';
|
|
|
7
7
|
import { ResourceLoader } from '../stages/load/ResourceLoader';
|
|
8
8
|
|
|
9
9
|
interface VideoClipSessionCallbacks {
|
|
10
|
-
onComposedStreamReady(stream: ReadableStream<VideoFrame>, fps: number): void
|
|
10
|
+
onComposedStreamReady(stream: ReadableStream<VideoFrame>, fps: number): Promise<void>;
|
|
11
11
|
onEncodedStreamReady(stream: ReadableStream<{
|
|
12
12
|
chunk: EncodedVideoChunk;
|
|
13
13
|
metadata: EncodedVideoChunkMetadata;
|
|
14
|
-
}>, track: 'video' | 'audio'): void
|
|
14
|
+
}>, track: 'video' | 'audio'): Promise<void>;
|
|
15
15
|
onAudioStreamReady?(stream: ReadableStream<AudioData>, metadata: {
|
|
16
16
|
sessionId: string;
|
|
17
17
|
clipStartUs: number;
|
|
@@ -44,13 +44,13 @@ export declare class VideoClipSession {
|
|
|
44
44
|
private readonly callbacks;
|
|
45
45
|
private readonly resourceLoader?;
|
|
46
46
|
private instructionContext;
|
|
47
|
-
|
|
47
|
+
composeWorker: BaseWorker | null;
|
|
48
48
|
private decodeWorker;
|
|
49
49
|
private videoEncodeWorker;
|
|
50
50
|
private videoDemuxWorker;
|
|
51
51
|
private audioDecodeWorker;
|
|
52
52
|
private visualStream;
|
|
53
|
-
private
|
|
53
|
+
private composeToEncodeChannel;
|
|
54
54
|
private decodeToVisualChannel;
|
|
55
55
|
private demuxToDecodeChannel;
|
|
56
56
|
private demuxAudioToDecodeChannel;
|
|
@@ -62,7 +62,6 @@ export declare class VideoClipSession {
|
|
|
62
62
|
deactivate(): Promise<void>;
|
|
63
63
|
dispose(): Promise<void>;
|
|
64
64
|
handlePlannerUpdate(update: ClipUpdateResult): Promise<void>;
|
|
65
|
-
get visualWorkerHandle(): BaseWorker | null;
|
|
66
65
|
private getClip;
|
|
67
66
|
private getResource;
|
|
68
67
|
private extractAttachmentImageResources;
|
|
@@ -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,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;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGpE,UAAU,yBAAyB;IACjC,qBAAqB,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"VideoClipSession.d.ts","sourceRoot":"","sources":["../../src/orchestrator/VideoClipSession.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,13 +11,13 @@ class VideoClipSession {
|
|
|
11
11
|
callbacks;
|
|
12
12
|
resourceLoader;
|
|
13
13
|
instructionContext = null;
|
|
14
|
-
|
|
14
|
+
composeWorker = null;
|
|
15
15
|
decodeWorker = null;
|
|
16
16
|
videoEncodeWorker = null;
|
|
17
17
|
videoDemuxWorker = null;
|
|
18
18
|
audioDecodeWorker = null;
|
|
19
19
|
visualStream = null;
|
|
20
|
-
|
|
20
|
+
composeToEncodeChannel = null;
|
|
21
21
|
decodeToVisualChannel = null;
|
|
22
22
|
demuxToDecodeChannel = null;
|
|
23
23
|
demuxAudioToDecodeChannel = null;
|
|
@@ -99,9 +99,6 @@ class VideoClipSession {
|
|
|
99
99
|
}
|
|
100
100
|
await this.activate();
|
|
101
101
|
}
|
|
102
|
-
get visualWorkerHandle() {
|
|
103
|
-
return this.visualWorker;
|
|
104
|
-
}
|
|
105
102
|
getClip() {
|
|
106
103
|
return this.compositionModel?.findClip?.(this.clipId) ?? null;
|
|
107
104
|
}
|
|
@@ -140,37 +137,37 @@ class VideoClipSession {
|
|
|
140
137
|
await this.connectImagePipeline();
|
|
141
138
|
}
|
|
142
139
|
async connectImagePipeline() {
|
|
143
|
-
if (!this.
|
|
140
|
+
if (!this.composeWorker) {
|
|
144
141
|
throw new Error("Pipeline workers not ready");
|
|
145
142
|
}
|
|
146
143
|
const sessionId = this.sessionId;
|
|
147
144
|
if (this.videoEncodeWorker) {
|
|
148
|
-
this.
|
|
149
|
-
await this.
|
|
145
|
+
this.composeToEncodeChannel = new MessageChannel();
|
|
146
|
+
await this.composeWorker.send(
|
|
150
147
|
"connect",
|
|
151
148
|
{
|
|
152
149
|
direction: "downstream",
|
|
153
|
-
port: this.
|
|
150
|
+
port: this.composeToEncodeChannel.port1,
|
|
154
151
|
streamType: "video",
|
|
155
152
|
sessionId
|
|
156
153
|
},
|
|
157
|
-
{ transfer: [this.
|
|
154
|
+
{ transfer: [this.composeToEncodeChannel.port1] }
|
|
158
155
|
);
|
|
159
156
|
await this.videoEncodeWorker.send(
|
|
160
157
|
"connect",
|
|
161
158
|
{
|
|
162
159
|
direction: "upstream",
|
|
163
|
-
port: this.
|
|
160
|
+
port: this.composeToEncodeChannel.port2,
|
|
164
161
|
streamType: "video",
|
|
165
162
|
sessionId
|
|
166
163
|
},
|
|
167
|
-
{ transfer: [this.
|
|
164
|
+
{ transfer: [this.composeToEncodeChannel.port2] }
|
|
168
165
|
);
|
|
169
166
|
}
|
|
170
|
-
this.
|
|
167
|
+
this.composeWorker.receiveStream(async (stream) => {
|
|
171
168
|
const visualConfig = this.workerConfigs.videoCompose ?? {};
|
|
172
169
|
const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;
|
|
173
|
-
this.callbacks.onComposedStreamReady(stream, fps);
|
|
170
|
+
await this.callbacks.onComposedStreamReady(stream, fps);
|
|
174
171
|
});
|
|
175
172
|
if (this.videoEncodeWorker) {
|
|
176
173
|
this.videoEncodeWorker.receiveStream((stream, metadata) => {
|
|
@@ -212,7 +209,7 @@ class VideoClipSession {
|
|
|
212
209
|
});
|
|
213
210
|
}
|
|
214
211
|
}
|
|
215
|
-
this.
|
|
212
|
+
this.composeWorker = await this.workerPool.getOrCreate("videoCompose", this.sessionId, {
|
|
216
213
|
lazy: true
|
|
217
214
|
});
|
|
218
215
|
const visualConfig = this.workerConfigs.videoCompose ?? {};
|
|
@@ -223,7 +220,7 @@ class VideoClipSession {
|
|
|
223
220
|
clipEndUs: clip.startUs + clip.durationUs,
|
|
224
221
|
compositionFps: this.compositionModel.fps ?? visualConfig.fps ?? 30
|
|
225
222
|
};
|
|
226
|
-
await this.
|
|
223
|
+
await this.composeWorker.send("configure", {
|
|
227
224
|
initial: true,
|
|
228
225
|
clipId: clip.id,
|
|
229
226
|
config: { ...visualConfig, ...renderOverrides, timeline }
|
|
@@ -247,31 +244,31 @@ class VideoClipSession {
|
|
|
247
244
|
}
|
|
248
245
|
}
|
|
249
246
|
async connectVideoPipeline() {
|
|
250
|
-
if (!this.
|
|
247
|
+
if (!this.composeWorker || !this.decodeWorker || !this.videoDemuxWorker) {
|
|
251
248
|
throw new Error("Pipeline workers not ready");
|
|
252
249
|
}
|
|
253
250
|
const sessionId = this.sessionId;
|
|
254
251
|
if (this.videoEncodeWorker) {
|
|
255
|
-
this.
|
|
256
|
-
await this.
|
|
252
|
+
this.composeToEncodeChannel = new MessageChannel();
|
|
253
|
+
await this.composeWorker.send(
|
|
257
254
|
"connect",
|
|
258
255
|
{
|
|
259
256
|
direction: "downstream",
|
|
260
|
-
port: this.
|
|
257
|
+
port: this.composeToEncodeChannel.port1,
|
|
261
258
|
streamType: "video",
|
|
262
259
|
sessionId
|
|
263
260
|
},
|
|
264
|
-
{ transfer: [this.
|
|
261
|
+
{ transfer: [this.composeToEncodeChannel.port1] }
|
|
265
262
|
);
|
|
266
263
|
await this.videoEncodeWorker.send(
|
|
267
264
|
"connect",
|
|
268
265
|
{
|
|
269
266
|
direction: "upstream",
|
|
270
|
-
port: this.
|
|
267
|
+
port: this.composeToEncodeChannel.port2,
|
|
271
268
|
streamType: "video",
|
|
272
269
|
sessionId
|
|
273
270
|
},
|
|
274
|
-
{ transfer: [this.
|
|
271
|
+
{ transfer: [this.composeToEncodeChannel.port2] }
|
|
275
272
|
);
|
|
276
273
|
}
|
|
277
274
|
this.decodeToVisualChannel = new MessageChannel();
|
|
@@ -285,7 +282,7 @@ class VideoClipSession {
|
|
|
285
282
|
},
|
|
286
283
|
{ transfer: [this.decodeToVisualChannel.port1] }
|
|
287
284
|
);
|
|
288
|
-
await this.
|
|
285
|
+
await this.composeWorker.send(
|
|
289
286
|
"connect",
|
|
290
287
|
{
|
|
291
288
|
direction: "upstream",
|
|
@@ -316,10 +313,10 @@ class VideoClipSession {
|
|
|
316
313
|
},
|
|
317
314
|
{ transfer: [this.demuxToDecodeChannel.port2] }
|
|
318
315
|
);
|
|
319
|
-
this.
|
|
316
|
+
this.composeWorker.receiveStream(async (stream) => {
|
|
320
317
|
const visualConfig = this.workerConfigs.videoCompose ?? {};
|
|
321
318
|
const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;
|
|
322
|
-
this.callbacks.onComposedStreamReady(stream, fps);
|
|
319
|
+
await this.callbacks.onComposedStreamReady(stream, fps);
|
|
323
320
|
});
|
|
324
321
|
if (this.videoEncodeWorker) {
|
|
325
322
|
this.videoEncodeWorker.receiveStream((stream, metadata) => {
|
|
@@ -384,8 +381,8 @@ class VideoClipSession {
|
|
|
384
381
|
instructions,
|
|
385
382
|
status: instructions.status
|
|
386
383
|
};
|
|
387
|
-
if (this.
|
|
388
|
-
await this.
|
|
384
|
+
if (this.composeWorker) {
|
|
385
|
+
await this.composeWorker.send("install_instructions", instructions);
|
|
389
386
|
}
|
|
390
387
|
}
|
|
391
388
|
async releasePipeline() {
|
|
@@ -404,8 +401,8 @@ class VideoClipSession {
|
|
|
404
401
|
}
|
|
405
402
|
}
|
|
406
403
|
}
|
|
407
|
-
if (this.
|
|
408
|
-
await this.
|
|
404
|
+
if (this.composeWorker && this.instructionContext) {
|
|
405
|
+
await this.composeWorker.notify("dispose_clip", {
|
|
409
406
|
sessionId: this.sessionId,
|
|
410
407
|
revision: this.instructionContext.revision
|
|
411
408
|
});
|
|
@@ -422,9 +419,9 @@ class VideoClipSession {
|
|
|
422
419
|
this.workerPool.terminate("videoDecode", this.sessionId);
|
|
423
420
|
this.decodeWorker = null;
|
|
424
421
|
}
|
|
425
|
-
if (this.
|
|
422
|
+
if (this.composeWorker) {
|
|
426
423
|
this.workerPool.terminate("videoCompose", this.sessionId);
|
|
427
|
-
this.
|
|
424
|
+
this.composeWorker = null;
|
|
428
425
|
}
|
|
429
426
|
if (this.videoEncodeWorker) {
|
|
430
427
|
this.workerPool.terminate("videoEncode", this.sessionId);
|
|
@@ -434,15 +431,15 @@ class VideoClipSession {
|
|
|
434
431
|
this.workerPool.terminate("audioDecode", this.sessionId);
|
|
435
432
|
this.audioDecodeWorker = null;
|
|
436
433
|
}
|
|
437
|
-
this.
|
|
438
|
-
this.
|
|
434
|
+
this.composeToEncodeChannel?.port1.close();
|
|
435
|
+
this.composeToEncodeChannel?.port2.close();
|
|
439
436
|
this.decodeToVisualChannel?.port1.close();
|
|
440
437
|
this.decodeToVisualChannel?.port2.close();
|
|
441
438
|
this.demuxToDecodeChannel?.port1.close();
|
|
442
439
|
this.demuxToDecodeChannel?.port2.close();
|
|
443
440
|
this.demuxAudioToDecodeChannel?.port1.close();
|
|
444
441
|
this.demuxAudioToDecodeChannel?.port2.close();
|
|
445
|
-
this.
|
|
442
|
+
this.composeToEncodeChannel = null;
|
|
446
443
|
this.decodeToVisualChannel = null;
|
|
447
444
|
this.demuxToDecodeChannel = null;
|
|
448
445
|
this.demuxAudioToDecodeChannel = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoClipSession.js","sources":["../../src/orchestrator/VideoClipSession.ts"],"sourcesContent":["import type { WorkerPool } from '../worker/WorkerPool';\nimport type { CacheManager } from '../cache/CacheManager';\nimport type { CompositionModel, Clip } from '../model';\nimport type { CompositionPlanner, ClipUpdateResult } from './CompositionPlanner';\nimport type { ClipInstructionSet } from '../stages/compose/instructions';\nimport type { BaseWorker } from '../worker/BaseWorker';\nimport type { WorkerType } from '../worker/types';\nimport type { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { hasResourceId } from '../model/types';\n\ninterface VideoClipSessionCallbacks {\n onComposedStreamReady(stream: ReadableStream<VideoFrame>, fps: number): void;\n onEncodedStreamReady(\n stream: ReadableStream<{ chunk: EncodedVideoChunk; metadata: EncodedVideoChunkMetadata }>,\n track: 'video' | 'audio'\n ): void;\n onAudioStreamReady?(\n stream: ReadableStream<AudioData>,\n metadata: {\n sessionId: string;\n clipStartUs: number;\n clipDurationUs: number;\n }\n ): void;\n onStreamDisposed?(): void;\n onPipelineReady?(attachmentResourceIds?: string[]): Promise<void>;\n}\n\ninterface VideoClipSessionConfig {\n clipId: string;\n sessionId?: string;\n forL2Only?: boolean;\n planner: CompositionPlanner;\n workerPool: WorkerPool;\n cacheManager: CacheManager;\n compositionModel: CompositionModel;\n workerConfigs: Record<WorkerType, any>;\n callbacks: VideoClipSessionCallbacks;\n resourceLoader?: ResourceLoader;\n}\n\ninterface InstructionContext {\n revision: number;\n instructions: ClipInstructionSet;\n status: ClipInstructionSet['status'];\n}\n\nexport class VideoClipSession {\n private readonly clipId: string;\n private readonly sessionId: string;\n private readonly forL2Only: boolean;\n private readonly planner: CompositionPlanner;\n private readonly workerPool: WorkerPool;\n private readonly cacheManager: CacheManager;\n private readonly compositionModel: CompositionModel;\n private readonly workerConfigs: Record<WorkerType, any>;\n private readonly callbacks: VideoClipSessionCallbacks;\n private readonly resourceLoader?: ResourceLoader;\n\n private instructionContext: InstructionContext | null = null;\n private visualWorker: BaseWorker | null = null;\n private decodeWorker: BaseWorker | null = null;\n private videoEncodeWorker: BaseWorker | null = null;\n private videoDemuxWorker: BaseWorker | null = null;\n private audioDecodeWorker: 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 demuxAudioToDecodeChannel: MessageChannel | null = null;\n private isActive = false;\n private isDisposed = false;\n\n static async create(config: VideoClipSessionConfig): Promise<VideoClipSession> {\n return new VideoClipSession(config);\n }\n\n private constructor(config: VideoClipSessionConfig) {\n this.clipId = config.clipId;\n this.sessionId = config.sessionId ?? config.clipId;\n this.forL2Only = config.forL2Only ?? false;\n this.planner = config.planner;\n this.workerPool = config.workerPool;\n this.cacheManager = config.cacheManager;\n this.compositionModel = config.compositionModel;\n this.workerConfigs = config.workerConfigs;\n this.callbacks = config.callbacks;\n this.resourceLoader = config.resourceLoader;\n }\n\n async activate(): Promise<void> {\n if (this.isActive || this.isDisposed) return;\n\n const clip = this.getClip();\n const resource = this.getResource();\n if (!clip || !resource) {\n console.warn('[VideoClipSession] activate: clip or resource not found', this.sessionId);\n return;\n }\n if (resource.type === 'video') {\n await this.setupVideoPipeline(clip, resource);\n } else if (resource.type === 'image') {\n await this.setupImagePipeline(clip);\n } else {\n console.warn(\n '[VideoClipSession] Unknown resource type:',\n resource.type,\n 'for',\n this.sessionId\n );\n }\n\n await this.ensureInstructions(clip);\n\n this.isActive = true;\n\n // Notify that pipeline is ready - triggers resource loading\n const attachmentResources = this.extractAttachmentImageResources();\n if (this.callbacks.onPipelineReady) {\n await this.callbacks.onPipelineReady(attachmentResources);\n }\n }\n\n async deactivate(): Promise<void> {\n if (!this.isActive || this.isDisposed) return;\n this.isActive = false;\n\n await this.releasePipeline();\n }\n\n async dispose(): Promise<void> {\n if (this.isDisposed) return;\n await this.deactivate();\n this.planner.releaseClip(this.clipId);\n this.isDisposed = true;\n }\n\n async handlePlannerUpdate(update: ClipUpdateResult): Promise<void> {\n if (this.isDisposed) {\n return;\n }\n\n if (update.type === 'remove') {\n await this.dispose();\n return;\n }\n\n const instructions = update.instructions;\n if (!instructions) {\n return;\n }\n\n const clip = this.getClip();\n const resource = this.getResource();\n if (!clip || !resource || (resource.type !== 'video' && resource.type !== 'image')) {\n return;\n }\n\n // Any update requires pipeline restart (stream is closed after cache eviction)\n if (this.isActive) {\n await this.releasePipeline();\n this.isActive = false;\n }\n await this.activate();\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 || !hasResourceId(clip)) return null;\n return this.compositionModel.getResource(clip.resourceId) ?? null;\n }\n\n private extractAttachmentImageResources(): string[] {\n if (!this.instructionContext?.instructions) return [];\n\n const resourceIdSet = new Set<string>();\n for (const layer of this.instructionContext.instructions.layers) {\n // Only process attachment layers (with attachmentId)\n if (!layer.payload.attachmentId) continue;\n\n if (layer.type === 'image') {\n const imagePayload = layer.payload;\n if (imagePayload.oldResourceId) {\n resourceIdSet.add(imagePayload.oldResourceId);\n }\n const resourceId = imagePayload.resourceId;\n if (resourceId) {\n resourceIdSet.add(resourceId);\n }\n }\n }\n return Array.from(resourceIdSet);\n }\n\n private async ensureInstructions(_clip: Clip): Promise<void> {\n const instructions = this.planner.getInstructions(this.clipId);\n if (!instructions) {\n throw new Error(`No instructions for clip ${this.clipId}`);\n }\n await this.installInstructions(instructions);\n }\n\n private async setupImagePipeline(clip: Clip): Promise<void> {\n await this.acquireWorkers(clip, false);\n await this.connectImagePipeline();\n }\n\n private async connectImagePipeline(): Promise<void> {\n if (!this.visualWorker) {\n throw new Error('Pipeline workers not ready');\n }\n\n // Use sessionId for worker communication\n const sessionId = this.sessionId;\n\n // Connect visualWorker to encodeWorker (only if L2 mode)\n if (this.videoEncodeWorker) {\n this.visualToEncodeChannel = new MessageChannel();\n await this.visualWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.visualToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port1] }\n );\n await this.videoEncodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.visualToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port2] }\n );\n }\n\n this.visualWorker.receiveStream((stream) => {\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;\n this.callbacks.onComposedStreamReady(stream, fps);\n });\n\n // Receive encoded chunks from EncodeWorker (only in L2 mode)\n if (this.videoEncodeWorker) {\n this.videoEncodeWorker.receiveStream((stream, metadata) => {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n metadata?.streamType ?? 'video'\n );\n });\n }\n }\n\n private async setupVideoPipeline(clip: Clip, _resource: { id: string }): Promise<void> {\n await this.acquireWorkers(clip, true);\n await this.connectVideoPipeline();\n }\n\n private async acquireWorkers(clip: Clip, isVideo: boolean): Promise<void> {\n // Acquire video pipeline workers\n if (isVideo) {\n // VideoDemuxWorker\n this.videoDemuxWorker = await this.workerPool.getOrCreate('videoDemux', this.sessionId, {\n lazy: true,\n });\n const demuxConfig = this.workerConfigs.videoDemux ?? {};\n await this.videoDemuxWorker.send('configure', {\n initial: true,\n config: {\n ...demuxConfig,\n emitAudioToMain: this.forL2Only,\n },\n });\n\n // VideoDecodeWorker\n this.decodeWorker = await this.workerPool.getOrCreate('videoDecode', this.sessionId, {\n lazy: true,\n });\n const decodeConfig = this.workerConfigs.videoDecode ?? {};\n await this.decodeWorker.send('configure', {\n initial: true,\n config: decodeConfig,\n });\n\n // AudioDemuxWorker (reuse VideoDemuxWorker output)\n // Note: We don't create a separate AudioDemuxWorker for video files,\n // VideoDemuxWorker will output both video and audio streams\n\n if (!this.forL2Only) {\n // AudioDecodeWorker\n // Note: No initial configure needed - codec info will be sent via\n // VideoDemuxWorker's configure message after demux ready\n this.audioDecodeWorker = await this.workerPool.getOrCreate('audioDecode', this.sessionId, {\n lazy: true,\n });\n }\n }\n\n // VideoComposeWorker (always needed)\n this.visualWorker = await this.workerPool.getOrCreate('videoCompose', this.sessionId, {\n lazy: true,\n });\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const renderOverrides = this.compositionModel.renderConfig ?? {};\n const timeline = {\n clipId: clip.id,\n clipStartUs: clip.startUs,\n clipEndUs: clip.startUs + clip.durationUs,\n compositionFps: this.compositionModel.fps ?? visualConfig.fps ?? 30,\n };\n await this.visualWorker.send('configure', {\n initial: true,\n clipId: clip.id,\n config: { ...visualConfig, ...renderOverrides, timeline },\n });\n\n // VideoEncodeWorker (only for L2)\n if (this.forL2Only) {\n this.videoEncodeWorker = await this.workerPool.getOrCreate('videoEncode', this.sessionId, {\n lazy: true,\n });\n const encodeConfig = this.workerConfigs.videoEncode ?? {};\n const encoderConfig = { ...encodeConfig };\n if (this.compositionModel.renderConfig?.width) {\n encoderConfig.width = this.compositionModel.renderConfig.width;\n }\n if (this.compositionModel.renderConfig?.height) {\n encoderConfig.height = this.compositionModel.renderConfig.height;\n }\n await this.videoEncodeWorker.send('configure', {\n initial: true,\n config: encoderConfig,\n });\n }\n }\n\n private async connectVideoPipeline(): Promise<void> {\n if (!this.visualWorker || !this.decodeWorker || !this.videoDemuxWorker) {\n throw new Error('Pipeline workers not ready');\n }\n\n // Use sessionId for worker communication\n const sessionId = this.sessionId;\n\n // Connect visualWorker to encodeWorker (only if L2 mode)\n if (this.videoEncodeWorker) {\n this.visualToEncodeChannel = new MessageChannel();\n await this.visualWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.visualToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port1] }\n );\n await this.videoEncodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.visualToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.visualToEncodeChannel.port2] }\n );\n }\n\n this.decodeToVisualChannel = new MessageChannel();\n await this.decodeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.decodeToVisualChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.decodeToVisualChannel.port1] }\n );\n await this.visualWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.decodeToVisualChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.decodeToVisualChannel.port2] }\n );\n\n this.demuxToDecodeChannel = new MessageChannel();\n await this.videoDemuxWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.demuxToDecodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.demuxToDecodeChannel.port1] }\n );\n await this.decodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.demuxToDecodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.demuxToDecodeChannel.port2] }\n );\n\n // Setup stream receivers\n this.visualWorker.receiveStream((stream) => {\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;\n this.callbacks.onComposedStreamReady(stream, fps);\n });\n\n // Receive encoded chunks from EncodeWorker (only in L2 mode)\n if (this.videoEncodeWorker) {\n this.videoEncodeWorker.receiveStream((stream, metadata) => {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n metadata?.streamType ?? 'video'\n );\n });\n }\n\n // Connect audio pipeline (VideoDemux audio output → AudioDecode)\n if (this.audioDecodeWorker) {\n this.demuxAudioToDecodeChannel = new MessageChannel();\n\n await this.videoDemuxWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.demuxAudioToDecodeChannel.port1,\n streamType: 'audio',\n sessionId,\n },\n { transfer: [this.demuxAudioToDecodeChannel.port1] }\n );\n\n const clip = this.getClip();\n await this.audioDecodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.demuxAudioToDecodeChannel.port2,\n streamType: 'audio',\n sessionId,\n clipStartUs: clip?.startUs || 0,\n clipDurationUs: clip?.durationUs || 0,\n },\n { transfer: [this.demuxAudioToDecodeChannel.port2] }\n );\n\n // Receive audio stream from AudioDecodeWorker\n this.audioDecodeWorker.receiveStream((stream) => {\n if (this.callbacks.onAudioStreamReady && clip) {\n this.callbacks.onAudioStreamReady(stream as ReadableStream<AudioData>, {\n sessionId,\n clipStartUs: clip.startUs,\n clipDurationUs: clip.durationUs,\n });\n }\n });\n }\n\n // In L2-only mode, receive encoded audio directly from demux and write to L2\n if (this.forL2Only && this.videoDemuxWorker) {\n this.videoDemuxWorker.receiveStream((stream, metadata) => {\n if (metadata?.streamType === 'audio') {\n this.cacheManager\n .receiveEncodedChunks(stream as any, this.clipId, 'audio', {\n onError: (err) => {\n console.error('[VideoClipSession] L2 audio write error:', err);\n },\n })\n .catch((err) => {\n console.error('[VideoClipSession] L2 audio stream error:', err);\n });\n }\n });\n }\n }\n\n private async installInstructions(instructions: ClipInstructionSet): Promise<void> {\n this.instructionContext = {\n revision: instructions.revision,\n instructions,\n status: instructions.status,\n };\n\n if (this.visualWorker) {\n await this.visualWorker.send('install_instructions', instructions);\n }\n }\n\n private async releasePipeline(): Promise<void> {\n if (!this.forL2Only) {\n const clip = this.getClip();\n if (clip && this.resourceLoader) {\n if (clip.oldResourceId) {\n this.resourceLoader.cancel(clip.oldResourceId);\n }\n if (hasResourceId(clip)) {\n this.resourceLoader.cancel(clip.resourceId);\n }\n\n const attachmentResources = this.extractAttachmentImageResources();\n for (const resourceId of attachmentResources) {\n this.resourceLoader.cancel(resourceId);\n }\n }\n }\n\n if (this.visualWorker && this.instructionContext) {\n await this.visualWorker.notify('dispose_clip', {\n sessionId: this.sessionId,\n revision: this.instructionContext.revision,\n });\n }\n\n if (this.visualStream && this.callbacks.onStreamDisposed) {\n this.callbacks.onStreamDisposed();\n }\n this.visualStream = null;\n\n // Terminate clip-local workers\n if (this.videoDemuxWorker) {\n this.workerPool.terminate('videoDemux', this.sessionId);\n this.videoDemuxWorker = null;\n }\n\n if (this.decodeWorker) {\n this.workerPool.terminate('videoDecode', this.sessionId);\n this.decodeWorker = null;\n }\n\n if (this.visualWorker) {\n this.workerPool.terminate('videoCompose', this.sessionId);\n this.visualWorker = null;\n }\n\n if (this.videoEncodeWorker) {\n this.workerPool.terminate('videoEncode', this.sessionId);\n this.videoEncodeWorker = null;\n }\n\n if (this.audioDecodeWorker) {\n this.workerPool.terminate('audioDecode', this.sessionId);\n this.audioDecodeWorker = null;\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 this.demuxAudioToDecodeChannel?.port1.close();\n this.demuxAudioToDecodeChannel?.port2.close();\n\n this.visualToEncodeChannel = null;\n this.decodeToVisualChannel = null;\n this.demuxToDecodeChannel = null;\n this.demuxAudioToDecodeChannel = null;\n }\n\n async invalidateClipCache(): Promise<void> {\n await this.cacheManager.invalidateClip(this.clipId);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"VideoClipSession.js","sources":["../../src/orchestrator/VideoClipSession.ts"],"sourcesContent":["import type { WorkerPool } from '../worker/WorkerPool';\nimport type { CacheManager } from '../cache/CacheManager';\nimport type { CompositionModel, Clip } from '../model';\nimport type { CompositionPlanner, ClipUpdateResult } from './CompositionPlanner';\nimport type { ClipInstructionSet } from '../stages/compose/instructions';\nimport type { BaseWorker } from '../worker/BaseWorker';\nimport type { WorkerType } from '../worker/types';\nimport type { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { hasResourceId } from '../model/types';\n\ninterface VideoClipSessionCallbacks {\n onComposedStreamReady(stream: ReadableStream<VideoFrame>, fps: number): Promise<void>;\n onEncodedStreamReady(\n stream: ReadableStream<{ chunk: EncodedVideoChunk; metadata: EncodedVideoChunkMetadata }>,\n track: 'video' | 'audio'\n ): Promise<void>;\n onAudioStreamReady?(\n stream: ReadableStream<AudioData>,\n metadata: {\n sessionId: string;\n clipStartUs: number;\n clipDurationUs: number;\n }\n ): void;\n onStreamDisposed?(): void;\n onPipelineReady?(attachmentResourceIds?: string[]): Promise<void>;\n}\n\ninterface VideoClipSessionConfig {\n clipId: string;\n sessionId?: string;\n forL2Only?: boolean;\n planner: CompositionPlanner;\n workerPool: WorkerPool;\n cacheManager: CacheManager;\n compositionModel: CompositionModel;\n workerConfigs: Record<WorkerType, any>;\n callbacks: VideoClipSessionCallbacks;\n resourceLoader?: ResourceLoader;\n}\n\ninterface InstructionContext {\n revision: number;\n instructions: ClipInstructionSet;\n status: ClipInstructionSet['status'];\n}\n\nexport class VideoClipSession {\n private readonly clipId: string;\n private readonly sessionId: string;\n private readonly forL2Only: boolean;\n private readonly planner: CompositionPlanner;\n private readonly workerPool: WorkerPool;\n private readonly cacheManager: CacheManager;\n private readonly compositionModel: CompositionModel;\n private readonly workerConfigs: Record<WorkerType, any>;\n private readonly callbacks: VideoClipSessionCallbacks;\n private readonly resourceLoader?: ResourceLoader;\n\n private instructionContext: InstructionContext | null = null;\n composeWorker: BaseWorker | null = null;\n private decodeWorker: BaseWorker | null = null;\n private videoEncodeWorker: BaseWorker | null = null;\n private videoDemuxWorker: BaseWorker | null = null;\n private audioDecodeWorker: BaseWorker | null = null;\n private visualStream: ReadableStream<VideoFrame> | null = null;\n private composeToEncodeChannel: MessageChannel | null = null;\n private decodeToVisualChannel: MessageChannel | null = null;\n private demuxToDecodeChannel: MessageChannel | null = null;\n private demuxAudioToDecodeChannel: MessageChannel | null = null;\n private isActive = false;\n private isDisposed = false;\n\n static async create(config: VideoClipSessionConfig): Promise<VideoClipSession> {\n return new VideoClipSession(config);\n }\n\n private constructor(config: VideoClipSessionConfig) {\n this.clipId = config.clipId;\n this.sessionId = config.sessionId ?? config.clipId;\n this.forL2Only = config.forL2Only ?? false;\n this.planner = config.planner;\n this.workerPool = config.workerPool;\n this.cacheManager = config.cacheManager;\n this.compositionModel = config.compositionModel;\n this.workerConfigs = config.workerConfigs;\n this.callbacks = config.callbacks;\n this.resourceLoader = config.resourceLoader;\n }\n\n async activate(): Promise<void> {\n if (this.isActive || this.isDisposed) return;\n\n const clip = this.getClip();\n const resource = this.getResource();\n if (!clip || !resource) {\n console.warn('[VideoClipSession] activate: clip or resource not found', this.sessionId);\n return;\n }\n if (resource.type === 'video') {\n await this.setupVideoPipeline(clip, resource);\n } else if (resource.type === 'image') {\n await this.setupImagePipeline(clip);\n } else {\n console.warn(\n '[VideoClipSession] Unknown resource type:',\n resource.type,\n 'for',\n this.sessionId\n );\n }\n\n await this.ensureInstructions(clip);\n\n this.isActive = true;\n\n // Notify that pipeline is ready - triggers resource loading\n const attachmentResources = this.extractAttachmentImageResources();\n if (this.callbacks.onPipelineReady) {\n await this.callbacks.onPipelineReady(attachmentResources);\n }\n }\n\n async deactivate(): Promise<void> {\n if (!this.isActive || this.isDisposed) return;\n this.isActive = false;\n\n await this.releasePipeline();\n }\n\n async dispose(): Promise<void> {\n if (this.isDisposed) return;\n await this.deactivate();\n this.planner.releaseClip(this.clipId);\n this.isDisposed = true;\n }\n\n async handlePlannerUpdate(update: ClipUpdateResult): Promise<void> {\n if (this.isDisposed) {\n return;\n }\n\n if (update.type === 'remove') {\n await this.dispose();\n return;\n }\n\n const instructions = update.instructions;\n if (!instructions) {\n return;\n }\n\n const clip = this.getClip();\n const resource = this.getResource();\n if (!clip || !resource || (resource.type !== 'video' && resource.type !== 'image')) {\n return;\n }\n\n // Any update requires pipeline restart (stream is closed after cache eviction)\n if (this.isActive) {\n await this.releasePipeline();\n this.isActive = false;\n }\n await this.activate();\n }\n\n private getClip(): Clip | null {\n return this.compositionModel?.findClip?.(this.clipId) ?? null;\n }\n\n private getResource() {\n const clip = this.getClip();\n if (!clip || !hasResourceId(clip)) return null;\n return this.compositionModel.getResource(clip.resourceId) ?? null;\n }\n\n private extractAttachmentImageResources(): string[] {\n if (!this.instructionContext?.instructions) return [];\n\n const resourceIdSet = new Set<string>();\n for (const layer of this.instructionContext.instructions.layers) {\n // Only process attachment layers (with attachmentId)\n if (!layer.payload.attachmentId) continue;\n\n if (layer.type === 'image') {\n const imagePayload = layer.payload;\n if (imagePayload.oldResourceId) {\n resourceIdSet.add(imagePayload.oldResourceId);\n }\n const resourceId = imagePayload.resourceId;\n if (resourceId) {\n resourceIdSet.add(resourceId);\n }\n }\n }\n return Array.from(resourceIdSet);\n }\n\n private async ensureInstructions(_clip: Clip): Promise<void> {\n const instructions = this.planner.getInstructions(this.clipId);\n if (!instructions) {\n throw new Error(`No instructions for clip ${this.clipId}`);\n }\n await this.installInstructions(instructions);\n }\n\n private async setupImagePipeline(clip: Clip): Promise<void> {\n await this.acquireWorkers(clip, false);\n await this.connectImagePipeline();\n }\n\n private async connectImagePipeline(): Promise<void> {\n if (!this.composeWorker) {\n throw new Error('Pipeline workers not ready');\n }\n\n // Use sessionId for worker communication\n const sessionId = this.sessionId;\n\n // Connect visualWorker to encodeWorker (only if L2 mode)\n if (this.videoEncodeWorker) {\n this.composeToEncodeChannel = new MessageChannel();\n await this.composeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.composeToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port1] }\n );\n await this.videoEncodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.composeToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port2] }\n );\n }\n\n this.composeWorker.receiveStream(async (stream) => {\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;\n await this.callbacks.onComposedStreamReady(stream, fps);\n });\n\n // Receive encoded chunks from EncodeWorker (only in L2 mode)\n if (this.videoEncodeWorker) {\n this.videoEncodeWorker.receiveStream((stream, metadata) => {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n metadata?.streamType ?? 'video'\n );\n });\n }\n }\n\n private async setupVideoPipeline(clip: Clip, _resource: { id: string }): Promise<void> {\n await this.acquireWorkers(clip, true);\n await this.connectVideoPipeline();\n }\n\n private async acquireWorkers(clip: Clip, isVideo: boolean): Promise<void> {\n // Acquire video pipeline workers\n if (isVideo) {\n // VideoDemuxWorker\n this.videoDemuxWorker = await this.workerPool.getOrCreate('videoDemux', this.sessionId, {\n lazy: true,\n });\n const demuxConfig = this.workerConfigs.videoDemux ?? {};\n await this.videoDemuxWorker.send('configure', {\n initial: true,\n config: {\n ...demuxConfig,\n emitAudioToMain: this.forL2Only,\n },\n });\n\n // VideoDecodeWorker\n this.decodeWorker = await this.workerPool.getOrCreate('videoDecode', this.sessionId, {\n lazy: true,\n });\n const decodeConfig = this.workerConfigs.videoDecode ?? {};\n await this.decodeWorker.send('configure', {\n initial: true,\n config: decodeConfig,\n });\n\n // AudioDemuxWorker (reuse VideoDemuxWorker output)\n // Note: We don't create a separate AudioDemuxWorker for video files,\n // VideoDemuxWorker will output both video and audio streams\n\n if (!this.forL2Only) {\n // AudioDecodeWorker\n // Note: No initial configure needed - codec info will be sent via\n // VideoDemuxWorker's configure message after demux ready\n this.audioDecodeWorker = await this.workerPool.getOrCreate('audioDecode', this.sessionId, {\n lazy: true,\n });\n }\n }\n\n // VideoComposeWorker (always needed)\n this.composeWorker = await this.workerPool.getOrCreate('videoCompose', this.sessionId, {\n lazy: true,\n });\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const renderOverrides = this.compositionModel.renderConfig ?? {};\n const timeline = {\n clipId: clip.id,\n clipStartUs: clip.startUs,\n clipEndUs: clip.startUs + clip.durationUs,\n compositionFps: this.compositionModel.fps ?? visualConfig.fps ?? 30,\n };\n await this.composeWorker.send('configure', {\n initial: true,\n clipId: clip.id,\n config: { ...visualConfig, ...renderOverrides, timeline },\n });\n\n // VideoEncodeWorker (only for L2)\n if (this.forL2Only) {\n this.videoEncodeWorker = await this.workerPool.getOrCreate('videoEncode', this.sessionId, {\n lazy: true,\n });\n const encodeConfig = this.workerConfigs.videoEncode ?? {};\n const encoderConfig = { ...encodeConfig };\n if (this.compositionModel.renderConfig?.width) {\n encoderConfig.width = this.compositionModel.renderConfig.width;\n }\n if (this.compositionModel.renderConfig?.height) {\n encoderConfig.height = this.compositionModel.renderConfig.height;\n }\n await this.videoEncodeWorker.send('configure', {\n initial: true,\n config: encoderConfig,\n });\n }\n }\n\n private async connectVideoPipeline(): Promise<void> {\n if (!this.composeWorker || !this.decodeWorker || !this.videoDemuxWorker) {\n throw new Error('Pipeline workers not ready');\n }\n\n // Use sessionId for worker communication\n const sessionId = this.sessionId;\n\n // Connect visualWorker to encodeWorker (only if L2 mode)\n if (this.videoEncodeWorker) {\n this.composeToEncodeChannel = new MessageChannel();\n await this.composeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.composeToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port1] }\n );\n await this.videoEncodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.composeToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port2] }\n );\n }\n\n this.decodeToVisualChannel = new MessageChannel();\n await this.decodeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.decodeToVisualChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.decodeToVisualChannel.port1] }\n );\n await this.composeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.decodeToVisualChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.decodeToVisualChannel.port2] }\n );\n\n this.demuxToDecodeChannel = new MessageChannel();\n await this.videoDemuxWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.demuxToDecodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.demuxToDecodeChannel.port1] }\n );\n await this.decodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.demuxToDecodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.demuxToDecodeChannel.port2] }\n );\n\n // Setup stream receivers\n this.composeWorker.receiveStream(async (stream) => {\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;\n await this.callbacks.onComposedStreamReady(stream, fps);\n });\n\n // Receive encoded chunks from EncodeWorker (only in L2 mode)\n if (this.videoEncodeWorker) {\n this.videoEncodeWorker.receiveStream((stream, metadata) => {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n metadata?.streamType ?? 'video'\n );\n });\n }\n\n // Connect audio pipeline (VideoDemux audio output → AudioDecode)\n if (this.audioDecodeWorker) {\n this.demuxAudioToDecodeChannel = new MessageChannel();\n\n await this.videoDemuxWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.demuxAudioToDecodeChannel.port1,\n streamType: 'audio',\n sessionId,\n },\n { transfer: [this.demuxAudioToDecodeChannel.port1] }\n );\n\n const clip = this.getClip();\n await this.audioDecodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.demuxAudioToDecodeChannel.port2,\n streamType: 'audio',\n sessionId,\n clipStartUs: clip?.startUs || 0,\n clipDurationUs: clip?.durationUs || 0,\n },\n { transfer: [this.demuxAudioToDecodeChannel.port2] }\n );\n\n // Receive audio stream from AudioDecodeWorker\n this.audioDecodeWorker.receiveStream((stream) => {\n if (this.callbacks.onAudioStreamReady && clip) {\n this.callbacks.onAudioStreamReady(stream as ReadableStream<AudioData>, {\n sessionId,\n clipStartUs: clip.startUs,\n clipDurationUs: clip.durationUs,\n });\n }\n });\n }\n\n // In L2-only mode, receive encoded audio directly from demux and write to L2\n if (this.forL2Only && this.videoDemuxWorker) {\n this.videoDemuxWorker.receiveStream((stream, metadata) => {\n if (metadata?.streamType === 'audio') {\n this.cacheManager\n .receiveEncodedChunks(stream as any, this.clipId, 'audio', {\n onError: (err) => {\n console.error('[VideoClipSession] L2 audio write error:', err);\n },\n })\n .catch((err) => {\n console.error('[VideoClipSession] L2 audio stream error:', err);\n });\n }\n });\n }\n }\n\n private async installInstructions(instructions: ClipInstructionSet): Promise<void> {\n this.instructionContext = {\n revision: instructions.revision,\n instructions,\n status: instructions.status,\n };\n\n if (this.composeWorker) {\n await this.composeWorker.send('install_instructions', instructions);\n }\n }\n\n private async releasePipeline(): Promise<void> {\n if (!this.forL2Only) {\n const clip = this.getClip();\n if (clip && this.resourceLoader) {\n if (clip.oldResourceId) {\n this.resourceLoader.cancel(clip.oldResourceId);\n }\n if (hasResourceId(clip)) {\n this.resourceLoader.cancel(clip.resourceId);\n }\n\n const attachmentResources = this.extractAttachmentImageResources();\n for (const resourceId of attachmentResources) {\n this.resourceLoader.cancel(resourceId);\n }\n }\n }\n\n if (this.composeWorker && this.instructionContext) {\n await this.composeWorker.notify('dispose_clip', {\n sessionId: this.sessionId,\n revision: this.instructionContext.revision,\n });\n }\n\n if (this.visualStream && this.callbacks.onStreamDisposed) {\n this.callbacks.onStreamDisposed();\n }\n this.visualStream = null;\n\n // Terminate clip-local workers\n if (this.videoDemuxWorker) {\n this.workerPool.terminate('videoDemux', this.sessionId);\n this.videoDemuxWorker = null;\n }\n\n if (this.decodeWorker) {\n this.workerPool.terminate('videoDecode', this.sessionId);\n this.decodeWorker = null;\n }\n\n if (this.composeWorker) {\n this.workerPool.terminate('videoCompose', this.sessionId);\n this.composeWorker = null;\n }\n\n if (this.videoEncodeWorker) {\n this.workerPool.terminate('videoEncode', this.sessionId);\n this.videoEncodeWorker = null;\n }\n\n if (this.audioDecodeWorker) {\n this.workerPool.terminate('audioDecode', this.sessionId);\n this.audioDecodeWorker = null;\n }\n\n this.composeToEncodeChannel?.port1.close();\n this.composeToEncodeChannel?.port2.close();\n this.decodeToVisualChannel?.port1.close();\n this.decodeToVisualChannel?.port2.close();\n this.demuxToDecodeChannel?.port1.close();\n this.demuxToDecodeChannel?.port2.close();\n this.demuxAudioToDecodeChannel?.port1.close();\n this.demuxAudioToDecodeChannel?.port2.close();\n\n this.composeToEncodeChannel = null;\n this.decodeToVisualChannel = null;\n this.demuxToDecodeChannel = null;\n this.demuxAudioToDecodeChannel = null;\n }\n\n async invalidateClipCache(): Promise<void> {\n await this.cacheManager.invalidateClip(this.clipId);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LayerRenderer.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/LayerRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAA8D,MAAM,SAAS,CAAC;AAMjG;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAAoC;IAC/C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,GAAG,CAAc;gBAGvB,GAAG,EAAE,iCAAiC,EACtC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,MAAW;IASlB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpC,OAAO,CAAC,0BAA0B;IAKlC;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IA0C9C,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,kBAAkB;IA+C1B,OAAO,CAAC,cAAc;YA2BR,gBAAgB;
|
|
1
|
+
{"version":3,"file":"LayerRenderer.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/LayerRenderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAA8D,MAAM,SAAS,CAAC;AAMjG;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAAoC;IAC/C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,GAAG,CAAc;gBAGvB,GAAG,EAAE,iCAAiC,EACtC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,MAAW;IASlB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpC,OAAO,CAAC,0BAA0B;IAKlC;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IA0C9C,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,kBAAkB;IA+C1B,OAAO,CAAC,cAAc;YA2BR,gBAAgB;YAwChB,gBAAgB;YAiFhB,eAAe;IAuC7B,OAAO,CAAC,SAAS;IAkBjB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAKtD"}
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { VideoComposeConfig, ComposeRequest, ComposeResult, TransitionEffect } from './types';
|
|
2
2
|
import { ClipInstructionSet } from './instructions';
|
|
3
3
|
|
|
4
|
-
interface ComposeStreams {
|
|
5
|
-
composeStream: WritableStream<ComposeRequest>;
|
|
6
|
-
cacheStream: ReadableStream<VideoFrame>;
|
|
7
|
-
encodeStream: ReadableStream<VideoFrame>;
|
|
8
|
-
}
|
|
9
4
|
/**
|
|
10
5
|
* VideoComposer - Main visual composition orchestrator
|
|
11
6
|
*/
|
|
@@ -19,7 +14,7 @@ export declare class VideoComposer {
|
|
|
19
14
|
private timelineContext;
|
|
20
15
|
constructor(config: VideoComposeConfig);
|
|
21
16
|
private applyDefaults;
|
|
22
|
-
createStreams(
|
|
17
|
+
createStreams(instruction?: ClipInstructionSet): TransformStream<ComposeRequest, VideoFrame>;
|
|
23
18
|
composeFrame(request: ComposeRequest): Promise<ComposeResult>;
|
|
24
19
|
composeTransition(fromRequest: ComposeRequest, toRequest: ComposeRequest, transition: TransitionEffect): Promise<ComposeResult>;
|
|
25
20
|
private clearCanvas;
|
|
@@ -28,5 +23,4 @@ export declare class VideoComposer {
|
|
|
28
23
|
updateConfig(config: Partial<VideoComposeConfig>): void;
|
|
29
24
|
dispose(): void;
|
|
30
25
|
}
|
|
31
|
-
export {};
|
|
32
26
|
//# sourceMappingURL=VideoComposer.d.ts.map
|
|
@@ -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,
|
|
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,EAIjB,MAAM,SAAS,CAAC;AAKjB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AASpD;;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;IAgCtC,OAAO,CAAC,aAAa;IAwBrB,aAAa,CAAC,WAAW,CAAC,EAAE,kBAAkB,GAAG,eAAe,CAAC,cAAc,EAAE,UAAU,CAAC;IA+BtF,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAqD7D,iBAAiB,CACrB,WAAW,EAAE,cAAc,EAC3B,SAAS,EAAE,cAAc,EACzB,UAAU,EAAE,gBAAgB,GAC3B,OAAO,CAAC,aAAa,CAAC;IAWzB,OAAO,CAAC,WAAW;YAaL,iBAAiB;YAWjB,SAAS;IAkBvB,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI;IAuBvD,OAAO,IAAI,IAAI;CAGhB"}
|