@meframe/core 0.1.7 → 0.1.9

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 (82) hide show
  1. package/dist/cache/CacheManager.d.ts +1 -1
  2. package/dist/cache/CacheManager.d.ts.map +1 -1
  3. package/dist/cache/CacheManager.js +4 -3
  4. package/dist/cache/CacheManager.js.map +1 -1
  5. package/dist/cache/l1/AudioL1Cache.d.ts +3 -8
  6. package/dist/cache/l1/AudioL1Cache.d.ts.map +1 -1
  7. package/dist/cache/l1/AudioL1Cache.js +41 -56
  8. package/dist/cache/l1/AudioL1Cache.js.map +1 -1
  9. package/dist/controllers/PlaybackController.d.ts +2 -0
  10. package/dist/controllers/PlaybackController.d.ts.map +1 -1
  11. package/dist/controllers/PlaybackController.js +75 -14
  12. package/dist/controllers/PlaybackController.js.map +1 -1
  13. package/dist/controllers/PlaybackStateMachine.d.ts.map +1 -1
  14. package/dist/controllers/PlaybackStateMachine.js +33 -10
  15. package/dist/controllers/PlaybackStateMachine.js.map +1 -1
  16. package/dist/controllers/types.d.ts +13 -3
  17. package/dist/controllers/types.d.ts.map +1 -1
  18. package/dist/controllers/types.js +2 -0
  19. package/dist/controllers/types.js.map +1 -1
  20. package/dist/model/types.d.ts +0 -1
  21. package/dist/model/types.d.ts.map +1 -1
  22. package/dist/model/types.js.map +1 -1
  23. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
  24. package/dist/orchestrator/ExportScheduler.js +22 -19
  25. package/dist/orchestrator/ExportScheduler.js.map +1 -1
  26. package/dist/orchestrator/GlobalAudioSession.d.ts +15 -2
  27. package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
  28. package/dist/orchestrator/GlobalAudioSession.js +48 -20
  29. package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
  30. package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
  31. package/dist/orchestrator/OnDemandVideoSession.js +13 -0
  32. package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
  33. package/dist/orchestrator/Orchestrator.d.ts +1 -1
  34. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  35. package/dist/orchestrator/Orchestrator.js +52 -20
  36. package/dist/orchestrator/Orchestrator.js.map +1 -1
  37. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  38. package/dist/orchestrator/VideoClipSession.js +4 -2
  39. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  40. package/dist/orchestrator/types.d.ts +7 -1
  41. package/dist/orchestrator/types.d.ts.map +1 -1
  42. package/dist/stages/load/ResourceLoader.d.ts +6 -5
  43. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  44. package/dist/stages/load/ResourceLoader.js +62 -41
  45. package/dist/stages/load/ResourceLoader.js.map +1 -1
  46. package/dist/stages/load/TaskManager.d.ts +5 -12
  47. package/dist/stages/load/TaskManager.d.ts.map +1 -1
  48. package/dist/stages/load/TaskManager.js +60 -46
  49. package/dist/stages/load/TaskManager.js.map +1 -1
  50. package/dist/stages/mux/MP4Muxer.d.ts.map +1 -1
  51. package/dist/stages/mux/MP4Muxer.js +3 -0
  52. package/dist/stages/mux/MP4Muxer.js.map +1 -1
  53. package/dist/utils/platform-utils.d.ts +1 -0
  54. package/dist/utils/platform-utils.d.ts.map +1 -1
  55. package/dist/utils/platform-utils.js +19 -6
  56. package/dist/utils/platform-utils.js.map +1 -1
  57. package/dist/worker/BaseWorker.d.ts +2 -0
  58. package/dist/worker/BaseWorker.d.ts.map +1 -1
  59. package/dist/worker/BaseWorker.js.map +1 -1
  60. package/dist/worker/WorkerChannel.d.ts +2 -0
  61. package/dist/worker/WorkerChannel.d.ts.map +1 -1
  62. package/dist/worker/WorkerChannel.js +17 -1
  63. package/dist/worker/WorkerChannel.js.map +1 -1
  64. package/dist/workers/{WorkerChannel.DjBEVvEA.js → WorkerChannel.DQK8rAab.js} +18 -2
  65. package/dist/workers/{WorkerChannel.DjBEVvEA.js.map → WorkerChannel.DQK8rAab.js.map} +1 -1
  66. package/dist/workers/stages/compose/{audio-compose.worker.CiM_KP27.js → audio-compose.worker.B4Io5w9i.js} +2 -2
  67. package/dist/workers/stages/compose/{audio-compose.worker.CiM_KP27.js.map → audio-compose.worker.B4Io5w9i.js.map} +1 -1
  68. package/dist/workers/stages/compose/{video-compose.worker.CQwmNfXT.js → video-compose.worker.CA2_Kpg-.js} +2 -2
  69. package/dist/workers/stages/compose/{video-compose.worker.CQwmNfXT.js.map → video-compose.worker.CA2_Kpg-.js.map} +1 -1
  70. package/dist/workers/stages/decode/{audio-decode.worker.CpjkrZtT.js → audio-decode.worker.-DGlQrJD.js} +2 -2
  71. package/dist/workers/stages/decode/{audio-decode.worker.CpjkrZtT.js.map → audio-decode.worker.-DGlQrJD.js.map} +1 -1
  72. package/dist/workers/stages/decode/{video-decode.worker.BQtw6eWn.js → video-decode.worker.BnWVUkng.js} +2 -2
  73. package/dist/workers/stages/decode/video-decode.worker.BnWVUkng.js.map +1 -0
  74. package/dist/workers/stages/demux/{audio-demux.worker.C4V11GQi.js → audio-demux.worker.D-_LoVqW.js} +2 -2
  75. package/dist/workers/stages/demux/{audio-demux.worker.C4V11GQi.js.map → audio-demux.worker.D-_LoVqW.js.map} +1 -1
  76. package/dist/workers/stages/demux/{video-demux.worker.5pJr0Ij-.js → video-demux.worker.BWDrLGni.js} +2 -2
  77. package/dist/workers/stages/demux/{video-demux.worker.5pJr0Ij-.js.map → video-demux.worker.BWDrLGni.js.map} +1 -1
  78. package/dist/workers/stages/encode/{video-encode.worker.CX2_3YhQ.js → video-encode.worker.D6aB_rF9.js} +2 -2
  79. package/dist/workers/stages/encode/{video-encode.worker.CX2_3YhQ.js.map → video-encode.worker.D6aB_rF9.js.map} +1 -1
  80. package/dist/workers/worker-manifest.json +7 -7
  81. package/package.json +1 -1
  82. package/dist/workers/stages/decode/video-decode.worker.BQtw6eWn.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.DjBEVvEA.js";
1
+ import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.DQK8rAab.js";
2
2
  import { B as BaseDecoder } from "../../BaseDecoder.CB5XmTpS.js";
