@meframe/core 0.0.29 → 0.0.30
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 +2 -30
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +7 -118
- package/dist/Meframe.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +42 -68
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +224 -188
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/l1/VideoL1Cache.d.ts +15 -2
- package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -1
- package/dist/cache/l1/VideoL1Cache.js +58 -38
- package/dist/cache/l1/VideoL1Cache.js.map +1 -1
- package/dist/cache/l2/L2Cache.d.ts.map +1 -1
- package/dist/cache/l2/L2OPFSStore.d.ts +37 -0
- package/dist/cache/l2/L2OPFSStore.d.ts.map +1 -0
- package/dist/cache/resource/AudioSampleCache.d.ts +52 -0
- package/dist/cache/resource/AudioSampleCache.d.ts.map +1 -0
- package/dist/cache/resource/AudioSampleCache.js +69 -0
- package/dist/cache/resource/AudioSampleCache.js.map +1 -0
- package/dist/cache/resource/ImageBitmapCache.d.ts +65 -0
- package/dist/cache/resource/ImageBitmapCache.d.ts.map +1 -0
- package/dist/cache/resource/ImageBitmapCache.js +101 -0
- package/dist/cache/resource/ImageBitmapCache.js.map +1 -0
- package/dist/cache/resource/MP4IndexCache.d.ts +48 -0
- package/dist/cache/resource/MP4IndexCache.d.ts.map +1 -0
- package/dist/cache/resource/MP4IndexCache.js +104 -0
- package/dist/cache/resource/MP4IndexCache.js.map +1 -0
- package/dist/cache/resource/ResourceCache.d.ts +46 -0
- package/dist/cache/resource/ResourceCache.d.ts.map +1 -0
- package/dist/cache/resource/ResourceCache.js +92 -0
- package/dist/cache/resource/ResourceCache.js.map +1 -0
- package/dist/cache/storage/indexeddb/ChunkRecordStore.d.ts +75 -0
- package/dist/cache/storage/indexeddb/ChunkRecordStore.d.ts.map +1 -0
- package/dist/cache/storage/opfs/OPFSManager.d.ts +54 -0
- package/dist/cache/storage/opfs/OPFSManager.d.ts.map +1 -0
- package/dist/cache/storage/opfs/OPFSManager.js +133 -0
- package/dist/cache/storage/opfs/OPFSManager.js.map +1 -0
- package/dist/cache/storage/opfs/types.d.ts +16 -0
- package/dist/cache/storage/opfs/types.d.ts.map +1 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +21 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/types.d.ts +28 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/controllers/ExportController.d.ts +16 -0
- package/dist/controllers/ExportController.d.ts.map +1 -0
- package/dist/controllers/ExportController.js +44 -0
- package/dist/controllers/ExportController.js.map +1 -0
- package/dist/controllers/PlaybackController.d.ts +28 -4
- package/dist/controllers/PlaybackController.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.js +115 -51
- package/dist/controllers/PlaybackController.js.map +1 -1
- package/dist/controllers/index.d.ts +2 -3
- package/dist/controllers/index.d.ts.map +1 -1
- package/dist/controllers/types.d.ts +0 -28
- package/dist/controllers/types.d.ts.map +1 -1
- package/dist/event/events.d.ts +8 -0
- package/dist/event/events.d.ts.map +1 -1
- package/dist/event/events.js +1 -0
- package/dist/event/events.js.map +1 -1
- package/dist/model/CompositionModel.d.ts.map +1 -1
- package/dist/model/CompositionModel.js +11 -6
- package/dist/model/CompositionModel.js.map +1 -1
- package/dist/model/RcFrame.d.ts +2 -0
- package/dist/model/RcFrame.d.ts.map +1 -1
- package/dist/model/RcFrame.js +3 -0
- package/dist/model/RcFrame.js.map +1 -1
- package/dist/model/types.d.ts +0 -1
- package/dist/model/types.d.ts.map +1 -1
- package/dist/model/types.js.map +1 -1
- package/dist/orchestrator/ExportScheduler.d.ts +35 -0
- package/dist/orchestrator/ExportScheduler.d.ts.map +1 -0
- package/dist/orchestrator/ExportScheduler.js +241 -0
- package/dist/orchestrator/ExportScheduler.js.map +1 -0
- package/dist/orchestrator/GlobalAudioSession.d.ts +24 -9
- package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
- package/dist/orchestrator/GlobalAudioSession.js +149 -152
- package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.d.ts +73 -0
- package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -0
- package/dist/orchestrator/OnDemandVideoSession.js +281 -0
- package/dist/orchestrator/OnDemandVideoSession.js.map +1 -0
- package/dist/orchestrator/Orchestrator.d.ts +22 -17
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +244 -312
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
- package/dist/orchestrator/VideoClipSession.js +3 -15
- package/dist/orchestrator/VideoClipSession.js.map +1 -1
- package/dist/orchestrator/index.d.ts +0 -1
- package/dist/orchestrator/index.d.ts.map +1 -1
- package/dist/orchestrator/types.d.ts +4 -4
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/dist/stages/compose/FilterProcessor.d.ts +1 -1
- package/dist/stages/compose/FilterProcessor.d.ts.map +1 -1
- package/dist/stages/compose/FilterProcessor.js +226 -0
- package/dist/stages/compose/FilterProcessor.js.map +1 -0
- package/dist/stages/compose/LayerRenderer.d.ts +1 -1
- package/dist/stages/compose/LayerRenderer.d.ts.map +1 -1
- package/dist/stages/compose/LayerRenderer.js +270 -0
- package/dist/stages/compose/LayerRenderer.js.map +1 -0
- package/dist/stages/compose/TransitionProcessor.d.ts +1 -1
- package/dist/stages/compose/TransitionProcessor.d.ts.map +1 -1
- package/dist/stages/compose/TransitionProcessor.js +189 -0
- package/dist/stages/compose/TransitionProcessor.js.map +1 -0
- package/dist/stages/compose/VideoComposer.d.ts +4 -2
- package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
- package/dist/stages/compose/VideoComposer.js +229 -0
- package/dist/stages/compose/VideoComposer.js.map +1 -0
- package/dist/stages/compose/text-renderers/animation-utils.js +76 -0
- package/dist/stages/compose/text-renderers/animation-utils.js.map +1 -0
- package/dist/stages/compose/text-renderers/basic-text-renderer.d.ts +2 -2
- package/dist/stages/compose/text-renderers/basic-text-renderer.d.ts.map +1 -1
- package/dist/stages/compose/text-renderers/basic-text-renderer.js +93 -0
- package/dist/stages/compose/text-renderers/basic-text-renderer.js.map +1 -0
- package/dist/stages/compose/text-renderers/character-ktv-renderer.d.ts +1 -1
- package/dist/stages/compose/text-renderers/character-ktv-renderer.d.ts.map +1 -1
- package/dist/stages/compose/text-renderers/character-ktv-renderer.js +132 -0
- package/dist/stages/compose/text-renderers/character-ktv-renderer.js.map +1 -0
- package/dist/stages/compose/text-renderers/word-by-word-renderer.d.ts +1 -1
- package/dist/stages/compose/text-renderers/word-by-word-renderer.d.ts.map +1 -1
- package/dist/stages/compose/text-renderers/word-by-word-renderer.js +128 -0
- package/dist/stages/compose/text-renderers/word-by-word-renderer.js.map +1 -0
- package/dist/stages/compose/text-renderers/word-fancy-renderer.d.ts +1 -1
- package/dist/stages/compose/text-renderers/word-fancy-renderer.d.ts.map +1 -1
- package/dist/stages/compose/text-renderers/word-fancy-renderer.js +135 -0
- package/dist/stages/compose/text-renderers/word-fancy-renderer.js.map +1 -0
- package/dist/stages/compose/text-utils/locale-detector.js +16 -0
- package/dist/stages/compose/text-utils/locale-detector.js.map +1 -0
- package/dist/stages/compose/text-utils/text-metrics.js +21 -0
- package/dist/stages/compose/text-utils/text-metrics.js.map +1 -0
- package/dist/stages/compose/text-utils/text-wrapper.js +225 -0
- package/dist/stages/compose/text-utils/text-wrapper.js.map +1 -0
- package/dist/stages/compose/types.d.ts +2 -1
- package/dist/stages/compose/types.d.ts.map +1 -1
- package/dist/stages/decode/BaseDecoder.js +0 -3
- package/dist/stages/decode/BaseDecoder.js.map +1 -1
- package/dist/stages/demux/MP4Demuxer.d.ts +5 -0
- package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
- package/dist/stages/demux/MP4Demuxer.js +281 -0
- package/dist/stages/demux/MP4Demuxer.js.map +1 -0
- package/dist/stages/demux/MP4IndexParser.d.ts +71 -0
- package/dist/stages/demux/MP4IndexParser.d.ts.map +1 -0
- package/dist/stages/demux/MP4IndexParser.js +416 -0
- package/dist/stages/demux/MP4IndexParser.js.map +1 -0
- package/dist/stages/demux/types.d.ts +48 -0
- package/dist/stages/demux/types.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.d.ts +50 -15
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +297 -80
- package/dist/stages/load/ResourceLoader.js.map +1 -1
- package/dist/stages/load/TaskManager.d.ts +6 -2
- package/dist/stages/load/TaskManager.d.ts.map +1 -1
- package/dist/stages/load/TaskManager.js +27 -4
- package/dist/stages/load/TaskManager.js.map +1 -1
- package/dist/stages/load/index.d.ts +0 -1
- package/dist/stages/load/index.d.ts.map +1 -1
- package/dist/stages/load/types.d.ts +9 -9
- package/dist/stages/load/types.d.ts.map +1 -1
- package/dist/stages/mux/MP4Muxer.d.ts +2 -2
- package/dist/stages/mux/MP4Muxer.d.ts.map +1 -1
- package/dist/stages/mux/MP4Muxer.js +24 -13
- package/dist/stages/mux/MP4Muxer.js.map +1 -1
- package/dist/stages/mux/MuxManager.d.ts +10 -21
- package/dist/stages/mux/MuxManager.d.ts.map +1 -1
- package/dist/stages/mux/MuxManager.js +21 -162
- package/dist/stages/mux/MuxManager.js.map +1 -1
- package/dist/stages/mux/index.d.ts +0 -1
- package/dist/stages/mux/index.d.ts.map +1 -1
- package/dist/utils/binary-search.d.ts +12 -4
- package/dist/utils/binary-search.d.ts.map +1 -1
- package/dist/utils/binary-search.js +52 -6
- package/dist/utils/binary-search.js.map +1 -1
- package/dist/workers/{BaseDecoder.BWYu1W0B.js → BaseDecoder.CTW-vr29.js} +1 -4
- package/dist/workers/BaseDecoder.CTW-vr29.js.map +1 -0
- package/dist/workers/{MP4Demuxer.lMOUMWFh.js → MP4Demuxer.BEa6PLJm.js} +9 -2
- package/dist/workers/{MP4Demuxer.lMOUMWFh.js.map → MP4Demuxer.BEa6PLJm.js.map} +1 -1
- package/dist/workers/stages/compose/{video-compose.worker.CIeEIJO7.js → video-compose.worker.DHQ8B105.js} +59 -31
- package/dist/workers/stages/compose/video-compose.worker.DHQ8B105.js.map +1 -0
- package/dist/workers/stages/decode/{audio-decode.worker.DnS17GD9.js → audio-decode.worker.CP8bXXa4.js} +2 -2
- package/dist/workers/stages/decode/{audio-decode.worker.DnS17GD9.js.map → audio-decode.worker.CP8bXXa4.js.map} +1 -1
- package/dist/workers/stages/decode/{video-decode.worker.BEYsjOXp.js → video-decode.worker.BIspTxgV.js} +2 -2
- package/dist/workers/stages/decode/{video-decode.worker.BEYsjOXp.js.map → video-decode.worker.BIspTxgV.js.map} +1 -1
- package/dist/workers/stages/demux/{audio-demux.worker.DcurGC8i.js → audio-demux.worker._VRQdLdv.js} +2 -2
- package/dist/workers/stages/demux/{audio-demux.worker.DcurGC8i.js.map → audio-demux.worker._VRQdLdv.js.map} +1 -1
- package/dist/workers/stages/demux/{video-demux.worker.B1_wntU4.js → video-demux.worker.CSkxGtmx.js} +3 -19
- package/dist/workers/stages/demux/video-demux.worker.CSkxGtmx.js.map +1 -0
- package/dist/workers/worker-manifest.json +5 -5
- package/package.json +1 -1
- package/dist/cache/l2/IndexedDBStore.js +0 -180
- package/dist/cache/l2/IndexedDBStore.js.map +0 -1
- package/dist/cache/l2/L2Cache.js +0 -329
- package/dist/cache/l2/L2Cache.js.map +0 -1
- package/dist/cache/l2/OPFSStore.js +0 -131
- package/dist/cache/l2/OPFSStore.js.map +0 -1
- package/dist/controllers/PreRenderService.d.ts +0 -59
- package/dist/controllers/PreRenderService.d.ts.map +0 -1
- package/dist/controllers/PreRenderService.js +0 -185
- package/dist/controllers/PreRenderService.js.map +0 -1
- package/dist/controllers/PreRenderTaskQueue.d.ts +0 -21
- package/dist/controllers/PreRenderTaskQueue.d.ts.map +0 -1
- package/dist/orchestrator/ClipSessionManager.d.ts +0 -70
- package/dist/orchestrator/ClipSessionManager.d.ts.map +0 -1
- package/dist/orchestrator/ClipSessionManager.js +0 -158
- package/dist/orchestrator/ClipSessionManager.js.map +0 -1
- package/dist/stages/decode/AudioChunkDecoder.js +0 -169
- package/dist/stages/decode/AudioChunkDecoder.js.map +0 -1
- package/dist/stages/load/EventHandlers.d.ts +0 -26
- package/dist/stages/load/EventHandlers.d.ts.map +0 -1
- package/dist/stages/load/EventHandlers.js +0 -42
- package/dist/stages/load/EventHandlers.js.map +0 -1
- package/dist/stages/mux/OPFSWriter.d.ts +0 -46
- package/dist/stages/mux/OPFSWriter.d.ts.map +0 -1
- package/dist/utils/BackpressureAdapter.d.ts +0 -26
- package/dist/utils/BackpressureAdapter.d.ts.map +0 -1
- package/dist/workers/BaseDecoder.BWYu1W0B.js.map +0 -1
- package/dist/workers/stages/compose/video-compose.worker.CIeEIJO7.js.map +0 -1
- package/dist/workers/stages/demux/video-demux.worker.B1_wntU4.js.map +0 -1
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import { EventBus } from "../event/EventBus.js";
|
|
2
2
|
import { WorkerPool } from "../worker/WorkerPool.js";
|
|
3
3
|
import { applyPatch } from "../model/patch.js";
|
|
4
|
-
import { ResourceLoader
|
|
4
|
+
import { ResourceLoader } from "../stages/load/ResourceLoader.js";
|
|
5
5
|
import { CacheManager } from "../cache/CacheManager.js";
|
|
6
6
|
import { ConfigLoader } from "../config/ConfigLoader.js";
|
|
7
|
-
import { hasResourceId } from "../model/types.js";
|
|
7
|
+
import { isVideoClip, hasResourceId } from "../model/types.js";
|
|
8
8
|
import { MeframeEvent } from "../event/events.js";
|
|
9
9
|
import { CompositionPlanner } from "./CompositionPlanner.js";
|
|
10
|
-
import { VideoClipSession } from "./VideoClipSession.js";
|
|
11
|
-
import { ClipSessionManager } from "./ClipSessionManager.js";
|
|
12
10
|
import { GlobalAudioSession } from "./GlobalAudioSession.js";
|
|
13
11
|
import { MuxManager } from "../stages/mux/MuxManager.js";
|
|
14
|
-
import {
|
|
12
|
+
import { OnDemandVideoSession } from "./OnDemandVideoSession.js";
|
|
13
|
+
import { ExportScheduler } from "./ExportScheduler.js";
|
|
15
14
|
class Orchestrator {
|
|
16
15
|
workers;
|
|
17
16
|
eventBus;
|
|
@@ -21,13 +20,11 @@ class Orchestrator {
|
|
|
21
20
|
planner;
|
|
22
21
|
audioSession;
|
|
23
22
|
muxManager;
|
|
24
|
-
|
|
23
|
+
exportScheduler;
|
|
25
24
|
isInitialized = false;
|
|
26
25
|
config = ConfigLoader.getInstance().getConfig();
|
|
27
|
-
clipSessionManager;
|
|
28
|
-
currentClipId = null;
|
|
29
26
|
ensureCacheDebounceTimer = null;
|
|
30
|
-
|
|
27
|
+
currentClipId = null;
|
|
31
28
|
events;
|
|
32
29
|
constructor(config) {
|
|
33
30
|
this.eventBus = config.eventBus || new EventBus();
|
|
@@ -40,15 +37,6 @@ class Orchestrator {
|
|
|
40
37
|
workerPath: config.workerPath,
|
|
41
38
|
workerExtension: config.workerExtension
|
|
42
39
|
});
|
|
43
|
-
this.resourceLoader = new ResourceLoader({
|
|
44
|
-
orchestrator: this,
|
|
45
|
-
eventBus: this.eventBus,
|
|
46
|
-
config: {
|
|
47
|
-
maxConcurrent: config.maxWorkers || this.config.load?.retry?.maxAttempts || 4
|
|
48
|
-
},
|
|
49
|
-
onStateChange: (resourceId, state) => this.handleResourceStateChange(resourceId, state)
|
|
50
|
-
});
|
|
51
|
-
this.planner = new CompositionPlanner();
|
|
52
40
|
const cacheConfig = config.cacheConfig || this.config.cache;
|
|
53
41
|
this.cacheManager = new CacheManager(
|
|
54
42
|
{
|
|
@@ -56,26 +44,27 @@ class Orchestrator {
|
|
|
56
44
|
maxMemoryMB: cacheConfig?.l1Size || cacheConfig?.l1?.maxMemoryMB || 1024,
|
|
57
45
|
maxGOPs: this.config.decode?.video?.maxGOPs || 4
|
|
58
46
|
},
|
|
59
|
-
|
|
60
|
-
maxSizeMB: cacheConfig?.l2Size || cacheConfig?.l2?.maxSizeMB || 2048,
|
|
47
|
+
resource: {
|
|
61
48
|
projectId: config.projectId || this.config.global.projectId || "default"
|
|
62
49
|
}
|
|
63
50
|
},
|
|
64
51
|
this.eventBus
|
|
65
52
|
);
|
|
66
|
-
this.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
53
|
+
this.resourceLoader = new ResourceLoader({
|
|
54
|
+
cacheManager: this.cacheManager,
|
|
55
|
+
workerPool: this.workers,
|
|
56
|
+
eventBus: this.eventBus,
|
|
57
|
+
config: {
|
|
58
|
+
maxConcurrent: config.maxWorkers || this.config.load?.retry?.maxAttempts || 4
|
|
70
59
|
},
|
|
71
|
-
|
|
60
|
+
onStateChange: (resourceId, state) => this.handleResourceStateChange(resourceId, state)
|
|
72
61
|
});
|
|
62
|
+
this.planner = new CompositionPlanner();
|
|
73
63
|
this.audioSession = new GlobalAudioSession({
|
|
74
64
|
cacheManager: this.cacheManager,
|
|
75
|
-
|
|
65
|
+
workerPool: this.workers,
|
|
76
66
|
resourceLoader: this.resourceLoader,
|
|
77
67
|
eventBus: this.eventBus,
|
|
78
|
-
getModel: () => this.compositionModel,
|
|
79
68
|
buildWorkerConfigs: () => this.buildWorkerConfigs()
|
|
80
69
|
});
|
|
81
70
|
this.muxManager = new MuxManager(
|
|
@@ -83,26 +72,48 @@ class Orchestrator {
|
|
|
83
72
|
this.audioSession,
|
|
84
73
|
this.config.encode.audio
|
|
85
74
|
);
|
|
75
|
+
this.exportScheduler = new ExportScheduler({
|
|
76
|
+
workerPool: this.workers,
|
|
77
|
+
planner: this.planner,
|
|
78
|
+
cacheManager: this.cacheManager,
|
|
79
|
+
resourceLoader: this.resourceLoader,
|
|
80
|
+
muxManager: this.muxManager,
|
|
81
|
+
audioSession: this.audioSession,
|
|
82
|
+
workerConfigsProvider: () => this.buildWorkerConfigs(),
|
|
83
|
+
eventBus: this.eventBus
|
|
84
|
+
});
|
|
85
|
+
this.setupResourceFirstFrameHandler();
|
|
86
|
+
this.setupPreloadHandlers();
|
|
86
87
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
88
|
+
setupResourceFirstFrameHandler() {
|
|
89
|
+
this.eventBus.on(MeframeEvent.ResourceFirstFrameReady, async (payload) => {
|
|
90
|
+
const { resourceId, clipId, index, chunks } = payload;
|
|
91
|
+
if (!this.compositionModel) return;
|
|
92
|
+
const clip = this.compositionModel.findClip(clipId);
|
|
93
|
+
if (!clip || !clip.trackId) return;
|
|
94
|
+
if (clip.startUs === 0) {
|
|
95
|
+
const fps = this.compositionModel.fps ?? 30;
|
|
96
|
+
await OnDemandVideoSession.decodeAndCacheFirstFrame(
|
|
97
|
+
resourceId,
|
|
98
|
+
chunks,
|
|
99
|
+
index,
|
|
100
|
+
clip,
|
|
101
|
+
this.cacheManager,
|
|
102
|
+
fps
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
setupPreloadHandlers() {
|
|
108
|
+
this.eventBus.on(MeframeEvent.PlaybackPlay, () => {
|
|
109
|
+
this.resourceLoader.setPreloadingEnabled(false);
|
|
110
|
+
});
|
|
111
|
+
this.eventBus.on(MeframeEvent.PlaybackPause, () => {
|
|
112
|
+
this.resourceLoader.setPreloadingEnabled(true);
|
|
113
|
+
});
|
|
114
|
+
this.eventBus.on(MeframeEvent.PlaybackStop, () => {
|
|
115
|
+
this.resourceLoader.setPreloadingEnabled(true);
|
|
116
|
+
});
|
|
106
117
|
}
|
|
107
118
|
async initialize() {
|
|
108
119
|
if (this.isInitialized) return;
|
|
@@ -120,40 +131,33 @@ class Orchestrator {
|
|
|
120
131
|
this.eventBus.once(event, handler);
|
|
121
132
|
}
|
|
122
133
|
async setCompositionModel(model, options) {
|
|
123
|
-
const { clearL1Cache = true
|
|
134
|
+
const { clearL1Cache = true } = options || {};
|
|
124
135
|
if (clearL1Cache) {
|
|
125
136
|
this.cacheManager.clearL1Cache();
|
|
126
137
|
}
|
|
127
|
-
if (clearL2Cache) {
|
|
128
|
-
await this.cacheManager.clearL2Cache();
|
|
129
|
-
}
|
|
130
138
|
this.compositionModel = model;
|
|
131
139
|
this.planner.setModel(model);
|
|
132
|
-
this.
|
|
140
|
+
await this.resourceLoader.setModel(model);
|
|
141
|
+
this.audioSession.setModel(model);
|
|
133
142
|
this.eventBus.emit(MeframeEvent.ModelSet, model);
|
|
134
143
|
this.eventBus.emit(MeframeEvent.CompositionUpdated, {
|
|
135
144
|
trackCount: model.tracks.length,
|
|
136
145
|
clipCount: model.tracks.reduce((acc, track) => acc + track.clips.length, 0),
|
|
137
146
|
durationUs: model.durationUs
|
|
138
147
|
});
|
|
139
|
-
await this.audioSession.activateAllAudioClips();
|
|
140
148
|
}
|
|
141
149
|
async applyPatch(patch) {
|
|
142
150
|
if (!this.compositionModel) {
|
|
143
151
|
throw new Error("No composition model set");
|
|
144
152
|
}
|
|
145
153
|
const affectedClipIds = applyPatch(this.compositionModel, patch);
|
|
146
|
-
|
|
154
|
+
this.planner.applyPatch(patch, affectedClipIds);
|
|
147
155
|
this.eventBus.emit(MeframeEvent.PatchApplied, {
|
|
148
156
|
operations: patch.operations.length,
|
|
149
157
|
affectedClips: Array.from(affectedClipIds)
|
|
150
158
|
});
|
|
151
|
-
for (const
|
|
152
|
-
|
|
153
|
-
this.activeClips.delete(update.clipId);
|
|
154
|
-
}
|
|
155
|
-
this.cacheManager.invalidateClip(update.clipId);
|
|
156
|
-
await this.clipSessionManager.handlePlannerUpdate(update.clipId, update);
|
|
159
|
+
for (const clipId of affectedClipIds) {
|
|
160
|
+
this.cacheManager.invalidateClip(clipId);
|
|
157
161
|
}
|
|
158
162
|
const reactivatedAudioClips = [];
|
|
159
163
|
const reactivatedVideoClips = [];
|
|
@@ -184,62 +188,10 @@ class Orchestrator {
|
|
|
184
188
|
if (state !== "ready") {
|
|
185
189
|
return;
|
|
186
190
|
}
|
|
187
|
-
const resource = this.compositionModel.getResource(resourceId);
|
|
188
|
-
if (!resource) {
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
if (resource.type === "video" || resource.type === "audio") {
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
const clipIds = this.compositionModel.getClipIdsByResourceId(resourceId);
|
|
195
|
-
for (const clipId of clipIds) {
|
|
196
|
-
if (!this.clipSessionManager.isClipActive(clipId)) {
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
const clip = this.compositionModel.findClip(clipId);
|
|
200
|
-
if (!clip) {
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
const instructions = this.planner.getInstructions(clipId);
|
|
204
|
-
if (!instructions) {
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
const session = this.clipSessionManager.getSession(clipId);
|
|
208
|
-
const composeWorker = session?.composeWorker;
|
|
209
|
-
if (composeWorker) {
|
|
210
|
-
composeWorker.send("install_instructions", instructions);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
async restartWorker(type, clipId) {
|
|
215
|
-
const clipLocalTypes = [
|
|
216
|
-
"videoDemux",
|
|
217
|
-
"audioDemux",
|
|
218
|
-
"videoDecode",
|
|
219
|
-
"audioDecode",
|
|
220
|
-
"videoCompose",
|
|
221
|
-
"videoEncode"
|
|
222
|
-
];
|
|
223
|
-
if (clipLocalTypes.includes(type) && !clipId) {
|
|
224
|
-
throw new Error(`clipId required for restarting ${type} worker`);
|
|
225
|
-
}
|
|
226
|
-
this.workers.terminate(type, clipId);
|
|
227
|
-
const worker = await this.workers.getOrCreate(type, clipId);
|
|
228
|
-
this.eventBus.emit(MeframeEvent.WorkerRestarted, {
|
|
229
|
-
type,
|
|
230
|
-
workerId: worker.getWorkerId(),
|
|
231
|
-
reason: "Manual restart"
|
|
232
|
-
});
|
|
233
|
-
if (clipId) {
|
|
234
|
-
const session = this.clipSessionManager.getSession(clipId);
|
|
235
|
-
if (session) {
|
|
236
|
-
await session.activate();
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
191
|
}
|
|
240
|
-
async
|
|
192
|
+
async getFrame(timeUs, options) {
|
|
241
193
|
const signal = options?.signal;
|
|
242
|
-
const
|
|
194
|
+
const preheat = options?.preheat ?? false;
|
|
243
195
|
if (!this.compositionModel) {
|
|
244
196
|
throw new Error("No composition model set");
|
|
245
197
|
}
|
|
@@ -247,112 +199,89 @@ class Orchestrator {
|
|
|
247
199
|
if (!clip) {
|
|
248
200
|
return null;
|
|
249
201
|
}
|
|
250
|
-
if (this.currentClipId !== clip.id) {
|
|
251
|
-
this.currentClipId = clip.id;
|
|
252
|
-
void this.ensureClipCache(timeUs, immediate);
|
|
253
|
-
}
|
|
254
202
|
let relativeTimeUs = options?.relativeTimeUs ?? timeUs - clip.startUs;
|
|
255
203
|
relativeTimeUs = Math.min(relativeTimeUs, clip.durationUs - 1);
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
this.
|
|
204
|
+
if (!preheat) {
|
|
205
|
+
this.cacheManager.setWindow(timeUs);
|
|
206
|
+
const cachedFrame = this.cacheManager.getFrame(relativeTimeUs, clip.id);
|
|
207
|
+
if (cachedFrame) {
|
|
208
|
+
this.preheatNextClip(clip);
|
|
209
|
+
this.eventBus.emit(MeframeEvent.CacheHit, {
|
|
210
|
+
timeUs,
|
|
211
|
+
level: "L1",
|
|
212
|
+
key: `${clip.id}-${relativeTimeUs}`
|
|
213
|
+
});
|
|
214
|
+
return cachedFrame;
|
|
215
|
+
}
|
|
216
|
+
this.eventBus.emit(MeframeEvent.CacheMiss, {
|
|
259
217
|
timeUs,
|
|
260
218
|
level: "L1",
|
|
261
219
|
key: `${clip.id}-${relativeTimeUs}`
|
|
262
220
|
});
|
|
263
|
-
return cachedFrame;
|
|
264
221
|
}
|
|
265
|
-
this.eventBus.emit(MeframeEvent.CacheMiss, {
|
|
266
|
-
timeUs,
|
|
267
|
-
level: "L1",
|
|
268
|
-
key: `${clip.id}-${relativeTimeUs}`
|
|
269
|
-
});
|
|
270
222
|
if (signal?.aborted) {
|
|
271
223
|
throw new DOMException("Render aborted", "AbortError");
|
|
272
224
|
}
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
void this.ensureAudioFromL2(clip.id);
|
|
276
|
-
return l2Frame;
|
|
277
|
-
}
|
|
278
|
-
return null;
|
|
225
|
+
const resourceFrame = await this.decodeFromResource(clip, relativeTimeUs, timeUs, options);
|
|
226
|
+
return resourceFrame;
|
|
279
227
|
}
|
|
280
|
-
async
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
// TODO: move to @ClipSessionManager
|
|
293
|
-
async decodeFromL2(timeUs, clip) {
|
|
294
|
-
const { id, trackId, startUs } = clip;
|
|
295
|
-
const [chunkStream, metadata] = await Promise.all([
|
|
296
|
-
this.cacheManager.l2Cache.createReadStream(id, "video"),
|
|
297
|
-
this.cacheManager.l2Cache.getClipMetadata(id, "video")
|
|
298
|
-
]);
|
|
299
|
-
if (!chunkStream || !metadata?.codec) {
|
|
300
|
-
return null;
|
|
301
|
-
}
|
|
302
|
-
const decoder = new VideoChunkDecoder(`l2-temp-${id}`, {
|
|
303
|
-
codec: metadata.codec,
|
|
304
|
-
width: metadata.codedWidth,
|
|
305
|
-
height: metadata.codedHeight,
|
|
306
|
-
description: metadata.description,
|
|
307
|
-
hardwareAcceleration: metadata.hardwareAcceleration || "no-preference",
|
|
308
|
-
thread: "main"
|
|
309
|
-
});
|
|
310
|
-
try {
|
|
311
|
-
const decodeStream = chunkStream.pipeThrough(decoder.createStream());
|
|
312
|
-
let targetFrame = null;
|
|
313
|
-
await this.cacheManager.receiveComposedFrames(decodeStream, {
|
|
314
|
-
clipId: id,
|
|
315
|
-
trackId: trackId || this.compositionModel?.mainTrackId || "main",
|
|
316
|
-
fps: this.compositionModel?.fps ?? 30,
|
|
317
|
-
clipStartUs: startUs,
|
|
318
|
-
onFrame: (info) => {
|
|
319
|
-
if (info.timeUs === timeUs) {
|
|
320
|
-
targetFrame = this.cacheManager.getFrame(timeUs, id);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
228
|
+
async preheatNextClip(clip) {
|
|
229
|
+
if (clip.id === this.currentClipId) return;
|
|
230
|
+
this.currentClipId = clip.id;
|
|
231
|
+
const nextClip = this.compositionModel?.getClipsAtTime(
|
|
232
|
+
clip.startUs + clip.durationUs,
|
|
233
|
+
this.compositionModel?.mainTrackId
|
|
234
|
+
)[0];
|
|
235
|
+
if (nextClip && isVideoClip(nextClip)) {
|
|
236
|
+
this.resourceLoader.fetch(nextClip.resourceId, {
|
|
237
|
+
priority: "normal",
|
|
238
|
+
clipId: nextClip.id,
|
|
239
|
+
trackId: nextClip.trackId
|
|
323
240
|
});
|
|
324
|
-
return targetFrame;
|
|
325
|
-
} finally {
|
|
326
|
-
await decoder.close();
|
|
327
241
|
}
|
|
328
242
|
}
|
|
329
243
|
/**
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
* @param timeUs - Target time for cache window
|
|
333
|
-
* @param immediate - Skip debounce if true (used for initial load)
|
|
244
|
+
* Compose frame from OPFS resource (on-demand decoding)
|
|
245
|
+
* This is the new path for long clips with window caching
|
|
334
246
|
*/
|
|
335
|
-
async
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
247
|
+
async decodeFromResource(clip, relativeTimeUs, globalTimeUs, options) {
|
|
248
|
+
if (!hasResourceId(clip)) return null;
|
|
249
|
+
const resourceId = clip.resourceId;
|
|
250
|
+
const resource = this.compositionModel?.getResource(resourceId);
|
|
251
|
+
const isReady = resource?.state === "ready";
|
|
252
|
+
const fetchOptions = {
|
|
253
|
+
priority: "high",
|
|
254
|
+
clipId: clip.id,
|
|
255
|
+
trackId: clip.trackId
|
|
341
256
|
};
|
|
342
|
-
if (
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
if (immediate) {
|
|
347
|
-
return executeCache();
|
|
257
|
+
if (options?.immediate && !isReady) {
|
|
258
|
+
this.resourceLoader.fetch(resourceId, fetchOptions);
|
|
259
|
+
return null;
|
|
348
260
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
261
|
+
await this.resourceLoader.fetch(resourceId, fetchOptions);
|
|
262
|
+
const session = await OnDemandVideoSession.create({
|
|
263
|
+
clipId: clip.id,
|
|
264
|
+
resourceId,
|
|
265
|
+
targetTimeUs: relativeTimeUs,
|
|
266
|
+
globalTimeUs,
|
|
267
|
+
resourceCache: this.cacheManager.resourceCache,
|
|
268
|
+
mp4IndexCache: this.cacheManager.mp4IndexCache,
|
|
269
|
+
cacheManager: this.cacheManager,
|
|
270
|
+
compositionModel: this.compositionModel,
|
|
271
|
+
fps: this.compositionModel?.fps ?? 30
|
|
355
272
|
});
|
|
273
|
+
try {
|
|
274
|
+
const DECODE_WINDOW_SIZE = 3e6;
|
|
275
|
+
const windowStart = relativeTimeUs;
|
|
276
|
+
const windowEnd = Math.min(clip.durationUs, relativeTimeUs + DECODE_WINDOW_SIZE);
|
|
277
|
+
await session.decodeWindow(windowStart, windowEnd);
|
|
278
|
+
return this.cacheManager.getFrame(relativeTimeUs, clip.id);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.error("[Orchestrator] Error composing from resource:", error);
|
|
281
|
+
return null;
|
|
282
|
+
} finally {
|
|
283
|
+
await session.dispose();
|
|
284
|
+
}
|
|
356
285
|
}
|
|
357
286
|
/**
|
|
358
287
|
* Wait for clip cache to be ready for playback
|
|
@@ -373,115 +302,7 @@ class Orchestrator {
|
|
|
373
302
|
return this.cacheManager.waitForClipReady(currentClip.id, {
|
|
374
303
|
minFrameCount: options?.minFrameCount ?? 5,
|
|
375
304
|
timeoutMs: options?.timeoutMs ?? 5e3
|
|
376
|
-
// Don't pass startTimeUs - count all frames in the clip
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
// TODO: move to @ClipSessionManager
|
|
380
|
-
async renderClipForL2(clipId) {
|
|
381
|
-
const sessionId = `${clipId}#l2`;
|
|
382
|
-
let session = null;
|
|
383
|
-
return new Promise((resolve, reject) => {
|
|
384
|
-
this.createSession(sessionId, {
|
|
385
|
-
forL2Only: true,
|
|
386
|
-
clipId,
|
|
387
|
-
onL2Complete: () => {
|
|
388
|
-
resolve(true);
|
|
389
|
-
},
|
|
390
|
-
onL2Error: (error) => {
|
|
391
|
-
console.error("[Orchestrator] L2 rendering failed for", clipId, error);
|
|
392
|
-
reject(error);
|
|
393
|
-
}
|
|
394
|
-
}).then((s) => {
|
|
395
|
-
session = s;
|
|
396
|
-
return session.activate();
|
|
397
|
-
}).catch(async (error) => {
|
|
398
|
-
if (session) {
|
|
399
|
-
await session.dispose();
|
|
400
|
-
}
|
|
401
|
-
if (error instanceof ResourceConflictError) {
|
|
402
|
-
resolve(false);
|
|
403
|
-
} else {
|
|
404
|
-
reject(error);
|
|
405
|
-
}
|
|
406
|
-
});
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
// TODO: move to @ClipSessionManager
|
|
410
|
-
async createSession(sessionId, options) {
|
|
411
|
-
const clipId = options?.clipId ?? sessionId;
|
|
412
|
-
const clip = this.compositionModel?.findClip(clipId);
|
|
413
|
-
if (!clip) {
|
|
414
|
-
throw new Error(`Clip ${clipId} not found`);
|
|
415
|
-
}
|
|
416
|
-
const session = await VideoClipSession.create({
|
|
417
|
-
clipId,
|
|
418
|
-
sessionId,
|
|
419
|
-
forL2Only: options?.forL2Only ?? false,
|
|
420
|
-
planner: this.planner,
|
|
421
|
-
workerPool: this.workers,
|
|
422
|
-
cacheManager: this.cacheManager,
|
|
423
|
-
compositionModel: this.compositionModel,
|
|
424
|
-
workerConfigs: this.buildWorkerConfigs(),
|
|
425
|
-
resourceLoader: this.resourceLoader,
|
|
426
|
-
callbacks: {
|
|
427
|
-
onComposedStreamReady: async (stream, fps) => {
|
|
428
|
-
await this.cacheManager.receiveComposedFrames(stream, {
|
|
429
|
-
clipId,
|
|
430
|
-
trackId: this.compositionModel.mainTrackId,
|
|
431
|
-
fps,
|
|
432
|
-
clipStartUs: clip.startUs,
|
|
433
|
-
onFrame: () => {
|
|
434
|
-
}
|
|
435
|
-
});
|
|
436
|
-
},
|
|
437
|
-
onEncodedStreamReady: async (stream, track) => {
|
|
438
|
-
await this.cacheManager.receiveEncodedChunks(stream, clipId, track, {
|
|
439
|
-
onComplete: () => {
|
|
440
|
-
session.dispose();
|
|
441
|
-
if (track === "video" && options?.onL2Complete) {
|
|
442
|
-
options.onL2Complete();
|
|
443
|
-
}
|
|
444
|
-
},
|
|
445
|
-
onError: (error) => {
|
|
446
|
-
console.error(`[Orchestrator] L2 encode stream error for ${clipId} ${track}:`, error);
|
|
447
|
-
session.dispose();
|
|
448
|
-
if (options?.onL2Error) {
|
|
449
|
-
options.onL2Error(error);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
},
|
|
454
|
-
onAudioStreamReady: (stream, metadata) => {
|
|
455
|
-
if (options?.forL2Only) {
|
|
456
|
-
stream.cancel();
|
|
457
|
-
} else {
|
|
458
|
-
this.audioSession.handleAudioStream(stream, metadata);
|
|
459
|
-
}
|
|
460
|
-
},
|
|
461
|
-
onPipelineReady: async (attachmentResourceIds) => {
|
|
462
|
-
const clip2 = this.compositionModel?.findClip(clipId);
|
|
463
|
-
if (clip2 && hasResourceId(clip2)) {
|
|
464
|
-
await this.resourceLoader.fetch(clip2.resourceId, {
|
|
465
|
-
priority: options?.forL2Only ? "low" : "high",
|
|
466
|
-
sessionId,
|
|
467
|
-
trackId: clip2.trackId,
|
|
468
|
-
isMainTrack: true
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
if (attachmentResourceIds && attachmentResourceIds.length > 0) {
|
|
472
|
-
for (const resourceId of attachmentResourceIds) {
|
|
473
|
-
await this.resourceLoader.fetch(resourceId, {
|
|
474
|
-
priority: "normal",
|
|
475
|
-
sessionId,
|
|
476
|
-
isMainTrack: false
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
305
|
});
|
|
483
|
-
this.activeClips.add(sessionId);
|
|
484
|
-
return session;
|
|
485
306
|
}
|
|
486
307
|
async dispose() {
|
|
487
308
|
if (this.ensureCacheDebounceTimer !== null) {
|
|
@@ -489,10 +310,7 @@ class Orchestrator {
|
|
|
489
310
|
this.ensureCacheDebounceTimer = null;
|
|
490
311
|
}
|
|
491
312
|
this.resourceLoader.dispose();
|
|
492
|
-
await this.clipSessionManager.dispose();
|
|
493
313
|
await this.cacheManager.clear();
|
|
494
|
-
this.currentClipId = null;
|
|
495
|
-
this.activeClips.clear();
|
|
496
314
|
this.workers.terminateAll();
|
|
497
315
|
this.compositionModel = null;
|
|
498
316
|
this.eventBus.dispose();
|
|
@@ -539,7 +357,121 @@ class Orchestrator {
|
|
|
539
357
|
};
|
|
540
358
|
}
|
|
541
359
|
async export(model, options) {
|
|
542
|
-
return this.
|
|
360
|
+
return this.exportScheduler.execute(model, options);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Get render state for real-time composition
|
|
364
|
+
* Returns layers ready for VideoComposer
|
|
365
|
+
*/
|
|
366
|
+
async getRenderState(timeUs, options) {
|
|
367
|
+
if (!this.compositionModel) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
const frame = await this.getFrame(timeUs, options);
|
|
371
|
+
if (options?.immediate && !frame) {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];
|
|
375
|
+
if (!clip) {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
const relativeTimeUs = timeUs - clip.startUs;
|
|
379
|
+
const instructions = this.planner.getInstructions(clip.id);
|
|
380
|
+
if (!instructions) {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
const layers = [];
|
|
384
|
+
const activeLayers = instructions.layers.filter((layer) => {
|
|
385
|
+
if (!layer.payload.attachmentId) {
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
if (layer.status !== "ready") {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
return layer.activeRanges.some(
|
|
392
|
+
(range) => relativeTimeUs >= range.startUs && relativeTimeUs < range.endUs
|
|
393
|
+
);
|
|
394
|
+
});
|
|
395
|
+
for (const layerPlan of activeLayers) {
|
|
396
|
+
const layer = this.materializeLayer(layerPlan, clip, relativeTimeUs, timeUs);
|
|
397
|
+
if (layer) {
|
|
398
|
+
layers.push(layer);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return { layers };
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Materialize a serialized layer plan into concrete Layer
|
|
405
|
+
*/
|
|
406
|
+
materializeLayer(layerPlan, clip, clipRelativeTimeUs, globalTimeUs) {
|
|
407
|
+
const baseLayer = {
|
|
408
|
+
id: layerPlan.layerId,
|
|
409
|
+
type: layerPlan.type,
|
|
410
|
+
zIndex: layerPlan.zIndex ?? 0,
|
|
411
|
+
visible: true,
|
|
412
|
+
opacity: layerPlan.opacity ?? 1
|
|
413
|
+
};
|
|
414
|
+
if (layerPlan.type === "video" && !layerPlan.payload.attachmentId) {
|
|
415
|
+
const rcFrame = this.cacheManager.getFrame(clipRelativeTimeUs, clip.id);
|
|
416
|
+
if (!rcFrame) {
|
|
417
|
+
console.warn("[Orchestrator] Video frame not found in L1:", clip.id, clipRelativeTimeUs);
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
...baseLayer,
|
|
422
|
+
type: "video",
|
|
423
|
+
rcFrame
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
if (layerPlan.type === "text") {
|
|
427
|
+
const payload = layerPlan.payload;
|
|
428
|
+
return {
|
|
429
|
+
...baseLayer,
|
|
430
|
+
type: "text",
|
|
431
|
+
text: payload.text,
|
|
432
|
+
localeCode: payload.localeCode,
|
|
433
|
+
fontConfig: payload.fontConfig,
|
|
434
|
+
animation: payload.animation,
|
|
435
|
+
wordTimings: payload.wordTimings,
|
|
436
|
+
letterCase: payload.letterCase
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
if (layerPlan.type === "image") {
|
|
440
|
+
const payload = layerPlan.payload;
|
|
441
|
+
const resourceId = payload.resourceId;
|
|
442
|
+
const source = this.cacheManager.imageBitmapCache.get(resourceId);
|
|
443
|
+
const imageLayer = {
|
|
444
|
+
...baseLayer,
|
|
445
|
+
type: "image",
|
|
446
|
+
source,
|
|
447
|
+
attachmentId: payload.attachmentId
|
|
448
|
+
};
|
|
449
|
+
if (payload.targetWidth !== void 0) {
|
|
450
|
+
imageLayer.targetWidth = payload.targetWidth;
|
|
451
|
+
}
|
|
452
|
+
if (payload.targetHeight !== void 0) {
|
|
453
|
+
imageLayer.targetHeight = payload.targetHeight;
|
|
454
|
+
}
|
|
455
|
+
if (payload.animation) {
|
|
456
|
+
const { position, keyframes, overlayClipStartUs } = payload.animation;
|
|
457
|
+
const relativeTimeUs = globalTimeUs - overlayClipStartUs;
|
|
458
|
+
if (relativeTimeUs < 0 || relativeTimeUs > keyframes[keyframes.length - 1].time) {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
const rotationRad = 0;
|
|
462
|
+
imageLayer.transform = {
|
|
463
|
+
x: position.x,
|
|
464
|
+
y: position.y,
|
|
465
|
+
scaleX: 1,
|
|
466
|
+
scaleY: 1,
|
|
467
|
+
rotation: rotationRad,
|
|
468
|
+
anchorX: 0.5,
|
|
469
|
+
anchorY: 0.5
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
return imageLayer;
|
|
473
|
+
}
|
|
474
|
+
return baseLayer;
|
|
543
475
|
}
|
|
544
476
|
}
|
|
545
477
|
export {
|