@meframe/core 0.3.6 → 0.3.8

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 (57) hide show
  1. package/dist/medeo-fe/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +1 -0
  2. package/dist/{node_modules → medeo-fe/node_modules}/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js +2 -2
  3. package/dist/medeo-fe/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +1 -0
  4. package/dist/orchestrator/ExportScheduler.d.ts +9 -7
  5. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
  6. package/dist/orchestrator/ExportScheduler.js +182 -80
  7. package/dist/orchestrator/ExportScheduler.js.map +1 -1
  8. package/dist/orchestrator/Orchestrator.js +22 -22
  9. package/dist/orchestrator/Orchestrator.js.map +1 -1
  10. package/dist/orchestrator/VideoWindowDecodeSession.d.ts.map +1 -1
  11. package/dist/orchestrator/VideoWindowDecodeSession.js +15 -3
  12. package/dist/orchestrator/VideoWindowDecodeSession.js.map +1 -1
  13. package/dist/stages/compose/VideoComposer.d.ts +2 -0
  14. package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
  15. package/dist/stages/compose/VideoComposer.js +41 -2
  16. package/dist/stages/compose/VideoComposer.js.map +1 -1
  17. package/dist/stages/decode/video-decoder.d.ts.map +1 -1
  18. package/dist/stages/decode/video-decoder.js +5 -3
  19. package/dist/stages/decode/video-decoder.js.map +1 -1
  20. package/dist/stages/encode/BaseEncoder.d.ts.map +1 -1
  21. package/dist/stages/encode/BaseEncoder.js +34 -3
  22. package/dist/stages/encode/BaseEncoder.js.map +1 -1
  23. package/dist/stages/encode/VideoChunkEncoder.d.ts.map +1 -1
  24. package/dist/stages/mux/MP4Muxer.js +1 -1
  25. package/dist/utils/mp4box.js +1 -1
  26. package/dist/utils/time-utils.d.ts +15 -0
  27. package/dist/utils/time-utils.d.ts.map +1 -1
  28. package/dist/utils/time-utils.js +33 -0
  29. package/dist/utils/time-utils.js.map +1 -0
  30. package/dist/worker/WorkerChannel.d.ts.map +1 -1
  31. package/dist/worker/WorkerChannel.js +3 -15
  32. package/dist/worker/WorkerChannel.js.map +1 -1
  33. package/dist/worker/WorkerPool.d.ts.map +1 -1
  34. package/dist/worker/WorkerPool.js +4 -12
  35. package/dist/worker/WorkerPool.js.map +1 -1
  36. package/dist/worker/types.d.ts +1 -1
  37. package/dist/worker/types.d.ts.map +1 -1
  38. package/dist/worker/types.js.map +1 -1
  39. package/dist/worker/worker-event-whitelist.d.ts.map +1 -1
  40. package/dist/workers/stages/{compose/video-compose.worker.KMZjuJuY.js → export/export.worker.SahP9aVK.js} +915 -217
  41. package/dist/workers/stages/export/export.worker.SahP9aVK.js.map +1 -0
  42. package/dist/workers/worker-manifest.json +1 -3
  43. package/package.json +1 -1
  44. package/dist/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +0 -1
  45. package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +0 -1
  46. package/dist/orchestrator/VideoClipSession.d.ts +0 -80
  47. package/dist/orchestrator/VideoClipSession.d.ts.map +0 -1
  48. package/dist/orchestrator/VideoClipSession.js +0 -361
  49. package/dist/orchestrator/VideoClipSession.js.map +0 -1
  50. package/dist/workers/WorkerChannel.DQK8rAab.js +0 -528
  51. package/dist/workers/WorkerChannel.DQK8rAab.js.map +0 -1
  52. package/dist/workers/stages/compose/audio-compose.worker.B4Io5w9i.js +0 -1063
  53. package/dist/workers/stages/compose/audio-compose.worker.B4Io5w9i.js.map +0 -1
  54. package/dist/workers/stages/compose/video-compose.worker.KMZjuJuY.js.map +0 -1
  55. package/dist/workers/stages/encode/video-encode.worker.D6aB_rF9.js +0 -334
  56. package/dist/workers/stages/encode/video-encode.worker.D6aB_rF9.js.map +0 -1
  57. /package/dist/{node_modules → medeo-fe/node_modules}/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js +0 -0
