@meframe/core 0.0.35 → 0.0.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/config/defaults.d.ts +1 -1
  2. package/dist/config/defaults.js +2 -2
  3. package/dist/config/defaults.js.map +1 -1
  4. package/dist/config/presets.js +4 -4
  5. package/dist/config/presets.js.map +1 -1
  6. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
  7. package/dist/orchestrator/ExportScheduler.js +11 -6
  8. package/dist/orchestrator/ExportScheduler.js.map +1 -1
  9. package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
  10. package/dist/orchestrator/OnDemandVideoSession.js +23 -1
  11. package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
  12. package/dist/orchestrator/Orchestrator.d.ts +5 -0
  13. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  14. package/dist/orchestrator/Orchestrator.js +21 -2
  15. package/dist/orchestrator/Orchestrator.js.map +1 -1
  16. package/dist/stages/decode/BaseDecoder.d.ts.map +1 -1
  17. package/dist/stages/decode/BaseDecoder.js +35 -1
  18. package/dist/stages/decode/BaseDecoder.js.map +1 -1
  19. package/dist/stages/encode/BaseEncoder.d.ts.map +1 -1
  20. package/dist/stages/encode/BaseEncoder.js +31 -1
  21. package/dist/stages/encode/BaseEncoder.js.map +1 -1
  22. package/dist/stages/encode/VideoChunkEncoder.d.ts.map +1 -1
  23. package/dist/stages/encode/types.d.ts +1 -0
  24. package/dist/stages/encode/types.d.ts.map +1 -1
  25. package/dist/workers/{BaseDecoder.CTW-vr29.js → BaseDecoder.DWHTBMDB.js} +36 -2
  26. package/dist/workers/BaseDecoder.DWHTBMDB.js.map +1 -0
  27. package/dist/workers/stages/decode/{audio-decode.worker.CP8bXXa4.js → audio-decode.worker._DbEy3nn.js} +2 -2
  28. package/dist/workers/stages/decode/{audio-decode.worker.CP8bXXa4.js.map → audio-decode.worker._DbEy3nn.js.map} +1 -1
  29. package/dist/workers/stages/decode/{video-decode.worker.BIspTxgV.js → video-decode.worker.qinOL8Zt.js} +2 -2
  30. package/dist/workers/stages/decode/{video-decode.worker.BIspTxgV.js.map → video-decode.worker.qinOL8Zt.js.map} +1 -1
  31. package/dist/workers/stages/encode/{video-encode.worker.u2o7iXCT.js → video-encode.worker.CXgr5E16.js} +37 -4
  32. package/dist/workers/stages/encode/video-encode.worker.CXgr5E16.js.map +1 -0
  33. package/dist/workers/worker-manifest.json +3 -3
  34. package/package.json +1 -1
  35. package/dist/workers/BaseDecoder.CTW-vr29.js.map +0 -1
  36. package/dist/workers/stages/encode/video-encode.worker.u2o7iXCT.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 } 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 activeOnDemandSession: OnDemandVideoSession | 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 cancelActiveDecoding(): void {\n if (this.activeOnDemandSession) {\n void this.activeOnDemandSession.dispose();\n this.activeOnDemandSession = null;\n }\n }\n\n /**\n * Decode and render nearest keyframe for fast seek preview\n * Returns the keyframe timestamp if successfully decoded, null otherwise\n */\n async tryRenderKeyframe(timeUs: TimeUs): Promise<TimeUs | null> {\n if (!this.compositionModel) return null;\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip || !hasResourceId(clip)) return null;\n\n const relativeTimeUs = timeUs - clip.startUs;\n const resourceId = clip.resourceId;\n\n // Check if keyframe is already in L1 cache\n const keyframeSample = this.cacheManager.mp4IndexCache.getNearestKeyframe(\n resourceId,\n relativeTimeUs\n );\n\n if (!keyframeSample) return null;\n\n const cachedKeyframe = this.cacheManager.getFrame(keyframeSample.timestamp, clip.id);\n if (cachedKeyframe) {\n return keyframeSample.timestamp;\n }\n\n // Resource must be ready to decode keyframe\n const resource = this.compositionModel.getResource(resourceId);\n if (resource?.state !== 'ready') return null;\n\n // Create temporary session to decode keyframe\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: relativeTimeUs,\n globalTimeUs: timeUs,\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 const keyframeTimeUs = await session.decodeKeyframe(relativeTimeUs);\n return keyframeTimeUs;\n } catch (error) {\n console.warn('[Orchestrator] Fast keyframe decode failed:', error);\n return null;\n } finally {\n await session.dispose();\n }\n }\n\n async setCompositionModel(model: CompositionModel): Promise<void> {\n this.compositionModel = model;\n this.cacheManager.clear();\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.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 return null;\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 /**\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.load(resourceId, fetchOptions);\n return null;\n }\n\n // Normal mode: wait for download\n await this.resourceLoader.load(resourceId, fetchOptions);\n\n this.cancelActiveDecoding();\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 this.activeOnDemandSession = session;\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 if (session.isDisposed) {\n return null;\n }\n console.error('[Orchestrator] Error composing from resource:', error);\n return null;\n } finally {\n if (this.activeOnDemandSession === session) {\n this.activeOnDemandSession = null;\n }\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 * Preheat a specific clip's window range\n * Used by PlaybackController for cross-clip window preheating\n *\n * @param clipId - Clip identifier\n * @param clipRelativeStart - Start time relative to clip (microseconds)\n * @param clipRelativeEnd - End time relative to clip (microseconds)\n * @param globalTimeUs - Global timeline position (for globalTimeUs in cache)\n */\n async preheatClipWindow(\n clipId: string,\n clipRelativeStart: TimeUs,\n clipRelativeEnd: TimeUs,\n globalTimeUs: TimeUs\n ): Promise<void> {\n if (!this.compositionModel) return;\n\n const clip = this.compositionModel.findClip(clipId);\n if (!clip || !hasResourceId(clip)) return;\n\n const resourceId = clip.resourceId;\n\n // Ensure resource is downloaded\n await this.resourceLoader.load(resourceId, {\n priority: 'normal',\n clipId: clip.id,\n trackId: clip.trackId,\n });\n\n // Create temporary on-demand session for this window\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: clipRelativeStart,\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 the entire window range for this clip\n await session.decodeWindow(clipRelativeStart, clipRelativeEnd);\n } catch (error) {\n console.warn(`[Orchestrator] Preheat clip ${clipId} window failed:`, error);\n // Non-critical, don't throw\n } finally {\n await session.dispose();\n }\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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"}
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 } 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 activeOnDemandSession: OnDemandVideoSession | 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 cancelActiveDecoding(): void {\n if (this.activeOnDemandSession) {\n void this.activeOnDemandSession.dispose();\n this.activeOnDemandSession = null;\n }\n }\n\n /**\n * Decode and render nearest keyframe for fast seek preview\n * Returns the keyframe timestamp if successfully decoded, null otherwise\n */\n async tryRenderKeyframe(timeUs: TimeUs): Promise<TimeUs | null> {\n if (!this.compositionModel) return null;\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip || !hasResourceId(clip)) return null;\n\n const relativeTimeUs = timeUs - clip.startUs;\n const resourceId = clip.resourceId;\n\n // Check if keyframe is already in L1 cache\n const keyframeSample = this.cacheManager.mp4IndexCache.getNearestKeyframe(\n resourceId,\n relativeTimeUs\n );\n\n if (!keyframeSample) return null;\n\n const cachedKeyframe = this.cacheManager.getFrame(keyframeSample.timestamp, clip.id);\n if (cachedKeyframe) {\n return keyframeSample.timestamp;\n }\n\n // Resource must be ready to decode keyframe\n const resource = this.compositionModel.getResource(resourceId);\n if (resource?.state !== 'ready') return null;\n\n // Create temporary session to decode keyframe\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: relativeTimeUs,\n globalTimeUs: timeUs,\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 const keyframeTimeUs = await session.decodeKeyframe(relativeTimeUs);\n return keyframeTimeUs;\n } catch (error) {\n console.warn('[Orchestrator] Fast keyframe decode failed:', error);\n return null;\n } finally {\n await session.dispose();\n }\n }\n\n async setCompositionModel(model: CompositionModel): Promise<void> {\n this.compositionModel = model;\n this.cacheManager.clear();\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.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 return null;\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 /**\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.load(resourceId, fetchOptions);\n return null;\n }\n\n // Normal mode: wait for download\n await this.resourceLoader.load(resourceId, fetchOptions);\n\n this.cancelActiveDecoding();\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 this.activeOnDemandSession = session;\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 if (session.isDisposed) {\n return null;\n }\n console.error('[Orchestrator] Error composing from resource:', error);\n return null;\n } finally {\n if (this.activeOnDemandSession === session) {\n this.activeOnDemandSession = null;\n }\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 // Main Profile Level 4.1 - better compression efficiency for social media\n codec: 'avc1.4D0029',\n width: defaultCanvasWidth,\n height: defaultCanvasHeight,\n bitrate: config.encode.video.bitrateKbps\n ? config.encode.video.bitrateKbps * 1000\n : this.calculateDefaultBitrate(defaultCanvasWidth, defaultCanvasHeight),\n framerate: targetFps,\n latencyMode: 'quality',\n bitrateMode: 'variable',\n hardwareAcceleration: 'no-preference',\n // Default 1 second keyframe interval for better social media compatibility\n keyFrameInterval: config.encode.video.keyIntervalS\n ? Math.round(config.encode.video.keyIntervalS * targetFps)\n : targetFps,\n ...config.encode.video,\n },\n };\n }\n\n /**\n * Calculate default video bitrate based on resolution\n * Optimized for social media platforms (YouTube, TikTok, WeChat, etc.)\n */\n private calculateDefaultBitrate(width: number, height: number): number {\n const pixels = width * height;\n\n // Bitrate recommendations for H.264 Main Profile VBR:\n // - 720p (921,600 px): 5 Mbps\n // - 1080p (2,073,600 px): 8 Mbps\n // - 4K (8,294,400 px): 25 Mbps\n if (pixels <= 921_600) {\n // 720p and below\n return 5_000_000;\n } else if (pixels <= 2_073_600) {\n // 1080p\n return 8_000_000;\n } else if (pixels <= 3_686_400) {\n // 1440p (2K)\n return 16_000_000;\n } else {\n // 4K and above\n return 25_000_000;\n }\n }\n\n async export(model: CompositionModel, options: ExportOptions): Promise<Blob> {\n return this.exportScheduler.execute(model, options);\n }\n\n /**\n * Preheat a specific clip's window range\n * Used by PlaybackController for cross-clip window preheating\n *\n * @param clipId - Clip identifier\n * @param clipRelativeStart - Start time relative to clip (microseconds)\n * @param clipRelativeEnd - End time relative to clip (microseconds)\n * @param globalTimeUs - Global timeline position (for globalTimeUs in cache)\n */\n async preheatClipWindow(\n clipId: string,\n clipRelativeStart: TimeUs,\n clipRelativeEnd: TimeUs,\n globalTimeUs: TimeUs\n ): Promise<void> {\n if (!this.compositionModel) return;\n\n const clip = this.compositionModel.findClip(clipId);\n if (!clip || !hasResourceId(clip)) return;\n\n const resourceId = clip.resourceId;\n\n // Ensure resource is downloaded\n await this.resourceLoader.load(resourceId, {\n priority: 'normal',\n clipId: clip.id,\n trackId: clip.trackId,\n });\n\n // Create temporary on-demand session for this window\n const session = await OnDemandVideoSession.create({\n clipId: clip.id,\n resourceId,\n targetTimeUs: clipRelativeStart,\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 the entire window range for this clip\n await session.decodeWindow(clipRelativeStart, clipRelativeEnd);\n } catch (error) {\n console.warn(`[Orchestrator] Preheat clip ${clipId} window failed:`, error);\n // Non-critical, don't throw\n } finally {\n await session.dispose();\n }\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":";;;;;;;;;;;;;AAkBO,MAAM,aAAsC;AAAA,EACjD;AAAA,EACA;AAAA,EACA,mBAA4C;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEQ,gBAAgB;AAAA,EAChB,SAAS,aAAa,YAAA,EAAc,UAAA;AAAA,EACpC,2BAA0C;AAAA,EAC1C,wBAAqD;AAAA,EACpD;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,UAAM,cAAc,OAAO,aAAa,UAAU,KAAK,OAAO,OAAO,IAAI,eAAe;AACxF,UAAM,UAAU,KAAK,OAAO,QAAQ,OAAO,WAAW;AAEtD,SAAK,eAAe,IAAI;AAAA,MACtB;AAAA,QACE,IAAI;AAAA,UACF;AAAA,UACA;AAAA,QAAA;AAAA,QAEF,UAAU;AAAA,UACR,WAAW,OAAO,aAAa,KAAK,OAAO,OAAO,aAAa;AAAA,QAAA;AAAA,MACjE;AAAA,MAEF,KAAK;AAAA,IAAA;AAGP,SAAK,iBAAiB,IAAI,eAAe;AAAA,MACvC,cAAc,KAAK;AAAA,MACnB,YAAY,KAAK;AAAA,MACjB,UAAU,KAAK;AAAA,MACf,QAAQ;AAAA,QACN,eAAe,KAAK,OAAO,KAAK;AAAA,QAChC,oBAAoB;AAAA;AAAA,MAAA;AAAA,MAEtB,eAAe,CAAC,YAAY,UAAU,KAAK,0BAA0B,YAAY,KAAK;AAAA,IAAA,CACvF;AAED,SAAK,UAAU,IAAI,mBAAA;AAEnB,SAAK,eAAe,IAAI,mBAAmB;AAAA,MACzC,cAAc,KAAK;AAAA,MACnB,YAAY,KAAK;AAAA,MACjB,gBAAgB,KAAK;AAAA,MACrB,UAAU,KAAK;AAAA,MACf,oBAAoB,MAAM,KAAK,mBAAA;AAAA,IAAmB,CACnD;AAED,SAAK,aAAa,IAAI;AAAA,MACpB,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,OAAO,OAAO;AAAA,IAAA;AAGrB,SAAK,kBAAkB,IAAI,gBAAgB;AAAA,MACzC,YAAY,KAAK;AAAA,MACjB,SAAS,KAAK;AAAA,MACd,cAAc,KAAK;AAAA,MACnB,gBAAgB,KAAK;AAAA,MACrB,YAAY,KAAK;AAAA,MACjB,cAAc,KAAK;AAAA,MACnB,uBAAuB,MAAM,KAAK,mBAAA;AAAA,MAClC,UAAU,KAAK;AAAA,IAAA,CAChB;AAED,SAAK,+BAAA;AACL,SAAK,qBAAA;AAAA,EACP;AAAA,EAEQ,iCAAuC;AAC7C,SAAK,SAAS,GAAG,aAAa,yBAAyB,OAAO,YAAY;AACxE,YAAM,EAAE,YAAY,QAAQ,OAAO,WAAW;AAE9C,UAAI,CAAC,KAAK,iBAAkB;AAG5B,YAAM,OAAO,KAAK,iBAAiB,SAAS,MAAM;AAClD,UAAI,CAAC,QAAQ,CAAC,KAAK,QAAS;AAI5B,UAAI,KAAK,YAAY,GAAG;AACtB,cAAM,MAAM,KAAK,iBAAiB,OAAO;AACzC,cAAM,qBAAqB;AAAA,UACzB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,KAAK;AAAA,UACL;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,uBAA6B;AAEnC,SAAK,SAAS,GAAG,aAAa,cAAc,MAAM;AAChD,WAAK,eAAe,qBAAqB,KAAK;AAAA,IAChD,CAAC;AAGD,SAAK,SAAS,GAAG,aAAa,eAAe,MAAM;AACjD,WAAK,eAAe,qBAAqB,IAAI;AAAA,IAC/C,CAAC;AAED,SAAK,SAAS,GAAG,aAAa,cAAc,MAAM;AAChD,WAAK,eAAe,qBAAqB,IAAI;AAAA,IAC/C,CAAC;AAAA,EAKH;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,uBAA6B;AAC3B,QAAI,KAAK,uBAAuB;AAC9B,WAAK,KAAK,sBAAsB,QAAA;AAChC,WAAK,wBAAwB;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kBAAkB,QAAwC;AAC9D,QAAI,CAAC,KAAK,iBAAkB,QAAO;AAEnC,UAAM,OAAO,KAAK,iBAAiB,eAAe,QAAQ,KAAK,iBAAiB,WAAW,EAAE,CAAC;AAC9F,QAAI,CAAC,QAAQ,CAAC,cAAc,IAAI,EAAG,QAAO;AAE1C,UAAM,iBAAiB,SAAS,KAAK;AACrC,UAAM,aAAa,KAAK;AAGxB,UAAM,iBAAiB,KAAK,aAAa,cAAc;AAAA,MACrD;AAAA,MACA;AAAA,IAAA;AAGF,QAAI,CAAC,eAAgB,QAAO;AAE5B,UAAM,iBAAiB,KAAK,aAAa,SAAS,eAAe,WAAW,KAAK,EAAE;AACnF,QAAI,gBAAgB;AAClB,aAAO,eAAe;AAAA,IACxB;AAGA,UAAM,WAAW,KAAK,iBAAiB,YAAY,UAAU;AAC7D,QAAI,UAAU,UAAU,QAAS,QAAO;AAGxC,UAAM,UAAU,MAAM,qBAAqB,OAAO;AAAA,MAChD,QAAQ,KAAK;AAAA,MACb;AAAA,MACA,cAAc;AAAA,MACd,cAAc;AAAA,MACd,gBAAgB,KAAK;AAAA,MACrB,eAAe,KAAK,aAAa;AAAA,MACjC,cAAc,KAAK;AAAA,MACnB,kBAAkB,KAAK;AAAA,MACvB,KAAK,KAAK,iBAAiB,OAAO;AAAA,IAAA,CACnC;AAED,QAAI;AACF,YAAM,iBAAiB,MAAM,QAAQ,eAAe,cAAc;AAClE,aAAO;AAAA,IACT,SAAS,OAAO;AACd,cAAQ,KAAK,+CAA+C,KAAK;AACjE,aAAO;AAAA,IACT,UAAA;AACE,YAAM,QAAQ,QAAA;AAAA,IAChB;AAAA,EACF;AAAA,EAEA,MAAM,oBAAoB,OAAwC;AAChE,SAAK,mBAAmB;AACxB,SAAK,aAAa,MAAA;AAClB,SAAK,QAAQ,SAAS,KAAK;AAE3B,UAAM,KAAK,eAAe,SAAS,KAAK;AACxC,SAAK,aAAa,SAAS,KAAK;AAEhC,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;AAAA,EACH;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,SAAK,QAAQ,WAAW,OAAO,eAAe;AAC9C,SAAK,SAAS,KAAK,aAAa,cAAc;AAAA,MAC5C,YAAY,MAAM,WAAW;AAAA,MAC7B,eAAe,MAAM,KAAK,eAAe;AAAA,IAAA,CAC1C;AAGD,eAAW,UAAU,iBAAiB;AACpC,WAAK,aAAa,eAAe,MAAM;AAAA,IACzC;AAKA,eAAW,UAAU,iBAAiB;AACpC,YAAM,OAAO,KAAK,iBAAiB,SAAS,MAAM;AAClD,UAAI,MAAM,cAAc,SAAS;AAC/B,cAAM,KAAK,aAAa,eAAe,MAAM;AAAA,MAE/C,WAAW,MAAM,cAAc,QAAS;AAAA,IAG1C;AAGA,UAAM,KAAK,aAAa,sBAAA;AAAA,EAI1B;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;AAAA,EAKF;AAAA,EAEA,MAAM,SAAS,QAAgB,SAAuD;AACpF,UAAM,SAAS,SAAS;AACxB,UAAM,UAAU,SAAS,WAAW;AAEpC,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,iBAAiB,SAAS,kBAAkB,SAAS,KAAK;AAG9D,qBAAiB,KAAK,IAAI,gBAAgB,KAAK,aAAa,CAAC;AAO7D,QAAI,CAAC,SAAS;AACZ,YAAM,cAAc,KAAK,aAAa,SAAS,gBAAgB,KAAK,EAAE;AACtE,UAAI,aAAa;AACf,aAAK,SAAS,KAAK,aAAa,UAAU;AAAA,UACxC;AAAA,UACA,OAAO;AAAA,UACP,KAAK,GAAG,KAAK,EAAE,IAAI,cAAc;AAAA,QAAA,CAClC;AACD,eAAO;AAAA,MACT;AAEA,WAAK,SAAS,KAAK,aAAa,WAAW;AAAA,QACzC;AAAA,QACA,OAAO;AAAA,QACP,KAAK,GAAG,KAAK,EAAE,IAAI,cAAc;AAAA,MAAA,CAClC;AAAA,IACH;AAEA,QAAI,QAAQ,SAAS;AACnB,aAAO;AAAA,IACT;AAGA,UAAM,gBAAgB,MAAM,KAAK,mBAAmB,MAAM,gBAAgB,QAAQ,OAAO;AACzF,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,mBACZ,MACA,gBACA,cACA,SACyB;AACzB,QAAI,CAAC,cAAc,IAAI,EAAG,QAAO;AAEjC,UAAM,aAAa,KAAK;AAGxB,UAAM,WAAW,KAAK,kBAAkB,YAAY,UAAU;AAC9D,UAAM,UAAU,UAAU,UAAU;AAEpC,UAAM,eAAe;AAAA,MACnB,UAAU;AAAA,MACV,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,IAAA;AAIhB,QAAI,SAAS,aAAa,CAAC,SAAS;AAElC,WAAK,eAAe,KAAK,YAAY,YAAY;AACjD,aAAO;AAAA,IACT;AAGA,UAAM,KAAK,eAAe,KAAK,YAAY,YAAY;AAEvD,SAAK,qBAAA;AAGL,UAAM,UAAU,MAAM,qBAAqB,OAAO;AAAA,MAChD,QAAQ,KAAK;AAAA,MACb;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA,gBAAgB,KAAK;AAAA,MACrB,eAAe,KAAK,aAAa;AAAA,MACjC,cAAc,KAAK;AAAA,MACnB,kBAAkB,KAAK;AAAA,MACvB,KAAK,KAAK,kBAAkB,OAAO;AAAA,IAAA,CACpC;AAED,SAAK,wBAAwB;AAE7B,QAAI;AAEF,YAAM,qBAAqB;AAE3B,YAAM,cAAc;AACpB,YAAM,YAAY,KAAK,IAAI,KAAK,YAAY,iBAAiB,kBAAkB;AAE/E,YAAM,QAAQ,aAAa,aAAa,SAAS;AAEjD,aAAO,KAAK,aAAa,SAAS,gBAAgB,KAAK,EAAE;AAAA,IAC3D,SAAS,OAAO;AACd,UAAI,QAAQ,YAAY;AACtB,eAAO;AAAA,MACT;AACA,cAAQ,MAAM,iDAAiD,KAAK;AACpE,aAAO;AAAA,IACT,UAAA;AACE,UAAI,KAAK,0BAA0B,SAAS;AAC1C,aAAK,wBAAwB;AAAA,MAC/B;AACA,YAAM,QAAQ,QAAA;AAAA,IAChB;AAAA,EACF;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;AAEA,WAAO,KAAK,aAAa,iBAAiB,YAAY,IAAI;AAAA,MACxD,eAAe,SAAS,iBAAiB;AAAA,MACzC,WAAW,SAAS,aAAa;AAAA,IAAA,CAClC;AAAA,EACH;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,aAAa,MAAA;AAExB,SAAK,QAAQ,aAAA;AACb,SAAK,mBAAmB;AACxB,SAAK,SAAS,QAAA;AAAA,EAChB;AAAA,EAEQ,qBAA8C;AACpD,UAAM,SAAS,KAAK;AACpB,UAAM,qBAAqB,OAAO,OAAO;AACzC,UAAM,sBAAsB,OAAO,OAAO;AAC1C,UAAM,aAAa,OAAO,OAAO;AAEjC,UAAM,YAAY,KAAK,kBAAkB,OAAO;AAEhD,WAAO;AAAA,MACL,YAAY;AAAA,QACV,eAAe,OAAO,MAAM,aAAa;AAAA,MAAA;AAAA,MAE3C,YAAY;AAAA,QACV,eAAe,OAAO,MAAM,aAAa;AAAA,MAAA;AAAA,MAE3C,aAAa,OAAO,OAAO;AAAA,MAC3B,aAAa,OAAO,OAAO;AAAA,MAC3B,cAAc;AAAA,QACZ,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,iBAAiB;AAAA,QACjB,iBAAiB;AAAA,QACjB,4BAA4B;AAAA,MAAA;AAAA,MAE9B,cAAc;AAAA,QACZ,eAAe,OAAO,QAAQ,MAAM;AAAA,MAAA;AAAA,MAEtC,aAAa;AAAA;AAAA,QAEX,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,SAAS,OAAO,OAAO,MAAM,cACzB,OAAO,OAAO,MAAM,cAAc,MAClC,KAAK,wBAAwB,oBAAoB,mBAAmB;AAAA,QACxE,WAAW;AAAA,QACX,aAAa;AAAA,QACb,aAAa;AAAA,QACb,sBAAsB;AAAA;AAAA,QAEtB,kBAAkB,OAAO,OAAO,MAAM,eAClC,KAAK,MAAM,OAAO,OAAO,MAAM,eAAe,SAAS,IACvD;AAAA,QACJ,GAAG,OAAO,OAAO;AAAA,MAAA;AAAA,IACnB;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,wBAAwB,OAAe,QAAwB;AACrE,UAAM,SAAS,QAAQ;AAMvB,QAAI,UAAU,QAAS;AAErB,aAAO;AAAA,IACT,WAAW,UAAU,SAAW;AAE9B,aAAO;AAAA,IACT,WAAW,UAAU,SAAW;AAE9B,aAAO;AAAA,IACT,OAAO;AAEL,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,OAAyB,SAAuC;AAC3E,WAAO,KAAK,gBAAgB,QAAQ,OAAO,OAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,kBACJ,QACA,mBACA,iBACA,cACe;AACf,QAAI,CAAC,KAAK,iBAAkB;AAE5B,UAAM,OAAO,KAAK,iBAAiB,SAAS,MAAM;AAClD,QAAI,CAAC,QAAQ,CAAC,cAAc,IAAI,EAAG;AAEnC,UAAM,aAAa,KAAK;AAGxB,UAAM,KAAK,eAAe,KAAK,YAAY;AAAA,MACzC,UAAU;AAAA,MACV,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,IAAA,CACf;AAGD,UAAM,UAAU,MAAM,qBAAqB,OAAO;AAAA,MAChD,QAAQ,KAAK;AAAA,MACb;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA,gBAAgB,KAAK;AAAA,MACrB,eAAe,KAAK,aAAa;AAAA,MACjC,cAAc,KAAK;AAAA,MACnB,kBAAkB,KAAK;AAAA,MACvB,KAAK,KAAK,iBAAiB,OAAO;AAAA,IAAA,CACnC;AAED,QAAI;AAEF,YAAM,QAAQ,aAAa,mBAAmB,eAAe;AAAA,IAC/D,SAAS,OAAO;AACd,cAAQ,KAAK,+BAA+B,MAAM,mBAAmB,KAAK;AAAA,IAE5E,UAAA;AACE,YAAM,QAAQ,QAAA;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eACJ,QACA,SACqD;AACrD,QAAI,CAAC,KAAK,kBAAkB;AAC1B,aAAO;AAAA,IACT;AAGA,UAAM,QAAQ,MAAM,KAAK,SAAS,QAAQ,OAAO;AAGjD,QAAI,SAAS,aAAa,CAAC,OAAO;AAChC,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,KAAK,iBAAiB,eAAe,QAAQ,KAAK,iBAAiB,WAAW,EAAE,CAAC;AAC9F,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,IACT;AAEA,UAAM,iBAAiB,SAAS,KAAK;AAGrC,UAAM,eAAe,KAAK,QAAQ,gBAAgB,KAAK,EAAE;AACzD,QAAI,CAAC,cAAc;AACjB,aAAO;AAAA,IACT;AAGA,UAAM,SAAgB,CAAA;AAGtB,UAAM,eAAe,aAAa,OAAO,OAAO,CAAC,UAAe;AAC9D,UAAI,CAAC,MAAM,QAAQ,cAAc;AAE/B,eAAO;AAAA,MACT;AACA,UAAI,MAAM,WAAW,SAAS;AAC5B,eAAO;AAAA,MACT;AAEA,aAAO,MAAM,aAAa;AAAA,QACxB,CAAC,UAAe,kBAAkB,MAAM,WAAW,iBAAiB,MAAM;AAAA,MAAA;AAAA,IAE9E,CAAC;AAGD,eAAW,aAAa,cAAc;AACpC,YAAM,QAAQ,MAAM,KAAK,iBAAiB,WAAW,MAAM,gBAAgB,MAAM;AACjF,UAAI,OAAO;AACT,eAAO,KAAK,KAAK;AAAA,MACnB;AAAA,IACF;AAEA,WAAO,EAAE,OAAA;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBACZ,WACA,MACA,oBACA,cACqB;AACrB,UAAM,YAAiB;AAAA,MACrB,IAAI,UAAU;AAAA,MACd,MAAM,UAAU;AAAA,MAChB,QAAQ,UAAU,UAAU;AAAA,MAC5B,SAAS;AAAA,MACT,SAAS,UAAU,WAAW;AAAA,IAAA;AAIhC,QAAI,UAAU,SAAS,WAAW,CAAC,UAAU,QAAQ,cAAc;AACjE,YAAM,UAAU,KAAK,aAAa,SAAS,oBAAoB,KAAK,EAAE;AACtE,UAAI,CAAC,SAAS;AACZ,gBAAQ,KAAK,+CAA+C,KAAK,IAAI,kBAAkB;AACvF,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM;AAAA,QACN;AAAA,MAAA;AAAA,IAEJ;AAGA,QAAI,UAAU,SAAS,QAAQ;AAC7B,YAAM,UAAU,UAAU;AAC1B,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM;AAAA,QACN,MAAM,QAAQ;AAAA,QACd,YAAY,QAAQ;AAAA,QACpB,YAAY,QAAQ;AAAA,QACpB,WAAW,QAAQ;AAAA,QACnB,aAAa,QAAQ;AAAA,QACrB,YAAY,QAAQ;AAAA,MAAA;AAAA,IAExB;AAGA,QAAI,UAAU,SAAS,SAAS;AAC9B,YAAM,UAAU,UAAU;AAC1B,YAAM,WAAW,KAAK,kBAAkB,YAAY,QAAQ,UAAU;AACtE,UAAI,CAAC,UAAU;AACb,eAAO;AAAA,MACT;AAEA,YAAM,SAAS,MAAM,KAAK,eAAe,UAAU,QAAQ;AAC3D,YAAM,aAAkB;AAAA,QACtB,GAAG;AAAA,QACH,MAAM;AAAA,QACN;AAAA,QACA,cAAc,QAAQ;AAAA,MAAA;AAGxB,UAAI,QAAQ,gBAAgB,QAAW;AACrC,mBAAW,cAAc,QAAQ;AAAA,MACnC;AACA,UAAI,QAAQ,iBAAiB,QAAW;AACtC,mBAAW,eAAe,QAAQ;AAAA,MACpC;AAGA,UAAI,QAAQ,WAAW;AACrB,cAAM,EAAE,UAAU,WAAW,mBAAA,IAAuB,QAAQ;AAG5D,cAAM,iBAAiB,eAAe;AAGtC,YAAI,iBAAiB,KAAK,iBAAiB,UAAU,UAAU,SAAS,CAAC,EAAE,MAAM;AAC/E,iBAAO;AAAA,QACT;AAEA,cAAM,cAAc;AAEpB,mBAAW,YAAY;AAAA,UACrB,GAAG,SAAS;AAAA,UACZ,GAAG,SAAS;AAAA,UACZ,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,SAAS;AAAA,UACT,SAAS;AAAA,QAAA;AAAA,MAEb;AAEA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AACF;"}
@@ -1 +1 @@
1
- {"version":3,"file":"BaseDecoder.d.ts","sourceRoot":"","sources":["../../../src/stages/decode/BaseDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEpF,8BAAsB,WAAW,CAC/B,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,kBAAkB,GAAG,kBAAkB,EACvD,MAAM,SAAS,iBAAiB,GAAG,iBAAiB,EACpD,OAAO,SAAS,UAAU,GAAG,SAAS,GAAG,iBAAiB;IAE1D,SAAS,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,UAAU,EAAE,gCAAgC,CAAC,OAAO,CAAC,GAAG,IAAI,CAAQ;gBAElE,MAAM,EAAE,OAAO;IAIrB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAyB3C,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAKhD,OAAO,CAAC,eAAe;IAMvB,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ;IAC7D,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IACnE,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,MAAM;IAC3C,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAC9C,SAAS,CAAC,OAAO,IAAI,IAAI;IAGzB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEzD,YAAY,IAAI,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC;CAyCjD;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACtC"}
1
+ {"version":3,"file":"BaseDecoder.d.ts","sourceRoot":"","sources":["../../../src/stages/decode/BaseDecoder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEpF,8BAAsB,WAAW,CAC/B,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,kBAAkB,GAAG,kBAAkB,EACvD,MAAM,SAAS,iBAAiB,GAAG,iBAAiB,EACpD,OAAO,SAAS,UAAU,GAAG,SAAS,GAAG,iBAAiB;IAE1D,SAAS,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,UAAU,EAAE,gCAAgC,CAAC,OAAO,CAAC,GAAG,IAAI,CAAQ;gBAElE,MAAM,EAAE,OAAO;IAIrB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgD3B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAW5B,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAyB3C,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAWhD,OAAO,CAAC,eAAe;IAMvB,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ;IAC7D,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IACnE,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,MAAM;IAC3C,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAC9C,SAAS,CAAC,OAAO,IAAI,IAAI;IAGzB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEzD,YAAY,IAAI,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC;CAyCjD;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACtC"}
@@ -9,7 +9,24 @@ class BaseDecoder {
9
9
  if (this.decoder?.state === "configured") {
10
10
  return;
11
11
  }
12
+ const configInfo = {
13
+ codec: this.config.codec,
14
+ width: this.config.width,
15
+ height: this.config.height,
16
+ sampleRate: this.config.sampleRate,
17
+ numberOfChannels: this.config.numberOfChannels,
18
+ hardwareAcceleration: this.config.hardwareAcceleration
19
+ };
20
+ console.log(`[${this.getDecoderType()}Decoder] Checking config support:`, {
21
+ platform: typeof navigator !== "undefined" ? navigator.platform : "unknown",
22
+ userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "unknown",
23
+ config: configInfo
24
+ });
12
25
  const isSupported = await this.isConfigSupported(this.config);
26
+ console.log(`[${this.getDecoderType()}Decoder] Config support result:`, {
27
+ supported: isSupported.supported,
28
+ config: configInfo
29
+ });
13
30
  if (!isSupported.supported) {
14
31
  throw new Error(
15
32
  `Codec not supported: ${this.config.codecString || this.config.codec}`
@@ -19,7 +36,12 @@ class BaseDecoder {
19
36
  output: this.handleOutput.bind(this),
20
37
  error: this.handleError.bind(this)
21
38
  });
39
+ console.log(`[${this.getDecoderType()}Decoder] Decoder created, configuring...`);
22
40
  await this.configureDecoder(this.config);
41
+ console.log(
42
+ `[${this.getDecoderType()}Decoder] Configured successfully, state:`,
43
+ this.decoder?.state
44
+ );
23
45
  }
24
46
  async reconfigure(config) {
25
47
  this.config = { ...this.config, ...config };
@@ -44,6 +66,12 @@ class BaseDecoder {
44
66
  }
45
67
  async reset() {
46
68
  if (!this.decoder) return;
69
+ if (this.decoder.state === "configured") {
70
+ try {
71
+ await this.decoder.flush();
72
+ } catch {
73
+ }
74
+ }
47
75
  this.decoder.reset();
48
76
  this.onReset();
49
77
  }
@@ -79,7 +107,13 @@ class BaseDecoder {
79
107
  }
80
108
  }
81
109
  handleError(error) {
82
- console.error(`${this.getDecoderType()} decoder error:`, error);
110
+ console.error(`[${this.getDecoderType()}Decoder] Decode error:`, {
111
+ name: error.name,
112
+ message: error.message,
113
+ decoderState: this.decoder?.state,
114
+ queueSize: this.queueSize,
115
+ platform: typeof navigator !== "undefined" ? navigator.platform : "unknown"
116
+ });
83
117
  this.controller?.error(error);
84
118
  }
85
119
  closeIfPossible(data) {
@@ -1 +1 @@
1
- {"version":3,"file":"BaseDecoder.js","sources":["../../../src/stages/decode/BaseDecoder.ts"],"sourcesContent":["/**\n * Base decoder class abstracting common WebCodecs decoder workflow.\n * Mirrors BaseEncoder but for decoding path.\n */\nimport { AudioDecoderConfig, DecodedVideoFrame, VideoDecoderConfig } from './types';\n\nexport abstract class BaseDecoder<\n TDecoder extends VideoDecoder | AudioDecoder,\n TConfig extends VideoDecoderConfig | AudioDecoderConfig,\n TInput extends EncodedVideoChunk | EncodedAudioChunk,\n TOutput extends VideoFrame | AudioData | DecodedVideoFrame,\n> {\n protected decoder?: TDecoder;\n protected config: TConfig;\n protected controller: TransformStreamDefaultController<TOutput> | null = null;\n\n constructor(config: TConfig) {\n this.config = config;\n }\n\n async initialize(): Promise<void> {\n if (this.decoder?.state === 'configured') {\n return;\n }\n const isSupported = await this.isConfigSupported(this.config);\n if (!isSupported.supported) {\n throw new Error(\n `Codec not supported: ${(this.config as any).codecString || (this.config as any).codec}`\n );\n }\n\n this.decoder = this.createDecoder({\n output: this.handleOutput.bind(this),\n error: this.handleError.bind(this),\n });\n\n await this.configureDecoder(this.config);\n }\n\n async reconfigure(config: Partial<TConfig>): Promise<void> {\n this.config = { ...this.config, ...config } as TConfig;\n\n if (!this.decoder) {\n await this.initialize();\n return;\n }\n\n // Flush before reconfiguring\n if (this.decoder.state === 'configured') {\n await this.decoder.flush();\n }\n\n const isSupported = await this.isConfigSupported(this.config);\n if (!isSupported.supported) {\n throw new Error(\n `New configuration not supported: ${(this.config as any).codecString || (this.config as any).codec}`\n );\n }\n\n await this.configureDecoder(this.config);\n }\n\n async flush(): Promise<void> {\n if (!this.decoder) return;\n await this.decoder.flush();\n }\n\n async reset(): Promise<void> {\n if (!this.decoder) return;\n this.decoder.reset();\n this.onReset();\n }\n\n async close(): Promise<void> {\n if (!this.decoder) return;\n\n if (this.decoder.state === 'configured') {\n await this.decoder.flush();\n }\n\n this.decoder.close();\n this.decoder = undefined;\n }\n\n get isReady(): boolean {\n return this.decoder?.state === 'configured';\n }\n\n get queueSize(): number {\n return (this.decoder as any)?.decodeQueueSize ?? 0;\n }\n\n protected handleOutput(data: TOutput): void {\n if (!this.controller) {\n this.closeIfPossible(data);\n return;\n }\n\n try {\n this.controller.enqueue(data);\n if (data instanceof VideoFrame && (this.config as VideoDecoderConfig).thread !== 'main') {\n // ugly hack to avoid memory leak\n // setTimeout(() => {\n // data.close();\n // }, 500);\n }\n } catch (error) {\n // Stream closed or errored - silently drop frame\n if (error instanceof TypeError) {\n this.closeIfPossible(data);\n return;\n }\n\n throw error;\n }\n }\n\n protected handleError(error: DOMException): void {\n console.error(`${this.getDecoderType()} decoder error:`, error);\n this.controller?.error(error);\n }\n\n private closeIfPossible(data: TOutput): void {\n (data as any)?.close?.();\n (data as DecodedVideoFrame)?.frame?.close?.();\n }\n\n // Abstracts\n protected abstract isConfigSupported(config: TConfig): Promise<{ supported: boolean }>;\n protected abstract createDecoder(init: DecoderInit): TDecoder;\n protected abstract configureDecoder(config: TConfig): Promise<void>;\n protected abstract getDecoderType(): string;\n protected abstract decode(input: TInput): void;\n protected onReset(): void {}\n\n // Backpressure\n protected abstract readonly highWaterMark: number;\n protected abstract readonly decodeQueueThreshold: number;\n\n createStream(): TransformStream<TInput, TOutput> {\n return new TransformStream<TInput, TOutput>(\n {\n start: async (controller) => {\n this.controller = controller;\n if (!this.isReady) {\n await this.initialize();\n }\n },\n transform: async (input) => {\n if (!this.decoder || this.decoder.state !== 'configured') {\n throw new Error('Decoder not configured');\n }\n // backpressure\n if ((this.decoder as any).decodeQueueSize >= this.decodeQueueThreshold) {\n await new Promise<void>((resolve) => {\n const check = () => {\n if (\n !this.decoder ||\n (this.decoder as any).decodeQueueSize < this.decodeQueueThreshold - 1\n ) {\n resolve();\n } else {\n setTimeout(check, 10);\n }\n };\n check();\n });\n }\n this.decode(input);\n },\n flush: async () => {\n await this.flush();\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: () => 1,\n }\n );\n }\n}\n\ninterface DecoderInit {\n output: (data: any) => void;\n error: (error: DOMException) => void;\n}\n"],"names":[],"mappings":"AAMO,MAAe,YAKpB;AAAA,EACU;AAAA,EACA;AAAA,EACA,aAA+D;AAAA,EAEzE,YAAY,QAAiB;AAC3B,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,aAA4B;AAChC,QAAI,KAAK,SAAS,UAAU,cAAc;AACxC;AAAA,IACF;AACA,UAAM,cAAc,MAAM,KAAK,kBAAkB,KAAK,MAAM;AAC5D,QAAI,CAAC,YAAY,WAAW;AAC1B,YAAM,IAAI;AAAA,QACR,wBAAyB,KAAK,OAAe,eAAgB,KAAK,OAAe,KAAK;AAAA,MAAA;AAAA,IAE1F;AAEA,SAAK,UAAU,KAAK,cAAc;AAAA,MAChC,QAAQ,KAAK,aAAa,KAAK,IAAI;AAAA,MACnC,OAAO,KAAK,YAAY,KAAK,IAAI;AAAA,IAAA,CAClC;AAED,UAAM,KAAK,iBAAiB,KAAK,MAAM;AAAA,EACzC;AAAA,EAEA,MAAM,YAAY,QAAyC;AACzD,SAAK,SAAS,EAAE,GAAG,KAAK,QAAQ,GAAG,OAAA;AAEnC,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,KAAK,WAAA;AACX;AAAA,IACF;AAGA,QAAI,KAAK,QAAQ,UAAU,cAAc;AACvC,YAAM,KAAK,QAAQ,MAAA;AAAA,IACrB;AAEA,UAAM,cAAc,MAAM,KAAK,kBAAkB,KAAK,MAAM;AAC5D,QAAI,CAAC,YAAY,WAAW;AAC1B,YAAM,IAAI;AAAA,QACR,oCAAqC,KAAK,OAAe,eAAgB,KAAK,OAAe,KAAK;AAAA,MAAA;AAAA,IAEtG;AAEA,UAAM,KAAK,iBAAiB,KAAK,MAAM;AAAA,EACzC;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,QAAS;AACnB,UAAM,KAAK,QAAQ,MAAA;AAAA,EACrB;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,QAAS;AACnB,SAAK,QAAQ,MAAA;AACb,SAAK,QAAA;AAAA,EACP;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,QAAS;AAEnB,QAAI,KAAK,QAAQ,UAAU,cAAc;AACvC,YAAM,KAAK,QAAQ,MAAA;AAAA,IACrB;AAEA,SAAK,QAAQ,MAAA;AACb,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,IAAI,UAAmB;AACrB,WAAO,KAAK,SAAS,UAAU;AAAA,EACjC;AAAA,EAEA,IAAI,YAAoB;AACtB,WAAQ,KAAK,SAAiB,mBAAmB;AAAA,EACnD;AAAA,EAEU,aAAa,MAAqB;AAC1C,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,gBAAgB,IAAI;AACzB;AAAA,IACF;AAEA,QAAI;AACF,WAAK,WAAW,QAAQ,IAAI;AAC5B,UAAI,gBAAgB,cAAe,KAAK,OAA8B,WAAW,QAAQ;AAAA,MAKzF;AAAA,IACF,SAAS,OAAO;AAEd,UAAI,iBAAiB,WAAW;AAC9B,aAAK,gBAAgB,IAAI;AACzB;AAAA,MACF;AAEA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEU,YAAY,OAA2B;AAC/C,YAAQ,MAAM,GAAG,KAAK,gBAAgB,mBAAmB,KAAK;AAC9D,SAAK,YAAY,MAAM,KAAK;AAAA,EAC9B;AAAA,EAEQ,gBAAgB,MAAqB;AAC1C,UAAc,QAAA;AACd,UAA4B,OAAO,QAAA;AAAA,EACtC;AAAA,EAQU,UAAgB;AAAA,EAAC;AAAA,EAM3B,eAAiD;AAC/C,WAAO,IAAI;AAAA,MACT;AAAA,QACE,OAAO,OAAO,eAAe;AAC3B,eAAK,aAAa;AAClB,cAAI,CAAC,KAAK,SAAS;AACjB,kBAAM,KAAK,WAAA;AAAA,UACb;AAAA,QACF;AAAA,QACA,WAAW,OAAO,UAAU;AAC1B,cAAI,CAAC,KAAK,WAAW,KAAK,QAAQ,UAAU,cAAc;AACxD,kBAAM,IAAI,MAAM,wBAAwB;AAAA,UAC1C;AAEA,cAAK,KAAK,QAAgB,mBAAmB,KAAK,sBAAsB;AACtE,kBAAM,IAAI,QAAc,CAAC,YAAY;AACnC,oBAAM,QAAQ,MAAM;AAClB,oBACE,CAAC,KAAK,WACL,KAAK,QAAgB,kBAAkB,KAAK,uBAAuB,GACpE;AACA,0BAAA;AAAA,gBACF,OAAO;AACL,6BAAW,OAAO,EAAE;AAAA,gBACtB;AAAA,cACF;AACA,oBAAA;AAAA,YACF,CAAC;AAAA,UACH;AACA,eAAK,OAAO,KAAK;AAAA,QACnB;AAAA,QACA,OAAO,YAAY;AACjB,gBAAM,KAAK,MAAA;AAAA,QACb;AAAA,MAAA;AAAA,MAEF;AAAA,QACE,eAAe,KAAK;AAAA,QACpB,MAAM,MAAM;AAAA,MAAA;AAAA,IACd;AAAA,EAEJ;AACF;"}
1
+ {"version":3,"file":"BaseDecoder.js","sources":["../../../src/stages/decode/BaseDecoder.ts"],"sourcesContent":["/**\n * Base decoder class abstracting common WebCodecs decoder workflow.\n * Mirrors BaseEncoder but for decoding path.\n */\nimport { AudioDecoderConfig, DecodedVideoFrame, VideoDecoderConfig } from './types';\n\nexport abstract class BaseDecoder<\n TDecoder extends VideoDecoder | AudioDecoder,\n TConfig extends VideoDecoderConfig | AudioDecoderConfig,\n TInput extends EncodedVideoChunk | EncodedAudioChunk,\n TOutput extends VideoFrame | AudioData | DecodedVideoFrame,\n> {\n protected decoder?: TDecoder;\n protected config: TConfig;\n protected controller: TransformStreamDefaultController<TOutput> | null = null;\n\n constructor(config: TConfig) {\n this.config = config;\n }\n\n async initialize(): Promise<void> {\n if (this.decoder?.state === 'configured') {\n return;\n }\n\n const configInfo = {\n codec: (this.config as any).codec,\n width: (this.config as any).width,\n height: (this.config as any).height,\n sampleRate: (this.config as any).sampleRate,\n numberOfChannels: (this.config as any).numberOfChannels,\n hardwareAcceleration: (this.config as any).hardwareAcceleration,\n };\n\n console.log(`[${this.getDecoderType()}Decoder] Checking config support:`, {\n platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown',\n config: configInfo,\n });\n\n const isSupported = await this.isConfigSupported(this.config);\n\n console.log(`[${this.getDecoderType()}Decoder] Config support result:`, {\n supported: isSupported.supported,\n config: configInfo,\n });\n\n if (!isSupported.supported) {\n throw new Error(\n `Codec not supported: ${(this.config as any).codecString || (this.config as any).codec}`\n );\n }\n\n this.decoder = this.createDecoder({\n output: this.handleOutput.bind(this),\n error: this.handleError.bind(this),\n });\n\n console.log(`[${this.getDecoderType()}Decoder] Decoder created, configuring...`);\n\n await this.configureDecoder(this.config);\n\n console.log(\n `[${this.getDecoderType()}Decoder] Configured successfully, state:`,\n this.decoder?.state\n );\n }\n\n async reconfigure(config: Partial<TConfig>): Promise<void> {\n this.config = { ...this.config, ...config } as TConfig;\n\n if (!this.decoder) {\n await this.initialize();\n return;\n }\n\n // Flush before reconfiguring\n if (this.decoder.state === 'configured') {\n await this.decoder.flush();\n }\n\n const isSupported = await this.isConfigSupported(this.config);\n if (!isSupported.supported) {\n throw new Error(\n `New configuration not supported: ${(this.config as any).codecString || (this.config as any).codec}`\n );\n }\n\n await this.configureDecoder(this.config);\n }\n\n async flush(): Promise<void> {\n if (!this.decoder) return;\n await this.decoder.flush();\n }\n\n async reset(): Promise<void> {\n if (!this.decoder) return;\n\n // Windows Media Foundation requires flush before reset to avoid decoder error state\n if (this.decoder.state === 'configured') {\n try {\n await this.decoder.flush();\n } catch {\n // Ignore flush errors during reset\n }\n }\n\n this.decoder.reset();\n this.onReset();\n }\n\n async close(): Promise<void> {\n if (!this.decoder) return;\n\n if (this.decoder.state === 'configured') {\n await this.decoder.flush();\n }\n\n this.decoder.close();\n this.decoder = undefined;\n }\n\n get isReady(): boolean {\n return this.decoder?.state === 'configured';\n }\n\n get queueSize(): number {\n return (this.decoder as any)?.decodeQueueSize ?? 0;\n }\n\n protected handleOutput(data: TOutput): void {\n if (!this.controller) {\n this.closeIfPossible(data);\n return;\n }\n\n try {\n this.controller.enqueue(data);\n if (data instanceof VideoFrame && (this.config as VideoDecoderConfig).thread !== 'main') {\n // ugly hack to avoid memory leak\n // setTimeout(() => {\n // data.close();\n // }, 500);\n }\n } catch (error) {\n // Stream closed or errored - silently drop frame\n if (error instanceof TypeError) {\n this.closeIfPossible(data);\n return;\n }\n\n throw error;\n }\n }\n\n protected handleError(error: DOMException): void {\n console.error(`[${this.getDecoderType()}Decoder] Decode error:`, {\n name: error.name,\n message: error.message,\n decoderState: this.decoder?.state,\n queueSize: this.queueSize,\n platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',\n });\n this.controller?.error(error);\n }\n\n private closeIfPossible(data: TOutput): void {\n (data as any)?.close?.();\n (data as DecodedVideoFrame)?.frame?.close?.();\n }\n\n // Abstracts\n protected abstract isConfigSupported(config: TConfig): Promise<{ supported: boolean }>;\n protected abstract createDecoder(init: DecoderInit): TDecoder;\n protected abstract configureDecoder(config: TConfig): Promise<void>;\n protected abstract getDecoderType(): string;\n protected abstract decode(input: TInput): void;\n protected onReset(): void {}\n\n // Backpressure\n protected abstract readonly highWaterMark: number;\n protected abstract readonly decodeQueueThreshold: number;\n\n createStream(): TransformStream<TInput, TOutput> {\n return new TransformStream<TInput, TOutput>(\n {\n start: async (controller) => {\n this.controller = controller;\n if (!this.isReady) {\n await this.initialize();\n }\n },\n transform: async (input) => {\n if (!this.decoder || this.decoder.state !== 'configured') {\n throw new Error('Decoder not configured');\n }\n // backpressure\n if ((this.decoder as any).decodeQueueSize >= this.decodeQueueThreshold) {\n await new Promise<void>((resolve) => {\n const check = () => {\n if (\n !this.decoder ||\n (this.decoder as any).decodeQueueSize < this.decodeQueueThreshold - 1\n ) {\n resolve();\n } else {\n setTimeout(check, 10);\n }\n };\n check();\n });\n }\n this.decode(input);\n },\n flush: async () => {\n await this.flush();\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: () => 1,\n }\n );\n }\n}\n\ninterface DecoderInit {\n output: (data: any) => void;\n error: (error: DOMException) => void;\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"BaseEncoder.d.ts","sourceRoot":"","sources":["../../../src/stages/encode/BaseEncoder.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,yBAAyB,CAAC;CACrC;AAED,8BAAsB,WAAW,CAC/B,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,kBAAkB,GAAG,kBAAkB,EACvD,MAAM,SAAS,UAAU,GAAG,SAAS,EACrC,MAAM,SAAS,iBAAiB,GAAG,iBAAiB,EACpD,SAAS,SAAS,yBAAyB,GAAG,yBAAyB;IAEvE,SAAS,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,UAAU,EAAE,gCAAgC,CAAC,YAAY,CAAC,GAAG,IAAI,CAAQ;gBAEvE,MAAM,EAAE,OAAO;IAI3B,SAAS,IAAI,OAAO;IAIpB,SAAS,KAAK,aAAa,IAAI,OAAO,CAErC;IAED,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO;IAW/D,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAiBlD,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAIjD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB3B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,IAAI;IAchE,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAMhD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ;IAC7D,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,MAAM;IAG3C,SAAS,CAAC,OAAO,IAAI,IAAI;IAKzB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEzD;;;OAGG;IACH,YAAY,IAAI,eAAe,CAAC,MAAM,EAAE,YAAY,CAAC;IAkDrD,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CACrC;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,CAAC;IAC5C,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACtC"}
1
+ {"version":3,"file":"BaseEncoder.d.ts","sourceRoot":"","sources":["../../../src/stages/encode/BaseEncoder.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,yBAAyB,CAAC;CACrC;AAED,8BAAsB,WAAW,CAC/B,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,kBAAkB,GAAG,kBAAkB,EACvD,MAAM,SAAS,UAAU,GAAG,SAAS,EACrC,MAAM,SAAS,iBAAiB,GAAG,iBAAiB,EACpD,SAAS,SAAS,yBAAyB,GAAG,yBAAyB;IAEvE,SAAS,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,UAAU,EAAE,gCAAgC,CAAC,YAAY,CAAC,GAAG,IAAI,CAAQ;gBAEvE,MAAM,EAAE,OAAO;IAI3B,SAAS,IAAI,OAAO;IAIpB,SAAS,KAAK,aAAa,IAAI,OAAO,CAErC;IAED,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO;IAW/D,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAiBlD,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAIjD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAgD3B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,IAAI;IAchE,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAYhD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ;IAC7D,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,MAAM;IAG3C,SAAS,CAAC,OAAO,IAAI,IAAI;IAKzB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEzD;;;OAGG;IACH,YAAY,IAAI,eAAe,CAAC,MAAM,EAAE,YAAY,CAAC;IAkDrD,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CACrC;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,CAAC;IAC5C,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACtC"}
@@ -42,7 +42,26 @@ class BaseEncoder {
42
42
  if (this.encoder?.state === "configured") {
43
43
  return;
44
44
  }
45
+ const configInfo = {
46
+ codec: this.config.codec,
47
+ width: this.config.width,
48
+ height: this.config.height,
49
+ bitrate: this.config.bitrate,
50
+ framerate: this.config.framerate,
51
+ sampleRate: this.config.sampleRate,
52
+ numberOfChannels: this.config.numberOfChannels,
53
+ hardwareAcceleration: this.config.hardwareAcceleration
54
+ };
55
+ console.log(`[${this.getEncoderType()}Encoder] Checking config support:`, {
56
+ platform: typeof navigator !== "undefined" ? navigator.platform : "unknown",
57
+ userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "unknown",
58
+ config: configInfo
59
+ });
45
60
  const isSupported = await this.isConfigSupported(this.config);
61
+ console.log(`[${this.getEncoderType()}Encoder] Config support result:`, {
62
+ supported: isSupported.supported,
63
+ config: configInfo
64
+ });
46
65
  if (!isSupported.supported) {
47
66
  throw new Error(`Codec not supported: ${JSON.stringify(this.config)}`);
48
67
  }
@@ -50,7 +69,12 @@ class BaseEncoder {
50
69
  output: this.handleOutput.bind(this),
51
70
  error: this.handleError.bind(this)
52
71
  });
72
+ console.log(`[${this.getEncoderType()}Encoder] Encoder created, configuring...`);
53
73
  this.encoder.configure(this.config);
74
+ console.log(
75
+ `[${this.getEncoderType()}Encoder] Configured successfully, state:`,
76
+ this.encoder?.state
77
+ );
54
78
  }
55
79
  async reconfigure(config) {
56
80
  if (!config || Object.keys(config).length === 0) {
@@ -116,7 +140,13 @@ class BaseEncoder {
116
140
  }
117
141
  }
118
142
  handleError(error) {
119
- console.error(`${this.getEncoderType()} encoder error:`, error);
143
+ console.error(`[${this.getEncoderType()}Encoder] Encode error:`, {
144
+ name: error.name,
145
+ message: error.message,
146
+ encoderState: this.encoder?.state,
147
+ queueSize: this.queueSize,
148
+ platform: typeof navigator !== "undefined" ? navigator.platform : "unknown"
149
+ });
120
150
  this.controller?.error(error);
121
151
  }
122
152
  // Hook for subclasses to handle reset
@@ -1 +1 @@
1
- {"version":3,"file":"BaseEncoder.js","sources":["../../../src/stages/encode/BaseEncoder.ts"],"sourcesContent":["// Base encoder implementation\n\n/**\n * Base encoder class for both video and audio encoding\n * Handles common WebCodecs encoder operations\n */\n\nexport interface EncoderChunk {\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n}\n\nexport abstract class BaseEncoder<\n TEncoder extends VideoEncoder | AudioEncoder,\n TConfig extends VideoEncoderConfig | AudioEncoderConfig,\n TInput extends VideoFrame | AudioData,\n TChunk extends EncodedVideoChunk | EncodedAudioChunk,\n TMetadata extends EncodedVideoChunkMetadata | EncodedAudioChunkMetadata,\n> {\n protected encoder?: TEncoder;\n protected config: TConfig;\n protected controller: TransformStreamDefaultController<EncoderChunk> | null = null;\n\n constructor(config: TConfig) {\n this.config = config;\n }\n\n getConfig(): TConfig {\n return { ...this.config };\n }\n\n protected get currentConfig(): TConfig {\n return this.config;\n }\n\n protected shouldReconfigure(partial: Partial<TConfig>): boolean {\n const next = { ...this.config, ...partial } as TConfig;\n const keys = Object.keys(partial ?? {}) as Array<keyof TConfig>;\n for (const key of keys) {\n if (partial[key] !== undefined && next[key] !== this.config[key]) {\n return true;\n }\n }\n return false;\n }\n\n protected hasConfigChanged(next: TConfig): boolean {\n const currentEntries = Object.entries(this.config) as Array<[keyof TConfig, any]>;\n for (const [key, value] of currentEntries) {\n if (next[key] !== value) {\n return true;\n }\n }\n\n for (const key of Object.keys(next) as Array<keyof TConfig>) {\n if (this.config[key] !== next[key]) {\n return true;\n }\n }\n\n return false;\n }\n\n protected configsEqual(a: TConfig, b: TConfig): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n }\n\n async initialize(): Promise<void> {\n if (this.encoder?.state === 'configured') {\n return;\n }\n\n const isSupported = await this.isConfigSupported(this.config);\n if (!isSupported.supported) {\n throw new Error(`Codec not supported: ${JSON.stringify(this.config)}`);\n }\n\n this.encoder = this.createEncoder({\n output: this.handleOutput.bind(this),\n error: this.handleError.bind(this),\n });\n\n (this.encoder as any).configure(this.config);\n }\n\n async reconfigure(config: Partial<TConfig>): Promise<void> {\n if (!config || Object.keys(config).length === 0) {\n return;\n }\n\n const nextConfig = { ...this.config, ...config } as TConfig;\n\n if (this.configsEqual(this.config, nextConfig)) {\n return;\n }\n\n if (!this.encoder) {\n this.config = nextConfig;\n await this.initialize();\n return;\n }\n\n if (this.encoder.state === 'configured') {\n await this.encoder.flush();\n }\n\n const isSupported = await this.isConfigSupported(nextConfig);\n if (!isSupported.supported) {\n throw new Error(`New configuration not supported: ${nextConfig.codec}`);\n }\n\n this.config = nextConfig;\n (this.encoder as any).configure(this.config);\n }\n\n async flush(): Promise<void> {\n if (!this.encoder) {\n return;\n }\n\n await this.encoder.flush();\n }\n\n async reset(): Promise<void> {\n if (!this.encoder) {\n return;\n }\n\n this.encoder.reset();\n this.onReset();\n }\n\n async close(): Promise<void> {\n if (!this.encoder) {\n return;\n }\n\n if (this.encoder.state === 'configured') {\n await this.encoder.flush();\n }\n\n this.encoder.close();\n this.encoder = undefined;\n }\n\n get isReady(): boolean {\n return this.encoder?.state === 'configured';\n }\n\n get queueSize(): number {\n return this.encoder?.encodeQueueSize ?? 0;\n }\n\n protected handleOutput(chunk: TChunk, metadata: TMetadata): void {\n // Only enqueue if controller exists and stream is not closed\n if (this.controller) {\n try {\n this.controller.enqueue({ chunk, metadata });\n } catch (error) {\n // Stream may be closed during flush, ignore enqueue errors\n if (!(error instanceof TypeError && error.message.includes('closed'))) {\n throw error;\n }\n }\n }\n }\n\n protected handleError(error: DOMException): void {\n console.error(`${this.getEncoderType()} encoder error:`, error);\n this.controller?.error(error);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract isConfigSupported(config: TConfig): Promise<{ supported: boolean }>;\n protected abstract createEncoder(init: EncoderInit): TEncoder;\n protected abstract getEncoderType(): string;\n\n // Hook for subclasses to handle reset\n protected onReset(): void {\n // Override in subclasses if needed\n }\n\n // Abstract properties for backpressure configuration\n protected abstract readonly highWaterMark: number;\n protected abstract readonly encodeQueueThreshold: number;\n\n /**\n * Create transform stream for encoding\n * Implements common stream logic with backpressure handling\n */\n createStream(): TransformStream<TInput, EncoderChunk> {\n return new TransformStream<TInput, EncoderChunk>(\n {\n start: async (controller) => {\n this.controller = controller;\n\n // Initialize encoder if not already initialized\n if (!this.isReady) {\n await this.initialize();\n }\n },\n\n transform: async (input) => {\n if (!this.encoder || this.encoder.state !== 'configured') {\n throw new Error('Encoder not configured');\n }\n\n // Check encoder queue pressure\n if (this.encoder.encodeQueueSize >= this.encodeQueueThreshold) {\n // Wait for queue to drain\n await new Promise<void>((resolve) => {\n const check = () => {\n if (!this.encoder || this.encoder.encodeQueueSize < this.encodeQueueThreshold - 1) {\n resolve();\n } else {\n setTimeout(check, 10);\n }\n };\n check();\n });\n }\n\n // Encode the input\n const frame = (input as any).frame || input;\n this.encode(frame);\n },\n\n flush: async () => {\n await this.flush();\n },\n },\n // Queuing strategy with backpressure configuration\n {\n highWaterMark: this.highWaterMark,\n size: () => 1, // Count-based\n }\n );\n }\n\n // Abstract method for encoding\n abstract encode(input: TInput): void;\n}\n\ninterface EncoderInit {\n output: (chunk: any, metadata: any) => void;\n error: (error: DOMException) => void;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BaseEncoder.js","sources":["../../../src/stages/encode/BaseEncoder.ts"],"sourcesContent":["// Base encoder implementation\n\n/**\n * Base encoder class for both video and audio encoding\n * Handles common WebCodecs encoder operations\n */\n\nexport interface EncoderChunk {\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n}\n\nexport abstract class BaseEncoder<\n TEncoder extends VideoEncoder | AudioEncoder,\n TConfig extends VideoEncoderConfig | AudioEncoderConfig,\n TInput extends VideoFrame | AudioData,\n TChunk extends EncodedVideoChunk | EncodedAudioChunk,\n TMetadata extends EncodedVideoChunkMetadata | EncodedAudioChunkMetadata,\n> {\n protected encoder?: TEncoder;\n protected config: TConfig;\n protected controller: TransformStreamDefaultController<EncoderChunk> | null = null;\n\n constructor(config: TConfig) {\n this.config = config;\n }\n\n getConfig(): TConfig {\n return { ...this.config };\n }\n\n protected get currentConfig(): TConfig {\n return this.config;\n }\n\n protected shouldReconfigure(partial: Partial<TConfig>): boolean {\n const next = { ...this.config, ...partial } as TConfig;\n const keys = Object.keys(partial ?? {}) as Array<keyof TConfig>;\n for (const key of keys) {\n if (partial[key] !== undefined && next[key] !== this.config[key]) {\n return true;\n }\n }\n return false;\n }\n\n protected hasConfigChanged(next: TConfig): boolean {\n const currentEntries = Object.entries(this.config) as Array<[keyof TConfig, any]>;\n for (const [key, value] of currentEntries) {\n if (next[key] !== value) {\n return true;\n }\n }\n\n for (const key of Object.keys(next) as Array<keyof TConfig>) {\n if (this.config[key] !== next[key]) {\n return true;\n }\n }\n\n return false;\n }\n\n protected configsEqual(a: TConfig, b: TConfig): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n }\n\n async initialize(): Promise<void> {\n if (this.encoder?.state === 'configured') {\n return;\n }\n\n const configInfo = {\n codec: (this.config as any).codec,\n width: (this.config as any).width,\n height: (this.config as any).height,\n bitrate: (this.config as any).bitrate,\n framerate: (this.config as any).framerate,\n sampleRate: (this.config as any).sampleRate,\n numberOfChannels: (this.config as any).numberOfChannels,\n hardwareAcceleration: (this.config as any).hardwareAcceleration,\n };\n\n console.log(`[${this.getEncoderType()}Encoder] Checking config support:`, {\n platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown',\n config: configInfo,\n });\n\n const isSupported = await this.isConfigSupported(this.config);\n\n console.log(`[${this.getEncoderType()}Encoder] Config support result:`, {\n supported: isSupported.supported,\n config: configInfo,\n });\n\n if (!isSupported.supported) {\n throw new Error(`Codec not supported: ${JSON.stringify(this.config)}`);\n }\n\n this.encoder = this.createEncoder({\n output: this.handleOutput.bind(this),\n error: this.handleError.bind(this),\n });\n\n console.log(`[${this.getEncoderType()}Encoder] Encoder created, configuring...`);\n\n (this.encoder as any).configure(this.config);\n\n console.log(\n `[${this.getEncoderType()}Encoder] Configured successfully, state:`,\n this.encoder?.state\n );\n }\n\n async reconfigure(config: Partial<TConfig>): Promise<void> {\n if (!config || Object.keys(config).length === 0) {\n return;\n }\n\n const nextConfig = { ...this.config, ...config } as TConfig;\n\n if (this.configsEqual(this.config, nextConfig)) {\n return;\n }\n\n if (!this.encoder) {\n this.config = nextConfig;\n await this.initialize();\n return;\n }\n\n if (this.encoder.state === 'configured') {\n await this.encoder.flush();\n }\n\n const isSupported = await this.isConfigSupported(nextConfig);\n if (!isSupported.supported) {\n throw new Error(`New configuration not supported: ${nextConfig.codec}`);\n }\n\n this.config = nextConfig;\n (this.encoder as any).configure(this.config);\n }\n\n async flush(): Promise<void> {\n if (!this.encoder) {\n return;\n }\n\n await this.encoder.flush();\n }\n\n async reset(): Promise<void> {\n if (!this.encoder) {\n return;\n }\n\n this.encoder.reset();\n this.onReset();\n }\n\n async close(): Promise<void> {\n if (!this.encoder) {\n return;\n }\n\n if (this.encoder.state === 'configured') {\n await this.encoder.flush();\n }\n\n this.encoder.close();\n this.encoder = undefined;\n }\n\n get isReady(): boolean {\n return this.encoder?.state === 'configured';\n }\n\n get queueSize(): number {\n return this.encoder?.encodeQueueSize ?? 0;\n }\n\n protected handleOutput(chunk: TChunk, metadata: TMetadata): void {\n // Only enqueue if controller exists and stream is not closed\n if (this.controller) {\n try {\n this.controller.enqueue({ chunk, metadata });\n } catch (error) {\n // Stream may be closed during flush, ignore enqueue errors\n if (!(error instanceof TypeError && error.message.includes('closed'))) {\n throw error;\n }\n }\n }\n }\n\n protected handleError(error: DOMException): void {\n console.error(`[${this.getEncoderType()}Encoder] Encode error:`, {\n name: error.name,\n message: error.message,\n encoderState: this.encoder?.state,\n queueSize: this.queueSize,\n platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',\n });\n this.controller?.error(error);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract isConfigSupported(config: TConfig): Promise<{ supported: boolean }>;\n protected abstract createEncoder(init: EncoderInit): TEncoder;\n protected abstract getEncoderType(): string;\n\n // Hook for subclasses to handle reset\n protected onReset(): void {\n // Override in subclasses if needed\n }\n\n // Abstract properties for backpressure configuration\n protected abstract readonly highWaterMark: number;\n protected abstract readonly encodeQueueThreshold: number;\n\n /**\n * Create transform stream for encoding\n * Implements common stream logic with backpressure handling\n */\n createStream(): TransformStream<TInput, EncoderChunk> {\n return new TransformStream<TInput, EncoderChunk>(\n {\n start: async (controller) => {\n this.controller = controller;\n\n // Initialize encoder if not already initialized\n if (!this.isReady) {\n await this.initialize();\n }\n },\n\n transform: async (input) => {\n if (!this.encoder || this.encoder.state !== 'configured') {\n throw new Error('Encoder not configured');\n }\n\n // Check encoder queue pressure\n if (this.encoder.encodeQueueSize >= this.encodeQueueThreshold) {\n // Wait for queue to drain\n await new Promise<void>((resolve) => {\n const check = () => {\n if (!this.encoder || this.encoder.encodeQueueSize < this.encodeQueueThreshold - 1) {\n resolve();\n } else {\n setTimeout(check, 10);\n }\n };\n check();\n });\n }\n\n // Encode the input\n const frame = (input as any).frame || input;\n this.encode(frame);\n },\n\n flush: async () => {\n await this.flush();\n },\n },\n // Queuing strategy with backpressure configuration\n {\n highWaterMark: this.highWaterMark,\n size: () => 1, // Count-based\n }\n );\n }\n\n // Abstract method for encoding\n abstract encode(input: TInput): void;\n}\n\ninterface EncoderInit {\n output: (chunk: any, metadata: any) => void;\n error: (error: DOMException) => void;\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VideoChunkEncoder.d.ts","sourceRoot":"","sources":["../../../src/stages/encode/VideoChunkEncoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,WAAW,CAChD,YAAY,EACZ,kBAAkB,EAClB,UAAU,EACV,iBAAiB,EACjB,yBAAyB,CAC1B;IACC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAK;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,8BAA8B,CAAK;IAE3D,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IACzC,SAAS,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEhD,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,gBAAgB,CAAM;gBAElB,MAAM,EAAE,kBAAkB;cAUtB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAK9F,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,gBAAgB,GAAG,YAAY;IAI7D,SAAS,CAAC,cAAc,IAAI,MAAM;cAIf,OAAO,IAAI,IAAI;IAIlC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAW/B,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI3C,OAAO,CAAC,sBAAsB;CAM/B"}
1
+ {"version":3,"file":"VideoChunkEncoder.d.ts","sourceRoot":"","sources":["../../../src/stages/encode/VideoChunkEncoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAElD;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,WAAW,CAChD,YAAY,EACZ,kBAAkB,EAClB,UAAU,EACV,iBAAiB,EACjB,yBAAyB,CAC1B;IACC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAK;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,8BAA8B,CAAK;IAE3D,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IACzC,SAAS,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEhD,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,gBAAgB,CAAM;gBAElB,MAAM,EAAE,kBAAkB;cAetB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAK9F,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,gBAAgB,GAAG,YAAY;IAI7D,SAAS,CAAC,cAAc,IAAI,MAAM;cAIf,OAAO,IAAI,IAAI;IAIlC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAW/B,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI3C,OAAO,CAAC,sBAAsB;CAM/B"}
@@ -9,6 +9,7 @@ export interface VideoEncoderConfig {
9
9
  latencyMode?: LatencyMode;
10
10
  bitrateMode?: BitrateMode;
11
11
  avc?: AvcEncoderConfig;
12
+ keyFrameInterval?: number;
12
13
  backpressure?: {
13
14
  highWaterMark?: number;
14
15
  encodeQueueThreshold?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/stages/encode/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/stages/encode/types.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,24 @@ class BaseDecoder {
9
9
  if (this.decoder?.state === "configured") {
10
10
  return;
11
11
  }
12
+ const configInfo = {
13
+ codec: this.config.codec,
14
+ width: this.config.width,
15
+ height: this.config.height,
16
+ sampleRate: this.config.sampleRate,
17
+ numberOfChannels: this.config.numberOfChannels,
18
+ hardwareAcceleration: this.config.hardwareAcceleration
19
+ };
20
+ console.log(`[${this.getDecoderType()}Decoder] Checking config support:`, {
21
+ platform: typeof navigator !== "undefined" ? navigator.platform : "unknown",
22
+ userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "unknown",
23
+ config: configInfo
24
+ });
12
25
  const isSupported = await this.isConfigSupported(this.config);
26
+ console.log(`[${this.getDecoderType()}Decoder] Config support result:`, {
27
+ supported: isSupported.supported,
28
+ config: configInfo
29
+ });
13
30
  if (!isSupported.supported) {
14
31
  throw new Error(
15
32
  `Codec not supported: ${this.config.codecString || this.config.codec}`
@@ -19,7 +36,12 @@ class BaseDecoder {
19
36
  output: this.handleOutput.bind(this),
20
37
  error: this.handleError.bind(this)
21
38
  });
39
+ console.log(`[${this.getDecoderType()}Decoder] Decoder created, configuring...`);
22
40
  await this.configureDecoder(this.config);
41
+ console.log(
42
+ `[${this.getDecoderType()}Decoder] Configured successfully, state:`,
43
+ this.decoder?.state
44
+ );
23
45
  }
24
46
  async reconfigure(config) {
25
47
  this.config = { ...this.config, ...config };
@@ -44,6 +66,12 @@ class BaseDecoder {
44
66
  }
45
67
  async reset() {
46
68
  if (!this.decoder) return;
69
+ if (this.decoder.state === "configured") {
70
+ try {
71
+ await this.decoder.flush();
72
+ } catch {
73
+ }
74
+ }
47
75
  this.decoder.reset();
48
76
  this.onReset();
49
77
  }
@@ -79,7 +107,13 @@ class BaseDecoder {
79
107
  }
80
108
  }
81
109
  handleError(error) {
82
- console.error(`${this.getDecoderType()} decoder error:`, error);
110
+ console.error(`[${this.getDecoderType()}Decoder] Decode error:`, {
111
+ name: error.name,
112
+ message: error.message,
113
+ decoderState: this.decoder?.state,
114
+ queueSize: this.queueSize,
115
+ platform: typeof navigator !== "undefined" ? navigator.platform : "unknown"
116
+ });
83
117
  this.controller?.error(error);
84
118
  }
85
119
  closeIfPossible(data) {
@@ -129,4 +163,4 @@ class BaseDecoder {
129
163
  export {
130
164
  BaseDecoder as B
131
165
  };
132
- //# sourceMappingURL=BaseDecoder.CTW-vr29.js.map
166
+ //# sourceMappingURL=BaseDecoder.DWHTBMDB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseDecoder.DWHTBMDB.js","sources":["../../src/stages/decode/BaseDecoder.ts"],"sourcesContent":["/**\n * Base decoder class abstracting common WebCodecs decoder workflow.\n * Mirrors BaseEncoder but for decoding path.\n */\nimport { AudioDecoderConfig, DecodedVideoFrame, VideoDecoderConfig } from './types';\n\nexport abstract class BaseDecoder<\n TDecoder extends VideoDecoder | AudioDecoder,\n TConfig extends VideoDecoderConfig | AudioDecoderConfig,\n TInput extends EncodedVideoChunk | EncodedAudioChunk,\n TOutput extends VideoFrame | AudioData | DecodedVideoFrame,\n> {\n protected decoder?: TDecoder;\n protected config: TConfig;\n protected controller: TransformStreamDefaultController<TOutput> | null = null;\n\n constructor(config: TConfig) {\n this.config = config;\n }\n\n async initialize(): Promise<void> {\n if (this.decoder?.state === 'configured') {\n return;\n }\n\n const configInfo = {\n codec: (this.config as any).codec,\n width: (this.config as any).width,\n height: (this.config as any).height,\n sampleRate: (this.config as any).sampleRate,\n numberOfChannels: (this.config as any).numberOfChannels,\n hardwareAcceleration: (this.config as any).hardwareAcceleration,\n };\n\n console.log(`[${this.getDecoderType()}Decoder] Checking config support:`, {\n platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',\n userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown',\n config: configInfo,\n });\n\n const isSupported = await this.isConfigSupported(this.config);\n\n console.log(`[${this.getDecoderType()}Decoder] Config support result:`, {\n supported: isSupported.supported,\n config: configInfo,\n });\n\n if (!isSupported.supported) {\n throw new Error(\n `Codec not supported: ${(this.config as any).codecString || (this.config as any).codec}`\n );\n }\n\n this.decoder = this.createDecoder({\n output: this.handleOutput.bind(this),\n error: this.handleError.bind(this),\n });\n\n console.log(`[${this.getDecoderType()}Decoder] Decoder created, configuring...`);\n\n await this.configureDecoder(this.config);\n\n console.log(\n `[${this.getDecoderType()}Decoder] Configured successfully, state:`,\n this.decoder?.state\n );\n }\n\n async reconfigure(config: Partial<TConfig>): Promise<void> {\n this.config = { ...this.config, ...config } as TConfig;\n\n if (!this.decoder) {\n await this.initialize();\n return;\n }\n\n // Flush before reconfiguring\n if (this.decoder.state === 'configured') {\n await this.decoder.flush();\n }\n\n const isSupported = await this.isConfigSupported(this.config);\n if (!isSupported.supported) {\n throw new Error(\n `New configuration not supported: ${(this.config as any).codecString || (this.config as any).codec}`\n );\n }\n\n await this.configureDecoder(this.config);\n }\n\n async flush(): Promise<void> {\n if (!this.decoder) return;\n await this.decoder.flush();\n }\n\n async reset(): Promise<void> {\n if (!this.decoder) return;\n\n // Windows Media Foundation requires flush before reset to avoid decoder error state\n if (this.decoder.state === 'configured') {\n try {\n await this.decoder.flush();\n } catch {\n // Ignore flush errors during reset\n }\n }\n\n this.decoder.reset();\n this.onReset();\n }\n\n async close(): Promise<void> {\n if (!this.decoder) return;\n\n if (this.decoder.state === 'configured') {\n await this.decoder.flush();\n }\n\n this.decoder.close();\n this.decoder = undefined;\n }\n\n get isReady(): boolean {\n return this.decoder?.state === 'configured';\n }\n\n get queueSize(): number {\n return (this.decoder as any)?.decodeQueueSize ?? 0;\n }\n\n protected handleOutput(data: TOutput): void {\n if (!this.controller) {\n this.closeIfPossible(data);\n return;\n }\n\n try {\n this.controller.enqueue(data);\n if (data instanceof VideoFrame && (this.config as VideoDecoderConfig).thread !== 'main') {\n // ugly hack to avoid memory leak\n // setTimeout(() => {\n // data.close();\n // }, 500);\n }\n } catch (error) {\n // Stream closed or errored - silently drop frame\n if (error instanceof TypeError) {\n this.closeIfPossible(data);\n return;\n }\n\n throw error;\n }\n }\n\n protected handleError(error: DOMException): void {\n console.error(`[${this.getDecoderType()}Decoder] Decode error:`, {\n name: error.name,\n message: error.message,\n decoderState: this.decoder?.state,\n queueSize: this.queueSize,\n platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',\n });\n this.controller?.error(error);\n }\n\n private closeIfPossible(data: TOutput): void {\n (data as any)?.close?.();\n (data as DecodedVideoFrame)?.frame?.close?.();\n }\n\n // Abstracts\n protected abstract isConfigSupported(config: TConfig): Promise<{ supported: boolean }>;\n protected abstract createDecoder(init: DecoderInit): TDecoder;\n protected abstract configureDecoder(config: TConfig): Promise<void>;\n protected abstract getDecoderType(): string;\n protected abstract decode(input: TInput): void;\n protected onReset(): void {}\n\n // Backpressure\n protected abstract readonly highWaterMark: number;\n protected abstract readonly decodeQueueThreshold: number;\n\n createStream(): TransformStream<TInput, TOutput> {\n return new TransformStream<TInput, TOutput>(\n {\n start: async (controller) => {\n this.controller = controller;\n if (!this.isReady) {\n await this.initialize();\n }\n },\n transform: async (input) => {\n if (!this.decoder || this.decoder.state !== 'configured') {\n throw new Error('Decoder not configured');\n }\n // backpressure\n if ((this.decoder as any).decodeQueueSize >= this.decodeQueueThreshold) {\n await new Promise<void>((resolve) => {\n const check = () => {\n if (\n !this.decoder ||\n (this.decoder as any).decodeQueueSize < this.decodeQueueThreshold - 1\n ) {\n resolve();\n } else {\n setTimeout(check, 10);\n }\n };\n check();\n });\n }\n this.decode(input);\n },\n flush: async () => {\n await this.flush();\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: () => 1,\n }\n );\n }\n}\n\ninterface DecoderInit {\n output: (data: any) => void;\n error: (error: DOMException) => void;\n}\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.CE5euh3R.js";
2
- import { B as BaseDecoder } from "../../BaseDecoder.CTW-vr29.js";
2
+ import { B as BaseDecoder } from "../../BaseDecoder.DWHTBMDB.js";
3
3
  class AudioChunkDecoder extends BaseDecoder {
4
4
  // Default values
5
5
  static DEFAULT_HIGH_WATER_MARK = 20;
@@ -334,4 +334,4 @@ export {
334
334
  AudioDecodeWorker,
335
335
  audioDecode_worker as default
336
336
  };
337
- //# sourceMappingURL=audio-decode.worker.CP8bXXa4.js.map
337
+ //# sourceMappingURL=audio-decode.worker._DbEy3nn.js.map