@meframe/core 0.0.31 → 0.0.32
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 +2 -2
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +3 -2
- package/dist/Meframe.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +12 -17
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +18 -280
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/l1/AudioL1Cache.d.ts +36 -19
- package/dist/cache/l1/AudioL1Cache.d.ts.map +1 -1
- package/dist/cache/l1/AudioL1Cache.js +182 -282
- package/dist/cache/l1/AudioL1Cache.js.map +1 -1
- package/dist/controllers/PlaybackController.d.ts +4 -2
- package/dist/controllers/PlaybackController.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.js +43 -13
- package/dist/controllers/PlaybackController.js.map +1 -1
- package/dist/model/types.d.ts +0 -4
- package/dist/model/types.d.ts.map +1 -1
- package/dist/model/types.js.map +1 -1
- package/dist/orchestrator/ExportScheduler.d.ts +6 -0
- package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
- package/dist/orchestrator/ExportScheduler.js +45 -66
- package/dist/orchestrator/ExportScheduler.js.map +1 -1
- package/dist/orchestrator/GlobalAudioSession.d.ts +35 -28
- package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
- package/dist/orchestrator/GlobalAudioSession.js +212 -421
- package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.d.ts +3 -3
- package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.js +4 -4
- package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
- package/dist/orchestrator/Orchestrator.d.ts +1 -2
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +34 -48
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/VideoClipSession.d.ts +0 -2
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
- package/dist/orchestrator/VideoClipSession.js +0 -49
- package/dist/orchestrator/VideoClipSession.js.map +1 -1
- package/dist/stages/compose/OfflineAudioMixer.d.ts.map +1 -1
- package/dist/stages/compose/OfflineAudioMixer.js +13 -18
- package/dist/stages/compose/OfflineAudioMixer.js.map +1 -1
- package/dist/stages/decode/AudioChunkDecoder.js +169 -0
- package/dist/stages/decode/AudioChunkDecoder.js.map +1 -0
- package/dist/stages/demux/MP3FrameParser.js +186 -0
- package/dist/stages/demux/MP3FrameParser.js.map +1 -0
- package/dist/stages/load/ResourceLoader.d.ts +20 -9
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +92 -135
- package/dist/stages/load/ResourceLoader.js.map +1 -1
- package/dist/stages/load/types.d.ts +1 -0
- package/dist/stages/load/types.d.ts.map +1 -1
- package/dist/utils/audio-data.d.ts +16 -0
- package/dist/utils/audio-data.d.ts.map +1 -0
- package/dist/utils/audio-data.js +111 -0
- package/dist/utils/audio-data.js.map +1 -0
- package/package.json +1 -1
- package/dist/cache/resource/ImageBitmapCache.d.ts +0 -65
- package/dist/cache/resource/ImageBitmapCache.d.ts.map +0 -1
- package/dist/cache/resource/ImageBitmapCache.js +0 -101
- package/dist/cache/resource/ImageBitmapCache.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 { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, RenderFrameOptions } from './types';\nimport { WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame, Clip } from '../model';\nimport { hasResourceId, isVideoClip, SetCompositionModelOptions } from '../model/types';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { GlobalAudioSession } from './GlobalAudioSession';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { ExportOptions } from '../types';\nimport { OnDemandVideoSession } from './OnDemandVideoSession';\nimport { ExportScheduler } from './ExportScheduler';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n muxManager: MuxManager;\n exportScheduler: ExportScheduler;\n\n private isInitialized = false;\n private config = ConfigLoader.getInstance().getConfig();\n private ensureCacheDebounceTimer: number | null = null;\n private currentClipId: string | null = null;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n // Initialize WorkerPool with worker path from config\n this.workers = new WorkerPool({\n eventBus: this.eventBus,\n workerConfigs,\n workerPath: config.workerPath,\n workerExtension: config.workerExtension,\n });\n\n const 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 resource: {\n projectId: config.projectId || this.config.global.projectId || 'default',\n },\n },\n this.eventBus\n );\n\n this.resourceLoader = new ResourceLoader({\n cacheManager: this.cacheManager,\n workerPool: this.workers,\n eventBus: this.eventBus,\n config: {\n maxConcurrent: 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 this.audioSession = new GlobalAudioSession({\n cacheManager: this.cacheManager,\n workerPool: this.workers,\n resourceLoader: this.resourceLoader,\n eventBus: this.eventBus,\n buildWorkerConfigs: () => this.buildWorkerConfigs(),\n });\n\n this.muxManager = new MuxManager(\n this.cacheManager,\n this.audioSession,\n this.config.encode.audio as AudioEncoderConfig\n );\n\n this.exportScheduler = new ExportScheduler({\n workerPool: this.workers,\n planner: this.planner,\n cacheManager: this.cacheManager,\n resourceLoader: this.resourceLoader,\n muxManager: this.muxManager,\n audioSession: this.audioSession,\n workerConfigsProvider: () => this.buildWorkerConfigs(),\n eventBus: this.eventBus,\n });\n\n this.setupResourceFirstFrameHandler();\n this.setupPreloadHandlers();\n }\n\n private setupResourceFirstFrameHandler(): void {\n this.eventBus.on(MeframeEvent.ResourceFirstFrameReady, async (payload) => {\n const { resourceId, clipId, index, chunks } = payload;\n\n if (!this.compositionModel) return;\n\n // Find the specific clip\n const clip = this.compositionModel.findClip(clipId);\n if (!clip || !clip.trackId) return;\n\n // Only decode first frame for clips that start at composition time 0\n // (these clips need the resource's first frame as cover)\n if (clip.startUs === 0) {\n const fps = this.compositionModel.fps ?? 30;\n await OnDemandVideoSession.decodeAndCacheFirstFrame(\n resourceId,\n chunks,\n index,\n clip,\n this.cacheManager,\n fps\n );\n }\n });\n }\n\n private setupPreloadHandlers(): void {\n // Stop preloading when playback starts\n this.eventBus.on(MeframeEvent.PlaybackPlay, () => {\n this.resourceLoader.setPreloadingEnabled(false);\n });\n\n // Enable preloading when playback pauses/stops\n this.eventBus.on(MeframeEvent.PlaybackPause, () => {\n this.resourceLoader.setPreloadingEnabled(true);\n });\n\n this.eventBus.on(MeframeEvent.PlaybackStop, () => {\n this.resourceLoader.setPreloadingEnabled(true);\n });\n\n // Note: ModelSet and PatchApplied are handled internally in ResourceLoader via handleModelSet\n // and direct calls isn't needed as ResourceLoader listens to updateResourceState via onStateChange?\n // Wait, ResourceLoader handles ModelSet via eventHandlers.\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n await this.cacheManager.init();\n\n this.isInitialized = true;\n }\n\n // Event methods - forward to eventBus\n on<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.on(event, handler);\n }\n\n off<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.off(event, handler);\n }\n\n once<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.once(event, handler);\n }\n\n async setCompositionModel(\n model: CompositionModel,\n options?: SetCompositionModelOptions\n ): Promise<void> {\n const { clearL1Cache = true } = options || {};\n\n if (clearL1Cache) {\n this.cacheManager.clearL1Cache();\n }\n\n this.compositionModel = model;\n this.planner.setModel(model);\n // ensure the cover resource is preloaded before audio is activated\n await this.resourceLoader.setModel(model);\n this.audioSession.setModel(model);\n\n this.eventBus.emit(MeframeEvent.ModelSet, model);\n\n this.eventBus.emit(MeframeEvent.CompositionUpdated, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce((acc: number, track: any) => acc + track.clips.length, 0),\n durationUs: model.durationUs,\n });\n }\n\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n // Apply patch and get affected clip IDs (simplified for 2-Clip strategy)\n // Note: addTrack/removeTrack already call buildIndexes() in patch.ts\n const affectedClipIds = applyModelPatch(this.compositionModel, patch);\n this.planner.applyPatch(patch, affectedClipIds);\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n affectedClips: Array.from(affectedClipIds),\n });\n\n // Process clip updates\n for (const clipId of affectedClipIds) {\n this.cacheManager.invalidateClip(clipId);\n }\n\n // Reactivate updated audio clips\n const reactivatedAudioClips: string[] = [];\n const reactivatedVideoClips: string[] = [];\n for (const clipId of affectedClipIds) {\n const clip = this.compositionModel.findClip(clipId);\n if (clip?.trackKind === 'audio') {\n await this.audioSession.deactivateClip(clipId);\n reactivatedAudioClips.push(clipId);\n } else if (clip?.trackKind === 'video') {\n reactivatedVideoClips.push(clipId);\n }\n }\n\n // Activate all audio clips (including reactivated ones)\n await this.audioSession.activateAllAudioClips();\n\n // 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 // For preview, simple cache invalidation or triggering re-render might be enough\n // if we were caching instructions. But PlaybackController pulls instructions every frame.\n // So just updating Model state is enough.\n }\n\n async getFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n const preheat = options?.preheat ?? false;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n // Calculate clip-relative time for cache lookup (global time - clip start time)\n let relativeTimeUs = options?.relativeTimeUs ?? timeUs - clip.startUs;\n\n // Clamp to clip duration to handle edge cases at clip boundaries\n relativeTimeUs = Math.min(relativeTimeUs, clip.durationUs - 1);\n\n // 1. Check L1 window cache\n if (!preheat) {\n this.cacheManager.setWindow(timeUs);\n const cachedFrame = this.cacheManager.getFrame(relativeTimeUs, clip.id);\n if (cachedFrame) {\n this.preheatNextClip(clip);\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n return cachedFrame;\n }\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n }\n\n if (signal?.aborted) {\n throw new DOMException('Render aborted', 'AbortError');\n }\n\n // 2. Try decode from OPFS resource (on-demand path)\n const resourceFrame = await this.decodeFromResource(clip, relativeTimeUs, timeUs, options);\n return resourceFrame;\n }\n\n private async preheatNextClip(clip: Clip): Promise<void> {\n if (clip.id === this.currentClipId) return;\n\n this.currentClipId = clip.id;\n const nextClip = this.compositionModel?.getClipsAtTime(\n clip.startUs + clip.durationUs,\n this.compositionModel?.mainTrackId\n )[0];\n if (nextClip && isVideoClip(nextClip)) {\n this.resourceLoader.fetch(nextClip.resourceId, {\n priority: 'normal',\n clipId: nextClip.id,\n trackId: nextClip.trackId,\n });\n }\n }\n\n /**\n * Compose frame from OPFS resource (on-demand decoding)\n * This is the new path for long clips with window caching\n */\n private async decodeFromResource(\n clip: Clip,\n relativeTimeUs: TimeUs,\n globalTimeUs: TimeUs,\n options?: RenderFrameOptions\n ): Promise<RcFrame | null> {\n if (!hasResourceId(clip)) return null;\n\n const resourceId = clip.resourceId;\n\n // Check resource state\n const resource = this.compositionModel?.getResource(resourceId);\n const isReady = resource?.state === 'ready';\n\n const fetchOptions = {\n priority: 'high' as const,\n clipId: clip.id,\n trackId: clip.trackId,\n };\n\n // In immediate mode, if not ready, trigger fetch in background and return null\n if (options?.immediate && !isReady) {\n // Fire and forget fetch to start loading\n this.resourceLoader.fetch(resourceId, fetchOptions);\n return null;\n }\n\n // Normal mode: wait for download\n await this.resourceLoader.fetch(resourceId, fetchOptions);\n\n // Create temporary on-demand video session\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: relativeTimeUs,\n globalTimeUs,\n resourceCache: this.cacheManager.resourceCache,\n mp4IndexCache: this.cacheManager.mp4IndexCache,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n fps: this.compositionModel?.fps ?? 30,\n });\n\n try {\n // Decode window: from GOP start frame to target position + 3s\n const DECODE_WINDOW_SIZE = 3_000_000;\n\n const windowStart = relativeTimeUs;\n const windowEnd = Math.min(clip.durationUs, relativeTimeUs + DECODE_WINDOW_SIZE);\n\n await session.decodeWindow(windowStart, windowEnd);\n // Return target frame from L1 cache (now composed)\n return this.cacheManager.getFrame(relativeTimeUs, clip.id);\n } catch (error) {\n console.error('[Orchestrator] Error composing from resource:', error);\n return null;\n } finally {\n // Dispose session immediately after composing\n await session.dispose();\n }\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId);\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 5,\n timeoutMs: options?.timeoutMs ?? 5_000,\n });\n }\n\n async dispose(): Promise<void> {\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n this.resourceLoader.dispose();\n await this.cacheManager.clear();\n\n this.workers.terminateAll();\n this.compositionModel = null;\n this.eventBus.dispose();\n }\n\n private buildWorkerConfigs(): Record<WorkerType, any> {\n const config = this.config as any;\n const defaultCanvasWidth = config.global?.defaultCanvasWidth ?? 720;\n const defaultCanvasHeight = config.global?.defaultCanvasHeight ?? 1280;\n const defaultFps = config.global?.defaultFps ?? 30;\n\n const targetFps = this.compositionModel?.fps ?? defaultFps;\n\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: targetFps,\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: targetFps,\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.exportScheduler.execute(model, options);\n }\n\n /**\n * Get render state for real-time composition\n * Returns layers ready for VideoComposer\n */\n async getRenderState(\n timeUs: TimeUs,\n options?: RenderFrameOptions\n ): Promise<{ layers: any[]; transition?: any } | null> {\n if (!this.compositionModel) {\n return null;\n }\n\n // Ensure frame/resource is ready (this populates L1 if needed)\n const frame = await this.getFrame(timeUs, options);\n\n // If immediate mode and no frame, return null to trigger buffering\n if (options?.immediate && !frame) {\n return null;\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n const relativeTimeUs = timeUs - clip.startUs;\n\n // Get instructions from planner\n const instructions = this.planner.getInstructions(clip.id);\n if (!instructions) {\n return null;\n }\n\n // Build layers array\n const layers: any[] = [];\n\n // 1. Filter active layers at this timestamp\n const activeLayers = instructions.layers.filter((layer: any) => {\n if (!layer.payload.attachmentId) {\n // Main track layer is always active\n return true;\n }\n if (layer.status !== 'ready') {\n return false;\n }\n // Check if layer is active at current timestamp\n return layer.activeRanges.some(\n (range: any) => relativeTimeUs >= range.startUs && relativeTimeUs < range.endUs\n );\n });\n\n // 2. Materialize layers\n for (const layerPlan of activeLayers) {\n const layer = this.materializeLayer(layerPlan, clip, relativeTimeUs, timeUs);\n if (layer) {\n layers.push(layer);\n }\n }\n\n return { layers };\n }\n\n /**\n * Materialize a serialized layer plan into concrete Layer\n */\n private materializeLayer(\n layerPlan: any,\n clip: Clip,\n clipRelativeTimeUs: TimeUs,\n globalTimeUs: TimeUs\n ): any | null {\n const baseLayer: any = {\n id: layerPlan.layerId,\n type: layerPlan.type,\n zIndex: layerPlan.zIndex ?? 0,\n visible: true,\n opacity: layerPlan.opacity ?? 1,\n };\n\n // Video layer - fetch raw VideoFrame from L1 (RcFrame wrapper)\n if (layerPlan.type === 'video' && !layerPlan.payload.attachmentId) {\n const rcFrame = this.cacheManager.getFrame(clipRelativeTimeUs, clip.id);\n if (!rcFrame) {\n console.warn('[Orchestrator] Video frame not found in L1:', clip.id, clipRelativeTimeUs);\n return null;\n }\n\n return {\n ...baseLayer,\n type: 'video',\n rcFrame: rcFrame,\n };\n }\n\n // Text layer\n if (layerPlan.type === 'text') {\n const payload = layerPlan.payload;\n return {\n ...baseLayer,\n type: 'text',\n text: payload.text,\n localeCode: payload.localeCode,\n fontConfig: payload.fontConfig,\n animation: payload.animation,\n wordTimings: payload.wordTimings,\n letterCase: payload.letterCase,\n };\n }\n\n // Image layer\n if (layerPlan.type === 'image') {\n const payload = layerPlan.payload;\n const resourceId = payload.resourceId;\n\n // Get ImageBitmap from CacheManager.imageBitmapCache\n const source = this.cacheManager.imageBitmapCache.get(resourceId);\n const imageLayer: any = {\n ...baseLayer,\n type: 'image',\n source,\n attachmentId: payload.attachmentId,\n };\n\n if (payload.targetWidth !== undefined) {\n imageLayer.targetWidth = payload.targetWidth;\n }\n if (payload.targetHeight !== undefined) {\n imageLayer.targetHeight = payload.targetHeight;\n }\n\n // Handle animation (overlays)\n if (payload.animation) {\n const { position, keyframes, overlayClipStartUs } = payload.animation;\n\n // Calculate time relative to overlay clip start\n const relativeTimeUs = globalTimeUs - overlayClipStartUs;\n\n // If outside keyframe range, hide\n if (relativeTimeUs < 0 || relativeTimeUs > keyframes[keyframes.length - 1].time) {\n return null; // Not visible at this time\n }\n\n const rotationRad = 0; // TODO: interpolate from keyframes\n\n imageLayer.transform = {\n x: position.x,\n y: position.y,\n scaleX: 1,\n scaleY: 1,\n rotation: rotationRad,\n anchorX: 0.5,\n anchorY: 0.5,\n };\n }\n\n return imageLayer;\n }\n\n return baseLayer;\n }\n}\n"],"names":["applyModelPatch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Orchestrator.js","sources":["../../src/orchestrator/Orchestrator.ts"],"sourcesContent":["import { EventBus } from '../event/EventBus';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { applyPatch as applyModelPatch } from '../model/patch';\nimport { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, RenderFrameOptions } from './types';\nimport { WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame, Clip } from '../model';\nimport { hasResourceId, isVideoClip } from '../model/types';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { GlobalAudioSession } from './GlobalAudioSession';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { ExportOptions } from '../types';\nimport { OnDemandVideoSession } from './OnDemandVideoSession';\nimport { ExportScheduler } from './ExportScheduler';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n muxManager: MuxManager;\n exportScheduler: ExportScheduler;\n\n private isInitialized = false;\n private config = ConfigLoader.getInstance().getConfig();\n private ensureCacheDebounceTimer: number | null = null;\n private currentClipId: string | null = null;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n // Initialize WorkerPool with worker path from config\n this.workers = new WorkerPool({\n eventBus: this.eventBus,\n workerConfigs,\n workerPath: config.workerPath,\n workerExtension: config.workerExtension,\n });\n\n const maxMemoryMB = config.cacheConfig?.l1Size || this.config.cache?.l1?.maxMemoryMB || 1024;\n const maxGOPs = this.config.decode?.video?.maxGOPs || 4;\n\n this.cacheManager = new CacheManager(\n {\n l1: {\n maxMemoryMB,\n maxGOPs,\n },\n resource: {\n projectId: config.projectId || this.config.global.projectId || 'default',\n },\n },\n this.eventBus\n );\n\n this.resourceLoader = new ResourceLoader({\n cacheManager: this.cacheManager,\n workerPool: this.workers,\n eventBus: this.eventBus,\n config: {\n maxConcurrent: this.config.load.maxConcurrent,\n preloadConcurrency: 2, // Fixed preload concurrency for idle background loading\n },\n onStateChange: (resourceId, state) => this.handleResourceStateChange(resourceId, state),\n });\n\n this.planner = new CompositionPlanner();\n\n this.audioSession = new GlobalAudioSession({\n cacheManager: this.cacheManager,\n workerPool: this.workers,\n resourceLoader: this.resourceLoader,\n eventBus: this.eventBus,\n buildWorkerConfigs: () => this.buildWorkerConfigs(),\n });\n\n this.muxManager = new MuxManager(\n this.cacheManager,\n this.audioSession,\n this.config.encode.audio as AudioEncoderConfig\n );\n\n this.exportScheduler = new ExportScheduler({\n workerPool: this.workers,\n planner: this.planner,\n cacheManager: this.cacheManager,\n resourceLoader: this.resourceLoader,\n muxManager: this.muxManager,\n audioSession: this.audioSession,\n workerConfigsProvider: () => this.buildWorkerConfigs(),\n eventBus: this.eventBus,\n });\n\n this.setupResourceFirstFrameHandler();\n this.setupPreloadHandlers();\n }\n\n private setupResourceFirstFrameHandler(): void {\n this.eventBus.on(MeframeEvent.ResourceFirstFrameReady, async (payload) => {\n const { resourceId, clipId, index, chunks } = payload;\n\n if (!this.compositionModel) return;\n\n // Find the specific clip\n const clip = this.compositionModel.findClip(clipId);\n if (!clip || !clip.trackId) return;\n\n // Only decode first frame for clips that start at composition time 0\n // (these clips need the resource's first frame as cover)\n if (clip.startUs === 0) {\n const fps = this.compositionModel.fps ?? 30;\n await OnDemandVideoSession.decodeAndCacheFirstFrame(\n resourceId,\n chunks,\n index,\n clip,\n this.cacheManager,\n fps\n );\n }\n });\n }\n\n private setupPreloadHandlers(): void {\n // Stop preloading when playback starts\n this.eventBus.on(MeframeEvent.PlaybackPlay, () => {\n this.resourceLoader.setPreloadingEnabled(false);\n });\n\n // Enable preloading when playback pauses/stops\n this.eventBus.on(MeframeEvent.PlaybackPause, () => {\n this.resourceLoader.setPreloadingEnabled(true);\n });\n\n this.eventBus.on(MeframeEvent.PlaybackStop, () => {\n this.resourceLoader.setPreloadingEnabled(true);\n });\n\n // Note: ModelSet and PatchApplied are handled internally in ResourceLoader via handleModelSet\n // and direct calls isn't needed as ResourceLoader listens to updateResourceState via onStateChange?\n // Wait, ResourceLoader handles ModelSet via eventHandlers.\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n await this.cacheManager.init();\n\n this.isInitialized = true;\n }\n\n // Event methods - forward to eventBus\n on<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.on(event, handler);\n }\n\n off<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.off(event, handler);\n }\n\n once<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.once(event, handler);\n }\n\n async setCompositionModel(model: CompositionModel): Promise<void> {\n this.compositionModel = model;\n this.planner.setModel(model);\n // ensure the cover resource is preloaded before audio is activated\n await this.resourceLoader.setModel(model);\n this.audioSession.setModel(model);\n\n this.eventBus.emit(MeframeEvent.ModelSet, model);\n\n this.eventBus.emit(MeframeEvent.CompositionUpdated, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce((acc: number, track: any) => acc + track.clips.length, 0),\n durationUs: model.durationUs,\n });\n }\n\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n // Apply patch and get affected clip IDs (simplified for 2-Clip strategy)\n // Note: addTrack/removeTrack already call buildIndexes() in patch.ts\n const affectedClipIds = applyModelPatch(this.compositionModel, patch);\n this.planner.applyPatch(patch, affectedClipIds);\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n affectedClips: Array.from(affectedClipIds),\n });\n\n // Process clip updates\n for (const clipId of affectedClipIds) {\n this.cacheManager.invalidateClip(clipId);\n }\n\n // Reactivate updated audio clips\n const reactivatedAudioClips: string[] = [];\n const reactivatedVideoClips: string[] = [];\n for (const clipId of affectedClipIds) {\n const clip = this.compositionModel.findClip(clipId);\n if (clip?.trackKind === 'audio') {\n await this.audioSession.deactivateClip(clipId);\n reactivatedAudioClips.push(clipId);\n } else if (clip?.trackKind === 'video') {\n reactivatedVideoClips.push(clipId);\n }\n }\n\n // Activate all audio clips (including reactivated ones)\n await this.audioSession.activateAllAudioClips();\n\n // Note: No need to restart per-clip playback in new architecture\n // scheduleAudio() uses OfflineAudioMixer which automatically includes all active clips\n }\n\n private handleResourceStateChange(resourceId: string, state: Resource['state']): void {\n if (!this.compositionModel) {\n return;\n }\n\n this.compositionModel.updateResourceState(resourceId, state ?? 'pending');\n\n if (state !== 'ready') {\n return;\n }\n\n // For preview, simple cache invalidation or triggering re-render might be enough\n // if we were caching instructions. But PlaybackController pulls instructions every frame.\n // So just updating Model state is enough.\n }\n\n async getFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n const preheat = options?.preheat ?? false;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n // Calculate clip-relative time for cache lookup (global time - clip start time)\n let relativeTimeUs = options?.relativeTimeUs ?? timeUs - clip.startUs;\n\n // Clamp to clip duration to handle edge cases at clip boundaries\n relativeTimeUs = Math.min(relativeTimeUs, clip.durationUs - 1);\n\n // 1. Check L1 window cache\n // Note: preheat mode skips cache check to force decoding the entire window\n // Why: Video cache is frame-level (point query), not window-level (range query)\n // A single cached frame doesn't guarantee the entire window is cached\n // Without preheat flag, preheating would return early on first frame hit\n if (!preheat) {\n const cachedFrame = this.cacheManager.getFrame(relativeTimeUs, clip.id);\n if (cachedFrame) {\n this.preheatNextClip(clip);\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n return cachedFrame;\n }\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n }\n\n if (signal?.aborted) {\n throw new DOMException('Render aborted', 'AbortError');\n }\n\n // 2. Try decode from OPFS resource (on-demand path)\n const resourceFrame = await this.decodeFromResource(clip, relativeTimeUs, timeUs, options);\n return resourceFrame;\n }\n\n private async preheatNextClip(clip: Clip): Promise<void> {\n if (clip.id === this.currentClipId) return;\n\n this.currentClipId = clip.id;\n const nextClip = this.compositionModel?.getClipsAtTime(\n clip.startUs + clip.durationUs,\n this.compositionModel?.mainTrackId\n )[0];\n if (nextClip && isVideoClip(nextClip)) {\n this.resourceLoader.fetch(nextClip.resourceId, {\n priority: 'normal',\n clipId: nextClip.id,\n trackId: nextClip.trackId,\n });\n }\n }\n\n /**\n * Compose frame from OPFS resource (on-demand decoding)\n * This is the new path for long clips with window caching\n */\n private async decodeFromResource(\n clip: Clip,\n relativeTimeUs: TimeUs,\n globalTimeUs: TimeUs,\n options?: RenderFrameOptions\n ): Promise<RcFrame | null> {\n if (!hasResourceId(clip)) return null;\n\n const resourceId = clip.resourceId;\n\n // Check resource state\n const resource = this.compositionModel?.getResource(resourceId);\n const isReady = resource?.state === 'ready';\n\n const fetchOptions = {\n priority: 'high' as const,\n clipId: clip.id,\n trackId: clip.trackId,\n };\n\n // In immediate mode, if not ready, trigger fetch in background and return null\n if (options?.immediate && !isReady) {\n // Fire and forget fetch to start loading\n this.resourceLoader.fetch(resourceId, fetchOptions);\n return null;\n }\n\n // Normal mode: wait for download\n await this.resourceLoader.fetch(resourceId, fetchOptions);\n\n // Create temporary on-demand video session\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: relativeTimeUs,\n globalTimeUs,\n resourceLoader: this.resourceLoader,\n mp4IndexCache: this.cacheManager.mp4IndexCache,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n fps: this.compositionModel?.fps ?? 30,\n });\n\n try {\n // Decode window: from GOP start frame to target position + 3s\n const DECODE_WINDOW_SIZE = 3_000_000;\n\n const windowStart = relativeTimeUs;\n const windowEnd = Math.min(clip.durationUs, relativeTimeUs + DECODE_WINDOW_SIZE);\n\n await session.decodeWindow(windowStart, windowEnd);\n // Return target frame from L1 cache (now composed)\n return this.cacheManager.getFrame(relativeTimeUs, clip.id);\n } catch (error) {\n console.error('[Orchestrator] Error composing from resource:', error);\n return null;\n } finally {\n // Dispose session immediately after composing\n await session.dispose();\n }\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId);\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 5,\n timeoutMs: options?.timeoutMs ?? 5_000,\n });\n }\n\n async dispose(): Promise<void> {\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n this.resourceLoader.dispose();\n await this.cacheManager.clear();\n\n this.workers.terminateAll();\n this.compositionModel = null;\n this.eventBus.dispose();\n }\n\n private buildWorkerConfigs(): Record<WorkerType, any> {\n const config = this.config;\n const defaultCanvasWidth = config.global.defaultCanvasWidth;\n const defaultCanvasHeight = config.global.defaultCanvasHeight;\n const defaultFps = config.global.defaultFps;\n\n const targetFps = this.compositionModel?.fps ?? defaultFps;\n\n return {\n videoDemux: {\n highWaterMark: config.demux.backpressure.highWaterMark,\n },\n audioDemux: {\n highWaterMark: config.demux.backpressure.highWaterMark,\n },\n videoDecode: config.decode.video,\n audioDecode: config.decode.audio,\n videoCompose: {\n width: defaultCanvasWidth,\n height: defaultCanvasHeight,\n fps: targetFps,\n backgroundColor: '#000000',\n enableSmoothing: true,\n enableHardwareAcceleration: true,\n },\n audioCompose: {\n enableDucking: config.compose.audio.enableDucking,\n },\n videoEncode: {\n codec: 'avc1.42002A',\n width: defaultCanvasWidth,\n height: defaultCanvasHeight,\n bitrate: config.encode.video.bitrateKbps\n ? config.encode.video.bitrateKbps * 1000\n : 12_000_000,\n framerate: targetFps,\n latencyMode: 'quality',\n bitrateMode: 'variable',\n hardwareAcceleration: 'no-preference',\n ...config.encode.video,\n },\n };\n }\n\n async export(model: CompositionModel, options: ExportOptions): Promise<Blob> {\n return this.exportScheduler.execute(model, options);\n }\n\n /**\n * Get render state for real-time composition\n * Returns layers ready for VideoComposer\n */\n async getRenderState(\n timeUs: TimeUs,\n options?: RenderFrameOptions\n ): Promise<{ layers: any[]; transition?: any } | null> {\n if (!this.compositionModel) {\n return null;\n }\n\n // Ensure frame/resource is ready (this populates L1 if needed)\n const frame = await this.getFrame(timeUs, options);\n\n // If immediate mode and no frame, return null to trigger buffering\n if (options?.immediate && !frame) {\n return null;\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n const relativeTimeUs = timeUs - clip.startUs;\n\n // Get instructions from planner\n const instructions = this.planner.getInstructions(clip.id);\n if (!instructions) {\n return null;\n }\n\n // Build layers array\n const layers: any[] = [];\n\n // 1. Filter active layers at this timestamp\n const activeLayers = instructions.layers.filter((layer: any) => {\n if (!layer.payload.attachmentId) {\n // Main track layer is always active\n return true;\n }\n if (layer.status !== 'ready') {\n return false;\n }\n // Check if layer is active at current timestamp\n return layer.activeRanges.some(\n (range: any) => relativeTimeUs >= range.startUs && relativeTimeUs < range.endUs\n );\n });\n\n // 2. Materialize layers\n for (const layerPlan of activeLayers) {\n const layer = await this.materializeLayer(layerPlan, clip, relativeTimeUs, timeUs);\n if (layer) {\n layers.push(layer);\n }\n }\n\n return { layers };\n }\n\n /**\n * Materialize a serialized layer plan into concrete Layer\n */\n private async materializeLayer(\n layerPlan: any,\n clip: Clip,\n clipRelativeTimeUs: TimeUs,\n globalTimeUs: TimeUs\n ): Promise<any | null> {\n const baseLayer: any = {\n id: layerPlan.layerId,\n type: layerPlan.type,\n zIndex: layerPlan.zIndex ?? 0,\n visible: true,\n opacity: layerPlan.opacity ?? 1,\n };\n\n // Video layer - fetch raw VideoFrame from L1 (RcFrame wrapper)\n if (layerPlan.type === 'video' && !layerPlan.payload.attachmentId) {\n const rcFrame = this.cacheManager.getFrame(clipRelativeTimeUs, clip.id);\n if (!rcFrame) {\n console.warn('[Orchestrator] Video frame not found in L1:', clip.id, clipRelativeTimeUs);\n return null;\n }\n\n return {\n ...baseLayer,\n type: 'video',\n rcFrame: rcFrame,\n };\n }\n\n // Text layer\n if (layerPlan.type === 'text') {\n const payload = layerPlan.payload;\n return {\n ...baseLayer,\n type: 'text',\n text: payload.text,\n localeCode: payload.localeCode,\n fontConfig: payload.fontConfig,\n animation: payload.animation,\n wordTimings: payload.wordTimings,\n letterCase: payload.letterCase,\n };\n }\n\n // Image layer\n if (layerPlan.type === 'image') {\n const payload = layerPlan.payload;\n const resource = this.compositionModel?.getResource(payload.resourceId);\n if (!resource) {\n return null;\n }\n\n const source = await this.resourceLoader.loadImage(resource);\n const imageLayer: any = {\n ...baseLayer,\n type: 'image',\n source,\n attachmentId: payload.attachmentId,\n };\n\n if (payload.targetWidth !== undefined) {\n imageLayer.targetWidth = payload.targetWidth;\n }\n if (payload.targetHeight !== undefined) {\n imageLayer.targetHeight = payload.targetHeight;\n }\n\n // Handle animation (overlays)\n if (payload.animation) {\n const { position, keyframes, overlayClipStartUs } = payload.animation;\n\n // Calculate time relative to overlay clip start\n const relativeTimeUs = globalTimeUs - overlayClipStartUs;\n\n // If outside keyframe range, hide\n if (relativeTimeUs < 0 || relativeTimeUs > keyframes[keyframes.length - 1].time) {\n return null; // Not visible at this time\n }\n\n const rotationRad = 0; // TODO: interpolate from keyframes\n\n imageLayer.transform = {\n x: position.x,\n y: position.y,\n scaleX: 1,\n scaleY: 1,\n rotation: rotationRad,\n anchorX: 0.5,\n anchorY: 0.5,\n };\n }\n\n return imageLayer;\n }\n\n return baseLayer;\n }\n}\n"],"names":["applyModelPatch"],"mappings":"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"}
|
|
@@ -48,12 +48,10 @@ export declare class VideoClipSession {
|
|
|
48
48
|
private decodeWorker;
|
|
49
49
|
private videoEncodeWorker;
|
|
50
50
|
private videoDemuxWorker;
|
|
51
|
-
private audioDecodeWorker;
|
|
52
51
|
private visualStream;
|
|
53
52
|
private composeToEncodeChannel;
|
|
54
53
|
private decodeToVisualChannel;
|
|
55
54
|
private demuxToDecodeChannel;
|
|
56
|
-
private demuxAudioToDecodeChannel;
|
|
57
55
|
private isActive;
|
|
58
56
|
private isDisposed;
|
|
59
57
|
static create(config: VideoClipSessionConfig): Promise<VideoClipSession>;
|
|
@@ -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,OAAO,CAAC,IAAI,CAAC,CAAC;IACtF,oBAAoB,CAClB,MAAM,EAAE,cAAc,CAAC;QAAE,KAAK,EAAE,iBAAiB,CAAC;QAAC,QAAQ,EAAE,yBAAyB,CAAA;KAAE,CAAC,EACzF,KAAK,EAAE,OAAO,GAAG,OAAO,GACvB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,kBAAkB,CAAC,CACjB,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC,EACjC,QAAQ,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;KACxB,GACA,IAAI,CAAC;IACR,gBAAgB,CAAC,IAAI,IAAI,CAAC;IAC1B,eAAe,CAAC,CAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE;AAED,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACvC,SAAS,EAAE,yBAAyB,CAAC;IACrC,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAQD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0B;IACxD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAiB;IAEjD,OAAO,CAAC,kBAAkB,CAAmC;IAC7D,aAAa,EAAE,UAAU,GAAG,IAAI,CAAQ;IACxC,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,iBAAiB,CAA2B;IACpD,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"VideoClipSession.d.ts","sourceRoot":"","sources":["../../src/orchestrator/VideoClipSession.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,12 +15,10 @@ class VideoClipSession {
|
|
|
15
15
|
decodeWorker = null;
|
|
16
16
|
videoEncodeWorker = null;
|
|
17
17
|
videoDemuxWorker = null;
|
|
18
|
-
audioDecodeWorker = null;
|
|
19
18
|
visualStream = null;
|
|
20
19
|
composeToEncodeChannel = null;
|
|
21
20
|
decodeToVisualChannel = null;
|
|
22
21
|
demuxToDecodeChannel = null;
|
|
23
|
-
demuxAudioToDecodeChannel = null;
|
|
24
22
|
isActive = false;
|
|
25
23
|
isDisposed = false;
|
|
26
24
|
static async create(config) {
|
|
@@ -204,11 +202,6 @@ class VideoClipSession {
|
|
|
204
202
|
initial: true,
|
|
205
203
|
config: decodeConfig
|
|
206
204
|
});
|
|
207
|
-
if (this.forL2Only) {
|
|
208
|
-
this.audioDecodeWorker = await this.workerPool.getOrCreate("audioDecode", this.sessionId, {
|
|
209
|
-
lazy: true
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
205
|
}
|
|
213
206
|
this.composeWorker = await this.workerPool.getOrCreate("videoCompose", this.sessionId, {
|
|
214
207
|
lazy: true
|
|
@@ -327,41 +320,6 @@ class VideoClipSession {
|
|
|
327
320
|
);
|
|
328
321
|
});
|
|
329
322
|
}
|
|
330
|
-
if (this.audioDecodeWorker) {
|
|
331
|
-
this.demuxAudioToDecodeChannel = new MessageChannel();
|
|
332
|
-
await this.videoDemuxWorker.send(
|
|
333
|
-
"connect",
|
|
334
|
-
{
|
|
335
|
-
direction: "downstream",
|
|
336
|
-
port: this.demuxAudioToDecodeChannel.port1,
|
|
337
|
-
streamType: "audio",
|
|
338
|
-
sessionId
|
|
339
|
-
},
|
|
340
|
-
{ transfer: [this.demuxAudioToDecodeChannel.port1] }
|
|
341
|
-
);
|
|
342
|
-
const clip = this.getClip();
|
|
343
|
-
await this.audioDecodeWorker.send(
|
|
344
|
-
"connect",
|
|
345
|
-
{
|
|
346
|
-
direction: "upstream",
|
|
347
|
-
port: this.demuxAudioToDecodeChannel.port2,
|
|
348
|
-
streamType: "audio",
|
|
349
|
-
sessionId,
|
|
350
|
-
clipStartUs: clip?.startUs || 0,
|
|
351
|
-
clipDurationUs: clip?.durationUs || 0
|
|
352
|
-
},
|
|
353
|
-
{ transfer: [this.demuxAudioToDecodeChannel.port2] }
|
|
354
|
-
);
|
|
355
|
-
this.audioDecodeWorker.receiveStream((stream) => {
|
|
356
|
-
if (this.callbacks.onAudioStreamReady && clip) {
|
|
357
|
-
this.callbacks.onAudioStreamReady(stream, {
|
|
358
|
-
sessionId,
|
|
359
|
-
clipStartUs: clip.startUs,
|
|
360
|
-
clipDurationUs: clip.durationUs
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
323
|
}
|
|
366
324
|
async installInstructions(instructions) {
|
|
367
325
|
this.instructionContext = {
|
|
@@ -415,22 +373,15 @@ class VideoClipSession {
|
|
|
415
373
|
this.workerPool.terminate("videoEncode", this.sessionId);
|
|
416
374
|
this.videoEncodeWorker = null;
|
|
417
375
|
}
|
|
418
|
-
if (this.audioDecodeWorker) {
|
|
419
|
-
this.workerPool.terminate("audioDecode", this.sessionId);
|
|
420
|
-
this.audioDecodeWorker = null;
|
|
421
|
-
}
|
|
422
376
|
this.composeToEncodeChannel?.port1.close();
|
|
423
377
|
this.composeToEncodeChannel?.port2.close();
|
|
424
378
|
this.decodeToVisualChannel?.port1.close();
|
|
425
379
|
this.decodeToVisualChannel?.port2.close();
|
|
426
380
|
this.demuxToDecodeChannel?.port1.close();
|
|
427
381
|
this.demuxToDecodeChannel?.port2.close();
|
|
428
|
-
this.demuxAudioToDecodeChannel?.port1.close();
|
|
429
|
-
this.demuxAudioToDecodeChannel?.port2.close();
|
|
430
382
|
this.composeToEncodeChannel = null;
|
|
431
383
|
this.decodeToVisualChannel = null;
|
|
432
384
|
this.demuxToDecodeChannel = null;
|
|
433
|
-
this.demuxAudioToDecodeChannel = null;
|
|
434
385
|
}
|
|
435
386
|
async invalidateClipCache() {
|
|
436
387
|
await this.cacheManager.invalidateClip(this.clipId);
|
|
@@ -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): 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 // Audio is handled by GlobalAudioSession via AudioSampleCache\n emitAudioToMain: false,\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 // Note: Audio pipeline is handled by GlobalAudioSession\n // For L2 export, we still want to use the Worker pipeline for audio to avoid main thread blocking/deadlocks\n // So we restore the AudioDecodeWorker logic here for export scenarios\n if (this.forL2Only) {\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\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
|
+
{"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 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 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 // Audio is handled by GlobalAudioSession via AudioSampleCache\n emitAudioToMain: false,\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 // Note: Audio is handled by GlobalAudioSession via AudioSampleCache (main thread)\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\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 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\n this.composeToEncodeChannel = null;\n this.decodeToVisualChannel = null;\n this.demuxToDecodeChannel = 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":"OfflineAudioMixer.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/OfflineAudioMixer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAS7D,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,QAAQ;IALlB,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,gBAAgB,CAAK;gBAGnB,YAAY,EAAE,YAAY,EAC1B,QAAQ,EAAE,MAAM,gBAAgB,GAAG,IAAI;IAG3C,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"OfflineAudioMixer.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/OfflineAudioMixer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAS7D,qBAAa,iBAAiB;IAK1B,OAAO,CAAC,YAAY;IACpB,OAAO,CAAC,QAAQ;IALlB,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,gBAAgB,CAAK;gBAGnB,YAAY,EAAE,YAAY,EAC1B,QAAQ,EAAE,MAAM,gBAAgB,GAAG,IAAI;IAG3C,GAAG,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IA0D3E,OAAO,CAAC,gBAAgB;CA2CzB"}
|
|
@@ -12,30 +12,25 @@ class OfflineAudioMixer {
|
|
|
12
12
|
const ctx = new OfflineAudioContext(this.numberOfChannels, frameCount, this.sampleRate);
|
|
13
13
|
const clips = this.getClipsInWindow(windowStartUs, windowEndUs);
|
|
14
14
|
for (const clip of clips) {
|
|
15
|
-
const pcmData = this.cacheManager.getClipPCMWithMetadata(clip.clipId, 0, clip.durationUs);
|
|
16
|
-
if (!pcmData || pcmData.planes.length === 0) {
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
15
|
const clipIntersectStartUs = Math.max(windowStartUs, clip.startUs);
|
|
20
16
|
const clipIntersectEndUs = Math.min(windowEndUs, clip.startUs + clip.durationUs);
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
intersectFrames,
|
|
28
|
-
pcmData.sampleRate
|
|
29
|
-
// Use actual sample rate, OfflineAudioContext will resample
|
|
17
|
+
const clipRelativeStartUs = clipIntersectStartUs - clip.startUs;
|
|
18
|
+
const clipRelativeEndUs = clipIntersectEndUs - clip.startUs;
|
|
19
|
+
const pcmData = this.cacheManager.getClipPCMWithMetadata(
|
|
20
|
+
clip.clipId,
|
|
21
|
+
clipRelativeStartUs,
|
|
22
|
+
clipRelativeEndUs
|
|
30
23
|
);
|
|
24
|
+
if (!pcmData || pcmData.planes.length === 0) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const intersectFrames = pcmData.planes[0]?.length ?? 0;
|
|
28
|
+
if (intersectFrames === 0) continue;
|
|
29
|
+
const buffer = ctx.createBuffer(pcmData.planes.length, intersectFrames, pcmData.sampleRate);
|
|
31
30
|
for (let channel = 0; channel < pcmData.planes.length; channel++) {
|
|
32
31
|
const plane = pcmData.planes[channel];
|
|
33
32
|
if (plane) {
|
|
34
|
-
|
|
35
|
-
const copyLength = Math.min(intersectFrames, plane.length - offsetFrames);
|
|
36
|
-
for (let i = 0; i < copyLength; i++) {
|
|
37
|
-
channelData[i] = plane[offsetFrames + i] ?? 0;
|
|
38
|
-
}
|
|
33
|
+
buffer.copyToChannel(new Float32Array(plane), channel);
|
|
39
34
|
}
|
|
40
35
|
}
|
|
41
36
|
const source = ctx.createBufferSource();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OfflineAudioMixer.js","sources":["../../../src/stages/compose/OfflineAudioMixer.ts"],"sourcesContent":["import type { TimeUs } from '../../model/types';\nimport { hasAudioConfig } from '../../model/types';\nimport type { CompositionModel } from '../../model';\nimport type { CacheManager } from '../../cache/CacheManager';\n\ninterface MixClipInfo {\n clipId: string;\n startUs: TimeUs;\n durationUs: TimeUs;\n volume: number;\n}\n\nexport class OfflineAudioMixer {\n private sampleRate = 48_000;\n private numberOfChannels = 2;\n\n constructor(\n private cacheManager: CacheManager,\n private getModel: () => CompositionModel | null\n ) {}\n\n async mix(windowStartUs: TimeUs, windowEndUs: TimeUs): Promise<AudioBuffer> {\n const durationUs = windowEndUs - windowStartUs;\n const frameCount = Math.ceil((durationUs / 1_000_000) * this.sampleRate);\n\n const ctx = new OfflineAudioContext(this.numberOfChannels, frameCount, this.sampleRate);\n\n const clips = this.getClipsInWindow(windowStartUs, windowEndUs);\n\n for (const clip of clips) {\n //
|
|
1
|
+
{"version":3,"file":"OfflineAudioMixer.js","sources":["../../../src/stages/compose/OfflineAudioMixer.ts"],"sourcesContent":["import type { TimeUs } from '../../model/types';\nimport { hasAudioConfig } from '../../model/types';\nimport type { CompositionModel } from '../../model';\nimport type { CacheManager } from '../../cache/CacheManager';\n\ninterface MixClipInfo {\n clipId: string;\n startUs: TimeUs;\n durationUs: TimeUs;\n volume: number;\n}\n\nexport class OfflineAudioMixer {\n private sampleRate = 48_000;\n private numberOfChannels = 2;\n\n constructor(\n private cacheManager: CacheManager,\n private getModel: () => CompositionModel | null\n ) {}\n\n async mix(windowStartUs: TimeUs, windowEndUs: TimeUs): Promise<AudioBuffer> {\n const durationUs = windowEndUs - windowStartUs;\n const frameCount = Math.ceil((durationUs / 1_000_000) * this.sampleRate);\n\n const ctx = new OfflineAudioContext(this.numberOfChannels, frameCount, this.sampleRate);\n\n const clips = this.getClipsInWindow(windowStartUs, windowEndUs);\n\n for (const clip of clips) {\n // Calculate clip-relative time range\n const clipIntersectStartUs = Math.max(windowStartUs, clip.startUs);\n const clipIntersectEndUs = Math.min(windowEndUs, clip.startUs + clip.durationUs);\n const clipRelativeStartUs = clipIntersectStartUs - clip.startUs;\n const clipRelativeEndUs = clipIntersectEndUs - clip.startUs;\n\n // Get PCM data for the exact range needed (no manual cropping)\n const pcmData = this.cacheManager.getClipPCMWithMetadata(\n clip.clipId,\n clipRelativeStartUs,\n clipRelativeEndUs\n );\n\n if (!pcmData || pcmData.planes.length === 0) {\n continue;\n }\n\n const intersectFrames = pcmData.planes[0]?.length ?? 0;\n if (intersectFrames === 0) continue;\n\n // Create AudioBuffer\n const buffer = ctx.createBuffer(pcmData.planes.length, intersectFrames, pcmData.sampleRate);\n\n for (let channel = 0; channel < pcmData.planes.length; channel++) {\n const plane = pcmData.planes[channel];\n if (plane) {\n // Create new Float32Array to ensure correct type (ArrayBuffer, not SharedArrayBuffer)\n buffer.copyToChannel(new Float32Array(plane), channel);\n }\n }\n\n const source = ctx.createBufferSource();\n source.buffer = buffer;\n\n const gainNode = ctx.createGain();\n gainNode.gain.value = clip.volume;\n\n source.connect(gainNode);\n gainNode.connect(ctx.destination);\n\n const relativeStartUs = clipIntersectStartUs - windowStartUs;\n const startTime = relativeStartUs / 1_000_000;\n source.start(startTime);\n }\n\n const mixedBuffer = await ctx.startRendering();\n return mixedBuffer;\n }\n\n private getClipsInWindow(windowStartUs: TimeUs, windowEndUs: TimeUs): MixClipInfo[] {\n const clips: MixClipInfo[] = [];\n const model = this.getModel();\n if (!model) {\n return clips;\n }\n\n for (const track of model.tracks) {\n for (const clip of track.clips) {\n const clipEndUs = clip.startUs + clip.durationUs;\n if (clip.startUs < windowEndUs && clipEndUs > windowStartUs) {\n // Read audio config (only video/audio clips have audioConfig)\n if (hasAudioConfig(clip)) {\n const muted = clip.audioConfig?.muted ?? false;\n\n // Skip muted clips in export (performance optimization)\n if (muted) {\n continue;\n }\n\n const volume = clip.audioConfig?.volume ?? 1.0;\n\n clips.push({\n clipId: clip.id,\n startUs: clip.startUs,\n durationUs: clip.durationUs,\n volume,\n });\n } else {\n // Caption/Fx clips in audio track should not happen, but handle gracefully\n clips.push({\n clipId: clip.id,\n startUs: clip.startUs,\n durationUs: clip.durationUs,\n volume: 1.0,\n });\n }\n }\n }\n }\n\n return clips;\n }\n}\n"],"names":[],"mappings":";AAYO,MAAM,kBAAkB;AAAA,EAI7B,YACU,cACA,UACR;AAFQ,SAAA,eAAA;AACA,SAAA,WAAA;AAAA,EACP;AAAA,EANK,aAAa;AAAA,EACb,mBAAmB;AAAA,EAO3B,MAAM,IAAI,eAAuB,aAA2C;AAC1E,UAAM,aAAa,cAAc;AACjC,UAAM,aAAa,KAAK,KAAM,aAAa,MAAa,KAAK,UAAU;AAEvE,UAAM,MAAM,IAAI,oBAAoB,KAAK,kBAAkB,YAAY,KAAK,UAAU;AAEtF,UAAM,QAAQ,KAAK,iBAAiB,eAAe,WAAW;AAE9D,eAAW,QAAQ,OAAO;AAExB,YAAM,uBAAuB,KAAK,IAAI,eAAe,KAAK,OAAO;AACjE,YAAM,qBAAqB,KAAK,IAAI,aAAa,KAAK,UAAU,KAAK,UAAU;AAC/E,YAAM,sBAAsB,uBAAuB,KAAK;AACxD,YAAM,oBAAoB,qBAAqB,KAAK;AAGpD,YAAM,UAAU,KAAK,aAAa;AAAA,QAChC,KAAK;AAAA,QACL;AAAA,QACA;AAAA,MAAA;AAGF,UAAI,CAAC,WAAW,QAAQ,OAAO,WAAW,GAAG;AAC3C;AAAA,MACF;AAEA,YAAM,kBAAkB,QAAQ,OAAO,CAAC,GAAG,UAAU;AACrD,UAAI,oBAAoB,EAAG;AAG3B,YAAM,SAAS,IAAI,aAAa,QAAQ,OAAO,QAAQ,iBAAiB,QAAQ,UAAU;AAE1F,eAAS,UAAU,GAAG,UAAU,QAAQ,OAAO,QAAQ,WAAW;AAChE,cAAM,QAAQ,QAAQ,OAAO,OAAO;AACpC,YAAI,OAAO;AAET,iBAAO,cAAc,IAAI,aAAa,KAAK,GAAG,OAAO;AAAA,QACvD;AAAA,MACF;AAEA,YAAM,SAAS,IAAI,mBAAA;AACnB,aAAO,SAAS;AAEhB,YAAM,WAAW,IAAI,WAAA;AACrB,eAAS,KAAK,QAAQ,KAAK;AAE3B,aAAO,QAAQ,QAAQ;AACvB,eAAS,QAAQ,IAAI,WAAW;AAEhC,YAAM,kBAAkB,uBAAuB;AAC/C,YAAM,YAAY,kBAAkB;AACpC,aAAO,MAAM,SAAS;AAAA,IACxB;AAEA,UAAM,cAAc,MAAM,IAAI,eAAA;AAC9B,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiB,eAAuB,aAAoC;AAClF,UAAM,QAAuB,CAAA;AAC7B,UAAM,QAAQ,KAAK,SAAA;AACnB,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAEA,eAAW,SAAS,MAAM,QAAQ;AAChC,iBAAW,QAAQ,MAAM,OAAO;AAC9B,cAAM,YAAY,KAAK,UAAU,KAAK;AACtC,YAAI,KAAK,UAAU,eAAe,YAAY,eAAe;AAE3D,cAAI,eAAe,IAAI,GAAG;AACxB,kBAAM,QAAQ,KAAK,aAAa,SAAS;AAGzC,gBAAI,OAAO;AACT;AAAA,YACF;AAEA,kBAAM,SAAS,KAAK,aAAa,UAAU;AAE3C,kBAAM,KAAK;AAAA,cACT,QAAQ,KAAK;AAAA,cACb,SAAS,KAAK;AAAA,cACd,YAAY,KAAK;AAAA,cACjB;AAAA,YAAA,CACD;AAAA,UACH,OAAO;AAEL,kBAAM,KAAK;AAAA,cACT,QAAQ,KAAK;AAAA,cACb,SAAS,KAAK;AAAA,cACd,YAAY,KAAK;AAAA,cACjB,QAAQ;AAAA,YAAA,CACT;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;"}
|