@meframe/core 0.0.29 → 0.0.30-beta
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 -13
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +6 -100
- package/dist/Meframe.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +35 -19
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +223 -134
- 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/L2Cache.js +5 -5
- package/dist/cache/l2/L2Cache.js.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/l2/L2OPFSStore.js +89 -0
- package/dist/cache/l2/L2OPFSStore.js.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/{l2/IndexedDBStore.js → storage/indexeddb/ChunkRecordStore.js} +3 -3
- package/dist/cache/storage/indexeddb/ChunkRecordStore.js.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 +116 -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/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 +21 -7
- package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
- package/dist/orchestrator/GlobalAudioSession.js +132 -140
- 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 +231 -297
- 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 +44 -2
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +281 -37
- 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/types.d.ts +7 -0
- 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.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/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();
|
|
@@ -63,13 +60,6 @@ class Orchestrator {
|
|
|
63
60
|
},
|
|
64
61
|
this.eventBus
|
|
65
62
|
);
|
|
66
|
-
this.clipSessionManager = new ClipSessionManager({
|
|
67
|
-
maxConcurrent: 2,
|
|
68
|
-
factory: {
|
|
69
|
-
createSession: (clipId) => this.createSession(clipId)
|
|
70
|
-
},
|
|
71
|
-
cacheManager: this.cacheManager
|
|
72
|
-
});
|
|
73
63
|
this.audioSession = new GlobalAudioSession({
|
|
74
64
|
cacheManager: this.cacheManager,
|
|
75
65
|
workers: this.workers,
|
|
@@ -83,26 +73,48 @@ class Orchestrator {
|
|
|
83
73
|
this.audioSession,
|
|
84
74
|
this.config.encode.audio
|
|
85
75
|
);
|
|
76
|
+
this.exportScheduler = new ExportScheduler({
|
|
77
|
+
workerPool: this.workers,
|
|
78
|
+
planner: this.planner,
|
|
79
|
+
cacheManager: this.cacheManager,
|
|
80
|
+
resourceLoader: this.resourceLoader,
|
|
81
|
+
muxManager: this.muxManager,
|
|
82
|
+
audioSession: this.audioSession,
|
|
83
|
+
workerConfigsProvider: () => this.buildWorkerConfigs(),
|
|
84
|
+
eventBus: this.eventBus
|
|
85
|
+
});
|
|
86
|
+
this.setupResourceFirstFrameHandler();
|
|
87
|
+
this.setupPreloadHandlers();
|
|
86
88
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
89
|
+
setupResourceFirstFrameHandler() {
|
|
90
|
+
this.eventBus.on(MeframeEvent.ResourceFirstFrameReady, async (payload) => {
|
|
91
|
+
const { resourceId, clipId, index, chunks } = payload;
|
|
92
|
+
if (!this.compositionModel) return;
|
|
93
|
+
const clip = this.compositionModel.findClip(clipId);
|
|
94
|
+
if (!clip || !clip.trackId) return;
|
|
95
|
+
if (clip.startUs === 0) {
|
|
96
|
+
const fps = this.compositionModel.fps ?? 30;
|
|
97
|
+
await OnDemandVideoSession.decodeAndCacheFirstFrame(
|
|
98
|
+
resourceId,
|
|
99
|
+
chunks,
|
|
100
|
+
index,
|
|
101
|
+
clip,
|
|
102
|
+
this.cacheManager,
|
|
103
|
+
fps
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
setupPreloadHandlers() {
|
|
109
|
+
this.eventBus.on(MeframeEvent.PlaybackPlay, () => {
|
|
110
|
+
this.resourceLoader.setPreloadingEnabled(false);
|
|
111
|
+
});
|
|
112
|
+
this.eventBus.on(MeframeEvent.PlaybackPause, () => {
|
|
113
|
+
this.resourceLoader.setPreloadingEnabled(true);
|
|
114
|
+
});
|
|
115
|
+
this.eventBus.on(MeframeEvent.PlaybackStop, () => {
|
|
116
|
+
this.resourceLoader.setPreloadingEnabled(true);
|
|
117
|
+
});
|
|
106
118
|
}
|
|
107
119
|
async initialize() {
|
|
108
120
|
if (this.isInitialized) return;
|
|
@@ -129,31 +141,25 @@ class Orchestrator {
|
|
|
129
141
|
}
|
|
130
142
|
this.compositionModel = model;
|
|
131
143
|
this.planner.setModel(model);
|
|
132
|
-
this.currentClipId = null;
|
|
133
144
|
this.eventBus.emit(MeframeEvent.ModelSet, model);
|
|
134
145
|
this.eventBus.emit(MeframeEvent.CompositionUpdated, {
|
|
135
146
|
trackCount: model.tracks.length,
|
|
136
147
|
clipCount: model.tracks.reduce((acc, track) => acc + track.clips.length, 0),
|
|
137
148
|
durationUs: model.durationUs
|
|
138
149
|
});
|
|
139
|
-
await this.audioSession.activateAllAudioClips();
|
|
140
150
|
}
|
|
141
151
|
async applyPatch(patch) {
|
|
142
152
|
if (!this.compositionModel) {
|
|
143
153
|
throw new Error("No composition model set");
|
|
144
154
|
}
|
|
145
155
|
const affectedClipIds = applyPatch(this.compositionModel, patch);
|
|
146
|
-
|
|
156
|
+
this.planner.applyPatch(patch, affectedClipIds);
|
|
147
157
|
this.eventBus.emit(MeframeEvent.PatchApplied, {
|
|
148
158
|
operations: patch.operations.length,
|
|
149
159
|
affectedClips: Array.from(affectedClipIds)
|
|
150
160
|
});
|
|
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);
|
|
161
|
+
for (const clipId of affectedClipIds) {
|
|
162
|
+
this.cacheManager.invalidateClip(clipId);
|
|
157
163
|
}
|
|
158
164
|
const reactivatedAudioClips = [];
|
|
159
165
|
const reactivatedVideoClips = [];
|
|
@@ -184,62 +190,10 @@ class Orchestrator {
|
|
|
184
190
|
if (state !== "ready") {
|
|
185
191
|
return;
|
|
186
192
|
}
|
|
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
193
|
}
|
|
240
|
-
async
|
|
194
|
+
async getFrame(timeUs, options) {
|
|
241
195
|
const signal = options?.signal;
|
|
242
|
-
const
|
|
196
|
+
const preheat = options?.preheat ?? false;
|
|
243
197
|
if (!this.compositionModel) {
|
|
244
198
|
throw new Error("No composition model set");
|
|
245
199
|
}
|
|
@@ -247,112 +201,89 @@ class Orchestrator {
|
|
|
247
201
|
if (!clip) {
|
|
248
202
|
return null;
|
|
249
203
|
}
|
|
250
|
-
if (this.currentClipId !== clip.id) {
|
|
251
|
-
this.currentClipId = clip.id;
|
|
252
|
-
void this.ensureClipCache(timeUs, immediate);
|
|
253
|
-
}
|
|
254
204
|
let relativeTimeUs = options?.relativeTimeUs ?? timeUs - clip.startUs;
|
|
255
205
|
relativeTimeUs = Math.min(relativeTimeUs, clip.durationUs - 1);
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
this.
|
|
206
|
+
if (!preheat) {
|
|
207
|
+
this.cacheManager.setWindow(timeUs);
|
|
208
|
+
const cachedFrame = this.cacheManager.getFrame(relativeTimeUs, clip.id);
|
|
209
|
+
if (cachedFrame) {
|
|
210
|
+
this.preheatNextClip(clip);
|
|
211
|
+
this.eventBus.emit(MeframeEvent.CacheHit, {
|
|
212
|
+
timeUs,
|
|
213
|
+
level: "L1",
|
|
214
|
+
key: `${clip.id}-${relativeTimeUs}`
|
|
215
|
+
});
|
|
216
|
+
return cachedFrame;
|
|
217
|
+
}
|
|
218
|
+
this.eventBus.emit(MeframeEvent.CacheMiss, {
|
|
259
219
|
timeUs,
|
|
260
220
|
level: "L1",
|
|
261
221
|
key: `${clip.id}-${relativeTimeUs}`
|
|
262
222
|
});
|
|
263
|
-
return cachedFrame;
|
|
264
223
|
}
|
|
265
|
-
this.eventBus.emit(MeframeEvent.CacheMiss, {
|
|
266
|
-
timeUs,
|
|
267
|
-
level: "L1",
|
|
268
|
-
key: `${clip.id}-${relativeTimeUs}`
|
|
269
|
-
});
|
|
270
224
|
if (signal?.aborted) {
|
|
271
225
|
throw new DOMException("Render aborted", "AbortError");
|
|
272
226
|
}
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
void this.ensureAudioFromL2(clip.id);
|
|
276
|
-
return l2Frame;
|
|
277
|
-
}
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
async ensureAudioFromL2(clipId) {
|
|
281
|
-
try {
|
|
282
|
-
if (this.cacheManager.hasClipPCM(clipId)) {
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
const hasAudio = await this.cacheManager.hasClipInL2(clipId, "audio");
|
|
286
|
-
if (!hasAudio) return;
|
|
287
|
-
await this.audioSession.ensureClipAudioFromL2(clipId);
|
|
288
|
-
} catch (error) {
|
|
289
|
-
console.warn("[Orchestrator] ensureAudioFromL2IfNeeded error:", error);
|
|
290
|
-
}
|
|
227
|
+
const resourceFrame = await this.decodeFromResource(clip, relativeTimeUs, timeUs, options);
|
|
228
|
+
return resourceFrame;
|
|
291
229
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
this.
|
|
298
|
-
]
|
|
299
|
-
if (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
}
|
|
230
|
+
async preheatNextClip(clip) {
|
|
231
|
+
if (clip.id === this.currentClipId) return;
|
|
232
|
+
this.currentClipId = clip.id;
|
|
233
|
+
const nextClip = this.compositionModel?.getClipsAtTime(
|
|
234
|
+
clip.startUs + clip.durationUs,
|
|
235
|
+
this.compositionModel?.mainTrackId
|
|
236
|
+
)[0];
|
|
237
|
+
if (nextClip && isVideoClip(nextClip)) {
|
|
238
|
+
this.resourceLoader.fetch(nextClip.resourceId, {
|
|
239
|
+
priority: "normal",
|
|
240
|
+
clipId: nextClip.id,
|
|
241
|
+
trackId: nextClip.trackId
|
|
323
242
|
});
|
|
324
|
-
return targetFrame;
|
|
325
|
-
} finally {
|
|
326
|
-
await decoder.close();
|
|
327
243
|
}
|
|
328
244
|
}
|
|
329
245
|
/**
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
* @param timeUs - Target time for cache window
|
|
333
|
-
* @param immediate - Skip debounce if true (used for initial load)
|
|
246
|
+
* Compose frame from OPFS resource (on-demand decoding)
|
|
247
|
+
* This is the new path for long clips with window caching
|
|
334
248
|
*/
|
|
335
|
-
async
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
249
|
+
async decodeFromResource(clip, relativeTimeUs, globalTimeUs, options) {
|
|
250
|
+
if (!hasResourceId(clip)) return null;
|
|
251
|
+
const resourceId = clip.resourceId;
|
|
252
|
+
const resource = this.compositionModel?.getResource(resourceId);
|
|
253
|
+
const isReady = resource?.state === "ready";
|
|
254
|
+
const fetchOptions = {
|
|
255
|
+
priority: "high",
|
|
256
|
+
clipId: clip.id,
|
|
257
|
+
trackId: clip.trackId
|
|
341
258
|
};
|
|
342
|
-
if (
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
if (immediate) {
|
|
347
|
-
return executeCache();
|
|
259
|
+
if (options?.immediate && !isReady) {
|
|
260
|
+
this.resourceLoader.fetch(resourceId, fetchOptions);
|
|
261
|
+
return null;
|
|
348
262
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
263
|
+
await this.resourceLoader.fetch(resourceId, fetchOptions);
|
|
264
|
+
const session = await OnDemandVideoSession.create({
|
|
265
|
+
clipId: clip.id,
|
|
266
|
+
resourceId,
|
|
267
|
+
targetTimeUs: relativeTimeUs,
|
|
268
|
+
globalTimeUs,
|
|
269
|
+
resourceCache: this.cacheManager.resourceCache,
|
|
270
|
+
mp4IndexCache: this.cacheManager.mp4IndexCache,
|
|
271
|
+
cacheManager: this.cacheManager,
|
|
272
|
+
compositionModel: this.compositionModel,
|
|
273
|
+
fps: this.compositionModel?.fps ?? 30
|
|
355
274
|
});
|
|
275
|
+
try {
|
|
276
|
+
const DECODE_WINDOW_SIZE = 3e6;
|
|
277
|
+
const windowStart = relativeTimeUs;
|
|
278
|
+
const windowEnd = Math.min(clip.durationUs, relativeTimeUs + DECODE_WINDOW_SIZE);
|
|
279
|
+
await session.decodeWindow(windowStart, windowEnd);
|
|
280
|
+
return this.cacheManager.getFrame(relativeTimeUs, clip.id);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.error("[Orchestrator] Error composing from resource:", error);
|
|
283
|
+
return null;
|
|
284
|
+
} finally {
|
|
285
|
+
await session.dispose();
|
|
286
|
+
}
|
|
356
287
|
}
|
|
357
288
|
/**
|
|
358
289
|
* Wait for clip cache to be ready for playback
|
|
@@ -373,115 +304,7 @@ class Orchestrator {
|
|
|
373
304
|
return this.cacheManager.waitForClipReady(currentClip.id, {
|
|
374
305
|
minFrameCount: options?.minFrameCount ?? 5,
|
|
375
306
|
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
307
|
});
|
|
483
|
-
this.activeClips.add(sessionId);
|
|
484
|
-
return session;
|
|
485
308
|
}
|
|
486
309
|
async dispose() {
|
|
487
310
|
if (this.ensureCacheDebounceTimer !== null) {
|
|
@@ -489,10 +312,7 @@ class Orchestrator {
|
|
|
489
312
|
this.ensureCacheDebounceTimer = null;
|
|
490
313
|
}
|
|
491
314
|
this.resourceLoader.dispose();
|
|
492
|
-
await this.clipSessionManager.dispose();
|
|
493
315
|
await this.cacheManager.clear();
|
|
494
|
-
this.currentClipId = null;
|
|
495
|
-
this.activeClips.clear();
|
|
496
316
|
this.workers.terminateAll();
|
|
497
317
|
this.compositionModel = null;
|
|
498
318
|
this.eventBus.dispose();
|
|
@@ -539,7 +359,121 @@ class Orchestrator {
|
|
|
539
359
|
};
|
|
540
360
|
}
|
|
541
361
|
async export(model, options) {
|
|
542
|
-
return this.
|
|
362
|
+
return this.exportScheduler.execute(model, options);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Get render state for real-time composition
|
|
366
|
+
* Returns layers ready for VideoComposer
|
|
367
|
+
*/
|
|
368
|
+
async getRenderState(timeUs, options) {
|
|
369
|
+
if (!this.compositionModel) {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
const frame = await this.getFrame(timeUs, options);
|
|
373
|
+
if (options?.immediate && !frame) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];
|
|
377
|
+
if (!clip) {
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
const relativeTimeUs = timeUs - clip.startUs;
|
|
381
|
+
const instructions = this.planner.getInstructions(clip.id);
|
|
382
|
+
if (!instructions) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
const layers = [];
|
|
386
|
+
const activeLayers = instructions.layers.filter((layer) => {
|
|
387
|
+
if (!layer.payload.attachmentId) {
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
if (layer.status !== "ready") {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
return layer.activeRanges.some(
|
|
394
|
+
(range) => relativeTimeUs >= range.startUs && relativeTimeUs < range.endUs
|
|
395
|
+
);
|
|
396
|
+
});
|
|
397
|
+
for (const layerPlan of activeLayers) {
|
|
398
|
+
const layer = this.materializeLayer(layerPlan, clip, relativeTimeUs, timeUs);
|
|
399
|
+
if (layer) {
|
|
400
|
+
layers.push(layer);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return { layers };
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Materialize a serialized layer plan into concrete Layer
|
|
407
|
+
*/
|
|
408
|
+
materializeLayer(layerPlan, clip, clipRelativeTimeUs, globalTimeUs) {
|
|
409
|
+
const baseLayer = {
|
|
410
|
+
id: layerPlan.layerId,
|
|
411
|
+
type: layerPlan.type,
|
|
412
|
+
zIndex: layerPlan.zIndex ?? 0,
|
|
413
|
+
visible: true,
|
|
414
|
+
opacity: layerPlan.opacity ?? 1
|
|
415
|
+
};
|
|
416
|
+
if (layerPlan.type === "video" && !layerPlan.payload.attachmentId) {
|
|
417
|
+
const rcFrame = this.cacheManager.getFrame(clipRelativeTimeUs, clip.id);
|
|
418
|
+
if (!rcFrame) {
|
|
419
|
+
console.warn("[Orchestrator] Video frame not found in L1:", clip.id, clipRelativeTimeUs);
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
return {
|
|
423
|
+
...baseLayer,
|
|
424
|
+
type: "video",
|
|
425
|
+
rcFrame
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
if (layerPlan.type === "text") {
|
|
429
|
+
const payload = layerPlan.payload;
|
|
430
|
+
return {
|
|
431
|
+
...baseLayer,
|
|
432
|
+
type: "text",
|
|
433
|
+
text: payload.text,
|
|
434
|
+
localeCode: payload.localeCode,
|
|
435
|
+
fontConfig: payload.fontConfig,
|
|
436
|
+
animation: payload.animation,
|
|
437
|
+
wordTimings: payload.wordTimings,
|
|
438
|
+
letterCase: payload.letterCase
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
if (layerPlan.type === "image") {
|
|
442
|
+
const payload = layerPlan.payload;
|
|
443
|
+
const resourceId = payload.resourceId;
|
|
444
|
+
const source = this.cacheManager.imageBitmapCache.get(resourceId);
|
|
445
|
+
const imageLayer = {
|
|
446
|
+
...baseLayer,
|
|
447
|
+
type: "image",
|
|
448
|
+
source,
|
|
449
|
+
attachmentId: payload.attachmentId
|
|
450
|
+
};
|
|
451
|
+
if (payload.targetWidth !== void 0) {
|
|
452
|
+
imageLayer.targetWidth = payload.targetWidth;
|
|
453
|
+
}
|
|
454
|
+
if (payload.targetHeight !== void 0) {
|
|
455
|
+
imageLayer.targetHeight = payload.targetHeight;
|
|
456
|
+
}
|
|
457
|
+
if (payload.animation) {
|
|
458
|
+
const { position, keyframes, overlayClipStartUs } = payload.animation;
|
|
459
|
+
const relativeTimeUs = globalTimeUs - overlayClipStartUs;
|
|
460
|
+
if (relativeTimeUs < 0 || relativeTimeUs > keyframes[keyframes.length - 1].time) {
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
const rotationRad = 0;
|
|
464
|
+
imageLayer.transform = {
|
|
465
|
+
x: position.x,
|
|
466
|
+
y: position.y,
|
|
467
|
+
scaleX: 1,
|
|
468
|
+
scaleY: 1,
|
|
469
|
+
rotation: rotationRad,
|
|
470
|
+
anchorX: 0.5,
|
|
471
|
+
anchorY: 0.5
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
return imageLayer;
|
|
475
|
+
}
|
|
476
|
+
return baseLayer;
|
|
543
477
|
}
|
|
544
478
|
}
|
|
545
479
|
export {
|