@meframe/core 0.0.6 → 0.0.7

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 (41) hide show
  1. package/dist/config/defaults.d.ts.map +1 -1
  2. package/dist/config/defaults.js +1 -0
  3. package/dist/config/defaults.js.map +1 -1
  4. package/dist/config/types.d.ts +1 -0
  5. package/dist/config/types.d.ts.map +1 -1
  6. package/dist/orchestrator/Orchestrator.d.ts +0 -1
  7. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  8. package/dist/orchestrator/Orchestrator.js +24 -37
  9. package/dist/orchestrator/Orchestrator.js.map +1 -1
  10. package/dist/orchestrator/VideoClipSession.d.ts +1 -0
  11. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  12. package/dist/orchestrator/VideoClipSession.js +96 -82
  13. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  14. package/dist/stages/compose/GlobalAudioSession.d.ts.map +1 -1
  15. package/dist/stages/compose/GlobalAudioSession.js +11 -3
  16. package/dist/stages/compose/GlobalAudioSession.js.map +1 -1
  17. package/dist/stages/load/ResourceLoader.js +1 -1
  18. package/dist/stages/load/ResourceLoader.js.map +1 -1
  19. package/dist/worker/WorkerPool.d.ts.map +1 -1
  20. package/dist/worker/WorkerPool.js +6 -2
  21. package/dist/worker/WorkerPool.js.map +1 -1
  22. package/dist/worker/types.d.ts +1 -1
  23. package/dist/worker/types.d.ts.map +1 -1
  24. package/dist/worker/types.js.map +1 -1
  25. package/dist/worker/worker-event-whitelist.d.ts.map +1 -1
  26. package/dist/workers/BaseDecoder.js +130 -0
  27. package/dist/workers/BaseDecoder.js.map +1 -0
  28. package/dist/workers/WorkerChannel.js.map +1 -1
  29. package/dist/workers/stages/compose/video-compose.worker.js +13 -9
  30. package/dist/workers/stages/compose/video-compose.worker.js.map +1 -1
  31. package/dist/workers/stages/decode/audio-decode.worker.js +243 -0
  32. package/dist/workers/stages/decode/audio-decode.worker.js.map +1 -0
  33. package/dist/workers/stages/decode/video-decode.worker.js +346 -0
  34. package/dist/workers/stages/decode/video-decode.worker.js.map +1 -0
  35. package/dist/workers/stages/encode/video-encode.worker.js +340 -0
  36. package/dist/workers/stages/encode/video-encode.worker.js.map +1 -0
  37. package/package.json +1 -1
  38. package/dist/workers/stages/decode/decode.worker.js +0 -826
  39. package/dist/workers/stages/decode/decode.worker.js.map +0 -1
  40. package/dist/workers/stages/encode/encode.worker.js +0 -547
  41. package/dist/workers/stages/encode/encode.worker.js.map +0 -1
