@meframe/core 0.0.24 → 0.0.26

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 (89) hide show
  1. package/dist/cache/CacheManager.d.ts +1 -4
  2. package/dist/cache/CacheManager.d.ts.map +1 -1
  3. package/dist/cache/CacheManager.js +3 -13
  4. package/dist/cache/CacheManager.js.map +1 -1
  5. package/dist/cache/index.d.ts +1 -1
  6. package/dist/cache/index.d.ts.map +1 -1
  7. package/dist/cache/l1/VideoL1Cache.d.ts +7 -40
  8. package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -1
  9. package/dist/cache/l1/VideoL1Cache.js +63 -251
  10. package/dist/cache/l1/VideoL1Cache.js.map +1 -1
  11. package/dist/cache/l1/types.d.ts +0 -2
  12. package/dist/cache/l1/types.d.ts.map +1 -1
  13. package/dist/cache/types.d.ts +0 -15
  14. package/dist/cache/types.d.ts.map +1 -1
  15. package/dist/controllers/PreRenderService.js +0 -3
  16. package/dist/controllers/PreRenderService.js.map +1 -1
  17. package/dist/model/CompositionModel.d.ts +6 -3
  18. package/dist/model/CompositionModel.d.ts.map +1 -1
  19. package/dist/model/CompositionModel.js +12 -5
  20. package/dist/model/CompositionModel.js.map +1 -1
  21. package/dist/model/RcFrame.d.ts +0 -4
  22. package/dist/model/RcFrame.d.ts.map +1 -1
  23. package/dist/model/RcFrame.js +0 -6
  24. package/dist/model/RcFrame.js.map +1 -1
  25. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  26. package/dist/orchestrator/Orchestrator.js +27 -38
  27. package/dist/orchestrator/Orchestrator.js.map +1 -1
  28. package/dist/orchestrator/VideoClipSession.d.ts +4 -5
  29. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  30. package/dist/orchestrator/VideoClipSession.js +32 -35
  31. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  32. package/dist/stages/compose/LayerRenderer.d.ts.map +1 -1
  33. package/dist/stages/compose/VideoComposer.d.ts +1 -7
  34. package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
  35. package/dist/stages/compose/types.d.ts +1 -9
  36. package/dist/stages/compose/types.d.ts.map +1 -1
  37. package/dist/stages/decode/BaseDecoder.d.ts.map +1 -1
  38. package/dist/stages/decode/BaseDecoder.js +5 -0
  39. package/dist/stages/decode/BaseDecoder.js.map +1 -1
  40. package/dist/stages/decode/VideoChunkDecoder.d.ts +0 -4
  41. package/dist/stages/decode/VideoChunkDecoder.d.ts.map +1 -1
  42. package/dist/stages/decode/VideoChunkDecoder.js +3 -27
  43. package/dist/stages/decode/VideoChunkDecoder.js.map +1 -1
  44. package/dist/utils/time-utils.js +0 -16
  45. package/dist/utils/time-utils.js.map +1 -1
  46. package/dist/worker/WorkerPool.d.ts +1 -0
  47. package/dist/worker/WorkerPool.d.ts.map +1 -1
  48. package/dist/worker/WorkerPool.js +19 -4
  49. package/dist/worker/WorkerPool.js.map +1 -1
  50. package/dist/worker/worker-manifest.d.ts +27 -0
  51. package/dist/worker/worker-manifest.d.ts.map +1 -0
  52. package/dist/worker/worker-manifest.js +37 -0
  53. package/dist/worker/worker-manifest.js.map +1 -0
  54. package/dist/workers/{BaseDecoder.js → BaseDecoder.BGsRDny7.js} +6 -1
  55. package/dist/workers/BaseDecoder.BGsRDny7.js.map +1 -0
  56. package/dist/workers/{MP4Demuxer.js → MP4Demuxer.CFHDkPYc.js} +1 -1
  57. package/dist/workers/MP4Demuxer.CFHDkPYc.js.map +1 -0
  58. package/dist/workers/{WorkerChannel.js → WorkerChannel.CE5euh3R.js} +1 -1
  59. package/dist/workers/WorkerChannel.CE5euh3R.js.map +1 -0
  60. package/dist/workers/stages/compose/{audio-compose.worker.js → audio-compose.worker.rW63uN6z.js} +2 -2
  61. package/dist/workers/stages/compose/audio-compose.worker.rW63uN6z.js.map +1 -0
  62. package/dist/workers/stages/compose/{video-compose.worker.js → video-compose.worker.M5uomNVr.js} +51 -65
  63. package/dist/workers/stages/compose/video-compose.worker.M5uomNVr.js.map +1 -0
  64. package/dist/workers/stages/decode/{audio-decode.worker.js → audio-decode.worker.3SD5i_Oe.js} +3 -3
  65. package/dist/workers/stages/decode/audio-decode.worker.3SD5i_Oe.js.map +1 -0
  66. package/dist/workers/stages/decode/{video-decode.worker.js → video-decode.worker.BAB6216v.js} +6 -30
  67. package/dist/workers/stages/decode/video-decode.worker.BAB6216v.js.map +1 -0
  68. package/dist/workers/stages/demux/{audio-demux.worker.js → audio-demux.worker.BTFPcY7P.js} +3 -3
  69. package/dist/workers/stages/demux/audio-demux.worker.BTFPcY7P.js.map +1 -0
  70. package/dist/workers/stages/demux/{video-demux.worker.js → video-demux.worker.D_WeHPkt.js} +3 -3
  71. package/dist/workers/stages/demux/video-demux.worker.D_WeHPkt.js.map +1 -0
  72. package/dist/workers/stages/encode/{video-encode.worker.js → video-encode.worker.u2o7iXCT.js} +2 -2
  73. package/dist/workers/stages/encode/video-encode.worker.u2o7iXCT.js.map +1 -0
  74. package/dist/workers/worker-manifest.json +9 -0
  75. package/package.json +2 -2
  76. package/dist/cache/l1/gop-utils.d.ts +0 -10
  77. package/dist/cache/l1/gop-utils.d.ts.map +0 -1
  78. package/dist/cache/l1/gop-utils.js +0 -78
  79. package/dist/cache/l1/gop-utils.js.map +0 -1
  80. package/dist/workers/BaseDecoder.js.map +0 -1
  81. package/dist/workers/MP4Demuxer.js.map +0 -1
  82. package/dist/workers/WorkerChannel.js.map +0 -1
  83. package/dist/workers/stages/compose/audio-compose.worker.js.map +0 -1
  84. package/dist/workers/stages/compose/video-compose.worker.js.map +0 -1
  85. package/dist/workers/stages/decode/audio-decode.worker.js.map +0 -1
  86. package/dist/workers/stages/decode/video-decode.worker.js.map +0 -1
  87. package/dist/workers/stages/demux/audio-demux.worker.js.map +0 -1
  88. package/dist/workers/stages/demux/video-demux.worker.js.map +0 -1
  89. package/dist/workers/stages/encode/video-encode.worker.js.map +0 -1
@@ -480,4 +480,4 @@ export {
480
480
  WorkerMessageType as a,
481
481
  WorkerState as b
482
482
  };
