@meframe/core 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Meframe.d.ts +16 -7
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +70 -86
- package/dist/Meframe.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +27 -10
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +92 -30
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/L2Cache.d.ts +31 -2
- package/dist/cache/L2Cache.d.ts.map +1 -1
- package/dist/cache/L2Cache.js +245 -44
- package/dist/cache/L2Cache.js.map +1 -1
- package/dist/cache/l1/VideoL1Cache.d.ts +1 -1
- package/dist/cache/l1/VideoL1Cache.js.map +1 -1
- package/dist/controllers/PreRenderService.d.ts +21 -4
- package/dist/controllers/PreRenderService.d.ts.map +1 -1
- package/dist/controllers/PreRenderService.js +67 -5
- package/dist/controllers/PreRenderService.js.map +1 -1
- package/dist/model/CompositionModel.d.ts +1 -1
- package/dist/model/CompositionModel.js +1 -1
- package/dist/model/CompositionModel.js.map +1 -1
- package/dist/model/patch.d.ts +1 -1
- package/dist/model/patch.js.map +1 -1
- package/dist/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js +1858 -0
- package/dist/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +1 -0
- package/dist/orchestrator/ClipSessionManager.d.ts +1 -2
- package/dist/orchestrator/ClipSessionManager.d.ts.map +1 -1
- package/dist/orchestrator/ClipSessionManager.js +1 -0
- package/dist/orchestrator/ClipSessionManager.js.map +1 -1
- package/dist/orchestrator/CompositionPlanner.d.ts +1 -1
- package/dist/orchestrator/CompositionPlanner.js +1 -1
- package/dist/orchestrator/CompositionPlanner.js.map +1 -1
- package/dist/orchestrator/Orchestrator.d.ts +9 -1
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +65 -35
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/VideoClipSession.d.ts +11 -4
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
- package/dist/orchestrator/VideoClipSession.js +54 -28
- package/dist/orchestrator/VideoClipSession.js.map +1 -1
- package/dist/stages/compose/GlobalAudioSession.d.ts +28 -1
- package/dist/stages/compose/GlobalAudioSession.d.ts.map +1 -1
- package/dist/stages/compose/GlobalAudioSession.js +133 -5
- package/dist/stages/compose/GlobalAudioSession.js.map +1 -1
- package/dist/stages/compose/VideoComposer.d.ts +1 -0
- package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
- package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
- package/dist/stages/encode/AudioChunkEncoder.d.ts +2 -1
- package/dist/stages/encode/AudioChunkEncoder.d.ts.map +1 -1
- package/dist/stages/encode/AudioChunkEncoder.js +41 -0
- package/dist/stages/encode/AudioChunkEncoder.js.map +1 -0
- package/dist/stages/encode/BaseEncoder.d.ts +7 -3
- package/dist/stages/encode/BaseEncoder.d.ts.map +1 -1
- package/dist/stages/encode/BaseEncoder.js +173 -0
- package/dist/stages/encode/BaseEncoder.js.map +1 -0
- package/dist/stages/encode/ClipEncoderManager.d.ts +64 -0
- package/dist/stages/encode/ClipEncoderManager.d.ts.map +1 -0
- package/dist/stages/encode/index.d.ts +1 -1
- package/dist/stages/encode/index.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +17 -12
- package/dist/stages/load/ResourceLoader.js.map +1 -1
- package/dist/stages/load/TaskManager.d.ts +1 -1
- package/dist/stages/load/TaskManager.d.ts.map +1 -1
- package/dist/stages/load/TaskManager.js +2 -2
- package/dist/stages/load/TaskManager.js.map +1 -1
- package/dist/stages/load/types.d.ts +2 -2
- package/dist/stages/load/types.d.ts.map +1 -1
- package/dist/stages/mux/MP4Muxer.d.ts +19 -38
- package/dist/stages/mux/MP4Muxer.d.ts.map +1 -1
- package/dist/stages/mux/MP4Muxer.js +60 -0
- package/dist/stages/mux/MP4Muxer.js.map +1 -0
- package/dist/stages/mux/MuxManager.d.ts +27 -0
- package/dist/stages/mux/MuxManager.d.ts.map +1 -0
- package/dist/stages/mux/MuxManager.js +148 -0
- package/dist/stages/mux/MuxManager.js.map +1 -0
- package/dist/stages/mux/index.d.ts +1 -0
- package/dist/stages/mux/index.d.ts.map +1 -1
- package/dist/stages/mux/types.d.ts +1 -0
- package/dist/stages/mux/types.d.ts.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/worker/WorkerPool.d.ts.map +1 -1
- package/dist/worker/WorkerPool.js +2 -4
- package/dist/worker/WorkerPool.js.map +1 -1
- package/dist/worker/types.d.ts +1 -4
- package/dist/worker/types.d.ts.map +1 -1
- package/dist/worker/types.js +0 -3
- package/dist/worker/types.js.map +1 -1
- package/dist/worker/worker-event-whitelist.d.ts.map +1 -1
- package/dist/workers/MP4Demuxer.js +7049 -6
- package/dist/workers/MP4Demuxer.js.map +1 -1
- package/dist/workers/WorkerChannel.js +0 -3
- package/dist/workers/WorkerChannel.js.map +1 -1
- package/dist/workers/stages/compose/video-compose.worker.js +25 -14
- package/dist/workers/stages/compose/video-compose.worker.js.map +1 -1
- package/dist/workers/stages/decode/decode.worker.js +25 -16
- package/dist/workers/stages/decode/decode.worker.js.map +1 -1
- package/dist/workers/stages/demux/audio-demux.worker.js +4 -4
- package/dist/workers/stages/demux/audio-demux.worker.js.map +1 -1
- package/dist/workers/stages/demux/video-demux.worker.js +9 -7
- package/dist/workers/stages/demux/video-demux.worker.js.map +1 -1
- package/dist/workers/stages/encode/encode.worker.js +191 -195
- package/dist/workers/stages/encode/encode.worker.js.map +1 -1
- package/package.json +2 -1
- package/dist/stages/encode/EncoderPool.d.ts +0 -28
- package/dist/stages/encode/EncoderPool.d.ts.map +0 -1
- package/dist/workers/mp4box.all.js +0 -7049
- package/dist/workers/mp4box.all.js.map +0 -1
- package/dist/workers/stages/mux/mux.worker.js +0 -501
- package/dist/workers/stages/mux/mux.worker.js.map +0 -1
|
@@ -9,6 +9,7 @@ import { CompositionPlanner } from "./CompositionPlanner.js";
|
|
|
9
9
|
import { VideoClipSession } from "./VideoClipSession.js";
|
|
10
10
|
import { ClipSessionManager } from "./ClipSessionManager.js";
|
|
11
11
|
import { GlobalAudioSession } from "../stages/compose/GlobalAudioSession.js";
|
|
12
|
+
import { MuxManager } from "../stages/mux/MuxManager.js";
|
|
12
13
|
class Orchestrator {
|
|
13
14
|
workers;
|
|
14
15
|
eventBus;
|
|
@@ -17,6 +18,7 @@ class Orchestrator {
|
|
|
17
18
|
cacheManager;
|
|
18
19
|
planner;
|
|
19
20
|
audioSession;
|
|
21
|
+
muxManager;
|
|
20
22
|
activeClips = /* @__PURE__ */ new Set();
|
|
21
23
|
isInitialized = false;
|
|
22
24
|
config = ConfigLoader.getInstance().getConfig();
|
|
@@ -73,6 +75,11 @@ class Orchestrator {
|
|
|
73
75
|
getModel: () => this.compositionModel,
|
|
74
76
|
buildWorkerConfigs: () => this.buildWorkerConfigs()
|
|
75
77
|
});
|
|
78
|
+
this.muxManager = new MuxManager(
|
|
79
|
+
this.cacheManager,
|
|
80
|
+
this.audioSession,
|
|
81
|
+
this.config.encode.audio
|
|
82
|
+
);
|
|
76
83
|
}
|
|
77
84
|
get workerStatus() {
|
|
78
85
|
const status = this.workers.status;
|
|
@@ -83,8 +90,7 @@ class Orchestrator {
|
|
|
83
90
|
"decode",
|
|
84
91
|
"videoCompose",
|
|
85
92
|
"audioCompose",
|
|
86
|
-
"encode"
|
|
87
|
-
"mux"
|
|
93
|
+
"encode"
|
|
88
94
|
];
|
|
89
95
|
for (const type of workerTypes) {
|
|
90
96
|
result[type] = status[type] || {
|
|
@@ -113,7 +119,6 @@ class Orchestrator {
|
|
|
113
119
|
async setCompositionModel(model) {
|
|
114
120
|
this.compositionModel = model;
|
|
115
121
|
this.planner.setModel(model);
|
|
116
|
-
this.cacheManager.configure(model);
|
|
117
122
|
this.currentClipId = null;
|
|
118
123
|
this.eventBus.emit(MeframeEvent.ModelSet, model);
|
|
119
124
|
this.eventBus.emit(MeframeEvent.CompositionUpdated, {
|
|
@@ -201,7 +206,7 @@ class Orchestrator {
|
|
|
201
206
|
if (session) {
|
|
202
207
|
await session.activate();
|
|
203
208
|
}
|
|
204
|
-
} else if (type === "decode" || type === "audioCompose" || type === "encode"
|
|
209
|
+
} else if (type === "decode" || type === "audioCompose" || type === "encode") {
|
|
205
210
|
await this.setupSharedConnections();
|
|
206
211
|
}
|
|
207
212
|
}
|
|
@@ -239,7 +244,7 @@ class Orchestrator {
|
|
|
239
244
|
return null;
|
|
240
245
|
}
|
|
241
246
|
/**
|
|
242
|
-
* Ensure clips are cached using
|
|
247
|
+
* Ensure clips are cached using 2-Clip strategy
|
|
243
248
|
* Called by PlaybackController
|
|
244
249
|
*/
|
|
245
250
|
async ensureClipCache(timeUs) {
|
|
@@ -269,44 +274,82 @@ class Orchestrator {
|
|
|
269
274
|
timeoutMs: options?.timeoutMs ?? 5e3
|
|
270
275
|
});
|
|
271
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Render a clip completely for L2 cache (bypass ClipSessionManager)
|
|
279
|
+
*/
|
|
280
|
+
async renderClipForL2(clipId) {
|
|
281
|
+
const sessionId = `${clipId}#l2`;
|
|
282
|
+
console.log("[Orchestrator] rendering clip for L2", clipId, sessionId);
|
|
283
|
+
const session = await this.createSession(sessionId, {
|
|
284
|
+
forL2Only: true,
|
|
285
|
+
clipId
|
|
286
|
+
});
|
|
287
|
+
await session.activate();
|
|
288
|
+
return session;
|
|
289
|
+
}
|
|
272
290
|
/**
|
|
273
291
|
* Create a new session for a clip
|
|
274
292
|
*/
|
|
275
|
-
async createSession(
|
|
293
|
+
async createSession(sessionId, options) {
|
|
294
|
+
const clipId = options?.clipId ?? sessionId;
|
|
276
295
|
const clip = this.compositionModel?.findClip(clipId);
|
|
277
296
|
if (!clip) {
|
|
278
297
|
throw new Error(`Clip ${clipId} not found`);
|
|
279
298
|
}
|
|
280
299
|
const session = await VideoClipSession.create({
|
|
281
300
|
clipId,
|
|
301
|
+
sessionId,
|
|
282
302
|
planner: this.planner,
|
|
283
303
|
workerPool: this.workers,
|
|
284
304
|
cacheManager: this.cacheManager,
|
|
285
305
|
compositionModel: this.compositionModel,
|
|
286
306
|
workerConfigs: this.buildWorkerConfigs(),
|
|
287
307
|
callbacks: {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
308
|
+
onComposedStreamReady: (stream, fps) => {
|
|
309
|
+
if (options?.forL2Only) {
|
|
310
|
+
stream.cancel();
|
|
311
|
+
} else {
|
|
312
|
+
this.cacheManager.receiveComposedFrames(stream, {
|
|
313
|
+
clipId,
|
|
314
|
+
trackId: this.compositionModel.mainTrackId,
|
|
315
|
+
fps,
|
|
316
|
+
onFrame: () => {
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
}
|
|
296
320
|
},
|
|
297
|
-
|
|
298
|
-
|
|
321
|
+
onEncodedStreamReady: (stream, track) => {
|
|
322
|
+
if (options?.forL2Only) {
|
|
323
|
+
console.log("[Orchestrator] receiving encoded stream for L2", clipId, track);
|
|
324
|
+
this.cacheManager.receiveEncodedChunks(stream, clipId, track, {
|
|
325
|
+
onComplete: () => {
|
|
326
|
+
session.dispose();
|
|
327
|
+
},
|
|
328
|
+
onError: (error) => {
|
|
329
|
+
console.error(
|
|
330
|
+
`[Orchestrator] L2 encode stream error for ${clipId} ${track}:`,
|
|
331
|
+
error
|
|
332
|
+
);
|
|
333
|
+
session.dispose();
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
} else {
|
|
337
|
+
stream.cancel();
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
onPipelineReady: async () => {
|
|
341
|
+
const clip2 = this.compositionModel?.findClip(clipId);
|
|
299
342
|
if (clip2?.resourceId) {
|
|
300
343
|
await this.resourceLoader.fetch(clip2.resourceId, {
|
|
301
|
-
priority: "high",
|
|
302
|
-
|
|
344
|
+
priority: options?.forL2Only ? "low" : "high",
|
|
345
|
+
sessionId,
|
|
303
346
|
trackId: clip2.trackId
|
|
304
347
|
});
|
|
305
348
|
}
|
|
306
349
|
}
|
|
307
350
|
}
|
|
308
351
|
});
|
|
309
|
-
this.activeClips.add(
|
|
352
|
+
this.activeClips.add(sessionId);
|
|
310
353
|
return session;
|
|
311
354
|
}
|
|
312
355
|
async dispose() {
|
|
@@ -366,9 +409,6 @@ class Orchestrator {
|
|
|
366
409
|
bitrate: config.encode?.audio?.bitrateKbps ? config.encode.audio.bitrateKbps * 1e3 : 128e3,
|
|
367
410
|
...config.encode?.audio
|
|
368
411
|
}
|
|
369
|
-
},
|
|
370
|
-
mux: {
|
|
371
|
-
container: config.export?.container || "mp4"
|
|
372
412
|
}
|
|
373
413
|
};
|
|
374
414
|
}
|
|
@@ -379,19 +419,9 @@ class Orchestrator {
|
|
|
379
419
|
this.audioSession.handleAudioStream(stream, metadata);
|
|
380
420
|
}
|
|
381
421
|
});
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
await encodeWorker.send(
|
|
386
|
-
"connect",
|
|
387
|
-
{ direction: "downstream", port: videoChannel.port1, streamType: "video" },
|
|
388
|
-
{ transfer: [videoChannel.port1] }
|
|
389
|
-
);
|
|
390
|
-
await muxWorker.send(
|
|
391
|
-
"connect",
|
|
392
|
-
{ direction: "upstream", port: videoChannel.port2, streamType: "video" },
|
|
393
|
-
{ transfer: [videoChannel.port2] }
|
|
394
|
-
);
|
|
422
|
+
}
|
|
423
|
+
async export(model, options) {
|
|
424
|
+
return this.muxManager.export(model, options);
|
|
395
425
|
}
|
|
396
426
|
}
|
|
397
427
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Orchestrator.js","sources":["../../src/orchestrator/Orchestrator.ts"],"sourcesContent":["import { EventBus } from '../event/EventBus';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { applyPatch as applyModelPatch } from '../model/patch';\nimport { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, RenderFrameOptions } from './types';\nimport { WorkerStatus, WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame } from '../model';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { VideoClipSession } from './VideoClipSession';\nimport { ClipSessionManager } from './ClipSessionManager';\nimport { GlobalAudioSession } from '../stages/compose/GlobalAudioSession';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n\n private activeClips = new Set<string>();\n private isInitialized = false;\n private config = ConfigLoader.getInstance().getConfig();\n private clipSessionManager: ClipSessionManager;\n private currentClipId: string | null = null;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n // Initialize WorkerPool with worker path from config\n this.workers = new WorkerPool({\n eventBus: this.eventBus,\n workerConfigs,\n workerPath: config.workerPath,\n workerExtension: config.workerExtension,\n });\n\n this.resourceLoader = new ResourceLoader({\n orchestrator: this as any,\n eventBus: this.eventBus,\n config: {\n maxConcurrent: config.maxWorkers || (this.config.load as any)?.retry?.maxAttempts || 4,\n },\n onStateChange: (resourceId, state) => this.handleResourceStateChange(resourceId, state),\n });\n\n this.planner = new CompositionPlanner();\n\n const cacheConfig = config.cacheConfig || this.config.cache;\n this.cacheManager = new CacheManager(\n {\n l1: {\n maxMemoryMB:\n (cacheConfig as any)?.l1Size || (cacheConfig as any)?.l1?.maxMemoryMB || 1024,\n maxGOPs: (this.config.decode as any)?.video?.maxGOPs || 4,\n },\n l2: {\n maxSizeMB: (cacheConfig as any)?.l2Size || (cacheConfig as any)?.l2?.maxSizeMB || 2048,\n projectId: 'default',\n },\n },\n this.eventBus\n );\n\n this.clipSessionManager = new ClipSessionManager({\n maxConcurrent: 2,\n factory: {\n createSession: (clipId) => this.createSession(clipId),\n },\n model: () => this.compositionModel,\n cacheManager: this.cacheManager,\n });\n\n this.audioSession = new GlobalAudioSession({\n cacheManager: this.cacheManager,\n workers: this.workers,\n resourceLoader: this.resourceLoader,\n eventBus: this.eventBus,\n getModel: () => this.compositionModel,\n buildWorkerConfigs: () => this.buildWorkerConfigs(),\n });\n }\n\n get workerStatus(): WorkerStatus {\n const status = this.workers.status;\n const result: WorkerStatus = {} as WorkerStatus;\n\n const workerTypes: WorkerType[] = [\n 'videoDemux',\n 'audioDemux',\n 'decode',\n 'videoCompose',\n 'audioCompose',\n 'encode',\n 'mux',\n ];\n\n for (const type of workerTypes) {\n result[type] = status[type] || {\n state: 'idle',\n taskCount: 0,\n };\n }\n\n return result;\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n await this.cacheManager.init();\n // Use unified stream pipeline wiring\n await this.setupSharedConnections();\n\n this.isInitialized = true;\n }\n\n // Event methods - forward to eventBus\n on<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.on(event, handler);\n }\n\n off<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.off(event, handler);\n }\n\n once<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.once(event, handler);\n }\n\n async setCompositionModel(model: CompositionModel): Promise<void> {\n this.compositionModel = model;\n this.planner.setModel(model);\n this.cacheManager.configure(model);\n this.currentClipId = null;\n\n this.eventBus.emit(MeframeEvent.ModelSet, model);\n\n this.eventBus.emit(MeframeEvent.CompositionUpdated, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce((acc: number, track: any) => acc + track.clips.length, 0),\n durationUs: model.durationUs,\n });\n\n await this.audioSession.activateAllAudioClips();\n\n this.ensureClipCache(0);\n }\n\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n // Apply patch and get affected clip IDs (simplified for 3-Clip strategy)\n const affectedClipIds = applyModelPatch(this.compositionModel, patch);\n const clipUpdates = this.planner.applyPatch(patch, affectedClipIds);\n\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n affectedClips: Array.from(affectedClipIds),\n });\n\n // Get currently active clips in the 3-Clip window\n const activeClipIds = new Set(\n Array.from(this.clipSessionManager['entries'].keys()).filter((clipId) =>\n this.clipSessionManager.isClipActive(clipId)\n )\n );\n\n // Process clip updates\n for (const update of clipUpdates) {\n if (update.type === 'remove') {\n this.activeClips.delete(update.clipId);\n this.cacheManager.evictClip(update.clipId);\n }\n\n // Notify ClipSessionManager to handle the update\n // This will install new instructions or restart pipeline as needed\n await this.clipSessionManager.handlePlannerUpdate(update.clipId, update);\n\n // Only evict cache for active clips (in current 3-Clip window)\n // Non-active clips will be regenerated when they enter the window\n if (activeClipIds.has(update.clipId) && update.type !== 'remove') {\n this.cacheManager.evictClip(update.clipId);\n }\n }\n }\n\n private handleResourceStateChange(resourceId: string, state: Resource['state']): void {\n if (!this.compositionModel) {\n return;\n }\n\n this.compositionModel.updateResourceState(resourceId, state ?? 'pending');\n\n if (state !== 'ready') {\n return;\n }\n\n const resource = this.compositionModel.getResource(resourceId);\n if (!resource) {\n return;\n }\n\n // Main video/audio resources: data will flow naturally into pipeline\n if (resource.type === 'video' || resource.type === 'audio') {\n return;\n }\n\n // Attachment resources (fonts, images): update instructions for active clips\n const clipIds = this.compositionModel.getClipIdsByResourceId(resourceId);\n for (const clipId of clipIds) {\n // Only update active clips (in 3-Clip window)\n if (!this.clipSessionManager.isClipActive(clipId)) {\n continue;\n }\n\n const clip = this.compositionModel.findClip(clipId);\n if (!clip) {\n continue;\n }\n\n // Rebuild instructions with updated resource status\n const instructions = this.planner.getInstructions(clipId);\n if (!instructions) {\n continue;\n }\n\n // Send updated instructions to worker (no pipeline restart needed)\n const session = this.clipSessionManager.getSession(clipId);\n const visualWorker = session?.visualWorkerHandle;\n if (visualWorker) {\n visualWorker.send('install_instructions', instructions);\n }\n }\n }\n\n async restartWorker(type: WorkerType, clipId?: string): Promise<void> {\n if ((type === 'videoDemux' || type === 'audioDemux' || type === 'videoCompose') && !clipId) {\n throw new Error(`clipId required for restarting ${type} worker`);\n }\n\n this.workers.terminate(type, clipId);\n const worker = await this.workers.get(type, clipId);\n\n this.eventBus.emit(MeframeEvent.WorkerRestarted, {\n type,\n workerId: worker.getWorkerId(),\n reason: 'Manual restart',\n });\n\n if (clipId) {\n const session = this.clipSessionManager.getSession(clipId);\n if (session) {\n await session.activate();\n }\n } else if (\n type === 'decode' ||\n type === 'audioCompose' ||\n type === 'encode' ||\n type === 'mux'\n ) {\n await this.setupSharedConnections();\n }\n }\n\n async renderFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n // Detect clip change and proactively ensure cache for prev/current/next\n if (this.currentClipId !== clip.id) {\n this.currentClipId = clip.id;\n void this.ensureClipCache(timeUs);\n }\n\n const cachedFrame = await this.cacheManager.getFrame(timeUs, clip.id);\n // console.log('>>>>>>>>>>>> renderFrame', timeUs, clip.id, cachedFrame);\n if (cachedFrame) {\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${timeUs}`,\n });\n return cachedFrame;\n }\n // Cache miss - also ensure cache (defensive, already called on clip change)\n void this.ensureClipCache(timeUs);\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${timeUs}`,\n });\n\n if (signal?.aborted) {\n throw new DOMException('Render aborted', 'AbortError');\n }\n\n // Return null immediately instead of waiting\n // This allows PlaybackController to detect miss and trigger buffering\n return null;\n }\n\n /**\n * Ensure clips are cached using 3-Clip strategy\n * Called by PlaybackController\n */\n async ensureClipCache(timeUs: TimeUs): Promise<void> {\n if (!this.compositionModel) {\n return;\n }\n\n await this.clipSessionManager.ensureClips(timeUs);\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId);\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 30,\n timeoutMs: options?.timeoutMs ?? 5_000,\n });\n }\n\n /**\n * Create a new session for a clip\n */\n private async createSession(clipId: string): Promise<VideoClipSession> {\n const clip = this.compositionModel?.findClip(clipId);\n if (!clip) {\n throw new Error(`Clip ${clipId} not found`);\n }\n\n const session = await VideoClipSession.create({\n clipId,\n planner: this.planner,\n workerPool: this.workers,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n workerConfigs: this.buildWorkerConfigs(),\n callbacks: {\n onStreamReady: (id, stream, fps) => {\n this.cacheManager.receiveComposedFrames(stream, {\n clipId: id,\n trackId: this.compositionModel!.mainTrackId,\n fps,\n onFrame: () => {\n // Frame arrived\n },\n });\n },\n onPipelineReady: async (clipId) => {\n // Pipeline is connected, now trigger resource loading\n const clip = this.compositionModel?.findClip(clipId);\n if (clip?.resourceId) {\n await this.resourceLoader.fetch(clip.resourceId, {\n priority: 'high',\n clipId,\n trackId: clip.trackId,\n });\n }\n },\n },\n });\n\n this.activeClips.add(clipId);\n return session;\n }\n\n async dispose(): Promise<void> {\n this.resourceLoader.dispose();\n await this.clipSessionManager.dispose();\n await this.cacheManager.clear();\n\n this.currentClipId = null;\n this.activeClips.clear();\n\n this.workers.terminateAll();\n this.compositionModel = null;\n this.eventBus.dispose();\n }\n\n private buildWorkerConfigs(): Record<WorkerType, any> {\n const config = this.config as any;\n const defaultCanvasWidth = config.global?.defaultCanvasWidth ?? 1280;\n const defaultCanvasHeight = config.global?.defaultCanvasHeight ?? 720;\n const defaultFps = config.global?.defaultFps ?? 30;\n return {\n videoDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n audioDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n decode: {\n video: config.decode?.video,\n audio: config.decode?.audio,\n },\n videoCompose: {\n width: config.compose?.canvas?.width ?? defaultCanvasWidth,\n height: config.compose?.canvas?.height ?? defaultCanvasHeight,\n fps: config.global?.defaultFps ?? defaultFps,\n backgroundColor: config.compose?.canvas?.backgroundColor ?? '#000000',\n enableSmoothing: config.compose?.visual?.enableSmoothing ?? true,\n enableHardwareAcceleration: config.compose?.visual?.enableHardwareAcceleration ?? true,\n },\n audioCompose: {\n ducking: config.compose?.audio?.ducking,\n mixing: config.compose?.audio?.mixing,\n },\n encode: {\n video: {\n codec: 'avc1.42002A',\n width: config.compose?.canvas?.width || defaultCanvasWidth,\n height: config.compose?.canvas?.height || defaultCanvasHeight,\n bitrate: config.encode?.video?.bitrateKbps\n ? config.encode.video.bitrateKbps * 1000\n : 12_000_000,\n framerate: config.encode?.video?.framerate || defaultFps,\n latencyMode: 'quality',\n bitrateMode: 'variable',\n hardwareAcceleration: 'prefer-hardware',\n ...(config.encode?.video as any),\n },\n audio: {\n codec: 'mp4a.40.2',\n sampleRate: 48000,\n numberOfChannels: 2,\n bitrate: config.encode?.audio?.bitrateKbps\n ? config.encode.audio.bitrateKbps * 1000\n : 128000,\n ...(config.encode?.audio as any),\n },\n },\n mux: {\n container: config.export?.container || 'mp4',\n },\n };\n }\n\n private async setupSharedConnections(): Promise<void> {\n const decodeWorker = await this.workers.get('decode');\n\n decodeWorker.receiveStream((stream, metadata) => {\n if (metadata?.streamType === 'audio') {\n this.audioSession.handleAudioStream(stream as ReadableStream<AudioData>, metadata);\n }\n });\n\n const encodeWorker = await this.workers.get('encode');\n const muxWorker = await this.workers.get('mux');\n const videoChannel = new MessageChannel();\n await encodeWorker.send(\n 'connect',\n { direction: 'downstream', port: videoChannel.port1, streamType: 'video' },\n { transfer: [videoChannel.port1] }\n );\n await muxWorker.send(\n 'connect',\n { direction: 'upstream', port: videoChannel.port2, streamType: 'video' },\n { transfer: [videoChannel.port2] }\n );\n }\n}\n"],"names":["applyModelPatch","clipId","clip"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Orchestrator.js","sources":["../../src/orchestrator/Orchestrator.ts"],"sourcesContent":["import { EventBus } from '../event/EventBus';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { applyPatch as applyModelPatch } from '../model/patch';\nimport { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, RenderFrameOptions } from './types';\nimport { WorkerStatus, WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame } from '../model';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { VideoClipSession } from './VideoClipSession';\nimport { ClipSessionManager } from './ClipSessionManager';\nimport { GlobalAudioSession } from '../stages/compose/GlobalAudioSession';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { ExportOptions } from '@/types';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n muxManager: MuxManager;\n\n private activeClips = new Set<string>();\n private isInitialized = false;\n private config = ConfigLoader.getInstance().getConfig();\n private clipSessionManager: ClipSessionManager;\n private currentClipId: string | null = null;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n // Initialize WorkerPool with worker path from config\n this.workers = new WorkerPool({\n eventBus: this.eventBus,\n workerConfigs,\n workerPath: config.workerPath,\n workerExtension: config.workerExtension,\n });\n\n this.resourceLoader = new ResourceLoader({\n orchestrator: this as any,\n eventBus: this.eventBus,\n config: {\n maxConcurrent: config.maxWorkers || (this.config.load as any)?.retry?.maxAttempts || 4,\n },\n onStateChange: (resourceId, state) => this.handleResourceStateChange(resourceId, state),\n });\n\n this.planner = new CompositionPlanner();\n\n const cacheConfig = config.cacheConfig || this.config.cache;\n this.cacheManager = new CacheManager(\n {\n l1: {\n maxMemoryMB:\n (cacheConfig as any)?.l1Size || (cacheConfig as any)?.l1?.maxMemoryMB || 1024,\n maxGOPs: (this.config.decode as any)?.video?.maxGOPs || 4,\n },\n l2: {\n maxSizeMB: (cacheConfig as any)?.l2Size || (cacheConfig as any)?.l2?.maxSizeMB || 2048,\n projectId: 'default',\n },\n },\n this.eventBus\n );\n\n this.clipSessionManager = new ClipSessionManager({\n maxConcurrent: 2,\n factory: {\n createSession: (clipId) => this.createSession(clipId),\n },\n model: () => this.compositionModel,\n cacheManager: this.cacheManager,\n });\n\n this.audioSession = new GlobalAudioSession({\n cacheManager: this.cacheManager,\n workers: this.workers,\n resourceLoader: this.resourceLoader,\n eventBus: this.eventBus,\n getModel: () => this.compositionModel,\n buildWorkerConfigs: () => this.buildWorkerConfigs(),\n });\n\n this.muxManager = new MuxManager(\n this.cacheManager,\n this.audioSession,\n this.config.encode.audio as AudioEncoderConfig\n );\n }\n\n get workerStatus(): WorkerStatus {\n const status = this.workers.status;\n const result: WorkerStatus = {} as WorkerStatus;\n\n const workerTypes: WorkerType[] = [\n 'videoDemux',\n 'audioDemux',\n 'decode',\n 'videoCompose',\n 'audioCompose',\n 'encode',\n ];\n\n for (const type of workerTypes) {\n result[type] = status[type] || {\n state: 'idle',\n taskCount: 0,\n };\n }\n\n return result;\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n await this.cacheManager.init();\n // Use unified stream pipeline wiring\n await this.setupSharedConnections();\n\n this.isInitialized = true;\n }\n\n // Event methods - forward to eventBus\n on<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.on(event, handler);\n }\n\n off<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.off(event, handler);\n }\n\n once<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.once(event, handler);\n }\n\n async setCompositionModel(model: CompositionModel): Promise<void> {\n this.compositionModel = model;\n this.planner.setModel(model);\n this.currentClipId = null;\n\n this.eventBus.emit(MeframeEvent.ModelSet, model);\n\n this.eventBus.emit(MeframeEvent.CompositionUpdated, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce((acc: number, track: any) => acc + track.clips.length, 0),\n durationUs: model.durationUs,\n });\n\n await this.audioSession.activateAllAudioClips();\n\n this.ensureClipCache(0);\n }\n\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n // Apply patch and get affected clip IDs (simplified for 2-Clip strategy)\n const affectedClipIds = applyModelPatch(this.compositionModel, patch);\n const clipUpdates = this.planner.applyPatch(patch, affectedClipIds);\n\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n affectedClips: Array.from(affectedClipIds),\n });\n\n // Get currently active clips in the 2-Clip window\n const activeClipIds = new Set(\n Array.from(this.clipSessionManager['entries'].keys()).filter((clipId) =>\n this.clipSessionManager.isClipActive(clipId)\n )\n );\n\n // Process clip updates\n for (const update of clipUpdates) {\n if (update.type === 'remove') {\n this.activeClips.delete(update.clipId);\n this.cacheManager.evictClip(update.clipId);\n }\n\n // Notify ClipSessionManager to handle the update\n // This will install new instructions or restart pipeline as needed\n await this.clipSessionManager.handlePlannerUpdate(update.clipId, update);\n\n // Only evict cache for active clips (in current 2-Clip window)\n // Non-active clips will be regenerated when they enter the window\n if (activeClipIds.has(update.clipId) && update.type !== 'remove') {\n this.cacheManager.evictClip(update.clipId);\n }\n }\n }\n\n private handleResourceStateChange(resourceId: string, state: Resource['state']): void {\n if (!this.compositionModel) {\n return;\n }\n\n this.compositionModel.updateResourceState(resourceId, state ?? 'pending');\n\n if (state !== 'ready') {\n return;\n }\n\n const resource = this.compositionModel.getResource(resourceId);\n if (!resource) {\n return;\n }\n\n // Main video/audio resources: data will flow naturally into pipeline\n if (resource.type === 'video' || resource.type === 'audio') {\n return;\n }\n\n // Attachment resources (fonts, images): update instructions for active clips\n const clipIds = this.compositionModel.getClipIdsByResourceId(resourceId);\n for (const clipId of clipIds) {\n // Only update active clips (in 2-Clip window)\n if (!this.clipSessionManager.isClipActive(clipId)) {\n continue;\n }\n\n const clip = this.compositionModel.findClip(clipId);\n if (!clip) {\n continue;\n }\n\n // Rebuild instructions with updated resource status\n const instructions = this.planner.getInstructions(clipId);\n if (!instructions) {\n continue;\n }\n\n // Send updated instructions to worker (no pipeline restart needed)\n const session = this.clipSessionManager.getSession(clipId);\n const visualWorker = session?.visualWorkerHandle;\n if (visualWorker) {\n visualWorker.send('install_instructions', instructions);\n }\n }\n }\n\n async restartWorker(type: WorkerType, clipId?: string): Promise<void> {\n if ((type === 'videoDemux' || type === 'audioDemux' || type === 'videoCompose') && !clipId) {\n throw new Error(`clipId required for restarting ${type} worker`);\n }\n\n this.workers.terminate(type, clipId);\n const worker = await this.workers.get(type, clipId);\n\n this.eventBus.emit(MeframeEvent.WorkerRestarted, {\n type,\n workerId: worker.getWorkerId(),\n reason: 'Manual restart',\n });\n\n if (clipId) {\n const session = this.clipSessionManager.getSession(clipId);\n if (session) {\n await session.activate();\n }\n } else if (type === 'decode' || type === 'audioCompose' || type === 'encode') {\n await this.setupSharedConnections();\n }\n }\n\n async renderFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n // Detect clip change and proactively ensure cache for prev/current/next\n if (this.currentClipId !== clip.id) {\n this.currentClipId = clip.id;\n void this.ensureClipCache(timeUs);\n }\n\n const cachedFrame = await this.cacheManager.getFrame(timeUs, clip.id);\n // console.log('>>>>>>>>>>>> renderFrame', timeUs, clip.id, cachedFrame);\n if (cachedFrame) {\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${timeUs}`,\n });\n return cachedFrame;\n }\n // Cache miss - also ensure cache (defensive, already called on clip change)\n void this.ensureClipCache(timeUs);\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${timeUs}`,\n });\n\n if (signal?.aborted) {\n throw new DOMException('Render aborted', 'AbortError');\n }\n\n // Return null immediately instead of waiting\n // This allows PlaybackController to detect miss and trigger buffering\n return null;\n }\n\n /**\n * Ensure clips are cached using 2-Clip strategy\n * Called by PlaybackController\n */\n async ensureClipCache(timeUs: TimeUs): Promise<void> {\n if (!this.compositionModel) {\n return;\n }\n\n await this.clipSessionManager.ensureClips(timeUs);\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId);\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 30,\n timeoutMs: options?.timeoutMs ?? 5_000,\n });\n }\n\n /**\n * Render a clip completely for L2 cache (bypass ClipSessionManager)\n */\n async renderClipForL2(clipId: string): Promise<any> {\n const sessionId = `${clipId}#l2`;\n console.log('[Orchestrator] rendering clip for L2', clipId, sessionId);\n const session = await this.createSession(sessionId, {\n forL2Only: true,\n clipId: clipId,\n });\n await session.activate();\n\n return session;\n }\n\n /**\n * Create a new session for a clip\n */\n private async createSession(\n sessionId: string,\n options?: {\n forL2Only?: boolean;\n clipId?: string;\n }\n ): Promise<VideoClipSession> {\n const clipId = options?.clipId ?? sessionId;\n const clip = this.compositionModel?.findClip(clipId);\n if (!clip) {\n throw new Error(`Clip ${clipId} not found`);\n }\n\n const session = await VideoClipSession.create({\n clipId,\n sessionId,\n planner: this.planner,\n workerPool: this.workers,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n workerConfigs: this.buildWorkerConfigs(),\n callbacks: {\n onComposedStreamReady: (stream, fps) => {\n if (options?.forL2Only) {\n // L2 channel: don't need L1, cancel stream\n stream.cancel();\n } else {\n // Preview channel: fill L1\n this.cacheManager.receiveComposedFrames(stream, {\n clipId: clipId,\n trackId: this.compositionModel!.mainTrackId,\n fps,\n onFrame: () => {},\n });\n }\n },\n onEncodedStreamReady: (stream, track) => {\n if (options?.forL2Only) {\n console.log('[Orchestrator] receiving encoded stream for L2', clipId, track);\n // L2 channel: write to L2 using clipId, notify on complete\n this.cacheManager.receiveEncodedChunks(stream, clipId, track, {\n onComplete: () => {\n session.dispose();\n },\n onError: (error) => {\n console.error(\n `[Orchestrator] L2 encode stream error for ${clipId} ${track}:`,\n error\n );\n session.dispose();\n },\n });\n } else {\n // Preview channel: don't write to L2, cancel stream\n stream.cancel();\n }\n },\n onPipelineReady: async () => {\n const clip = this.compositionModel?.findClip(clipId);\n if (clip?.resourceId) {\n await this.resourceLoader.fetch(clip.resourceId, {\n priority: options?.forL2Only ? 'low' : 'high',\n sessionId: sessionId,\n trackId: clip.trackId,\n });\n }\n },\n },\n });\n\n this.activeClips.add(sessionId);\n return session;\n }\n\n async dispose(): Promise<void> {\n this.resourceLoader.dispose();\n await this.clipSessionManager.dispose();\n await this.cacheManager.clear();\n\n this.currentClipId = null;\n this.activeClips.clear();\n\n this.workers.terminateAll();\n this.compositionModel = null;\n this.eventBus.dispose();\n }\n\n private buildWorkerConfigs(): Record<WorkerType, any> {\n const config = this.config as any;\n const defaultCanvasWidth = config.global?.defaultCanvasWidth ?? 1280;\n const defaultCanvasHeight = config.global?.defaultCanvasHeight ?? 720;\n const defaultFps = config.global?.defaultFps ?? 30;\n return {\n videoDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n audioDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n decode: {\n video: config.decode?.video,\n audio: config.decode?.audio,\n },\n videoCompose: {\n width: config.compose?.canvas?.width ?? defaultCanvasWidth,\n height: config.compose?.canvas?.height ?? defaultCanvasHeight,\n fps: config.global?.defaultFps ?? defaultFps,\n backgroundColor: config.compose?.canvas?.backgroundColor ?? '#000000',\n enableSmoothing: config.compose?.visual?.enableSmoothing ?? true,\n enableHardwareAcceleration: config.compose?.visual?.enableHardwareAcceleration ?? true,\n },\n audioCompose: {\n ducking: config.compose?.audio?.ducking,\n mixing: config.compose?.audio?.mixing,\n },\n encode: {\n video: {\n codec: 'avc1.42002A',\n width: config.compose?.canvas?.width || defaultCanvasWidth,\n height: config.compose?.canvas?.height || defaultCanvasHeight,\n bitrate: config.encode?.video?.bitrateKbps\n ? config.encode.video.bitrateKbps * 1000\n : 12_000_000,\n framerate: config.encode?.video?.framerate || defaultFps,\n latencyMode: 'quality',\n bitrateMode: 'variable',\n hardwareAcceleration: 'prefer-hardware',\n ...(config.encode?.video as any),\n },\n audio: {\n codec: 'mp4a.40.2',\n sampleRate: 48000,\n numberOfChannels: 2,\n bitrate: config.encode?.audio?.bitrateKbps\n ? config.encode.audio.bitrateKbps * 1000\n : 128000,\n ...(config.encode?.audio as any),\n },\n },\n };\n }\n\n private async setupSharedConnections(): Promise<void> {\n const decodeWorker = await this.workers.get('decode');\n\n decodeWorker.receiveStream((stream, metadata) => {\n if (metadata?.streamType === 'audio') {\n this.audioSession.handleAudioStream(stream as ReadableStream<AudioData>, metadata);\n }\n });\n }\n\n async export(model: CompositionModel, options: ExportOptions): Promise<Blob> {\n return this.muxManager.export(model, options);\n }\n}\n"],"names":["applyModelPatch","clip"],"mappings":"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"}
|
|
@@ -6,12 +6,18 @@ import { BaseWorker } from '../worker/BaseWorker';
|
|
|
6
6
|
import { WorkerType } from '../worker/types';
|
|
7
7
|
|
|
8
8
|
interface VideoClipSessionCallbacks {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
onComposedStreamReady(stream: ReadableStream<VideoFrame>, fps: number): void;
|
|
10
|
+
onEncodedStreamReady(stream: ReadableStream<{
|
|
11
|
+
chunk: EncodedVideoChunk;
|
|
12
|
+
metadata: EncodedVideoChunkMetadata;
|
|
13
|
+
}>, track: 'video' | 'audio'): void;
|
|
14
|
+
onStreamDisposed?(): void;
|
|
15
|
+
onPipelineReady?(): Promise<void>;
|
|
12
16
|
}
|
|
13
17
|
interface VideoClipSessionConfig {
|
|
14
18
|
clipId: string;
|
|
19
|
+
sessionId?: string;
|
|
20
|
+
forL2Only?: boolean;
|
|
15
21
|
planner: CompositionPlanner;
|
|
16
22
|
workerPool: WorkerPool;
|
|
17
23
|
cacheManager: CacheManager;
|
|
@@ -21,6 +27,7 @@ interface VideoClipSessionConfig {
|
|
|
21
27
|
}
|
|
22
28
|
export declare class VideoClipSession {
|
|
23
29
|
private readonly clipId;
|
|
30
|
+
private readonly sessionId;
|
|
24
31
|
private readonly planner;
|
|
25
32
|
private readonly workerPool;
|
|
26
33
|
private readonly cacheManager;
|
|
@@ -56,7 +63,7 @@ export declare class VideoClipSession {
|
|
|
56
63
|
private installInstructions;
|
|
57
64
|
private restartPipeline;
|
|
58
65
|
private releasePipeline;
|
|
59
|
-
|
|
66
|
+
invalidateClipCache(): Promise<void>;
|
|
60
67
|
}
|
|
61
68
|
export {};
|
|
62
69
|
//# sourceMappingURL=VideoClipSession.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoClipSession.d.ts","sourceRoot":"","sources":["../../src/orchestrator/VideoClipSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAQ,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,UAAU,yBAAyB;IACjC,
|
|
1
|
+
{"version":3,"file":"VideoClipSession.d.ts","sourceRoot":"","sources":["../../src/orchestrator/VideoClipSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAQ,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,UAAU,yBAAyB;IACjC,qBAAqB,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7E,oBAAoB,CAClB,MAAM,EAAE,cAAc,CAAC;QAAE,KAAK,EAAE,iBAAiB,CAAC;QAAC,QAAQ,EAAE,yBAAyB,CAAA;KAAE,CAAC,EACzF,KAAK,EAAE,OAAO,GAAG,OAAO,GACvB,IAAI,CAAC;IACR,gBAAgB,CAAC,IAAI,IAAI,CAAC;IAC1B,eAAe,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAED,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACvC,SAAS,EAAE,yBAAyB,CAAC;CACtC;AAQD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0B;IACxD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IAEtD,OAAO,CAAC,kBAAkB,CAAmC;IAC7D,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,oBAAoB,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAS;WAEd,MAAM,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI9E,OAAO;IAWD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOxB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BlE,IAAI,kBAAkB,IAAI,UAAU,GAAG,IAAI,CAE1C;IAED,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,WAAW;YAML,kBAAkB;YAQlB,kBAAkB;YAKlB,oBAAoB;YA0DpB,kBAAkB;YAKlB,cAAc;YA0Bd,oBAAoB;YA8GpB,mBAAmB;YAYnB,eAAe;YAgBf,eAAe;IAuCvB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;CAG3C"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
class VideoClipSession {
|
|
2
2
|
clipId;
|
|
3
|
+
sessionId;
|
|
3
4
|
planner;
|
|
4
5
|
workerPool;
|
|
5
6
|
cacheManager;
|
|
@@ -22,6 +23,7 @@ class VideoClipSession {
|
|
|
22
23
|
}
|
|
23
24
|
constructor(config) {
|
|
24
25
|
this.clipId = config.clipId;
|
|
26
|
+
this.sessionId = config.sessionId ?? config.clipId;
|
|
25
27
|
this.planner = config.planner;
|
|
26
28
|
this.workerPool = config.workerPool;
|
|
27
29
|
this.cacheManager = config.cacheManager;
|
|
@@ -30,7 +32,6 @@ class VideoClipSession {
|
|
|
30
32
|
this.callbacks = config.callbacks;
|
|
31
33
|
}
|
|
32
34
|
async activate() {
|
|
33
|
-
console.log(">>>>>>>>>>>> activate", this.clipId);
|
|
34
35
|
if (this.isActive || this.isDisposed) return;
|
|
35
36
|
const clip = this.getClip();
|
|
36
37
|
const resource = this.getResource();
|
|
@@ -45,13 +46,12 @@ class VideoClipSession {
|
|
|
45
46
|
await this.ensureInstructions(clip);
|
|
46
47
|
this.isActive = true;
|
|
47
48
|
if (this.callbacks.onPipelineReady) {
|
|
48
|
-
await this.callbacks.onPipelineReady(
|
|
49
|
+
await this.callbacks.onPipelineReady();
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
async deactivate() {
|
|
52
53
|
if (!this.isActive || this.isDisposed) return;
|
|
53
54
|
this.isActive = false;
|
|
54
|
-
await this.invalidateClipCache();
|
|
55
55
|
await this.releasePipeline();
|
|
56
56
|
}
|
|
57
57
|
async dispose() {
|
|
@@ -78,7 +78,6 @@ class VideoClipSession {
|
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
80
|
if (this.isActive) {
|
|
81
|
-
console.log(">>>>>>>>>>>> restartPipeline", this.clipId);
|
|
82
81
|
await this.restartPipeline(clip, resource, instructions);
|
|
83
82
|
} else {
|
|
84
83
|
await this.activate();
|
|
@@ -104,13 +103,13 @@ class VideoClipSession {
|
|
|
104
103
|
}
|
|
105
104
|
async setupImagePipeline(clip) {
|
|
106
105
|
await this.acquireWorkers(clip, false);
|
|
107
|
-
await this.connectImagePipeline(
|
|
106
|
+
await this.connectImagePipeline();
|
|
108
107
|
}
|
|
109
|
-
async connectImagePipeline(
|
|
108
|
+
async connectImagePipeline() {
|
|
110
109
|
if (!this.visualWorker || !this.encodeWorker) {
|
|
111
110
|
throw new Error("Pipeline workers not ready");
|
|
112
111
|
}
|
|
113
|
-
const
|
|
112
|
+
const sessionId = this.sessionId;
|
|
114
113
|
this.visualToEncodeChannel = new MessageChannel();
|
|
115
114
|
await this.visualWorker.send(
|
|
116
115
|
"connect",
|
|
@@ -118,7 +117,7 @@ class VideoClipSession {
|
|
|
118
117
|
direction: "downstream",
|
|
119
118
|
port: this.visualToEncodeChannel.port1,
|
|
120
119
|
streamType: "video",
|
|
121
|
-
|
|
120
|
+
sessionId
|
|
122
121
|
},
|
|
123
122
|
{ transfer: [this.visualToEncodeChannel.port1] }
|
|
124
123
|
);
|
|
@@ -128,28 +127,42 @@ class VideoClipSession {
|
|
|
128
127
|
direction: "upstream",
|
|
129
128
|
port: this.visualToEncodeChannel.port2,
|
|
130
129
|
streamType: "video",
|
|
131
|
-
|
|
130
|
+
sessionId
|
|
132
131
|
},
|
|
133
132
|
{ transfer: [this.visualToEncodeChannel.port2] }
|
|
134
133
|
);
|
|
135
134
|
this.visualWorker.receiveStream((stream) => {
|
|
136
135
|
const visualConfig = this.workerConfigs.videoCompose ?? {};
|
|
137
136
|
const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;
|
|
138
|
-
this.callbacks.
|
|
137
|
+
this.callbacks.onComposedStreamReady(stream, fps);
|
|
138
|
+
});
|
|
139
|
+
this.encodeWorker.receiveStream((stream, metadata) => {
|
|
140
|
+
if (metadata?.streamType === "encoded-video") {
|
|
141
|
+
this.callbacks.onEncodedStreamReady(
|
|
142
|
+
stream,
|
|
143
|
+
"video"
|
|
144
|
+
);
|
|
145
|
+
} else if (metadata?.streamType === "encoded-audio") {
|
|
146
|
+
this.callbacks.onEncodedStreamReady(
|
|
147
|
+
stream,
|
|
148
|
+
"audio"
|
|
149
|
+
);
|
|
150
|
+
}
|
|
139
151
|
});
|
|
140
152
|
}
|
|
141
153
|
async setupVideoPipeline(clip, resource) {
|
|
142
|
-
console.log(">>>>>>>>>>>> setupVideoPipeline", this.clipId);
|
|
143
154
|
await this.acquireWorkers(clip, true);
|
|
144
|
-
await this.connectVideoPipeline(
|
|
155
|
+
await this.connectVideoPipeline(resource.id);
|
|
145
156
|
}
|
|
146
157
|
async acquireWorkers(clip, isVideo) {
|
|
147
158
|
if (isVideo) {
|
|
148
159
|
this.decodeWorker = await this.workerPool.get("decode");
|
|
149
|
-
this.videoDemuxWorker = await this.workerPool.get("videoDemux",
|
|
160
|
+
this.videoDemuxWorker = await this.workerPool.get("videoDemux", this.sessionId, {
|
|
161
|
+
lazy: true
|
|
162
|
+
});
|
|
150
163
|
}
|
|
151
164
|
this.encodeWorker = await this.workerPool.get("encode");
|
|
152
|
-
this.visualWorker = await this.workerPool.get("videoCompose",
|
|
165
|
+
this.visualWorker = await this.workerPool.get("videoCompose", this.sessionId, { lazy: true });
|
|
153
166
|
const visualConfig = this.workerConfigs.videoCompose ?? {};
|
|
154
167
|
const renderOverrides = this.compositionModel.renderConfig ?? {};
|
|
155
168
|
const timeline = {
|
|
@@ -164,11 +177,11 @@ class VideoClipSession {
|
|
|
164
177
|
config: { ...visualConfig, ...renderOverrides, timeline }
|
|
165
178
|
});
|
|
166
179
|
}
|
|
167
|
-
async connectVideoPipeline(
|
|
180
|
+
async connectVideoPipeline(resourceId) {
|
|
168
181
|
if (!this.visualWorker || !this.decodeWorker || !this.encodeWorker || !this.videoDemuxWorker) {
|
|
169
182
|
throw new Error("Pipeline workers not ready");
|
|
170
183
|
}
|
|
171
|
-
const
|
|
184
|
+
const sessionId = this.sessionId;
|
|
172
185
|
this.visualToEncodeChannel = new MessageChannel();
|
|
173
186
|
await this.visualWorker.send(
|
|
174
187
|
"connect",
|
|
@@ -176,7 +189,7 @@ class VideoClipSession {
|
|
|
176
189
|
direction: "downstream",
|
|
177
190
|
port: this.visualToEncodeChannel.port1,
|
|
178
191
|
streamType: "video",
|
|
179
|
-
|
|
192
|
+
sessionId
|
|
180
193
|
},
|
|
181
194
|
{ transfer: [this.visualToEncodeChannel.port1] }
|
|
182
195
|
);
|
|
@@ -186,7 +199,7 @@ class VideoClipSession {
|
|
|
186
199
|
direction: "upstream",
|
|
187
200
|
port: this.visualToEncodeChannel.port2,
|
|
188
201
|
streamType: "video",
|
|
189
|
-
|
|
202
|
+
sessionId
|
|
190
203
|
},
|
|
191
204
|
{ transfer: [this.visualToEncodeChannel.port2] }
|
|
192
205
|
);
|
|
@@ -197,7 +210,7 @@ class VideoClipSession {
|
|
|
197
210
|
direction: "downstream",
|
|
198
211
|
port: this.decodeToVisualChannel.port1,
|
|
199
212
|
streamType: "video",
|
|
200
|
-
|
|
213
|
+
sessionId
|
|
201
214
|
},
|
|
202
215
|
{ transfer: [this.decodeToVisualChannel.port1] }
|
|
203
216
|
);
|
|
@@ -207,7 +220,7 @@ class VideoClipSession {
|
|
|
207
220
|
direction: "upstream",
|
|
208
221
|
port: this.decodeToVisualChannel.port2,
|
|
209
222
|
streamType: "video",
|
|
210
|
-
|
|
223
|
+
sessionId
|
|
211
224
|
},
|
|
212
225
|
{ transfer: [this.decodeToVisualChannel.port2] }
|
|
213
226
|
);
|
|
@@ -218,7 +231,7 @@ class VideoClipSession {
|
|
|
218
231
|
direction: "downstream",
|
|
219
232
|
port: this.demuxToDecodeChannel.port1,
|
|
220
233
|
streamType: "video",
|
|
221
|
-
|
|
234
|
+
sessionId
|
|
222
235
|
},
|
|
223
236
|
{ transfer: [this.demuxToDecodeChannel.port1] }
|
|
224
237
|
);
|
|
@@ -228,7 +241,7 @@ class VideoClipSession {
|
|
|
228
241
|
direction: "upstream",
|
|
229
242
|
port: this.demuxToDecodeChannel.port2,
|
|
230
243
|
streamType: "video",
|
|
231
|
-
|
|
244
|
+
sessionId
|
|
232
245
|
},
|
|
233
246
|
{ transfer: [this.demuxToDecodeChannel.port2] }
|
|
234
247
|
);
|
|
@@ -236,13 +249,26 @@ class VideoClipSession {
|
|
|
236
249
|
await this.videoDemuxWorker.send("configure", {
|
|
237
250
|
initial: true,
|
|
238
251
|
resourceId,
|
|
239
|
-
|
|
252
|
+
sessionId,
|
|
240
253
|
config: demuxConfig
|
|
241
254
|
});
|
|
242
255
|
this.visualWorker.receiveStream((stream) => {
|
|
243
256
|
const visualConfig = this.workerConfigs.videoCompose ?? {};
|
|
244
257
|
const fps = this.compositionModel.fps ?? visualConfig.fps ?? 30;
|
|
245
|
-
this.callbacks.
|
|
258
|
+
this.callbacks.onComposedStreamReady(stream, fps);
|
|
259
|
+
});
|
|
260
|
+
this.encodeWorker.receiveStream((stream, metadata) => {
|
|
261
|
+
if (metadata?.streamType === "video") {
|
|
262
|
+
this.callbacks.onEncodedStreamReady(
|
|
263
|
+
stream,
|
|
264
|
+
"video"
|
|
265
|
+
);
|
|
266
|
+
} else if (metadata?.streamType === "audio") {
|
|
267
|
+
this.callbacks.onEncodedStreamReady(
|
|
268
|
+
stream,
|
|
269
|
+
"audio"
|
|
270
|
+
);
|
|
271
|
+
}
|
|
246
272
|
});
|
|
247
273
|
}
|
|
248
274
|
async installInstructions(instructions) {
|
|
@@ -267,7 +293,7 @@ class VideoClipSession {
|
|
|
267
293
|
async releasePipeline() {
|
|
268
294
|
if (this.visualWorker && this.instructionContext) {
|
|
269
295
|
await this.visualWorker.notify("dispose_clip", {
|
|
270
|
-
|
|
296
|
+
sessionId: this.sessionId,
|
|
271
297
|
revision: this.instructionContext.revision
|
|
272
298
|
});
|
|
273
299
|
}
|
|
@@ -281,15 +307,15 @@ class VideoClipSession {
|
|
|
281
307
|
this.decodeToVisualChannel = null;
|
|
282
308
|
this.demuxToDecodeChannel = null;
|
|
283
309
|
if (this.visualStream && this.callbacks.onStreamDisposed) {
|
|
284
|
-
this.callbacks.onStreamDisposed(
|
|
310
|
+
this.callbacks.onStreamDisposed();
|
|
285
311
|
}
|
|
286
312
|
this.visualStream = null;
|
|
287
313
|
if (this.videoDemuxWorker) {
|
|
288
|
-
this.workerPool.terminate("videoDemux", this.
|
|
314
|
+
this.workerPool.terminate("videoDemux", this.sessionId);
|
|
289
315
|
this.videoDemuxWorker = null;
|
|
290
316
|
}
|
|
291
317
|
if (this.visualWorker) {
|
|
292
|
-
this.workerPool.terminate("videoCompose", this.
|
|
318
|
+
this.workerPool.terminate("videoCompose", this.sessionId);
|
|
293
319
|
this.visualWorker = null;
|
|
294
320
|
}
|
|
295
321
|
this.decodeWorker = null;
|