@meframe/core 0.0.1 → 0.0.3
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/README.md +17 -4
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +2 -4
- package/dist/Meframe.js.map +1 -1
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +8 -1
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +2 -9
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/types.d.ts +3 -4
- package/dist/config/types.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.d.ts +4 -2
- package/dist/controllers/PlaybackController.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.js +7 -13
- package/dist/controllers/PlaybackController.js.map +1 -1
- package/dist/controllers/PreRenderService.d.ts +3 -2
- package/dist/controllers/PreRenderService.d.ts.map +1 -1
- package/dist/controllers/PreRenderService.js.map +1 -1
- package/dist/controllers/PreviewHandle.d.ts +2 -0
- package/dist/controllers/PreviewHandle.d.ts.map +1 -1
- package/dist/controllers/PreviewHandle.js +6 -0
- package/dist/controllers/PreviewHandle.js.map +1 -1
- package/dist/controllers/index.d.ts +1 -1
- package/dist/controllers/index.d.ts.map +1 -1
- package/dist/controllers/types.d.ts +2 -12
- package/dist/controllers/types.d.ts.map +1 -1
- package/dist/event/events.d.ts +5 -59
- package/dist/event/events.d.ts.map +1 -1
- package/dist/event/events.js +1 -6
- package/dist/event/events.js.map +1 -1
- package/dist/model/CompositionModel.js +1 -2
- package/dist/model/CompositionModel.js.map +1 -1
- package/dist/orchestrator/CompositionPlanner.d.ts.map +1 -1
- package/dist/orchestrator/CompositionPlanner.js +1 -0
- package/dist/orchestrator/CompositionPlanner.js.map +1 -1
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +3 -13
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
- package/dist/orchestrator/VideoClipSession.js +4 -5
- package/dist/orchestrator/VideoClipSession.js.map +1 -1
- package/dist/orchestrator/types.d.ts +1 -1
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/dist/stages/compose/GlobalAudioSession.d.ts.map +1 -1
- package/dist/stages/compose/GlobalAudioSession.js +3 -2
- package/dist/stages/compose/GlobalAudioSession.js.map +1 -1
- package/dist/stages/compose/VideoComposer.d.ts.map +1 -1
- package/dist/stages/compose/types.d.ts +3 -1
- package/dist/stages/compose/types.d.ts.map +1 -1
- package/dist/stages/decode/AudioChunkDecoder.d.ts.map +1 -1
- package/dist/stages/decode/VideoChunkDecoder.d.ts +0 -1
- package/dist/stages/decode/VideoChunkDecoder.d.ts.map +1 -1
- package/dist/stages/demux/MP4Demuxer.d.ts +2 -1
- package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
- package/dist/stages/load/EventHandlers.d.ts +2 -11
- package/dist/stages/load/EventHandlers.d.ts.map +1 -1
- package/dist/stages/load/EventHandlers.js +1 -24
- package/dist/stages/load/EventHandlers.js.map +1 -1
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +11 -13
- package/dist/stages/load/ResourceLoader.js.map +1 -1
- package/dist/stages/load/TaskManager.d.ts +1 -1
- package/dist/stages/load/TaskManager.d.ts.map +1 -1
- package/dist/stages/load/TaskManager.js +3 -2
- package/dist/stages/load/TaskManager.js.map +1 -1
- package/dist/stages/load/types.d.ts +2 -0
- package/dist/stages/load/types.d.ts.map +1 -1
- package/dist/utils/time-utils.d.ts +3 -2
- package/dist/utils/time-utils.d.ts.map +1 -1
- package/dist/utils/time-utils.js +2 -1
- package/dist/utils/time-utils.js.map +1 -1
- package/dist/vite-plugin.d.ts +19 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +145 -0
- package/dist/vite-plugin.js.map +1 -0
- package/dist/worker/WorkerPool.d.ts +7 -4
- package/dist/worker/WorkerPool.d.ts.map +1 -1
- package/dist/worker/WorkerPool.js +29 -18
- package/dist/worker/WorkerPool.js.map +1 -1
- package/dist/{stages/demux → workers}/MP4Demuxer.js +17 -15
- package/dist/workers/MP4Demuxer.js.map +1 -0
- package/dist/workers/WorkerChannel.js +486 -0
- package/dist/workers/WorkerChannel.js.map +1 -0
- package/dist/workers/mp4box.all.js +7049 -0
- package/dist/workers/mp4box.all.js.map +1 -0
- package/dist/workers/stages/compose/audio-compose.worker.js +1063 -0
- package/dist/workers/stages/compose/audio-compose.worker.js.map +1 -0
- package/dist/workers/stages/compose/video-compose.worker.js +1209 -0
- package/dist/workers/stages/compose/video-compose.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/decode/decode.worker.js +401 -20
- package/dist/workers/stages/decode/decode.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/demux/audio-demux.worker.js +184 -4
- package/dist/workers/stages/demux/audio-demux.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/demux/video-demux.worker.js +7 -30
- package/dist/workers/stages/demux/video-demux.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/encode/encode.worker.js +238 -5
- package/dist/workers/stages/encode/encode.worker.js.map +1 -0
- package/dist/{stages/mux/MP4Muxer.js → workers/stages/mux/mux.worker.js} +244 -5
- package/dist/workers/stages/mux/mux.worker.js.map +1 -0
- package/package.json +27 -21
- package/dist/model/types.js +0 -5
- package/dist/model/types.js.map +0 -1
- package/dist/plugins/BackpressureMonitor.js +0 -62
- package/dist/plugins/BackpressureMonitor.js.map +0 -1
- package/dist/stages/compose/AudioDucker.js +0 -161
- package/dist/stages/compose/AudioDucker.js.map +0 -1
- package/dist/stages/compose/AudioMixer.js +0 -373
- package/dist/stages/compose/AudioMixer.js.map +0 -1
- package/dist/stages/compose/FilterProcessor.js +0 -226
- package/dist/stages/compose/FilterProcessor.js.map +0 -1
- package/dist/stages/compose/LayerRenderer.js +0 -215
- package/dist/stages/compose/LayerRenderer.js.map +0 -1
- package/dist/stages/compose/TransitionProcessor.js +0 -189
- package/dist/stages/compose/TransitionProcessor.js.map +0 -1
- package/dist/stages/compose/VideoComposer.js +0 -186
- package/dist/stages/compose/VideoComposer.js.map +0 -1
- package/dist/stages/compose/audio-compose.worker.d.ts +0 -79
- package/dist/stages/compose/audio-compose.worker.d.ts.map +0 -1
- package/dist/stages/compose/audio-compose.worker.js +0 -541
- package/dist/stages/compose/audio-compose.worker.js.map +0 -1
- package/dist/stages/compose/video-compose.worker.d.ts +0 -60
- package/dist/stages/compose/video-compose.worker.d.ts.map +0 -1
- package/dist/stages/compose/video-compose.worker.js +0 -369
- package/dist/stages/compose/video-compose.worker.js.map +0 -1
- package/dist/stages/decode/AudioChunkDecoder.js +0 -83
- package/dist/stages/decode/AudioChunkDecoder.js.map +0 -1
- package/dist/stages/decode/BaseDecoder.js +0 -130
- package/dist/stages/decode/BaseDecoder.js.map +0 -1
- package/dist/stages/decode/VideoChunkDecoder.js +0 -209
- package/dist/stages/decode/VideoChunkDecoder.js.map +0 -1
- package/dist/stages/decode/decode.worker.d.ts +0 -70
- package/dist/stages/decode/decode.worker.d.ts.map +0 -1
- package/dist/stages/decode/decode.worker.js.map +0 -1
- package/dist/stages/demux/MP3FrameParser.js +0 -186
- package/dist/stages/demux/MP3FrameParser.js.map +0 -1
- package/dist/stages/demux/MP4Demuxer.js.map +0 -1
- package/dist/stages/demux/audio-demux.worker.d.ts +0 -51
- package/dist/stages/demux/audio-demux.worker.d.ts.map +0 -1
- package/dist/stages/demux/audio-demux.worker.js.map +0 -1
- package/dist/stages/demux/video-demux.worker.d.ts +0 -48
- package/dist/stages/demux/video-demux.worker.d.ts.map +0 -1
- package/dist/stages/demux/video-demux.worker.js.map +0 -1
- package/dist/stages/encode/AudioChunkEncoder.js +0 -37
- package/dist/stages/encode/AudioChunkEncoder.js.map +0 -1
- package/dist/stages/encode/BaseEncoder.js +0 -164
- package/dist/stages/encode/BaseEncoder.js.map +0 -1
- package/dist/stages/encode/VideoChunkEncoder.js +0 -50
- package/dist/stages/encode/VideoChunkEncoder.js.map +0 -1
- package/dist/stages/encode/encode.worker.d.ts +0 -3
- package/dist/stages/encode/encode.worker.d.ts.map +0 -1
- package/dist/stages/encode/encode.worker.js.map +0 -1
- package/dist/stages/mux/MP4Muxer.js.map +0 -1
- package/dist/stages/mux/mux.worker.d.ts +0 -65
- package/dist/stages/mux/mux.worker.d.ts.map +0 -1
- package/dist/stages/mux/mux.worker.js +0 -219
- package/dist/stages/mux/mux.worker.js.map +0 -1
- package/dist/stages/mux/utils.js +0 -34
- package/dist/stages/mux/utils.js.map +0 -1
package/README.md
CHANGED
|
@@ -32,6 +32,22 @@ MEFrame Core 是完全重新设计的视频处理引擎,基于 WebCodecs API
|
|
|
32
32
|
npm install @meframe/core
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
### 配置 Vite 插件
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// vite.config.ts
|
|
39
|
+
import { defineConfig } from 'vite';
|
|
40
|
+
import { meframePlugin } from '@meframe/core/vite-plugin';
|
|
41
|
+
|
|
42
|
+
export default defineConfig({
|
|
43
|
+
plugins: [
|
|
44
|
+
meframePlugin(), // 必需:复制 worker 文件到输出目录
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
> **为什么需要插件?** Worker 文件在 `node_modules` 中,生产环境需要复制到输出目录才能访问。插件会自动处理这个过程。
|
|
50
|
+
|
|
35
51
|
### 基础使用
|
|
36
52
|
|
|
37
53
|
```typescript
|
|
@@ -39,10 +55,7 @@ import { Meframe } from '@meframe/core';
|
|
|
39
55
|
import { parseDSL } from '@meframe/dsl-parser'; // 可选:DSL 解析
|
|
40
56
|
|
|
41
57
|
// 1. 创建实例
|
|
42
|
-
const core = await Meframe.create(
|
|
43
|
-
canvas: document.getElementById('preview'),
|
|
44
|
-
workerBaseUrl: '/workers/',
|
|
45
|
-
});
|
|
58
|
+
const core = await Meframe.create();
|
|
46
59
|
|
|
47
60
|
// 2. 设置合成树(两种方式)
|
|
48
61
|
|
package/dist/Meframe.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Meframe.d.ts","sourceRoot":"","sources":["../src/Meframe.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC1F,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAM5D,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGpE,qBAAa,OAAO;IAClB,wDAAwD;IACxD,KAAK,EAAE,YAAY,CAAU;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,2DAA2D;IAC3D,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IACtC,oCAAoC;IACpC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC,CAAC;IAExE,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,kBAAkB,CAAmC;IAC7D,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,MAAM,CAAC,CAAsC;IAErD,OAAO;
|
|
1
|
+
{"version":3,"file":"Meframe.d.ts","sourceRoot":"","sources":["../src/Meframe.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC1F,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAM5D,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGpE,qBAAa,OAAO;IAClB,wDAAwD;IACxD,KAAK,EAAE,YAAY,CAAU;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,2DAA2D;IAC3D,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IACtC,oCAAoC;IACpC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC,CAAC;IAExE,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,QAAQ,CAA4B;IAC5C,OAAO,CAAC,kBAAkB,CAAmC;IAC7D,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,MAAM,CAAC,CAAsC;IAErD,OAAO;IAgBP;;OAEG;WACU,MAAM,CAAC,MAAM,GAAE,aAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAQjE;;OAEG;YACW,UAAU;IAyBxB;;OAEG;IACG,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBxF;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAcxD;;OAEG;IACH,YAAY,CAAC,OAAO,CAAC,EAAE;QACrB,MAAM,CAAC,EAAE,iBAAiB,GAAG,eAAe,CAAC;QAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,aAAa;IA+BjB;;OAEG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IA+EzE;;OAEG;YACW,iBAAiB;IA8C/B;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC;IA0BlE;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB9B;;OAEG;IACH,OAAO,CAAC,QAAQ;IAYhB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;OAEG;IACH,OAAO,CAAC,WAAW;CAWpB"}
|
package/dist/Meframe.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { CompositionModel } from "./model/CompositionModel.js";
|
|
2
|
-
import "./config/defaults.js";
|
|
3
2
|
import { loadConfig } from "./config/ConfigLoader.js";
|
|
4
3
|
import { Orchestrator } from "./orchestrator/Orchestrator.js";
|
|
5
4
|
import { PlaybackController } from "./controllers/PlaybackController.js";
|
|
@@ -30,10 +29,10 @@ class Meframe {
|
|
|
30
29
|
this.events = this.eventBus.asReadonly();
|
|
31
30
|
this.pluginManager = new PluginManager(this);
|
|
32
31
|
this.orchestrator = new Orchestrator({
|
|
33
|
-
workerBaseUrl: config.global?.workerBaseUrl || "/src/stages",
|
|
34
32
|
maxWorkers: config.maxWorkers,
|
|
35
33
|
cacheConfig: config.cache,
|
|
36
|
-
eventBus: this.eventBus
|
|
34
|
+
eventBus: this.eventBus,
|
|
35
|
+
workerPath: config.global.workerPath
|
|
37
36
|
});
|
|
38
37
|
}
|
|
39
38
|
/**
|
|
@@ -74,7 +73,6 @@ class Meframe {
|
|
|
74
73
|
const compositionModel = model instanceof CompositionModel ? model : new CompositionModel(model);
|
|
75
74
|
await this.orchestrator.setCompositionModel(compositionModel);
|
|
76
75
|
this.model = compositionModel;
|
|
77
|
-
console.log("[Meframe] setCompositionModel: model", this.model);
|
|
78
76
|
this.setState("ready");
|
|
79
77
|
this.eventBus.emit(MeframeEvent.Ready, {
|
|
80
78
|
trackCount: model.tracks.length,
|
package/dist/Meframe.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Meframe.js","sources":["../src/Meframe.ts"],"sourcesContent":["/**\n * Meframe - Main entry point for the media processing framework\n */\n\nimport type { MeframeConfig, ResolvedConfig, MeframeState, ExportOptions } from './types';\nimport type { CompositionModelData, CompositionPatch, TimeUs } from './model/types';\nimport type { PreviewHandle } from './controllers/types';\nimport type { Plugin } from './plugins/types';\nimport { CompositionModel } from './model/CompositionModel';\nimport { loadConfig } from './config';\nimport { Orchestrator } from './orchestrator/Orchestrator';\nimport { PlaybackController } from './controllers/PlaybackController';\nimport { PreRenderService } from './controllers/PreRenderService';\nimport { PreviewHandleImpl } from './controllers/PreviewHandle';\nimport { PluginManager } from './plugins/PluginManager';\nimport { EventBus } from './event/EventBus';\nimport { MeframeEvent, type EventPayloadMap } from './event/events';\n// MuxWorker will be imported when needed for export\n\nexport class Meframe {\n /** Current state - managed internally via setState() */\n state: MeframeState = 'idle';\n /** Configuration - immutable after construction */\n readonly config: ResolvedConfig;\n /** Composition model - managed via setCompositionTree() */\n model: CompositionModel | null = null;\n /** Plugin manager for extensions */\n readonly pluginManager: PluginManager;\n /** Event bus for subscribing to Meframe events */\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n private orchestrator: Orchestrator;\n private eventBus: EventBus<EventPayloadMap>;\n private playbackController: PlaybackController | null = null;\n private preRenderService: PreRenderService | null = null;\n private canvas?: HTMLCanvasElement | OffscreenCanvas;\n\n private constructor(config: ResolvedConfig) {\n this.config = config;\n this.eventBus = new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n this.pluginManager = new PluginManager(this);\n\n // Initialize orchestrator with configuration and shared eventBus\n this.orchestrator = new Orchestrator({\n workerBaseUrl: config.global?.workerBaseUrl || '/src/stages',\n maxWorkers: (config as any).maxWorkers,\n cacheConfig: config.cache as any,\n eventBus: this.eventBus,\n });\n }\n\n /**\n * Create a new Meframe instance\n */\n static async create(config: MeframeConfig = {}): Promise<Meframe> {\n // Load and resolve configuration using ConfigLoader\n const resolvedConfig = await loadConfig({ override: config });\n const instance = new Meframe(resolvedConfig);\n await instance.initialize();\n return instance;\n }\n\n /**\n * Initialize the core engine\n */\n private async initialize(): Promise<void> {\n this.setState('loading');\n\n try {\n // Initialize orchestrator (sets up workers and cache)\n await this.orchestrator.initialize();\n\n // Initialize plugins if provided\n const plugins = (this.config as any).plugins;\n if (plugins && Array.isArray(plugins)) {\n for (const plugin of plugins) {\n // Ensure plugin type matches the Plugin interface\n if (plugin && typeof plugin === 'object' && 'name' in plugin && 'install' in plugin) {\n this.pluginManager.register(plugin as Plugin);\n }\n }\n }\n\n this.setState('idle');\n } catch (error) {\n this.setState('error');\n throw error;\n }\n }\n\n /**\n * Set the composition model\n */\n async setCompositionModel(model: CompositionModel | CompositionModelData): Promise<void> {\n this.ensureNotDestroyed();\n\n // Convert plain object to CompositionModel instance if needed\n const compositionModel =\n model instanceof CompositionModel ? model : new CompositionModel(model);\n\n // Set the model in orchestrator\n await this.orchestrator.setCompositionModel(compositionModel);\n this.model = compositionModel;\n console.log('[Meframe] setCompositionModel: model', this.model);\n this.setState('ready');\n this.eventBus.emit(MeframeEvent.Ready, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce(\n (acc: number, track: any) => acc + track.clips?.length || 0,\n 0\n ),\n durationUs: model.durationUs,\n });\n }\n\n /**\n * Apply a patch to the composition model\n */\n async applyPatch(patch: CompositionPatch): Promise<void> {\n this.ensureNotDestroyed();\n\n if (!this.model) {\n throw new Error('No composition model set');\n }\n\n // Apply patch through orchestrator\n await this.orchestrator.applyPatch(patch);\n\n // Patch is applied to the model by orchestrator\n this.model = this.orchestrator.compositionModel!;\n }\n\n /**\n * Start preview and return a handle for control\n */\n startPreview(options?: {\n canvas?: HTMLCanvasElement | OffscreenCanvas;\n startUs?: TimeUs;\n }): PreviewHandle {\n this.ensureReady();\n\n // Use provided canvas or the one from config\n const canvas = options?.canvas || this.canvas;\n if (!canvas) {\n throw new Error('Canvas is required for preview');\n }\n\n // Store canvas for later use\n this.canvas = canvas;\n\n // Create playback controller\n if (!this.playbackController) {\n this.playbackController = new PlaybackController(this.orchestrator as any, this.eventBus, {\n canvas,\n startUs: options?.startUs,\n });\n this.playbackController.setAudioSession(this.orchestrator.audioSession);\n }\n\n // Start pre-render service for background caching\n if (!this.preRenderService) {\n this.preRenderService = new PreRenderService(this.orchestrator as any, this.events);\n // this.preRenderService.start();\n }\n\n // Return preview handle\n return new PreviewHandleImpl(this.playbackController, this.eventBus);\n }\n\n /**\n * Export the composition\n */\n async export(options: ExportOptions): Promise<ReadableStream<Uint8Array>> {\n this.ensureReady();\n this.setState('exporting');\n\n try {\n // Get composition model\n const model = this.model;\n if (!model) {\n throw new Error('No composition model set');\n }\n\n // Determine export parameters\n const width = options.width || (model as any).renderConfig?.width || 1920;\n const height = options.height || (model as any).renderConfig?.height || 1080;\n const fps = options.fps || model.fps || 30;\n const videoBitrate =\n options.videoBitrate ||\n (options.quality === 'highest'\n ? 10000000\n : options.quality === 'high'\n ? 5000000\n : options.quality === 'medium'\n ? 2500000\n : 1000000);\n const audioBitrate = options.audioBitrate || 128000;\n\n // Create export configuration\n const exportConfig = {\n width,\n height,\n fps,\n videoBitrate,\n audioBitrate,\n videoCodec: options.videoCodec || 'h264',\n audioCodec: options.audioCodec || 'aac',\n container: options.format,\n };\n\n // Get encode worker from orchestrator\n const encodeWorker = await this.orchestrator.workers.get('encode');\n const muxWorker = await this.orchestrator.workers.get('mux');\n\n // Configure workers for export\n await encodeWorker.send('configure', exportConfig);\n await muxWorker.send('configure', { container: options.format });\n\n // Create export stream\n const { readable, writable } = new TransformStream<Uint8Array>();\n\n // Start export process\n const exportPromise = this.runExportPipeline(writable, exportConfig);\n\n // Handle export completion\n exportPromise\n .then(() => {\n this.setState('ready');\n const totalFrames = Math.ceil((model.durationUs / 1000000) * fps);\n this.eventBus.emit(MeframeEvent.ExportComplete, {\n size: width * height * totalFrames * 4, // Approximate size\n durationMs: model.durationUs / 1000,\n format: options.format,\n });\n })\n .catch((error) => {\n this.setState('error');\n this.eventBus.emit(MeframeEvent.Error, {\n source: 'export',\n error: error as Error,\n context: { format: options.format },\n });\n });\n\n return readable;\n } catch (error) {\n this.setState('error');\n throw error;\n }\n }\n\n /**\n * Run the export pipeline\n */\n private async runExportPipeline(output: WritableStream<Uint8Array>, config: any): Promise<void> {\n const model = this.model!;\n const totalFrames = Math.ceil((model.durationUs / 1000000) * config.fps);\n let processedFrames = 0;\n\n // Process frames\n const frameTimeUs = 1000000 / config.fps;\n for (let timeUs = 0; timeUs < model.durationUs; timeUs += frameTimeUs) {\n // Render frame through orchestrator\n const frameHandle = await this.orchestrator.renderFrame(timeUs);\n\n // Skip if no frame (e.g., gap in timeline)\n if (frameHandle) {\n const encodeWorker = await this.orchestrator.workers.get('encode');\n await frameHandle.use(async (frame) => {\n await encodeWorker.send('encodeFrame', { frame, timeUs });\n });\n }\n\n // Update progress\n processedFrames++;\n const progress = processedFrames / totalFrames;\n this.eventBus.emit(MeframeEvent.ExportProgress, {\n progress,\n currentFrame: processedFrames,\n totalFrames,\n timeUs,\n });\n }\n\n // Flush encoder and muxer\n const encodeWorker = await this.orchestrator.workers.get('encode');\n const muxWorker = await this.orchestrator.workers.get('mux');\n\n await encodeWorker.send('flush');\n await muxWorker.send('flush');\n\n // Get final output from muxer\n const result = await muxWorker.send<void, Uint8Array>('getOutput');\n\n // Write to output stream\n const writer = output.getWriter();\n await writer.write(result);\n await writer.close();\n }\n\n /**\n * Export to buffer (convenience method)\n */\n async exportToBuffer(options: ExportOptions): Promise<ArrayBuffer> {\n const stream = await this.export(options);\n const chunks: Uint8Array[] = [];\n const reader = stream.getReader();\n\n let done = false;\n while (!done) {\n const result = await reader.read();\n done = result.done;\n if (result.value) {\n chunks.push(result.value);\n }\n }\n\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n\n return result.buffer;\n }\n\n /**\n * Get current playback time\n */\n getCurrentTime(): number {\n return this.playbackController?.currentTime || 0;\n }\n\n /**\n * Clean up and destroy the instance\n */\n async dispose(): Promise<void> {\n if (this.state === 'destroyed') return;\n\n // Stop playback\n this.playbackController?.stop();\n this.playbackController?.dispose();\n this.playbackController = null;\n\n // Stop pre-render service\n this.preRenderService?.stop();\n this.preRenderService = null;\n\n // Cleanup plugins\n await this.pluginManager.disposeAll();\n\n // Dispose orchestrator (stops workers and clears cache)\n await this.orchestrator.dispose();\n\n // Clear event bus\n this.eventBus.dispose();\n\n this.setState('destroyed');\n }\n\n /**\n * Set internal state\n */\n private setState(state: MeframeState): void {\n const oldState = this.state;\n this.state = state;\n\n // Emit state change event\n // Use a generic event for now, can be added to MeframeEvent enum later\n this.eventBus.emit('state:changed' as any, {\n oldState,\n newState: state,\n });\n }\n\n /**\n * Ensure the instance is not destroyed\n */\n private ensureNotDestroyed(): void {\n if (this.state === 'destroyed') {\n throw new Error('Core instance is destroyed');\n }\n }\n\n /**\n * Ensure the instance is ready\n */\n private ensureReady(): void {\n this.ensureNotDestroyed();\n\n if (!this.model) {\n throw new Error('No composition model set');\n }\n\n if (this.state === 'loading' || this.state === 'idle') {\n throw new Error('Core is not ready');\n }\n }\n}\n"],"names":["encodeWorker","result"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Meframe.js","sources":["../src/Meframe.ts"],"sourcesContent":["/**\n * Meframe - Main entry point for the media processing framework\n */\n\nimport type { MeframeConfig, ResolvedConfig, MeframeState, ExportOptions } from './types';\nimport type { CompositionModelData, CompositionPatch, TimeUs } from './model/types';\nimport type { PreviewHandle } from './controllers/types';\nimport type { Plugin } from './plugins/types';\nimport { CompositionModel } from './model/CompositionModel';\nimport { loadConfig } from './config';\nimport { Orchestrator } from './orchestrator/Orchestrator';\nimport { PlaybackController } from './controllers/PlaybackController';\nimport { PreRenderService } from './controllers/PreRenderService';\nimport { PreviewHandleImpl } from './controllers/PreviewHandle';\nimport { PluginManager } from './plugins/PluginManager';\nimport { EventBus } from './event/EventBus';\nimport { MeframeEvent, type EventPayloadMap } from './event/events';\n// MuxWorker will be imported when needed for export\n\nexport class Meframe {\n /** Current state - managed internally via setState() */\n state: MeframeState = 'idle';\n /** Configuration - immutable after construction */\n readonly config: ResolvedConfig;\n /** Composition model - managed via setCompositionTree() */\n model: CompositionModel | null = null;\n /** Plugin manager for extensions */\n readonly pluginManager: PluginManager;\n /** Event bus for subscribing to Meframe events */\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n private orchestrator: Orchestrator;\n private eventBus: EventBus<EventPayloadMap>;\n private playbackController: PlaybackController | null = null;\n private preRenderService: PreRenderService | null = null;\n private canvas?: HTMLCanvasElement | OffscreenCanvas;\n\n private constructor(config: ResolvedConfig) {\n this.config = config;\n this.eventBus = new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n this.pluginManager = new PluginManager(this);\n\n // Initialize orchestrator with configuration and shared eventBus\n // Worker paths are passed via config\n this.orchestrator = new Orchestrator({\n maxWorkers: (config as any).maxWorkers,\n cacheConfig: config.cache as any,\n eventBus: this.eventBus,\n workerPath: config.global.workerPath,\n });\n }\n\n /**\n * Create a new Meframe instance\n */\n static async create(config: MeframeConfig = {}): Promise<Meframe> {\n // Load and resolve configuration using ConfigLoader\n const resolvedConfig = await loadConfig({ override: config });\n const instance = new Meframe(resolvedConfig);\n await instance.initialize();\n return instance;\n }\n\n /**\n * Initialize the core engine\n */\n private async initialize(): Promise<void> {\n this.setState('loading');\n\n try {\n // Initialize orchestrator (sets up workers and cache)\n await this.orchestrator.initialize();\n\n // Initialize plugins if provided\n const plugins = (this.config as any).plugins;\n if (plugins && Array.isArray(plugins)) {\n for (const plugin of plugins) {\n // Ensure plugin type matches the Plugin interface\n if (plugin && typeof plugin === 'object' && 'name' in plugin && 'install' in plugin) {\n this.pluginManager.register(plugin as Plugin);\n }\n }\n }\n\n this.setState('idle');\n } catch (error) {\n this.setState('error');\n throw error;\n }\n }\n\n /**\n * Set the composition model\n */\n async setCompositionModel(model: CompositionModel | CompositionModelData): Promise<void> {\n this.ensureNotDestroyed();\n\n // Convert plain object to CompositionModel instance if needed\n const compositionModel =\n model instanceof CompositionModel ? model : new CompositionModel(model);\n\n // Set the model in orchestrator\n await this.orchestrator.setCompositionModel(compositionModel);\n this.model = compositionModel;\n this.setState('ready');\n this.eventBus.emit(MeframeEvent.Ready, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce(\n (acc: number, track: any) => acc + track.clips?.length || 0,\n 0\n ),\n durationUs: model.durationUs,\n });\n }\n\n /**\n * Apply a patch to the composition model\n */\n async applyPatch(patch: CompositionPatch): Promise<void> {\n this.ensureNotDestroyed();\n\n if (!this.model) {\n throw new Error('No composition model set');\n }\n\n // Apply patch through orchestrator\n await this.orchestrator.applyPatch(patch);\n\n // Patch is applied to the model by orchestrator\n this.model = this.orchestrator.compositionModel!;\n }\n\n /**\n * Start preview and return a handle for control\n */\n startPreview(options?: {\n canvas?: HTMLCanvasElement | OffscreenCanvas;\n startUs?: TimeUs;\n }): PreviewHandle {\n this.ensureReady();\n\n // Use provided canvas or the one from config\n const canvas = options?.canvas || this.canvas;\n if (!canvas) {\n throw new Error('Canvas is required for preview');\n }\n\n // Store canvas for later use\n this.canvas = canvas;\n\n // Create playback controller\n if (!this.playbackController) {\n this.playbackController = new PlaybackController(this.orchestrator as any, this.eventBus, {\n canvas,\n startUs: options?.startUs,\n });\n this.playbackController.setAudioSession(this.orchestrator.audioSession);\n }\n\n // Start pre-render service for background caching\n if (!this.preRenderService) {\n this.preRenderService = new PreRenderService(this.orchestrator as any, this.events);\n // this.preRenderService.start();\n }\n\n // Return preview handle\n return new PreviewHandleImpl(this.playbackController, this.eventBus);\n }\n\n /**\n * Export the composition\n */\n async export(options: ExportOptions): Promise<ReadableStream<Uint8Array>> {\n this.ensureReady();\n this.setState('exporting');\n\n try {\n // Get composition model\n const model = this.model;\n if (!model) {\n throw new Error('No composition model set');\n }\n\n // Determine export parameters\n const width = options.width || (model as any).renderConfig?.width || 1920;\n const height = options.height || (model as any).renderConfig?.height || 1080;\n const fps = options.fps || model.fps || 30;\n const videoBitrate =\n options.videoBitrate ||\n (options.quality === 'highest'\n ? 10000000\n : options.quality === 'high'\n ? 5000000\n : options.quality === 'medium'\n ? 2500000\n : 1000000);\n const audioBitrate = options.audioBitrate || 128000;\n\n // Create export configuration\n const exportConfig = {\n width,\n height,\n fps,\n videoBitrate,\n audioBitrate,\n videoCodec: options.videoCodec || 'h264',\n audioCodec: options.audioCodec || 'aac',\n container: options.format,\n };\n\n // Get encode worker from orchestrator\n const encodeWorker = await this.orchestrator.workers.get('encode');\n const muxWorker = await this.orchestrator.workers.get('mux');\n\n // Configure workers for export\n await encodeWorker.send('configure', exportConfig);\n await muxWorker.send('configure', { container: options.format });\n\n // Create export stream\n const { readable, writable } = new TransformStream<Uint8Array>();\n\n // Start export process\n const exportPromise = this.runExportPipeline(writable, exportConfig);\n\n // Handle export completion\n exportPromise\n .then(() => {\n this.setState('ready');\n const totalFrames = Math.ceil((model.durationUs / 1000000) * fps);\n this.eventBus.emit(MeframeEvent.ExportComplete, {\n size: width * height * totalFrames * 4, // Approximate size\n durationMs: model.durationUs / 1000,\n format: options.format,\n });\n })\n .catch((error) => {\n this.setState('error');\n this.eventBus.emit(MeframeEvent.Error, {\n source: 'export',\n error: error as Error,\n context: { format: options.format },\n });\n });\n\n return readable;\n } catch (error) {\n this.setState('error');\n throw error;\n }\n }\n\n /**\n * Run the export pipeline\n */\n private async runExportPipeline(output: WritableStream<Uint8Array>, config: any): Promise<void> {\n const model = this.model!;\n const totalFrames = Math.ceil((model.durationUs / 1000000) * config.fps);\n let processedFrames = 0;\n\n // Process frames\n const frameTimeUs = 1000000 / config.fps;\n for (let timeUs = 0; timeUs < model.durationUs; timeUs += frameTimeUs) {\n // Render frame through orchestrator\n const frameHandle = await this.orchestrator.renderFrame(timeUs);\n\n // Skip if no frame (e.g., gap in timeline)\n if (frameHandle) {\n const encodeWorker = await this.orchestrator.workers.get('encode');\n await frameHandle.use(async (frame) => {\n await encodeWorker.send('encodeFrame', { frame, timeUs });\n });\n }\n\n // Update progress\n processedFrames++;\n const progress = processedFrames / totalFrames;\n this.eventBus.emit(MeframeEvent.ExportProgress, {\n progress,\n currentFrame: processedFrames,\n totalFrames,\n timeUs,\n });\n }\n\n // Flush encoder and muxer\n const encodeWorker = await this.orchestrator.workers.get('encode');\n const muxWorker = await this.orchestrator.workers.get('mux');\n\n await encodeWorker.send('flush');\n await muxWorker.send('flush');\n\n // Get final output from muxer\n const result = await muxWorker.send<void, Uint8Array>('getOutput');\n\n // Write to output stream\n const writer = output.getWriter();\n await writer.write(result);\n await writer.close();\n }\n\n /**\n * Export to buffer (convenience method)\n */\n async exportToBuffer(options: ExportOptions): Promise<ArrayBuffer> {\n const stream = await this.export(options);\n const chunks: Uint8Array[] = [];\n const reader = stream.getReader();\n\n let done = false;\n while (!done) {\n const result = await reader.read();\n done = result.done;\n if (result.value) {\n chunks.push(result.value);\n }\n }\n\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.length;\n }\n\n return result.buffer;\n }\n\n /**\n * Get current playback time\n */\n getCurrentTime(): number {\n return this.playbackController?.currentTime || 0;\n }\n\n /**\n * Clean up and destroy the instance\n */\n async dispose(): Promise<void> {\n if (this.state === 'destroyed') return;\n\n // Stop playback\n this.playbackController?.stop();\n this.playbackController?.dispose();\n this.playbackController = null;\n\n // Stop pre-render service\n this.preRenderService?.stop();\n this.preRenderService = null;\n\n // Cleanup plugins\n await this.pluginManager.disposeAll();\n\n // Dispose orchestrator (stops workers and clears cache)\n await this.orchestrator.dispose();\n\n // Clear event bus\n this.eventBus.dispose();\n\n this.setState('destroyed');\n }\n\n /**\n * Set internal state\n */\n private setState(state: MeframeState): void {\n const oldState = this.state;\n this.state = state;\n\n // Emit state change event\n // Use a generic event for now, can be added to MeframeEvent enum later\n this.eventBus.emit('state:changed' as any, {\n oldState,\n newState: state,\n });\n }\n\n /**\n * Ensure the instance is not destroyed\n */\n private ensureNotDestroyed(): void {\n if (this.state === 'destroyed') {\n throw new Error('Core instance is destroyed');\n }\n }\n\n /**\n * Ensure the instance is ready\n */\n private ensureReady(): void {\n this.ensureNotDestroyed();\n\n if (!this.model) {\n throw new Error('No composition model set');\n }\n\n if (this.state === 'loading' || this.state === 'idle') {\n throw new Error('Core is not ready');\n }\n }\n}\n"],"names":["encodeWorker","result"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CacheManager.d.ts","sourceRoot":"","sources":["../../src/cache/CacheManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAIjD,UAAU,kBAAkB;IAC1B,EAAE,EAAE;QACF,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,EAAE,EAAE;QACF,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,UAAU,mBAAmB;IAC3B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAuBD,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,IAAI,GAAG,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAoB;IACtD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,oBAAoB,CAAkD;IAC9E,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,gBAAgB,CAAwC;IAChE,OAAO,CAAC,QAAQ,CAAC,CAA4B;gBAEjC,MAAM,EAAE,kBAAkB,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,eAAe,CAAC;IAQtE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI;IAKhD,oBAAoB,CAClB,MAAM,EAAE,cAAc,CAAC,UAAU,GAAG;QAAE,KAAK,EAAE,UAAU,CAAC;QAAC,QAAQ,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC,EAC1E,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,CAAC,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;KAC7D,GACA,IAAI;
|
|
1
|
+
{"version":3,"file":"CacheManager.d.ts","sourceRoot":"","sources":["../../src/cache/CacheManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,7 +26,6 @@ class CacheManager {
|
|
|
26
26
|
configure(_model) {
|
|
27
27
|
}
|
|
28
28
|
acceptComposedFrames(stream, params) {
|
|
29
|
-
console.log("[CacheManager] acceptComposedFrames", params.clipId);
|
|
30
29
|
const reader = stream.getReader();
|
|
31
30
|
const process = async () => {
|
|
32
31
|
const { done, value } = await reader.read();
|
|
@@ -54,6 +53,14 @@ class CacheManager {
|
|
|
54
53
|
return;
|
|
55
54
|
}
|
|
56
55
|
this.notifyFrameWaiters(params.clipId, timestamp, frameDuration, rcFrame);
|
|
56
|
+
if (timestamp === 0) {
|
|
57
|
+
this.eventBus?.emit(MeframeEvent.CacheCover, {
|
|
58
|
+
timeUs: timestamp,
|
|
59
|
+
clipId: params.clipId,
|
|
60
|
+
level: "L1",
|
|
61
|
+
size: rcFrame.sizeEstimate ?? 0
|
|
62
|
+
});
|
|
63
|
+
}
|
|
57
64
|
const info = { clipId: params.clipId, timeUs: timestamp };
|
|
58
65
|
this.eventBus?.emit(MeframeEvent.ComposeFrameReady, {
|
|
59
66
|
timeUs: timestamp,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CacheManager.js","sources":["../../src/cache/CacheManager.ts"],"sourcesContent":["import type { TimeUs } from '../model/types';\nimport { RcFrame } from '../model';\nimport type { GOP } from './types';\nimport { VideoL1Cache } from './l1/VideoL1Cache';\nimport { L2Cache } from './L2Cache';\nimport { MeframeEvent } from '../event/events';\nimport type { EventBus } from '../event/EventBus';\nimport type { EventPayloadMap } from '../event/events';\nimport type { CompositionModel } from '../model';\nimport { AudioL1Cache } from './l1/AudioL1Cache';\nimport { MixedAudioL1Cache } from './l1/MixedAudioL1Cache';\n\ninterface CacheManagerConfig {\n l1: {\n maxMemoryMB: number;\n maxGOPs?: number;\n gopIntervalUs?: number;\n };\n l2: {\n maxSizeMB: number;\n projectId: string;\n };\n}\n\ninterface WaitForFrameOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n toleranceUs?: number;\n}\n\ninterface FrameWaiter {\n requestKey: string;\n clipId: string;\n targetTimeUs: TimeUs;\n resolve: (result: WaitForFrameResult) => void;\n reject: (reason?: unknown) => void;\n toleranceUs: number;\n timeoutId?: ReturnType<typeof setTimeout>;\n abortCleanup?: () => void;\n}\n\ninterface ClipReadyWaiter {\n clipId: string;\n minFrameCount: number;\n resolve: (ready: boolean) => void;\n reject: (reason?: unknown) => void;\n timeoutId?: ReturnType<typeof setTimeout>;\n}\n\nconst DEFAULT_WAIT_TOLERANCE_US = 33_333; // ≈1 frame @30fps\n\nexport interface WaitForFrameResult {\n frame: RcFrame | null;\n source: 'l1' | 'wait';\n timestampUs: TimeUs;\n clipId: string;\n}\n\n/**\n * Simplified CacheManager for 3-Clip strategy\n *\n * Core features:\n * - L1 (VRAM) for composed VideoFrames\n * - L2 (IndexedDB/OPFS) for encoded chunks\n * - Clip-level cache management\n */\nexport class CacheManager {\n private readonly videoL1Cache: VideoL1Cache;\n private readonly audioL1Cache: AudioL1Cache;\n private readonly mixedAudioL1Cache: MixedAudioL1Cache;\n readonly l2Cache: L2Cache;\n private pendingFramePromises = new Map<string, Promise<WaitForFrameResult>>();\n private frameWaiters = new Map<string, Set<FrameWaiter>>();\n private clipReadyWaiters = new Map<string, ClipReadyWaiter[]>();\n private eventBus?: EventBus<EventPayloadMap>;\n\n constructor(config: CacheManagerConfig, eventBus?: EventBus<EventPayloadMap>) {\n this.videoL1Cache = new VideoL1Cache(config.l1);\n this.l2Cache = new L2Cache(config.l2);\n this.audioL1Cache = new AudioL1Cache();\n this.mixedAudioL1Cache = new MixedAudioL1Cache();\n this.eventBus = eventBus;\n }\n\n async init(): Promise<void> {\n await this.l2Cache.init();\n }\n\n configure(_model: CompositionModel | null): void {\n // Simplified - no complex window configuration needed\n // 3-Clip strategy uses fixed memory budget\n }\n\n acceptComposedFrames(\n stream: ReadableStream<VideoFrame | { frame: VideoFrame; metadata?: any }>,\n params: {\n clipId: string;\n trackId: string;\n fps: number;\n onFrame: (info: { clipId: string; timeUs: TimeUs }) => void;\n }\n ): void {\n console.log('[CacheManager] acceptComposedFrames', params.clipId);\n const reader = stream.getReader();\n const process = async (): Promise<void> => {\n const { done, value } = await reader.read();\n // console.log('[CacheManager] acceptComposedFrames', params.clipId, 'value: ', value?.frame?.timestamp);\n if (done) {\n reader.releaseLock();\n return;\n }\n if (value) {\n const fps = params.fps > 0 ? params.fps : 30;\n const frameDuration = Math.round(1_000_000 / fps);\n\n const frame = (value as any).frame || value;\n const metadata = (value as any).metadata;\n const gopSerial = metadata?.gopSerial;\n const isKeyframe = metadata?.isKeyframe;\n\n const timestamp = frame.timestamp ?? 0;\n\n const rcFrame = this.videoL1Cache.addFrame(\n frame,\n params.clipId,\n frameDuration,\n gopSerial,\n isKeyframe\n );\n if (!rcFrame) {\n await process();\n return;\n }\n\n this.notifyFrameWaiters(params.clipId, timestamp, frameDuration, rcFrame);\n\n const info = { clipId: params.clipId, timeUs: timestamp };\n this.eventBus?.emit(MeframeEvent.ComposeFrameReady, {\n timeUs: timestamp,\n frameNumber: Math.floor(timestamp / frameDuration),\n renderTimeMs: 0,\n trackId: params.trackId,\n clipId: params.clipId,\n });\n\n params.onFrame(info);\n }\n\n await process();\n };\n\n process().catch((error) => {\n this.eventBus?.emit(MeframeEvent.ComposeFrameDropped, {\n timeUs: 0,\n reason: 'compose_slow',\n });\n reader.releaseLock();\n throw error;\n });\n }\n\n acceptEncodedChunks(\n stream: ReadableStream<EncodedVideoChunk | EncodedAudioChunk>,\n clipId: string,\n track: 'video' | 'audio'\n ): void {\n const reader = stream.getReader();\n const chunks: Array<EncodedVideoChunk | EncodedAudioChunk> = [];\n\n const process = async (): Promise<void> => {\n const { done, value } = await reader.read();\n if (done) {\n if (chunks.length > 0) {\n await this.l2Cache.put(clipId, chunks, track);\n }\n reader.releaseLock();\n return;\n }\n\n if (value) {\n chunks.push(value);\n this.eventBus?.emit(MeframeEvent.EncodeChunkReady, {\n timeUs: value.timestamp,\n durationUs: value.duration ?? 0,\n track,\n size: value.byteLength,\n });\n }\n\n await process();\n };\n\n process().catch((error) => {\n this.eventBus?.emit(MeframeEvent.EncodeChunkError, {\n timeUs: 0,\n track,\n error,\n });\n reader.releaseLock();\n throw error;\n });\n }\n\n acceptMixedAudio(\n stream: ReadableStream<AudioData>,\n metadata: { sampleRate: number; numberOfChannels: number }\n ): void {\n this.audioL1Cache.attachStream(stream, metadata);\n }\n\n putClipAudioData(\n clipId: string,\n audioData: AudioData,\n clipStartUs: TimeUs,\n clipDurationUs: TimeUs\n ): void {\n this.audioL1Cache.putClipAudioData(clipId, audioData, clipStartUs, clipDurationUs);\n }\n\n getClipPCM(clipId: string, startUs: TimeUs, endUs: TimeUs): Float32Array[] | null {\n return this.audioL1Cache.getPCM(clipId, startUs, endUs);\n }\n\n hasClipPCM(clipId: string): boolean {\n return this.audioL1Cache.hasClipPCM(clipId);\n }\n\n putMixedAudio(startUs: TimeUs, endUs: TimeUs, buffer: AudioBuffer): void {\n this.mixedAudioL1Cache.putMixed(startUs, endUs, buffer);\n }\n\n getMixedAudio(startUs: TimeUs, endUs: TimeUs): AudioBuffer | null {\n return this.mixedAudioL1Cache.getMixed(startUs, endUs);\n }\n\n resetAudioCache(): void {\n this.audioL1Cache.reset();\n this.mixedAudioL1Cache.clear();\n }\n\n async getFrame(timeUs: TimeUs, clipId: string): Promise<RcFrame | null> {\n const rcFrame = this.videoL1Cache.get(timeUs, clipId);\n if (rcFrame) {\n return rcFrame;\n }\n\n const decodeKey = this.makeRequestKey(clipId, timeUs);\n const pending = this.pendingFramePromises.get(decodeKey);\n if (pending) {\n const result = await pending;\n return result.frame;\n }\n\n const decodePromise = this.decodeFromL2(timeUs, clipId);\n const tracked = decodePromise.finally(() => {\n this.pendingFramePromises.delete(decodeKey);\n });\n\n this.pendingFramePromises.set(\n decodeKey,\n tracked.then((frame) => ({ frame, source: 'wait' as const, timestampUs: timeUs, clipId }))\n );\n return tracked;\n }\n\n waitForFrame(\n timeUs: TimeUs,\n clipId: string,\n options: WaitForFrameOptions = {}\n ): Promise<WaitForFrameResult> {\n const existing = this.videoL1Cache.get(timeUs, clipId);\n if (existing) {\n return Promise.resolve({ frame: existing, source: 'l1', timestampUs: timeUs, clipId });\n }\n\n const requestKey = this.makeRequestKey(clipId, timeUs);\n const existingPromise = this.pendingFramePromises.get(requestKey);\n if (existingPromise) {\n return existingPromise;\n }\n\n const promise = new Promise<WaitForFrameResult>((resolve, reject) => {\n const toleranceUs = Math.max(\n options.toleranceUs ?? DEFAULT_WAIT_TOLERANCE_US,\n DEFAULT_WAIT_TOLERANCE_US\n );\n\n const waiter: FrameWaiter = {\n requestKey,\n clipId,\n targetTimeUs: timeUs,\n resolve,\n reject,\n toleranceUs,\n };\n\n let waiters = this.frameWaiters.get(clipId);\n if (!waiters) {\n waiters = new Set();\n this.frameWaiters.set(clipId, waiters);\n }\n waiters.add(waiter);\n\n const signal = options.signal;\n if (signal) {\n const onAbort = (): void => {\n this.removeWaiter(waiter);\n this.cleanupWaiter(waiter);\n reject(new DOMException('Render aborted', 'AbortError'));\n };\n\n if (signal.aborted) {\n onAbort();\n return;\n }\n\n signal.addEventListener('abort', onAbort, { once: true });\n waiter.abortCleanup = () => {\n signal.removeEventListener('abort', onAbort);\n };\n }\n\n if (options.timeoutMs && options.timeoutMs > 0) {\n waiter.timeoutId = setTimeout(() => {\n this.removeWaiter(waiter);\n this.cleanupWaiter(waiter);\n reject(new Error('waitForFrame timeout'));\n }, options.timeoutMs);\n }\n });\n\n const trackedPromise = promise.finally(() => {\n this.pendingFramePromises.delete(requestKey);\n });\n\n this.pendingFramePromises.set(requestKey, trackedPromise);\n return trackedPromise;\n }\n\n async putGOP(gop: GOP): Promise<void> {\n if (!gop.clipId) {\n throw new Error('GOP clipId is required for clip-aware caching');\n }\n this.videoL1Cache.putGOP(gop, gop.clipId);\n this.encodeToL2(gop);\n }\n\n async putEncodedChunks(\n clipHash: string,\n chunks: Array<EncodedVideoChunk | EncodedAudioChunk>,\n track: 'video' | 'audio'\n ): Promise<void> {\n await this.l2Cache.put(clipHash, chunks, track);\n }\n\n invalidateRange(startUs: TimeUs, endUs: TimeUs, clipId?: string): void {\n this.videoL1Cache.invalidateRange(startUs, endUs, clipId);\n this.l2Cache.invalidateRange(startUs, endUs, clipId);\n }\n\n async invalidateClip(clipId: string): Promise<void> {\n this.videoL1Cache.invalidateRange(0, Infinity, clipId);\n await this.l2Cache.invalidateClip(clipId);\n }\n\n /**\n * Evict a clip from L1 cache\n */\n evictClip(clipId: string): void {\n this.videoL1Cache.evictClip(clipId);\n }\n\n /**\n * Check if a clip is cached in L1\n */\n isClipCached(clipId: string): boolean {\n return this.videoL1Cache.isClipCached(clipId);\n }\n\n /**\n * Wait for a clip to have minimum frames cached\n * Used by PlaybackController for buffering state\n */\n waitForClipReady(\n clipId: string,\n options: { minFrameCount?: number; timeoutMs?: number } = {}\n ): Promise<boolean> {\n const minFrameCount = options.minFrameCount ?? 30;\n const currentFrameCount = this.videoL1Cache.getClipFrameCount(clipId);\n\n if (currentFrameCount >= minFrameCount) {\n return Promise.resolve(true);\n }\n\n return new Promise<boolean>((resolve, reject) => {\n const waiter: ClipReadyWaiter = {\n clipId,\n minFrameCount,\n resolve,\n reject,\n };\n\n const waiters = this.clipReadyWaiters.get(clipId) || [];\n waiters.push(waiter);\n this.clipReadyWaiters.set(clipId, waiters);\n\n if (options.timeoutMs && options.timeoutMs > 0) {\n waiter.timeoutId = setTimeout(() => {\n const waiters = this.clipReadyWaiters.get(clipId);\n if (waiters) {\n const remaining = waiters.filter((w) => w !== waiter);\n if (remaining.length === 0) {\n this.clipReadyWaiters.delete(clipId);\n } else {\n this.clipReadyWaiters.set(clipId, remaining);\n }\n }\n resolve(false);\n }, options.timeoutMs);\n }\n });\n }\n\n async clear(): Promise<void> {\n this.videoL1Cache.clear();\n await this.l2Cache.clear();\n }\n\n getMetadata() {\n return {\n l1: this.videoL1Cache.getMetadata(),\n l2: this.l2Cache.getMetadata(),\n };\n }\n\n private async decodeFromL2(timeUs: TimeUs, clipId: string): Promise<RcFrame | null> {\n const encodedChunk = await this.l2Cache.get(timeUs, clipId);\n if (!encodedChunk) {\n return null;\n }\n\n return null;\n }\n\n private async encodeToL2(_gop: GOP): Promise<void> {\n // Placeholder for L2 encoding\n }\n\n private notifyFrameWaiters(\n clipId: string,\n timestampUs: TimeUs,\n frameDurationUs: TimeUs,\n frame: RcFrame\n ): void {\n const waiters = this.frameWaiters.get(clipId);\n if (!waiters || waiters.size === 0) {\n return;\n }\n\n const resolved: FrameWaiter[] = [];\n\n for (const waiter of waiters) {\n const matches = this.matchesTimestamp(\n waiter.targetTimeUs,\n timestampUs,\n frameDurationUs,\n waiter.toleranceUs\n );\n\n if (!matches) continue;\n\n resolved.push(waiter);\n this.cleanupWaiter(waiter);\n\n waiter.resolve({\n frame,\n source: 'wait',\n timestampUs,\n clipId,\n });\n }\n\n for (const waiter of resolved) {\n waiters.delete(waiter);\n }\n\n if (waiters.size === 0) {\n this.frameWaiters.delete(clipId);\n }\n\n this.notifyClipReadyWaiters(clipId);\n }\n\n private notifyClipReadyWaiters(clipId: string): void {\n const waiters = this.clipReadyWaiters.get(clipId);\n if (!waiters || waiters.length === 0) {\n return;\n }\n\n const frameCount = this.videoL1Cache.getClipFrameCount(clipId);\n const resolved: ClipReadyWaiter[] = [];\n\n for (const waiter of waiters) {\n if (frameCount >= waiter.minFrameCount) {\n resolved.push(waiter);\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n }\n waiter.resolve(true);\n }\n }\n\n const remaining = waiters.filter((w) => !resolved.includes(w));\n if (remaining.length === 0) {\n this.clipReadyWaiters.delete(clipId);\n } else {\n this.clipReadyWaiters.set(clipId, remaining);\n }\n }\n\n private cleanupWaiter(waiter: FrameWaiter): void {\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n waiter.timeoutId = undefined;\n }\n\n if (waiter.abortCleanup) {\n waiter.abortCleanup();\n waiter.abortCleanup = undefined;\n }\n }\n\n private removeWaiter(waiter: FrameWaiter): void {\n const waiters = this.frameWaiters.get(waiter.clipId);\n if (!waiters) return;\n\n waiters.delete(waiter);\n if (waiters.size === 0) {\n this.frameWaiters.delete(waiter.clipId);\n }\n }\n\n private matchesTimestamp(\n targetTimeUs: TimeUs,\n actualTimeUs: TimeUs,\n frameDurationUs: TimeUs,\n toleranceUs: TimeUs\n ): boolean {\n if (targetTimeUs === actualTimeUs) return true;\n\n const delta = Math.abs(targetTimeUs - actualTimeUs);\n if (delta <= toleranceUs) {\n return true;\n }\n\n if (actualTimeUs >= targetTimeUs && actualTimeUs < targetTimeUs + frameDurationUs) {\n return true;\n }\n\n return false;\n }\n\n private makeRequestKey(clipId: string, timeUs: TimeUs): string {\n return `${clipId}:${timeUs}`;\n }\n}\n"],"names":["waiters"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CacheManager.js","sources":["../../src/cache/CacheManager.ts"],"sourcesContent":["import type { TimeUs } from '../model/types';\nimport { RcFrame } from '../model';\nimport type { GOP } from './types';\nimport { VideoL1Cache } from './l1/VideoL1Cache';\nimport { L2Cache } from './L2Cache';\nimport { MeframeEvent } from '../event/events';\nimport type { EventBus } from '../event/EventBus';\nimport type { EventPayloadMap } from '../event/events';\nimport type { CompositionModel } from '../model';\nimport { AudioL1Cache } from './l1/AudioL1Cache';\nimport { MixedAudioL1Cache } from './l1/MixedAudioL1Cache';\n\ninterface CacheManagerConfig {\n l1: {\n maxMemoryMB: number;\n maxGOPs?: number;\n gopIntervalUs?: number;\n };\n l2: {\n maxSizeMB: number;\n projectId: string;\n };\n}\n\ninterface WaitForFrameOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n toleranceUs?: number;\n}\n\ninterface FrameWaiter {\n requestKey: string;\n clipId: string;\n targetTimeUs: TimeUs;\n resolve: (result: WaitForFrameResult) => void;\n reject: (reason?: unknown) => void;\n toleranceUs: number;\n timeoutId?: ReturnType<typeof setTimeout>;\n abortCleanup?: () => void;\n}\n\ninterface ClipReadyWaiter {\n clipId: string;\n minFrameCount: number;\n resolve: (ready: boolean) => void;\n reject: (reason?: unknown) => void;\n timeoutId?: ReturnType<typeof setTimeout>;\n}\n\nconst DEFAULT_WAIT_TOLERANCE_US = 33_333; // ≈1 frame @30fps\n\nexport interface WaitForFrameResult {\n frame: RcFrame | null;\n source: 'l1' | 'wait';\n timestampUs: TimeUs;\n clipId: string;\n}\n\n/**\n * Simplified CacheManager for 3-Clip strategy\n *\n * Core features:\n * - L1 (VRAM) for composed VideoFrames\n * - L2 (IndexedDB/OPFS) for encoded chunks\n * - Clip-level cache management\n */\nexport class CacheManager {\n private readonly videoL1Cache: VideoL1Cache;\n private readonly audioL1Cache: AudioL1Cache;\n private readonly mixedAudioL1Cache: MixedAudioL1Cache;\n readonly l2Cache: L2Cache;\n private pendingFramePromises = new Map<string, Promise<WaitForFrameResult>>();\n private frameWaiters = new Map<string, Set<FrameWaiter>>();\n private clipReadyWaiters = new Map<string, ClipReadyWaiter[]>();\n private eventBus?: EventBus<EventPayloadMap>;\n\n constructor(config: CacheManagerConfig, eventBus?: EventBus<EventPayloadMap>) {\n this.videoL1Cache = new VideoL1Cache(config.l1);\n this.l2Cache = new L2Cache(config.l2);\n this.audioL1Cache = new AudioL1Cache();\n this.mixedAudioL1Cache = new MixedAudioL1Cache();\n this.eventBus = eventBus;\n }\n\n async init(): Promise<void> {\n await this.l2Cache.init();\n }\n\n configure(_model: CompositionModel | null): void {\n // Simplified - no complex window configuration needed\n // 3-Clip strategy uses fixed memory budget\n }\n\n acceptComposedFrames(\n stream: ReadableStream<VideoFrame | { frame: VideoFrame; metadata?: any }>,\n params: {\n clipId: string;\n trackId: string;\n fps: number;\n onFrame: (info: { clipId: string; timeUs: TimeUs }) => void;\n }\n ): void {\n const reader = stream.getReader();\n const process = async (): Promise<void> => {\n const { done, value } = await reader.read();\n if (done) {\n reader.releaseLock();\n return;\n }\n if (value) {\n const fps = params.fps > 0 ? params.fps : 30;\n const frameDuration = Math.round(1_000_000 / fps);\n\n const frame = (value as any).frame || value;\n const metadata = (value as any).metadata;\n const gopSerial = metadata?.gopSerial;\n const isKeyframe = metadata?.isKeyframe;\n\n const timestamp = frame.timestamp ?? 0;\n\n const rcFrame = this.videoL1Cache.addFrame(\n frame,\n params.clipId,\n frameDuration,\n gopSerial,\n isKeyframe\n );\n if (!rcFrame) {\n await process();\n return;\n }\n\n this.notifyFrameWaiters(params.clipId, timestamp, frameDuration, rcFrame);\n\n if (timestamp === 0) {\n this.eventBus?.emit(MeframeEvent.CacheCover, {\n timeUs: timestamp,\n clipId: params.clipId,\n level: 'L1',\n size: rcFrame.sizeEstimate ?? 0,\n });\n }\n\n const info = { clipId: params.clipId, timeUs: timestamp };\n this.eventBus?.emit(MeframeEvent.ComposeFrameReady, {\n timeUs: timestamp,\n frameNumber: Math.floor(timestamp / frameDuration),\n renderTimeMs: 0,\n trackId: params.trackId,\n clipId: params.clipId,\n });\n\n params.onFrame(info);\n }\n\n await process();\n };\n\n process().catch((error) => {\n this.eventBus?.emit(MeframeEvent.ComposeFrameDropped, {\n timeUs: 0,\n reason: 'compose_slow',\n });\n reader.releaseLock();\n throw error;\n });\n }\n\n acceptEncodedChunks(\n stream: ReadableStream<EncodedVideoChunk | EncodedAudioChunk>,\n clipId: string,\n track: 'video' | 'audio'\n ): void {\n const reader = stream.getReader();\n const chunks: Array<EncodedVideoChunk | EncodedAudioChunk> = [];\n\n const process = async (): Promise<void> => {\n const { done, value } = await reader.read();\n if (done) {\n if (chunks.length > 0) {\n await this.l2Cache.put(clipId, chunks, track);\n }\n reader.releaseLock();\n return;\n }\n\n if (value) {\n chunks.push(value);\n this.eventBus?.emit(MeframeEvent.EncodeChunkReady, {\n timeUs: value.timestamp,\n durationUs: value.duration ?? 0,\n track,\n size: value.byteLength,\n });\n }\n\n await process();\n };\n\n process().catch((error) => {\n this.eventBus?.emit(MeframeEvent.EncodeChunkError, {\n timeUs: 0,\n track,\n error,\n });\n reader.releaseLock();\n throw error;\n });\n }\n\n acceptMixedAudio(\n stream: ReadableStream<AudioData>,\n metadata: { sampleRate: number; numberOfChannels: number }\n ): void {\n this.audioL1Cache.attachStream(stream, metadata);\n }\n\n putClipAudioData(\n clipId: string,\n audioData: AudioData,\n clipStartUs: TimeUs,\n clipDurationUs: TimeUs\n ): void {\n this.audioL1Cache.putClipAudioData(clipId, audioData, clipStartUs, clipDurationUs);\n }\n\n getClipPCM(clipId: string, startUs: TimeUs, endUs: TimeUs): Float32Array[] | null {\n return this.audioL1Cache.getPCM(clipId, startUs, endUs);\n }\n\n hasClipPCM(clipId: string): boolean {\n return this.audioL1Cache.hasClipPCM(clipId);\n }\n\n putMixedAudio(startUs: TimeUs, endUs: TimeUs, buffer: AudioBuffer): void {\n this.mixedAudioL1Cache.putMixed(startUs, endUs, buffer);\n }\n\n getMixedAudio(startUs: TimeUs, endUs: TimeUs): AudioBuffer | null {\n return this.mixedAudioL1Cache.getMixed(startUs, endUs);\n }\n\n resetAudioCache(): void {\n this.audioL1Cache.reset();\n this.mixedAudioL1Cache.clear();\n }\n\n async getFrame(timeUs: TimeUs, clipId: string): Promise<RcFrame | null> {\n const rcFrame = this.videoL1Cache.get(timeUs, clipId);\n if (rcFrame) {\n return rcFrame;\n }\n\n const decodeKey = this.makeRequestKey(clipId, timeUs);\n const pending = this.pendingFramePromises.get(decodeKey);\n if (pending) {\n const result = await pending;\n return result.frame;\n }\n\n const decodePromise = this.decodeFromL2(timeUs, clipId);\n const tracked = decodePromise.finally(() => {\n this.pendingFramePromises.delete(decodeKey);\n });\n\n this.pendingFramePromises.set(\n decodeKey,\n tracked.then((frame) => ({ frame, source: 'wait' as const, timestampUs: timeUs, clipId }))\n );\n return tracked;\n }\n\n waitForFrame(\n timeUs: TimeUs,\n clipId: string,\n options: WaitForFrameOptions = {}\n ): Promise<WaitForFrameResult> {\n const existing = this.videoL1Cache.get(timeUs, clipId);\n if (existing) {\n return Promise.resolve({ frame: existing, source: 'l1', timestampUs: timeUs, clipId });\n }\n\n const requestKey = this.makeRequestKey(clipId, timeUs);\n const existingPromise = this.pendingFramePromises.get(requestKey);\n if (existingPromise) {\n return existingPromise;\n }\n\n const promise = new Promise<WaitForFrameResult>((resolve, reject) => {\n const toleranceUs = Math.max(\n options.toleranceUs ?? DEFAULT_WAIT_TOLERANCE_US,\n DEFAULT_WAIT_TOLERANCE_US\n );\n\n const waiter: FrameWaiter = {\n requestKey,\n clipId,\n targetTimeUs: timeUs,\n resolve,\n reject,\n toleranceUs,\n };\n\n let waiters = this.frameWaiters.get(clipId);\n if (!waiters) {\n waiters = new Set();\n this.frameWaiters.set(clipId, waiters);\n }\n waiters.add(waiter);\n\n const signal = options.signal;\n if (signal) {\n const onAbort = (): void => {\n this.removeWaiter(waiter);\n this.cleanupWaiter(waiter);\n reject(new DOMException('Render aborted', 'AbortError'));\n };\n\n if (signal.aborted) {\n onAbort();\n return;\n }\n\n signal.addEventListener('abort', onAbort, { once: true });\n waiter.abortCleanup = () => {\n signal.removeEventListener('abort', onAbort);\n };\n }\n\n if (options.timeoutMs && options.timeoutMs > 0) {\n waiter.timeoutId = setTimeout(() => {\n this.removeWaiter(waiter);\n this.cleanupWaiter(waiter);\n reject(new Error('waitForFrame timeout'));\n }, options.timeoutMs);\n }\n });\n\n const trackedPromise = promise.finally(() => {\n this.pendingFramePromises.delete(requestKey);\n });\n\n this.pendingFramePromises.set(requestKey, trackedPromise);\n return trackedPromise;\n }\n\n async putGOP(gop: GOP): Promise<void> {\n if (!gop.clipId) {\n throw new Error('GOP clipId is required for clip-aware caching');\n }\n this.videoL1Cache.putGOP(gop, gop.clipId);\n this.encodeToL2(gop);\n }\n\n async putEncodedChunks(\n clipHash: string,\n chunks: Array<EncodedVideoChunk | EncodedAudioChunk>,\n track: 'video' | 'audio'\n ): Promise<void> {\n await this.l2Cache.put(clipHash, chunks, track);\n }\n\n invalidateRange(startUs: TimeUs, endUs: TimeUs, clipId?: string): void {\n this.videoL1Cache.invalidateRange(startUs, endUs, clipId);\n this.l2Cache.invalidateRange(startUs, endUs, clipId);\n }\n\n async invalidateClip(clipId: string): Promise<void> {\n this.videoL1Cache.invalidateRange(0, Infinity, clipId);\n await this.l2Cache.invalidateClip(clipId);\n }\n\n /**\n * Evict a clip from L1 cache\n */\n evictClip(clipId: string): void {\n this.videoL1Cache.evictClip(clipId);\n }\n\n /**\n * Check if a clip is cached in L1\n */\n isClipCached(clipId: string): boolean {\n return this.videoL1Cache.isClipCached(clipId);\n }\n\n /**\n * Wait for a clip to have minimum frames cached\n * Used by PlaybackController for buffering state\n */\n waitForClipReady(\n clipId: string,\n options: { minFrameCount?: number; timeoutMs?: number } = {}\n ): Promise<boolean> {\n const minFrameCount = options.minFrameCount ?? 30;\n const currentFrameCount = this.videoL1Cache.getClipFrameCount(clipId);\n\n if (currentFrameCount >= minFrameCount) {\n return Promise.resolve(true);\n }\n\n return new Promise<boolean>((resolve, reject) => {\n const waiter: ClipReadyWaiter = {\n clipId,\n minFrameCount,\n resolve,\n reject,\n };\n\n const waiters = this.clipReadyWaiters.get(clipId) || [];\n waiters.push(waiter);\n this.clipReadyWaiters.set(clipId, waiters);\n\n if (options.timeoutMs && options.timeoutMs > 0) {\n waiter.timeoutId = setTimeout(() => {\n const waiters = this.clipReadyWaiters.get(clipId);\n if (waiters) {\n const remaining = waiters.filter((w) => w !== waiter);\n if (remaining.length === 0) {\n this.clipReadyWaiters.delete(clipId);\n } else {\n this.clipReadyWaiters.set(clipId, remaining);\n }\n }\n resolve(false);\n }, options.timeoutMs);\n }\n });\n }\n\n async clear(): Promise<void> {\n this.videoL1Cache.clear();\n await this.l2Cache.clear();\n }\n\n getMetadata() {\n return {\n l1: this.videoL1Cache.getMetadata(),\n l2: this.l2Cache.getMetadata(),\n };\n }\n\n private async decodeFromL2(timeUs: TimeUs, clipId: string): Promise<RcFrame | null> {\n const encodedChunk = await this.l2Cache.get(timeUs, clipId);\n if (!encodedChunk) {\n return null;\n }\n\n return null;\n }\n\n private async encodeToL2(_gop: GOP): Promise<void> {\n // Placeholder for L2 encoding\n }\n\n private notifyFrameWaiters(\n clipId: string,\n timestampUs: TimeUs,\n frameDurationUs: TimeUs,\n frame: RcFrame\n ): void {\n const waiters = this.frameWaiters.get(clipId);\n if (!waiters || waiters.size === 0) {\n return;\n }\n\n const resolved: FrameWaiter[] = [];\n\n for (const waiter of waiters) {\n const matches = this.matchesTimestamp(\n waiter.targetTimeUs,\n timestampUs,\n frameDurationUs,\n waiter.toleranceUs\n );\n\n if (!matches) continue;\n\n resolved.push(waiter);\n this.cleanupWaiter(waiter);\n\n waiter.resolve({\n frame,\n source: 'wait',\n timestampUs,\n clipId,\n });\n }\n\n for (const waiter of resolved) {\n waiters.delete(waiter);\n }\n\n if (waiters.size === 0) {\n this.frameWaiters.delete(clipId);\n }\n\n this.notifyClipReadyWaiters(clipId);\n }\n\n private notifyClipReadyWaiters(clipId: string): void {\n const waiters = this.clipReadyWaiters.get(clipId);\n if (!waiters || waiters.length === 0) {\n return;\n }\n\n const frameCount = this.videoL1Cache.getClipFrameCount(clipId);\n const resolved: ClipReadyWaiter[] = [];\n\n for (const waiter of waiters) {\n if (frameCount >= waiter.minFrameCount) {\n resolved.push(waiter);\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n }\n waiter.resolve(true);\n }\n }\n\n const remaining = waiters.filter((w) => !resolved.includes(w));\n if (remaining.length === 0) {\n this.clipReadyWaiters.delete(clipId);\n } else {\n this.clipReadyWaiters.set(clipId, remaining);\n }\n }\n\n private cleanupWaiter(waiter: FrameWaiter): void {\n if (waiter.timeoutId) {\n clearTimeout(waiter.timeoutId);\n waiter.timeoutId = undefined;\n }\n\n if (waiter.abortCleanup) {\n waiter.abortCleanup();\n waiter.abortCleanup = undefined;\n }\n }\n\n private removeWaiter(waiter: FrameWaiter): void {\n const waiters = this.frameWaiters.get(waiter.clipId);\n if (!waiters) return;\n\n waiters.delete(waiter);\n if (waiters.size === 0) {\n this.frameWaiters.delete(waiter.clipId);\n }\n }\n\n private matchesTimestamp(\n targetTimeUs: TimeUs,\n actualTimeUs: TimeUs,\n frameDurationUs: TimeUs,\n toleranceUs: TimeUs\n ): boolean {\n if (targetTimeUs === actualTimeUs) return true;\n\n const delta = Math.abs(targetTimeUs - actualTimeUs);\n if (delta <= toleranceUs) {\n return true;\n }\n\n if (actualTimeUs >= targetTimeUs && actualTimeUs < targetTimeUs + frameDurationUs) {\n return true;\n }\n\n return false;\n }\n\n private makeRequestKey(clipId: string, timeUs: TimeUs): string {\n return `${clipId}:${timeUs}`;\n }\n}\n"],"names":["waiters"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,cAiE5B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc;IACzB;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IA8BH;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;qCAyBmC,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE;;;;;;;;;;;;;;IAezD;;;OAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BK,CAAC"}
|
package/dist/config/defaults.js
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
import { CANVAS_PRESETS } from "./presets.js";
|
|
2
|
-
function getWorkerConfig() {
|
|
3
|
-
const isDev = typeof process !== "undefined" && process.env.NODE_ENV !== "production" || typeof window !== "undefined" && window.location?.hostname === "localhost";
|
|
4
|
-
return {
|
|
5
|
-
workerBaseUrl: isDev ? "/src/stages" : "/dist/stages",
|
|
6
|
-
workerExtension: isDev ? ".ts" : ".js"
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
2
|
const DEFAULT_CONFIG = {
|
|
10
3
|
global: {
|
|
11
4
|
logLevel: "info",
|
|
12
5
|
enablePerfMonitor: false,
|
|
13
|
-
...getWorkerConfig(),
|
|
14
6
|
defaultCanvasWidth: CANVAS_PRESETS.MOBILE_PORTRAIT.width,
|
|
15
7
|
defaultCanvasHeight: CANVAS_PRESETS.MOBILE_PORTRAIT.height,
|
|
16
|
-
defaultFps: 30
|
|
8
|
+
defaultFps: 30,
|
|
9
|
+
workerPath: "/meframe-workers"
|
|
17
10
|
},
|
|
18
11
|
load: {
|
|
19
12
|
backpressure: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.js","sources":["../../src/config/defaults.ts"],"sourcesContent":["import type { ResolvedConfig } from './types';\nimport { CANVAS_PRESETS } from './presets';\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"defaults.js","sources":["../../src/config/defaults.ts"],"sourcesContent":["import type { ResolvedConfig } from './types';\nimport { CANVAS_PRESETS } from './presets';\n\n/**\n * Default configuration values based on mobile 1080p30 memory budget\n *\n * Note: Canvas dimensions are fixed for the entire project.\n * All video clips will be scaled/fitted to this resolution.\n * Choose based on your primary content type:\n * - 720×1280: Mobile vertical video\n * - 1080×1920: HD vertical video (TikTok, Instagram Reels) ← default\n * - 1920×1080: HD horizontal video (YouTube)\n */\nexport const DEFAULT_CONFIG: ResolvedConfig = {\n global: {\n logLevel: 'info',\n enablePerfMonitor: false,\n defaultCanvasWidth: CANVAS_PRESETS.MOBILE_PORTRAIT.width,\n defaultCanvasHeight: CANVAS_PRESETS.MOBILE_PORTRAIT.height,\n defaultFps: 30,\n workerPath: '/meframe-workers',\n },\n\n load: {\n backpressure: {\n highWaterMark: 64 * 1024, // 64 KB\n stallTimeoutMs: 500,\n },\n retry: {\n maxAttempts: 3,\n baseDelayMs: 500,\n },\n window: {\n maxInflightPerClip: 1,\n maxInflight: 4,\n chunkSize: 1 * 1024 * 1024, // 1 MB\n },\n },\n\n demux: {\n backpressure: {\n highWaterMark: 10, // 10 EncodedChunks\n },\n },\n\n decode: {\n video: {\n backpressure: {\n highWaterMark: 4, // 4 EncodedVideoChunks\n decodeQueueThreshold: 16, // Pause when decoder has 16+ chunks\n },\n maxGOPs: 4, // Cache 4 GOPs for seeking\n },\n audio: {\n backpressure: {\n highWaterMark: 20, // 20 EncodedAudioChunks\n },\n },\n },\n\n compose: {\n visual: {},\n audio: {\n enableDucking: false, // Default: no ducking\n },\n },\n\n encode: {\n video: {},\n audio: {},\n },\n\n cache: {\n l1: {},\n l2: {},\n },\n\n mux: {},\n};\n\n/**\n * Tuning presets for common scenarios\n */\nexport const TUNING_PRESETS = {\n /**\n * Low-latency live streaming\n * Minimize buffering for real-time playback\n */\n lowLatency: {\n global: {\n logLevel: 'info' as const,\n },\n load: {\n backpressure: {\n highWaterMark: 16 * 1024, // 16 KB\n },\n },\n demux: {\n backpressure: {\n highWaterMark: 3,\n },\n },\n decode: {\n video: {\n backpressure: {\n highWaterMark: 2,\n decodeQueueThreshold: 4,\n },\n },\n audio: {\n backpressure: {\n highWaterMark: 10,\n },\n },\n },\n },\n\n /**\n * 4K60 playback/export\n * Larger buffers for high bitrate content\n */\n highQuality: {\n global: {\n logLevel: 'info' as const,\n },\n load: {\n backpressure: {\n highWaterMark: 256 * 1024, // 256 KB\n },\n retry: {\n maxAttempts: 3,\n baseDelayMs: 500,\n },\n },\n demux: {\n backpressure: {\n highWaterMark: 20,\n },\n },\n decode: {\n video: {\n backpressure: {\n highWaterMark: 8,\n decodeQueueThreshold: 24,\n },\n codecHints: ['h264', 'hevc'] as ('h264' | 'hevc')[],\n },\n audio: {\n backpressure: {\n highWaterMark: 30,\n },\n },\n },\n cache: {\n l2: {\n quotaGb: 1,\n },\n },\n },\n\n /**\n * Batch offline transcode\n * Maximum throughput, memory not a concern\n */\n offline: {\n global: {\n logLevel: 'warn' as const,\n },\n load: {\n backpressure: {\n highWaterMark: 512 * 1024, // 512 KB\n },\n },\n demux: {\n backpressure: {\n highWaterMark: 25,\n },\n },\n decode: {\n video: {\n backpressure: {\n highWaterMark: 12,\n decodeQueueThreshold: 32,\n },\n },\n audio: {\n backpressure: {\n highWaterMark: 40,\n },\n },\n },\n },\n} as const;\n"],"names":[],"mappings":"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"}
|
package/dist/config/types.d.ts
CHANGED
|
@@ -23,12 +23,12 @@ export interface MeframeConfig {
|
|
|
23
23
|
/** Global cross-stage options */
|
|
24
24
|
global?: {
|
|
25
25
|
logLevel?: 'verbose' | 'info' | 'warn' | 'error';
|
|
26
|
-
workerBaseUrl?: string;
|
|
27
|
-
workerExtension?: string;
|
|
28
26
|
enablePerfMonitor?: boolean;
|
|
29
27
|
defaultCanvasWidth?: number;
|
|
30
28
|
defaultCanvasHeight?: number;
|
|
31
29
|
defaultFps?: number;
|
|
30
|
+
/** Worker files base path (must match meframePlugin workerPath) */
|
|
31
|
+
workerPath?: string;
|
|
32
32
|
};
|
|
33
33
|
/** Resource loading stage - fetch → ReadableStream */
|
|
34
34
|
load?: {
|
|
@@ -129,12 +129,11 @@ export interface ConfigLoaderOptions {
|
|
|
129
129
|
export interface ResolvedConfig {
|
|
130
130
|
global: {
|
|
131
131
|
logLevel: 'verbose' | 'info' | 'warn' | 'error';
|
|
132
|
-
workerBaseUrl?: string;
|
|
133
|
-
workerExtension?: string;
|
|
134
132
|
enablePerfMonitor: boolean;
|
|
135
133
|
defaultCanvasWidth: number;
|
|
136
134
|
defaultCanvasHeight: number;
|
|
137
135
|
defaultFps: number;
|
|
136
|
+
workerPath: string;
|
|
138
137
|
};
|
|
139
138
|
load: {
|
|
140
139
|
backpressure: Required<LoadBackpressure>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,iCAAiC;IACjC,MAAM,CAAC,EAAE;QACP,QAAQ,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;QACjD,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { IPlaybackController, PlaybackState, PlaybackOptions,
|
|
1
|
+
import { IPlaybackController, PlaybackState, PlaybackOptions, IEventBus } from './types';
|
|
2
2
|
import { TimeUs } from '../model/types';
|
|
3
3
|
import { GlobalAudioSession } from '../stages/compose/GlobalAudioSession';
|
|
4
|
+
import { Orchestrator } from '../orchestrator';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Playback controller for preview
|
|
@@ -26,7 +27,7 @@ export declare class PlaybackController implements IPlaybackController {
|
|
|
26
27
|
private audioGainNode;
|
|
27
28
|
private audioSession;
|
|
28
29
|
private isBuffering;
|
|
29
|
-
constructor(orchestrator:
|
|
30
|
+
constructor(orchestrator: Orchestrator, eventBus: IEventBus, options: PlaybackOptions);
|
|
30
31
|
play(): void;
|
|
31
32
|
private startPlayback;
|
|
32
33
|
pause(): void;
|
|
@@ -42,6 +43,7 @@ export declare class PlaybackController implements IPlaybackController {
|
|
|
42
43
|
get playing(): boolean;
|
|
43
44
|
setAudioSession(session: GlobalAudioSession): void;
|
|
44
45
|
resume(): void;
|
|
46
|
+
private setupListeners;
|
|
45
47
|
private playbackLoop;
|
|
46
48
|
private updateTime;
|
|
47
49
|
private renderCurrentFrame;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlaybackController.d.ts","sourceRoot":"","sources":["../../src/controllers/PlaybackController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"PlaybackController.d.ts","sourceRoot":"","sources":["../../src/controllers/PlaybackController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC9F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAG7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,mBAAmB;IAC5D,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,GAAG,CAA+D;IAG1E,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,YAAY,CAAO;IAC3B,OAAO,CAAC,MAAM,CAAO;IACrB,OAAO,CAAC,IAAI,CAAS;IAGrB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,SAAS,CAAK;IAGtB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,YAAY,CAAmC;IAGvD,OAAO,CAAC,WAAW,CAAS;gBAEhB,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe;IAyCrF,IAAI,IAAI,IAAI;YAME,aAAa;IAgC3B,KAAK,IAAI,IAAI;IAeb,IAAI,IAAI,IAAI;IAiBN,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+CzC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAa3B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAS/B,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAQ7B,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;IAK5B,IAAI,WAAW,IAAI,MAAM,CAMxB;IAED,IAAI,QAAQ,IAAI,MAAM,CAOrB;IAED,IAAI,KAAK,IAAI,aAAa,CAEzB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,eAAe,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IAKlD,MAAM,IAAI,IAAI;IAId,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,YAAY;IAwCpB,OAAO,CAAC,UAAU;YAuBJ,kBAAkB;YAyBlB,uBAAuB;IA8CrC,OAAO,CAAC,SAAS;IAKjB,OAAO,IAAI,IAAI;YAID,kBAAkB;IAWhC,OAAO,CAAC,kBAAkB;IAmC1B,OAAO,CAAC,iBAAiB;CAW1B"}
|
|
@@ -51,6 +51,7 @@ class PlaybackController {
|
|
|
51
51
|
if (options.autoStart) {
|
|
52
52
|
this.play();
|
|
53
53
|
}
|
|
54
|
+
this.setupListeners();
|
|
54
55
|
}
|
|
55
56
|
// Playback control
|
|
56
57
|
play() {
|
|
@@ -118,7 +119,7 @@ class PlaybackController {
|
|
|
118
119
|
await this.orchestrator.ensureClipCache(clamped);
|
|
119
120
|
const ready = await this.orchestrator.waitForClipReady(clamped, {
|
|
120
121
|
minFrameCount: 10,
|
|
121
|
-
timeoutMs:
|
|
122
|
+
timeoutMs: 3e3
|
|
122
123
|
});
|
|
123
124
|
if (!ready) {
|
|
124
125
|
console.warn("[PlaybackController] Seek buffering timeout");
|
|
@@ -197,6 +198,11 @@ class PlaybackController {
|
|
|
197
198
|
resume() {
|
|
198
199
|
this.play();
|
|
199
200
|
}
|
|
201
|
+
setupListeners() {
|
|
202
|
+
this.orchestrator.on(MeframeEvent.CacheCover, (event) => {
|
|
203
|
+
this.renderCurrentFrame(event.timeUs);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
200
206
|
// Private methods
|
|
201
207
|
playbackLoop() {
|
|
202
208
|
if (this._state !== "playing") {
|
|
@@ -206,7 +212,6 @@ class PlaybackController {
|
|
|
206
212
|
}
|
|
207
213
|
return;
|
|
208
214
|
}
|
|
209
|
-
const frameStartTime = performance.now();
|
|
210
215
|
this.rafId = requestAnimationFrame(async () => {
|
|
211
216
|
if (this._state !== "playing") {
|
|
212
217
|
return;
|
|
@@ -224,16 +229,6 @@ class PlaybackController {
|
|
|
224
229
|
}
|
|
225
230
|
this.lastFrameTime = now;
|
|
226
231
|
this.frameCount++;
|
|
227
|
-
const primaryClip = this.orchestrator.compositionModel?.primaryClip;
|
|
228
|
-
if (primaryClip) {
|
|
229
|
-
this.eventBus.emit(MeframeEvent.ComposeFrameReady, {
|
|
230
|
-
timeUs: this._currentTimeUs,
|
|
231
|
-
frameNumber: this.frameCount,
|
|
232
|
-
renderTimeMs: now - frameStartTime,
|
|
233
|
-
trackId: primaryClip.trackId || "main",
|
|
234
|
-
clipId: primaryClip.id
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
232
|
this.playbackLoop();
|
|
238
233
|
});
|
|
239
234
|
}
|
|
@@ -284,7 +279,6 @@ class PlaybackController {
|
|
|
284
279
|
this.isBuffering = true;
|
|
285
280
|
this._state = "buffering";
|
|
286
281
|
this.eventBus.emit(MeframeEvent.PlaybackBuffering);
|
|
287
|
-
console.log(`[PlaybackController] Buffering at ${timeUs}µs (clip transition)`);
|
|
288
282
|
try {
|
|
289
283
|
await this.orchestrator.ensureClipCache(timeUs);
|
|
290
284
|
const ready = await this.orchestrator.waitForClipReady(timeUs, {
|