483
- //# sourceMappingURL=WorkerChannel.js.map
483
+ //# sourceMappingURL=WorkerChannel.CE5euh3R.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkerChannel.CE5euh3R.js","sources":["../../src/worker/types.ts","../../src/worker/worker-retry.ts","../../src/worker/transferable-helper.ts","../../src/worker/WorkerChannel.ts"],"sourcesContent":["/**\n * Worker Communication Protocol Types\n * Define standard message types and protocols for worker communication\n */\n\n// A port-like object that supports postMessage/onmessage used by WorkerChannel\nexport type PortLike = MessagePort | DedicatedWorkerGlobalScope | Worker;\n\n/**\n * Worker types in the pipeline\n */\nexport type WorkerType =\n | 'videoDemux'\n | 'audioDemux'\n | 'videoDecode' // Clip-local video decoder\n | 'audioDecode' // Clip-local audio decoder\n | 'videoCompose'\n | 'audioCompose'\n | 'videoEncode'; // Clip-local video encoder (L2 only)\n\nexport interface WorkerConfigurePayload<T = any> {\n config: T;\n initial: boolean;\n}\n\n/**\n * Worker state definition\n */\nexport interface WorkerStateInfo {\n state: 'idle' | 'busy' | 'error' | 'terminated';\n taskCount: number;\n lastError?: string;\n}\n\n/**\n * Worker status for all workers\n */\nexport type WorkerStatus = Record<WorkerType, WorkerStateInfo>;\n\n/**\n * Worker wrapper interface\n */\nexport interface IWorker {\n readonly type: WorkerType;\n readonly state: WorkerStateInfo;\n\n initialize(): Promise<void>;\n terminate(): Promise<void>;\n restart(): Promise<void>;\n\n postMessage(message: any, transfer?: Transferable[]): void;\n on(event: string, handler: (data: any) => void): void;\n off(event: string, handler: (data: any) => void): void;\n}\n\n// Message types for different workers\nexport enum WorkerMessageType {\n // Init = 'init', // configure, { initial: true }\n Ready = 'ready',\n Error = 'error',\n Dispose = 'dispose',\n Configure = 'configure',\n LoadResource = 'load_resource',\n ResourceLoaded = 'resource_loaded',\n ResourceProgress = 'resource_progress',\n ConfigureDemux = 'configure_demux',\n AppendBuffer = 'append_buffer',\n DemuxSamples = 'demux_samples',\n FlushDemux = 'flush_demux',\n ConfigureDecode = 'configure_decode',\n DecodeChunk = 'decode_chunk',\n DecodedFrame = 'decoded_frame',\n SeekGop = 'seek_gop',\n SetComposition = 'set_composition',\n ApplyPatch = 'apply_patch',\n RenderFrame = 'render_frame',\n ComposeFrameReady = 'compose_frame_ready',\n ConfigureEncode = 'configure_encode',\n EncodeFrame = 'encode_frame',\n EncodeAudio = 'encode_audio',\n EncodedChunk = 'encoded_chunk',\n FlushEncode = 'flush_encode',\n AddChunk = 'add_chunk',\n PerformanceStats = 'performance_stats',\n RenderWindow = 'renderWindow',\n AudioTrackAdd = 'audio_track:add',\n AudioTrackRemove = 'audio_track:remove',\n AudioTrackUpdate = 'audio_track:update',\n}\n\n// Base message structure\nexport interface WorkerMessage<T = any> {\n type: WorkerMessageType;\n id: string; // Unique message ID for request-response pairing\n payload?: T;\n transfer?: Transferable[]; // Objects to transfer ownership\n timestamp: number;\n}\n\n// Response message structure\nexport interface WorkerResponse<T = any> {\n id: string; // Matches request ID\n success: boolean;\n result?: T;\n error?: WorkerError;\n timestamp: number;\n}\n\n// Error structure\nexport interface WorkerError {\n code: string;\n message: string;\n details?: any;\n stack?: string;\n}\n\n// Worker configuration\nexport interface WorkerConfig {\n name: string;\n workerUrl?: string;\n capabilities?: string[];\n maxRetries?: number;\n timeout?: number;\n}\n\n// Message handler type\nexport type MessageHandler<T = any, R = any> = (\n payload: T,\n transfer?: Transferable[]\n) => Promise<R> | R;\n\n// Message handler registry\nexport interface MessageHandlers {\n [key: string]: MessageHandler;\n}\n\n/**\n * Unified stream connection message for the data plane\n * Used by the stream-pipeline refactor to reduce message surface\n */\nexport interface ConnectMsg {\n direction: 'upstream';\n port: MessagePort;\n streamType: 'video' | 'audio' | 'frame' | 'chunk';\n clipId?: string;\n trackId?: string;\n}\n\n// Transferable types used in workers\nexport interface TransferableFrame {\n frame: VideoFrame;\n timestamp: number;\n duration?: number;\n}\n\nexport interface TransferableChunk {\n chunk: EncodedVideoChunk | EncodedAudioChunk;\n timestamp: number;\n duration?: number;\n trackId: string;\n}\n\nexport interface TransferableBuffer {\n buffer: ArrayBuffer;\n offset?: number;\n length?: number;\n}\n\n// Worker state\nexport enum WorkerState {\n Idle = 'idle',\n Initializing = 'initializing',\n Ready = 'ready',\n Processing = 'processing',\n Error = 'error',\n Disposed = 'disposed',\n}\n\n// Performance metrics\nexport interface WorkerPerformanceStats {\n workerId: string;\n workerName: string;\n messagesProcessed: number;\n averageProcessingTime: number;\n maxProcessingTime: number;\n errorCount: number;\n queueSize: number;\n memoryUsage?: number;\n}\n\n// Request tracking\nexport interface PendingRequest {\n id: string;\n type: WorkerMessageType;\n timestamp: number;\n timeout?: number;\n resolve: (value: any) => void;\n reject: (error: any) => void;\n retryCount?: number;\n maxRetries?: number;\n}\n\n// Stream support\nexport interface StreamConfig {\n highWaterMark?: number;\n strategy?: QueuingStrategy;\n}\n\n// Worker pool settings\nexport interface WorkerPoolSettings {\n maxWorkers?: number;\n workerIdleTimeout?: number;\n enableSharedArrayBuffer?: boolean;\n enableOffscreenCanvas?: boolean;\n}\n\n// Message priority levels\nexport enum MessagePriority {\n Low = 0,\n Normal = 1,\n High = 2,\n Critical = 3,\n}\n\n// Priority message\nexport interface PriorityMessage<T = any> extends WorkerMessage<T> {\n priority: MessagePriority;\n}\n","/**\n * Retry utility for worker communication\n * Provides configurable retry logic with exponential backoff\n */\n\nimport { WorkerMessageType } from './types';\n\nexport interface RetryConfig {\n maxRetries: number;\n initialDelay?: number; // Initial delay in ms\n maxDelay?: number; // Max delay in ms\n backoffFactor?: number; // Exponential backoff factor\n retryableErrors?: string[]; // Error codes that can be retried\n}\n\nexport interface RetryContext {\n attempt: number;\n maxRetries: number;\n lastError?: Error;\n messageType: WorkerMessageType;\n}\n\n/**\n * Default retry configuration\n */\nexport const defaultRetryConfig: Required<RetryConfig> = {\n maxRetries: 3,\n initialDelay: 100,\n maxDelay: 5000,\n backoffFactor: 2,\n retryableErrors: ['TIMEOUT', 'NETWORK_ERROR', 'WORKER_BUSY'],\n};\n\n/**\n * Calculate delay for next retry attempt\n */\nexport function calculateRetryDelay(attempt: number, config: RetryConfig): number {\n const { initialDelay = 100, maxDelay = 5000, backoffFactor = 2 } = config;\n\n const delay = initialDelay * Math.pow(backoffFactor, attempt - 1);\n return Math.min(delay, maxDelay);\n}\n\n/**\n * Check if error is retryable\n */\nexport function isRetryableError(error: Error | any, config: RetryConfig): boolean {\n const { retryableErrors = defaultRetryConfig.retryableErrors } = config;\n\n if (!error) return false;\n\n // Check error code\n const errorCode = error.code || error.name;\n if (errorCode && retryableErrors.includes(errorCode)) {\n return true;\n }\n\n // Check error message for known patterns\n const message = error.message || '';\n if (message.includes('timeout') || message.includes('Timeout')) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Execute function with retry logic\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config: RetryConfig\n // context?: Partial<RetryContext>\n): Promise<T> {\n const { maxRetries } = config;\n let lastError: Error | undefined;\n\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error as Error;\n\n // Check if error is retryable\n if (!isRetryableError(error, config)) {\n throw error;\n }\n\n // Check if we've exhausted retries\n if (attempt === maxRetries) {\n throw error;\n }\n\n // Calculate and apply delay\n const delay = calculateRetryDelay(attempt, config);\n await sleep(delay);\n }\n }\n\n // This should never be reached, but TypeScript needs it\n throw lastError || new Error('Retry failed');\n}\n\n/**\n * Sleep for specified milliseconds\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Create a retry wrapper for a specific operation\n */\nexport function createRetryWrapper<T extends (...args: any[]) => Promise<any>>(\n fn: T,\n config: RetryConfig\n): T {\n return (async (...args: Parameters<T>) => {\n return withRetry(() => fn(...args), config);\n }) as T;\n}\n","/**\n * Utility functions for handling Transferable objects\n * Simplifies the process of transferring ownership of objects between workers\n */\n\n/**\n * Check if an object is transferable\n */\nexport function isTransferable(obj: any): obj is Transferable {\n return (\n obj instanceof ArrayBuffer ||\n obj instanceof MessagePort ||\n (typeof ImageBitmap !== 'undefined' && obj instanceof ImageBitmap) ||\n (typeof OffscreenCanvas !== 'undefined' && obj instanceof OffscreenCanvas) ||\n (typeof ReadableStream !== 'undefined' && obj instanceof ReadableStream) ||\n (typeof WritableStream !== 'undefined' && obj instanceof WritableStream) ||\n (typeof TransformStream !== 'undefined' && obj instanceof TransformStream)\n );\n}\n\n/**\n * Recursively find transferable objects in a payload\n */\nfunction findTransferables(obj: any, transferables: Transferable[]): void {\n if (!obj || typeof obj !== 'object') {\n return;\n }\n\n // Check if object itself is transferable\n if (isTransferable(obj)) {\n transferables.push(obj);\n return;\n }\n\n // Check VideoFrame\n if (obj instanceof VideoFrame) {\n transferables.push(obj);\n return;\n }\n\n // Check AudioData\n if (typeof AudioData !== 'undefined' && obj instanceof AudioData) {\n transferables.push(obj);\n return;\n }\n\n // EncodedVideoChunk and EncodedAudioChunk are not transferable\n if (\n (typeof EncodedVideoChunk !== 'undefined' && obj instanceof EncodedVideoChunk) ||\n (typeof EncodedAudioChunk !== 'undefined' && obj instanceof EncodedAudioChunk)\n ) {\n return;\n }\n\n // Recursively check object properties\n if (Array.isArray(obj)) {\n for (const item of obj) {\n findTransferables(item, transferables);\n }\n } else {\n for (const key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n findTransferables(obj[key], transferables);\n }\n }\n }\n}\n\n/**\n * Extract transferable objects from a payload\n */\nexport function extractTransferables(payload: any): Transferable[] {\n const transferables: Transferable[] = [];\n findTransferables(payload, transferables);\n return transferables;\n}\n\n/**\n * Wrap VideoFrame for transfer with metadata\n */\nexport function wrapVideoFrame(frame: VideoFrame) {\n return {\n frame,\n metadata: {\n timestamp: frame.timestamp,\n duration: frame.duration,\n codedWidth: frame.codedWidth,\n codedHeight: frame.codedHeight,\n displayWidth: frame.displayWidth,\n displayHeight: frame.displayHeight,\n format: frame.format,\n },\n };\n}\n\n/**\n * Wrap AudioData for transfer with metadata\n */\nexport function wrapAudioData(audio: AudioData) {\n return {\n audio,\n metadata: {\n timestamp: audio.timestamp,\n duration: audio.duration,\n numberOfFrames: audio.numberOfFrames,\n numberOfChannels: audio.numberOfChannels,\n sampleRate: audio.sampleRate,\n format: audio.format,\n },\n };\n}\n\n/**\n * Convert EncodedVideoChunk to transferable format\n */\nexport function encodedVideoChunkToTransferable(chunk: EncodedVideoChunk) {\n const data = new ArrayBuffer(chunk.byteLength);\n chunk.copyTo(new Uint8Array(data));\n\n return {\n data,\n type: chunk.type,\n timestamp: chunk.timestamp,\n duration: chunk.duration,\n };\n}\n\n/**\n * Convert EncodedAudioChunk to transferable format\n */\nexport function encodedAudioChunkToTransferable(chunk: EncodedAudioChunk) {\n const data = new ArrayBuffer(chunk.byteLength);\n chunk.copyTo(new Uint8Array(data));\n\n return {\n data,\n type: chunk.type,\n timestamp: chunk.timestamp,\n duration: chunk.duration,\n };\n}\n\n/**\n * Recreate EncodedVideoChunk from transferable format\n */\nexport function transferableToEncodedVideoChunk(transferable: {\n data: ArrayBuffer;\n type: 'key' | 'delta';\n timestamp: number;\n duration?: number;\n}): EncodedVideoChunk {\n return new EncodedVideoChunk({\n type: transferable.type,\n timestamp: transferable.timestamp,\n duration: transferable.duration,\n data: transferable.data,\n });\n}\n\n/**\n * Recreate EncodedAudioChunk from transferable format\n */\nexport function transferableToEncodedAudioChunk(transferable: {\n data: ArrayBuffer;\n type: 'key' | 'delta';\n timestamp: number;\n duration?: number;\n}): EncodedAudioChunk {\n return new EncodedAudioChunk({\n type: transferable.type,\n timestamp: transferable.timestamp,\n duration: transferable.duration,\n data: transferable.data,\n });\n}\n\n/**\n * Transfer OffscreenCanvas control to worker\n */\nexport function transferCanvas(canvas: HTMLCanvasElement): OffscreenCanvas {\n return canvas.transferControlToOffscreen();\n}\n\n/**\n * Create a transferable stream pair\n */\nexport function createStreamPair<T>(): {\n readable: ReadableStream<T>;\n writable: WritableStream<T>;\n} {\n const { readable, writable } = new TransformStream<T>();\n return { readable, writable };\n}\n","/**\n * WorkerChannel - MessageChannel wrapper for worker communication\n * Provides type-safe message passing with automatic serialization/deserialization\n */\n\nimport {\n WorkerMessage,\n WorkerResponse,\n WorkerError,\n WorkerMessageType,\n PendingRequest,\n MessageHandler,\n MessageHandlers,\n WorkerState,\n PortLike,\n} from './types';\nimport { withRetry, RetryConfig, defaultRetryConfig } from './worker-retry';\nimport { isTransferable, extractTransferables } from './transferable-helper';\n\nexport interface WorkerChannelConfig {\n name: string;\n timeout?: number; // Default timeout in milliseconds\n maxRetries?: number; // Default max retries\n}\n\nexport class WorkerChannel {\n name: string;\n port: PortLike;\n pendingRequests = new Map<string, PendingRequest>();\n messageHandlers: MessageHandlers = {};\n state: WorkerState = WorkerState.Idle;\n defaultTimeout: number;\n defaultMaxRetries: number;\n\n constructor(port: PortLike, config: WorkerChannelConfig) {\n this.name = config.name;\n this.port = port;\n this.defaultTimeout = config.timeout ?? 30000; // 30 seconds default\n this.defaultMaxRetries = config.maxRetries ?? 3;\n\n this.setupMessageHandler();\n this.state = WorkerState.Ready;\n }\n\n /**\n * Send a message and wait for response with retry support\n */\n async send<T = any, R = any>(\n type: WorkerMessageType,\n payload?: T,\n options?: {\n timeout?: number;\n maxRetries?: number;\n transfer?: Transferable[];\n retryConfig?: Partial<RetryConfig>;\n }\n ): Promise<R> {\n const maxRetries = options?.maxRetries ?? this.defaultMaxRetries;\n\n // Create retry config\n const retryConfig: RetryConfig = {\n ...defaultRetryConfig,\n maxRetries,\n ...options?.retryConfig,\n };\n\n // Wrap send logic for retry\n return withRetry(() => this.sendOnce<T, R>(type, payload, options), retryConfig);\n }\n\n /**\n * Send a message once (without retry)\n */\n private async sendOnce<T = any, R = any>(\n type: WorkerMessageType,\n payload?: T,\n options?: {\n timeout?: number;\n transfer?: Transferable[];\n }\n ): Promise<R> {\n const id = this.generateMessageId();\n const timeout = options?.timeout ?? this.defaultTimeout;\n\n const message: WorkerMessage<T> = {\n type,\n id,\n payload,\n timestamp: Date.now(),\n };\n\n return new Promise((resolve, reject) => {\n const request: PendingRequest = {\n id,\n type,\n timestamp: Date.now(),\n timeout,\n resolve,\n reject,\n };\n\n this.pendingRequests.set(id, request);\n\n // Set timeout\n const timeoutId = setTimeout(() => {\n const pending = this.pendingRequests.get(id);\n if (pending) {\n this.pendingRequests.delete(id);\n const error = new Error(`Request timeout: ${id} ${type} (${timeout}ms)`);\n (error as any).code = 'TIMEOUT';\n pending.reject(error);\n }\n }, timeout);\n\n // Store timeout ID for cleanup\n (request as any).timeoutId = timeoutId;\n // Send message\n if (options?.transfer) {\n this.port.postMessage(message, options.transfer);\n } else {\n this.port.postMessage(message);\n }\n });\n }\n\n /**\n * Send a message without waiting for response\n */\n post<T = any>(type: WorkerMessageType, payload?: T, transfer?: Transferable[]): void {\n const message: WorkerMessage<T> = {\n type,\n id: this.generateMessageId(),\n payload,\n timestamp: Date.now(),\n };\n\n if (transfer) {\n this.port.postMessage(message, transfer);\n } else {\n this.port.postMessage(message);\n }\n }\n\n /**\n * Register a message handler\n */\n on<T = any, R = any>(type: WorkerMessageType, handler: MessageHandler<T, R>): void {\n this.messageHandlers[type] = handler;\n }\n\n /**\n * Unregister a message handler\n */\n off(type: WorkerMessageType): void {\n delete this.messageHandlers[type];\n }\n\n /**\n * Dispose the channel\n */\n dispose(): void {\n this.state = WorkerState.Disposed;\n\n // Clear all pending requests\n for (const [, request] of this.pendingRequests) {\n if ((request as any).timeoutId) {\n clearTimeout((request as any).timeoutId);\n }\n request.reject(new Error('Channel disposed'));\n }\n this.pendingRequests.clear();\n\n // Remove message handler\n this.port.onmessage = null;\n }\n\n /**\n * Setup message handler for incoming messages\n */\n private setupMessageHandler(): void {\n this.port.onmessage = async (event: MessageEvent) => {\n const data = event.data;\n\n // Handle response messages\n if (this.isResponse(data)) {\n this.handleResponse(data as WorkerResponse);\n return;\n }\n\n // Handle request messages\n if (this.isRequest(data)) {\n await this.handleRequest(data as WorkerMessage);\n return;\n }\n };\n }\n\n /**\n * Handle incoming request\n */\n private async handleRequest(message: WorkerMessage): Promise<void> {\n const handler = this.messageHandlers[message.type];\n\n if (!handler) {\n // Send error response if no handler registered\n this.sendResponse(message.id, false, null, {\n code: 'NO_HANDLER',\n message: `No handler registered for message type: ${message.type}`,\n });\n return;\n }\n\n this.state = WorkerState.Processing;\n\n // Handler execution with error handling at framework level\n Promise.resolve()\n .then(() => handler(message.payload, message.transfer))\n .then((result) => {\n this.sendResponse(message.id, true, result);\n this.state = WorkerState.Ready;\n })\n .catch((error) => {\n const workerError: WorkerError = {\n code: 'HANDLER_ERROR',\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n this.sendResponse(message.id, false, null, workerError);\n this.state = WorkerState.Ready;\n });\n }\n\n /**\n * Handle incoming response\n */\n private handleResponse(response: WorkerResponse): void {\n const request = this.pendingRequests.get(response.id);\n if (!request) {\n return; // Response for unknown request\n }\n\n this.pendingRequests.delete(response.id);\n\n // Clear timeout\n if ((request as any).timeoutId) {\n clearTimeout((request as any).timeoutId);\n }\n\n if (response.success) {\n request.resolve(response.result);\n } else {\n const error = new Error(response.error?.message || 'Unknown error');\n if (response.error) {\n Object.assign(error, response.error);\n }\n request.reject(error);\n }\n }\n\n /**\n * Send a response message\n */\n private sendResponse(id: string, success: boolean, result?: any, error?: WorkerError): void {\n let transfer: Transferable[] = [];\n if (isTransferable(result)) {\n // Still include reference so caller can access the transferred object\n transfer.push(result);\n }\n const response: WorkerResponse = {\n id,\n success,\n result,\n error,\n timestamp: Date.now(),\n };\n\n this.port.postMessage(response, transfer);\n }\n\n /**\n * Check if message is a response\n */\n private isResponse(data: any): boolean {\n return (\n data && typeof data === 'object' && 'id' in data && 'success' in data && !('type' in data)\n );\n }\n\n /**\n * Check if message is a request\n */\n private isRequest(data: any): boolean {\n return data && typeof data === 'object' && 'id' in data && 'type' in data;\n }\n\n /**\n * Generate unique message ID\n */\n private generateMessageId(): string {\n return `${this.name}-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n }\n\n /**\n * Send a notification message without waiting for response\n * Alias for post() method for compatibility\n */\n notify<T = any>(type: WorkerMessageType | string, payload?: T, transfer?: Transferable[]): void {\n this.post(type as WorkerMessageType, payload, transfer);\n }\n\n /**\n * Register a message handler\n * Alias for on() method for compatibility\n */\n registerHandler<T = any, R = any>(\n type: WorkerMessageType | string,\n handler: MessageHandler<T, R>\n ): void {\n this.on(type as WorkerMessageType, handler);\n }\n\n /**\n * Send a ReadableStream to another worker\n * Automatically handles transferable streams vs chunk-by-chunk fallback\n */\n async sendStream<T = Uint8Array>(\n stream: ReadableStream<T>,\n metadata?: {\n type?: string;\n url?: string;\n streamId?: string;\n [key: string]: any;\n }\n ): Promise<void> {\n const streamId = metadata?.streamId || this.generateMessageId();\n // Check if streams are transferable in this environment\n if (isTransferable(stream)) {\n // Direct stream transfer (preferred)\n this.port.postMessage(\n {\n type: 'stream_transfer',\n ...metadata,\n stream,\n streamId,\n },\n [stream] // Transfer ownership\n );\n } else {\n // Fallback: chunk-by-chunk transfer\n await this.streamChunks(stream, streamId, metadata);\n }\n }\n\n /**\n * Stream chunks from a ReadableStream (fallback when transfer is not supported)\n */\n private async streamChunks<T = Uint8Array>(\n stream: ReadableStream<T>,\n streamId: string,\n metadata?: Record<string, any>\n ): Promise<void> {\n const reader = stream.getReader();\n\n // Send stream start notification\n this.post('stream_start' as WorkerMessageType, {\n streamId,\n ...metadata,\n mode: 'chunk_transfer',\n });\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n // Send stream end notification\n this.post('stream_end' as WorkerMessageType, {\n streamId,\n ...metadata,\n });\n break;\n }\n\n // Send chunk with transfer - use extractTransferables for complex objects\n const transfer: Transferable[] = [];\n if (value instanceof ArrayBuffer) {\n transfer.push(value);\n } else if (value instanceof Uint8Array) {\n transfer.push(value.buffer);\n } else if (typeof AudioData !== 'undefined' && value instanceof AudioData) {\n transfer.push(value as unknown as Transferable);\n } else if (typeof VideoFrame !== 'undefined' && value instanceof VideoFrame) {\n transfer.push(value as unknown as Transferable);\n } else if (typeof value === 'object' && value !== null) {\n // For wrapped objects (e.g., {frame: VideoFrame, metadata...}), extract transferables\n const extracted = extractTransferables(value);\n transfer.push(...extracted);\n }\n\n this.post(\n 'stream_chunk' as WorkerMessageType,\n {\n streamId,\n chunk: value,\n ...metadata,\n },\n transfer\n );\n }\n } catch (error) {\n // Send error notification\n this.post('stream_error' as WorkerMessageType, {\n streamId,\n error: error instanceof Error ? error.message : String(error),\n ...metadata,\n });\n throw error;\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Receive a stream from another worker\n * Handles both transferable streams and chunk-by-chunk reconstruction\n */\n async receiveStream<T = Uint8Array>(\n onStream: (stream: ReadableStream<T>, metadata?: Record<string, any>) => void\n ): Promise<void> {\n const chunkedStreams = new Map<\n string,\n {\n controller: ReadableStreamDefaultController<T>;\n metadata?: Record<string, any>;\n }\n >();\n\n // Intercept stream messages via onmessage to avoid extra event plumbing\n const prev = this.port.onmessage as any;\n // Use function assignment with explicit type to satisfy esbuild parser\n const handler: (event: MessageEvent) => void = (event) => {\n const raw = (event as any).data;\n const envelopeType = raw?.type;\n const hasPayload = raw && typeof raw === 'object' && 'payload' in raw;\n const payload = hasPayload ? (raw as any).payload : raw;\n\n if (envelopeType === 'stream_transfer' && (payload as any)?.stream) {\n onStream((payload as any).stream, payload);\n return;\n }\n if (envelopeType === 'stream_start' && (payload as any)?.streamId) {\n const stream = new ReadableStream<T>({\n start(controller) {\n chunkedStreams.set((payload as any).streamId, { controller, metadata: payload });\n },\n });\n onStream(stream, payload);\n return;\n }\n if (\n envelopeType === 'stream_chunk' &&\n (payload as any)?.streamId &&\n chunkedStreams.has((payload as any).streamId)\n ) {\n const s = chunkedStreams.get((payload as any).streamId);\n if (s) s.controller.enqueue((payload as any).chunk as T);\n return;\n }\n if (\n envelopeType === 'stream_end' &&\n (payload as any)?.streamId &&\n chunkedStreams.has((payload as any).streamId)\n ) {\n const s = chunkedStreams.get((payload as any).streamId);\n if (s) {\n s.controller.close();\n chunkedStreams.delete((payload as any).streamId);\n }\n return;\n }\n if (\n envelopeType === 'stream_error' &&\n (payload as any)?.streamId &&\n chunkedStreams.has((payload as any).streamId)\n ) {\n const s = chunkedStreams.get((payload as any).streamId);\n if (s) {\n s.controller.error(new Error(String((payload as any).error || 'stream error')));\n chunkedStreams.delete((payload as any).streamId);\n }\n return;\n }\n\n // Delegate non-stream messages to previous handler\n if (typeof prev === 'function') prev.call(this.port, event);\n };\n this.port.onmessage = handler as any;\n }\n}\n"],"names":["WorkerMessageType","WorkerState"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.js";
1
+ import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.CE5euh3R.js";
2
2
  class AudioMixer {
3
3
  config;
4
4
  tracksMap = /* @__PURE__ */ new Map();
@@ -1060,4 +1060,4 @@ export {
1060
1060
  AudioComposeWorker,
1061
1061
  audioCompose_worker as default
1062
1062
  };
1063
- //# sourceMappingURL=audio-compose.worker.js.map
1063
+ //# sourceMappingURL=audio-compose.worker.rW63uN6z.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-compose.worker.rW63uN6z.js","sources":["../../../../src/stages/compose/AudioMixer.ts","../../../../src/stages/compose/AudioDucker.ts","../../../../src/stages/compose/audio-compose.worker.ts"],"sourcesContent":["import type {\n AudioComposeConfig,\n AudioTrack,\n AudioTrackConfig,\n FadeConfig,\n MixRequest,\n MixResult,\n} from './types';\nimport type { AudioDucker } from './AudioDucker';\n\nexport class AudioMixer {\n config: AudioComposeConfig;\n private tracksMap = new Map<string, AudioTrack>();\n\n constructor(config: AudioComposeConfig) {\n this.config = config;\n }\n\n getConfig(): AudioComposeConfig {\n return { ...this.config };\n }\n\n updateConfig(update: Partial<AudioComposeConfig>): void {\n this.config = { ...this.config, ...update };\n }\n\n get tracks(): AudioTrack[] {\n return Array.from(this.tracksMap.values());\n }\n\n createMixStream(ducker?: AudioDucker): TransformStream<MixRequest, MixResult> {\n return new TransformStream<MixRequest, MixResult>(\n {\n transform: async (request, controller) => {\n try {\n const frameCount = this.getFrameCount(request.durationUs);\n\n if (ducker && request.duckingConfig?.enabled && frameCount > 0) {\n const envelope = await ducker.generateDuckingEnvelope(request.tracks, frameCount);\n\n for (const track of request.tracks) {\n if (request.duckingConfig.targetTracks.includes(track.trackId)) {\n track.duckingEnvelope = ducker.applyEnvelopeToVolume(1, envelope);\n }\n }\n }\n\n const result = await this.mixTracks(request, frameCount);\n controller.enqueue(result);\n } catch (error) {\n controller.error(error);\n }\n },\n },\n {\n highWaterMark: 2,\n size: () => 1,\n }\n );\n }\n\n async mixTracks(request: MixRequest, precomputedFrameCount?: number): Promise<MixResult> {\n const tracks = request.tracks ?? [];\n const frameCount = precomputedFrameCount ?? this.getFrameCount(request.durationUs);\n\n const requestedChannelCount = this.config.numberOfChannels ?? 0;\n const inferredChannelCount = tracks.reduce((max, track) => {\n const trackChannels =\n track?.numberOfChannels ??\n track?.audioData?.numberOfChannels ??\n this.config.numberOfChannels ??\n 0;\n return trackChannels > max ? trackChannels : max;\n }, 0);\n const channelCount =\n requestedChannelCount > 0 ? requestedChannelCount : Math.max(inferredChannelCount, 1);\n\n const outputChannels: Float32Array[] = Array.from({ length: channelCount }, () => {\n return new Float32Array(frameCount);\n });\n\n for (const track of tracks) {\n if (!track) {\n continue;\n }\n\n const resolvedAudioData = track.audioData;\n if (!resolvedAudioData) {\n continue;\n }\n\n this.mixTrackIntoOutput(\n outputChannels,\n {\n ...track,\n audioData: resolvedAudioData,\n numberOfChannels:\n track.numberOfChannels ??\n resolvedAudioData.numberOfChannels ??\n this.config.numberOfChannels,\n sampleRate: track.sampleRate ?? resolvedAudioData.sampleRate ?? this.config.sampleRate,\n },\n request.timeUs,\n frameCount\n );\n }\n\n const { peakLevel, rmsLevel } = this.limitAndMeasure(outputChannels);\n const audioData = this.createAudioData(outputChannels, request.timeUs);\n\n return {\n audioData,\n timeUs: request.timeUs,\n durationUs: request.durationUs,\n peakLevel,\n rmsLevel,\n };\n }\n\n addTrack(track: AudioTrack): void {\n this.tracksMap.set(track.id, track);\n }\n\n removeTrack(trackId: string): void {\n this.tracksMap.delete(trackId);\n }\n\n updateTrack(\n trackId: string,\n patch: Partial<AudioTrack> & { config?: Partial<AudioTrackConfig> }\n ): void {\n const track = this.tracksMap.get(trackId);\n if (!track) {\n return;\n }\n\n const { config, ...rest } = patch;\n if (config) {\n Object.assign(track.config, config);\n }\n Object.assign(track, rest);\n }\n\n private mixTrackIntoOutput(\n outputChannels: Float32Array[],\n track: MixRequest['tracks'][0],\n mixStartUs: number,\n totalFrameCount: number\n ): void {\n if (totalFrameCount === 0) {\n track.audioData.close();\n return;\n }\n\n if (track.sampleRate !== this.config.sampleRate) {\n track.audioData.close();\n throw new Error('AudioMixer: sample rate mismatch');\n }\n\n const trackChannelCount = track.audioData.numberOfChannels ?? track.numberOfChannels ?? 0;\n if (trackChannelCount === 0) {\n track.audioData.close();\n return;\n }\n\n const trackChannels = this.extractChannels(track.audioData);\n if (trackChannels.length === 0) {\n track.audioData.close();\n return;\n }\n const trackFrameCount = track.audioData.numberOfFrames;\n\n if (trackFrameCount === 0) {\n track.audioData.close();\n return;\n }\n\n const timestampUs = track.audioData.timestamp ?? mixStartUs;\n const deltaUs = timestampUs - mixStartUs;\n let outputOffsetFrames = Math.round((deltaUs / 1_000_000) * this.config.sampleRate);\n let sourceOffsetFrames = 0;\n\n if (outputOffsetFrames < 0) {\n sourceOffsetFrames = Math.min(trackFrameCount, -outputOffsetFrames);\n outputOffsetFrames = 0;\n }\n\n if (outputOffsetFrames >= totalFrameCount) {\n track.audioData.close();\n return;\n }\n\n const availableFrames = Math.min(\n trackFrameCount - sourceOffsetFrames,\n totalFrameCount - outputOffsetFrames\n );\n\n if (availableFrames <= 0) {\n track.audioData.close();\n return;\n }\n\n const gains = this.buildGainEnvelope(\n track,\n availableFrames,\n outputOffsetFrames,\n sourceOffsetFrames,\n trackFrameCount\n );\n\n const destinationChannelCount = outputChannels.length;\n const sourceChannelCount = trackChannels.length;\n\n for (let channelIndex = 0; channelIndex < destinationChannelCount; channelIndex++) {\n const destination = outputChannels[channelIndex];\n const source = trackChannels[channelIndex] ?? trackChannels[sourceChannelCount - 1];\n if (!destination || !source) continue;\n\n for (let frameIndex = 0; frameIndex < availableFrames; frameIndex++) {\n const sample = source[sourceOffsetFrames + frameIndex] ?? 0;\n const gain = gains[frameIndex] ?? 0;\n destination[outputOffsetFrames + frameIndex] =\n (destination[outputOffsetFrames + frameIndex] ?? 0) + sample * gain;\n }\n }\n\n track.audioData.close();\n }\n\n private buildGainEnvelope(\n track: MixRequest['tracks'][0],\n length: number,\n outputOffsetFrames: number,\n sourceOffsetFrames: number,\n trackFrameCount: number\n ): Float32Array {\n const gains = new Float32Array(length);\n const baseVolume = typeof track.config.volume === 'number' ? track.config.volume : 1;\n gains.fill(baseVolume);\n\n const fadeInSamples = this.getFadeSampleCount(track.config.fadeIn);\n const fadeOutSamples = this.getFadeSampleCount(track.config.fadeOut);\n const clipDurationSamples = this.getClipSampleCount(track.config.durationUs) || trackFrameCount;\n const trackStartFrame = this.computeTrackStartFrame(track);\n\n for (let i = 0; i < length; i++) {\n const envelopeIndex = outputOffsetFrames + i;\n const absoluteFrame = trackStartFrame + sourceOffsetFrames + i;\n let gain = baseVolume;\n\n if (fadeInSamples > 0 && absoluteFrame < fadeInSamples) {\n const progress = Math.min(1, absoluteFrame / fadeInSamples);\n gain *= this.getCurveValue(progress, track.config.fadeIn?.curve);\n }\n\n if (fadeOutSamples > 0 && clipDurationSamples > 0) {\n const fadeStart = Math.max(0, clipDurationSamples - fadeOutSamples);\n if (absoluteFrame >= fadeStart) {\n const progress = Math.min(1, (absoluteFrame - fadeStart) / fadeOutSamples);\n const remaining = Math.max(0, 1 - progress);\n gain *= this.getCurveValue(remaining, track.config.fadeOut?.curve);\n }\n }\n\n if (\n track.duckingEnvelope &&\n envelopeIndex < track.duckingEnvelope.length &&\n envelopeIndex >= 0\n ) {\n gain *= track.duckingEnvelope[envelopeIndex] ?? 1;\n }\n\n gains[i] = gain;\n }\n\n return gains;\n }\n\n private extractChannels(audioData: AudioData): Float32Array[] {\n const configuredChannels = this.config.numberOfChannels ?? 0;\n const channelCount = audioData.numberOfChannels ?? configuredChannels;\n const frameCount = audioData.numberOfFrames;\n const format: string = (audioData as any).format ?? 'f32';\n\n if (!channelCount || !frameCount) {\n return [];\n }\n\n const toFloat = (value: number): number => value / 32768;\n\n const zeroChannels = (): Float32Array[] =>\n Array.from(\n { length: configuredChannels || channelCount },\n () => new Float32Array(frameCount)\n );\n\n if (format === 'f32') {\n const interleaved = new Float32Array(frameCount * channelCount);\n audioData.copyTo(interleaved, { format: 'f32', planeIndex: 0 });\n const channels = zeroChannels();\n for (let frame = 0; frame < frameCount; frame++) {\n const offset = frame * channelCount;\n for (let channel = 0; channel < channels.length; channel++) {\n const channelArray = channels[channel];\n if (!channelArray) continue;\n const sourceChannel = channel < channelCount ? channel : channelCount - 1;\n channelArray[frame] = interleaved[offset + sourceChannel] ?? 0;\n }\n }\n return channels;\n }\n\n if (format === 's16') {\n const interleaved = new Int16Array(frameCount * channelCount);\n audioData.copyTo(interleaved, { format: 's16', planeIndex: 0 });\n const channels = zeroChannels();\n for (let frame = 0; frame < frameCount; frame++) {\n const offset = frame * channelCount;\n for (let channel = 0; channel < channels.length; channel++) {\n const channelArray = channels[channel];\n if (!channelArray) continue;\n const sourceChannel = channel < channelCount ? channel : channelCount - 1;\n channelArray[frame] = toFloat(interleaved[offset + sourceChannel] ?? 0);\n }\n }\n return channels;\n }\n\n if (format === 'f32-planar') {\n const channels = zeroChannels();\n for (let channel = 0; channel < channels.length; channel++) {\n const channelArray = channels[channel];\n if (!channelArray) continue;\n const sourceChannel = channel < channelCount ? channel : channelCount - 1;\n audioData.copyTo(channelArray, { planeIndex: sourceChannel, format: 'f32-planar' });\n }\n return channels;\n }\n\n if (format === 's16-planar') {\n const tmp = new Int16Array(frameCount);\n const channels = zeroChannels();\n for (let channel = 0; channel < channels.length; channel++) {\n const channelArray = channels[channel];\n if (!channelArray) continue;\n const sourceChannel = channel < channelCount ? channel : channelCount - 1;\n audioData.copyTo(tmp, { planeIndex: sourceChannel, format: 's16-planar' as any });\n for (let i = 0; i < frameCount; i++) {\n channelArray[i] = toFloat(tmp[i] ?? 0);\n }\n }\n return channels;\n }\n\n const channels = zeroChannels();\n for (let channel = 0; channel < channels.length; channel++) {\n const channelArray = channels[channel];\n if (!channelArray) continue;\n const sourceChannel = channel < channelCount ? channel : channelCount - 1;\n audioData.copyTo(channelArray, { planeIndex: sourceChannel });\n }\n return channels;\n }\n\n private limitAndMeasure(channels: Float32Array[]): { peakLevel: number; rmsLevel: number } {\n let peak = 0;\n let sumSquares = 0;\n let samples = 0;\n\n for (const channel of channels) {\n for (let i = 0; i < channel.length; i++) {\n let sample = channel[i] ?? 0;\n if (sample > 1) {\n sample = 1;\n } else if (sample < -1) {\n sample = -1;\n }\n\n channel[i] = sample;\n\n const absSample = Math.abs(sample);\n if (absSample > peak) {\n peak = absSample;\n }\n\n sumSquares += sample * sample;\n samples++;\n }\n }\n\n const rmsLevel = samples > 0 ? Math.sqrt(sumSquares / samples) : 0;\n\n return {\n peakLevel: peak,\n rmsLevel,\n };\n }\n\n private createAudioData(channels: Float32Array[], timestampUs: number): AudioData {\n const configuredChannels = this.config.numberOfChannels ?? 0;\n const inferredChannels = channels.length;\n const numberOfChannels = (inferredChannels > 0 ? inferredChannels : configuredChannels) || 1;\n const numberOfFrames = channels[0]?.length ?? 0;\n // console.log('>>>>>>>>>>>>>> createAudioData', channels, this.config, numberOfChannels, numberOfFrames);\n if (numberOfFrames === 0) {\n return new AudioData({\n format: 'f32',\n sampleRate: this.config.sampleRate,\n numberOfFrames: 0,\n numberOfChannels,\n timestamp: timestampUs,\n data: new Float32Array(0),\n });\n }\n\n const interleaved = new Float32Array(numberOfFrames * numberOfChannels);\n\n for (let frame = 0; frame < numberOfFrames; frame++) {\n for (let channel = 0; channel < numberOfChannels; channel++) {\n const sourceChannel = channels[channel] ?? channels[channels.length - 1];\n interleaved[frame * numberOfChannels + channel] = sourceChannel?.[frame] ?? 0;\n }\n }\n\n return new AudioData({\n format: 'f32',\n sampleRate: this.config.sampleRate,\n numberOfFrames,\n numberOfChannels,\n timestamp: timestampUs,\n data: interleaved,\n });\n }\n\n private getFrameCount(durationUs: number): number {\n if (durationUs <= 0) {\n return 0;\n }\n\n return Math.ceil((durationUs / 1_000_000) * this.config.sampleRate);\n }\n\n private getFadeSampleCount(fade?: FadeConfig): number {\n if (!fade || fade.durationUs <= 0) {\n return 0;\n }\n\n return Math.round((fade.durationUs / 1_000_000) * this.config.sampleRate);\n }\n\n private getClipSampleCount(durationUs?: number): number {\n if (!durationUs || durationUs <= 0) {\n return 0;\n }\n\n return Math.round((durationUs / 1_000_000) * this.config.sampleRate);\n }\n\n private computeTrackStartFrame(track: MixRequest['tracks'][0]): number {\n const audioTimestamp = track.audioData.timestamp ?? track.config.startTimeUs;\n const relativeUs = audioTimestamp - track.config.startTimeUs;\n const relativeFrames = Math.round((relativeUs / 1_000_000) * this.config.sampleRate);\n return relativeFrames > 0 ? relativeFrames : 0;\n }\n\n private getCurveValue(progress: number, curve: FadeConfig['curve'] = 'linear'): number {\n const clamped = Math.min(Math.max(progress, 0), 1);\n\n switch (curve) {\n case 'exponential':\n return clamped * clamped;\n case 'logarithmic':\n return Math.log10(clamped * 9 + 1);\n case 'cosine':\n return (1 - Math.cos(clamped * Math.PI)) / 2;\n default:\n return clamped;\n }\n }\n}\n","import type { DuckingConfig, MixRequest } from './types';\n\n/**\n * AudioDucker - Automatic volume ducking for background music\n *\n * Ducking: Audio engineering technique where the volume of one audio source\n * is automatically reduced when another audio source is present.\n *\n * Common use case: Reduce background music volume when voice/narration plays\n * to improve speech intelligibility without completely muting the music.\n *\n * Key parameters:\n * - Threshold: Level at which ducking triggers\n * - Ratio: How much to reduce volume (e.g., 3:1 means reduce by 1/3)\n * - Attack: How quickly volume reduces (typically 10-50ms)\n * - Release: How quickly volume returns to normal (typically 100-500ms)\n * - Hold: Time to maintain ducking after trigger ends\n */\nexport class AudioDucker {\n private config: DuckingConfig | null = null;\n private sampleRate: number;\n\n constructor(sampleRate: number) {\n this.sampleRate = sampleRate;\n }\n\n configure(config: DuckingConfig): void {\n this.config = config;\n }\n\n /**\n * Analyze trigger tracks (voice) and generate ducking envelope\n * Returns gain values (0-1) to apply to target tracks (BGM)\n */\n async generateDuckingEnvelope(\n tracks: MixRequest['tracks'],\n frameCount: number\n ): Promise<Float32Array> {\n if (!this.config?.enabled) {\n return new Float32Array(frameCount).fill(1.0);\n }\n\n // Initialize envelope with no ducking (gain = 1.0)\n const envelope = new Float32Array(frameCount);\n envelope.fill(1.0);\n\n // Find trigger tracks (typically voice/narration)\n const triggerTracks = tracks.filter((t) => this.config!.triggerTracks.includes(t.trackId));\n\n if (triggerTracks.length === 0) {\n return envelope;\n }\n\n // Analyze each trigger track for voice activity\n for (const track of triggerTracks) {\n const voiceActivity = await this.detectVoiceActivity(track.audioData);\n this.applyDuckingToEnvelope(envelope, voiceActivity);\n }\n\n return envelope;\n }\n\n /**\n * Voice Activity Detection (VAD)\n * Simple energy-based detection with smoothing\n * More sophisticated implementations could use:\n * - Zero-crossing rate (ZCR) for speech/music discrimination\n * - Spectral centroid for voice frequency detection\n * - Machine learning models for robust VAD\n */\n private async detectVoiceActivity(audioData: AudioData): Promise<Float32Array> {\n const frameCount = audioData.numberOfFrames;\n const activity = new Float32Array(frameCount);\n\n // Convert to mono for analysis\n const monoData = new Float32Array(frameCount);\n const channelData = new Float32Array(frameCount);\n\n for (let ch = 0; ch < audioData.numberOfChannels; ch++) {\n audioData.copyTo(channelData, {\n planeIndex: ch,\n format: 'f32-planar' as const,\n });\n\n for (let i = 0; i < frameCount; i++) {\n if (monoData && channelData) {\n monoData[i] = (monoData[i] || 0) + (channelData[i] || 0) / audioData.numberOfChannels;\n }\n }\n }\n\n // Energy calculation with windowing\n // Window size: 20ms is typical for speech analysis\n const windowSize = Math.floor(this.sampleRate * 0.02);\n const hopSize = Math.floor(windowSize / 2); // 50% overlap\n\n for (let i = 0; i < frameCount; i += hopSize) {\n const end = Math.min(i + windowSize, frameCount);\n\n // Calculate RMS energy in window\n let energy = 0;\n for (let j = i; j < end; j++) {\n if (monoData && monoData[j] !== undefined) {\n const sample = monoData[j];\n if (sample !== undefined) {\n energy += sample * sample;\n }\n }\n }\n energy = Math.sqrt(energy / (end - i));\n\n // Simple threshold-based VAD\n // Typical speech energy threshold: -40dB to -30dB\n const threshold = 0.01; // Approximately -40dB\n const isVoice = energy > threshold;\n\n // Fill activity array for this window\n for (let j = i; j < end; j++) {\n activity[j] = isVoice ? 1.0 : 0.0;\n }\n }\n\n // Smooth activity detection to avoid rapid changes\n return this.smoothActivityDetection(activity);\n }\n\n /**\n * Smooth voice activity detection to avoid choppy ducking\n * Uses a simple moving average filter\n */\n private smoothActivityDetection(activity: Float32Array): Float32Array {\n const smoothed = new Float32Array(activity.length);\n const smoothWindow = Math.floor(this.sampleRate * 0.05); // 50ms smoothing\n\n for (let i = 0; i < activity.length; i++) {\n let sum = 0;\n let count = 0;\n\n for (\n let j = Math.max(0, i - smoothWindow);\n j <= Math.min(activity.length - 1, i + smoothWindow);\n j++\n ) {\n if (activity && activity[j] !== undefined) {\n const val = activity[j];\n if (val !== undefined) {\n sum += val;\n }\n }\n count++;\n }\n\n smoothed[i] = sum / count;\n }\n\n return smoothed;\n }\n\n /**\n * Apply ducking based on voice activity\n * Implements attack/release envelope shaping\n */\n private applyDuckingToEnvelope(envelope: Float32Array, voiceActivity: Float32Array): void {\n if (!this.config) return;\n\n const duckingLevel = 1.0 - this.config.duckingLevel;\n const attackSamples = Math.floor((this.config.attackTimeMs / 1000) * this.sampleRate);\n const releaseSamples = Math.floor((this.config.releaseTimeMs / 1000) * this.sampleRate);\n const lookAheadSamples = this.config.lookAheadMs\n ? Math.floor((this.config.lookAheadMs / 1000) * this.sampleRate)\n : 0;\n\n let currentGain = 1.0;\n let releaseCounter = 0;\n\n for (let i = 0; i < envelope.length; i++) {\n // Look ahead for upcoming voice activity\n const lookAheadIndex = Math.min(i + lookAheadSamples, voiceActivity.length - 1);\n const activity = voiceActivity[lookAheadIndex];\n\n if (activity !== undefined && activity > 0.5) {\n // Voice detected - apply ducking with attack curve\n if (currentGain > duckingLevel) {\n // Attack phase - reduce gain\n currentGain = Math.max(duckingLevel, currentGain - (1.0 - duckingLevel) / attackSamples);\n } else {\n currentGain = duckingLevel;\n }\n releaseCounter = 0;\n } else if (currentGain < 1.0) {\n // No voice - apply release curve\n releaseCounter++;\n if (releaseCounter > releaseSamples * 0.1) {\n // Small hold time\n currentGain = Math.min(1.0, currentGain + (1.0 - duckingLevel) / releaseSamples);\n }\n }\n\n // Apply the calculated gain\n envelope[i] = Math.min(envelope[i] || 1, currentGain);\n }\n }\n\n /**\n * Apply ducking envelope to audio buffer\n * This modulates the volume over time according to the envelope\n */\n applyEnvelopeToVolume(baseVolume: number, envelope: Float32Array): Float32Array {\n const result = new Float32Array(envelope.length);\n for (let i = 0; i < envelope.length; i++) {\n result[i] = baseVolume * (envelope[i] || 1);\n }\n return result;\n }\n\n /**\n * Calculate dynamic range to avoid over-compression\n * Returns the difference between peak and RMS levels in dB\n */\n calculateDynamicRange(envelope: Float32Array): number {\n let peak = 0;\n let sumSquares = 0;\n\n for (const value of envelope) {\n peak = Math.max(peak, value);\n sumSquares += value * value;\n }\n\n const rms = Math.sqrt(sumSquares / envelope.length);\n\n // Convert to dB (20 * log10(ratio))\n const peakDb = 20 * Math.log10(peak);\n const rmsDb = 20 * Math.log10(rms);\n\n return peakDb - rmsDb;\n }\n}\n","import { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { AudioMixer } from './AudioMixer';\nimport { AudioDucker } from './AudioDucker';\nimport {\n AudioComposeConfig,\n MixRequest,\n MixResult,\n DuckingConfig,\n AudioTrack,\n AudioTrackConfig,\n} from './types';\n\ntype TimeUs = number;\n\ninterface TrackBufferItem {\n audioData: AudioData;\n timestampUs: TimeUs;\n durationUs: TimeUs;\n}\n\ninterface TrackBuffer {\n clipId: string;\n queue: TrackBufferItem[];\n ended: boolean;\n config: AudioTrackConfig;\n type: AudioTrack['type'];\n}\n\n/**\n * AudioComposeWorker - Audio mixing and ducking stage\n * Mixes multiple audio tracks with volume, ducking, and effects\n *\n * Pipeline: DecodeWorker → AudioComposeWorker → EncodeWorker\n *\n * Features:\n * - Multi-track PCM mixing\n * - BGM ducking for voice tracks\n * - Stream-based processing with backpressure\n * - Real-time volume and effect adjustments\n */\nexport class AudioComposeWorker {\n private channel: WorkerChannel;\n private mixer: AudioMixer | null = null;\n private ducker: AudioDucker | null = null;\n private mixStream: TransformStream<MixRequest, MixResult> | null = null;\n\n // Connections to other workers\n private decoderPort: MessagePort | null = null;\n private encoderPort: MessagePort | null = null;\n // Track buffer map\n private trackBuffers = new Map<string, TrackBuffer>();\n private trackQueueWaiters = new Map<string, Array<() => void>>();\n private encoderStreamAttached = false;\n private readonly mixWindowUs: TimeUs = 40_000; // 40ms window\n private readonly maxQueuedSegments = 8;\n private mixing = false;\n\n constructor() {\n // Initialize WorkerChannel\n this.channel = new WorkerChannel(self as any, {\n name: 'AudioComposeWorker',\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\n this.channel.registerHandler('connect' as any, this.handleConnect.bind(this));\n this.channel.registerHandler('add_track', this.handleAddTrack.bind(this));\n this.channel.registerHandler('remove_track', this.handleRemoveTrack.bind(this));\n this.channel.registerHandler('update_track', this.handleUpdateTrack.bind(this));\n this.channel.registerHandler('configure_ducking', this.handleConfigureDucking.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 /** Unified connect handler mapping for stream pipeline */\n private async handleConnect(payload: {\n direction: 'upstream' | 'downstream';\n port: MessagePort;\n streamType?: string;\n }): Promise<{ success: boolean }> {\n if (payload.direction === 'upstream') {\n this.decoderPort = payload.port;\n const decoderChannel = new WorkerChannel(this.decoderPort, {\n name: 'AudioCompose-Decoder',\n timeout: 30000,\n });\n\n decoderChannel.registerHandler('audio_track:add', this.handleAddTrack.bind(this));\n decoderChannel.registerHandler('audio_track:remove', this.handleRemoveTrack.bind(this));\n decoderChannel.registerHandler('audio_track:update', this.handleUpdateTrack.bind(this));\n\n decoderChannel.receiveStream(this.handleReceiveStream.bind(this));\n return { success: true };\n }\n\n if (payload.direction === 'downstream') {\n // if (payload.streamType === 'preview') {\n // this.previewPort = payload.port;\n // this.previewChannel?.dispose?.();\n // this.previewChannel = new WorkerChannel(this.previewPort, {\n // name: 'AudioCompose-Preview',\n // timeout: 30000,\n // });\n // return { success: true };\n // }\n\n this.encoderPort = payload.port;\n return { success: true };\n }\n\n return { success: true };\n }\n\n private async handleReceiveStream(\n stream: ReadableStream,\n metadata?: Record<string, any>\n ): Promise<void> {\n if (metadata?.streamType !== 'audio' || !this.mixStream || !this.mixer) {\n return;\n }\n\n const update: Partial<AudioComposeConfig> = {};\n const currentConfig = this.mixer.getConfig();\n\n if (typeof metadata?.sampleRate === 'number' && metadata.sampleRate > 0) {\n if (!currentConfig.sampleRate || currentConfig.sampleRate !== metadata.sampleRate) {\n update.sampleRate = metadata.sampleRate;\n }\n }\n\n if (typeof metadata?.numberOfChannels === 'number' && metadata.numberOfChannels > 0) {\n if (\n !currentConfig.numberOfChannels ||\n metadata.numberOfChannels > currentConfig.numberOfChannels\n ) {\n update.numberOfChannels = metadata.numberOfChannels;\n }\n }\n\n if (Object.keys(update).length > 0) {\n this.mixer.updateConfig(update);\n }\n\n const mixerConfig = this.mixer.getConfig();\n\n const streamMetadata = {\n ...metadata,\n streamType: 'audio',\n sampleRate: mixerConfig.sampleRate,\n numberOfChannels: mixerConfig.numberOfChannels,\n };\n\n await this.attachEncodeStream(streamMetadata);\n const trackId = (metadata as any)?.trackId ?? (metadata as any)?.clipId;\n if (!trackId) {\n console.warn('[AudioComposeWorker] Missing track identifier in audio stream metadata');\n await stream.cancel();\n return;\n }\n\n await this.bufferTrackStream(trackId, stream as ReadableStream<AudioData>, streamMetadata);\n this.scheduleMix();\n }\n\n /**\n * Configure audio composer\n * @param payload.config - Audio composition configuration\n * @param payload.initial - If true, initialize worker state; otherwise just update config\n */\n private async handleConfigure(payload: {\n config: AudioComposeConfig;\n initial?: boolean;\n }): Promise<{ success: boolean }> {\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 mixer and ducker instances\n // Previous instances will be garbage collected\n\n this.mixer = new AudioMixer(config);\n this.ducker = new AudioDucker(config.sampleRate);\n\n // Create stream with ducking support\n this.mixStream = this.mixer.createMixStream(this.ducker);\n\n // Notify configuration complete\n this.channel.notify('configured', {\n sampleRate: config.sampleRate,\n numberOfChannels: config.numberOfChannels,\n });\n } else {\n // Update configuration only (e.g., backpressure settings)\n if (!this.mixer || !this.ducker) {\n throw {\n code: 'NOT_INITIALIZED',\n message: 'Audio composer not initialized. Call configure with initial=true first',\n };\n }\n\n // AudioMixer doesn't support runtime config updates\n // Would need initial=true for changes\n }\n\n return { success: true };\n } catch (error: any) {\n throw {\n code: error.code || 'CONFIG_ERROR',\n message: error.message,\n };\n }\n }\n\n /**\n * Connect to decoder worker to receive audio streams\n */\n /**\n * Add an audio track\n */\n private handleAddTrack(payload: {\n clipId: string;\n trackId: string;\n config: AudioTrackConfig;\n type?: AudioTrack['type'];\n }): { success: boolean } {\n if (!this.mixer) {\n throw {\n code: 'NOT_CONFIGURED',\n message: 'Mixer not configured',\n };\n }\n\n const config = this.cloneTrackConfig(payload.config);\n const track: AudioTrack = {\n id: payload.trackId,\n clipId: payload.clipId,\n type: payload.type ?? 'other',\n config,\n };\n\n this.mixer.addTrack(track);\n this.trackBuffers.set(payload.trackId, {\n clipId: payload.clipId,\n queue: [],\n ended: false,\n config,\n type: payload.type ?? 'other',\n });\n\n this.channel.notify('track_added', {\n trackId: track.id,\n trackType: track.type,\n });\n\n return { success: true };\n }\n\n /**\n * Remove an audio track\n */\n private handleRemoveTrack(payload: { clipId?: string; trackId: string }): { success: boolean } {\n if (!this.mixer) {\n throw {\n code: 'NOT_CONFIGURED',\n message: 'Mixer not configured',\n };\n }\n\n this.mixer.removeTrack(payload.trackId);\n this.disposeTrackBuffer(payload.trackId);\n\n this.channel.notify('track_removed', {\n trackId: payload.trackId,\n });\n\n return { success: true };\n }\n\n /**\n * Update track configuration\n */\n private handleUpdateTrack(payload: {\n trackId: string;\n config: Partial<AudioTrackConfig> & { type?: AudioTrack['type'] };\n }): {\n success: boolean;\n } {\n if (!this.mixer) {\n throw {\n code: 'NOT_CONFIGURED',\n message: 'Mixer not configured',\n };\n }\n\n this.mixer.updateTrack(payload.trackId, payload.config);\n const buffer = this.trackBuffers.get(payload.trackId);\n if (buffer) {\n if (payload.config.type) {\n buffer.type = payload.config.type;\n }\n this.applyTrackConfigPatch(buffer.config, payload.config);\n }\n\n this.channel.notify('track_updated', {\n trackId: payload.trackId,\n });\n\n return { success: true };\n }\n\n /**\n * Configure audio ducking\n */\n private handleConfigureDucking(config: DuckingConfig): { success: boolean } {\n if (!this.ducker) {\n throw {\n code: 'NOT_CONFIGURED',\n message: 'Ducker not configured',\n };\n }\n\n this.ducker.configure(config);\n\n this.channel.notify('ducking_configured', {\n enabled: config.enabled,\n });\n\n return { success: true };\n }\n\n /**\n * Get mixer statistics\n */\n private async handleGetStats(): Promise<{\n tracks?: any[];\n ducking?: any;\n state?: WorkerState;\n }> {\n if (!this.mixer) {\n return { state: this.channel.state };\n }\n\n return {\n tracks: this.mixer.tracks,\n ducking: this.ducker\n ? {\n configured: this.ducker !== null,\n }\n : null,\n state: this.channel.state,\n };\n }\n\n /**\n * Dispose worker and cleanup resources\n */\n private async handleDispose(): Promise<{ success: boolean }> {\n // Clean up references\n this.mixer = null;\n this.ducker = null;\n this.mixStream = null;\n\n // Close connections\n this.decoderPort?.close();\n this.decoderPort = null;\n this.encoderPort?.close();\n this.encoderPort = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n\n private async attachEncodeStream(metadata: Record<string, any>): Promise<void> {\n if (!this.mixStream || !this.encoderPort || this.encoderStreamAttached || !this.mixer) {\n return;\n }\n\n const encoderChannel = new WorkerChannel(this.encoderPort, {\n name: 'AudioCompose-Encoder',\n timeout: 30000,\n });\n\n const mixerConfig = this.mixer?.getConfig();\n\n const streamMetadata = {\n ...metadata,\n streamType: 'audio',\n sampleRate: mixerConfig?.sampleRate ?? metadata?.sampleRate,\n numberOfChannels: mixerConfig?.numberOfChannels ?? metadata?.numberOfChannels,\n };\n\n const [encoderResultStream, previewResultStream] = this.mixStream.readable.tee();\n\n const createAudioDataStream = (\n source: ReadableStream<MixResult>\n ): ReadableStream<AudioData> => {\n return new ReadableStream<AudioData>({\n start: (controller) => {\n const reader = source.getReader();\n\n const pump = async (): Promise<void> => {\n const { done, value } = await reader.read();\n if (done) {\n reader.releaseLock();\n controller.close();\n return;\n }\n\n try {\n controller.enqueue(value.audioData);\n } catch (error) {\n controller.error(error);\n reader.releaseLock();\n return;\n }\n\n await pump();\n };\n\n pump().catch((error) => {\n reader.releaseLock();\n controller.error(error);\n });\n },\n });\n };\n\n const encoderStream = createAudioDataStream(encoderResultStream);\n const previewStream = createAudioDataStream(previewResultStream);\n\n await encoderChannel.sendStream(encoderStream, streamMetadata);\n\n this.channel.sendStream(previewStream, streamMetadata);\n\n this.encoderStreamAttached = true;\n await this.scheduleMix();\n }\n\n private async bufferTrackStream(\n trackId: string,\n stream: ReadableStream<AudioData>,\n metadata: Record<string, any>\n ): Promise<void> {\n const buffer = this.trackBuffers.get(trackId);\n if (!buffer) {\n await stream.cancel();\n return;\n }\n\n const reader = stream.getReader();\n const process = async (): Promise<void> => {\n while (true) {\n if (buffer.queue.length >= this.maxQueuedSegments) {\n await this.waitForQueueSpace(trackId);\n }\n\n const { done, value } = await reader.read();\n if (done) {\n buffer.ended = true;\n reader.releaseLock();\n return;\n }\n\n buffer.queue.push({\n audioData: value,\n timestampUs: value.timestamp ?? 0,\n durationUs:\n value.duration ??\n Math.round((value.numberOfFrames / (metadata.sampleRate ?? 48_000)) * 1_000_000),\n });\n\n // mix scheduling is triggered after queue updates via scheduleMix()\n this.scheduleMix();\n }\n };\n\n process().catch((error) => {\n buffer.ended = true;\n reader.releaseLock();\n console.error('[AudioComposeWorker] Track stream error:', error);\n });\n }\n\n private async scheduleMix(): Promise<void> {\n if (this.mixing || !this.mixStream || !this.encoderStreamAttached || !this.mixer) {\n return;\n }\n\n const window = this.computeNextWindow();\n if (!window) {\n return;\n }\n\n this.mixing = true;\n try {\n const request = this.buildMixRequest(window.timeUs, window.durationUs);\n if (!request) {\n return;\n }\n\n const writer = this.mixStream.writable.getWriter();\n try {\n await writer.write(request);\n } finally {\n writer.releaseLock();\n }\n } catch (error) {\n console.error('[AudioComposeWorker] Failed to enqueue mix request:', error);\n } finally {\n this.mixing = false;\n if (this.hasBufferedAudio()) {\n queueMicrotask(() => {\n void this.scheduleMix();\n });\n }\n }\n }\n\n private computeNextWindow(): { timeUs: TimeUs; durationUs: TimeUs } | null {\n let earliest: TimeUs | null = null;\n\n for (const buffer of this.trackBuffers.values()) {\n if (buffer.queue.length === 0) {\n continue;\n }\n const firstFrame = buffer.queue[0];\n if (!firstFrame) {\n continue;\n }\n const ts = firstFrame.timestampUs;\n if (earliest === null || ts < earliest) {\n earliest = ts;\n }\n }\n\n if (earliest === null) {\n return null;\n }\n\n return {\n timeUs: earliest,\n durationUs: this.mixWindowUs,\n };\n }\n\n private buildMixRequest(timeUs: TimeUs, _durationUs: TimeUs): MixRequest | null {\n if (!this.mixer) {\n return null;\n }\n\n const tracks: MixRequest['tracks'] = [];\n let resolvedDurationUs = this.mixWindowUs;\n\n for (const [trackId, buffer] of this.trackBuffers.entries()) {\n const segment = this.consumeSegment(trackId, buffer);\n if (!segment) {\n continue;\n }\n\n tracks.push({\n trackId,\n clipId: buffer.clipId,\n audioData: segment.audioData,\n config: segment.config,\n type: buffer.type,\n sampleRate: this.mixer.config.sampleRate,\n numberOfChannels: this.mixer.config.numberOfChannels,\n });\n\n resolvedDurationUs = Math.min(resolvedDurationUs, segment.durationUs ?? this.mixWindowUs);\n }\n\n if (tracks.length === 0) {\n return null;\n }\n\n return {\n tracks,\n timeUs,\n durationUs: resolvedDurationUs,\n };\n }\n\n private consumeSegment(\n trackId: string,\n buffer: TrackBuffer\n ): {\n audioData: AudioData;\n config: AudioTrackConfig;\n sampleRate: number;\n numberOfChannels: number;\n durationUs?: TimeUs;\n } | null {\n if (buffer.queue.length === 0) {\n return null;\n }\n\n const head = buffer.queue[0];\n if (!head) {\n return null;\n }\n\n const queueItem = buffer.queue.shift();\n if (!queueItem) {\n return null;\n }\n\n this.resolveQueueWaiter(trackId);\n\n return {\n audioData: queueItem.audioData,\n config: buffer.config,\n sampleRate: this.mixer!.config.sampleRate,\n numberOfChannels: this.mixer!.config.numberOfChannels,\n durationUs: queueItem.durationUs,\n };\n }\n\n private hasBufferedAudio(): boolean {\n for (const buffer of this.trackBuffers.values()) {\n if (buffer.queue.length > 0) {\n return true;\n }\n }\n return false;\n }\n\n private cloneTrackConfig(config: AudioTrackConfig): AudioTrackConfig {\n return {\n startTimeUs: config.startTimeUs,\n durationUs: config.durationUs,\n volume: config.volume,\n fadeIn: config.fadeIn ? { ...config.fadeIn } : undefined,\n fadeOut: config.fadeOut ? { ...config.fadeOut } : undefined,\n effects: config.effects\n ? config.effects.map((effect) => ({\n type: effect.type,\n params: effect.params ?? {},\n }))\n : undefined,\n duckingTag: config.duckingTag,\n };\n }\n\n private applyTrackConfigPatch(target: AudioTrackConfig, patch: Partial<AudioTrackConfig>): void {\n if (patch.fadeIn) {\n target.fadeIn = { ...patch.fadeIn };\n } else if (patch.fadeIn === null) {\n target.fadeIn = undefined;\n }\n\n if (patch.fadeOut) {\n target.fadeOut = { ...patch.fadeOut };\n } else if (patch.fadeOut === null) {\n target.fadeOut = undefined;\n }\n\n if (patch.effects) {\n target.effects = patch.effects.map((effect) => ({\n type: effect.type,\n params: effect.params ?? {},\n }));\n }\n\n if (typeof patch.volume === 'number') {\n target.volume = patch.volume;\n }\n\n if (patch.startTimeUs !== undefined) {\n target.startTimeUs = patch.startTimeUs;\n }\n\n if (patch.durationUs !== undefined) {\n target.durationUs = patch.durationUs;\n }\n\n if (patch.duckingTag !== undefined) {\n target.duckingTag = patch.duckingTag;\n }\n }\n\n private async waitForQueueSpace(trackId: string): Promise<void> {\n const existing = this.trackQueueWaiters.get(trackId);\n if (existing && existing.length === 0) {\n this.trackQueueWaiters.delete(trackId);\n }\n\n await new Promise<void>((resolve) => {\n const waiters = this.trackQueueWaiters.get(trackId);\n if (waiters) {\n waiters.push(resolve);\n } else {\n this.trackQueueWaiters.set(trackId, [resolve]);\n }\n });\n }\n\n private resolveQueueWaiter(trackId: string): void {\n const waiters = this.trackQueueWaiters.get(trackId);\n if (!waiters || waiters.length === 0) {\n return;\n }\n\n const resolve = waiters.shift();\n resolve?.();\n if (!waiters.length) {\n this.trackQueueWaiters.delete(trackId);\n }\n }\n\n private disposeTrackBuffer(trackId: string): void {\n const buffer = this.trackBuffers.get(trackId);\n if (!buffer) {\n return;\n }\n\n for (const item of buffer.queue) {\n try {\n item.audioData.close();\n } catch (error) {\n console.warn('[AudioComposeWorker] Failed to close AudioData on disposal', error);\n }\n }\n\n this.trackBuffers.delete(trackId);\n\n const waiters = this.trackQueueWaiters.get(trackId);\n if (waiters) {\n waiters.forEach((resolve) => resolve());\n this.trackQueueWaiters.delete(trackId);\n }\n }\n}\n\n// Initialize worker\nconst worker = new AudioComposeWorker();\n\n// Handle worker termination\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null; // Required for TypeScript worker compilation\n"],"names":["channels"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.js";
1
+ import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.CE5euh3R.js";
2
2
  const MICROSECONDS_PER_SECOND = 1e6;
3
3
  const DEFAULT_FPS = 30;
4
4
  function normalizeFps(value) {
@@ -928,6 +928,7 @@ class LayerRenderer {
928
928
  } else {
929
929
  this.ctx.drawImage(videoFrame, renderX, renderY, renderWidth, renderHeight);
930
930
  }
931
+ videoFrame.close();
931
932
  }
932
933
  async renderImageLayer(layer) {
933
934
  const { source, crop } = layer;
@@ -1452,6 +1453,12 @@ class FilterProcessor {
1452
1453
  return this.filterCache.size;
1453
1454
  }
1454
1455
  }
1456
+ const closeLayerFrame = (layer) => {
1457
+ if (layer.type === "video") {
1458
+ const vf = layer.videoFrame;
1459
+ vf?.close?.();
1460
+ }
1461
+ };
1455
1462
  class VideoComposer {
1456
1463
  config;
1457
1464
  canvas;
@@ -1509,18 +1516,18 @@ class VideoComposer {
1509
1516
  fonts: config.fonts ?? []
1510
1517
  };
1511
1518
  }
1512
- createStreams(_instruction) {
1513
- if (_instruction?.baseConfig.timeline) {
1514
- this.timelineContext = _instruction.baseConfig.timeline;
1519
+ createStreams(instruction) {
1520
+ if (instruction?.baseConfig.timeline) {
1521
+ this.timelineContext = instruction.baseConfig.timeline;
1515
1522
  }
1516
1523
  const stream = new TransformStream(
1517
1524
  {
1518
1525
  transform: async (request, controller) => {
1519
1526
  const result = await this.composeFrame(request);
1520
- controller.enqueue({
1521
- frame: result.frame,
1522
- metadata: result.metadata
1523
- });
1527
+ controller.enqueue(result.frame);
1528
+ setTimeout(() => {
1529
+ result.frame.close();
1530
+ }, 1e3);
1524
1531
  },
1525
1532
  flush: async () => {
1526
1533
  this.filterProcessor.clearCache();
@@ -1533,12 +1540,7 @@ class VideoComposer {
1533
1540
  highWaterMark: this.config.outputHighWaterMark
1534
1541
  }
1535
1542
  );
1536
- const [encodeStream, cacheStream] = stream.readable.tee();
1537
- return {
1538
- composeStream: stream.writable,
1539
- cacheStream,
1540
- encodeStream
1541
- };
1543
+ return stream;
1542
1544
  }
1543
1545
  async composeFrame(request) {
1544
1546
  if (request.layers.length > this.config.maxLayers) {
@@ -1554,10 +1556,7 @@ class VideoComposer {
1554
1556
  }
1555
1557
  for (const layer of request.layers) {
1556
1558
  if (!layer.visible || layer.opacity <= 0) {
1557
- if (layer.type === "video") {
1558
- const vf = layer.videoFrame;
1559
- vf?.close?.();
1560
- }
1559
+ closeLayerFrame(layer);
1561
1560
  continue;
1562
1561
  }
1563
1562
  try {
@@ -1569,29 +1568,18 @@ class VideoComposer {
1569
1568
  if (layer.filters && layer.filters.length > 0) {
1570
1569
  this.ctx.restore();
1571
1570
  }
1572
- } finally {
1573
- if (layer.type === "video") {
1574
- const vf = layer.videoFrame;
1575
- vf?.close?.();
1576
- }
1571
+ } catch (error) {
1572
+ console.error("[VideoComposer] composeFrame error: ", error);
1573
+ closeLayerFrame(layer);
1577
1574
  }
1578
1575
  }
1579
1576
  if (request.transition) {
1580
1577
  this.ctx.restore();
1581
1578
  }
1582
1579
  const frame = await this.createOutputFrame(request.timeUs);
1583
- const gopSerial = request.gopSerial;
1584
- const isKeyframe = request.isKeyframe;
1585
1580
  return {
1586
1581
  frame,
1587
- timeUs: request.timeUs,
1588
- metadata: {
1589
- layerCount: request.layers.length,
1590
- renderTime: 0,
1591
- gpuAccelerated: this.config.enableHardwareAcceleration && this.config.renderer !== "canvas2d",
1592
- ...typeof gopSerial === "number" && { gopSerial },
1593
- ...typeof isKeyframe === "boolean" && { isKeyframe }
1594
- }
1582
+ timeUs: request.timeUs
1595
1583
  };
1596
1584
  }
1597
1585
  async composeTransition(fromRequest, toRequest, transition) {
@@ -1939,44 +1927,38 @@ class VideoComposeWorker {
1939
1927
  }
1940
1928
  const filteredStream = stream.pipeThrough(
1941
1929
  new TransformStream({
1942
- transform: (wrappedFrame, controller) => {
1930
+ transform: (frame, controller) => {
1943
1931
  try {
1944
- const frame = wrappedFrame.frame || wrappedFrame;
1945
- const gopSerial = wrappedFrame.gopSerial;
1946
- const isKeyframe = wrappedFrame.isKeyframe;
1947
1932
  const request = this.buildComposeRequest(this.instructions, frame);
1948
1933
  if (!request) {
1949
1934
  frame.close();
1950
1935
  return;
1951
1936
  }
1952
- request.gopSerial = gopSerial;
1953
- request.isKeyframe = isKeyframe;
1954
1937
  controller.enqueue(request);
1955
1938
  } catch (error) {
1956
- const frame = wrappedFrame.frame || wrappedFrame;
1957
1939
  frame?.close?.();
1958
1940
  throw error;
1959
1941
  }
1960
1942
  }
1961
1943
  })
1962
1944
  );
1963
- const { composeStream, cacheStream, encodeStream } = this.composer.createStreams();
1964
- this.channel.sendStream(cacheStream, metadata);
1965
- filteredStream.pipeTo(composeStream).catch((error) => {
1945
+ const composeStream = this.composer.createStreams();
1946
+ filteredStream.pipeTo(composeStream.writable).catch((error) => {
1966
1947
  console.error("[VideoComposeWorker] compose stream error", this.sessionId, error);
1967
1948
  });
1949
+ const meta = {
1950
+ ...metadata,
1951
+ streamType: "video",
1952
+ sessionId: this.sessionId
1953
+ };
1968
1954
  if (this.downstreamPort) {
1969
1955
  const encodeChannel = new WorkerChannel(this.downstreamPort, {
1970
1956
  name: "VideoCompose-Encode",
1971
1957
  timeout: 3e4
1972
1958
  });
1973
- encodeChannel.sendStream(encodeStream, {
1974
- ...metadata,
1975
- streamType: "video",
1976
- sessionId: this.sessionId
1977
- });
1959
+ encodeChannel.sendStream(composeStream.readable, meta);
1978
1960
  } else {
1979
- encodeStream.cancel();
1961
+ this.channel.sendStream(composeStream.readable, meta);
1980
1962
  }
1981
1963
  }
1982
1964
  // private handleGetStream(): ReadableStream<VideoFrame> | undefined {
@@ -2088,7 +2070,7 @@ class VideoComposeWorker {
2088
2070
  console.warn("[VideoComposeWorker] Main track ImageBitmap not found:", mainResourceId);
2089
2071
  return;
2090
2072
  }
2091
- const { composeStream, cacheStream, encodeStream } = this.composer.createStreams();
2073
+ const composeStream = this.composer.createStreams();
2092
2074
  const { clipDurationUs, compositionFps } = timeline;
2093
2075
  let currentTimeUs = 0;
2094
2076
  const readableStream = new ReadableStream({
@@ -2101,31 +2083,35 @@ class VideoComposeWorker {
2101
2083
  timestamp: currentTimeUs,
2102
2084
  duration: frameDurationFromFps(compositionFps)
2103
2085
  });
2104
- const request = this.buildComposeRequest(this.instructions, videoFrame);
2105
- if (request) {
2106
- controller.enqueue(request);
2086
+ try {
2087
+ const request = this.buildComposeRequest(this.instructions, videoFrame);
2088
+ if (request) {
2089
+ controller.enqueue(request);
2090
+ } else {
2091
+ videoFrame.close();
2092
+ }
2093
+ currentTimeUs += frameDurationFromFps(compositionFps);
2094
+ } catch (error) {
2095
+ videoFrame.close();
2096
+ throw error;
2107
2097
  }
2108
- currentTimeUs += frameDurationFromFps(compositionFps);
2109
2098
  }
2110
2099
  });
2111
- this.channel.sendStream(cacheStream, {
2112
- streamType: "video",
2113
- sessionId: this.sessionId
2114
- });
2115
- readableStream.pipeTo(composeStream).catch((error) => {
2100
+ readableStream.pipeTo(composeStream.writable).catch((error) => {
2116
2101
  console.error("[VideoComposeWorker] image frame stream error", this.sessionId, error);
2117
2102
  });
2103
+ const meta = {
2104
+ streamType: "video",
2105
+ sessionId: this.sessionId
2106
+ };
2118
2107
  if (this.downstreamPort) {
2119
2108
  const encodeChannel = new WorkerChannel(this.downstreamPort, {
2120
2109
  name: "VideoCompose-Encode",
2121
2110
  timeout: 3e4
2122
2111
  });
2123
- encodeChannel.sendStream(encodeStream, {
2124
- streamType: "video",
2125
- sessionId: this.sessionId
2126
- });
2112
+ encodeChannel.sendStream(composeStream.readable, meta);
2127
2113
  } else {
2128
- encodeStream.cancel();
2114
+ this.channel.sendStream(composeStream.readable, meta);
2129
2115
  }
2130
2116
  }
2131
2117
  buildComposeRequest(instruction, frame) {
@@ -2212,4 +2198,4 @@ export {
2212
2198
  VideoComposeWorker,
2213
2199
  videoCompose_worker as default
2214
2200
  };
2215
- //# sourceMappingURL=video-compose.worker.js.map
2201
+ //# sourceMappingURL=video-compose.worker.M5uomNVr.js.map