@meframe/core 0.0.31 → 0.0.33
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 -2
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +3 -2
- package/dist/Meframe.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +12 -17
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +18 -280
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/l1/AudioL1Cache.d.ts +36 -19
- package/dist/cache/l1/AudioL1Cache.d.ts.map +1 -1
- package/dist/cache/l1/AudioL1Cache.js +182 -282
- package/dist/cache/l1/AudioL1Cache.js.map +1 -1
- package/dist/controllers/PlaybackController.d.ts +5 -3
- package/dist/controllers/PlaybackController.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.js +58 -16
- package/dist/controllers/PlaybackController.js.map +1 -1
- package/dist/event/events.d.ts +1 -1
- package/dist/event/events.d.ts.map +1 -1
- package/dist/event/events.js.map +1 -1
- package/dist/model/CompositionModel.d.ts +8 -0
- package/dist/model/CompositionModel.d.ts.map +1 -1
- package/dist/model/CompositionModel.js +18 -0
- package/dist/model/CompositionModel.js.map +1 -1
- package/dist/model/types.d.ts +0 -4
- package/dist/model/types.d.ts.map +1 -1
- package/dist/model/types.js.map +1 -1
- package/dist/orchestrator/ExportScheduler.d.ts +10 -0
- package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
- package/dist/orchestrator/ExportScheduler.js +66 -83
- package/dist/orchestrator/ExportScheduler.js.map +1 -1
- package/dist/orchestrator/GlobalAudioSession.d.ts +35 -28
- package/dist/orchestrator/GlobalAudioSession.d.ts.map +1 -1
- package/dist/orchestrator/GlobalAudioSession.js +213 -422
- package/dist/orchestrator/GlobalAudioSession.js.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.d.ts +3 -3
- package/dist/orchestrator/OnDemandVideoSession.d.ts.map +1 -1
- package/dist/orchestrator/OnDemandVideoSession.js +4 -4
- package/dist/orchestrator/OnDemandVideoSession.js.map +1 -1
- package/dist/orchestrator/Orchestrator.d.ts +11 -4
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +75 -68
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/VideoClipSession.d.ts +0 -2
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
- package/dist/orchestrator/VideoClipSession.js +0 -49
- package/dist/orchestrator/VideoClipSession.js.map +1 -1
- package/dist/stages/compose/OfflineAudioMixer.d.ts.map +1 -1
- package/dist/stages/compose/OfflineAudioMixer.js +13 -18
- package/dist/stages/compose/OfflineAudioMixer.js.map +1 -1
- package/dist/stages/decode/AudioChunkDecoder.js +169 -0
- package/dist/stages/decode/AudioChunkDecoder.js.map +1 -0
- package/dist/stages/demux/MP3FrameParser.js +186 -0
- package/dist/stages/demux/MP3FrameParser.js.map +1 -0
- package/dist/stages/load/ResourceLoader.d.ts +49 -30
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +255 -189
- package/dist/stages/load/ResourceLoader.js.map +1 -1
- package/dist/stages/load/TaskManager.d.ts +4 -0
- package/dist/stages/load/TaskManager.d.ts.map +1 -1
- package/dist/stages/load/TaskManager.js +11 -0
- package/dist/stages/load/TaskManager.js.map +1 -1
- package/dist/stages/load/types.d.ts +1 -0
- package/dist/stages/load/types.d.ts.map +1 -1
- package/dist/utils/audio-data.d.ts +16 -0
- package/dist/utils/audio-data.d.ts.map +1 -0
- package/dist/utils/audio-data.js +111 -0
- package/dist/utils/audio-data.js.map +1 -0
- package/package.json +1 -1
- package/dist/cache/resource/ImageBitmapCache.d.ts +0 -65
- package/dist/cache/resource/ImageBitmapCache.d.ts.map +0 -1
- package/dist/cache/resource/ImageBitmapCache.js +0 -101
- package/dist/cache/resource/ImageBitmapCache.js.map +0 -1
package/dist/Meframe.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MeframeConfig, ResolvedConfig, MeframeState, ExportOptions } from './types';
|
|
2
|
-
import { CompositionModelData, CompositionPatch,
|
|
2
|
+
import { CompositionModelData, CompositionPatch, TimeUs } from './model/types';
|
|
3
3
|
import { PreviewHandle } from './controllers/types';
|
|
4
4
|
import { CompositionModel } from './model/CompositionModel';
|
|
5
5
|
import { PluginManager } from './plugins/PluginManager';
|
|
@@ -34,7 +34,7 @@ export declare class Meframe {
|
|
|
34
34
|
/**
|
|
35
35
|
* Set the composition model
|
|
36
36
|
*/
|
|
37
|
-
setCompositionModel(model: CompositionModel | CompositionModelData
|
|
37
|
+
setCompositionModel(model: CompositionModel | CompositionModelData): Promise<void>;
|
|
38
38
|
/**
|
|
39
39
|
* Apply a patch to the composition model
|
|
40
40
|
*/
|
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,
|
|
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;AAK5D,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEpE,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,CAAmB;IAC3C,OAAO,CAAC,MAAM,CAAC,CAAsC;IAErD,OAAO;IAoBP;;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;IAuBxF;;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;IA4BjB;;;OAGG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA+CnD;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAYjC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB9B;;OAEG;IACH,OAAO,CAAC,QAAQ;IAYhB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;OAEG;IACH,OAAO,CAAC,WAAW;CAWpB"}
|
package/dist/Meframe.js
CHANGED
|
@@ -71,10 +71,10 @@ class Meframe {
|
|
|
71
71
|
/**
|
|
72
72
|
* Set the composition model
|
|
73
73
|
*/
|
|
74
|
-
async setCompositionModel(model
|
|
74
|
+
async setCompositionModel(model) {
|
|
75
75
|
this.ensureNotDestroyed();
|
|
76
76
|
const compositionModel = model instanceof CompositionModel ? model : new CompositionModel(model);
|
|
77
|
-
await this.orchestrator.setCompositionModel(compositionModel
|
|
77
|
+
await this.orchestrator.setCompositionModel(compositionModel);
|
|
78
78
|
this.model = compositionModel;
|
|
79
79
|
this.setState("ready");
|
|
80
80
|
this.eventBus.emit(MeframeEvent.Ready, {
|
|
@@ -123,6 +123,7 @@ class Meframe {
|
|
|
123
123
|
*/
|
|
124
124
|
async export(options) {
|
|
125
125
|
this.ensureReady();
|
|
126
|
+
this.playbackController?.pause();
|
|
126
127
|
this.setState("exporting");
|
|
127
128
|
try {
|
|
128
129
|
const model = this.model;
|
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 {\n CompositionModelData,\n CompositionPatch,\n SetCompositionModelOptions,\n TimeUs,\n} 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 { ExportController } from './controllers/ExportController';\nimport { PluginManager } from './plugins/PluginManager';\nimport { EventBus } from './event/EventBus';\nimport { MeframeEvent, type EventPayloadMap } from './event/events';\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 exportController: ExportController;\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 projectId: config.global.projectId,\n maxWorkers: (config as any).maxWorkers,\n cacheConfig: config.cache as any,\n eventBus: this.eventBus,\n workerPath: config.global.workerPath,\n workerExtension: config.global.workerExtension,\n });\n\n this.exportController = new ExportController(this.orchestrator);\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(\n model: CompositionModel | CompositionModelData,\n options?: SetCompositionModelOptions\n ): 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, options);\n this.model = compositionModel;\n\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 // await this.playbackController?.renderCover();\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 // Render initial frame\n this.playbackController.renderCover();\n\n // Return preview handle\n return this.playbackController;\n }\n\n /**\n * Export the composition\n * Uses ExportController for direct export\n */\n async export(options: ExportOptions): Promise<Blob> {\n this.ensureReady();\n this.setState('exporting');\n\n try {\n const model = this.model;\n if (!model) {\n throw new Error('No composition model set');\n }\n\n const width = options.width || (model as any).renderConfig?.width || 720;\n const height = options.height || (model as any).renderConfig?.height || 1280;\n const fps = options.fps || model.fps || 30;\n\n this.eventBus.emit(MeframeEvent.ExportStart, {\n format: options.format || 'mp4',\n width,\n height,\n fps,\n durationUs: model.durationUs,\n });\n\n // Delegate to ExportController\n const blob = await this.exportController.export(model, options);\n\n this.setState('ready');\n this.eventBus.emit(MeframeEvent.ExportComplete, {\n size: blob.size,\n durationMs: model.durationUs / 1000,\n format: options.format || 'mp4',\n });\n this.eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 1,\n });\n\n return blob;\n } catch (error) {\n this.setState('error');\n this.eventBus.emit(MeframeEvent.ExportError, {\n error: error as Error,\n stage: 'export',\n });\n throw error;\n }\n }\n\n /**\n * Get current playback time\n */\n getCurrentTime(): number {\n return this.playbackController?.currentTimeUs || 0;\n }\n\n /**\n * Clear all L1/L2 cache data\n * Useful for debugging or forcing re-render\n */\n async clearCache(): Promise<void> {\n this.ensureReady();\n\n // Stop playback first\n this.playbackController?.stop();\n\n // Clear cache through CacheManager\n await this.orchestrator.cacheManager.clear();\n\n console.log('[Meframe] Cache cleared successfully');\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 // 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":[],"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 { ExportController } from './controllers/ExportController';\nimport { PluginManager } from './plugins/PluginManager';\nimport { EventBus } from './event/EventBus';\nimport { MeframeEvent, type EventPayloadMap } from './event/events';\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 exportController: ExportController;\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 projectId: config.global.projectId,\n maxWorkers: (config as any).maxWorkers,\n cacheConfig: config.cache as any,\n eventBus: this.eventBus,\n workerPath: config.global.workerPath,\n workerExtension: config.global.workerExtension,\n });\n\n this.exportController = new ExportController(this.orchestrator);\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\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 // await this.playbackController?.renderCover();\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 // Render initial frame\n this.playbackController.renderCover();\n\n // Return preview handle\n return this.playbackController;\n }\n\n /**\n * Export the composition\n * Uses ExportController for direct export\n */\n async export(options: ExportOptions): Promise<Blob> {\n this.ensureReady();\n this.playbackController?.pause();\n this.setState('exporting');\n\n try {\n const model = this.model;\n if (!model) {\n throw new Error('No composition model set');\n }\n\n const width = options.width || (model as any).renderConfig?.width || 720;\n const height = options.height || (model as any).renderConfig?.height || 1280;\n const fps = options.fps || model.fps || 30;\n\n this.eventBus.emit(MeframeEvent.ExportStart, {\n format: options.format || 'mp4',\n width,\n height,\n fps,\n durationUs: model.durationUs,\n });\n\n // Delegate to ExportController\n const blob = await this.exportController.export(model, options);\n\n this.setState('ready');\n this.eventBus.emit(MeframeEvent.ExportComplete, {\n size: blob.size,\n durationMs: model.durationUs / 1000,\n format: options.format || 'mp4',\n });\n this.eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 1,\n });\n\n return blob;\n } catch (error) {\n this.setState('error');\n this.eventBus.emit(MeframeEvent.ExportError, {\n error: error as Error,\n stage: 'export',\n });\n throw error;\n }\n }\n\n /**\n * Get current playback time\n */\n getCurrentTime(): number {\n return this.playbackController?.currentTimeUs || 0;\n }\n\n /**\n * Clear all L1/L2 cache data\n * Useful for debugging or forcing re-render\n */\n async clearCache(): Promise<void> {\n this.ensureReady();\n\n // Stop playback first\n this.playbackController?.stop();\n\n // Clear cache through CacheManager\n await this.orchestrator.cacheManager.clear();\n\n console.log('[Meframe] Cache cleared successfully');\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 // 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":[],"mappings":"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"}
|
|
@@ -6,7 +6,6 @@ import { EventPayloadMap } from '../event/events';
|
|
|
6
6
|
import { ResourceCache } from './resource/ResourceCache';
|
|
7
7
|
import { MP4IndexCache } from './resource/MP4IndexCache';
|
|
8
8
|
import { AudioSampleCache } from './resource/AudioSampleCache';
|
|
9
|
-
import { ImageBitmapCache } from './resource/ImageBitmapCache';
|
|
10
9
|
import { MP4Index } from '../stages/demux/types';
|
|
11
10
|
|
|
12
11
|
interface CacheManagerConfig {
|
|
@@ -27,7 +26,6 @@ interface CacheManagerConfig {
|
|
|
27
26
|
* - ResourceCache (OPFS) for original MP4 files
|
|
28
27
|
* - MP4IndexCache (RAM) for GOP/Sample indexes
|
|
29
28
|
* - AudioSampleCache (RAM) for extracted audio chunks
|
|
30
|
-
* - ImageBitmapCache (RAM) for decoded images
|
|
31
29
|
*/
|
|
32
30
|
export declare class CacheManager {
|
|
33
31
|
readonly videoL1Cache: VideoL1Cache;
|
|
@@ -35,36 +33,39 @@ export declare class CacheManager {
|
|
|
35
33
|
readonly resourceCache: ResourceCache;
|
|
36
34
|
readonly mp4IndexCache: MP4IndexCache;
|
|
37
35
|
readonly audioSampleCache: AudioSampleCache;
|
|
38
|
-
readonly imageBitmapCache: ImageBitmapCache;
|
|
39
36
|
private clipReadyWaiters;
|
|
40
37
|
private eventBus?;
|
|
41
38
|
constructor(config: CacheManagerConfig, eventBus?: EventBus<EventPayloadMap>);
|
|
42
39
|
init(): Promise<void>;
|
|
43
40
|
/**
|
|
44
|
-
* Set window center for L1 cache management
|
|
45
|
-
* L1 cache uses center ±
|
|
41
|
+
* Set window center for L1 cache management (unified for video and audio)
|
|
42
|
+
* L1 cache uses center ±3.5s window for video, ±5s for audio
|
|
46
43
|
*/
|
|
47
44
|
setWindow(centerTimeUs: TimeUs): void;
|
|
48
45
|
/**
|
|
49
46
|
* Check if resource exists in OPFS cache
|
|
50
47
|
*/
|
|
51
48
|
hasResourceInCache(resourceId: string): Promise<boolean>;
|
|
49
|
+
/**
|
|
50
|
+
* Read byte range from resource (for GOP-level access)
|
|
51
|
+
*/
|
|
52
|
+
readResourceRange(resourceId: string, start: number, end: number): Promise<ArrayBuffer>;
|
|
52
53
|
/**
|
|
53
54
|
* Get MP4 index for a resource
|
|
54
55
|
*/
|
|
55
56
|
getMP4Index(resourceId: string): MP4Index | null;
|
|
56
|
-
|
|
57
|
-
sampleRate: number;
|
|
58
|
-
numberOfChannels: number;
|
|
59
|
-
}): void;
|
|
60
|
-
putClipAudioData(clipId: string, audioData: AudioData, clipDurationUs: TimeUs): void;
|
|
61
|
-
getClipPCM(clipId: string, startUs: TimeUs, endUs: TimeUs): Float32Array[] | null;
|
|
57
|
+
putClipAudioData(clipId: string, audioData: AudioData, clipDurationUs: TimeUs, globalTimeUs?: TimeUs): void;
|
|
62
58
|
getClipPCMWithMetadata(clipId: string, startUs: TimeUs, endUs: TimeUs): {
|
|
63
59
|
planes: Float32Array[];
|
|
64
60
|
sampleRate: number;
|
|
65
61
|
numberOfChannels: number;
|
|
66
62
|
} | null;
|
|
67
63
|
hasClipPCM(clipId: string): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Check if sufficient PCM data exists for the requested time window
|
|
66
|
+
* Returns true only if at least 80% of requested duration is available
|
|
67
|
+
*/
|
|
68
|
+
hasWindowPCM(clipId: string, startUs: TimeUs, endUs: TimeUs): boolean;
|
|
68
69
|
resetAudioCache(): void;
|
|
69
70
|
clearClipAudioData(clipId: string): void;
|
|
70
71
|
/**
|
|
@@ -83,12 +84,6 @@ export declare class CacheManager {
|
|
|
83
84
|
* @param clipId - Clip identifier
|
|
84
85
|
*/
|
|
85
86
|
getFrame(timeUs: TimeUs, clipId: string): RcFrame | null;
|
|
86
|
-
/**
|
|
87
|
-
* Wait for frame to be available in cache
|
|
88
|
-
* @param timeUs - Clip-relative timestamp (0-based)
|
|
89
|
-
* @param clipId - Clip identifier
|
|
90
|
-
* @param options - Wait options (timeout, tolerance, etc.)
|
|
91
|
-
*/
|
|
92
87
|
invalidateClip(clipId: string): Promise<void>;
|
|
93
88
|
/**
|
|
94
89
|
* Evict a clip from L1 cache
|
|
@@ -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,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAIvD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,
|
|
1
|
+
{"version":3,"file":"CacheManager.d.ts","sourceRoot":"","sources":["../../src/cache/CacheManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,14 +6,12 @@ import { OPFSManager } from "./storage/opfs/OPFSManager.js";
|
|
|
6
6
|
import { ResourceCache } from "./resource/ResourceCache.js";
|
|
7
7
|
import { MP4IndexCache } from "./resource/MP4IndexCache.js";
|
|
8
8
|
import { AudioSampleCache } from "./resource/AudioSampleCache.js";
|
|
9
|
-
import { ImageBitmapCache } from "./resource/ImageBitmapCache.js";
|
|
10
9
|
class CacheManager {
|
|
11
10
|
videoL1Cache;
|
|
12
11
|
audioL1Cache;
|
|
13
12
|
resourceCache;
|
|
14
13
|
mp4IndexCache;
|
|
15
14
|
audioSampleCache;
|
|
16
|
-
imageBitmapCache;
|
|
17
15
|
clipReadyWaiters = /* @__PURE__ */ new Map();
|
|
18
16
|
eventBus;
|
|
19
17
|
constructor(config, eventBus) {
|
|
@@ -23,18 +21,18 @@ class CacheManager {
|
|
|
23
21
|
this.resourceCache = new ResourceCache(opfsManager, config.resource.projectId);
|
|
24
22
|
this.mp4IndexCache = new MP4IndexCache();
|
|
25
23
|
this.audioSampleCache = new AudioSampleCache();
|
|
26
|
-
this.imageBitmapCache = new ImageBitmapCache(100);
|
|
27
24
|
this.eventBus = eventBus;
|
|
28
25
|
}
|
|
29
26
|
async init() {
|
|
30
27
|
await this.resourceCache.init();
|
|
31
28
|
}
|
|
32
29
|
/**
|
|
33
|
-
* Set window center for L1 cache management
|
|
34
|
-
* L1 cache uses center ±
|
|
30
|
+
* Set window center for L1 cache management (unified for video and audio)
|
|
31
|
+
* L1 cache uses center ±3.5s window for video, ±5s for audio
|
|
35
32
|
*/
|
|
36
33
|
setWindow(centerTimeUs) {
|
|
37
34
|
this.videoL1Cache.setWindow(centerTimeUs);
|
|
35
|
+
this.audioL1Cache.setWindow(centerTimeUs);
|
|
38
36
|
}
|
|
39
37
|
/**
|
|
40
38
|
* Check if resource exists in OPFS cache
|
|
@@ -42,181 +40,20 @@ class CacheManager {
|
|
|
42
40
|
async hasResourceInCache(resourceId) {
|
|
43
41
|
return await this.resourceCache.hasResource(resourceId);
|
|
44
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Read byte range from resource (for GOP-level access)
|
|
45
|
+
*/
|
|
46
|
+
async readResourceRange(resourceId, start, end) {
|
|
47
|
+
return await this.resourceCache.readRange(resourceId, start, end);
|
|
48
|
+
}
|
|
45
49
|
/**
|
|
46
50
|
* Get MP4 index for a resource
|
|
47
51
|
*/
|
|
48
52
|
getMP4Index(resourceId) {
|
|
49
53
|
return this.mp4IndexCache.get(resourceId);
|
|
50
54
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// params: {
|
|
54
|
-
// clipId: string;
|
|
55
|
-
// trackId: string;
|
|
56
|
-
// fps: number;
|
|
57
|
-
// clipStartUs?: TimeUs;
|
|
58
|
-
// onFrame: (info: { clipId: string; timeUs: TimeUs }) => void;
|
|
59
|
-
// }
|
|
60
|
-
// ): Promise<void> {
|
|
61
|
-
// const reader = stream.getReader();
|
|
62
|
-
// const process = async (): Promise<void> => {
|
|
63
|
-
// const { done, value } = await reader.read();
|
|
64
|
-
// if (done) {
|
|
65
|
-
// reader.releaseLock();
|
|
66
|
-
// return;
|
|
67
|
-
// }
|
|
68
|
-
// if (value) {
|
|
69
|
-
// const fps = params.fps > 0 ? params.fps : 30;
|
|
70
|
-
// const frameDuration = Math.round(1_000_000 / fps);
|
|
71
|
-
// const frame = value;
|
|
72
|
-
// const timestamp = frame.timestamp ?? 0;
|
|
73
|
-
// const rcFrame = this.videoL1Cache.addFrame(
|
|
74
|
-
// frame,
|
|
75
|
-
// params.clipId,
|
|
76
|
-
// frameDuration,
|
|
77
|
-
// params.trackId
|
|
78
|
-
// );
|
|
79
|
-
// // Calculate global time for event emission
|
|
80
|
-
// const globalTimeUs = (params.clipStartUs ?? 0) + timestamp;
|
|
81
|
-
// this.checkAndNotifyClipReady(params.clipId, timestamp);
|
|
82
|
-
// // Emit cover event only for the first frame of the composition (global time = 0)
|
|
83
|
-
// if (globalTimeUs === 0) {
|
|
84
|
-
// this.eventBus?.emit(MeframeEvent.CacheCover, {
|
|
85
|
-
// timeUs: globalTimeUs,
|
|
86
|
-
// clipId: params.clipId,
|
|
87
|
-
// level: 'L1',
|
|
88
|
-
// size: rcFrame.sizeEstimate ?? 0,
|
|
89
|
-
// });
|
|
90
|
-
// }
|
|
91
|
-
// const info = { clipId: params.clipId, timeUs: timestamp };
|
|
92
|
-
// this.eventBus?.emit(MeframeEvent.ComposeFrameReady, {
|
|
93
|
-
// timeUs: globalTimeUs,
|
|
94
|
-
// frameNumber: Math.floor(globalTimeUs / frameDuration),
|
|
95
|
-
// renderTimeMs: 0,
|
|
96
|
-
// trackId: params.trackId,
|
|
97
|
-
// clipId: params.clipId,
|
|
98
|
-
// });
|
|
99
|
-
// params.onFrame(info);
|
|
100
|
-
// }
|
|
101
|
-
// await process();
|
|
102
|
-
// };
|
|
103
|
-
// try {
|
|
104
|
-
// await process();
|
|
105
|
-
// } catch (error) {
|
|
106
|
-
// this.eventBus?.emit(MeframeEvent.ComposeFrameDropped, {
|
|
107
|
-
// timeUs: 0,
|
|
108
|
-
// reason: 'compose_slow',
|
|
109
|
-
// });
|
|
110
|
-
// reader.releaseLock();
|
|
111
|
-
// console.error('[CacheManager] receiveComposedFrames error: ', error);
|
|
112
|
-
// }
|
|
113
|
-
// }
|
|
114
|
-
// async receiveEncodedChunks(
|
|
115
|
-
// stream: ReadableStream<{ chunk: EncodedVideoChunk; metadata: EncodedVideoChunkMetadata }>,
|
|
116
|
-
// clipId: string,
|
|
117
|
-
// track: 'video' | 'audio',
|
|
118
|
-
// options?: {
|
|
119
|
-
// onComplete?: (metadata: EncodedVideoChunkMetadata) => void;
|
|
120
|
-
// onError?: (error: Error) => void;
|
|
121
|
-
// }
|
|
122
|
-
// ): Promise<void> {
|
|
123
|
-
// const reader = stream.getReader();
|
|
124
|
-
// const chunks: Array<EncodedVideoChunk | EncodedAudioChunk> = [];
|
|
125
|
-
// const batchSize = track === 'video' ? 30 : 50;
|
|
126
|
-
// let decoderConfig: any = null;
|
|
127
|
-
// let metadata: EncodedVideoChunkMetadata | EncodedAudioChunkMetadata | undefined;
|
|
128
|
-
// const process = async (): Promise<void> => {
|
|
129
|
-
// const { done, value } = await reader.read();
|
|
130
|
-
// const { chunk, metadata: chunkMetadata } = value ?? {};
|
|
131
|
-
// if (chunkMetadata) {
|
|
132
|
-
// metadata = chunkMetadata;
|
|
133
|
-
// // Extract decoderConfig from first chunk's metadata
|
|
134
|
-
// if (!decoderConfig && chunkMetadata.decoderConfig) {
|
|
135
|
-
// decoderConfig = chunkMetadata.decoderConfig;
|
|
136
|
-
// }
|
|
137
|
-
// }
|
|
138
|
-
// if (done) {
|
|
139
|
-
// // Flush remaining chunks on stream end
|
|
140
|
-
// if (chunks.length > 0) {
|
|
141
|
-
// await this.l2Cache.put(clipId, chunks, track, {
|
|
142
|
-
// isComplete: true,
|
|
143
|
-
// metadata: decoderConfig,
|
|
144
|
-
// });
|
|
145
|
-
// const firstChunk = chunks[0];
|
|
146
|
-
// if (firstChunk) {
|
|
147
|
-
// this.eventBus?.emit(MeframeEvent.CacheWrite, {
|
|
148
|
-
// clipId,
|
|
149
|
-
// timeUs: firstChunk.timestamp,
|
|
150
|
-
// level: 'L2',
|
|
151
|
-
// size: chunks.reduce((sum, c) => sum + c.byteLength, 0),
|
|
152
|
-
// });
|
|
153
|
-
// }
|
|
154
|
-
// } else {
|
|
155
|
-
// // Mark as complete even if no chunks in final batch
|
|
156
|
-
// await this.l2Cache.markComplete(clipId, track);
|
|
157
|
-
// }
|
|
158
|
-
// reader.releaseLock();
|
|
159
|
-
// // Notify completion
|
|
160
|
-
// if (options?.onComplete) {
|
|
161
|
-
// options.onComplete(metadata!);
|
|
162
|
-
// }
|
|
163
|
-
// return;
|
|
164
|
-
// }
|
|
165
|
-
// if (chunk) {
|
|
166
|
-
// chunks.push(chunk);
|
|
167
|
-
// this.eventBus?.emit(MeframeEvent.EncodeChunkReady, {
|
|
168
|
-
// timeUs: chunk.timestamp,
|
|
169
|
-
// durationUs: chunk.duration ?? 0,
|
|
170
|
-
// track,
|
|
171
|
-
// size: chunk.byteLength,
|
|
172
|
-
// });
|
|
173
|
-
// // Batch write to L2 when buffer is full
|
|
174
|
-
// if (chunks.length >= batchSize) {
|
|
175
|
-
// const batchToWrite = chunks.splice(0);
|
|
176
|
-
// if (batchToWrite.length > 0) {
|
|
177
|
-
// await this.l2Cache.put(clipId, batchToWrite, track, {
|
|
178
|
-
// metadata: decoderConfig,
|
|
179
|
-
// });
|
|
180
|
-
// this.eventBus?.emit(MeframeEvent.CacheWrite, {
|
|
181
|
-
// clipId,
|
|
182
|
-
// timeUs: chunk.timestamp,
|
|
183
|
-
// level: 'L2',
|
|
184
|
-
// size: batchToWrite.reduce((sum, c) => sum + c.byteLength, 0),
|
|
185
|
-
// });
|
|
186
|
-
// }
|
|
187
|
-
// }
|
|
188
|
-
// }
|
|
189
|
-
// await process();
|
|
190
|
-
// };
|
|
191
|
-
// try {
|
|
192
|
-
// await process();
|
|
193
|
-
// } catch (error) {
|
|
194
|
-
// // Flush any accumulated chunks before throwing
|
|
195
|
-
// if (chunks.length > 0) {
|
|
196
|
-
// await this.l2Cache.put(clipId, chunks, track, {
|
|
197
|
-
// metadata: decoderConfig,
|
|
198
|
-
// });
|
|
199
|
-
// }
|
|
200
|
-
// this.eventBus?.emit(MeframeEvent.EncodeChunkError, {
|
|
201
|
-
// timeUs: 0,
|
|
202
|
-
// track,
|
|
203
|
-
// error: error as Error,
|
|
204
|
-
// });
|
|
205
|
-
// reader.releaseLock();
|
|
206
|
-
// // Notify error
|
|
207
|
-
// if (options?.onError) {
|
|
208
|
-
// options.onError(error as Error);
|
|
209
|
-
// }
|
|
210
|
-
// }
|
|
211
|
-
// }
|
|
212
|
-
acceptMixedAudio(stream, metadata) {
|
|
213
|
-
this.audioL1Cache.attachStream(stream, metadata);
|
|
214
|
-
}
|
|
215
|
-
putClipAudioData(clipId, audioData, clipDurationUs) {
|
|
216
|
-
this.audioL1Cache.putClipAudioData(clipId, audioData, clipDurationUs);
|
|
217
|
-
}
|
|
218
|
-
getClipPCM(clipId, startUs, endUs) {
|
|
219
|
-
return this.audioL1Cache.getPCM(clipId, startUs, endUs);
|
|
55
|
+
putClipAudioData(clipId, audioData, clipDurationUs, globalTimeUs) {
|
|
56
|
+
this.audioL1Cache.putClipAudioData(clipId, audioData, clipDurationUs, globalTimeUs);
|
|
220
57
|
}
|
|
221
58
|
getClipPCMWithMetadata(clipId, startUs, endUs) {
|
|
222
59
|
return this.audioL1Cache.getPCMWithMetadata(clipId, startUs, endUs);
|
|
@@ -224,6 +61,13 @@ class CacheManager {
|
|
|
224
61
|
hasClipPCM(clipId) {
|
|
225
62
|
return this.audioL1Cache.hasClipPCM(clipId);
|
|
226
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Check if sufficient PCM data exists for the requested time window
|
|
66
|
+
* Returns true only if at least 80% of requested duration is available
|
|
67
|
+
*/
|
|
68
|
+
hasWindowPCM(clipId, startUs, endUs) {
|
|
69
|
+
return this.audioL1Cache.hasWindowData(clipId, startUs, endUs);
|
|
70
|
+
}
|
|
227
71
|
resetAudioCache() {
|
|
228
72
|
this.audioL1Cache.clear();
|
|
229
73
|
}
|
|
@@ -268,75 +112,6 @@ class CacheManager {
|
|
|
268
112
|
getFrame(timeUs, clipId) {
|
|
269
113
|
return this.videoL1Cache.get(timeUs, clipId);
|
|
270
114
|
}
|
|
271
|
-
/**
|
|
272
|
-
* Wait for frame to be available in cache
|
|
273
|
-
* @param timeUs - Clip-relative timestamp (0-based)
|
|
274
|
-
* @param clipId - Clip identifier
|
|
275
|
-
* @param options - Wait options (timeout, tolerance, etc.)
|
|
276
|
-
*/
|
|
277
|
-
// waitForFrame(
|
|
278
|
-
// timeUs: TimeUs,
|
|
279
|
-
// clipId: string,
|
|
280
|
-
// options: WaitForFrameOptions = {}
|
|
281
|
-
// ): Promise<WaitForFrameResult> {
|
|
282
|
-
// const existing = this.videoL1Cache.get(timeUs, clipId);
|
|
283
|
-
// if (existing) {
|
|
284
|
-
// return Promise.resolve({ frame: existing, source: 'l1', timestampUs: timeUs, clipId });
|
|
285
|
-
// }
|
|
286
|
-
// const requestKey = this.makeRequestKey(clipId, timeUs);
|
|
287
|
-
// const existingPromise = this.pendingFramePromises.get(requestKey);
|
|
288
|
-
// if (existingPromise) {
|
|
289
|
-
// return existingPromise;
|
|
290
|
-
// }
|
|
291
|
-
// const promise = new Promise<WaitForFrameResult>((resolve, reject) => {
|
|
292
|
-
// const toleranceUs = Math.max(
|
|
293
|
-
// options.toleranceUs ?? DEFAULT_WAIT_TOLERANCE_US,
|
|
294
|
-
// DEFAULT_WAIT_TOLERANCE_US
|
|
295
|
-
// );
|
|
296
|
-
// const waiter: FrameWaiter = {
|
|
297
|
-
// requestKey,
|
|
298
|
-
// clipId,
|
|
299
|
-
// targetTimeUs: timeUs,
|
|
300
|
-
// resolve,
|
|
301
|
-
// reject,
|
|
302
|
-
// toleranceUs,
|
|
303
|
-
// };
|
|
304
|
-
// let waiters = this.frameWaiters.get(clipId);
|
|
305
|
-
// if (!waiters) {
|
|
306
|
-
// waiters = new Set();
|
|
307
|
-
// this.frameWaiters.set(clipId, waiters);
|
|
308
|
-
// }
|
|
309
|
-
// waiters.add(waiter);
|
|
310
|
-
// const signal = options.signal;
|
|
311
|
-
// if (signal) {
|
|
312
|
-
// const onAbort = (): void => {
|
|
313
|
-
// this.removeWaiter(waiter);
|
|
314
|
-
// this.cleanupWaiter(waiter);
|
|
315
|
-
// reject(new DOMException('Render aborted', 'AbortError'));
|
|
316
|
-
// };
|
|
317
|
-
// if (signal.aborted) {
|
|
318
|
-
// onAbort();
|
|
319
|
-
// return;
|
|
320
|
-
// }
|
|
321
|
-
// signal.addEventListener('abort', onAbort, { once: true });
|
|
322
|
-
// waiter.abortCleanup = () => {
|
|
323
|
-
// signal.removeEventListener('abort', onAbort);
|
|
324
|
-
// };
|
|
325
|
-
// }
|
|
326
|
-
// if (options.timeoutMs && options.timeoutMs > 0) {
|
|
327
|
-
// waiter.timeoutId = setTimeout(() => {
|
|
328
|
-
// this.removeWaiter(waiter);
|
|
329
|
-
// this.cleanupWaiter(waiter);
|
|
330
|
-
// reject(new Error('waitForFrame timeout'));
|
|
331
|
-
// }, options.timeoutMs);
|
|
332
|
-
// }
|
|
333
|
-
// });
|
|
334
|
-
// const trackedPromise = promise.finally(() => {
|
|
335
|
-
// this.pendingFramePromises.delete(requestKey);
|
|
336
|
-
// });
|
|
337
|
-
// this.pendingFramePromises.set(requestKey, trackedPromise);
|
|
338
|
-
// return trackedPromise;
|
|
339
|
-
// }
|
|
340
115
|
async invalidateClip(clipId) {
|
|
341
116
|
this.videoL1Cache.invalidateClip(clipId);
|
|
342
117
|
}
|
|
@@ -423,43 +198,6 @@ class CacheManager {
|
|
|
423
198
|
}
|
|
424
199
|
}
|
|
425
200
|
}
|
|
426
|
-
// private cleanupWaiter(waiter: FrameWaiter): void {
|
|
427
|
-
// if (waiter.timeoutId) {
|
|
428
|
-
// clearTimeout(waiter.timeoutId);
|
|
429
|
-
// waiter.timeoutId = undefined;
|
|
430
|
-
// }
|
|
431
|
-
// if (waiter.abortCleanup) {
|
|
432
|
-
// waiter.abortCleanup();
|
|
433
|
-
// waiter.abortCleanup = undefined;
|
|
434
|
-
// }
|
|
435
|
-
// }
|
|
436
|
-
// private removeWaiter(waiter: FrameWaiter): void {
|
|
437
|
-
// const waiters = this.frameWaiters.get(waiter.clipId);
|
|
438
|
-
// if (!waiters) return;
|
|
439
|
-
// waiters.delete(waiter);
|
|
440
|
-
// if (waiters.size === 0) {
|
|
441
|
-
// this.frameWaiters.delete(waiter.clipId);
|
|
442
|
-
// }
|
|
443
|
-
// }
|
|
444
|
-
// private matchesTimestamp(
|
|
445
|
-
// targetTimeUs: TimeUs,
|
|
446
|
-
// actualTimeUs: TimeUs,
|
|
447
|
-
// frameDurationUs: TimeUs,
|
|
448
|
-
// toleranceUs: TimeUs
|
|
449
|
-
// ): boolean {
|
|
450
|
-
// if (targetTimeUs === actualTimeUs) return true;
|
|
451
|
-
// const delta = Math.abs(targetTimeUs - actualTimeUs);
|
|
452
|
-
// if (delta <= toleranceUs) {
|
|
453
|
-
// return true;
|
|
454
|
-
// }
|
|
455
|
-
// if (actualTimeUs >= targetTimeUs && actualTimeUs < targetTimeUs + frameDurationUs) {
|
|
456
|
-
// return true;
|
|
457
|
-
// }
|
|
458
|
-
// return false;
|
|
459
|
-
// }
|
|
460
|
-
// private makeRequestKey(clipId: string, timeUs: TimeUs): string {
|
|
461
|
-
// return `${clipId}:${timeUs}`;
|
|
462
|
-
// }
|
|
463
201
|
}
|
|
464
202
|
export {
|
|
465
203
|
CacheManager
|