@@ -1,80 +0,0 @@
1
- import { WorkerPool } from '../worker/WorkerPool';
2
- import { CacheManager } from '../cache/CacheManager';
3
- import { CompositionModel } from '../model';
4
- import { CompositionPlanner, ClipUpdateResult } from './CompositionPlanner';
5
- import { WorkerType } from '../worker/types';
6
- import { ResourceLoader } from '../stages/load/ResourceLoader';
7
-
8
- interface VideoClipSessionCallbacks {
9
- onEncodedStreamReady(stream: ReadableStream<{
10
- chunk: EncodedVideoChunk;
11
- metadata: EncodedVideoChunkMetadata;
12
- }>, track: 'video' | 'audio'): Promise<void>;
13
- onAudioStreamReady?(stream: ReadableStream<AudioData>, metadata: {
14
- sessionId: string;
15
- clipStartUs: number;
16
- clipDurationUs: number;
17
- }): void;
18
- onStreamDisposed?(): void;
19
- }
20
- interface VideoClipSessionConfig {
21
- clipId: string;
22
- sessionId?: string;
23
- planner: CompositionPlanner;
24
- workerPool: WorkerPool;
25
- cacheManager: CacheManager;
26
- compositionModel: CompositionModel;
27
- workerConfigs: Record<WorkerType, any>;
28
- callbacks: VideoClipSessionCallbacks;
29
- resourceLoader?: ResourceLoader;
30
- }
31
- /**
32
- * VideoClipSession - Export-only session for rendering video clips
33
- *
34
- * Pipeline: VideoWindowDecodeSession (decode) -> ComposeWorker -> EncodeWorker
35
- * All sessions now use this architecture for export processing
36
- */
37
- export declare class VideoClipSession {
38
- private readonly clipId;
39
- private readonly sessionId;
40
- private readonly planner;
41
- private readonly workerPool;
42
- private readonly cacheManager;
43
- private readonly compositionModel;
44
- private readonly workerConfigs;
45
- private readonly callbacks;
46
- private readonly resourceLoader?;
47
- private instructionContext;
48
- private composeWorker;
49
- private videoEncodeWorker;
50
- private onDemandSession;
51
- private visualStream;
52
- private composeToEncodeChannel;
53
- private isActive;
54
- private isDisposed;
55
- static create(config: VideoClipSessionConfig): Promise<VideoClipSession>;
56
- private constructor();
57
- activate(): Promise<void>;
58
- deactivate(): Promise<void>;
59
- dispose(): Promise<void>;
60
- handlePlannerUpdate(update: ClipUpdateResult): Promise<void>;
61
- private getClip;
62
- private getResource;
63
- private extractAttachmentImageResources;
64
- /**
65
- * Load and transfer attachment images to ComposeWorker
66
- * Must be called after workers are created and before sending video stream
67
- */
68
- private loadAndTransferAttachments;
69
- private ensureInstructions;
70
- private setupImagePipeline;
71
- private connectImagePipeline;
72
- private setupVideoPipeline;
73
- private acquireWorkers;
74
- private connectVideoPipeline;
75
- private installInstructions;
76
- private releasePipeline;
77
- invalidateClipCache(): Promise<void>;
78
- }
79
- export {};
80
- //# sourceMappingURL=VideoClipSession.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"VideoClipSession.d.ts","sourceRoot":"","sources":["../../src/orchestrator/VideoClipSession.ts"],"names":[],"mappings":"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"}
@@ -1,361 +0,0 @@
1
- import { hasResourceId } from "../model/types.js";
2
- import { VideoWindowDecodeSession } from "./VideoWindowDecodeSession.js";
3
- class VideoClipSession {
4
- clipId;
5
- sessionId;
6
- planner;
7
- workerPool;
8
- cacheManager;
9
- compositionModel;
10
- workerConfigs;
11
- callbacks;
12
- resourceLoader;
13
- instructionContext = null;
14
- composeWorker = null;
15
- videoEncodeWorker = null;
16
- onDemandSession = null;
17
- visualStream = null;
18
- composeToEncodeChannel = null;
19
- isActive = false;
20
- isDisposed = false;
21
- static async create(config) {
22
- return new VideoClipSession(config);
23
- }
24
- constructor(config) {
25
- this.clipId = config.clipId;
26
- this.sessionId = config.sessionId ?? config.clipId;
27
- this.planner = config.planner;
28
- this.workerPool = config.workerPool;
29
- this.cacheManager = config.cacheManager;
30
- this.compositionModel = config.compositionModel;
31
- this.workerConfigs = config.workerConfigs;
32
- this.callbacks = config.callbacks;
33
- this.resourceLoader = config.resourceLoader;
34
- }
35
- async activate() {
36
- if (this.isActive || this.isDisposed) return;
37
- await this.ensureInstructions();
38
- const clip = this.getClip();
39
- const resource = this.getResource();
40
- if (!clip || !resource) {
41
- console.warn("[VideoClipSession] activate: clip or resource not found", this.sessionId);
42
- return;
43
- }
44
- if (resource.type === "video") {
45
- await this.setupVideoPipeline(clip, resource);
46
- } else if (resource.type === "image") {
47
- await this.setupImagePipeline(clip);
48
- } else {
49
- console.warn(
50
- "[VideoClipSession] Unknown resource type:",
51
- resource.type,
52
- "for",
53
- this.sessionId
54
- );
55
- }
56
- this.isActive = true;
57
- }
58
- async deactivate() {
59
- if (!this.isActive || this.isDisposed) return;
60
- this.isActive = false;
61
- await this.releasePipeline();
62
- }
63
- async dispose() {
64
- if (this.isDisposed) return;
65
- await this.deactivate();
66
- this.planner.releaseClip(this.clipId);
67
- this.isDisposed = true;
68
- }
69
- async handlePlannerUpdate(update) {
70
- if (this.isDisposed) {
71
- return;
72
- }
73
- if (update.type === "remove") {
74
- await this.dispose();
75
- return;
76
- }
77
- const instructions = update.instructions;
78
- if (!instructions) {
79
- return;
80
- }
81
- const clip = this.getClip();
82
- const resource = this.getResource();
83
- if (!clip || !resource || resource.type !== "video" && resource.type !== "image") {
84
- return;
85
- }
86
- if (this.isActive) {
87
- await this.releasePipeline();
88
- this.isActive = false;
89
- }
90
- await this.activate();
91
- }
92
- getClip() {
93
- return this.compositionModel?.findClip?.(this.clipId) ?? null;
94
- }
95
- getResource() {
96
- const clip = this.getClip();
97
- if (!clip || !hasResourceId(clip)) return null;
98
- return this.compositionModel.getResource(clip.resourceId) ?? null;
99
- }
100
- extractAttachmentImageResources() {
101
- if (!this.instructionContext?.instructions) return [];
102
- const resourceIdSet = /* @__PURE__ */ new Set();
103
- for (const layer of this.instructionContext.instructions.layers) {
104
- if (!layer.payload.attachmentId) continue;
105
- if (layer.type === "image") {
106
- const imagePayload = layer.payload;
107
- if (imagePayload.oldResourceId) {
108
- resourceIdSet.add(imagePayload.oldResourceId);
109
- }
110
- const resourceId = imagePayload.resourceId;
111
- if (resourceId) {
112
- resourceIdSet.add(resourceId);
113
- }
114
- }
115
- }
116
- return Array.from(resourceIdSet);
117
- }
118
- /**
119
- * Load and transfer attachment images to ComposeWorker
120
- * Must be called after workers are created and before sending video stream
121
- */
122
- async loadAndTransferAttachments(sessionId, clipId) {
123
- const attachmentResources = this.extractAttachmentImageResources();
124
- if (attachmentResources.length === 0 || !this.resourceLoader) {
125
- return;
126
- }
127
- await Promise.all(
128
- attachmentResources.map(async (resourceId) => {
129
- const resource = this.compositionModel.getResource(resourceId);
130
- if (!resource) {
131
- console.warn(`[VideoClipSession] Resource not found: ${resourceId}`);
132
- return;
133
- }
134
- const imageBitmap = await this.resourceLoader.loadImage(resource);
135
- if (!imageBitmap) {
136
- console.warn(`[VideoClipSession] Failed to load attachment: ${resourceId}`);
137
- return;
138
- }
139
- await this.composeWorker.send(
140
- "receive_image",
141
- { clipId, resourceId, sessionId, imageBitmap },
142
- { transfer: [imageBitmap] }
143
- );
144
- })
145
- );
146
- }
147
- async ensureInstructions() {
148
- const clip = this.getClip();
149
- if (!clip) {
150
- throw new Error(`Clip ${this.clipId} not found`);
151
- }
152
- const plan = this.planner.buildClipPlan(clip, { cache: false });
153
- await this.installInstructions(plan.instructions);
154
- }
155
- async setupImagePipeline(clip) {
156
- await this.acquireWorkers(clip);
157
- await this.connectImagePipeline();
158
- }
159
- async connectImagePipeline() {
160
- if (!this.composeWorker || !this.videoEncodeWorker) {
161
- throw new Error("Pipeline workers not ready");
162
- }
163
- if (!this.resourceLoader) {
164
- throw new Error("[VideoClipSession] ResourceLoader not available for image pipeline");
165
- }
166
- const sessionId = this.sessionId;
167
- const clip = this.getClip();
168
- if (!clip) {
169
- throw new Error("[VideoClipSession] Clip not found for pipeline connection");
170
- }
171
- if (!this.instructionContext) {
172
- throw new Error("[VideoClipSession] Instructions not installed before connecting pipeline");
173
- }
174
- this.composeToEncodeChannel = new MessageChannel();
175
- await this.composeWorker.send(
176
- "connect",
177
- {
178
- direction: "downstream",
179
- port: this.composeToEncodeChannel.port1,
180
- streamType: "video",
181
- sessionId
182
- },
183
- { transfer: [this.composeToEncodeChannel.port1] }
184
- );
185
- await this.videoEncodeWorker.send(
186
- "connect",
187
- {
188
- direction: "upstream",
189
- port: this.composeToEncodeChannel.port2,
190
- streamType: "video",
191
- sessionId
192
- },
193
- { transfer: [this.composeToEncodeChannel.port2] }
194
- );
195
- this.videoEncodeWorker.receiveStream((stream, metadata) => {
196
- this.callbacks.onEncodedStreamReady(
197
- stream,
198
- metadata?.streamType ?? "video"
199
- );
200
- });
201
- await this.loadAndTransferAttachments(sessionId, clip.id);
202
- if (hasResourceId(clip)) {
203
- const resource = this.getResource();
204
- if (!resource) {
205
- throw new Error("[VideoClipSession] Resource not found for image pipeline");
206
- }
207
- const imageBitmap = await this.resourceLoader.loadImage(resource);
208
- await this.composeWorker.send(
209
- "receive_image",
210
- {
211
- resourceId: resource.id,
212
- sessionId,
213
- imageBitmap,
214
- instructions: this.instructionContext.instructions
215
- },
216
- { transfer: [imageBitmap] }
217
- );
218
- }
219
- }
220
- async setupVideoPipeline(clip, resource) {
221
- await this.acquireWorkers(clip);
222
- this.onDemandSession = await VideoWindowDecodeSession.create({
223
- clipId: this.clipId,
224
- resourceId: resource.id,
225
- targetTimeUs: clip.trimStartUs ?? 0,
226
- globalTimeUs: clip.startUs,
227
- mp4IndexCache: this.cacheManager.mp4IndexCache,
228
- cacheManager: this.cacheManager,
229
- compositionModel: this.compositionModel,
230
- resourceLoader: this.resourceLoader,
231
- fps: this.compositionModel.fps ?? 30
232
- });
233
- await this.connectVideoPipeline();
234
- }
235
- async acquireWorkers(clip) {
236
- this.composeWorker = await this.workerPool.getOrCreate("videoCompose", this.sessionId, {
237
- lazy: true
238
- });
239
- const visualConfig = this.workerConfigs.videoCompose ?? {};
240
- const renderOverrides = this.compositionModel.renderConfig ?? {};
241
- const timeline = {
242
- clipId: clip.id,
243
- clipStartUs: clip.startUs,
244
- clipEndUs: clip.startUs + clip.durationUs,
245
- clipDurationUs: clip.durationUs,
246
- compositionFps: this.compositionModel.fps ?? visualConfig.fps ?? 30
247
- };
248
- await this.composeWorker.send("configure", {
249
- initial: true,
250
- clipId: clip.id,
251
- config: { ...visualConfig, ...renderOverrides, timeline }
252
- });
253
- this.videoEncodeWorker = await this.workerPool.getOrCreate("videoEncode", this.sessionId, {
254
- lazy: true
255
- });
256
- const encodeConfig = this.workerConfigs.videoEncode ?? {};
257
- const encoderConfig = { ...encodeConfig };
258
- if (this.compositionModel.renderConfig?.width) {
259
- encoderConfig.width = this.compositionModel.renderConfig.width;
260
- }
261
- if (this.compositionModel.renderConfig?.height) {
262
- encoderConfig.height = this.compositionModel.renderConfig.height;
263
- }
264
- await this.videoEncodeWorker.send("configure", {
265
- initial: true,
266
- config: encoderConfig
267
- });
268
- }
269
- async connectVideoPipeline() {
270
- if (!this.composeWorker || !this.videoEncodeWorker || !this.onDemandSession) {
271
- throw new Error("Pipeline workers not ready");
272
- }
273
- const sessionId = this.sessionId;
274
- const clip = this.getClip();
275
- if (!clip) {
276
- throw new Error("[VideoClipSession] Clip not found for pipeline connection");
277
- }
278
- if (!this.instructionContext) {
279
- throw new Error("[VideoClipSession] Instructions not installed before connecting pipeline");
280
- }
281
- this.composeToEncodeChannel = new MessageChannel();
282
- await this.composeWorker.send(
283
- "connect",
284
- {
285
- direction: "downstream",
286
- port: this.composeToEncodeChannel.port1,
287
- streamType: "video",
288
- sessionId
289
- },
290
- { transfer: [this.composeToEncodeChannel.port1] }
291
- );
292
- await this.videoEncodeWorker.send(
293
- "connect",
294
- {
295
- direction: "upstream",
296
- port: this.composeToEncodeChannel.port2,
297
- streamType: "video",
298
- sessionId
299
- },
300
- { transfer: [this.composeToEncodeChannel.port2] }
301
- );
302
- this.videoEncodeWorker.receiveStream((stream, metadata) => {
303
- this.callbacks.onEncodedStreamReady(
304
- stream,
305
- metadata?.streamType ?? "video"
306
- );
307
- });
308
- await this.loadAndTransferAttachments(sessionId, clip.id);
309
- const trimStartUs = clip.trimStartUs ?? 0;
310
- const frameStream = await this.onDemandSession.decodeRangeToStream(
311
- trimStartUs,
312
- trimStartUs + clip.durationUs
313
- );
314
- await this.composeWorker.sendStream(frameStream, {
315
- sessionId,
316
- streamType: "video",
317
- instructions: this.instructionContext.instructions
318
- });
319
- }
320
- async installInstructions(instructions) {
321
- this.instructionContext = {
322
- revision: instructions.revision,
323
- instructions,
324
- status: instructions.status
325
- };
326
- }
327
- async releasePipeline() {
328
- if (this.composeWorker && this.instructionContext) {
329
- await this.composeWorker.notify("dispose_clip", {
330
- sessionId: this.sessionId,
331
- revision: this.instructionContext.revision
332
- });
333
- }
334
- if (this.visualStream && this.callbacks.onStreamDisposed) {
335
- this.callbacks.onStreamDisposed();
336
- }
337
- this.visualStream = null;
338
- if (this.onDemandSession) {
339
- await this.onDemandSession.dispose();
340
- this.onDemandSession = null;
341
- }
342
- if (this.composeWorker) {
343
- this.workerPool.terminate("videoCompose", this.sessionId);
344
- this.composeWorker = null;
345
- }
346
- if (this.videoEncodeWorker) {
347
- this.workerPool.terminate("videoEncode", this.sessionId);
348
- this.videoEncodeWorker = null;
349
- }
350
- this.composeToEncodeChannel?.port1.close();
351
- this.composeToEncodeChannel?.port2.close();
352
- this.composeToEncodeChannel = null;
353
- }
354
- async invalidateClipCache() {
355
- await this.cacheManager.invalidateClip(this.clipId);
356
- }
357
- }
358
- export {
359
- VideoClipSession
360
- };
361
- //# sourceMappingURL=VideoClipSession.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"VideoClipSession.js","sources":["../../src/orchestrator/VideoClipSession.ts"],"sourcesContent":["import type { WorkerPool } from '../worker/WorkerPool';\nimport type { CacheManager } from '../cache/CacheManager';\nimport type { CompositionModel, Clip } from '../model';\nimport type { CompositionPlanner, ClipUpdateResult } from './CompositionPlanner';\nimport type { ClipInstructionSet } from '../stages/compose/instructions';\nimport type { BaseWorker } from '../worker/BaseWorker';\nimport type { WorkerType } from '../worker/types';\nimport type { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { hasResourceId } from '../model/types';\nimport { VideoWindowDecodeSession } from './VideoWindowDecodeSession';\n\ninterface VideoClipSessionCallbacks {\n onEncodedStreamReady(\n stream: ReadableStream<{ chunk: EncodedVideoChunk; metadata: EncodedVideoChunkMetadata }>,\n track: 'video' | 'audio'\n ): Promise<void>;\n onAudioStreamReady?(\n stream: ReadableStream<AudioData>,\n metadata: {\n sessionId: string;\n clipStartUs: number;\n clipDurationUs: number;\n }\n ): void;\n onStreamDisposed?(): void;\n}\n\ninterface VideoClipSessionConfig {\n clipId: string;\n sessionId?: string;\n planner: CompositionPlanner;\n workerPool: WorkerPool;\n cacheManager: CacheManager;\n compositionModel: CompositionModel;\n workerConfigs: Record<WorkerType, any>;\n callbacks: VideoClipSessionCallbacks;\n resourceLoader?: ResourceLoader;\n}\n\ninterface InstructionContext {\n revision: number;\n instructions: ClipInstructionSet;\n status: ClipInstructionSet['status'];\n}\n\n/**\n * VideoClipSession - Export-only session for rendering video clips\n *\n * Pipeline: VideoWindowDecodeSession (decode) -> ComposeWorker -> EncodeWorker\n * All sessions now use this architecture for export processing\n */\nexport class VideoClipSession {\n private readonly clipId: string;\n private readonly sessionId: string;\n private readonly planner: CompositionPlanner;\n private readonly workerPool: WorkerPool;\n private readonly cacheManager: CacheManager;\n private readonly compositionModel: CompositionModel;\n private readonly workerConfigs: Record<WorkerType, any>;\n private readonly callbacks: VideoClipSessionCallbacks;\n private readonly resourceLoader?: ResourceLoader;\n\n private instructionContext: InstructionContext | null = null;\n private composeWorker: BaseWorker | null = null;\n private videoEncodeWorker: BaseWorker | null = null;\n private onDemandSession: VideoWindowDecodeSession | null = null;\n private visualStream: ReadableStream<VideoFrame> | null = null;\n private composeToEncodeChannel: MessageChannel | null = null;\n private isActive = false;\n private isDisposed = false;\n\n static async create(config: VideoClipSessionConfig): Promise<VideoClipSession> {\n return new VideoClipSession(config);\n }\n\n private constructor(config: VideoClipSessionConfig) {\n this.clipId = config.clipId;\n this.sessionId = config.sessionId ?? config.clipId;\n this.planner = config.planner;\n this.workerPool = config.workerPool;\n this.cacheManager = config.cacheManager;\n this.compositionModel = config.compositionModel;\n this.workerConfigs = config.workerConfigs;\n this.callbacks = config.callbacks;\n this.resourceLoader = config.resourceLoader;\n }\n\n async activate(): Promise<void> {\n if (this.isActive || this.isDisposed) return;\n\n // Prepare instructions (but don't send yet - will send with stream)\n await this.ensureInstructions();\n\n const clip = this.getClip();\n const resource = this.getResource();\n if (!clip || !resource) {\n console.warn('[VideoClipSession] activate: clip or resource not found', this.sessionId);\n return;\n }\n if (resource.type === 'video') {\n await this.setupVideoPipeline(clip, resource);\n } else if (resource.type === 'image') {\n await this.setupImagePipeline(clip);\n } else {\n console.warn(\n '[VideoClipSession] Unknown resource type:',\n resource.type,\n 'for',\n this.sessionId\n );\n }\n\n this.isActive = true;\n\n // Note: Attachment resources are loaded in connectVideoPipeline/connectImagePipeline\n // before sending video stream, ensuring watermarks appear from the first frame\n }\n\n async deactivate(): Promise<void> {\n if (!this.isActive || this.isDisposed) return;\n this.isActive = false;\n\n await this.releasePipeline();\n }\n\n async dispose(): Promise<void> {\n if (this.isDisposed) return;\n await this.deactivate();\n this.planner.releaseClip(this.clipId);\n this.isDisposed = true;\n }\n\n async handlePlannerUpdate(update: ClipUpdateResult): Promise<void> {\n if (this.isDisposed) {\n return;\n }\n\n if (update.type === 'remove') {\n await this.dispose();\n return;\n }\n\n const instructions = update.instructions;\n if (!instructions) {\n return;\n }\n\n const clip = this.getClip();\n const resource = this.getResource();\n if (!clip || !resource || (resource.type !== 'video' && resource.type !== 'image')) {\n return;\n }\n\n // Any update requires pipeline restart (stream is closed after cache eviction)\n if (this.isActive) {\n await this.releasePipeline();\n this.isActive = false;\n }\n await this.activate();\n }\n\n private getClip(): Clip | null {\n return this.compositionModel?.findClip?.(this.clipId) ?? null;\n }\n\n private getResource() {\n const clip = this.getClip();\n if (!clip || !hasResourceId(clip)) return null;\n return this.compositionModel.getResource(clip.resourceId) ?? null;\n }\n\n private extractAttachmentImageResources(): string[] {\n if (!this.instructionContext?.instructions) return [];\n\n const resourceIdSet = new Set<string>();\n for (const layer of this.instructionContext.instructions.layers) {\n // Only process attachment layers (with attachmentId)\n if (!layer.payload.attachmentId) continue;\n\n if (layer.type === 'image') {\n const imagePayload = layer.payload;\n if (imagePayload.oldResourceId) {\n resourceIdSet.add(imagePayload.oldResourceId);\n }\n const resourceId = imagePayload.resourceId;\n if (resourceId) {\n resourceIdSet.add(resourceId);\n }\n }\n }\n return Array.from(resourceIdSet);\n }\n\n /**\n * Load and transfer attachment images to ComposeWorker\n * Must be called after workers are created and before sending video stream\n */\n private async loadAndTransferAttachments(sessionId: string, clipId: string): Promise<void> {\n const attachmentResources = this.extractAttachmentImageResources();\n if (attachmentResources.length === 0 || !this.resourceLoader) {\n return;\n }\n\n // Parallel load all attachments\n await Promise.all(\n attachmentResources.map(async (resourceId) => {\n const resource = this.compositionModel.getResource(resourceId);\n if (!resource) {\n console.warn(`[VideoClipSession] Resource not found: ${resourceId}`);\n return;\n }\n const imageBitmap = await this.resourceLoader!.loadImage(resource);\n if (!imageBitmap) {\n console.warn(`[VideoClipSession] Failed to load attachment: ${resourceId}`);\n return;\n }\n\n await this.composeWorker!.send(\n 'receive_image',\n { clipId, resourceId, sessionId, imageBitmap },\n { transfer: [imageBitmap] }\n );\n })\n );\n }\n\n private async ensureInstructions(): Promise<void> {\n // Always get fresh clip from model to ensure latest attachments\n const clip = this.getClip();\n if (!clip) {\n throw new Error(`Clip ${this.clipId} not found`);\n }\n\n // For export, always build fresh instructions (no cache) to ensure latest attachments\n const plan = this.planner.buildClipPlan(clip, { cache: false });\n await this.installInstructions(plan.instructions);\n }\n\n private async setupImagePipeline(clip: Clip): Promise<void> {\n await this.acquireWorkers(clip);\n await this.connectImagePipeline();\n }\n\n private async connectImagePipeline(): Promise<void> {\n if (!this.composeWorker || !this.videoEncodeWorker) {\n throw new Error('Pipeline workers not ready');\n }\n\n if (!this.resourceLoader) {\n throw new Error('[VideoClipSession] ResourceLoader not available for image pipeline');\n }\n\n const sessionId = this.sessionId;\n const clip = this.getClip();\n if (!clip) {\n throw new Error('[VideoClipSession] Clip not found for pipeline connection');\n }\n\n if (!this.instructionContext) {\n throw new Error('[VideoClipSession] Instructions not installed before connecting pipeline');\n }\n\n // STEP 1: Connect ComposeWorker -> EncodeWorker\n this.composeToEncodeChannel = new MessageChannel();\n await this.composeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.composeToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port1] }\n );\n await this.videoEncodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.composeToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port2] }\n );\n\n // STEP 2: Setup EncodeWorker stream receiver\n this.videoEncodeWorker.receiveStream((stream, metadata) => {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n metadata?.streamType ?? 'video'\n );\n });\n\n // STEP 3: Load and transfer attachment images (before sending main track image)\n await this.loadAndTransferAttachments(sessionId, clip.id);\n\n // STEP 4: Load main track image and send to ComposeWorker with instructions\n if (hasResourceId(clip)) {\n const resource = this.getResource();\n if (!resource) {\n throw new Error('[VideoClipSession] Resource not found for image pipeline');\n }\n\n // Load image directly\n const imageBitmap = await this.resourceLoader.loadImage(resource);\n\n // Send to ComposeWorker with instructions (same as video pipeline metadata)\n await this.composeWorker.send(\n 'receive_image',\n {\n resourceId: resource.id,\n sessionId,\n imageBitmap,\n instructions: this.instructionContext.instructions,\n },\n { transfer: [imageBitmap] }\n );\n }\n }\n\n private async setupVideoPipeline(clip: Clip, resource: { id: string }): Promise<void> {\n await this.acquireWorkers(clip);\n\n // Create VideoWindowDecodeSession for main-thread decoding\n this.onDemandSession = await VideoWindowDecodeSession.create({\n clipId: this.clipId,\n resourceId: resource.id,\n targetTimeUs: clip.trimStartUs ?? 0,\n globalTimeUs: clip.startUs,\n mp4IndexCache: this.cacheManager.mp4IndexCache,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel,\n resourceLoader: this.resourceLoader!,\n fps: this.compositionModel.fps ?? 30,\n });\n\n await this.connectVideoPipeline();\n }\n\n private async acquireWorkers(clip: Clip): Promise<void> {\n // VideoComposeWorker\n this.composeWorker = await this.workerPool.getOrCreate('videoCompose', this.sessionId, {\n lazy: true,\n });\n const visualConfig = this.workerConfigs.videoCompose ?? {};\n const renderOverrides = this.compositionModel.renderConfig ?? {};\n const timeline = {\n clipId: clip.id,\n clipStartUs: clip.startUs,\n clipEndUs: clip.startUs + clip.durationUs,\n clipDurationUs: clip.durationUs,\n compositionFps: this.compositionModel.fps ?? visualConfig.fps ?? 30,\n };\n await this.composeWorker.send('configure', {\n initial: true,\n clipId: clip.id,\n config: { ...visualConfig, ...renderOverrides, timeline },\n });\n\n // VideoEncodeWorker (always needed for export)\n this.videoEncodeWorker = await this.workerPool.getOrCreate('videoEncode', this.sessionId, {\n lazy: true,\n });\n const encodeConfig = this.workerConfigs.videoEncode ?? {};\n const encoderConfig = { ...encodeConfig };\n if (this.compositionModel.renderConfig?.width) {\n encoderConfig.width = this.compositionModel.renderConfig.width;\n }\n if (this.compositionModel.renderConfig?.height) {\n encoderConfig.height = this.compositionModel.renderConfig.height;\n }\n await this.videoEncodeWorker.send('configure', {\n initial: true,\n config: encoderConfig,\n });\n }\n\n private async connectVideoPipeline(): Promise<void> {\n if (!this.composeWorker || !this.videoEncodeWorker || !this.onDemandSession) {\n throw new Error('Pipeline workers not ready');\n }\n\n const sessionId = this.sessionId;\n const clip = this.getClip();\n if (!clip) {\n throw new Error('[VideoClipSession] Clip not found for pipeline connection');\n }\n\n if (!this.instructionContext) {\n throw new Error('[VideoClipSession] Instructions not installed before connecting pipeline');\n }\n\n // STEP 1: Connect ComposeWorker -> EncodeWorker\n this.composeToEncodeChannel = new MessageChannel();\n await this.composeWorker.send(\n 'connect',\n {\n direction: 'downstream',\n port: this.composeToEncodeChannel.port1,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port1] }\n );\n await this.videoEncodeWorker.send(\n 'connect',\n {\n direction: 'upstream',\n port: this.composeToEncodeChannel.port2,\n streamType: 'video',\n sessionId,\n },\n { transfer: [this.composeToEncodeChannel.port2] }\n );\n\n // STEP 2: Setup EncodeWorker stream receiver\n this.videoEncodeWorker.receiveStream((stream, metadata) => {\n this.callbacks.onEncodedStreamReady(\n stream as ReadableStream<{\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n }>,\n metadata?.streamType ?? 'video'\n );\n });\n\n // STEP 3: Load and transfer attachment images (before sending video stream)\n await this.loadAndTransferAttachments(sessionId, clip.id);\n\n // STEP 4: Start decoding and send frames to ComposeWorker\n const trimStartUs = clip.trimStartUs ?? 0;\n const frameStream = await this.onDemandSession.decodeRangeToStream(\n trimStartUs,\n trimStartUs + clip.durationUs\n );\n\n // Send VideoFrame stream to ComposeWorker (main thread → worker)\n // Include instructions in metadata to avoid race conditions\n await this.composeWorker.sendStream(frameStream, {\n sessionId,\n streamType: 'video',\n instructions: this.instructionContext.instructions,\n });\n }\n\n private async installInstructions(instructions: ClipInstructionSet): Promise<void> {\n this.instructionContext = {\n revision: instructions.revision,\n instructions,\n status: instructions.status,\n };\n }\n\n private async releasePipeline(): Promise<void> {\n if (this.composeWorker && this.instructionContext) {\n await this.composeWorker.notify('dispose_clip', {\n sessionId: this.sessionId,\n revision: this.instructionContext.revision,\n });\n }\n\n if (this.visualStream && this.callbacks.onStreamDisposed) {\n this.callbacks.onStreamDisposed();\n }\n this.visualStream = null;\n\n // Cleanup VideoWindowDecodeSession\n if (this.onDemandSession) {\n await this.onDemandSession.dispose();\n this.onDemandSession = null;\n }\n\n // Terminate workers\n if (this.composeWorker) {\n this.workerPool.terminate('videoCompose', this.sessionId);\n this.composeWorker = null;\n }\n\n if (this.videoEncodeWorker) {\n this.workerPool.terminate('videoEncode', this.sessionId);\n this.videoEncodeWorker = null;\n }\n\n this.composeToEncodeChannel?.port1.close();\n this.composeToEncodeChannel?.port2.close();\n this.composeToEncodeChannel = null;\n }\n\n async invalidateClipCache(): Promise<void> {\n await this.cacheManager.invalidateClip(this.clipId);\n }\n}\n"],"names":[],"mappings":"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"}