3
3
  class AudioChunkDecoder extends BaseDecoder {
4
4
  // Default values
@@ -317,4 +317,4 @@ export {
317
317
  AudioDecodeWorker,
318
318
  audioDecode_worker as default
319
319
  };
320
- //# sourceMappingURL=audio-decode.worker.CpjkrZtT.js.map
320
+ //# sourceMappingURL=audio-decode.worker.-DGlQrJD.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"audio-decode.worker.CpjkrZtT.js","sources":["../../../../src/stages/decode/AudioChunkDecoder.ts","../../../../src/stages/decode/audio-decode.worker.ts"],"sourcesContent":["import { AudioDecoderConfig } from './types';\nimport { BaseDecoder } from './BaseDecoder';\n\n/**\n * Audio decoder with streaming support\n * Extends BaseDecoder for common WebCodecs operations\n */\nexport class AudioChunkDecoder extends BaseDecoder<\n AudioDecoder,\n AudioDecoderConfig,\n EncodedAudioChunk,\n AudioData\n> {\n // Default values\n private static readonly DEFAULT_HIGH_WATER_MARK = 20;\n private static readonly DEFAULT_DECODE_QUEUE_THRESHOLD = 16;\n\n // Exposed properties\n readonly trackId: string;\n\n // Backpressure configuration\n protected readonly highWaterMark: number;\n protected readonly decodeQueueThreshold: number;\n\n // Buffering support for delayed configuration\n private bufferedChunks: EncodedAudioChunk[] = [];\n private isProcessingBuffer: boolean = false;\n\n constructor(trackId: string, config?: Partial<AudioDecoderConfig>) {\n // Initialize with empty config, will be configured later\n super(config as AudioDecoderConfig);\n\n this.trackId = trackId;\n\n // Set backpressure configuration\n this.highWaterMark =\n config?.backpressure?.highWaterMark ?? AudioChunkDecoder.DEFAULT_HIGH_WATER_MARK;\n this.decodeQueueThreshold = AudioChunkDecoder.DEFAULT_DECODE_QUEUE_THRESHOLD;\n }\n\n // Computed properties\n get isConfigured(): boolean {\n return this.isReady;\n }\n\n get state(): string {\n return this.decoder?.state || 'unconfigured';\n }\n\n /**\n * Update configuration - can be called before or after initialization\n */\n async updateConfig(config: Partial<AudioDecoderConfig>): Promise<void> {\n // If decoder is not ready and we have codec info, configure it\n if (!this.isReady && config.codec) {\n await this.configure(config as AudioDecoderConfig);\n await this.processBufferedChunks();\n return;\n }\n\n // Note: AudioDecoder doesn't have many runtime-configurable options\n // Backpressure settings are readonly in this implementation\n // if (config.backpressure) {\n // console.warn('Backpressure settings cannot be changed at runtime');\n // }\n }\n\n // Override createStream to handle buffering\n override createStream(): TransformStream<EncodedAudioChunk, AudioData> {\n return new TransformStream<EncodedAudioChunk, AudioData>(\n {\n start: async (controller) => {\n this.controller = controller;\n // Don't initialize if no codec config yet\n if (this.config?.codec && !this.isReady) {\n await this.initialize();\n }\n },\n\n transform: async (chunk) => {\n // If not configured yet, buffer the chunk\n if (!this.isReady) {\n this.bufferedChunks.push(chunk);\n return; // Don't process yet\n }\n\n // If we're processing buffered chunks, add to buffer to maintain order\n if (this.isProcessingBuffer) {\n this.bufferedChunks.push(chunk);\n return;\n }\n\n // Normal processing\n await this.processChunk(chunk);\n },\n\n flush: async () => {\n if (this.isReady) {\n await this.flush();\n }\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: () => 1,\n }\n );\n }\n\n /**\n * Process a single chunk (extracted from transform for reuse)\n */\n private async processChunk(chunk: EncodedAudioChunk): Promise<void> {\n if (!this.decoder) {\n throw new Error('Decoder not initialized');\n }\n\n if (this.decoder.state !== 'configured') {\n console.error('[AudioChunkDecoder] Decoder in unexpected state:', this.decoder.state);\n throw new Error(`Decoder not configured, state: ${this.decoder.state}`);\n }\n\n this.decode(chunk);\n }\n\n // Implement abstract methods\n protected async isConfigSupported(config: AudioDecoderConfig): Promise<{ supported: boolean }> {\n const result = await AudioDecoder.isConfigSupported({\n codec: config.codec,\n sampleRate: config.sampleRate,\n numberOfChannels: config.numberOfChannels,\n });\n return { supported: result.supported ?? false };\n }\n\n protected createDecoder(init: {\n output: (data: AudioData) => void;\n error: (error: DOMException) => void;\n }): AudioDecoder {\n return new AudioDecoder(init);\n }\n\n protected getDecoderType(): string {\n return 'Audio';\n }\n\n protected async configureDecoder(config: AudioDecoderConfig): Promise<void> {\n if (!this.decoder) return;\n\n await this.decoder.configure({\n codec: config.codec,\n sampleRate: config.sampleRate,\n numberOfChannels: config.numberOfChannels,\n ...(config.description && { description: config.description }),\n });\n }\n\n protected decode(chunk: EncodedAudioChunk): void {\n this.decoder?.decode(chunk);\n }\n\n /**\n * Configure the decoder with codec info (can be called after creation)\n */\n async configure(config: AudioDecoderConfig): Promise<void> {\n if (this.isReady) {\n // If already configured, reconfigure\n await this.reconfigure(config);\n return;\n }\n\n this.config = config as any;\n\n // Initialize decoder with new config\n await this.initialize();\n }\n\n /**\n * Process any buffered chunks after configuration\n */\n async processBufferedChunks(): Promise<void> {\n if (!this.isReady || this.bufferedChunks.length === 0) {\n return;\n }\n\n this.isProcessingBuffer = true;\n\n // Process buffered chunks in order\n const chunks = [...this.bufferedChunks];\n this.bufferedChunks = [];\n\n for (const chunk of chunks) {\n try {\n await this.processChunk(chunk);\n } catch (error) {\n console.error('[AudioChunkDecoder] Error processing buffered chunk:', error);\n }\n }\n\n this.isProcessingBuffer = false;\n\n // Process any new chunks that arrived while processing buffer\n if (this.bufferedChunks.length > 0) {\n await this.processBufferedChunks();\n }\n }\n\n // Override close to clean up buffered chunks\n override async close(): Promise<void> {\n // Clear buffered chunks\n this.bufferedChunks = [];\n\n // Call parent close\n await super.close();\n }\n}\n","import { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { AudioChunkDecoder } from './AudioChunkDecoder';\nimport { AudioDecoderConfig } from './types';\nimport type { AudioTrackConfig } from '../compose/types';\n\ninterface TrackMetadata {\n clipId: string;\n config: AudioTrackConfig;\n sampleRate?: number;\n numberOfChannels?: number;\n type: 'bgm' | 'voice' | 'sfx' | 'other';\n}\n\nconst normalizeDescription = (desc?: ArrayBuffer | ArrayBufferView): ArrayBuffer | undefined => {\n if (!desc) return undefined;\n\n if (desc instanceof ArrayBuffer) return desc;\n\n const view = desc as ArrayBufferView;\n return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength) as ArrayBuffer;\n};\n\n/**\n * AudioDecodeWorker (Clip Local) - Decodes audio for a single clip\n * Receives encoded audio chunks from AudioDemuxWorker and outputs decoded audio data\n *\n * Pipeline: AudioDemuxWorker → AudioDecodeWorker → Main Thread (OfflineAudioContext)\n *\n * Features:\n * - Single clip, single AudioDecoder instance\n * - Outputs AudioData stream to main thread for mixing\n * - Stream-based processing with backpressure\n * - Lifecycle tied to clip pipeline\n */\nexport class AudioDecodeWorker {\n private channel: WorkerChannel;\n private decoder: AudioChunkDecoder | null = null;\n private clipId: string = '';\n private trackId: string = '';\n\n private defaultConfig: Partial<AudioDecoderConfig> = {};\n private trackMetadata: TrackMetadata | null = null;\n\n private upstreamPort: MessagePort | null = null;\n\n constructor() {\n this.channel = new WorkerChannel(self as any, {\n name: 'AudioDecodeWorker',\n timeout: 30000,\n });\n\n this.setupHandlers();\n }\n\n private setupHandlers(): void {\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n this.channel.registerHandler('connect', this.handleConnect.bind(this));\n this.channel.registerHandler('flush', this.handleFlush.bind(this));\n this.channel.registerHandler('reset', this.handleReset.bind(this));\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n }\n\n private async handleConnect(payload: {\n direction: 'upstream' | 'downstream';\n port: MessagePort;\n streamType?: 'audio';\n sessionId?: string;\n trackId?: string;\n clipStartUs?: number;\n clipDurationUs?: number;\n }): Promise<{ success: boolean }> {\n const { port, direction, sessionId, trackId } = payload;\n\n if (direction === 'upstream') {\n this.upstreamPort = port;\n this.clipId = sessionId || 'default';\n this.trackId = trackId || sessionId || 'default';\n\n const channel = new WorkerChannel(port, {\n name: 'Demux-AudioDecode',\n timeout: 30000,\n });\n\n channel.receiveStream((stream, metadata) => {\n this.handleReceiveStream(stream, {\n ...metadata,\n clipStartUs: payload.clipStartUs,\n clipDurationUs: payload.clipDurationUs,\n });\n });\n\n channel.registerHandler('configure', this.handleConfigure.bind(this));\n }\n\n return { success: true };\n }\n\n private async handleConfigure(payload: {\n config?: { audio?: Partial<AudioDecoderConfig> };\n sessionId?: string;\n streamType?: 'audio';\n codec?: string;\n sampleRate?: number;\n numberOfChannels?: number;\n description?: ArrayBuffer | Uint8Array;\n }): Promise<{ success: boolean }> {\n const { sessionId, streamType, codec, sampleRate, numberOfChannels, description, config } =\n payload;\n\n if (sessionId && streamType === 'audio') {\n // Save codec info to defaultConfig for decoder creation\n Object.assign(this.defaultConfig, {\n codec,\n sampleRate,\n numberOfChannels,\n description: normalizeDescription(description),\n });\n\n // Update existing decoder if present (will process buffered chunks)\n if (this.decoder) {\n await this.decoder.updateConfig({\n codec,\n sampleRate,\n numberOfChannels,\n description: normalizeDescription(description),\n });\n }\n\n return { success: true };\n }\n\n if (config?.audio) {\n Object.assign(this.defaultConfig, config.audio);\n\n if (this.decoder) {\n await this.decoder.updateConfig(config.audio);\n }\n }\n\n this.channel.state = WorkerState.Ready;\n\n return { success: true };\n }\n\n private async handleReceiveStream(\n stream: ReadableStream,\n metadata?: Record<string, any>\n ): Promise<void> {\n const sessionId = metadata?.sessionId || this.clipId;\n const trackId = metadata?.trackId || this.trackId;\n if (!this.decoder) {\n // Prefer metadata, fallback to defaultConfig\n // Note: If codec is not available, decoder will buffer chunks internally\n const decoderConfig = {\n ...this.defaultConfig,\n codec: metadata?.codec ?? this.defaultConfig.codec,\n sampleRate: metadata?.sampleRate ?? this.defaultConfig.sampleRate,\n numberOfChannels: metadata?.numberOfChannels ?? this.defaultConfig.numberOfChannels,\n description: normalizeDescription(metadata?.description) ?? this.defaultConfig.description,\n };\n\n this.decoder = new AudioChunkDecoder(trackId, decoderConfig);\n }\n\n this.trackMetadata = {\n clipId: this.clipId,\n config: this.extractTrackConfig(metadata?.runtimeConfig),\n sampleRate: metadata?.sampleRate,\n numberOfChannels: metadata?.numberOfChannels,\n type: (metadata?.trackType as TrackMetadata['type']) ?? 'other',\n };\n\n const transform = this.decoder.createStream();\n this.channel.sendStream(transform.readable as ReadableStream<AudioData>, {\n streamType: 'audio',\n sessionId,\n trackId,\n clipStartUs: metadata?.clipStartUs ?? 0,\n clipDurationUs: metadata?.clipDurationUs ?? 0,\n trackMetadata: this.trackMetadata,\n });\n\n stream\n .pipeTo(transform.writable)\n .catch((error) => console.error('[AudioDecodeWorker] Audio stream pipe error:', error));\n }\n\n private extractTrackConfig(config: any): AudioTrackConfig {\n return {\n startTimeUs: config?.startTimeUs ?? 0,\n durationUs: config?.durationUs,\n volume: config?.volume ?? 1,\n fadeIn: config?.fadeIn,\n fadeOut: config?.fadeOut,\n effects: config?.effects ?? [],\n duckingTag: config?.duckingTag,\n };\n }\n\n private async handleFlush(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.flush();\n }\n return { success: true };\n }\n\n private async handleReset(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.reset();\n }\n\n this.channel.notify('reset_complete', {\n type: 'audio',\n });\n\n return { success: true };\n }\n\n private async handleGetStats(): Promise<{ audio?: any }> {\n if (!this.decoder) {\n return {};\n }\n\n return {\n audio: {\n clipId: this.clipId,\n trackId: this.trackId,\n configured: this.decoder.isConfigured,\n queueSize: this.decoder.queueSize,\n state: this.decoder.state,\n },\n };\n }\n\n private async handleDispose(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.close();\n this.decoder = null;\n }\n\n this.upstreamPort?.close();\n this.upstreamPort = null;\n\n this.trackMetadata = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n}\n\nconst worker = new AudioDecodeWorker();\n\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audio-decode.worker.-DGlQrJD.js","sources":["../../../../src/stages/decode/AudioChunkDecoder.ts","../../../../src/stages/decode/audio-decode.worker.ts"],"sourcesContent":["import { AudioDecoderConfig } from './types';\nimport { BaseDecoder } from './BaseDecoder';\n\n/**\n * Audio decoder with streaming support\n * Extends BaseDecoder for common WebCodecs operations\n */\nexport class AudioChunkDecoder extends BaseDecoder<\n AudioDecoder,\n AudioDecoderConfig,\n EncodedAudioChunk,\n AudioData\n> {\n // Default values\n private static readonly DEFAULT_HIGH_WATER_MARK = 20;\n private static readonly DEFAULT_DECODE_QUEUE_THRESHOLD = 16;\n\n // Exposed properties\n readonly trackId: string;\n\n // Backpressure configuration\n protected readonly highWaterMark: number;\n protected readonly decodeQueueThreshold: number;\n\n // Buffering support for delayed configuration\n private bufferedChunks: EncodedAudioChunk[] = [];\n private isProcessingBuffer: boolean = false;\n\n constructor(trackId: string, config?: Partial<AudioDecoderConfig>) {\n // Initialize with empty config, will be configured later\n super(config as AudioDecoderConfig);\n\n this.trackId = trackId;\n\n // Set backpressure configuration\n this.highWaterMark =\n config?.backpressure?.highWaterMark ?? AudioChunkDecoder.DEFAULT_HIGH_WATER_MARK;\n this.decodeQueueThreshold = AudioChunkDecoder.DEFAULT_DECODE_QUEUE_THRESHOLD;\n }\n\n // Computed properties\n get isConfigured(): boolean {\n return this.isReady;\n }\n\n get state(): string {\n return this.decoder?.state || 'unconfigured';\n }\n\n /**\n * Update configuration - can be called before or after initialization\n */\n async updateConfig(config: Partial<AudioDecoderConfig>): Promise<void> {\n // If decoder is not ready and we have codec info, configure it\n if (!this.isReady && config.codec) {\n await this.configure(config as AudioDecoderConfig);\n await this.processBufferedChunks();\n return;\n }\n\n // Note: AudioDecoder doesn't have many runtime-configurable options\n // Backpressure settings are readonly in this implementation\n // if (config.backpressure) {\n // console.warn('Backpressure settings cannot be changed at runtime');\n // }\n }\n\n // Override createStream to handle buffering\n override createStream(): TransformStream<EncodedAudioChunk, AudioData> {\n return new TransformStream<EncodedAudioChunk, AudioData>(\n {\n start: async (controller) => {\n this.controller = controller;\n // Don't initialize if no codec config yet\n if (this.config?.codec && !this.isReady) {\n await this.initialize();\n }\n },\n\n transform: async (chunk) => {\n // If not configured yet, buffer the chunk\n if (!this.isReady) {\n this.bufferedChunks.push(chunk);\n return; // Don't process yet\n }\n\n // If we're processing buffered chunks, add to buffer to maintain order\n if (this.isProcessingBuffer) {\n this.bufferedChunks.push(chunk);\n return;\n }\n\n // Normal processing\n await this.processChunk(chunk);\n },\n\n flush: async () => {\n if (this.isReady) {\n await this.flush();\n }\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: () => 1,\n }\n );\n }\n\n /**\n * Process a single chunk (extracted from transform for reuse)\n */\n private async processChunk(chunk: EncodedAudioChunk): Promise<void> {\n if (!this.decoder) {\n throw new Error('Decoder not initialized');\n }\n\n if (this.decoder.state !== 'configured') {\n console.error('[AudioChunkDecoder] Decoder in unexpected state:', this.decoder.state);\n throw new Error(`Decoder not configured, state: ${this.decoder.state}`);\n }\n\n this.decode(chunk);\n }\n\n // Implement abstract methods\n protected async isConfigSupported(config: AudioDecoderConfig): Promise<{ supported: boolean }> {\n const result = await AudioDecoder.isConfigSupported({\n codec: config.codec,\n sampleRate: config.sampleRate,\n numberOfChannels: config.numberOfChannels,\n });\n return { supported: result.supported ?? false };\n }\n\n protected createDecoder(init: {\n output: (data: AudioData) => void;\n error: (error: DOMException) => void;\n }): AudioDecoder {\n return new AudioDecoder(init);\n }\n\n protected getDecoderType(): string {\n return 'Audio';\n }\n\n protected async configureDecoder(config: AudioDecoderConfig): Promise<void> {\n if (!this.decoder) return;\n\n await this.decoder.configure({\n codec: config.codec,\n sampleRate: config.sampleRate,\n numberOfChannels: config.numberOfChannels,\n ...(config.description && { description: config.description }),\n });\n }\n\n protected decode(chunk: EncodedAudioChunk): void {\n this.decoder?.decode(chunk);\n }\n\n /**\n * Configure the decoder with codec info (can be called after creation)\n */\n async configure(config: AudioDecoderConfig): Promise<void> {\n if (this.isReady) {\n // If already configured, reconfigure\n await this.reconfigure(config);\n return;\n }\n\n this.config = config as any;\n\n // Initialize decoder with new config\n await this.initialize();\n }\n\n /**\n * Process any buffered chunks after configuration\n */\n async processBufferedChunks(): Promise<void> {\n if (!this.isReady || this.bufferedChunks.length === 0) {\n return;\n }\n\n this.isProcessingBuffer = true;\n\n // Process buffered chunks in order\n const chunks = [...this.bufferedChunks];\n this.bufferedChunks = [];\n\n for (const chunk of chunks) {\n try {\n await this.processChunk(chunk);\n } catch (error) {\n console.error('[AudioChunkDecoder] Error processing buffered chunk:', error);\n }\n }\n\n this.isProcessingBuffer = false;\n\n // Process any new chunks that arrived while processing buffer\n if (this.bufferedChunks.length > 0) {\n await this.processBufferedChunks();\n }\n }\n\n // Override close to clean up buffered chunks\n override async close(): Promise<void> {\n // Clear buffered chunks\n this.bufferedChunks = [];\n\n // Call parent close\n await super.close();\n }\n}\n","import { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { AudioChunkDecoder } from './AudioChunkDecoder';\nimport { AudioDecoderConfig } from './types';\nimport type { AudioTrackConfig } from '../compose/types';\n\ninterface TrackMetadata {\n clipId: string;\n config: AudioTrackConfig;\n sampleRate?: number;\n numberOfChannels?: number;\n type: 'bgm' | 'voice' | 'sfx' | 'other';\n}\n\nconst normalizeDescription = (desc?: ArrayBuffer | ArrayBufferView): ArrayBuffer | undefined => {\n if (!desc) return undefined;\n\n if (desc instanceof ArrayBuffer) return desc;\n\n const view = desc as ArrayBufferView;\n return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength) as ArrayBuffer;\n};\n\n/**\n * AudioDecodeWorker (Clip Local) - Decodes audio for a single clip\n * Receives encoded audio chunks from AudioDemuxWorker and outputs decoded audio data\n *\n * Pipeline: AudioDemuxWorker → AudioDecodeWorker → Main Thread (OfflineAudioContext)\n *\n * Features:\n * - Single clip, single AudioDecoder instance\n * - Outputs AudioData stream to main thread for mixing\n * - Stream-based processing with backpressure\n * - Lifecycle tied to clip pipeline\n */\nexport class AudioDecodeWorker {\n private channel: WorkerChannel;\n private decoder: AudioChunkDecoder | null = null;\n private clipId: string = '';\n private trackId: string = '';\n\n private defaultConfig: Partial<AudioDecoderConfig> = {};\n private trackMetadata: TrackMetadata | null = null;\n\n private upstreamPort: MessagePort | null = null;\n\n constructor() {\n this.channel = new WorkerChannel(self as any, {\n name: 'AudioDecodeWorker',\n timeout: 30000,\n });\n\n this.setupHandlers();\n }\n\n private setupHandlers(): void {\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n this.channel.registerHandler('connect', this.handleConnect.bind(this));\n this.channel.registerHandler('flush', this.handleFlush.bind(this));\n this.channel.registerHandler('reset', this.handleReset.bind(this));\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n }\n\n private async handleConnect(payload: {\n direction: 'upstream' | 'downstream';\n port: MessagePort;\n streamType?: 'audio';\n sessionId?: string;\n trackId?: string;\n clipStartUs?: number;\n clipDurationUs?: number;\n }): Promise<{ success: boolean }> {\n const { port, direction, sessionId, trackId } = payload;\n\n if (direction === 'upstream') {\n this.upstreamPort = port;\n this.clipId = sessionId || 'default';\n this.trackId = trackId || sessionId || 'default';\n\n const channel = new WorkerChannel(port, {\n name: 'Demux-AudioDecode',\n timeout: 30000,\n });\n\n channel.receiveStream((stream, metadata) => {\n this.handleReceiveStream(stream, {\n ...metadata,\n clipStartUs: payload.clipStartUs,\n clipDurationUs: payload.clipDurationUs,\n });\n });\n\n channel.registerHandler('configure', this.handleConfigure.bind(this));\n }\n\n return { success: true };\n }\n\n private async handleConfigure(payload: {\n config?: { audio?: Partial<AudioDecoderConfig> };\n sessionId?: string;\n streamType?: 'audio';\n codec?: string;\n sampleRate?: number;\n numberOfChannels?: number;\n description?: ArrayBuffer | Uint8Array;\n }): Promise<{ success: boolean }> {\n const { sessionId, streamType, codec, sampleRate, numberOfChannels, description, config } =\n payload;\n\n if (sessionId && streamType === 'audio') {\n // Save codec info to defaultConfig for decoder creation\n Object.assign(this.defaultConfig, {\n codec,\n sampleRate,\n numberOfChannels,\n description: normalizeDescription(description),\n });\n\n // Update existing decoder if present (will process buffered chunks)\n if (this.decoder) {\n await this.decoder.updateConfig({\n codec,\n sampleRate,\n numberOfChannels,\n description: normalizeDescription(description),\n });\n }\n\n return { success: true };\n }\n\n if (config?.audio) {\n Object.assign(this.defaultConfig, config.audio);\n\n if (this.decoder) {\n await this.decoder.updateConfig(config.audio);\n }\n }\n\n this.channel.state = WorkerState.Ready;\n\n return { success: true };\n }\n\n private async handleReceiveStream(\n stream: ReadableStream,\n metadata?: Record<string, any>\n ): Promise<void> {\n const sessionId = metadata?.sessionId || this.clipId;\n const trackId = metadata?.trackId || this.trackId;\n if (!this.decoder) {\n // Prefer metadata, fallback to defaultConfig\n // Note: If codec is not available, decoder will buffer chunks internally\n const decoderConfig = {\n ...this.defaultConfig,\n codec: metadata?.codec ?? this.defaultConfig.codec,\n sampleRate: metadata?.sampleRate ?? this.defaultConfig.sampleRate,\n numberOfChannels: metadata?.numberOfChannels ?? this.defaultConfig.numberOfChannels,\n description: normalizeDescription(metadata?.description) ?? this.defaultConfig.description,\n };\n\n this.decoder = new AudioChunkDecoder(trackId, decoderConfig);\n }\n\n this.trackMetadata = {\n clipId: this.clipId,\n config: this.extractTrackConfig(metadata?.runtimeConfig),\n sampleRate: metadata?.sampleRate,\n numberOfChannels: metadata?.numberOfChannels,\n type: (metadata?.trackType as TrackMetadata['type']) ?? 'other',\n };\n\n const transform = this.decoder.createStream();\n this.channel.sendStream(transform.readable as ReadableStream<AudioData>, {\n streamType: 'audio',\n sessionId,\n trackId,\n clipStartUs: metadata?.clipStartUs ?? 0,\n clipDurationUs: metadata?.clipDurationUs ?? 0,\n trackMetadata: this.trackMetadata,\n });\n\n stream\n .pipeTo(transform.writable)\n .catch((error) => console.error('[AudioDecodeWorker] Audio stream pipe error:', error));\n }\n\n private extractTrackConfig(config: any): AudioTrackConfig {\n return {\n startTimeUs: config?.startTimeUs ?? 0,\n durationUs: config?.durationUs,\n volume: config?.volume ?? 1,\n fadeIn: config?.fadeIn,\n fadeOut: config?.fadeOut,\n effects: config?.effects ?? [],\n duckingTag: config?.duckingTag,\n };\n }\n\n private async handleFlush(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.flush();\n }\n return { success: true };\n }\n\n private async handleReset(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.reset();\n }\n\n this.channel.notify('reset_complete', {\n type: 'audio',\n });\n\n return { success: true };\n }\n\n private async handleGetStats(): Promise<{ audio?: any }> {\n if (!this.decoder) {\n return {};\n }\n\n return {\n audio: {\n clipId: this.clipId,\n trackId: this.trackId,\n configured: this.decoder.isConfigured,\n queueSize: this.decoder.queueSize,\n state: this.decoder.state,\n },\n };\n }\n\n private async handleDispose(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.close();\n this.decoder = null;\n }\n\n this.upstreamPort?.close();\n this.upstreamPort = null;\n\n this.trackMetadata = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n}\n\nconst worker = new AudioDecodeWorker();\n\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null;\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.DjBEVvEA.js";
1
+ import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.DQK8rAab.js";
2
2
  import { B as BaseDecoder } from "../../BaseDecoder.CB5XmTpS.js";
3
3
  let cachedPlatformInfo = null;
4
4
  function detectPlatform() {
@@ -331,4 +331,4 @@ export {
331
331
  VideoDecodeWorker,
332
332
  videoDecode_worker as default
333
333
  };
334
- //# sourceMappingURL=video-decode.worker.BQtw6eWn.js.map
334
+ //# sourceMappingURL=video-decode.worker.BnWVUkng.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-decode.worker.BnWVUkng.js","sources":["../../../../src/utils/platform-utils.ts","../../../../src/stages/decode/VideoChunkDecoder.ts","../../../../src/stages/decode/video-decode.worker.ts"],"sourcesContent":["/**\n * Platform detection utilities with caching for performance\n */\n\nexport interface PlatformInfo {\n isWindows: boolean;\n isMacOS: boolean;\n isLinux: boolean;\n platform: string;\n userAgent: string;\n}\n\n// Cache platform detection result (only detect once)\nlet cachedPlatformInfo: PlatformInfo | null = null;\n\n/**\n * Detect current platform (cached after first call)\n */\nexport function detectPlatform(): PlatformInfo {\n if (cachedPlatformInfo) {\n return cachedPlatformInfo;\n }\n\n const platform = typeof navigator !== 'undefined' ? navigator.platform : '';\n const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n\n cachedPlatformInfo = {\n isWindows: /Win/i.test(platform) || /Win/i.test(userAgent),\n isMacOS: /Mac/i.test(platform),\n isLinux: /Linux/i.test(platform),\n platform,\n userAgent,\n };\n\n return cachedPlatformInfo;\n}\n\n/**\n * Check if current platform is Windows (cached)\n */\nexport function isWindows(): boolean {\n return detectPlatform().isWindows;\n}\n\n/**\n * Check if current platform is macOS (cached)\n */\nexport function isMacOS(): boolean {\n return detectPlatform().isMacOS;\n}\n\n/**\n * Check if current platform is Linux (cached)\n */\nexport function isLinux(): boolean {\n return detectPlatform().isLinux;\n}\n\n/**\n * Get platform-recommended hardware acceleration setting for video decoding\n *\n * Background:\n * Windows hardware video decoders (especially with DXVA2/D3D11) may hang indefinitely\n * when calling VideoDecoder.flush() in certain scenarios (frequent seeks, large GOPs).\n * This appears to be a driver/platform-specific issue affecting Chromium's WebCodecs.\n *\n * Related discussions:\n * - https://github.com/w3c/webcodecs/issues\n * - Observed in production on Windows 10/11 with various GPU vendors\n *\n * Workaround:\n * Use software decoding on Windows to avoid flush() hangs, with ~4x slower decode\n * but reliable operation. Other platforms use hardware acceleration by default.\n *\n * @returns Platform-recommended hardware acceleration setting\n */\nexport function getRecommendedHardwareAcceleration(): HardwareAcceleration {\n // Windows: prefer software to avoid flush hang in hardware decoders\n if (isWindows()) {\n return 'prefer-software';\n }\n\n // Other platforms: no preference (let browser choose)\n return 'no-preference';\n}\n\nexport interface BrowserCompatibility {\n webCodecsAvailable: boolean;\n audioWebCodecsAvailable: boolean;\n opfsAvailable: boolean;\n missingFeatures: string[];\n browserInfo: {\n name: string;\n version: string;\n recommended: string;\n };\n}\n\nlet cachedCompatibility: BrowserCompatibility | null = null;\n\n/**\n * Check browser compatibility for WebCodecs and OPFS (sync, < 1ms, cached)\n * Only checks API existence, no file operations\n */\nexport function checkBrowserCompatibility(): BrowserCompatibility {\n if (cachedCompatibility) return cachedCompatibility;\n\n const missingFeatures: string[] = [];\n let webCodecsAvailable = true;\n let audioWebCodecsAvailable = true;\n let opfsAvailable = false;\n\n // Check WebCodecs APIs (sync, < 1ms)\n if (typeof VideoDecoder === 'undefined') {\n missingFeatures.push('VideoDecoder');\n webCodecsAvailable = false;\n }\n if (typeof VideoEncoder === 'undefined') {\n missingFeatures.push('VideoEncoder');\n webCodecsAvailable = false;\n }\n if (typeof VideoFrame === 'undefined') {\n missingFeatures.push('VideoFrame');\n webCodecsAvailable = false;\n }\n if (typeof EncodedVideoChunk === 'undefined') {\n missingFeatures.push('EncodedVideoChunk');\n webCodecsAvailable = false;\n }\n\n // Check Audio WebCodecs APIs (sync, < 1ms)\n if (typeof AudioDecoder === 'undefined') {\n missingFeatures.push('AudioDecoder');\n audioWebCodecsAvailable = false;\n }\n if (typeof AudioEncoder === 'undefined') {\n missingFeatures.push('AudioEncoder');\n audioWebCodecsAvailable = false;\n }\n if (typeof AudioData === 'undefined') {\n missingFeatures.push('AudioData');\n audioWebCodecsAvailable = false;\n }\n if (typeof EncodedAudioChunk === 'undefined') {\n missingFeatures.push('EncodedAudioChunk');\n audioWebCodecsAvailable = false;\n }\n\n // Detect browser (sync, < 1ms)\n const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n let browserName = 'Unknown';\n let browserVersion = 'Unknown';\n let recommended = 'Chrome 94+ / Edge 94+ / Safari 26+';\n\n if (userAgent.includes('Edg/')) {\n browserName = 'Edge';\n const match = userAgent.match(/Edg\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Edge 94+';\n } else if (userAgent.includes('Chrome/')) {\n browserName = 'Chrome';\n const match = userAgent.match(/Chrome\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Chrome 94+';\n } else if (userAgent.includes('Safari/') && !userAgent.includes('Chrome')) {\n browserName = 'Safari';\n const match = userAgent.match(/Version\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Safari 26+';\n } else if (userAgent.includes('Firefox/')) {\n browserName = 'Firefox';\n const match = userAgent.match(/Firefox\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Not supported';\n }\n\n // Check OPFS API (sync, no file operations)\n if (webCodecsAvailable) {\n if (\n typeof navigator !== 'undefined' &&\n navigator.storage &&\n typeof navigator.storage.getDirectory === 'function'\n ) {\n // Assume support if the API exists (no file operations performed here).\n opfsAvailable = true;\n } else {\n missingFeatures.push('OPFS');\n }\n }\n\n cachedCompatibility = {\n webCodecsAvailable,\n audioWebCodecsAvailable,\n opfsAvailable,\n missingFeatures,\n browserInfo: { name: browserName, version: browserVersion, recommended },\n };\n\n return cachedCompatibility;\n}\n","import { VideoDecoderConfig } from './types';\nimport { BaseDecoder } from './BaseDecoder';\nimport { getRecommendedHardwareAcceleration } from '../../utils/platform-utils';\n\n/**\n * Video decoder with GOP tracking\n * Tracks keyframe boundaries and attaches GOP metadata to decoded frames\n */\nexport class VideoChunkDecoder extends BaseDecoder<\n VideoDecoder,\n VideoDecoderConfig,\n EncodedVideoChunk,\n VideoFrame\n> {\n private static readonly DEFAULT_HIGH_WATER_MARK = 4;\n private static readonly DEFAULT_DECODE_QUEUE_THRESHOLD = 16;\n\n readonly trackId: string;\n\n protected readonly highWaterMark: number;\n protected readonly decodeQueueThreshold: number;\n\n // Buffering support for delayed configuration\n private bufferedChunks: EncodedVideoChunk[] = [];\n private isProcessingBuffer: boolean = false;\n\n constructor(trackId: string, config?: Partial<VideoDecoderConfig>) {\n super((config || {}) as VideoDecoderConfig);\n\n this.trackId = trackId;\n this.highWaterMark =\n config?.backpressure?.highWaterMark ?? VideoChunkDecoder.DEFAULT_HIGH_WATER_MARK;\n this.decodeQueueThreshold =\n config?.backpressure?.decodeQueueThreshold ??\n VideoChunkDecoder.DEFAULT_DECODE_QUEUE_THRESHOLD;\n }\n\n // Computed properties\n get isConfigured(): boolean {\n return this.isReady;\n }\n\n get state(): string {\n return this.decoder?.state || 'unconfigured';\n }\n\n /**\n * Update configuration - can be called before or after initialization\n */\n async updateConfig(config: Partial<VideoDecoderConfig>): Promise<void> {\n if (!this.isReady && config.codec) {\n await this.configure(config as VideoDecoderConfig);\n await this.processBufferedChunks();\n }\n }\n\n // Override createStream to handle GOP tracking and buffering\n // Always create new stream for each clip (ReadableStreams can only be consumed once)\n override createStream(): TransformStream<EncodedVideoChunk, VideoFrame> {\n return new TransformStream<EncodedVideoChunk, VideoFrame>(\n {\n start: async (controller) => {\n this.controller = controller;\n // Don't initialize if no codec config yet\n if (this.config?.codec && this.config?.description && !this.isReady) {\n await this.initialize();\n }\n },\n\n transform: async (chunk) => {\n // If not configured yet, buffer the chunk\n if (!this.isReady) {\n this.bufferedChunks.push(chunk);\n return; // Don't process yet\n }\n\n // If we're processing buffered chunks, add to buffer to maintain order\n if (this.isProcessingBuffer) {\n this.bufferedChunks.push(chunk);\n return;\n }\n\n // Normal processing\n await this.processChunk(chunk);\n },\n\n flush: async () => {\n if (this.isReady) {\n await this.flush();\n }\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: () => 1,\n }\n );\n }\n\n /**\n * Process a single chunk (extracted from transform for reuse)\n */\n private async processChunk(chunk: EncodedVideoChunk): Promise<void> {\n if (!this.decoder) {\n throw new Error('Decoder not initialized');\n }\n\n if (this.decoder.state !== 'configured') {\n console.error('[VideoChunkDecoder] Decoder in unexpected state:', this.decoder.state);\n throw new Error(`Decoder not configured, state: ${this.decoder.state}`);\n }\n\n this.decode(chunk);\n }\n\n // Override handleOutput to enqueue decoded frames\n protected override handleOutput(frame: VideoFrame): void {\n super.handleOutput(frame);\n }\n\n // Implement abstract methods\n protected async isConfigSupported(config: VideoDecoderConfig): Promise<{ supported: boolean }> {\n const result = await VideoDecoder.isConfigSupported({\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n });\n return { supported: result.supported ?? false };\n }\n\n protected createDecoder(init: {\n output: (frame: VideoFrame) => void;\n error: (error: DOMException) => void;\n }): VideoDecoder {\n return new VideoDecoder(init);\n }\n\n protected getDecoderType(): string {\n return 'Video';\n }\n\n protected async configureDecoder(config: VideoDecoderConfig): Promise<void> {\n if (!this.decoder) return;\n\n const hardwareAcceleration =\n config.hardwareAcceleration ?? getRecommendedHardwareAcceleration();\n\n const decoderConfig = {\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n hardwareAcceleration,\n optimizeForLatency: true,\n ...(config.description && { description: config.description }),\n ...(config.displayAspectWidth && { displayAspectWidth: config.displayAspectWidth }),\n ...(config.displayAspectHeight && { displayAspectHeight: config.displayAspectHeight }),\n };\n\n this.decoder.configure(decoderConfig as any);\n\n // Log when using software decoding for debugging\n if (hardwareAcceleration === 'prefer-software') {\n console.info('[VideoChunkDecoder] Using software decoding for platform compatibility');\n }\n }\n\n protected decode(chunk: EncodedVideoChunk): void {\n this.decoder?.decode(chunk);\n }\n\n /**\n * Configure the decoder with codec info (can be called after creation)\n */\n async configure(config: VideoDecoderConfig): Promise<void> {\n // console.log('[VideoChunkDecoder] Configuring with:', config);\n\n if (this.isReady) {\n // If already configured, reconfigure\n await this.reconfigure(config);\n return;\n }\n\n this.config = config as any;\n\n // Initialize decoder with new config\n await this.initialize();\n }\n\n /**\n * Process any buffered chunks after configuration\n */\n async processBufferedChunks(): Promise<void> {\n if (!this.isReady || this.bufferedChunks.length === 0) {\n return;\n }\n\n // console.log('[VideoChunkDecoder] Processing', this.bufferedChunks.length, 'buffered chunks');\n this.isProcessingBuffer = true;\n\n // Process buffered chunks in order\n const chunks = [...this.bufferedChunks];\n this.bufferedChunks = [];\n\n for (const chunk of chunks) {\n try {\n await this.processChunk(chunk);\n } catch (error) {\n console.error('[VideoChunkDecoder] Error processing buffered chunk:', error);\n }\n }\n\n this.isProcessingBuffer = false;\n\n // Process any new chunks that arrived while processing buffer\n if (this.bufferedChunks.length > 0) {\n await this.processBufferedChunks();\n }\n }\n\n // Override close to clean up buffered chunks\n override async close(): Promise<void> {\n // Clear buffered chunks\n this.bufferedChunks = [];\n\n // Call parent close\n await super.close();\n }\n}\n","/**\n * @deprecated VideoDecodeWorker is deprecated and will be removed in a future version.\n *\n * Reason: Export pipeline now uses OnDemandVideoSession (main thread) for decoding.\n * This eliminates the need for EncodedVideoChunk serialization and provides better\n * code reuse with preview's decoding logic.\n *\n * Replacement: OnDemandVideoSession (packages/core/src/orchestrator/OnDemandVideoSession.ts)\n *\n * New pipeline: IndexedVideoSource → OnDemandVideoSession (decode) → ComposeWorker → EncodeWorker\n * All in main thread before ComposeWorker, avoiding worker communication overhead.\n *\n * This file is kept for backward compatibility only and may be removed in the future.\n */\n\nimport { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { VideoChunkDecoder } from './VideoChunkDecoder';\nimport { VideoDecoderConfig } from './types';\n\nconst normalizeDescription = (desc?: ArrayBuffer | ArrayBufferView): ArrayBuffer | undefined => {\n if (!desc) return undefined;\n\n if (desc instanceof ArrayBuffer) return desc;\n\n const view = desc as ArrayBufferView;\n return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength) as ArrayBuffer;\n};\n\n/**\n * VideoDecodeWorker (Clip Local) - Decodes video for a single clip\n * Receives encoded video chunks from VideoDemuxWorker and outputs decoded frames\n *\n * Pipeline: VideoDemuxWorker → VideoDecodeWorker → VideoComposeWorker\n *\n * Features:\n * - Single clip, single VideoDecoder instance (no routing)\n * - GOP-based decoding with metadata\n * - Stream-based processing with backpressure\n * - Lifecycle tied to clip pipeline\n */\nexport class VideoDecodeWorker {\n private channel: WorkerChannel;\n private decoder: VideoChunkDecoder | null = null;\n private clipId: string = '';\n\n private defaultConfig: Partial<VideoDecoderConfig> = {};\n\n private upstreamPort: MessagePort | null = null;\n private downstreamPort: MessagePort | null = null;\n\n constructor() {\n this.channel = new WorkerChannel(self as any, {\n name: 'VideoDecodeWorker',\n timeout: 30000,\n });\n\n this.setupHandlers();\n }\n\n private setupHandlers(): void {\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n this.channel.registerHandler('connect', this.handleConnect.bind(this));\n this.channel.registerHandler('flush', this.handleFlush.bind(this));\n this.channel.registerHandler('reset', this.handleReset.bind(this));\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n }\n\n private async handleConnect(payload: {\n direction: 'upstream' | 'downstream';\n port: MessagePort;\n streamType?: 'video';\n sessionId?: string;\n clipStartUs?: number;\n clipDurationUs?: number;\n }): Promise<{ success: boolean }> {\n const { port, direction, sessionId } = payload;\n\n if (direction === 'upstream') {\n this.upstreamPort = port;\n this.clipId = sessionId || 'default';\n\n const channel = new WorkerChannel(port, {\n name: 'Demux-VideoDecode',\n timeout: 30000,\n });\n\n channel.receiveStream((stream, metadata) => {\n this.handleReceiveStream(stream, {\n ...metadata,\n clipStartUs: payload.clipStartUs,\n clipDurationUs: payload.clipDurationUs,\n });\n });\n\n channel.registerHandler('configure', this.handleConfigure.bind(this));\n }\n\n if (direction === 'downstream') {\n this.downstreamPort = port;\n }\n\n return { success: true };\n }\n\n private async handleConfigure(payload: {\n config?: { video?: Partial<VideoDecoderConfig> };\n sessionId?: string;\n streamType?: 'video';\n codec?: string;\n width?: number;\n height?: number;\n description?: ArrayBuffer | Uint8Array;\n }): Promise<{ success: boolean }> {\n const { sessionId, streamType, codec, width, height, description, config } = payload;\n\n if (sessionId && streamType === 'video') {\n if (this.decoder) {\n await this.decoder.updateConfig({\n codec,\n width,\n height,\n description: normalizeDescription(description),\n });\n }\n return { success: true };\n }\n\n if (config?.video) {\n Object.assign(this.defaultConfig, config.video);\n\n if (this.decoder) {\n await this.decoder.updateConfig(config.video);\n }\n }\n\n this.channel.state = WorkerState.Ready;\n\n return { success: true };\n }\n\n private async handleReceiveStream(\n stream: ReadableStream,\n metadata?: Record<string, any>\n ): Promise<void> {\n const sessionId = metadata?.sessionId || this.clipId;\n\n if (!this.decoder) {\n this.decoder = new VideoChunkDecoder(sessionId, {\n ...this.defaultConfig,\n codec: metadata?.codec,\n width: metadata?.width,\n height: metadata?.height,\n description: normalizeDescription(metadata?.description),\n });\n }\n\n const transform = this.decoder.createStream();\n\n if (this.downstreamPort) {\n const channel = new WorkerChannel(this.downstreamPort, {\n name: 'VideoDecode-Compose',\n timeout: 30000,\n });\n\n channel.sendStream(transform.readable as ReadableStream, {\n streamType: 'video',\n sessionId,\n });\n\n stream\n .pipeTo(transform.writable)\n .catch((error) =>\n console.error('[VideoDecodeWorker] Video stream pipe error:', sessionId, error)\n );\n }\n }\n\n private async handleFlush(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.flush();\n }\n return { success: true };\n }\n\n private async handleReset(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.reset();\n }\n\n this.channel.notify('reset_complete', {\n type: 'video',\n });\n\n return { success: true };\n }\n\n private async handleGetStats(): Promise<{ video?: any }> {\n if (!this.decoder) {\n return {};\n }\n\n return {\n video: {\n clipId: this.clipId,\n configured: this.decoder.isConfigured,\n queueSize: this.decoder.queueSize,\n state: this.decoder.state,\n },\n };\n }\n\n private async handleDispose(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.close();\n this.decoder = null;\n }\n\n this.upstreamPort?.close();\n this.upstreamPort = null;\n\n this.downstreamPort?.close();\n this.downstreamPort = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n}\n\nconst worker = new VideoDecodeWorker();\n\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null;\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.DjBEVvEA.js";
1
+ import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.DQK8rAab.js";
2
2
  import { M as MP4Demuxer } from "../../MP4Demuxer.DfWiwyjB.js";
3
3
  const BITRATE_TABLE = {
4
4
  "1-3": [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0],
@@ -499,4 +499,4 @@ export {
499
499
  AudioDemuxWorker,
500
500
  audioDemux_worker as default
501
501
  };
502
- //# sourceMappingURL=audio-demux.worker.C4V11GQi.js.map
502
+ //# sourceMappingURL=audio-demux.worker.D-_LoVqW.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"audio-demux.worker.C4V11GQi.js","sources":["../../../../src/stages/demux/MP3FrameParser.ts","../../../../src/stages/demux/audio-demux.worker.ts"],"sourcesContent":["const BITRATE_TABLE: Record<string, number[]> = {\n '1-3': [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0],\n '1-2': [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0],\n '1-1': [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0],\n '2-3': [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],\n '2-2': [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],\n '2-1': [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],\n};\n\nconst SAMPLE_RATE_TABLE: Record<number, number[]> = {\n 1: [44100, 48000, 32000],\n 2: [22050, 24000, 16000],\n 2.5: [11025, 12000, 8000],\n};\n\nexport interface MP3Config {\n codec: 'mp3';\n sampleRate: number;\n channels: number;\n bitrateKbps: number | null;\n samplesPerFrame: number;\n}\n\nexport interface MP3Frame {\n data: Uint8Array;\n timestampUs: number;\n durationUs: number;\n}\n\ninterface ParseResult {\n frames: MP3Frame[];\n config?: MP3Config;\n}\n\nexport class MP3FrameParser {\n private buffer = new Uint8Array(0);\n private config: MP3Config | null = null;\n private timestampUs = 0;\n\n push(chunk: Uint8Array): ParseResult {\n this.appendToBuffer(chunk);\n this.skipId3Headers();\n\n const frames: MP3Frame[] = [];\n let configEmitted: MP3Config | undefined;\n\n let offset = 0;\n while (offset <= this.buffer.length - 4) {\n const header = this.parseHeader(this.buffer, offset);\n if (!header) {\n offset += 1;\n continue;\n }\n\n if (offset + header.frameSize > this.buffer.length) {\n break;\n }\n\n const frameBytes = this.buffer.slice(offset, offset + header.frameSize);\n const durationUs = Math.round((header.samplesPerFrame * 1_000_000) / header.sampleRate);\n\n if (!this.config) {\n this.config = {\n codec: 'mp3',\n sampleRate: header.sampleRate,\n channels: header.channels,\n bitrateKbps: header.bitrateKbps,\n samplesPerFrame: header.samplesPerFrame,\n };\n configEmitted = this.config;\n }\n\n frames.push({\n data: frameBytes,\n timestampUs: this.timestampUs,\n durationUs,\n });\n\n this.timestampUs += durationUs;\n offset += header.frameSize;\n }\n\n if (offset > 0) {\n this.buffer = this.buffer.slice(offset);\n }\n\n return { frames, config: configEmitted };\n }\n\n flush(): MP3Frame[] {\n const { frames } = this.push(new Uint8Array(0));\n const remainder = this.buffer;\n this.buffer = new Uint8Array(0);\n if (remainder.length) {\n console.warn('[MP3FrameParser] Remaining unparsed bytes:', remainder.length);\n }\n return frames;\n }\n\n private appendToBuffer(chunk: Uint8Array): void {\n if (chunk.length === 0) {\n return;\n }\n const combined = new Uint8Array(this.buffer.length + chunk.length);\n combined.set(this.buffer, 0);\n combined.set(chunk, this.buffer.length);\n this.buffer = combined;\n }\n\n private skipId3Headers(): void {\n let offset = 0;\n while (this.buffer.length - offset >= 10) {\n if (\n this.buffer[offset] === 0x49 &&\n this.buffer[offset + 1] === 0x44 &&\n this.buffer[offset + 2] === 0x33\n ) {\n const size = this.readSynchsafeInteger(this.buffer.subarray(offset + 6, offset + 10));\n const total = size + 10;\n if (offset + total <= this.buffer.length) {\n offset += total;\n continue;\n }\n }\n break;\n }\n if (offset > 0) {\n this.buffer = this.buffer.slice(offset);\n }\n }\n\n private parseHeader(\n buffer: Uint8Array,\n offset: number\n ): {\n frameSize: number;\n sampleRate: number;\n channels: number;\n bitrateKbps: number | null;\n samplesPerFrame: number;\n } | null {\n if (offset + 3 >= buffer.length) {\n return null;\n }\n if (buffer[offset] !== 0xff || ((buffer[offset + 1] ?? 0) & 0xe0) !== 0xe0) {\n return null;\n }\n\n const versionBits = ((buffer[offset + 1] ?? 0) >> 3) & 0x03;\n const layerBits = ((buffer[offset + 1] ?? 0) >> 1) & 0x03;\n const bitrateIndex = ((buffer[offset + 2] ?? 0) >> 4) & 0x0f;\n const sampleRateIndex = ((buffer[offset + 2] ?? 0) >> 2) & 0x03;\n const paddingBit = ((buffer[offset + 2] ?? 0) >> 1) & 0x01;\n const channelMode = ((buffer[offset + 3] ?? 0) >> 6) & 0x03;\n\n const version = this.getVersion(versionBits);\n const layer = this.getLayer(layerBits);\n if (!version || !layer) {\n return null;\n }\n\n const sampleRate = SAMPLE_RATE_TABLE[version]?.[sampleRateIndex] ?? null;\n if (!sampleRate) {\n return null;\n }\n\n const bitrateKey = `${version === 1 ? 1 : 2}-${layer}`;\n const bitrateKbps = BITRATE_TABLE[bitrateKey]?.[bitrateIndex] ?? null;\n if (bitrateKbps === null) {\n return null;\n }\n\n const samplesPerFrame = this.getSamplesPerFrame(version, layer);\n const frameSize = this.calculateFrameSize(layer, bitrateKbps, sampleRate, paddingBit);\n if (!frameSize || frameSize < 24) {\n return null;\n }\n\n const channels = channelMode === 3 ? 1 : 2;\n\n return {\n frameSize,\n sampleRate,\n channels,\n bitrateKbps: bitrateKbps || null,\n samplesPerFrame,\n };\n }\n\n private getVersion(bits: number): 1 | 2 | 2.5 | null {\n switch (bits) {\n case 0b11:\n return 1;\n case 0b10:\n return 2;\n case 0b00:\n return 2.5;\n default:\n return null;\n }\n }\n\n private getLayer(bits: number): 1 | 2 | 3 | null {\n switch (bits) {\n case 0b11:\n return 1;\n case 0b10:\n return 2;\n case 0b01:\n return 3;\n default:\n return null;\n }\n }\n\n private getSamplesPerFrame(version: 1 | 2 | 2.5, layer: 1 | 2 | 3): number {\n if (layer === 1) {\n return 384;\n }\n if (layer === 2) {\n return 1152;\n }\n return version === 1 ? 1152 : 576;\n }\n\n private calculateFrameSize(\n layer: 1 | 2 | 3,\n bitrateKbps: number,\n sampleRate: number,\n padding: number\n ): number {\n if (bitrateKbps <= 0) {\n return 0;\n }\n\n if (layer === 1) {\n return ((12 * bitrateKbps * 1000) / sampleRate + padding) * 4;\n }\n\n return Math.floor((144 * bitrateKbps * 1000) / sampleRate + padding);\n }\n\n private readSynchsafeInteger(bytes: Uint8Array): number {\n if (bytes.length !== 4) {\n return 0;\n }\n return (\n (((bytes[0] ?? 0) & 0x7f) << 21) |\n (((bytes[1] ?? 0) & 0x7f) << 14) |\n (((bytes[2] ?? 0) & 0x7f) << 7) |\n ((bytes[3] ?? 0) & 0x7f)\n );\n }\n}\n","import { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { MP4Demuxer } from './MP4Demuxer';\nimport { MP3FrameParser, type MP3Config, type MP3Frame } from './MP3FrameParser';\nimport type { DemuxConfig } from './types';\n\n/**\n * AudioDemuxWorker - First stage for audio processing\n * Extracts audio tracks from various container formats (MP3, MP4/M4A, etc.)\n *\n * Pipeline: ResourceLoader (Main Thread) → AudioDemuxWorker → DecodeWorker\n *\n * Features:\n * - Multi-format support (MP3, AAC in MP4/M4A)\n * - Stream-based processing with backpressure\n * - Direct streaming to DecodeWorker\n */\nexport class AudioDemuxWorker {\n private channel: WorkerChannel;\n private demuxer: MP4Demuxer | null = null;\n private audioStream: TransformStream<Uint8Array, EncodedAudioChunk> | null = null;\n private mp3Parser: MP3FrameParser | null = null;\n private currentFormat: 'mp3' | 'mp4' | null = null;\n\n // Connection to decoder worker\n private decoderPort: MessagePort | null = null;\n\n constructor() {\n // Initialize WorkerChannel\n this.channel = new WorkerChannel(self as any, {\n name: 'AudioDemuxWorker',\n timeout: 30000,\n });\n\n this.setupHandlers();\n }\n\n private setupHandlers(): void {\n // Register message handlers\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n // Unified stream connect (feature-flagged)\n this.channel.registerHandler('connect' as any, this.handleConnect.bind(this));\n // Unified stream connect only\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n\n // Setup stream receiver from ResourceLoader (main thread)\n this.channel.receiveStream(this.handleReceiveStream.bind(this));\n }\n\n /**\n * Unified connect handler used by stream pipeline\n */\n private async handleConnect(payload: {\n direction: 'upstream';\n port: MessagePort;\n streamType: 'video' | 'audio' | 'frame' | 'chunk';\n }): Promise<{ success: boolean }> {\n this.decoderPort = payload.port;\n // Demux connects upstream to decoder\n return { success: true };\n }\n\n /**\n * Configure demuxer with format settings\n * @param payload.config - Demuxer configuration\n * @param payload.initial - If true, initialize worker state; otherwise just update config\n */\n private async handleConfigure(payload: {\n config: DemuxConfig;\n initial?: boolean;\n }): Promise<{ success: boolean; tracks?: any[] }> {\n const { config, initial = false } = payload;\n\n try {\n if (initial) {\n // Initial setup - set worker state to ready\n this.channel.state = WorkerState.Ready;\n\n // Detect format and create appropriate demuxer\n const format = config.container || this.detectFormat(config);\n\n if (format === 'mp3') {\n this.mp3Parser = new MP3FrameParser();\n this.demuxer?.destroy();\n this.demuxer = null;\n this.audioStream = null;\n this.currentFormat = 'mp3';\n\n const mp3Track = { id: 1, type: 'audio', codec: 'mp3' };\n this.channel.notify('configured', {\n tracks: [mp3Track],\n format,\n codec: 'mp3',\n });\n\n return { success: true, tracks: [mp3Track] };\n } else if (format === 'mp4' || format === 'm4a') {\n if (this.demuxer) {\n this.demuxer.destroy();\n }\n\n this.demuxer = new MP4Demuxer({\n ...config,\n skipVideo: true, // Audio only\n });\n this.audioStream = this.demuxer.createAudioStream();\n this.mp3Parser = null;\n this.currentFormat = 'mp4';\n } else {\n throw {\n code: 'UNSUPPORTED_FORMAT',\n message: `Unsupported audio format: ${format}`,\n };\n }\n\n if (!this.audioStream) {\n throw {\n code: 'NO_AUDIO_TRACK',\n message: 'No audio track found in container',\n };\n }\n\n const tracks = Array.from(this.demuxer.tracks.values());\n\n // Notify configuration complete\n this.channel.notify('configured', {\n tracks,\n format,\n codec: tracks[0]?.codec,\n });\n\n return { success: true, tracks };\n } else {\n // Update configuration only (e.g., backpressure settings)\n if (!this.demuxer) {\n throw {\n code: 'NOT_INITIALIZED',\n message: 'Demuxer not initialized. Call configure with initial=true first',\n };\n }\n\n // Demuxers don't support runtime config updates\n // Would need initial=true for changes\n\n return { success: true };\n }\n } catch (error: any) {\n throw {\n code: error.code || 'CONFIG_ERROR',\n message: error.message,\n };\n }\n }\n\n /**\n * Detect audio format from configuration\n */\n private detectFormat(config: DemuxConfig): string {\n // Simple format detection based on codec or file extension\n if (config.codec?.includes('mp3')) return 'mp3';\n if (config.codec?.includes('aac')) return 'mp4';\n if (config.codec?.includes('opus')) return 'webm';\n\n return 'mp3';\n }\n\n /**\n * Handle input stream from ResourceLoader (main thread)\n */\n private async handleReceiveStream(\n stream: ReadableStream<Uint8Array>,\n metadata?: Record<string, any>\n ): Promise<void> {\n // Initialize demuxer if not configured\n if (this.currentFormat === 'mp3' && !this.mp3Parser) {\n this.mp3Parser = new MP3FrameParser();\n }\n\n if (this.currentFormat === 'mp4' && (!this.demuxer || !this.audioStream)) {\n await this.handleConfigure({\n config: { container: 'mp4', highWaterMark: 10 },\n initial: true,\n }).catch((error) => {\n console.error('[AudioDemuxWorker] Configure error:', metadata?.sessionId, error);\n return;\n });\n }\n\n if (!this.decoderPort) {\n console.error('[AudioDemuxWorker] Decoder not connected');\n return;\n }\n\n // Setup channel to decoder\n const decoderChannel = new WorkerChannel(this.decoderPort, {\n name: 'AudioDemux-Decoder',\n timeout: 30000,\n });\n\n if (this.currentFormat === 'mp3') {\n await this.pipeMp3Stream(stream, decoderChannel, metadata).catch((error) => {\n console.error('[AudioDemuxWorker] MP3 stream error:', metadata?.sessionId, error);\n });\n return;\n }\n\n if (!this.demuxer || !this.audioStream) {\n console.error('[AudioDemuxWorker] Audio demuxer not initialized');\n return;\n }\n\n const transformed = stream.pipeThrough(this.audioStream);\n const trackInfo = Array.from(this.demuxer.tracks.values())[0];\n\n await decoderChannel\n .sendStream(transformed, {\n streamType: 'audio',\n sessionId: metadata?.sessionId ?? 'default',\n trackId: metadata?.trackId ?? 'main',\n codec: trackInfo?.codec ?? 'mp4a.40.2',\n sampleRate: trackInfo?.sampleRate ?? 44_100,\n numberOfChannels: trackInfo?.numberOfChannels ?? 2,\n description: trackInfo?.description,\n })\n .catch((error) => {\n console.error('[AudioDemuxWorker] Send stream error:', metadata?.sessionId, error);\n });\n\n this.channel.notify('demux_complete', {\n tracksProcessed: this.demuxer?.tracks.size || 0,\n });\n }\n\n private async pipeMp3Stream(\n stream: ReadableStream<Uint8Array>,\n decoderChannel: WorkerChannel,\n metadata?: Record<string, any>\n ): Promise<void> {\n if (!this.mp3Parser) {\n this.mp3Parser = new MP3FrameParser();\n }\n\n const reader = stream.getReader();\n let currentConfig: MP3Config | null = null;\n let configured = false;\n const bufferedFrames: MP3Frame[] = [];\n let writer: WritableStreamDefaultWriter<EncodedAudioChunk> | null = null;\n\n const ensureWriter = async (): Promise<WritableStreamDefaultWriter<EncodedAudioChunk>> => {\n if (!writer) {\n const cfg = currentConfig;\n if (!cfg) {\n throw new Error('MP3 config missing while creating writer');\n }\n const transform = new TransformStream<EncodedAudioChunk, EncodedAudioChunk>();\n writer = transform.writable.getWriter();\n await decoderChannel.sendStream(transform.readable, {\n streamType: 'audio',\n sessionId: metadata?.sessionId ?? 'default',\n trackId: metadata?.trackId ?? 'main',\n codec: 'mp3',\n sampleRate: cfg.sampleRate,\n numberOfChannels: cfg.channels,\n description: undefined,\n });\n }\n return writer!;\n };\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n if (!value) {\n continue;\n }\n\n const { frames, config } = this.mp3Parser.push(value);\n\n if (config) {\n currentConfig = config;\n }\n\n if (config && !configured) {\n await decoderChannel.send('configure' as any, {\n streamType: 'audio',\n clipId: metadata?.clipId ?? 'default',\n codec: 'mp3',\n sampleRate: config.sampleRate,\n numberOfChannels: config.channels,\n description: undefined,\n });\n this.channel.notify('demux_configured', {\n clipId: metadata?.clipId ?? 'default',\n codec: 'mp3',\n sampleRate: config.sampleRate,\n numberOfChannels: config.channels,\n });\n configured = true;\n }\n\n if (config) {\n this.currentFormat = 'mp3';\n }\n\n if (!configured) {\n bufferedFrames.push(...frames);\n continue;\n }\n\n const targetWriter = await ensureWriter();\n const readyFrames = bufferedFrames.splice(0, bufferedFrames.length);\n readyFrames.push(...frames);\n for (const frame of readyFrames) {\n const chunk = new EncodedAudioChunk({\n type: 'key',\n timestamp: frame.timestampUs,\n duration: frame.durationUs,\n data: frame.data.buffer.slice(\n frame.data.byteOffset,\n frame.data.byteOffset + frame.data.byteLength\n ),\n });\n await targetWriter.write(chunk);\n }\n }\n\n const remaining = this.mp3Parser.flush();\n if (remaining.length) {\n if (!configured) {\n throw new Error('MP3 stream ended before configuration');\n }\n const targetWriter = await ensureWriter();\n for (const frame of remaining) {\n const chunk = new EncodedAudioChunk({\n type: 'key',\n timestamp: frame.timestampUs,\n duration: frame.durationUs,\n data: frame.data.buffer.slice(\n frame.data.byteOffset,\n frame.data.byteOffset + frame.data.byteLength\n ),\n });\n await targetWriter.write(chunk);\n }\n }\n\n if (writer) {\n await (writer as WritableStreamDefaultWriter<EncodedAudioChunk>).close();\n }\n\n this.channel.notify('demux_complete', {\n tracksProcessed: 1,\n });\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Get demuxer statistics\n */\n private async handleGetStats(): Promise<{\n queueSize?: number;\n tracksInfo?: any[];\n format?: string;\n state?: WorkerState;\n }> {\n if (this.currentFormat === 'mp3') {\n return {\n tracksInfo: this.mp3Parser ? [{ id: 1, type: 'audio', codec: 'mp3' }] : [],\n format: 'mp3',\n state: this.channel.state,\n };\n }\n\n if (this.demuxer) {\n return {\n tracksInfo: Array.from(this.demuxer.tracks.values()),\n format: 'mp4',\n state: this.channel.state,\n };\n }\n\n return { state: this.channel.state };\n }\n\n /**\n * Dispose worker and cleanup resources\n */\n private async handleDispose(): Promise<{ success: boolean }> {\n if (this.demuxer) {\n this.demuxer.destroy();\n }\n this.demuxer = null;\n this.audioStream = null;\n this.mp3Parser = null;\n this.currentFormat = null;\n\n this.decoderPort?.close();\n this.decoderPort = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n}\n\n// Initialize worker\nconst worker = new AudioDemuxWorker();\n\n// Handle worker termination\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null; // Required for TypeScript worker compilation\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"audio-demux.worker.D-_LoVqW.js","sources":["../../../../src/stages/demux/MP3FrameParser.ts","../../../../src/stages/demux/audio-demux.worker.ts"],"sourcesContent":["const BITRATE_TABLE: Record<string, number[]> = {\n '1-3': [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0],\n '1-2': [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0],\n '1-1': [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0],\n '2-3': [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],\n '2-2': [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],\n '2-1': [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],\n};\n\nconst SAMPLE_RATE_TABLE: Record<number, number[]> = {\n 1: [44100, 48000, 32000],\n 2: [22050, 24000, 16000],\n 2.5: [11025, 12000, 8000],\n};\n\nexport interface MP3Config {\n codec: 'mp3';\n sampleRate: number;\n channels: number;\n bitrateKbps: number | null;\n samplesPerFrame: number;\n}\n\nexport interface MP3Frame {\n data: Uint8Array;\n timestampUs: number;\n durationUs: number;\n}\n\ninterface ParseResult {\n frames: MP3Frame[];\n config?: MP3Config;\n}\n\nexport class MP3FrameParser {\n private buffer = new Uint8Array(0);\n private config: MP3Config | null = null;\n private timestampUs = 0;\n\n push(chunk: Uint8Array): ParseResult {\n this.appendToBuffer(chunk);\n this.skipId3Headers();\n\n const frames: MP3Frame[] = [];\n let configEmitted: MP3Config | undefined;\n\n let offset = 0;\n while (offset <= this.buffer.length - 4) {\n const header = this.parseHeader(this.buffer, offset);\n if (!header) {\n offset += 1;\n continue;\n }\n\n if (offset + header.frameSize > this.buffer.length) {\n break;\n }\n\n const frameBytes = this.buffer.slice(offset, offset + header.frameSize);\n const durationUs = Math.round((header.samplesPerFrame * 1_000_000) / header.sampleRate);\n\n if (!this.config) {\n this.config = {\n codec: 'mp3',\n sampleRate: header.sampleRate,\n channels: header.channels,\n bitrateKbps: header.bitrateKbps,\n samplesPerFrame: header.samplesPerFrame,\n };\n configEmitted = this.config;\n }\n\n frames.push({\n data: frameBytes,\n timestampUs: this.timestampUs,\n durationUs,\n });\n\n this.timestampUs += durationUs;\n offset += header.frameSize;\n }\n\n if (offset > 0) {\n this.buffer = this.buffer.slice(offset);\n }\n\n return { frames, config: configEmitted };\n }\n\n flush(): MP3Frame[] {\n const { frames } = this.push(new Uint8Array(0));\n const remainder = this.buffer;\n this.buffer = new Uint8Array(0);\n if (remainder.length) {\n console.warn('[MP3FrameParser] Remaining unparsed bytes:', remainder.length);\n }\n return frames;\n }\n\n private appendToBuffer(chunk: Uint8Array): void {\n if (chunk.length === 0) {\n return;\n }\n const combined = new Uint8Array(this.buffer.length + chunk.length);\n combined.set(this.buffer, 0);\n combined.set(chunk, this.buffer.length);\n this.buffer = combined;\n }\n\n private skipId3Headers(): void {\n let offset = 0;\n while (this.buffer.length - offset >= 10) {\n if (\n this.buffer[offset] === 0x49 &&\n this.buffer[offset + 1] === 0x44 &&\n this.buffer[offset + 2] === 0x33\n ) {\n const size = this.readSynchsafeInteger(this.buffer.subarray(offset + 6, offset + 10));\n const total = size + 10;\n if (offset + total <= this.buffer.length) {\n offset += total;\n continue;\n }\n }\n break;\n }\n if (offset > 0) {\n this.buffer = this.buffer.slice(offset);\n }\n }\n\n private parseHeader(\n buffer: Uint8Array,\n offset: number\n ): {\n frameSize: number;\n sampleRate: number;\n channels: number;\n bitrateKbps: number | null;\n samplesPerFrame: number;\n } | null {\n if (offset + 3 >= buffer.length) {\n return null;\n }\n if (buffer[offset] !== 0xff || ((buffer[offset + 1] ?? 0) & 0xe0) !== 0xe0) {\n return null;\n }\n\n const versionBits = ((buffer[offset + 1] ?? 0) >> 3) & 0x03;\n const layerBits = ((buffer[offset + 1] ?? 0) >> 1) & 0x03;\n const bitrateIndex = ((buffer[offset + 2] ?? 0) >> 4) & 0x0f;\n const sampleRateIndex = ((buffer[offset + 2] ?? 0) >> 2) & 0x03;\n const paddingBit = ((buffer[offset + 2] ?? 0) >> 1) & 0x01;\n const channelMode = ((buffer[offset + 3] ?? 0) >> 6) & 0x03;\n\n const version = this.getVersion(versionBits);\n const layer = this.getLayer(layerBits);\n if (!version || !layer) {\n return null;\n }\n\n const sampleRate = SAMPLE_RATE_TABLE[version]?.[sampleRateIndex] ?? null;\n if (!sampleRate) {\n return null;\n }\n\n const bitrateKey = `${version === 1 ? 1 : 2}-${layer}`;\n const bitrateKbps = BITRATE_TABLE[bitrateKey]?.[bitrateIndex] ?? null;\n if (bitrateKbps === null) {\n return null;\n }\n\n const samplesPerFrame = this.getSamplesPerFrame(version, layer);\n const frameSize = this.calculateFrameSize(layer, bitrateKbps, sampleRate, paddingBit);\n if (!frameSize || frameSize < 24) {\n return null;\n }\n\n const channels = channelMode === 3 ? 1 : 2;\n\n return {\n frameSize,\n sampleRate,\n channels,\n bitrateKbps: bitrateKbps || null,\n samplesPerFrame,\n };\n }\n\n private getVersion(bits: number): 1 | 2 | 2.5 | null {\n switch (bits) {\n case 0b11:\n return 1;\n case 0b10:\n return 2;\n case 0b00:\n return 2.5;\n default:\n return null;\n }\n }\n\n private getLayer(bits: number): 1 | 2 | 3 | null {\n switch (bits) {\n case 0b11:\n return 1;\n case 0b10:\n return 2;\n case 0b01:\n return 3;\n default:\n return null;\n }\n }\n\n private getSamplesPerFrame(version: 1 | 2 | 2.5, layer: 1 | 2 | 3): number {\n if (layer === 1) {\n return 384;\n }\n if (layer === 2) {\n return 1152;\n }\n return version === 1 ? 1152 : 576;\n }\n\n private calculateFrameSize(\n layer: 1 | 2 | 3,\n bitrateKbps: number,\n sampleRate: number,\n padding: number\n ): number {\n if (bitrateKbps <= 0) {\n return 0;\n }\n\n if (layer === 1) {\n return ((12 * bitrateKbps * 1000) / sampleRate + padding) * 4;\n }\n\n return Math.floor((144 * bitrateKbps * 1000) / sampleRate + padding);\n }\n\n private readSynchsafeInteger(bytes: Uint8Array): number {\n if (bytes.length !== 4) {\n return 0;\n }\n return (\n (((bytes[0] ?? 0) & 0x7f) << 21) |\n (((bytes[1] ?? 0) & 0x7f) << 14) |\n (((bytes[2] ?? 0) & 0x7f) << 7) |\n ((bytes[3] ?? 0) & 0x7f)\n );\n }\n}\n","import { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { MP4Demuxer } from './MP4Demuxer';\nimport { MP3FrameParser, type MP3Config, type MP3Frame } from './MP3FrameParser';\nimport type { DemuxConfig } from './types';\n\n/**\n * AudioDemuxWorker - First stage for audio processing\n * Extracts audio tracks from various container formats (MP3, MP4/M4A, etc.)\n *\n * Pipeline: ResourceLoader (Main Thread) → AudioDemuxWorker → DecodeWorker\n *\n * Features:\n * - Multi-format support (MP3, AAC in MP4/M4A)\n * - Stream-based processing with backpressure\n * - Direct streaming to DecodeWorker\n */\nexport class AudioDemuxWorker {\n private channel: WorkerChannel;\n private demuxer: MP4Demuxer | null = null;\n private audioStream: TransformStream<Uint8Array, EncodedAudioChunk> | null = null;\n private mp3Parser: MP3FrameParser | null = null;\n private currentFormat: 'mp3' | 'mp4' | null = null;\n\n // Connection to decoder worker\n private decoderPort: MessagePort | null = null;\n\n constructor() {\n // Initialize WorkerChannel\n this.channel = new WorkerChannel(self as any, {\n name: 'AudioDemuxWorker',\n timeout: 30000,\n });\n\n this.setupHandlers();\n }\n\n private setupHandlers(): void {\n // Register message handlers\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n // Unified stream connect (feature-flagged)\n this.channel.registerHandler('connect' as any, this.handleConnect.bind(this));\n // Unified stream connect only\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n\n // Setup stream receiver from ResourceLoader (main thread)\n this.channel.receiveStream(this.handleReceiveStream.bind(this));\n }\n\n /**\n * Unified connect handler used by stream pipeline\n */\n private async handleConnect(payload: {\n direction: 'upstream';\n port: MessagePort;\n streamType: 'video' | 'audio' | 'frame' | 'chunk';\n }): Promise<{ success: boolean }> {\n this.decoderPort = payload.port;\n // Demux connects upstream to decoder\n return { success: true };\n }\n\n /**\n * Configure demuxer with format settings\n * @param payload.config - Demuxer configuration\n * @param payload.initial - If true, initialize worker state; otherwise just update config\n */\n private async handleConfigure(payload: {\n config: DemuxConfig;\n initial?: boolean;\n }): Promise<{ success: boolean; tracks?: any[] }> {\n const { config, initial = false } = payload;\n\n try {\n if (initial) {\n // Initial setup - set worker state to ready\n this.channel.state = WorkerState.Ready;\n\n // Detect format and create appropriate demuxer\n const format = config.container || this.detectFormat(config);\n\n if (format === 'mp3') {\n this.mp3Parser = new MP3FrameParser();\n this.demuxer?.destroy();\n this.demuxer = null;\n this.audioStream = null;\n this.currentFormat = 'mp3';\n\n const mp3Track = { id: 1, type: 'audio', codec: 'mp3' };\n this.channel.notify('configured', {\n tracks: [mp3Track],\n format,\n codec: 'mp3',\n });\n\n return { success: true, tracks: [mp3Track] };\n } else if (format === 'mp4' || format === 'm4a') {\n if (this.demuxer) {\n this.demuxer.destroy();\n }\n\n this.demuxer = new MP4Demuxer({\n ...config,\n skipVideo: true, // Audio only\n });\n this.audioStream = this.demuxer.createAudioStream();\n this.mp3Parser = null;\n this.currentFormat = 'mp4';\n } else {\n throw {\n code: 'UNSUPPORTED_FORMAT',\n message: `Unsupported audio format: ${format}`,\n };\n }\n\n if (!this.audioStream) {\n throw {\n code: 'NO_AUDIO_TRACK',\n message: 'No audio track found in container',\n };\n }\n\n const tracks = Array.from(this.demuxer.tracks.values());\n\n // Notify configuration complete\n this.channel.notify('configured', {\n tracks,\n format,\n codec: tracks[0]?.codec,\n });\n\n return { success: true, tracks };\n } else {\n // Update configuration only (e.g., backpressure settings)\n if (!this.demuxer) {\n throw {\n code: 'NOT_INITIALIZED',\n message: 'Demuxer not initialized. Call configure with initial=true first',\n };\n }\n\n // Demuxers don't support runtime config updates\n // Would need initial=true for changes\n\n return { success: true };\n }\n } catch (error: any) {\n throw {\n code: error.code || 'CONFIG_ERROR',\n message: error.message,\n };\n }\n }\n\n /**\n * Detect audio format from configuration\n */\n private detectFormat(config: DemuxConfig): string {\n // Simple format detection based on codec or file extension\n if (config.codec?.includes('mp3')) return 'mp3';\n if (config.codec?.includes('aac')) return 'mp4';\n if (config.codec?.includes('opus')) return 'webm';\n\n return 'mp3';\n }\n\n /**\n * Handle input stream from ResourceLoader (main thread)\n */\n private async handleReceiveStream(\n stream: ReadableStream<Uint8Array>,\n metadata?: Record<string, any>\n ): Promise<void> {\n // Initialize demuxer if not configured\n if (this.currentFormat === 'mp3' && !this.mp3Parser) {\n this.mp3Parser = new MP3FrameParser();\n }\n\n if (this.currentFormat === 'mp4' && (!this.demuxer || !this.audioStream)) {\n await this.handleConfigure({\n config: { container: 'mp4', highWaterMark: 10 },\n initial: true,\n }).catch((error) => {\n console.error('[AudioDemuxWorker] Configure error:', metadata?.sessionId, error);\n return;\n });\n }\n\n if (!this.decoderPort) {\n console.error('[AudioDemuxWorker] Decoder not connected');\n return;\n }\n\n // Setup channel to decoder\n const decoderChannel = new WorkerChannel(this.decoderPort, {\n name: 'AudioDemux-Decoder',\n timeout: 30000,\n });\n\n if (this.currentFormat === 'mp3') {\n await this.pipeMp3Stream(stream, decoderChannel, metadata).catch((error) => {\n console.error('[AudioDemuxWorker] MP3 stream error:', metadata?.sessionId, error);\n });\n return;\n }\n\n if (!this.demuxer || !this.audioStream) {\n console.error('[AudioDemuxWorker] Audio demuxer not initialized');\n return;\n }\n\n const transformed = stream.pipeThrough(this.audioStream);\n const trackInfo = Array.from(this.demuxer.tracks.values())[0];\n\n await decoderChannel\n .sendStream(transformed, {\n streamType: 'audio',\n sessionId: metadata?.sessionId ?? 'default',\n trackId: metadata?.trackId ?? 'main',\n codec: trackInfo?.codec ?? 'mp4a.40.2',\n sampleRate: trackInfo?.sampleRate ?? 44_100,\n numberOfChannels: trackInfo?.numberOfChannels ?? 2,\n description: trackInfo?.description,\n })\n .catch((error) => {\n console.error('[AudioDemuxWorker] Send stream error:', metadata?.sessionId, error);\n });\n\n this.channel.notify('demux_complete', {\n tracksProcessed: this.demuxer?.tracks.size || 0,\n });\n }\n\n private async pipeMp3Stream(\n stream: ReadableStream<Uint8Array>,\n decoderChannel: WorkerChannel,\n metadata?: Record<string, any>\n ): Promise<void> {\n if (!this.mp3Parser) {\n this.mp3Parser = new MP3FrameParser();\n }\n\n const reader = stream.getReader();\n let currentConfig: MP3Config | null = null;\n let configured = false;\n const bufferedFrames: MP3Frame[] = [];\n let writer: WritableStreamDefaultWriter<EncodedAudioChunk> | null = null;\n\n const ensureWriter = async (): Promise<WritableStreamDefaultWriter<EncodedAudioChunk>> => {\n if (!writer) {\n const cfg = currentConfig;\n if (!cfg) {\n throw new Error('MP3 config missing while creating writer');\n }\n const transform = new TransformStream<EncodedAudioChunk, EncodedAudioChunk>();\n writer = transform.writable.getWriter();\n await decoderChannel.sendStream(transform.readable, {\n streamType: 'audio',\n sessionId: metadata?.sessionId ?? 'default',\n trackId: metadata?.trackId ?? 'main',\n codec: 'mp3',\n sampleRate: cfg.sampleRate,\n numberOfChannels: cfg.channels,\n description: undefined,\n });\n }\n return writer!;\n };\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n if (!value) {\n continue;\n }\n\n const { frames, config } = this.mp3Parser.push(value);\n\n if (config) {\n currentConfig = config;\n }\n\n if (config && !configured) {\n await decoderChannel.send('configure' as any, {\n streamType: 'audio',\n clipId: metadata?.clipId ?? 'default',\n codec: 'mp3',\n sampleRate: config.sampleRate,\n numberOfChannels: config.channels,\n description: undefined,\n });\n this.channel.notify('demux_configured', {\n clipId: metadata?.clipId ?? 'default',\n codec: 'mp3',\n sampleRate: config.sampleRate,\n numberOfChannels: config.channels,\n });\n configured = true;\n }\n\n if (config) {\n this.currentFormat = 'mp3';\n }\n\n if (!configured) {\n bufferedFrames.push(...frames);\n continue;\n }\n\n const targetWriter = await ensureWriter();\n const readyFrames = bufferedFrames.splice(0, bufferedFrames.length);\n readyFrames.push(...frames);\n for (const frame of readyFrames) {\n const chunk = new EncodedAudioChunk({\n type: 'key',\n timestamp: frame.timestampUs,\n duration: frame.durationUs,\n data: frame.data.buffer.slice(\n frame.data.byteOffset,\n frame.data.byteOffset + frame.data.byteLength\n ),\n });\n await targetWriter.write(chunk);\n }\n }\n\n const remaining = this.mp3Parser.flush();\n if (remaining.length) {\n if (!configured) {\n throw new Error('MP3 stream ended before configuration');\n }\n const targetWriter = await ensureWriter();\n for (const frame of remaining) {\n const chunk = new EncodedAudioChunk({\n type: 'key',\n timestamp: frame.timestampUs,\n duration: frame.durationUs,\n data: frame.data.buffer.slice(\n frame.data.byteOffset,\n frame.data.byteOffset + frame.data.byteLength\n ),\n });\n await targetWriter.write(chunk);\n }\n }\n\n if (writer) {\n await (writer as WritableStreamDefaultWriter<EncodedAudioChunk>).close();\n }\n\n this.channel.notify('demux_complete', {\n tracksProcessed: 1,\n });\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Get demuxer statistics\n */\n private async handleGetStats(): Promise<{\n queueSize?: number;\n tracksInfo?: any[];\n format?: string;\n state?: WorkerState;\n }> {\n if (this.currentFormat === 'mp3') {\n return {\n tracksInfo: this.mp3Parser ? [{ id: 1, type: 'audio', codec: 'mp3' }] : [],\n format: 'mp3',\n state: this.channel.state,\n };\n }\n\n if (this.demuxer) {\n return {\n tracksInfo: Array.from(this.demuxer.tracks.values()),\n format: 'mp4',\n state: this.channel.state,\n };\n }\n\n return { state: this.channel.state };\n }\n\n /**\n * Dispose worker and cleanup resources\n */\n private async handleDispose(): Promise<{ success: boolean }> {\n if (this.demuxer) {\n this.demuxer.destroy();\n }\n this.demuxer = null;\n this.audioStream = null;\n this.mp3Parser = null;\n this.currentFormat = null;\n\n this.decoderPort?.close();\n this.decoderPort = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n}\n\n// Initialize worker\nconst worker = new AudioDemuxWorker();\n\n// Handle worker termination\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null; // Required for TypeScript worker compilation\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.DjBEVvEA.js";
1
+ import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.DQK8rAab.js";
2
2
  import { M as MP4Demuxer } from "../../MP4Demuxer.DfWiwyjB.js";
3
3
  class VideoDemuxWorker {
4
4
  channel;
@@ -207,4 +207,4 @@ export {
207
207
  VideoDemuxWorker,
208
208
  videoDemux_worker as default
209
209
  };
210
- //# sourceMappingURL=video-demux.worker.5pJr0Ij-.js.map
210
+ //# sourceMappingURL=video-demux.worker.BWDrLGni.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"video-demux.worker.5pJr0Ij-.js","sources":["../../../../src/stages/demux/video-demux.worker.ts"],"sourcesContent":["/**\n * @deprecated VideoDemuxWorker is deprecated and will be removed in a future version.\n *\n * Reason: This worker is no longer needed for export pipeline.\n * The export process now uses IndexedVideoSource (main thread) which directly reads\n * from OPFS using pre-parsed MP4 index, avoiding the need to transfer entire files\n * to this worker and re-parse moov.\n *\n * Replacement: IndexedVideoSource (packages/core/src/stages/demux/IndexedVideoSource.ts)\n *\n * This file is kept for backward compatibility only and may be removed in the future.\n */\n\nimport { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { MP4Demuxer } from './MP4Demuxer';\nimport type { DemuxConfig } from './types';\n\ninterface LoaderStreamMetadata {\n sessionId?: string;\n byteStart?: number;\n byteEnd?: number;\n}\n/**\n * VideoDemuxWorker - First stage for video processing\n * Extracts video tracks from container formats (MP4, etc.)\n *\n * Pipeline: ResourceLoader (Main Thread) → VideoDemuxWorker → DecodeWorker\n *\n * Architecture Note:\n * - One VideoDemuxWorker instance per CLIP (not per resource)\n * - Multiple clips can share the same resource (different workers, independent processing)\n * - This enables clean 2-Clip strategy lifecycle management\n *\n * Features:\n * - MP4 container demuxing with mp4box.js\n * - Stream-based processing with backpressure\n * - Direct streaming to DecodeWorker\n */\nexport class VideoDemuxWorker {\n private channel: WorkerChannel;\n private demuxer: MP4Demuxer | null = null;\n private sessionId: string | null = null;\n private videoDownstreamPort: MessagePort | null = null;\n private audioDownstreamPort: MessagePort | null = null;\n\n constructor() {\n // Initialize WorkerChannel\n this.channel = new WorkerChannel(self as any, {\n name: 'VideoDemuxWorker',\n timeout: 30000,\n });\n this.setupHandlers();\n }\n\n /* @better-ai.mdc For test visibility */\n protected setupHandlers(): void {\n // Register message handlers\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n this.channel.registerHandler('connect', this.handleConnect.bind(this));\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n\n // Setup stream receiver from ResourceLoader (main thread)\n this.channel.receiveStream(this.handleReceiveStream.bind(this));\n }\n\n // ===== Helper methods (reduce duplication) =====\n private buildAudioDecoderConfig(): any {\n const info = this.demuxer?.audioTrackInfo;\n if (!info) return undefined;\n return {\n codec: info.codec,\n sampleRate: info.sampleRate,\n numberOfChannels: info.numberOfChannels,\n description: info.description,\n };\n }\n\n private sendVideoConfigure(): void {\n if (!this.videoDownstreamPort || !this.demuxer) return;\n const videoTrackInfo = this.demuxer.videoTrackInfo;\n if (!videoTrackInfo) {\n console.error('[VideoDemuxWorker] No video track found after ready');\n return;\n }\n const downstreamChannel = new WorkerChannel(this.videoDownstreamPort, {\n name: 'VideoDemux-Decoder',\n timeout: 30000,\n });\n downstreamChannel.send('configure' as any, {\n sessionId: this.sessionId,\n streamType: 'video',\n codec: videoTrackInfo.codec,\n width: videoTrackInfo.width,\n height: videoTrackInfo.height,\n description: videoTrackInfo.description,\n });\n }\n\n private sendAudioConfigure(): void {\n if (!this.audioDownstreamPort) return;\n const cfg = this.buildAudioDecoderConfig();\n if (!cfg) return;\n const audioChannel = new WorkerChannel(this.audioDownstreamPort, {\n name: 'VideoDemux-AudioDecoder',\n timeout: 30000,\n });\n audioChannel.send('configure' as any, {\n sessionId: this.sessionId,\n streamType: 'audio',\n ...cfg,\n });\n }\n\n private sendVideoStream(videoStream: ReadableStream<EncodedVideoChunk>): void {\n if (!this.videoDownstreamPort) return;\n const videoChannel = new WorkerChannel(this.videoDownstreamPort, {\n name: 'VideoDemux-Decoder',\n timeout: 30000,\n });\n videoChannel.sendStream(videoStream, {\n streamType: 'video',\n sessionId: this.sessionId,\n });\n }\n\n private sendAudioStream(audioStream: ReadableStream<EncodedAudioChunk> | null): void {\n if (!audioStream) return;\n\n // Prefer decoder when connected; otherwise emit to main when enabled\n if (this.audioDownstreamPort) {\n const audioChannel = new WorkerChannel(this.audioDownstreamPort, {\n name: 'VideoDemux-AudioDecoder',\n timeout: 30000,\n });\n audioChannel.sendStream(audioStream, {\n streamType: 'audio',\n sessionId: this.sessionId,\n });\n return;\n }\n }\n\n /**\n * Handle connection from orchestrator\n */\n private async handleConnect(payload: {\n port?: MessagePort;\n streamType?: string;\n sessionId?: string;\n direction?: string;\n }): Promise<{ success: boolean }> {\n const { port, sessionId, direction, streamType } = payload;\n\n if (!port) {\n return { success: false };\n }\n\n if (direction === 'downstream') {\n if (streamType === 'audio') {\n this.audioDownstreamPort = port;\n } else {\n this.videoDownstreamPort = port;\n }\n }\n\n this.sessionId = sessionId || null;\n return { success: true };\n }\n\n /**\n * Configure demuxer with format settings\n * @param payload.config - Demuxer configuration\n * @param payload.initial - If true, initialize worker state; otherwise just update config\n */\n private async handleConfigure(payload: {\n config: DemuxConfig;\n initial?: boolean;\n }): Promise<{ success: boolean; tracks?: any[] }> {\n const { config, initial = false } = payload;\n\n try {\n if (initial) {\n // Initial setup - set worker state to ready\n this.channel.state = WorkerState.Ready;\n\n // Create new demuxer instance\n if (this.demuxer) {\n this.demuxer.destroy();\n }\n\n this.demuxer = new MP4Demuxer({\n ...config,\n onReady: () => this.handleDemuxerReady(),\n });\n\n // Notify configuration complete\n this.channel.notify('configured');\n\n return { success: true };\n } else {\n // Update configuration only (e.g., backpressure settings)\n this.demuxer?.updateConfig(config);\n return { success: true };\n }\n } catch (error: any) {\n throw {\n code: error.code || 'CONFIG_ERROR',\n message: error.message,\n };\n }\n }\n\n /**\n * Handle input stream from ResourceLoader (main thread)\n * Strategy: Stream immediately, send codec info when ready\n */\n private async handleReceiveStream(\n stream: ReadableStream<Uint8Array | ArrayBuffer>,\n metadata?: LoaderStreamMetadata\n ): Promise<void> {\n // Store sessionId from metadata (only happens once per worker lifecycle)\n // Note: Do NOT override if already set by handleConnect (which has correct sessionId)\n if (!this.sessionId) {\n this.sessionId = metadata?.sessionId || this.sessionId;\n }\n\n // Initialize demuxer on first stream\n if (!this.demuxer) {\n this.demuxer = new MP4Demuxer({\n highWaterMark: 10,\n onReady: () => this.handleDemuxerReady(),\n });\n }\n\n // If no video downstream is connected, we can still proceed when\n // we only need to emit audio to the main thread for L2 caching.\n if (!this.videoDownstreamPort) {\n console.error('[VideoDemuxWorker] Decoder not connected', this.sessionId);\n return;\n }\n\n // Create output streams (readable only)\n const videoStream = this.demuxer.createVideoStream();\n const audioStream = this.demuxer.createAudioStream();\n\n // Send streams\n this.sendVideoStream(videoStream);\n this.sendAudioStream(audioStream);\n\n // Create input stream and pipe source to it (single appendBuffer)\n const inputStream = this.demuxer.createInputStream();\n await stream.pipeTo(inputStream).catch((error) => {\n console.error('[VideoDemuxWorker] Input stream error:', this.sessionId, error);\n });\n }\n\n private handleDemuxerReady(): void {\n if (!this.demuxer) return;\n this.sendVideoConfigure();\n this.sendAudioConfigure();\n }\n\n /**\n * Get demuxer statistics\n */\n private async handleGetStats(): Promise<{\n queueSize?: number;\n tracksInfo?: any[];\n state?: WorkerState;\n }> {\n if (!this.demuxer) {\n return { state: this.channel.state };\n }\n\n return {\n tracksInfo: Array.from(this.demuxer.tracks.values()),\n state: this.channel.state,\n };\n }\n\n /**\n * Dispose worker and cleanup resources\n */\n private async handleDispose(): Promise<{ success: boolean }> {\n // Destroy demuxer\n this.demuxer?.destroy();\n this.demuxer = null;\n this.sessionId = null;\n\n // Close connections\n this.videoDownstreamPort?.close();\n this.videoDownstreamPort = null;\n this.audioDownstreamPort?.close();\n this.audioDownstreamPort = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n}\n// Initialize worker\nconst worker = new VideoDemuxWorker();\n\n// Handle worker termination\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null; // Required for TypeScript worker compilation\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"video-demux.worker.BWDrLGni.js","sources":["../../../../src/stages/demux/video-demux.worker.ts"],"sourcesContent":["/**\n * @deprecated VideoDemuxWorker is deprecated and will be removed in a future version.\n *\n * Reason: This worker is no longer needed for export pipeline.\n * The export process now uses IndexedVideoSource (main thread) which directly reads\n * from OPFS using pre-parsed MP4 index, avoiding the need to transfer entire files\n * to this worker and re-parse moov.\n *\n * Replacement: IndexedVideoSource (packages/core/src/stages/demux/IndexedVideoSource.ts)\n *\n * This file is kept for backward compatibility only and may be removed in the future.\n */\n\nimport { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { MP4Demuxer } from './MP4Demuxer';\nimport type { DemuxConfig } from './types';\n\ninterface LoaderStreamMetadata {\n sessionId?: string;\n byteStart?: number;\n byteEnd?: number;\n}\n/**\n * VideoDemuxWorker - First stage for video processing\n * Extracts video tracks from container formats (MP4, etc.)\n *\n * Pipeline: ResourceLoader (Main Thread) → VideoDemuxWorker → DecodeWorker\n *\n * Architecture Note:\n * - One VideoDemuxWorker instance per CLIP (not per resource)\n * - Multiple clips can share the same resource (different workers, independent processing)\n * - This enables clean 2-Clip strategy lifecycle management\n *\n * Features:\n * - MP4 container demuxing with mp4box.js\n * - Stream-based processing with backpressure\n * - Direct streaming to DecodeWorker\n */\nexport class VideoDemuxWorker {\n private channel: WorkerChannel;\n private demuxer: MP4Demuxer | null = null;\n private sessionId: string | null = null;\n private videoDownstreamPort: MessagePort | null = null;\n private audioDownstreamPort: MessagePort | null = null;\n\n constructor() {\n // Initialize WorkerChannel\n this.channel = new WorkerChannel(self as any, {\n name: 'VideoDemuxWorker',\n timeout: 30000,\n });\n this.setupHandlers();\n }\n\n /* @better-ai.mdc For test visibility */\n protected setupHandlers(): void {\n // Register message handlers\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n this.channel.registerHandler('connect', this.handleConnect.bind(this));\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n\n // Setup stream receiver from ResourceLoader (main thread)\n this.channel.receiveStream(this.handleReceiveStream.bind(this));\n }\n\n // ===== Helper methods (reduce duplication) =====\n private buildAudioDecoderConfig(): any {\n const info = this.demuxer?.audioTrackInfo;\n if (!info) return undefined;\n return {\n codec: info.codec,\n sampleRate: info.sampleRate,\n numberOfChannels: info.numberOfChannels,\n description: info.description,\n };\n }\n\n private sendVideoConfigure(): void {\n if (!this.videoDownstreamPort || !this.demuxer) return;\n const videoTrackInfo = this.demuxer.videoTrackInfo;\n if (!videoTrackInfo) {\n console.error('[VideoDemuxWorker] No video track found after ready');\n return;\n }\n const downstreamChannel = new WorkerChannel(this.videoDownstreamPort, {\n name: 'VideoDemux-Decoder',\n timeout: 30000,\n });\n downstreamChannel.send('configure' as any, {\n sessionId: this.sessionId,\n streamType: 'video',\n codec: videoTrackInfo.codec,\n width: videoTrackInfo.width,\n height: videoTrackInfo.height,\n description: videoTrackInfo.description,\n });\n }\n\n private sendAudioConfigure(): void {\n if (!this.audioDownstreamPort) return;\n const cfg = this.buildAudioDecoderConfig();\n if (!cfg) return;\n const audioChannel = new WorkerChannel(this.audioDownstreamPort, {\n name: 'VideoDemux-AudioDecoder',\n timeout: 30000,\n });\n audioChannel.send('configure' as any, {\n sessionId: this.sessionId,\n streamType: 'audio',\n ...cfg,\n });\n }\n\n private sendVideoStream(videoStream: ReadableStream<EncodedVideoChunk>): void {\n if (!this.videoDownstreamPort) return;\n const videoChannel = new WorkerChannel(this.videoDownstreamPort, {\n name: 'VideoDemux-Decoder',\n timeout: 30000,\n });\n videoChannel.sendStream(videoStream, {\n streamType: 'video',\n sessionId: this.sessionId,\n });\n }\n\n private sendAudioStream(audioStream: ReadableStream<EncodedAudioChunk> | null): void {\n if (!audioStream) return;\n\n // Prefer decoder when connected; otherwise emit to main when enabled\n if (this.audioDownstreamPort) {\n const audioChannel = new WorkerChannel(this.audioDownstreamPort, {\n name: 'VideoDemux-AudioDecoder',\n timeout: 30000,\n });\n audioChannel.sendStream(audioStream, {\n streamType: 'audio',\n sessionId: this.sessionId,\n });\n return;\n }\n }\n\n /**\n * Handle connection from orchestrator\n */\n private async handleConnect(payload: {\n port?: MessagePort;\n streamType?: string;\n sessionId?: string;\n direction?: string;\n }): Promise<{ success: boolean }> {\n const { port, sessionId, direction, streamType } = payload;\n\n if (!port) {\n return { success: false };\n }\n\n if (direction === 'downstream') {\n if (streamType === 'audio') {\n this.audioDownstreamPort = port;\n } else {\n this.videoDownstreamPort = port;\n }\n }\n\n this.sessionId = sessionId || null;\n return { success: true };\n }\n\n /**\n * Configure demuxer with format settings\n * @param payload.config - Demuxer configuration\n * @param payload.initial - If true, initialize worker state; otherwise just update config\n */\n private async handleConfigure(payload: {\n config: DemuxConfig;\n initial?: boolean;\n }): Promise<{ success: boolean; tracks?: any[] }> {\n const { config, initial = false } = payload;\n\n try {\n if (initial) {\n // Initial setup - set worker state to ready\n this.channel.state = WorkerState.Ready;\n\n // Create new demuxer instance\n if (this.demuxer) {\n this.demuxer.destroy();\n }\n\n this.demuxer = new MP4Demuxer({\n ...config,\n onReady: () => this.handleDemuxerReady(),\n });\n\n // Notify configuration complete\n this.channel.notify('configured');\n\n return { success: true };\n } else {\n // Update configuration only (e.g., backpressure settings)\n this.demuxer?.updateConfig(config);\n return { success: true };\n }\n } catch (error: any) {\n throw {\n code: error.code || 'CONFIG_ERROR',\n message: error.message,\n };\n }\n }\n\n /**\n * Handle input stream from ResourceLoader (main thread)\n * Strategy: Stream immediately, send codec info when ready\n */\n private async handleReceiveStream(\n stream: ReadableStream<Uint8Array | ArrayBuffer>,\n metadata?: LoaderStreamMetadata\n ): Promise<void> {\n // Store sessionId from metadata (only happens once per worker lifecycle)\n // Note: Do NOT override if already set by handleConnect (which has correct sessionId)\n if (!this.sessionId) {\n this.sessionId = metadata?.sessionId || this.sessionId;\n }\n\n // Initialize demuxer on first stream\n if (!this.demuxer) {\n this.demuxer = new MP4Demuxer({\n highWaterMark: 10,\n onReady: () => this.handleDemuxerReady(),\n });\n }\n\n // If no video downstream is connected, we can still proceed when\n // we only need to emit audio to the main thread for L2 caching.\n if (!this.videoDownstreamPort) {\n console.error('[VideoDemuxWorker] Decoder not connected', this.sessionId);\n return;\n }\n\n // Create output streams (readable only)\n const videoStream = this.demuxer.createVideoStream();\n const audioStream = this.demuxer.createAudioStream();\n\n // Send streams\n this.sendVideoStream(videoStream);\n this.sendAudioStream(audioStream);\n\n // Create input stream and pipe source to it (single appendBuffer)\n const inputStream = this.demuxer.createInputStream();\n await stream.pipeTo(inputStream).catch((error) => {\n console.error('[VideoDemuxWorker] Input stream error:', this.sessionId, error);\n });\n }\n\n private handleDemuxerReady(): void {\n if (!this.demuxer) return;\n this.sendVideoConfigure();\n this.sendAudioConfigure();\n }\n\n /**\n * Get demuxer statistics\n */\n private async handleGetStats(): Promise<{\n queueSize?: number;\n tracksInfo?: any[];\n state?: WorkerState;\n }> {\n if (!this.demuxer) {\n return { state: this.channel.state };\n }\n\n return {\n tracksInfo: Array.from(this.demuxer.tracks.values()),\n state: this.channel.state,\n };\n }\n\n /**\n * Dispose worker and cleanup resources\n */\n private async handleDispose(): Promise<{ success: boolean }> {\n // Destroy demuxer\n this.demuxer?.destroy();\n this.demuxer = null;\n this.sessionId = null;\n\n // Close connections\n this.videoDownstreamPort?.close();\n this.videoDownstreamPort = null;\n this.audioDownstreamPort?.close();\n this.audioDownstreamPort = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n}\n// Initialize worker\nconst worker = new VideoDemuxWorker();\n\n// Handle worker termination\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null; // Required for TypeScript worker compilation\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.DjBEVvEA.js";
1
+ import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.DQK8rAab.js";
2
2
  class BaseEncoder {
3
3
  encoder;
4
4
  config;
@@ -331,4 +331,4 @@ const videoEncode_worker = null;
331
331
  export {
332
332
  videoEncode_worker as default
333
333
  };
334
- //# sourceMappingURL=video-encode.worker.CX2_3YhQ.js.map
334
+ //# sourceMappingURL=video-encode.worker.D6aB_rF9.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"video-encode.worker.CX2_3YhQ.js","sources":["../../../../src/stages/encode/BaseEncoder.ts","../../../../src/stages/encode/VideoChunkEncoder.ts","../../../../src/stages/encode/video-encode.worker.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\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] 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","import { BaseEncoder } from './BaseEncoder';\nimport type { VideoEncoderConfig } from './types';\n\n/**\n * VideoChunkEncoder - Encodes VideoFrame to EncodedVideoChunk\n * Stream-based encoder with backpressure handling\n */\nexport class VideoChunkEncoder extends BaseEncoder<\n VideoEncoder,\n VideoEncoderConfig,\n VideoFrame,\n EncodedVideoChunk,\n EncodedVideoChunkMetadata\n> {\n private static readonly DEFAULT_HIGH_WATER_MARK = 2;\n private static readonly DEFAULT_ENCODE_QUEUE_THRESHOLD = 8;\n\n protected readonly highWaterMark: number;\n protected readonly encodeQueueThreshold: number;\n\n private frameCount = 0;\n // Default 1 second at 30fps for better social media compatibility\n private keyFrameInterval = 30;\n\n constructor(config: VideoEncoderConfig) {\n super(config);\n\n // Initialize backpressure settings from config or use defaults\n this.highWaterMark =\n config.backpressure?.highWaterMark ?? VideoChunkEncoder.DEFAULT_HIGH_WATER_MARK;\n this.encodeQueueThreshold =\n config.backpressure?.encodeQueueThreshold ?? VideoChunkEncoder.DEFAULT_ENCODE_QUEUE_THRESHOLD;\n\n // Allow custom keyframe interval from config\n if (config.keyFrameInterval !== undefined) {\n this.keyFrameInterval = Math.max(1, config.keyFrameInterval);\n }\n }\n\n protected async isConfigSupported(config: VideoEncoderConfig): Promise<{ supported: boolean }> {\n const result = await VideoEncoder.isConfigSupported(config);\n return { supported: result.supported ?? false };\n }\n\n protected createEncoder(init: VideoEncoderInit): VideoEncoder {\n return new VideoEncoder(init);\n }\n\n protected getEncoderType(): string {\n return 'Video';\n }\n\n protected override onReset(): void {\n this.frameCount = 0;\n }\n\n encode(frame: VideoFrame): void {\n const keyFrame = this.shouldGenerateKeyFrame();\n const encodeOptions: VideoEncoderEncodeOptions = {\n keyFrame,\n };\n\n this.encoder!.encode(frame, encodeOptions);\n this.frameCount++;\n frame.close();\n }\n\n setKeyFrameInterval(interval: number): void {\n this.keyFrameInterval = Math.max(1, interval);\n }\n\n private shouldGenerateKeyFrame(): boolean {\n if (this.frameCount === 0) {\n return true;\n }\n return this.frameCount % this.keyFrameInterval === 0;\n }\n}\n","import { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { VideoChunkEncoder } from './VideoChunkEncoder';\nimport { VideoEncoderConfig } from './types';\n\n/**\n * VideoEncodeWorker (Clip Local) - Encodes video for a single clip\n * Receives composed frames from VideoComposeWorker and outputs encoded chunks\n *\n * Pipeline: VideoComposeWorker → VideoEncodeWorker → CacheManager (L2)\n *\n * Features:\n * - Single clip, single VideoEncoder instance (no manager needed)\n * - Hardware-accelerated encoding via WebCodecs\n * - Only used in L2 channel (preview channel skips encoding)\n * - Lifecycle tied to L2 clip pipeline\n */\nclass VideoEncodeWorker {\n private channel: WorkerChannel;\n private encoder: VideoChunkEncoder | null = null;\n private clipId: string = '';\n\n private upstreamPort: MessagePort | null = null;\n\n constructor() {\n this.channel = new WorkerChannel(self as any, {\n name: 'VideoEncodeWorker',\n timeout: 30000,\n });\n\n this.setupHandlers();\n }\n\n private setupHandlers(): void {\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n this.channel.registerHandler('connect', this.handleConnect.bind(this));\n this.channel.registerHandler('flush', this.handleFlush.bind(this));\n this.channel.registerHandler('reset', this.handleReset.bind(this));\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n }\n\n private async handleConnect(payload: {\n direction: 'upstream' | 'downstream';\n port: MessagePort;\n streamType: 'video' | 'frame';\n sessionId?: string;\n }): Promise<{ success: boolean }> {\n const { port, streamType, sessionId, direction } = payload;\n\n if (direction === 'upstream' && streamType === 'video') {\n return this.handleConnectComposer({ port, sessionId });\n }\n\n return { success: true };\n }\n\n private async handleConfigure(payload: {\n config: VideoEncoderConfig;\n initial?: boolean;\n }): Promise<{ success: boolean }> {\n const { config, initial = false } = payload;\n\n if (initial) {\n this.channel.state = WorkerState.Ready;\n this.encoder = new VideoChunkEncoder(config);\n this.encoder.initialize();\n }\n\n return { success: true };\n }\n\n private async handleConnectComposer(payload: {\n port: MessagePort;\n sessionId?: string;\n }): Promise<{ success: boolean }> {\n const { port, sessionId } = payload;\n\n this.upstreamPort = port;\n this.clipId = sessionId || 'default';\n\n const composeChannel = new WorkerChannel(port, {\n name: 'Encode-VideoCompose',\n timeout: 30000,\n });\n\n composeChannel.receiveStream(async (stream, metadata) => {\n const streamSessionId = (metadata as any)?.sessionId || sessionId || 'unknown';\n\n if (metadata?.streamType === 'video') {\n if (!this.encoder) {\n console.error('[VideoEncoderWorker] error: no encoder');\n return;\n }\n\n const encodingTransform = this.encoder.createStream();\n const encodedStream = (stream as unknown as ReadableStream<VideoFrame>).pipeThrough(\n encodingTransform\n );\n\n this.channel.sendStream(encodedStream, {\n streamType: 'video',\n track: 'video',\n sessionId: streamSessionId,\n });\n }\n });\n\n return { success: true };\n }\n\n private async handleFlush(): Promise<{ success: boolean }> {\n if (this.encoder) {\n await this.encoder.flush();\n }\n return { success: true };\n }\n\n private async handleReset(): Promise<{ success: boolean }> {\n if (this.encoder) {\n await this.encoder.reset();\n }\n\n this.channel.notify('reset_complete', {\n type: 'video',\n });\n\n return { success: true };\n }\n\n private async handleGetStats(): Promise<{\n video?: any;\n }> {\n if (!this.encoder) {\n return {};\n }\n\n return {\n video: {\n clipId: this.clipId,\n configured: true,\n },\n };\n }\n\n private async handleDispose(): Promise<{ success: boolean }> {\n if (this.encoder) {\n await this.encoder.close();\n this.encoder = null;\n }\n\n this.upstreamPort?.close();\n this.upstreamPort = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n}\n\nconst worker = new VideoEncodeWorker();\n\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null;\n"],"names":[],"mappings":";AAYO,MAAe,YAMpB;AAAA,EACU;AAAA,EACA;AAAA,EACA,aAAoE;AAAA,EAE9E,YAAY,QAAiB;AAC3B,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,YAAqB;AACnB,WAAO,EAAE,GAAG,KAAK,OAAA;AAAA,EACnB;AAAA,EAEA,IAAc,gBAAyB;AACrC,WAAO,KAAK;AAAA,EACd;AAAA,EAEU,kBAAkB,SAAoC;AAC9D,UAAM,OAAO,EAAE,GAAG,KAAK,QAAQ,GAAG,QAAA;AAClC,UAAM,OAAO,OAAO,KAAK,WAAW,CAAA,CAAE;AACtC,eAAW,OAAO,MAAM;AACtB,UAAI,QAAQ,GAAG,MAAM,UAAa,KAAK,GAAG,MAAM,KAAK,OAAO,GAAG,GAAG;AAChE,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEU,iBAAiB,MAAwB;AACjD,UAAM,iBAAiB,OAAO,QAAQ,KAAK,MAAM;AACjD,eAAW,CAAC,KAAK,KAAK,KAAK,gBAAgB;AACzC,UAAI,KAAK,GAAG,MAAM,OAAO;AACvB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,eAAW,OAAO,OAAO,KAAK,IAAI,GAA2B;AAC3D,UAAI,KAAK,OAAO,GAAG,MAAM,KAAK,GAAG,GAAG;AAClC,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEU,aAAa,GAAY,GAAqB;AACtD,WAAO,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;AAAA,EAC/C;AAAA,EAEA,MAAM,aAA4B;AAChC,QAAI,KAAK,SAAS,UAAU,cAAc;AACxC;AAAA,IACF;AAEA,UAAM,cAAc,MAAM,KAAK,kBAAkB,KAAK,MAAM;AAE5D,QAAI,CAAC,YAAY,WAAW;AAC1B,YAAM,IAAI,MAAM,wBAAwB,KAAK,UAAU,KAAK,MAAM,CAAC,EAAE;AAAA,IACvE;AAEA,SAAK,UAAU,KAAK,cAAc;AAAA,MAChC,QAAQ,KAAK,aAAa,KAAK,IAAI;AAAA,MACnC,OAAO,KAAK,YAAY,KAAK,IAAI;AAAA,IAAA,CAClC;AAEA,SAAK,QAAgB,UAAU,KAAK,MAAM;AAAA,EAC7C;AAAA,EAEA,MAAM,YAAY,QAAyC;AACzD,QAAI,CAAC,UAAU,OAAO,KAAK,MAAM,EAAE,WAAW,GAAG;AAC/C;AAAA,IACF;AAEA,UAAM,aAAa,EAAE,GAAG,KAAK,QAAQ,GAAG,OAAA;AAExC,QAAI,KAAK,aAAa,KAAK,QAAQ,UAAU,GAAG;AAC9C;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,SAAS;AACjB,WAAK,SAAS;AACd,YAAM,KAAK,WAAA;AACX;AAAA,IACF;AAEA,QAAI,KAAK,QAAQ,UAAU,cAAc;AACvC,YAAM,KAAK,QAAQ,MAAA;AAAA,IACrB;AAEA,UAAM,cAAc,MAAM,KAAK,kBAAkB,UAAU;AAC3D,QAAI,CAAC,YAAY,WAAW;AAC1B,YAAM,IAAI,MAAM,oCAAoC,WAAW,KAAK,EAAE;AAAA,IACxE;AAEA,SAAK,SAAS;AACb,SAAK,QAAgB,UAAU,KAAK,MAAM;AAAA,EAC7C;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,SAAS;AACjB;AAAA,IACF;AAEA,UAAM,KAAK,QAAQ,MAAA;AAAA,EACrB;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,SAAS;AACjB;AAAA,IACF;AAEA,SAAK,QAAQ,MAAA;AACb,SAAK,QAAA;AAAA,EACP;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,CAAC,KAAK,SAAS;AACjB;AAAA,IACF;AAEA,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,WAAO,KAAK,SAAS,mBAAmB;AAAA,EAC1C;AAAA,EAEU,aAAa,OAAe,UAA2B;AAE/D,QAAI,KAAK,YAAY;AACnB,UAAI;AACF,aAAK,WAAW,QAAQ,EAAE,OAAO,UAAU;AAAA,MAC7C,SAAS,OAAO;AAEd,YAAI,EAAE,iBAAiB,aAAa,MAAM,QAAQ,SAAS,QAAQ,IAAI;AACrE,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEU,YAAY,OAA2B;AAC/C,YAAQ,MAAM,IAAI,KAAK,eAAA,CAAgB,0BAA0B;AAAA,MAC/D,MAAM,MAAM;AAAA,MACZ,SAAS,MAAM;AAAA,MACf,cAAc,KAAK,SAAS;AAAA,MAC5B,WAAW,KAAK;AAAA,MAChB,UAAU,OAAO,cAAc,cAAc,UAAU,WAAW;AAAA,IAAA,CACnE;AACD,SAAK,YAAY,MAAM,KAAK;AAAA,EAC9B;AAAA;AAAA,EAQU,UAAgB;AAAA,EAE1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,eAAsD;AACpD,WAAO,IAAI;AAAA,MACT;AAAA,QACE,OAAO,OAAO,eAAe;AAC3B,eAAK,aAAa;AAGlB,cAAI,CAAC,KAAK,SAAS;AACjB,kBAAM,KAAK,WAAA;AAAA,UACb;AAAA,QACF;AAAA,QAEA,WAAW,OAAO,UAAU;AAC1B,cAAI,CAAC,KAAK,WAAW,KAAK,QAAQ,UAAU,cAAc;AACxD,kBAAM,IAAI,MAAM,wBAAwB;AAAA,UAC1C;AAGA,cAAI,KAAK,QAAQ,mBAAmB,KAAK,sBAAsB;AAE7D,kBAAM,IAAI,QAAc,CAAC,YAAY;AACnC,oBAAM,QAAQ,MAAM;AAClB,oBAAI,CAAC,KAAK,WAAW,KAAK,QAAQ,kBAAkB,KAAK,uBAAuB,GAAG;AACjF,0BAAA;AAAA,gBACF,OAAO;AACL,6BAAW,OAAO,EAAE;AAAA,gBACtB;AAAA,cACF;AACA,oBAAA;AAAA,YACF,CAAC;AAAA,UACH;AAGA,gBAAM,QAAS,MAAc,SAAS;AACtC,eAAK,OAAO,KAAK;AAAA,QACnB;AAAA,QAEA,OAAO,YAAY;AACjB,gBAAM,KAAK,MAAA;AAAA,QACb;AAAA,MAAA;AAAA;AAAA,MAGF;AAAA,QACE,eAAe,KAAK;AAAA,QACpB,MAAM,MAAM;AAAA;AAAA,MAAA;AAAA,IACd;AAAA,EAEJ;AAIF;ACjPO,MAAM,0BAA0B,YAMrC;AAAA,EACA,OAAwB,0BAA0B;AAAA,EAClD,OAAwB,iCAAiC;AAAA,EAEtC;AAAA,EACA;AAAA,EAEX,aAAa;AAAA;AAAA,EAEb,mBAAmB;AAAA,EAE3B,YAAY,QAA4B;AACtC,UAAM,MAAM;AAGZ,SAAK,gBACH,OAAO,cAAc,iBAAiB,kBAAkB;AAC1D,SAAK,uBACH,OAAO,cAAc,wBAAwB,kBAAkB;AAGjE,QAAI,OAAO,qBAAqB,QAAW;AACzC,WAAK,mBAAmB,KAAK,IAAI,GAAG,OAAO,gBAAgB;AAAA,IAC7D;AAAA,EACF;AAAA,EAEA,MAAgB,kBAAkB,QAA6D;AAC7F,UAAM,SAAS,MAAM,aAAa,kBAAkB,MAAM;AAC1D,WAAO,EAAE,WAAW,OAAO,aAAa,MAAA;AAAA,EAC1C;AAAA,EAEU,cAAc,MAAsC;AAC5D,WAAO,IAAI,aAAa,IAAI;AAAA,EAC9B;AAAA,EAEU,iBAAyB;AACjC,WAAO;AAAA,EACT;AAAA,EAEmB,UAAgB;AACjC,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,OAAO,OAAyB;AAC9B,UAAM,WAAW,KAAK,uBAAA;AACtB,UAAM,gBAA2C;AAAA,MAC/C;AAAA,IAAA;AAGF,SAAK,QAAS,OAAO,OAAO,aAAa;AACzC,SAAK;AACL,UAAM,MAAA;AAAA,EACR;AAAA,EAEA,oBAAoB,UAAwB;AAC1C,SAAK,mBAAmB,KAAK,IAAI,GAAG,QAAQ;AAAA,EAC9C;AAAA,EAEQ,yBAAkC;AACxC,QAAI,KAAK,eAAe,GAAG;AACzB,aAAO;AAAA,IACT;AACA,WAAO,KAAK,aAAa,KAAK,qBAAqB;AAAA,EACrD;AACF;AC5DA,MAAM,kBAAkB;AAAA,EACd;AAAA,EACA,UAAoC;AAAA,EACpC,SAAiB;AAAA,EAEjB,eAAmC;AAAA,EAE3C,cAAc;AACZ,SAAK,UAAU,IAAI,cAAc,MAAa;AAAA,MAC5C,MAAM;AAAA,MACN,SAAS;AAAA,IAAA,CACV;AAED,SAAK,cAAA;AAAA,EACP;AAAA,EAEQ,gBAAsB;AAC5B,SAAK,QAAQ,gBAAgB,aAAa,KAAK,gBAAgB,KAAK,IAAI,CAAC;AACzE,SAAK,QAAQ,gBAAgB,WAAW,KAAK,cAAc,KAAK,IAAI,CAAC;AACrE,SAAK,QAAQ,gBAAgB,SAAS,KAAK,YAAY,KAAK,IAAI,CAAC;AACjE,SAAK,QAAQ,gBAAgB,SAAS,KAAK,YAAY,KAAK,IAAI,CAAC;AACjE,SAAK,QAAQ,gBAAgB,aAAa,KAAK,eAAe,KAAK,IAAI,CAAC;AACxE,SAAK,QAAQ,gBAAgB,kBAAkB,SAAS,KAAK,cAAc,KAAK,IAAI,CAAC;AAAA,EACvF;AAAA,EAEA,MAAc,cAAc,SAKM;AAChC,UAAM,EAAE,MAAM,YAAY,WAAW,cAAc;AAEnD,QAAI,cAAc,cAAc,eAAe,SAAS;AACtD,aAAO,KAAK,sBAAsB,EAAE,MAAM,WAAW;AAAA,IACvD;AAEA,WAAO,EAAE,SAAS,KAAA;AAAA,EACpB;AAAA,EAEA,MAAc,gBAAgB,SAGI;AAChC,UAAM,EAAE,QAAQ,UAAU,MAAA,IAAU;AAEpC,QAAI,SAAS;AACX,WAAK,QAAQ,QAAQ,YAAY;AACjC,WAAK,UAAU,IAAI,kBAAkB,MAAM;AAC3C,WAAK,QAAQ,WAAA;AAAA,IACf;AAEA,WAAO,EAAE,SAAS,KAAA;AAAA,EACpB;AAAA,EAEA,MAAc,sBAAsB,SAGF;AAChC,UAAM,EAAE,MAAM,UAAA,IAAc;AAE5B,SAAK,eAAe;AACpB,SAAK,SAAS,aAAa;AAE3B,UAAM,iBAAiB,IAAI,cAAc,MAAM;AAAA,MAC7C,MAAM;AAAA,MACN,SAAS;AAAA,IAAA,CACV;AAED,mBAAe,cAAc,OAAO,QAAQ,aAAa;AACvD,YAAM,kBAAmB,UAAkB,aAAa,aAAa;AAErE,UAAI,UAAU,eAAe,SAAS;AACpC,YAAI,CAAC,KAAK,SAAS;AACjB,kBAAQ,MAAM,wCAAwC;AACtD;AAAA,QACF;AAEA,cAAM,oBAAoB,KAAK,QAAQ,aAAA;AACvC,cAAM,gBAAiB,OAAiD;AAAA,UACtE;AAAA,QAAA;AAGF,aAAK,QAAQ,WAAW,eAAe;AAAA,UACrC,YAAY;AAAA,UACZ,OAAO;AAAA,UACP,WAAW;AAAA,QAAA,CACZ;AAAA,MACH;AAAA,IACF,CAAC;AAED,WAAO,EAAE,SAAS,KAAA;AAAA,EACpB;AAAA,EAEA,MAAc,cAA6C;AACzD,QAAI,KAAK,SAAS;AAChB,YAAM,KAAK,QAAQ,MAAA;AAAA,IACrB;AACA,WAAO,EAAE,SAAS,KAAA;AAAA,EACpB;AAAA,EAEA,MAAc,cAA6C;AACzD,QAAI,KAAK,SAAS;AAChB,YAAM,KAAK,QAAQ,MAAA;AAAA,IACrB;AAEA,SAAK,QAAQ,OAAO,kBAAkB;AAAA,MACpC,MAAM;AAAA,IAAA,CACP;AAED,WAAO,EAAE,SAAS,KAAA;AAAA,EACpB;AAAA,EAEA,MAAc,iBAEX;AACD,QAAI,CAAC,KAAK,SAAS;AACjB,aAAO,CAAA;AAAA,IACT;AAEA,WAAO;AAAA,MACL,OAAO;AAAA,QACL,QAAQ,KAAK;AAAA,QACb,YAAY;AAAA,MAAA;AAAA,IACd;AAAA,EAEJ;AAAA,EAEA,MAAc,gBAA+C;AAC3D,QAAI,KAAK,SAAS;AAChB,YAAM,KAAK,QAAQ,MAAA;AACnB,WAAK,UAAU;AAAA,IACjB;AAEA,SAAK,cAAc,MAAA;AACnB,SAAK,eAAe;AAEpB,SAAK,QAAQ,QAAQ,YAAY;AAEjC,WAAO,EAAE,SAAS,KAAA;AAAA,EACpB;AACF;AAEA,MAAM,SAAS,IAAI,kBAAA;AAEnB,KAAK,iBAAiB,gBAAgB,MAAM;AAC1C,SAAO,eAAe,EAAA;AACxB,CAAC;AAED,MAAA,qBAAe;"}
1
+ {"version":3,"file":"video-encode.worker.D6aB_rF9.js","sources":["../../../../src/stages/encode/BaseEncoder.ts","../../../../src/stages/encode/VideoChunkEncoder.ts","../../../../src/stages/encode/video-encode.worker.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\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] 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","import { BaseEncoder } from './BaseEncoder';\nimport type { VideoEncoderConfig } from './types';\n\n/**\n * VideoChunkEncoder - Encodes VideoFrame to EncodedVideoChunk\n * Stream-based encoder with backpressure handling\n */\nexport class VideoChunkEncoder extends BaseEncoder<\n VideoEncoder,\n VideoEncoderConfig,\n VideoFrame,\n EncodedVideoChunk,\n EncodedVideoChunkMetadata\n> {\n private static readonly DEFAULT_HIGH_WATER_MARK = 2;\n private static readonly DEFAULT_ENCODE_QUEUE_THRESHOLD = 8;\n\n protected readonly highWaterMark: number;\n protected readonly encodeQueueThreshold: number;\n\n private frameCount = 0;\n // Default 1 second at 30fps for better social media compatibility\n private keyFrameInterval = 30;\n\n constructor(config: VideoEncoderConfig) {\n super(config);\n\n // Initialize backpressure settings from config or use defaults\n this.highWaterMark =\n config.backpressure?.highWaterMark ?? VideoChunkEncoder.DEFAULT_HIGH_WATER_MARK;\n this.encodeQueueThreshold =\n config.backpressure?.encodeQueueThreshold ?? VideoChunkEncoder.DEFAULT_ENCODE_QUEUE_THRESHOLD;\n\n // Allow custom keyframe interval from config\n if (config.keyFrameInterval !== undefined) {\n this.keyFrameInterval = Math.max(1, config.keyFrameInterval);\n }\n }\n\n protected async isConfigSupported(config: VideoEncoderConfig): Promise<{ supported: boolean }> {\n const result = await VideoEncoder.isConfigSupported(config);\n return { supported: result.supported ?? false };\n }\n\n protected createEncoder(init: VideoEncoderInit): VideoEncoder {\n return new VideoEncoder(init);\n }\n\n protected getEncoderType(): string {\n return 'Video';\n }\n\n protected override onReset(): void {\n this.frameCount = 0;\n }\n\n encode(frame: VideoFrame): void {\n const keyFrame = this.shouldGenerateKeyFrame();\n const encodeOptions: VideoEncoderEncodeOptions = {\n keyFrame,\n };\n\n this.encoder!.encode(frame, encodeOptions);\n this.frameCount++;\n frame.close();\n }\n\n setKeyFrameInterval(interval: number): void {\n this.keyFrameInterval = Math.max(1, interval);\n }\n\n private shouldGenerateKeyFrame(): boolean {\n if (this.frameCount === 0) {\n return true;\n }\n return this.frameCount % this.keyFrameInterval === 0;\n }\n}\n","import { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { VideoChunkEncoder } from './VideoChunkEncoder';\nimport { VideoEncoderConfig } from './types';\n\n/**\n * VideoEncodeWorker (Clip Local) - Encodes video for a single clip\n * Receives composed frames from VideoComposeWorker and outputs encoded chunks\n *\n * Pipeline: VideoComposeWorker → VideoEncodeWorker → CacheManager (L2)\n *\n * Features:\n * - Single clip, single VideoEncoder instance (no manager needed)\n * - Hardware-accelerated encoding via WebCodecs\n * - Only used in L2 channel (preview channel skips encoding)\n * - Lifecycle tied to L2 clip pipeline\n */\nclass VideoEncodeWorker {\n private channel: WorkerChannel;\n private encoder: VideoChunkEncoder | null = null;\n private clipId: string = '';\n\n private upstreamPort: MessagePort | null = null;\n\n constructor() {\n this.channel = new WorkerChannel(self as any, {\n name: 'VideoEncodeWorker',\n timeout: 30000,\n });\n\n this.setupHandlers();\n }\n\n private setupHandlers(): void {\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n this.channel.registerHandler('connect', this.handleConnect.bind(this));\n this.channel.registerHandler('flush', this.handleFlush.bind(this));\n this.channel.registerHandler('reset', this.handleReset.bind(this));\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n }\n\n private async handleConnect(payload: {\n direction: 'upstream' | 'downstream';\n port: MessagePort;\n streamType: 'video' | 'frame';\n sessionId?: string;\n }): Promise<{ success: boolean }> {\n const { port, streamType, sessionId, direction } = payload;\n\n if (direction === 'upstream' && streamType === 'video') {\n return this.handleConnectComposer({ port, sessionId });\n }\n\n return { success: true };\n }\n\n private async handleConfigure(payload: {\n config: VideoEncoderConfig;\n initial?: boolean;\n }): Promise<{ success: boolean }> {\n const { config, initial = false } = payload;\n\n if (initial) {\n this.channel.state = WorkerState.Ready;\n this.encoder = new VideoChunkEncoder(config);\n this.encoder.initialize();\n }\n\n return { success: true };\n }\n\n private async handleConnectComposer(payload: {\n port: MessagePort;\n sessionId?: string;\n }): Promise<{ success: boolean }> {\n const { port, sessionId } = payload;\n\n this.upstreamPort = port;\n this.clipId = sessionId || 'default';\n\n const composeChannel = new WorkerChannel(port, {\n name: 'Encode-VideoCompose',\n timeout: 30000,\n });\n\n composeChannel.receiveStream(async (stream, metadata) => {\n const streamSessionId = (metadata as any)?.sessionId || sessionId || 'unknown';\n\n if (metadata?.streamType === 'video') {\n if (!this.encoder) {\n console.error('[VideoEncoderWorker] error: no encoder');\n return;\n }\n\n const encodingTransform = this.encoder.createStream();\n const encodedStream = (stream as unknown as ReadableStream<VideoFrame>).pipeThrough(\n encodingTransform\n );\n\n this.channel.sendStream(encodedStream, {\n streamType: 'video',\n track: 'video',\n sessionId: streamSessionId,\n });\n }\n });\n\n return { success: true };\n }\n\n private async handleFlush(): Promise<{ success: boolean }> {\n if (this.encoder) {\n await this.encoder.flush();\n }\n return { success: true };\n }\n\n private async handleReset(): Promise<{ success: boolean }> {\n if (this.encoder) {\n await this.encoder.reset();\n }\n\n this.channel.notify('reset_complete', {\n type: 'video',\n });\n\n return { success: true };\n }\n\n private async handleGetStats(): Promise<{\n video?: any;\n }> {\n if (!this.encoder) {\n return {};\n }\n\n return {\n video: {\n clipId: this.clipId,\n configured: true,\n },\n };\n }\n\n private async handleDispose(): Promise<{ success: boolean }> {\n if (this.encoder) {\n await this.encoder.close();\n this.encoder = null;\n }\n\n this.upstreamPort?.close();\n this.upstreamPort = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n}\n\nconst worker = new VideoEncodeWorker();\n\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null;\n"],"names":[],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  {
2
- "audio-compose.worker": "stages/compose/audio-compose.worker.CiM_KP27.js",
3
- "video-compose.worker": "stages/compose/video-compose.worker.CQwmNfXT.js",
4
- "audio-decode.worker": "stages/decode/audio-decode.worker.CpjkrZtT.js",
5
- "video-decode.worker": "stages/decode/video-decode.worker.BQtw6eWn.js",
6
- "audio-demux.worker": "stages/demux/audio-demux.worker.C4V11GQi.js",
7
- "video-demux.worker": "stages/demux/video-demux.worker.5pJr0Ij-.js",
8
- "video-encode.worker": "stages/encode/video-encode.worker.CX2_3YhQ.js"
2
+ "audio-compose.worker": "stages/compose/audio-compose.worker.B4Io5w9i.js",
3
+ "video-compose.worker": "stages/compose/video-compose.worker.CA2_Kpg-.js",
4
+ "audio-decode.worker": "stages/decode/audio-decode.worker.-DGlQrJD.js",
5
+ "video-decode.worker": "stages/decode/video-decode.worker.BnWVUkng.js",
6
+ "audio-demux.worker": "stages/demux/audio-demux.worker.D-_LoVqW.js",
7
+ "video-demux.worker": "stages/demux/video-demux.worker.BWDrLGni.js",
8
+ "video-encode.worker": "stages/encode/video-encode.worker.D6aB_rF9.js"
9
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meframe/core",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Next generation media processing framework based on WebCodecs",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",