@@ -0,0 +1,340 @@
1
+ import { W as WorkerChannel, a as WorkerMessageType, b as WorkerState } from "../../WorkerChannel.js";
2
+ class BaseEncoder {
3
+ encoder;
4
+ config;
5
+ controller = null;
6
+ constructor(config) {
7
+ this.config = config;
8
+ }
9
+ getConfig() {
10
+ return { ...this.config };
11
+ }
12
+ get currentConfig() {
13
+ return this.config;
14
+ }
15
+ shouldReconfigure(partial) {
16
+ const next = { ...this.config, ...partial };
17
+ const keys = Object.keys(partial ?? {});
18
+ for (const key of keys) {
19
+ if (partial[key] !== void 0 && next[key] !== this.config[key]) {
20
+ return true;
21
+ }
22
+ }
23
+ return false;
24
+ }
25
+ hasConfigChanged(next) {
26
+ const currentEntries = Object.entries(this.config);
27
+ for (const [key, value] of currentEntries) {
28
+ if (next[key] !== value) {
29
+ return true;
30
+ }
31
+ }
32
+ for (const key of Object.keys(next)) {
33
+ if (this.config[key] !== next[key]) {
34
+ return true;
35
+ }
36
+ }
37
+ return false;
38
+ }
39
+ configsEqual(a, b) {
40
+ return JSON.stringify(a) === JSON.stringify(b);
41
+ }
42
+ async initialize() {
43
+ if (this.encoder?.state === "configured") {
44
+ return;
45
+ }
46
+ const isSupported = await this.isConfigSupported(this.config);
47
+ if (!isSupported.supported) {
48
+ throw new Error(`Codec not supported: ${JSON.stringify(this.config)}`);
49
+ }
50
+ this.encoder = this.createEncoder({
51
+ output: this.handleOutput.bind(this),
52
+ error: this.handleError.bind(this)
53
+ });
54
+ this.encoder.configure(this.config);
55
+ }
56
+ async reconfigure(config) {
57
+ if (!config || Object.keys(config).length === 0) {
58
+ return;
59
+ }
60
+ const nextConfig = { ...this.config, ...config };
61
+ if (this.configsEqual(this.config, nextConfig)) {
62
+ return;
63
+ }
64
+ if (!this.encoder) {
65
+ this.config = nextConfig;
66
+ await this.initialize();
67
+ return;
68
+ }
69
+ if (this.encoder.state === "configured") {
70
+ await this.encoder.flush();
71
+ }
72
+ const isSupported = await this.isConfigSupported(nextConfig);
73
+ if (!isSupported.supported) {
74
+ throw new Error(`New configuration not supported: ${nextConfig.codec}`);
75
+ }
76
+ this.config = nextConfig;
77
+ this.encoder.configure(this.config);
78
+ }
79
+ async flush() {
80
+ if (!this.encoder) {
81
+ return;
82
+ }
83
+ await this.encoder.flush();
84
+ }
85
+ async reset() {
86
+ if (!this.encoder) {
87
+ return;
88
+ }
89
+ this.encoder.reset();
90
+ this.onReset();
91
+ }
92
+ async close() {
93
+ if (!this.encoder) {
94
+ return;
95
+ }
96
+ if (this.encoder.state === "configured") {
97
+ await this.encoder.flush();
98
+ }
99
+ this.encoder.close();
100
+ this.encoder = void 0;
101
+ }
102
+ get isReady() {
103
+ return this.encoder?.state === "configured";
104
+ }
105
+ get queueSize() {
106
+ return this.encoder?.encodeQueueSize ?? 0;
107
+ }
108
+ handleOutput(chunk, metadata) {
109
+ if (this.controller) {
110
+ try {
111
+ this.controller.enqueue({ chunk, metadata });
112
+ } catch (error) {
113
+ if (!(error instanceof TypeError && error.message.includes("closed"))) {
114
+ throw error;
115
+ }
116
+ }
117
+ }
118
+ }
119
+ handleError(error) {
120
+ console.error(`${this.getEncoderType()} encoder error:`, error);
121
+ this.controller?.error(error);
122
+ }
123
+ // Hook for subclasses to handle reset
124
+ onReset() {
125
+ }
126
+ /**
127
+ * Create transform stream for encoding
128
+ * Implements common stream logic with backpressure handling
129
+ */
130
+ createStream() {
131
+ return new TransformStream(
132
+ {
133
+ start: async (controller) => {
134
+ this.controller = controller;
135
+ if (!this.isReady) {
136
+ await this.initialize();
137
+ }
138
+ },
139
+ transform: async (input) => {
140
+ if (!this.encoder || this.encoder.state !== "configured") {
141
+ throw new Error("Encoder not configured");
142
+ }
143
+ if (this.encoder.encodeQueueSize >= this.encodeQueueThreshold) {
144
+ await new Promise((resolve) => {
145
+ const check = () => {
146
+ if (!this.encoder || this.encoder.encodeQueueSize < this.encodeQueueThreshold - 1) {
147
+ resolve();
148
+ } else {
149
+ setTimeout(check, 10);
150
+ }
151
+ };
152
+ check();
153
+ });
154
+ }
155
+ const frame = input.frame || input;
156
+ this.encode(frame);
157
+ },
158
+ flush: async () => {
159
+ await this.flush();
160
+ }
161
+ },
162
+ // Queuing strategy with backpressure configuration
163
+ {
164
+ highWaterMark: this.highWaterMark,
165
+ size: () => 1
166
+ // Count-based
167
+ }
168
+ );
169
+ }
170
+ }
171
+ class VideoChunkEncoder extends BaseEncoder {
172
+ static DEFAULT_HIGH_WATER_MARK = 2;
173
+ static DEFAULT_ENCODE_QUEUE_THRESHOLD = 8;
174
+ highWaterMark;
175
+ encodeQueueThreshold;
176
+ frameCount = 0;
177
+ keyFrameInterval = 60;
178
+ // 2 seconds at 30fps
179
+ constructor(config) {
180
+ super(config);
181
+ this.highWaterMark = config.backpressure?.highWaterMark ?? VideoChunkEncoder.DEFAULT_HIGH_WATER_MARK;
182
+ this.encodeQueueThreshold = config.backpressure?.encodeQueueThreshold ?? VideoChunkEncoder.DEFAULT_ENCODE_QUEUE_THRESHOLD;
183
+ }
184
+ async isConfigSupported(config) {
185
+ const result = await VideoEncoder.isConfigSupported(config);
186
+ return { supported: result.supported ?? false };
187
+ }
188
+ createEncoder(init) {
189
+ return new VideoEncoder(init);
190
+ }
191
+ getEncoderType() {
192
+ return "Video";
193
+ }
194
+ onReset() {
195
+ this.frameCount = 0;
196
+ }
197
+ encode(frame) {
198
+ const keyFrame = this.shouldGenerateKeyFrame();
199
+ const encodeOptions = {
200
+ keyFrame
201
+ };
202
+ this.encoder.encode(frame, encodeOptions);
203
+ this.frameCount++;
204
+ frame.close();
205
+ }
206
+ setKeyFrameInterval(interval) {
207
+ this.keyFrameInterval = Math.max(1, interval);
208
+ }
209
+ shouldGenerateKeyFrame() {
210
+ if (this.frameCount === 0) {
211
+ return true;
212
+ }
213
+ return this.frameCount % this.keyFrameInterval === 0;
214
+ }
215
+ }
216
+ class VideoEncodeWorker {
217
+ channel;
218
+ encoder = null;
219
+ clipId = "";
220
+ defaultConfig = null;
221
+ upstreamPort = null;
222
+ constructor() {
223
+ this.channel = new WorkerChannel(self, {
224
+ name: "VideoEncodeWorker",
225
+ timeout: 3e4
226
+ });
227
+ this.setupHandlers();
228
+ }
229
+ setupHandlers() {
230
+ this.channel.registerHandler("configure", this.handleConfigure.bind(this));
231
+ this.channel.registerHandler("connect", this.handleConnect.bind(this));
232
+ this.channel.registerHandler("configure_video", this.handleConfigureVideo.bind(this));
233
+ this.channel.registerHandler("flush", this.handleFlush.bind(this));
234
+ this.channel.registerHandler("reset", this.handleReset.bind(this));
235
+ this.channel.registerHandler("get_stats", this.handleGetStats.bind(this));
236
+ this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));
237
+ }
238
+ async handleConnect(payload) {
239
+ const { port, streamType, sessionId, direction } = payload;
240
+ if (direction === "upstream" && streamType === "video") {
241
+ return this.handleConnectComposer({ port, sessionId });
242
+ }
243
+ return { success: true };
244
+ }
245
+ async handleConfigure(payload) {
246
+ const { config, initial = false } = payload;
247
+ if (initial) {
248
+ this.channel.state = WorkerState.Ready;
249
+ }
250
+ if (config.video) {
251
+ this.defaultConfig = config.video;
252
+ }
253
+ return { success: true };
254
+ }
255
+ async handleConnectComposer(payload) {
256
+ const { port, sessionId } = payload;
257
+ this.upstreamPort = port;
258
+ this.clipId = sessionId || "default";
259
+ const composeChannel = new WorkerChannel(port, {
260
+ name: "Encode-VideoCompose",
261
+ timeout: 3e4
262
+ });
263
+ composeChannel.receiveStream(async (stream, metadata) => {
264
+ const streamSessionId = metadata?.sessionId || sessionId || "unknown";
265
+ if (metadata?.streamType === "video" && this.defaultConfig) {
266
+ if (!this.encoder) {
267
+ this.encoder = new VideoChunkEncoder(this.defaultConfig);
268
+ await this.encoder.initialize();
269
+ }
270
+ const encodingTransform = this.encoder.createStream();
271
+ const encodedStream = stream.pipeThrough(
272
+ encodingTransform
273
+ );
274
+ this.channel.sendStream(encodedStream, {
275
+ streamType: "video",
276
+ track: "video",
277
+ sessionId: streamSessionId
278
+ });
279
+ }
280
+ });
281
+ return { success: true };
282
+ }
283
+ async handleConfigureVideo(config) {
284
+ this.defaultConfig = config;
285
+ this.channel.notify("video_configured", {
286
+ codec: config.codec,
287
+ width: config.width,
288
+ height: config.height,
289
+ bitrate: config.bitrate
290
+ });
291
+ return { success: true };
292
+ }
293
+ async handleFlush() {
294
+ if (this.encoder) {
295
+ await this.encoder.flush();
296
+ }
297
+ return { success: true };
298
+ }
299
+ async handleReset() {
300
+ if (this.encoder) {
301
+ await this.encoder.reset();
302
+ }
303
+ this.channel.notify("reset_complete", {
304
+ type: "video"
305
+ });
306
+ return { success: true };
307
+ }
308
+ async handleGetStats() {
309
+ if (!this.encoder) {
310
+ return {};
311
+ }
312
+ return {
313
+ video: {
314
+ clipId: this.clipId,
315
+ configured: true,
316
+ state: this.encoder.state
317
+ }
318
+ };
319
+ }
320
+ async handleDispose() {
321
+ if (this.encoder) {
322
+ await this.encoder.close();
323
+ this.encoder = null;
324
+ }
325
+ this.defaultConfig = null;
326
+ this.upstreamPort?.close();
327
+ this.upstreamPort = null;
328
+ this.channel.state = WorkerState.Disposed;
329
+ return { success: true };
330
+ }
331
+ }
332
+ const worker = new VideoEncodeWorker();
333
+ self.addEventListener("beforeunload", () => {
334
+ worker["handleDispose"]();
335
+ });
336
+ const videoEncode_worker = null;
337
+ export {
338
+ videoEncode_worker as default
339
+ };
340
+ //# sourceMappingURL=video-encode.worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-encode.worker.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 if (!isSupported.supported) {\n throw new Error(`Codec not supported: ${JSON.stringify(this.config)}`);\n }\n\n this.encoder = this.createEncoder({\n output: this.handleOutput.bind(this),\n error: this.handleError.bind(this),\n });\n\n (this.encoder as any).configure(this.config);\n }\n\n async reconfigure(config: Partial<TConfig>): Promise<void> {\n if (!config || Object.keys(config).length === 0) {\n return;\n }\n\n const nextConfig = { ...this.config, ...config } as TConfig;\n\n if (this.configsEqual(this.config, nextConfig)) {\n return;\n }\n\n if (!this.encoder) {\n this.config = nextConfig;\n await this.initialize();\n return;\n }\n\n if (this.encoder.state === 'configured') {\n await this.encoder.flush();\n }\n\n const isSupported = await this.isConfigSupported(nextConfig);\n if (!isSupported.supported) {\n throw new Error(`New configuration not supported: ${nextConfig.codec}`);\n }\n\n this.config = nextConfig;\n (this.encoder as any).configure(this.config);\n }\n\n async flush(): Promise<void> {\n if (!this.encoder) {\n return;\n }\n\n await this.encoder.flush();\n }\n\n async reset(): Promise<void> {\n if (!this.encoder) {\n return;\n }\n\n this.encoder.reset();\n this.onReset();\n }\n\n async close(): Promise<void> {\n if (!this.encoder) {\n return;\n }\n\n if (this.encoder.state === 'configured') {\n await this.encoder.flush();\n }\n\n this.encoder.close();\n this.encoder = undefined;\n }\n\n get isReady(): boolean {\n return this.encoder?.state === 'configured';\n }\n\n get queueSize(): number {\n return this.encoder?.encodeQueueSize ?? 0;\n }\n\n protected handleOutput(chunk: TChunk, metadata: TMetadata): void {\n // Only enqueue if controller exists and stream is not closed\n if (this.controller) {\n try {\n this.controller.enqueue({ chunk, metadata });\n } catch (error) {\n // Stream may be closed during flush, ignore enqueue errors\n if (!(error instanceof TypeError && error.message.includes('closed'))) {\n throw error;\n }\n }\n }\n }\n\n protected handleError(error: DOMException): void {\n console.error(`${this.getEncoderType()} encoder error:`, error);\n this.controller?.error(error);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract isConfigSupported(config: TConfig): Promise<{ supported: boolean }>;\n protected abstract createEncoder(init: EncoderInit): TEncoder;\n protected abstract getEncoderType(): string;\n\n // Hook for subclasses to handle reset\n protected onReset(): void {\n // Override in subclasses if needed\n }\n\n // Abstract properties for backpressure configuration\n protected abstract readonly highWaterMark: number;\n protected abstract readonly encodeQueueThreshold: number;\n\n /**\n * Create transform stream for encoding\n * Implements common stream logic with backpressure handling\n */\n createStream(): TransformStream<TInput, EncoderChunk> {\n return new TransformStream<TInput, EncoderChunk>(\n {\n start: async (controller) => {\n this.controller = controller;\n\n // Initialize encoder if not already initialized\n if (!this.isReady) {\n await this.initialize();\n }\n },\n\n transform: async (input) => {\n if (!this.encoder || this.encoder.state !== 'configured') {\n throw new Error('Encoder not configured');\n }\n\n // Check encoder queue pressure\n if (this.encoder.encodeQueueSize >= this.encodeQueueThreshold) {\n // Wait for queue to drain\n await new Promise<void>((resolve) => {\n const check = () => {\n if (!this.encoder || this.encoder.encodeQueueSize < this.encodeQueueThreshold - 1) {\n resolve();\n } else {\n setTimeout(check, 10);\n }\n };\n check();\n });\n }\n\n // Encode the input\n const frame = (input as any).frame || input;\n this.encode(frame);\n },\n\n flush: async () => {\n await this.flush();\n },\n },\n // Queuing strategy with backpressure configuration\n {\n highWaterMark: this.highWaterMark,\n size: () => 1, // Count-based\n }\n );\n }\n\n // Abstract method for encoding\n abstract encode(input: TInput): void;\n}\n\ninterface EncoderInit {\n output: (chunk: any, metadata: any) => void;\n error: (error: DOMException) => void;\n}\n","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 private keyFrameInterval = 60; // 2 seconds at 30fps\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\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 defaultConfig: VideoEncoderConfig | null = null;\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('configure_video', this.handleConfigureVideo.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: {\n video?: Partial<VideoEncoderConfig>;\n };\n initial?: boolean;\n }): Promise<{ success: boolean }> {\n const { config, initial = false } = payload;\n\n if (initial) {\n this.channel.state = WorkerState.Ready;\n }\n\n if (config.video) {\n this.defaultConfig = config.video as VideoEncoderConfig;\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' && this.defaultConfig) {\n if (!this.encoder) {\n this.encoder = new VideoChunkEncoder(this.defaultConfig);\n await this.encoder.initialize();\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 handleConfigureVideo(config: VideoEncoderConfig): Promise<{ success: boolean }> {\n this.defaultConfig = config;\n\n this.channel.notify('video_configured', {\n codec: config.codec,\n width: config.width,\n height: config.height,\n bitrate: config.bitrate,\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 state: this.encoder.state,\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.defaultConfig = null;\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meframe/core",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Next generation media processing framework based on WebCodecs",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",