@meframe/core 0.3.8 → 0.4.0
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 +9 -6
- package/dist/Meframe.js.map +1 -1
- package/dist/controllers/ExportController.d.ts.map +1 -1
- package/dist/controllers/ExportController.js +2 -0
- package/dist/controllers/ExportController.js.map +1 -1
- package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
- package/dist/orchestrator/ExportScheduler.js +9 -0
- package/dist/orchestrator/ExportScheduler.js.map +1 -1
- package/dist/stages/encode/BaseEncoder.d.ts.map +1 -1
- package/dist/stages/encode/BaseEncoder.js +12 -21
- package/dist/stages/encode/BaseEncoder.js.map +1 -1
- package/dist/workers/stages/export/{export.worker.SahP9aVK.js → export.worker.DCStS1mL.js} +13 -22
- package/dist/workers/stages/export/export.worker.DCStS1mL.js.map +1 -0
- package/dist/workers/worker-manifest.json +1 -1
- package/package.json +1 -1
- package/dist/workers/stages/export/export.worker.SahP9aVK.js.map +0 -1
package/dist/Meframe.d.ts
CHANGED
|
@@ -57,10 +57,10 @@ export declare class Meframe {
|
|
|
57
57
|
* Export the composition
|
|
58
58
|
* Uses ExportController for direct export
|
|
59
59
|
*/
|
|
60
|
-
export(options: ExportOptions & {
|
|
60
|
+
export(model: CompositionModel | CompositionModelData, options: ExportOptions & {
|
|
61
61
|
exportMode?: 'blob';
|
|
62
62
|
}): Promise<Blob>;
|
|
63
|
-
export(options: ExportOptions & {
|
|
63
|
+
export(model: CompositionModel | CompositionModelData, options: ExportOptions & {
|
|
64
64
|
exportMode: 'stream';
|
|
65
65
|
onMuxData: (data: Uint8Array, position: number) => void;
|
|
66
66
|
}): Promise<null>;
|
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;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;AACpE,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAGnE,qBAAa,OAAO;IAClB,wDAAwD;IACxD,KAAK,EAAE,YAAY,CAAU;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,4DAA4D;IAC5D,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,CAAoB;IAEnC;;;OAGG;IACH,MAAM,CAAC,kBAAkB,IAAI,UAAU,CAAC,OAAO,yBAAyB,CAAC;IAIzE,OAAO;IAoBP;;OAEG;WACU,MAAM,CAAC,MAAM,GAAE,aAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAcjE;;OAEG;YACW,UAAU;IAyBxB;;OAEG;IACG,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBxF;;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,CAAC;QAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,GAAG,aAAa;IA6BjB;;;OAGG;IACG,MAAM,
|
|
1
|
+
{"version":3,"file":"Meframe.d.ts","sourceRoot":"","sources":["../src/Meframe.ts"],"names":[],"mappings":"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"}
|
package/dist/Meframe.js
CHANGED
|
@@ -132,25 +132,28 @@ class Meframe {
|
|
|
132
132
|
this.playbackController.preheatNextWindow();
|
|
133
133
|
return this.playbackController;
|
|
134
134
|
}
|
|
135
|
-
async export(options) {
|
|
135
|
+
async export(model, options) {
|
|
136
136
|
this.ensureReady();
|
|
137
137
|
this.playbackController?.pause();
|
|
138
138
|
this.setState("exporting");
|
|
139
|
+
console.info("[Meframe] Export is starting, exportMode:", options.exportMode);
|
|
139
140
|
try {
|
|
140
|
-
const
|
|
141
|
-
if (!
|
|
142
|
-
throw new Error("No composition model set");
|
|
141
|
+
const compositionModel = model instanceof CompositionModel ? model : new CompositionModel(model);
|
|
142
|
+
if (!compositionModel) {
|
|
143
|
+
throw new Error("No export composition model set");
|
|
143
144
|
}
|
|
144
145
|
const blob = options.exportMode === "stream" ? await this.exportController.export(
|
|
145
|
-
|
|
146
|
+
compositionModel,
|
|
146
147
|
options
|
|
147
148
|
) : await this.exportController.export(
|
|
148
|
-
|
|
149
|
+
compositionModel,
|
|
149
150
|
options
|
|
150
151
|
);
|
|
151
152
|
this.setState("ready");
|
|
153
|
+
console.info("[Meframe] Export is completed successfully");
|
|
152
154
|
return blob;
|
|
153
155
|
} catch (error) {
|
|
156
|
+
console.error("[Meframe] Export error:", error);
|
|
154
157
|
this.setState("error");
|
|
155
158
|
throw error;
|
|
156
159
|
}
|
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 { ExportController } from './controllers/ExportController';\nimport { PluginManager } from './plugins/PluginManager';\nimport { EventBus } from './event/EventBus';\nimport { MeframeEvent, type EventPayloadMap } from './event/events';\nimport { checkBrowserCompatibility } from './utils/platform-utils';\nimport { BrowserCompatibilityError } from './utils/errors';\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 setCompositionModel() */\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;\n\n /**\n * Check if current browser is compatible with Meframe\n * Returns compatibility info including browser name, version, and missing features\n */\n static checkCompatibility(): ReturnType<typeof checkBrowserCompatibility> {\n return checkBrowserCompatibility();\n }\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 // Check browser compatibility first\n const compatibility = checkBrowserCompatibility();\n if (!compatibility.webCodecsAvailable || !compatibility.opfsAvailable) {\n throw new BrowserCompatibilityError(compatibility);\n }\n\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 this.playbackController?.pause();\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;\n startUs?: TimeUs;\n autoStart?: boolean;\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 autoStart: options?.autoStart,\n });\n }\n\n // Render initial frame\n this.playbackController.renderCover();\n this.playbackController.preheatNextWindow();\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 & { exportMode?: 'blob' }): Promise<Blob>;\n async export(\n options: ExportOptions & {\n exportMode: 'stream';\n onMuxData: (data: Uint8Array, position: number) => void;\n }\n ): Promise<null>;\n async export(options: ExportOptions): Promise<Blob | null> {\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 // Delegate to ExportController\n const blob =\n options.exportMode === 'stream'\n ? await this.exportController.export(\n model,\n options as ExportOptions & {\n exportMode: 'stream';\n onMuxData: (data: Uint8Array, position: number) => void;\n }\n )\n : await this.exportController.export(\n model,\n options as ExportOptions & { exportMode?: 'blob' }\n );\n\n this.setState('ready');\n\n return blob;\n } catch (error) {\n this.setState('error');\n throw error;\n }\n }\n\n /**\n * Cancel ongoing export operation\n */\n cancelExport(): void {\n this.exportController.cancel();\n }\n\n /**\n * Pause ongoing export operation\n */\n pauseExport(): void {\n this.exportController.pause();\n }\n\n /**\n * Resume paused export operation\n */\n resumeExport(): void {\n this.exportController.resume();\n }\n\n /**\n * Check if export is paused\n */\n isExportPaused(): boolean {\n return this.exportController.isPaused();\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":";;;;;;;;;;;AAmBO,MAAM,QAAQ;AAAA;AAAA,EAEnB,QAAsB;AAAA;AAAA,EAEb;AAAA;AAAA,EAET,QAAiC;AAAA;AAAA,EAExB;AAAA;AAAA,EAEA;AAAA,EAED;AAAA,EACA;AAAA,EACA,qBAAgD;AAAA,EAChD;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMR,OAAO,qBAAmE;AACxE,WAAO,0BAAA;AAAA,EACT;AAAA,EAEQ,YAAY,QAAwB;AAC1C,SAAK,SAAS;AACd,SAAK,WAAW,IAAI,SAAA;AACpB,SAAK,SAAS,KAAK,SAAS,WAAA;AAC5B,SAAK,gBAAgB,IAAI,cAAc,IAAI;AAI3C,SAAK,eAAe,IAAI,aAAa;AAAA,MACnC,WAAW,OAAO,OAAO;AAAA,MACzB,YAAa,OAAe;AAAA,MAC5B,aAAa,OAAO;AAAA,MACpB,UAAU,KAAK;AAAA,MACf,YAAY,OAAO,OAAO;AAAA,MAC1B,iBAAiB,OAAO,OAAO;AAAA,IAAA,CAChC;AAED,SAAK,mBAAmB,IAAI,iBAAiB,KAAK,YAAY;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,OAAO,SAAwB,IAAsB;AAEhE,UAAM,gBAAgB,0BAAA;AACtB,QAAI,CAAC,cAAc,sBAAsB,CAAC,cAAc,eAAe;AACrE,YAAM,IAAI,0BAA0B,aAAa;AAAA,IACnD;AAGA,UAAM,iBAAiB,MAAM,WAAW,EAAE,UAAU,QAAQ;AAC5D,UAAM,WAAW,IAAI,QAAQ,cAAc;AAC3C,UAAM,SAAS,WAAA;AACf,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,aAA4B;AACxC,SAAK,SAAS,SAAS;AAEvB,QAAI;AAEF,YAAM,KAAK,aAAa,WAAA;AAGxB,YAAM,UAAW,KAAK,OAAe;AACrC,UAAI,WAAW,MAAM,QAAQ,OAAO,GAAG;AACrC,mBAAW,UAAU,SAAS;AAE5B,cAAI,UAAU,OAAO,WAAW,YAAY,UAAU,UAAU,aAAa,QAAQ;AACnF,iBAAK,cAAc,SAAS,MAAgB;AAAA,UAC9C;AAAA,QACF;AAAA,MACF;AAEA,WAAK,SAAS,MAAM;AAAA,IACtB,SAAS,OAAO;AACd,WAAK,SAAS,OAAO;AACrB,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAAoB,OAA+D;AACvF,SAAK,mBAAA;AAEL,SAAK,oBAAoB,MAAA;AAGzB,UAAM,mBACJ,iBAAiB,mBAAmB,QAAQ,IAAI,iBAAiB,KAAK;AAGxE,UAAM,KAAK,aAAa,oBAAoB,gBAAgB;AAC5D,SAAK,QAAQ;AAEb,SAAK,SAAS,OAAO;AACrB,SAAK,SAAS,KAAK,aAAa,OAAO;AAAA,MACrC,YAAY,MAAM,OAAO;AAAA,MACzB,WAAW,MAAM,OAAO;AAAA,QACtB,CAAC,KAAa,UAAe,MAAM,MAAM,OAAO,UAAU;AAAA,QAC1D;AAAA,MAAA;AAAA,MAEF,YAAY,MAAM;AAAA,IAAA,CACnB;AAAA,EAEH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAAW,OAAwC;AACvD,SAAK,mBAAA;AAEL,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAGA,UAAM,KAAK,aAAa,WAAW,KAAK;AAGxC,SAAK,QAAQ,KAAK,aAAa;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,SAIK;AAChB,SAAK,YAAA;AAGL,UAAM,SAAS,SAAS,UAAU,KAAK;AACvC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AAGA,SAAK,SAAS;AAGd,QAAI,CAAC,KAAK,oBAAoB;AAC5B,WAAK,qBAAqB,IAAI,mBAAmB,KAAK,cAAqB,KAAK,UAAU;AAAA,QACxF;AAAA,QACA,SAAS,SAAS;AAAA,QAClB,WAAW,SAAS;AAAA,MAAA,CACrB;AAAA,IACH;AAGA,SAAK,mBAAmB,YAAA;AACxB,SAAK,mBAAmB,kBAAA;AAGxB,WAAO,KAAK;AAAA,EACd;AAAA,EAaA,MAAM,OAAO,SAA8C;AACzD,SAAK,YAAA;AACL,SAAK,oBAAoB,MAAA;AACzB,SAAK,SAAS,WAAW;AAEzB,QAAI;AACF,YAAM,QAAQ,KAAK;AACnB,UAAI,CAAC,OAAO;AACV,cAAM,IAAI,MAAM,0BAA0B;AAAA,MAC5C;AAGA,YAAM,OACJ,QAAQ,eAAe,WACnB,MAAM,KAAK,iBAAiB;AAAA,QAC1B;AAAA,QACA;AAAA,MAAA,IAKF,MAAM,KAAK,iBAAiB;AAAA,QAC1B;AAAA,QACA;AAAA,MAAA;AAGR,WAAK,SAAS,OAAO;AAErB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,WAAK,SAAS,OAAO;AACrB,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAqB;AACnB,SAAK,iBAAiB,OAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAoB;AAClB,SAAK,iBAAiB,MAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,eAAqB;AACnB,SAAK,iBAAiB,OAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,iBAA0B;AACxB,WAAO,KAAK,iBAAiB,SAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAyB;AACvB,WAAO,KAAK,oBAAoB,iBAAiB;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAA4B;AAChC,SAAK,YAAA;AAGL,SAAK,oBAAoB,KAAA;AAGzB,UAAM,KAAK,aAAa,aAAa,MAAA;AAErC,YAAQ,IAAI,sCAAsC;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAC7B,QAAI,KAAK,UAAU,YAAa;AAGhC,SAAK,oBAAoB,KAAA;AACzB,SAAK,oBAAoB,QAAA;AACzB,SAAK,qBAAqB;AAG1B,UAAM,KAAK,cAAc,WAAA;AAGzB,UAAM,KAAK,aAAa,QAAA;AAGxB,SAAK,SAAS,QAAA;AAEd,SAAK,SAAS,WAAW;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKQ,SAAS,OAA2B;AAC1C,UAAM,WAAW,KAAK;AACtB,SAAK,QAAQ;AAIb,SAAK,SAAS,KAAK,iBAAwB;AAAA,MACzC;AAAA,MACA,UAAU;AAAA,IAAA,CACX;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAA2B;AACjC,QAAI,KAAK,UAAU,aAAa;AAC9B,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAoB;AAC1B,SAAK,mBAAA;AAEL,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAEA,QAAI,KAAK,UAAU,aAAa,KAAK,UAAU,QAAQ;AACrD,YAAM,IAAI,MAAM,mBAAmB;AAAA,IACrC;AAAA,EACF;AACF;"}
|
|
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';\nimport { checkBrowserCompatibility } from './utils/platform-utils';\nimport { BrowserCompatibilityError } from './utils/errors';\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 setCompositionModel() */\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;\n\n /**\n * Check if current browser is compatible with Meframe\n * Returns compatibility info including browser name, version, and missing features\n */\n static checkCompatibility(): ReturnType<typeof checkBrowserCompatibility> {\n return checkBrowserCompatibility();\n }\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 // Check browser compatibility first\n const compatibility = checkBrowserCompatibility();\n if (!compatibility.webCodecsAvailable || !compatibility.opfsAvailable) {\n throw new BrowserCompatibilityError(compatibility);\n }\n\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 this.playbackController?.pause();\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;\n startUs?: TimeUs;\n autoStart?: boolean;\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 autoStart: options?.autoStart,\n });\n }\n\n // Render initial frame\n this.playbackController.renderCover();\n this.playbackController.preheatNextWindow();\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(\n model: CompositionModel | CompositionModelData,\n options: ExportOptions & { exportMode?: 'blob' }\n ): Promise<Blob>;\n\n async export(\n model: CompositionModel | CompositionModelData,\n options: ExportOptions & {\n exportMode: 'stream';\n onMuxData: (data: Uint8Array, position: number) => void;\n }\n ): Promise<null>;\n\n async export(\n model: CompositionModel | CompositionModelData,\n options: ExportOptions\n ): Promise<Blob | null> {\n this.ensureReady();\n this.playbackController?.pause();\n this.setState('exporting');\n\n console.info('[Meframe] Export is starting, exportMode:', options.exportMode);\n try {\n // Convert plain object to CompositionModel instance if needed\n const compositionModel =\n model instanceof CompositionModel ? model : new CompositionModel(model);\n if (!compositionModel) {\n throw new Error('No export composition model set');\n }\n\n // Delegate to ExportController\n const blob =\n options.exportMode === 'stream'\n ? await this.exportController.export(\n compositionModel,\n options as ExportOptions & {\n exportMode: 'stream';\n onMuxData: (data: Uint8Array, position: number) => void;\n }\n )\n : await this.exportController.export(\n compositionModel,\n options as ExportOptions & { exportMode?: 'blob' }\n );\n\n this.setState('ready');\n console.info('[Meframe] Export is completed successfully');\n return blob;\n } catch (error) {\n console.error('[Meframe] Export error:', error);\n this.setState('error');\n throw error;\n }\n }\n\n /**\n * Cancel ongoing export operation\n */\n cancelExport(): void {\n this.exportController.cancel();\n }\n\n /**\n * Pause ongoing export operation\n */\n pauseExport(): void {\n this.exportController.pause();\n }\n\n /**\n * Resume paused export operation\n */\n resumeExport(): void {\n this.exportController.resume();\n }\n\n /**\n * Check if export is paused\n */\n isExportPaused(): boolean {\n return this.exportController.isPaused();\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExportController.d.ts","sourceRoot":"","sources":["../../src/controllers/ExportController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,MAAM,CAAS;gBAEX,YAAY,EAAE,aAAa;IAIjC,MAAM,CACV,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,aAAa,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAC/C,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ExportController.d.ts","sourceRoot":"","sources":["../../src/controllers/ExportController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,MAAM,CAAS;gBAEX,YAAY,EAAE,aAAa;IAIjC,MAAM,CACV,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,aAAa,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAC/C,OAAO,CAAC,IAAI,CAAC;IAEV,MAAM,CACV,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,aAAa,GAAG;QACvB,UAAU,EAAE,QAAQ,CAAC;QACrB,SAAS,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;KACzD,GACA,OAAO,CAAC,IAAI,CAAC;IA0BhB,MAAM,IAAI,IAAI;IAOd,KAAK,IAAI,IAAI;IAOb,MAAM,IAAI,IAAI;IAId,QAAQ,IAAI,OAAO;CAGpB"}
|
|
@@ -9,6 +9,7 @@ class ExportController {
|
|
|
9
9
|
this.abortController = new AbortController();
|
|
10
10
|
this.paused = false;
|
|
11
11
|
try {
|
|
12
|
+
console.info("[ExportController] Export is starting");
|
|
12
13
|
const extendedOptions = {
|
|
13
14
|
...options,
|
|
14
15
|
signal: this.abortController.signal,
|
|
@@ -18,6 +19,7 @@ class ExportController {
|
|
|
18
19
|
};
|
|
19
20
|
return await this.orchestrator.export(model, extendedOptions);
|
|
20
21
|
} finally {
|
|
22
|
+
console.info("[ExportController] Export is completed");
|
|
21
23
|
this.abortController = null;
|
|
22
24
|
this.paused = false;
|
|
23
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExportController.js","sources":["../../src/controllers/ExportController.ts"],"sourcesContent":["import type { IOrchestrator } from '../orchestrator/types';\nimport type { ExportOptions } from '../types';\nimport type { CompositionModel } from '../model';\n\nexport class ExportController {\n private orchestrator: IOrchestrator;\n private abortController: AbortController | null = null;\n private paused = false;\n\n constructor(orchestrator: IOrchestrator) {\n this.orchestrator = orchestrator;\n }\n\n async export(\n model: CompositionModel,\n options: ExportOptions & { exportMode?: 'blob' }\n ): Promise<Blob>;\n async export(\n model: CompositionModel,\n options: ExportOptions & {\n exportMode: 'stream';\n onMuxData: (data: Uint8Array, position: number) => void;\n }\n ): Promise<null>;\n async export(model: CompositionModel, options: ExportOptions): Promise<Blob | null> {\n this.abortController = new AbortController();\n this.paused = false;\n\n try {\n // Pass abort signal to orchestrator (ExportScheduler needs to handle it)\n // Note: ExportOptions type currently doesn't have 'signal', we might need to extend it or pass separately\n // For now, assume Orchestrator.export supports an extended options object internally\n const extendedOptions = {\n ...options,\n signal: this.abortController.signal,\n controller: this, // Pass controller reference to allow polling paused state? Or event based?\n // Ideally ExportScheduler checks `signal.aborted` and maybe a `paused` flag or callback.\n };\n\n return await this.orchestrator.export(model, extendedOptions as any);\n } finally {\n this.abortController = null;\n this.paused = false;\n }\n }\n\n cancel(): void {\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n pause(): void {\n this.paused = true;\n // Notify scheduler?\n // Since ExportScheduler is running a loop, we need a way to signal it.\n // Ideally we pass an interface to check status.\n }\n\n resume(): void {\n this.paused = false;\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n}\n"],"names":[],"mappings":"AAIO,MAAM,iBAAiB;AAAA,EACpB;AAAA,EACA,kBAA0C;AAAA,EAC1C,SAAS;AAAA,EAEjB,YAAY,cAA6B;AACvC,SAAK,eAAe;AAAA,EACtB;AAAA,
|
|
1
|
+
{"version":3,"file":"ExportController.js","sources":["../../src/controllers/ExportController.ts"],"sourcesContent":["import type { IOrchestrator } from '../orchestrator/types';\nimport type { ExportOptions } from '../types';\nimport type { CompositionModel } from '../model';\n\nexport class ExportController {\n private orchestrator: IOrchestrator;\n private abortController: AbortController | null = null;\n private paused = false;\n\n constructor(orchestrator: IOrchestrator) {\n this.orchestrator = orchestrator;\n }\n\n async export(\n model: CompositionModel,\n options: ExportOptions & { exportMode?: 'blob' }\n ): Promise<Blob>;\n\n async export(\n model: CompositionModel,\n options: ExportOptions & {\n exportMode: 'stream';\n onMuxData: (data: Uint8Array, position: number) => void;\n }\n ): Promise<null>;\n\n async export(model: CompositionModel, options: ExportOptions): Promise<Blob | null> {\n this.abortController = new AbortController();\n this.paused = false;\n\n try {\n console.info('[ExportController] Export is starting');\n // Pass abort signal to orchestrator (ExportScheduler needs to handle it)\n // Note: ExportOptions type currently doesn't have 'signal', we might need to extend it or pass separately\n // For now, assume Orchestrator.export supports an extended options object internally\n const extendedOptions = {\n ...options,\n signal: this.abortController.signal,\n controller: this, // Pass controller reference to allow polling paused state? Or event based?\n // Ideally ExportScheduler checks `signal.aborted` and maybe a `paused` flag or callback.\n };\n\n return await this.orchestrator.export(model, extendedOptions as any);\n } finally {\n console.info('[ExportController] Export is completed');\n this.abortController = null;\n this.paused = false;\n }\n }\n\n cancel(): void {\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n pause(): void {\n this.paused = true;\n // Notify scheduler?\n // Since ExportScheduler is running a loop, we need a way to signal it.\n // Ideally we pass an interface to check status.\n }\n\n resume(): void {\n this.paused = false;\n }\n\n isPaused(): boolean {\n return this.paused;\n }\n}\n"],"names":[],"mappings":"AAIO,MAAM,iBAAiB;AAAA,EACpB;AAAA,EACA,kBAA0C;AAAA,EAC1C,SAAS;AAAA,EAEjB,YAAY,cAA6B;AACvC,SAAK,eAAe;AAAA,EACtB;AAAA,EAeA,MAAM,OAAO,OAAyB,SAA8C;AAClF,SAAK,kBAAkB,IAAI,gBAAA;AAC3B,SAAK,SAAS;AAEd,QAAI;AACF,cAAQ,KAAK,uCAAuC;AAIpD,YAAM,kBAAkB;AAAA,QACtB,GAAG;AAAA,QACH,QAAQ,KAAK,gBAAgB;AAAA,QAC7B,YAAY;AAAA;AAAA;AAAA,MAAA;AAId,aAAO,MAAM,KAAK,aAAa,OAAO,OAAO,eAAsB;AAAA,IACrE,UAAA;AACE,cAAQ,KAAK,wCAAwC;AACrD,WAAK,kBAAkB;AACvB,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA,EAEA,SAAe;AACb,QAAI,KAAK,iBAAiB;AACxB,WAAK,gBAAgB,MAAA;AACrB,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS;AAAA,EAIhB;AAAA,EAEA,SAAe;AACb,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,WAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AACF;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExportScheduler.d.ts","sourceRoot":"","sources":["../../src/orchestrator/ExportScheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAQ,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAgB,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKhE,UAAU,mBAAmB;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,kBAAkB,CAAC;IACjC,qBAAqB,EAAE,MAAM,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACrD,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;CACrC;AAED,UAAU,qBAAsB,SAAQ,aAAa;IACnD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAKD,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAAsB;gBAEtB,IAAI,EAAE,mBAAmB;IAI/B,OAAO,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ExportScheduler.d.ts","sourceRoot":"","sources":["../../src/orchestrator/ExportScheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAQ,MAAM,UAAU,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAgB,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAKhE,UAAU,mBAAmB;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,kBAAkB,CAAC;IACjC,qBAAqB,EAAE,MAAM,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACrD,QAAQ,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;CACrC;AAED,UAAU,qBAAsB,SAAQ,aAAa;IACnD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAKD,qBAAa,eAAe;IAC1B,OAAO,CAAC,IAAI,CAAsB;gBAEtB,IAAI,EAAE,mBAAmB;IAI/B,OAAO,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;YAe9E,eAAe;YA8If,gBAAgB;YAyDhB,qBAAqB;IAwBnC;;;OAGG;YACW,oBAAoB;YAyIpB,wBAAwB;YAuDxB,gBAAgB;IA8B9B,OAAO,CAAC,qBAAqB;YAKf,0BAA0B;IA2BxC,OAAO,CAAC,+BAA+B;YAazB,mBAAmB;CA0ClC"}
|
|
@@ -12,6 +12,7 @@ class ExportScheduler {
|
|
|
12
12
|
async execute(model, options) {
|
|
13
13
|
this.deps.cacheManager.clear();
|
|
14
14
|
const projectId = this.deps.cacheManager.resourceCache.projectId;
|
|
15
|
+
console.info("[ExportScheduler] Export is starting, projectId:", projectId);
|
|
15
16
|
if (!navigator.locks) {
|
|
16
17
|
return this.executeInternal(model, options);
|
|
17
18
|
}
|
|
@@ -23,6 +24,7 @@ class ExportScheduler {
|
|
|
23
24
|
const signal = options.signal;
|
|
24
25
|
const controller = options.controller;
|
|
25
26
|
const exportMode = options.exportMode ?? "blob";
|
|
27
|
+
console.info("[ExportScheduler] Export is starting, exportMode:", exportMode);
|
|
26
28
|
if (exportMode === "stream" && typeof options.onMuxData !== "function") {
|
|
27
29
|
throw new Error("onMuxData callback is required when exportMode is stream");
|
|
28
30
|
}
|
|
@@ -50,6 +52,7 @@ class ExportScheduler {
|
|
|
50
52
|
});
|
|
51
53
|
this.deps.cacheManager.beginExport();
|
|
52
54
|
try {
|
|
55
|
+
console.info("[ExportScheduler] Export is preloading resources");
|
|
53
56
|
await this.preloadResources(model, resourceLoader, eventBus, checkStatus);
|
|
54
57
|
const hasAudioSamples = this.deps.cacheManager.audioSampleCache.getTotalBytes() > 0;
|
|
55
58
|
const { enableAudio, disabledReason } = await this.decideAudioStrategy({
|
|
@@ -64,6 +67,7 @@ class ExportScheduler {
|
|
|
64
67
|
message: disabledReason
|
|
65
68
|
});
|
|
66
69
|
}
|
|
70
|
+
console.info("[ExportScheduler] Export is starting muxer");
|
|
67
71
|
muxManager.start({
|
|
68
72
|
width,
|
|
69
73
|
height,
|
|
@@ -79,6 +83,7 @@ class ExportScheduler {
|
|
|
79
83
|
chunked: options.muxChunked
|
|
80
84
|
} : { kind: "blob" }
|
|
81
85
|
});
|
|
86
|
+
console.info("[ExportScheduler] Export is processing video and audio");
|
|
82
87
|
const mainTrack = model.tracks.find((t) => t.id === model.mainTrackId);
|
|
83
88
|
if (mainTrack && mainTrack.clips.length > 0) {
|
|
84
89
|
const audioPromise = enableAudio ? this.processAudioInWindows(model.durationUs, audioSession, muxManager, checkStatus) : Promise.resolve();
|
|
@@ -99,6 +104,7 @@ class ExportScheduler {
|
|
|
99
104
|
if (signal?.aborted) {
|
|
100
105
|
throw new DOMException("Export aborted", "AbortError");
|
|
101
106
|
}
|
|
107
|
+
console.info("[ExportScheduler] Export is finalizing");
|
|
102
108
|
const blob = await muxManager.finalize();
|
|
103
109
|
eventBus.emit(MeframeEvent.ExportComplete, {
|
|
104
110
|
size: blob?.size ?? streamedBytes,
|
|
@@ -109,14 +115,17 @@ class ExportScheduler {
|
|
|
109
115
|
progress: 1,
|
|
110
116
|
stage: "muxing"
|
|
111
117
|
});
|
|
118
|
+
console.info("[ExportScheduler] Export is completed successfully");
|
|
112
119
|
return blob;
|
|
113
120
|
} catch (error) {
|
|
121
|
+
console.error("[ExportScheduler] Export error:", error);
|
|
114
122
|
eventBus.emit(MeframeEvent.ExportError, {
|
|
115
123
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
116
124
|
stage: "export"
|
|
117
125
|
});
|
|
118
126
|
throw error;
|
|
119
127
|
} finally {
|
|
128
|
+
console.info("[ExportScheduler] Export is completed");
|
|
120
129
|
this.deps.cacheManager.endExport();
|
|
121
130
|
}
|
|
122
131
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExportScheduler.js","sources":["../../src/orchestrator/ExportScheduler.ts"],"sourcesContent":["import { CompositionModel, Clip } from '../model';\nimport { ExportOptions } from '../types';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { AudioExportSession } from './AudioExportSession';\nimport { VideoWindowDecodeSession } from './VideoWindowDecodeSession';\nimport { WorkerType } from '../worker/types';\nimport { hasResourceId, type TimeUs, type Resource } from '../model/types';\nimport type { ExportController } from '../controllers/ExportController';\nimport type { BaseWorker } from '../worker/BaseWorker';\nimport { EventBus } from '../event/EventBus';\nimport { MeframeEvent, EventPayloadMap } from '../event/events';\nimport { AudioChunkEncoder } from '../stages/encode/AudioChunkEncoder';\nimport type { ClipInstructionSet } from '../stages/compose/instructions';\nimport { computeGOPAlignedWindows, computeFixedWindows } from '../utils/time-utils';\n\ninterface ExportSchedulerDeps {\n workerPool: WorkerPool;\n planner: CompositionPlanner;\n cacheManager: CacheManager;\n resourceLoader: ResourceLoader;\n muxManager: MuxManager;\n audioSession: AudioExportSession;\n workerConfigsProvider: () => Record<WorkerType, any>;\n eventBus: EventBus<EventPayloadMap>;\n}\n\ninterface ExtendedExportOptions extends ExportOptions {\n signal?: AbortSignal;\n controller?: ExportController;\n exportMode?: 'blob' | 'stream';\n onMuxData?: (data: Uint8Array, position: number) => void;\n muxChunkSizeBytes?: number;\n muxChunked?: boolean;\n}\n\n// 5 seconds per window, ~150 frames at 30fps\nconst VIDEO_WINDOW_DURATION_US: TimeUs = 5_000_000;\n\nexport class ExportScheduler {\n private deps: ExportSchedulerDeps;\n\n constructor(deps: ExportSchedulerDeps) {\n this.deps = deps;\n }\n\n async execute(model: CompositionModel, options: ExtendedExportOptions): Promise<Blob | null> {\n this.deps.cacheManager.clear();\n\n const projectId = this.deps.cacheManager.resourceCache.projectId;\n\n if (!navigator.locks) {\n return this.executeInternal(model, options);\n }\n\n const lockName = `meframe-resource-${projectId}`;\n return navigator.locks.request(lockName, () => this.executeInternal(model, options));\n }\n\n private async executeInternal(\n model: CompositionModel,\n options: ExtendedExportOptions\n ): Promise<Blob | null> {\n const { muxManager, audioSession, eventBus, resourceLoader } = this.deps;\n const signal = options.signal;\n const controller = options.controller;\n const exportMode = options.exportMode ?? 'blob';\n\n if (exportMode === 'stream' && typeof options.onMuxData !== 'function') {\n throw new Error('onMuxData callback is required when exportMode is stream');\n }\n\n let streamedBytes = 0;\n\n const checkStatus = async () => {\n if (signal?.aborted) {\n throw new DOMException('Export aborted', 'AbortError');\n }\n if (controller?.isPaused()) {\n while (controller.isPaused()) {\n if (signal?.aborted) throw new DOMException('Export aborted', 'AbortError');\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n };\n\n const width = options.width || model.renderConfig?.width || 720;\n const height = options.height || model.renderConfig?.height || 1280;\n const fps = options.fps || model.fps || 30;\n\n eventBus.emit(MeframeEvent.ExportStart, {\n format: options.format || 'mp4',\n width,\n height,\n fps,\n durationUs: model.durationUs,\n });\n\n this.deps.cacheManager.beginExport();\n\n try {\n // 1. Preload and parse all resources (0-40%)\n await this.preloadResources(model, resourceLoader, eventBus, checkStatus);\n\n const hasAudioSamples = this.deps.cacheManager.audioSampleCache.getTotalBytes() > 0;\n const { enableAudio, disabledReason } = await this.decideAudioStrategy({\n hasAudioSamples,\n requestedFormat: options.format ?? 'mp4',\n requestedAudioCodec: options.audioCodec ?? 'aac',\n });\n if (disabledReason) {\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 0.4,\n stage: 'encoding',\n message: disabledReason,\n });\n }\n\n // 2. Start Muxer\n muxManager.start({\n width,\n height,\n fps,\n enableAudio,\n output:\n exportMode === 'stream'\n ? {\n kind: 'stream',\n onData: (data, position) => {\n streamedBytes = Math.max(streamedBytes, position + data.byteLength);\n options.onMuxData?.(data, position);\n },\n chunkSize: options.muxChunkSizeBytes,\n chunked: options.muxChunked,\n }\n : { kind: 'blob' },\n });\n\n // 3. Process Video and Audio\n const mainTrack = model.tracks.find((t) => t.id === model.mainTrackId);\n if (mainTrack && mainTrack.clips.length > 0) {\n const audioPromise = enableAudio\n ? this.processAudioInWindows(model.durationUs, audioSession, muxManager, checkStatus)\n : Promise.resolve();\n\n await this.processVideoWindowed(mainTrack.clips, muxManager, model, checkStatus);\n\n try {\n await audioPromise;\n } catch (error) {\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 0.95,\n stage: 'encoding',\n message: `Audio skipped (runtime failure): ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n } else {\n console.warn('[ExportScheduler] No video clips found');\n }\n\n await audioSession.finalizeExportAudio();\n\n if (signal?.aborted) {\n throw new DOMException('Export aborted', 'AbortError');\n }\n\n // 4. Finalize\n const blob = await muxManager.finalize();\n\n eventBus.emit(MeframeEvent.ExportComplete, {\n size: blob?.size ?? streamedBytes,\n durationMs: model.durationUs / 1000,\n format: options.format || 'mp4',\n });\n\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 1,\n stage: 'muxing',\n });\n\n return blob;\n } catch (error) {\n eventBus.emit(MeframeEvent.ExportError, {\n error: error instanceof Error ? error : new Error(String(error)),\n stage: 'export',\n });\n throw error;\n } finally {\n this.deps.cacheManager.endExport();\n }\n }\n\n private async preloadResources(\n model: CompositionModel,\n resourceLoader: ResourceLoader,\n eventBus: EventBus<EventPayloadMap>,\n checkStatus: () => Promise<void>\n ): Promise<void> {\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 0,\n stage: 'preparing',\n message: 'Loading and parsing resources...',\n });\n\n const tracks = model.tracks.filter((track) => ['video', 'audio'].includes(track.kind));\n if (tracks.length === 0) return;\n\n const maxClipCount = Math.max(...tracks.map((track) => track.clips.length));\n const resourcesToLoad: string[] = [];\n const seen = new Set<string>();\n\n for (let clipIndex = 0; clipIndex < maxClipCount; clipIndex++) {\n for (const track of tracks) {\n const clip = track.clips[clipIndex];\n if (clip && hasResourceId(clip)) {\n if (!seen.has(clip.resourceId)) {\n seen.add(clip.resourceId);\n resourcesToLoad.push(clip.resourceId);\n }\n }\n }\n }\n\n const total = resourcesToLoad.length;\n let completed = 0;\n\n await Promise.all(\n resourcesToLoad.map(async (resourceId) => {\n await checkStatus();\n await resourceLoader.load(resourceId, { isPreload: false });\n\n const resource = model.getResource(resourceId);\n if (resource?.state === 'error') {\n const details = resource.error?.message ?? 'Unknown resource error';\n throw new Error(`Export preload failed for ${resourceId}: ${details}`);\n }\n\n completed++;\n\n const progress = total > 0 ? (completed / total) * 0.4 : 0.4;\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress,\n stage: 'preparing',\n message: `Loading resources... (${completed}/${total})`,\n });\n })\n );\n }\n\n private async processAudioInWindows(\n totalDurationUs: TimeUs,\n audioSession: AudioExportSession,\n muxManager: MuxManager,\n checkStatus: () => Promise<void>\n ): Promise<void> {\n const WINDOW_DURATION_US = 5 * 60 * 1_000_000;\n let currentUs = 0;\n\n while (currentUs < totalDurationUs) {\n await checkStatus();\n\n const endUs = Math.min(currentUs + WINDOW_DURATION_US, totalDurationUs);\n\n await audioSession.mixAndEncodeSegment(currentUs, endUs, (chunk, meta) =>\n muxManager.writeAudioChunk(chunk, meta)\n );\n\n this.deps.cacheManager.clearAudioCache();\n\n currentUs = endUs;\n }\n }\n\n /**\n * Windowed video processing: one ExportWorker for all clips and windows.\n * Memory bounded by window size (~150 frames).\n */\n private async processVideoWindowed(\n clips: Clip[],\n muxManager: MuxManager,\n model: CompositionModel,\n checkStatus: () => Promise<void>\n ) {\n const { workerPool, planner, cacheManager, resourceLoader } = this.deps;\n const workerConfigs = this.deps.workerConfigsProvider();\n\n // --- One-time setup: create and configure ExportWorker ---\n const exportWorker = await workerPool.getOrCreate('videoExport', 'export', { lazy: true });\n const exportConfig = workerConfigs.videoExport ?? {};\n\n // Apply render overrides from model\n const composeConfig = { ...(exportConfig.compose ?? {}) };\n const encodeConfig = { ...(exportConfig.encode ?? {}) };\n if (model.renderConfig?.width) {\n composeConfig.width = model.renderConfig.width;\n encodeConfig.width = model.renderConfig.width;\n }\n if (model.renderConfig?.height) {\n composeConfig.height = model.renderConfig.height;\n encodeConfig.height = model.renderConfig.height;\n }\n\n await exportWorker.send('configure', { compose: composeConfig, encode: encodeConfig });\n\n // --- Stream receiver: called once per window's encoded output ---\n let windowResolver!: () => void;\n let windowRejecter!: (err: any) => void;\n let nextClipStartUs = 0;\n let lastChunkEndUs = 0;\n\n exportWorker.receiveStream(async (stream, _metadata) => {\n const reader = stream.getReader();\n try {\n while (true) {\n await checkStatus();\n\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n\n const { chunk: originalChunk, metadata } = value as {\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n };\n const chunkDuration = originalChunk.duration ?? 33333;\n\n // Chunks arrive with clip-relative timestamps; remap to global timeline\n const remappedTimestamp = originalChunk.timestamp + nextClipStartUs;\n\n const buffer = new ArrayBuffer(originalChunk.byteLength);\n originalChunk.copyTo(buffer);\n\n const remappedChunk = new EncodedVideoChunk({\n type: originalChunk.type,\n timestamp: remappedTimestamp,\n duration: chunkDuration,\n data: buffer,\n });\n\n muxManager.writeVideoChunk(remappedChunk, metadata);\n\n lastChunkEndUs = remappedTimestamp + chunkDuration;\n\n const encodingProgress = remappedTimestamp / model.durationUs;\n const totalProgress = 0.4 + encodingProgress * 0.6;\n\n this.deps.eventBus.emit(MeframeEvent.ExportProgress, {\n progress: Math.min(1.0, totalProgress),\n stage: 'encoding',\n timeUs: remappedTimestamp,\n });\n }\n windowResolver();\n } catch (error) {\n windowRejecter(error);\n } finally {\n reader.releaseLock();\n }\n });\n\n // --- Per-clip + per-window processing ---\n try {\n for (const clip of clips) {\n await checkStatus();\n\n const resource = hasResourceId(clip) ? model.getResource(clip.resourceId) : null;\n if (!resource) {\n console.warn('[ExportScheduler] Resource not found for clip:', clip.id);\n continue;\n }\n\n const instructions = this.buildClipInstructions(clip, planner);\n\n const createWindowPromise = () =>\n new Promise<void>((resolve, reject) => {\n windowResolver = resolve;\n windowRejecter = reject;\n });\n\n if (resource.type === 'image') {\n await this.processImageClip(\n exportWorker,\n clip,\n resource,\n instructions,\n model,\n resourceLoader,\n createWindowPromise\n );\n } else {\n await this.processVideoClipWindowed(\n exportWorker,\n clip,\n resource,\n instructions,\n model,\n cacheManager,\n resourceLoader,\n checkStatus,\n createWindowPromise\n );\n }\n\n await exportWorker.send('dispose_clip');\n nextClipStartUs = lastChunkEndUs;\n }\n\n // Flush encoder to drain remaining frames\n await exportWorker.send('flush');\n } finally {\n workerPool.terminate('videoExport', 'export');\n }\n }\n\n private async processVideoClipWindowed(\n exportWorker: BaseWorker,\n clip: Clip,\n resource: Resource,\n instructions: ClipInstructionSet,\n model: CompositionModel,\n cacheManager: CacheManager,\n resourceLoader: ResourceLoader,\n checkStatus: () => Promise<void>,\n createWindowPromise: () => Promise<void>\n ) {\n await exportWorker.send('install_instructions', instructions);\n await this.loadAndTransferAttachments(exportWorker, clip, instructions, model, resourceLoader);\n\n const trimStartUs = clip.trimStartUs ?? 0;\n const trimEndUs = trimStartUs + clip.durationUs;\n const fps = model.fps ?? 30;\n\n const index = cacheManager.mp4IndexCache.get(resource.id);\n const gopIndex = index?.tracks?.video?.gopIndex;\n const windows =\n gopIndex && gopIndex.length > 0\n ? computeGOPAlignedWindows(gopIndex, trimStartUs, trimEndUs, VIDEO_WINDOW_DURATION_US)\n : computeFixedWindows(trimStartUs, trimEndUs, VIDEO_WINDOW_DURATION_US);\n\n for (const { startUs, endUs } of windows) {\n await checkStatus();\n\n const windowDone = createWindowPromise();\n\n const decodeSession = await VideoWindowDecodeSession.create({\n clipId: clip.id,\n resourceId: resource.id,\n targetTimeUs: trimStartUs,\n globalTimeUs: clip.startUs,\n mp4IndexCache: cacheManager.mp4IndexCache,\n cacheManager,\n compositionModel: model,\n resourceLoader,\n fps,\n });\n\n const frameStream = await decodeSession.decodeRangeToStream(startUs, endUs);\n\n await exportWorker.sendStream(frameStream, {\n streamType: 'video',\n windowStartUs: startUs,\n windowEndUs: endUs,\n });\n\n await windowDone;\n await decodeSession.dispose();\n }\n }\n\n private async processImageClip(\n exportWorker: BaseWorker,\n clip: Clip,\n resource: Resource,\n instructions: ClipInstructionSet,\n model: CompositionModel,\n resourceLoader: ResourceLoader,\n createWindowPromise: () => Promise<void>\n ) {\n await this.loadAndTransferAttachments(exportWorker, clip, instructions, model, resourceLoader);\n\n const clipDone = createWindowPromise();\n\n const imageBitmap = await resourceLoader.loadImage(resource);\n await exportWorker.send(\n 'receive_image',\n {\n resourceId: resource.id,\n sessionId: clip.id,\n imageBitmap,\n instructions,\n },\n { transfer: [imageBitmap] }\n );\n\n // ExportWorker's startImageFrameStream sends encoded stream back;\n // the receiveStream handler resolves clipDone when stream ends\n await clipDone;\n }\n\n private buildClipInstructions(clip: Clip, planner: CompositionPlanner): ClipInstructionSet {\n const plan = planner.buildClipPlan(clip, { cache: false });\n return plan.instructions;\n }\n\n private async loadAndTransferAttachments(\n exportWorker: BaseWorker,\n clip: Clip,\n instructions: ClipInstructionSet,\n model: CompositionModel,\n resourceLoader: ResourceLoader\n ): Promise<void> {\n const attachmentResources = this.extractAttachmentImageResources(instructions);\n if (attachmentResources.length === 0) return;\n\n await Promise.all(\n attachmentResources.map(async (resourceId) => {\n const resource = model.getResource(resourceId);\n if (!resource) return;\n\n const imageBitmap = await resourceLoader.loadImage(resource);\n if (!imageBitmap) return;\n\n await exportWorker.send(\n 'receive_image',\n { clipId: clip.id, resourceId, sessionId: clip.id, imageBitmap },\n { transfer: [imageBitmap] }\n );\n })\n );\n }\n\n private extractAttachmentImageResources(instructions: ClipInstructionSet): string[] {\n const resourceIds = new Set<string>();\n for (const layer of instructions.layers) {\n if (!layer.payload.attachmentId) continue;\n if (layer.type === 'image') {\n const payload = layer.payload;\n if (payload.oldResourceId) resourceIds.add(payload.oldResourceId);\n if (payload.resourceId) resourceIds.add(payload.resourceId);\n }\n }\n return Array.from(resourceIds);\n }\n\n private async decideAudioStrategy(input: {\n hasAudioSamples: boolean;\n requestedFormat: NonNullable<ExportOptions['format']>;\n requestedAudioCodec: NonNullable<ExportOptions['audioCodec']>;\n }): Promise<{ enableAudio: boolean; disabledReason?: string }> {\n if (!input.hasAudioSamples) {\n return { enableAudio: false };\n }\n\n if (input.requestedFormat !== 'mp4') {\n return {\n enableAudio: false,\n disabledReason: `Audio skipped: format ${input.requestedFormat} is not supported.`,\n };\n }\n\n if (input.requestedAudioCodec !== 'aac') {\n return {\n enableAudio: false,\n disabledReason: `Audio skipped: mp4 output currently supports only AAC (requested ${input.requestedAudioCodec}).`,\n };\n }\n\n if (typeof AudioEncoder === 'undefined') {\n return {\n enableAudio: false,\n disabledReason:\n 'Audio skipped: WebCodecs AudioEncoder is not available in this browser/runtime (AAC required for mp4).',\n };\n }\n\n const support = await AudioEncoder.isConfigSupported(AudioChunkEncoder.DEFAULT_CONFIG);\n if (!support.supported) {\n return {\n enableAudio: false,\n disabledReason:\n 'Audio skipped: WebCodecs AAC (mp4a.40.2) AudioEncoder is not supported in this browser/runtime.',\n };\n }\n\n return { enableAudio: true };\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ExportScheduler.js","sources":["../../src/orchestrator/ExportScheduler.ts"],"sourcesContent":["import { CompositionModel, Clip } from '../model';\nimport { ExportOptions } from '../types';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ResourceLoader } from '../stages/load/ResourceLoader';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { AudioExportSession } from './AudioExportSession';\nimport { VideoWindowDecodeSession } from './VideoWindowDecodeSession';\nimport { WorkerType } from '../worker/types';\nimport { hasResourceId, type TimeUs, type Resource } from '../model/types';\nimport type { ExportController } from '../controllers/ExportController';\nimport type { BaseWorker } from '../worker/BaseWorker';\nimport { EventBus } from '../event/EventBus';\nimport { MeframeEvent, EventPayloadMap } from '../event/events';\nimport { AudioChunkEncoder } from '../stages/encode/AudioChunkEncoder';\nimport type { ClipInstructionSet } from '../stages/compose/instructions';\nimport { computeGOPAlignedWindows, computeFixedWindows } from '../utils/time-utils';\n\ninterface ExportSchedulerDeps {\n workerPool: WorkerPool;\n planner: CompositionPlanner;\n cacheManager: CacheManager;\n resourceLoader: ResourceLoader;\n muxManager: MuxManager;\n audioSession: AudioExportSession;\n workerConfigsProvider: () => Record<WorkerType, any>;\n eventBus: EventBus<EventPayloadMap>;\n}\n\ninterface ExtendedExportOptions extends ExportOptions {\n signal?: AbortSignal;\n controller?: ExportController;\n exportMode?: 'blob' | 'stream';\n onMuxData?: (data: Uint8Array, position: number) => void;\n muxChunkSizeBytes?: number;\n muxChunked?: boolean;\n}\n\n// 5 seconds per window, ~150 frames at 30fps\nconst VIDEO_WINDOW_DURATION_US: TimeUs = 5_000_000;\n\nexport class ExportScheduler {\n private deps: ExportSchedulerDeps;\n\n constructor(deps: ExportSchedulerDeps) {\n this.deps = deps;\n }\n\n async execute(model: CompositionModel, options: ExtendedExportOptions): Promise<Blob | null> {\n this.deps.cacheManager.clear();\n\n const projectId = this.deps.cacheManager.resourceCache.projectId;\n\n console.info('[ExportScheduler] Export is starting, projectId:', projectId);\n\n if (!navigator.locks) {\n return this.executeInternal(model, options);\n }\n\n const lockName = `meframe-resource-${projectId}`;\n return navigator.locks.request(lockName, () => this.executeInternal(model, options));\n }\n\n private async executeInternal(\n model: CompositionModel,\n options: ExtendedExportOptions\n ): Promise<Blob | null> {\n const { muxManager, audioSession, eventBus, resourceLoader } = this.deps;\n const signal = options.signal;\n const controller = options.controller;\n const exportMode = options.exportMode ?? 'blob';\n\n console.info('[ExportScheduler] Export is starting, exportMode:', exportMode);\n\n if (exportMode === 'stream' && typeof options.onMuxData !== 'function') {\n throw new Error('onMuxData callback is required when exportMode is stream');\n }\n\n let streamedBytes = 0;\n\n const checkStatus = async () => {\n if (signal?.aborted) {\n throw new DOMException('Export aborted', 'AbortError');\n }\n if (controller?.isPaused()) {\n while (controller.isPaused()) {\n if (signal?.aborted) throw new DOMException('Export aborted', 'AbortError');\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n }\n };\n\n const width = options.width || model.renderConfig?.width || 720;\n const height = options.height || model.renderConfig?.height || 1280;\n const fps = options.fps || model.fps || 30;\n\n eventBus.emit(MeframeEvent.ExportStart, {\n format: options.format || 'mp4',\n width,\n height,\n fps,\n durationUs: model.durationUs,\n });\n\n this.deps.cacheManager.beginExport();\n\n try {\n // 1. Preload and parse all resources (0-40%)\n console.info('[ExportScheduler] Export is preloading resources');\n await this.preloadResources(model, resourceLoader, eventBus, checkStatus);\n\n const hasAudioSamples = this.deps.cacheManager.audioSampleCache.getTotalBytes() > 0;\n const { enableAudio, disabledReason } = await this.decideAudioStrategy({\n hasAudioSamples,\n requestedFormat: options.format ?? 'mp4',\n requestedAudioCodec: options.audioCodec ?? 'aac',\n });\n if (disabledReason) {\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 0.4,\n stage: 'encoding',\n message: disabledReason,\n });\n }\n\n // 2. Start Muxer\n console.info('[ExportScheduler] Export is starting muxer');\n muxManager.start({\n width,\n height,\n fps,\n enableAudio,\n output:\n exportMode === 'stream'\n ? {\n kind: 'stream',\n onData: (data, position) => {\n streamedBytes = Math.max(streamedBytes, position + data.byteLength);\n options.onMuxData?.(data, position);\n },\n chunkSize: options.muxChunkSizeBytes,\n chunked: options.muxChunked,\n }\n : { kind: 'blob' },\n });\n\n // 3. Process Video and Audio\n console.info('[ExportScheduler] Export is processing video and audio');\n const mainTrack = model.tracks.find((t) => t.id === model.mainTrackId);\n if (mainTrack && mainTrack.clips.length > 0) {\n const audioPromise = enableAudio\n ? this.processAudioInWindows(model.durationUs, audioSession, muxManager, checkStatus)\n : Promise.resolve();\n\n await this.processVideoWindowed(mainTrack.clips, muxManager, model, checkStatus);\n\n try {\n await audioPromise;\n } catch (error) {\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 0.95,\n stage: 'encoding',\n message: `Audio skipped (runtime failure): ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n } else {\n console.warn('[ExportScheduler] No video clips found');\n }\n\n await audioSession.finalizeExportAudio();\n\n if (signal?.aborted) {\n throw new DOMException('Export aborted', 'AbortError');\n }\n\n // 4. Finalize\n console.info('[ExportScheduler] Export is finalizing');\n const blob = await muxManager.finalize();\n\n eventBus.emit(MeframeEvent.ExportComplete, {\n size: blob?.size ?? streamedBytes,\n durationMs: model.durationUs / 1000,\n format: options.format || 'mp4',\n });\n\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 1,\n stage: 'muxing',\n });\n\n console.info('[ExportScheduler] Export is completed successfully');\n return blob;\n } catch (error) {\n console.error('[ExportScheduler] Export error:', error);\n eventBus.emit(MeframeEvent.ExportError, {\n error: error instanceof Error ? error : new Error(String(error)),\n stage: 'export',\n });\n throw error;\n } finally {\n console.info('[ExportScheduler] Export is completed');\n this.deps.cacheManager.endExport();\n }\n }\n\n private async preloadResources(\n model: CompositionModel,\n resourceLoader: ResourceLoader,\n eventBus: EventBus<EventPayloadMap>,\n checkStatus: () => Promise<void>\n ): Promise<void> {\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 0,\n stage: 'preparing',\n message: 'Loading and parsing resources...',\n });\n\n const tracks = model.tracks.filter((track) => ['video', 'audio'].includes(track.kind));\n if (tracks.length === 0) return;\n\n const maxClipCount = Math.max(...tracks.map((track) => track.clips.length));\n const resourcesToLoad: string[] = [];\n const seen = new Set<string>();\n\n for (let clipIndex = 0; clipIndex < maxClipCount; clipIndex++) {\n for (const track of tracks) {\n const clip = track.clips[clipIndex];\n if (clip && hasResourceId(clip)) {\n if (!seen.has(clip.resourceId)) {\n seen.add(clip.resourceId);\n resourcesToLoad.push(clip.resourceId);\n }\n }\n }\n }\n\n const total = resourcesToLoad.length;\n let completed = 0;\n\n await Promise.all(\n resourcesToLoad.map(async (resourceId) => {\n await checkStatus();\n await resourceLoader.load(resourceId, { isPreload: false });\n\n const resource = model.getResource(resourceId);\n if (resource?.state === 'error') {\n const details = resource.error?.message ?? 'Unknown resource error';\n throw new Error(`Export preload failed for ${resourceId}: ${details}`);\n }\n\n completed++;\n\n const progress = total > 0 ? (completed / total) * 0.4 : 0.4;\n eventBus.emit(MeframeEvent.ExportProgress, {\n progress,\n stage: 'preparing',\n message: `Loading resources... (${completed}/${total})`,\n });\n })\n );\n }\n\n private async processAudioInWindows(\n totalDurationUs: TimeUs,\n audioSession: AudioExportSession,\n muxManager: MuxManager,\n checkStatus: () => Promise<void>\n ): Promise<void> {\n const WINDOW_DURATION_US = 5 * 60 * 1_000_000;\n let currentUs = 0;\n\n while (currentUs < totalDurationUs) {\n await checkStatus();\n\n const endUs = Math.min(currentUs + WINDOW_DURATION_US, totalDurationUs);\n\n await audioSession.mixAndEncodeSegment(currentUs, endUs, (chunk, meta) =>\n muxManager.writeAudioChunk(chunk, meta)\n );\n\n this.deps.cacheManager.clearAudioCache();\n\n currentUs = endUs;\n }\n }\n\n /**\n * Windowed video processing: one ExportWorker for all clips and windows.\n * Memory bounded by window size (~150 frames).\n */\n private async processVideoWindowed(\n clips: Clip[],\n muxManager: MuxManager,\n model: CompositionModel,\n checkStatus: () => Promise<void>\n ) {\n const { workerPool, planner, cacheManager, resourceLoader } = this.deps;\n const workerConfigs = this.deps.workerConfigsProvider();\n\n // --- One-time setup: create and configure ExportWorker ---\n const exportWorker = await workerPool.getOrCreate('videoExport', 'export', { lazy: true });\n const exportConfig = workerConfigs.videoExport ?? {};\n\n // Apply render overrides from model\n const composeConfig = { ...(exportConfig.compose ?? {}) };\n const encodeConfig = { ...(exportConfig.encode ?? {}) };\n if (model.renderConfig?.width) {\n composeConfig.width = model.renderConfig.width;\n encodeConfig.width = model.renderConfig.width;\n }\n if (model.renderConfig?.height) {\n composeConfig.height = model.renderConfig.height;\n encodeConfig.height = model.renderConfig.height;\n }\n\n await exportWorker.send('configure', { compose: composeConfig, encode: encodeConfig });\n\n // --- Stream receiver: called once per window's encoded output ---\n let windowResolver!: () => void;\n let windowRejecter!: (err: any) => void;\n let nextClipStartUs = 0;\n let lastChunkEndUs = 0;\n\n exportWorker.receiveStream(async (stream, _metadata) => {\n const reader = stream.getReader();\n try {\n while (true) {\n await checkStatus();\n\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n\n const { chunk: originalChunk, metadata } = value as {\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n };\n const chunkDuration = originalChunk.duration ?? 33333;\n\n // Chunks arrive with clip-relative timestamps; remap to global timeline\n const remappedTimestamp = originalChunk.timestamp + nextClipStartUs;\n\n const buffer = new ArrayBuffer(originalChunk.byteLength);\n originalChunk.copyTo(buffer);\n\n const remappedChunk = new EncodedVideoChunk({\n type: originalChunk.type,\n timestamp: remappedTimestamp,\n duration: chunkDuration,\n data: buffer,\n });\n\n muxManager.writeVideoChunk(remappedChunk, metadata);\n\n lastChunkEndUs = remappedTimestamp + chunkDuration;\n\n const encodingProgress = remappedTimestamp / model.durationUs;\n const totalProgress = 0.4 + encodingProgress * 0.6;\n\n this.deps.eventBus.emit(MeframeEvent.ExportProgress, {\n progress: Math.min(1.0, totalProgress),\n stage: 'encoding',\n timeUs: remappedTimestamp,\n });\n }\n windowResolver();\n } catch (error) {\n windowRejecter(error);\n } finally {\n reader.releaseLock();\n }\n });\n\n // --- Per-clip + per-window processing ---\n try {\n for (const clip of clips) {\n await checkStatus();\n\n const resource = hasResourceId(clip) ? model.getResource(clip.resourceId) : null;\n if (!resource) {\n console.warn('[ExportScheduler] Resource not found for clip:', clip.id);\n continue;\n }\n\n const instructions = this.buildClipInstructions(clip, planner);\n\n const createWindowPromise = () =>\n new Promise<void>((resolve, reject) => {\n windowResolver = resolve;\n windowRejecter = reject;\n });\n\n if (resource.type === 'image') {\n await this.processImageClip(\n exportWorker,\n clip,\n resource,\n instructions,\n model,\n resourceLoader,\n createWindowPromise\n );\n } else {\n await this.processVideoClipWindowed(\n exportWorker,\n clip,\n resource,\n instructions,\n model,\n cacheManager,\n resourceLoader,\n checkStatus,\n createWindowPromise\n );\n }\n\n await exportWorker.send('dispose_clip');\n nextClipStartUs = lastChunkEndUs;\n }\n\n // Flush encoder to drain remaining frames\n await exportWorker.send('flush');\n } finally {\n workerPool.terminate('videoExport', 'export');\n }\n }\n\n private async processVideoClipWindowed(\n exportWorker: BaseWorker,\n clip: Clip,\n resource: Resource,\n instructions: ClipInstructionSet,\n model: CompositionModel,\n cacheManager: CacheManager,\n resourceLoader: ResourceLoader,\n checkStatus: () => Promise<void>,\n createWindowPromise: () => Promise<void>\n ) {\n await exportWorker.send('install_instructions', instructions);\n await this.loadAndTransferAttachments(exportWorker, clip, instructions, model, resourceLoader);\n\n const trimStartUs = clip.trimStartUs ?? 0;\n const trimEndUs = trimStartUs + clip.durationUs;\n const fps = model.fps ?? 30;\n\n const index = cacheManager.mp4IndexCache.get(resource.id);\n const gopIndex = index?.tracks?.video?.gopIndex;\n const windows =\n gopIndex && gopIndex.length > 0\n ? computeGOPAlignedWindows(gopIndex, trimStartUs, trimEndUs, VIDEO_WINDOW_DURATION_US)\n : computeFixedWindows(trimStartUs, trimEndUs, VIDEO_WINDOW_DURATION_US);\n\n for (const { startUs, endUs } of windows) {\n await checkStatus();\n\n const windowDone = createWindowPromise();\n\n const decodeSession = await VideoWindowDecodeSession.create({\n clipId: clip.id,\n resourceId: resource.id,\n targetTimeUs: trimStartUs,\n globalTimeUs: clip.startUs,\n mp4IndexCache: cacheManager.mp4IndexCache,\n cacheManager,\n compositionModel: model,\n resourceLoader,\n fps,\n });\n\n const frameStream = await decodeSession.decodeRangeToStream(startUs, endUs);\n\n await exportWorker.sendStream(frameStream, {\n streamType: 'video',\n windowStartUs: startUs,\n windowEndUs: endUs,\n });\n\n await windowDone;\n await decodeSession.dispose();\n }\n }\n\n private async processImageClip(\n exportWorker: BaseWorker,\n clip: Clip,\n resource: Resource,\n instructions: ClipInstructionSet,\n model: CompositionModel,\n resourceLoader: ResourceLoader,\n createWindowPromise: () => Promise<void>\n ) {\n await this.loadAndTransferAttachments(exportWorker, clip, instructions, model, resourceLoader);\n\n const clipDone = createWindowPromise();\n\n const imageBitmap = await resourceLoader.loadImage(resource);\n await exportWorker.send(\n 'receive_image',\n {\n resourceId: resource.id,\n sessionId: clip.id,\n imageBitmap,\n instructions,\n },\n { transfer: [imageBitmap] }\n );\n\n // ExportWorker's startImageFrameStream sends encoded stream back;\n // the receiveStream handler resolves clipDone when stream ends\n await clipDone;\n }\n\n private buildClipInstructions(clip: Clip, planner: CompositionPlanner): ClipInstructionSet {\n const plan = planner.buildClipPlan(clip, { cache: false });\n return plan.instructions;\n }\n\n private async loadAndTransferAttachments(\n exportWorker: BaseWorker,\n clip: Clip,\n instructions: ClipInstructionSet,\n model: CompositionModel,\n resourceLoader: ResourceLoader\n ): Promise<void> {\n const attachmentResources = this.extractAttachmentImageResources(instructions);\n if (attachmentResources.length === 0) return;\n\n await Promise.all(\n attachmentResources.map(async (resourceId) => {\n const resource = model.getResource(resourceId);\n if (!resource) return;\n\n const imageBitmap = await resourceLoader.loadImage(resource);\n if (!imageBitmap) return;\n\n await exportWorker.send(\n 'receive_image',\n { clipId: clip.id, resourceId, sessionId: clip.id, imageBitmap },\n { transfer: [imageBitmap] }\n );\n })\n );\n }\n\n private extractAttachmentImageResources(instructions: ClipInstructionSet): string[] {\n const resourceIds = new Set<string>();\n for (const layer of instructions.layers) {\n if (!layer.payload.attachmentId) continue;\n if (layer.type === 'image') {\n const payload = layer.payload;\n if (payload.oldResourceId) resourceIds.add(payload.oldResourceId);\n if (payload.resourceId) resourceIds.add(payload.resourceId);\n }\n }\n return Array.from(resourceIds);\n }\n\n private async decideAudioStrategy(input: {\n hasAudioSamples: boolean;\n requestedFormat: NonNullable<ExportOptions['format']>;\n requestedAudioCodec: NonNullable<ExportOptions['audioCodec']>;\n }): Promise<{ enableAudio: boolean; disabledReason?: string }> {\n if (!input.hasAudioSamples) {\n return { enableAudio: false };\n }\n\n if (input.requestedFormat !== 'mp4') {\n return {\n enableAudio: false,\n disabledReason: `Audio skipped: format ${input.requestedFormat} is not supported.`,\n };\n }\n\n if (input.requestedAudioCodec !== 'aac') {\n return {\n enableAudio: false,\n disabledReason: `Audio skipped: mp4 output currently supports only AAC (requested ${input.requestedAudioCodec}).`,\n };\n }\n\n if (typeof AudioEncoder === 'undefined') {\n return {\n enableAudio: false,\n disabledReason:\n 'Audio skipped: WebCodecs AudioEncoder is not available in this browser/runtime (AAC required for mp4).',\n };\n }\n\n const support = await AudioEncoder.isConfigSupported(AudioChunkEncoder.DEFAULT_CONFIG);\n if (!support.supported) {\n return {\n enableAudio: false,\n disabledReason:\n 'Audio skipped: WebCodecs AAC (mp4a.40.2) AudioEncoder is not supported in this browser/runtime.',\n };\n }\n\n return { enableAudio: true };\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseEncoder.d.ts","sourceRoot":"","sources":["../../../src/stages/encode/BaseEncoder.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,yBAAyB,CAAC;CACrC;AAED,8BAAsB,WAAW,CAC/B,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,kBAAkB,GAAG,kBAAkB,EACvD,MAAM,SAAS,UAAU,GAAG,SAAS,EACrC,MAAM,SAAS,iBAAiB,GAAG,iBAAiB,EACpD,SAAS,SAAS,yBAAyB,GAAG,yBAAyB;IAEvE,SAAS,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,UAAU,EAAE,gCAAgC,CAAC,YAAY,CAAC,GAAG,IAAI,CAAQ;gBAEvE,MAAM,EAAE,OAAO;IAI3B,SAAS,IAAI,OAAO;IAIpB,SAAS,KAAK,aAAa,IAAI,OAAO,CAErC;IAED,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO;IAW/D,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAiBlD,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAIjD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,IAAI;IAehE,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAiBhD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ;IAC7D,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,MAAM;IAG3C,SAAS,CAAC,OAAO,IAAI,IAAI;IAKzB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEzD;;;OAGG;IACH,YAAY,IAAI,eAAe,CAAC,MAAM,EAAE,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"BaseEncoder.d.ts","sourceRoot":"","sources":["../../../src/stages/encode/BaseEncoder.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,yBAAyB,CAAC;CACrC;AAED,8BAAsB,WAAW,CAC/B,QAAQ,SAAS,YAAY,GAAG,YAAY,EAC5C,OAAO,SAAS,kBAAkB,GAAG,kBAAkB,EACvD,MAAM,SAAS,UAAU,GAAG,SAAS,EACrC,MAAM,SAAS,iBAAiB,GAAG,iBAAiB,EACpD,SAAS,SAAS,yBAAyB,GAAG,yBAAyB;IAEvE,SAAS,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,UAAU,EAAE,gCAAgC,CAAC,YAAY,CAAC,GAAG,IAAI,CAAQ;gBAEvE,MAAM,EAAE,OAAO;IAI3B,SAAS,IAAI,OAAO;IAIpB,SAAS,KAAK,aAAa,IAAI,OAAO,CAErC;IAED,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO;IAW/D,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAiBlD,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAIjD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,GAAG,IAAI;IAehE,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAiBhD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IACtF,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,QAAQ;IAC7D,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,MAAM;IAG3C,SAAS,CAAC,OAAO,IAAI,IAAI;IAKzB,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IAEzD;;;OAGG;IACH,YAAY,IAAI,eAAe,CAAC,MAAM,EAAE,YAAY,CAAC;IAuDrD,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CACrC;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,KAAK,IAAI,CAAC;IAC5C,KAAK,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACtC"}
|
|
@@ -39,7 +39,7 @@ class BaseEncoder {
|
|
|
39
39
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
40
40
|
}
|
|
41
41
|
async initialize() {
|
|
42
|
-
console.info("Encoder is initializing
|
|
42
|
+
console.info("Encoder is initializing, config:", this.config);
|
|
43
43
|
if (this.encoder?.state === "configured") {
|
|
44
44
|
console.warn("Encoder is already initialized, skipping initialization");
|
|
45
45
|
return;
|
|
@@ -156,14 +156,10 @@ class BaseEncoder {
|
|
|
156
156
|
},
|
|
157
157
|
transform: async (input) => {
|
|
158
158
|
if (!this.isReady) {
|
|
159
|
-
console.warn("Encoder is not ready, initializing...");
|
|
159
|
+
console.warn("Encoder is not ready, initializing 1...");
|
|
160
160
|
await this.initialize();
|
|
161
161
|
}
|
|
162
|
-
if (
|
|
163
|
-
console.error("Encoder not configured, throwing error");
|
|
164
|
-
throw new Error("Encoder not configured");
|
|
165
|
-
}
|
|
166
|
-
if (this.encoder.encodeQueueSize >= this.encodeQueueThreshold) {
|
|
162
|
+
if (this.encoder && this.encoder.encodeQueueSize >= this.encodeQueueThreshold) {
|
|
167
163
|
await new Promise((resolve) => {
|
|
168
164
|
const check = () => {
|
|
169
165
|
if (!this.encoder || this.encoder.encodeQueueSize < this.encodeQueueThreshold - 1) {
|
|
@@ -175,21 +171,16 @@ class BaseEncoder {
|
|
|
175
171
|
check();
|
|
176
172
|
});
|
|
177
173
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
this.
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
console.error("Encoder error
|
|
184
|
-
|
|
185
|
-
console.warn("Codec reclaimed due to inactivity, reset encoder...");
|
|
186
|
-
this.encoder = void 0;
|
|
187
|
-
await this.initialize();
|
|
188
|
-
this.encode(frame);
|
|
189
|
-
} else {
|
|
190
|
-
throw err;
|
|
191
|
-
}
|
|
174
|
+
if (!this.isReady) {
|
|
175
|
+
console.warn("Encoder is not ready, initializing 2...");
|
|
176
|
+
await this.initialize();
|
|
177
|
+
}
|
|
178
|
+
if (!this.encoder || this.encoder.state !== "configured") {
|
|
179
|
+
console.error("Encoder not configured, throwing error");
|
|
180
|
+
throw new Error("Encoder not configured");
|
|
192
181
|
}
|
|
182
|
+
const frame = input.frame || input;
|
|
183
|
+
this.encode(frame);
|
|
193
184
|
},
|
|
194
185
|
flush: async () => {
|
|
195
186
|
await this.flush();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseEncoder.js","sources":["../../../src/stages/encode/BaseEncoder.ts"],"sourcesContent":["// Base encoder implementation\n\n/**\n * Base encoder class for both video and audio encoding\n * Handles common WebCodecs encoder operations\n */\n\nexport interface EncoderChunk {\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n}\n\nexport abstract class BaseEncoder<\n TEncoder extends VideoEncoder | AudioEncoder,\n TConfig extends VideoEncoderConfig | AudioEncoderConfig,\n TInput extends VideoFrame | AudioData,\n TChunk extends EncodedVideoChunk | EncodedAudioChunk,\n TMetadata extends EncodedVideoChunkMetadata | EncodedAudioChunkMetadata,\n> {\n protected encoder?: TEncoder;\n protected config: TConfig;\n protected controller: TransformStreamDefaultController<EncoderChunk> | null = null;\n\n constructor(config: TConfig) {\n this.config = config;\n }\n\n getConfig(): TConfig {\n return { ...this.config };\n }\n\n protected get currentConfig(): TConfig {\n return this.config;\n }\n\n protected shouldReconfigure(partial: Partial<TConfig>): boolean {\n const next = { ...this.config, ...partial } as TConfig;\n const keys = Object.keys(partial ?? {}) as Array<keyof TConfig>;\n for (const key of keys) {\n if (partial[key] !== undefined && next[key] !== this.config[key]) {\n return true;\n }\n }\n return false;\n }\n\n protected hasConfigChanged(next: TConfig): boolean {\n const currentEntries = Object.entries(this.config) as Array<[keyof TConfig, any]>;\n for (const [key, value] of currentEntries) {\n if (next[key] !== value) {\n return true;\n }\n }\n\n for (const key of Object.keys(next) as Array<keyof TConfig>) {\n if (this.config[key] !== next[key]) {\n return true;\n }\n }\n\n return false;\n }\n\n protected configsEqual(a: TConfig, b: TConfig): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n }\n\n async initialize(): Promise<void> {\n console.info('Encoder is initializing...');\n if (this.encoder?.state === 'configured') {\n console.warn('Encoder is already initialized, skipping initialization');\n return;\n }\n\n const isSupported = await this.isConfigSupported(this.config);\n\n if (!isSupported.supported) {\n console.warn('Codec not supported:', JSON.stringify(this.config));\n throw new Error(`Codec not supported: ${JSON.stringify(this.config)}`);\n }\n\n this.encoder = this.createEncoder({\n output: this.handleOutput.bind(this),\n error: this.handleError.bind(this),\n });\n\n (this.encoder as any).configure(this.config);\n console.info('Encoder is initialized');\n }\n\n async reconfigure(config: Partial<TConfig>): Promise<void> {\n console.info('Encoder is reconfiguring...');\n if (!config || Object.keys(config).length === 0) {\n return;\n }\n\n const nextConfig = { ...this.config, ...config } as TConfig;\n\n if (this.configsEqual(this.config, nextConfig)) {\n return;\n }\n\n if (!this.encoder) {\n this.config = nextConfig;\n await this.initialize();\n return;\n }\n\n if (this.encoder.state === 'configured') {\n await this.encoder.flush();\n }\n\n const isSupported = await this.isConfigSupported(nextConfig);\n if (!isSupported.supported) {\n throw new Error(`New configuration not supported: ${nextConfig.codec}`);\n }\n\n this.config = nextConfig;\n (this.encoder as any).configure(this.config);\n console.info('Encoder is reconfigured');\n }\n\n async flush(): Promise<void> {\n if (!this.encoder) {\n return;\n }\n\n await this.encoder.flush();\n }\n\n async reset(): Promise<void> {\n console.info('Encoder is resetting...');\n if (!this.encoder) {\n console.warn('Encoder is not initialized, skipping reset');\n return;\n }\n\n this.encoder.reset();\n this.onReset();\n console.info('Encoder is resetted');\n }\n\n async close(): Promise<void> {\n console.info('Encoder is closing...');\n if (!this.encoder) {\n return;\n }\n\n if (this.encoder.state === 'configured') {\n await this.encoder.flush();\n }\n\n this.encoder.close();\n this.encoder = undefined;\n console.info('Encoder is closed');\n }\n\n get isReady(): boolean {\n return this.encoder?.state === 'configured';\n }\n\n get queueSize(): number {\n return this.encoder?.encodeQueueSize ?? 0;\n }\n\n protected handleOutput(chunk: TChunk, metadata: TMetadata): void {\n // Only enqueue if controller exists and stream is not closed\n if (this.controller) {\n try {\n this.controller.enqueue({ chunk, metadata });\n } catch (error) {\n console.error('Encoder output error:', error);\n // Stream may be closed during flush, ignore enqueue errors\n if (!(error instanceof TypeError && error.message.includes('closed'))) {\n throw error;\n }\n }\n }\n }\n\n protected handleError(error: DOMException): void {\n // Codec reclaimed by browser due to inactivity, let transform re-initialize on next frame\n if (error.message.includes('reclaimed')) {\n console.warn('Encoder reclaimed by browser due to inactivity, skipping error handling');\n return;\n }\n console.error(`[${this.getEncoderType()}Encoder] Encode error:`, {\n name: error.name,\n message: error.message,\n encoderState: this.encoder?.state,\n queueSize: this.queueSize,\n platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',\n });\n this.controller?.error(error);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract isConfigSupported(config: TConfig): Promise<{ supported: boolean }>;\n protected abstract createEncoder(init: EncoderInit): TEncoder;\n protected abstract getEncoderType(): string;\n\n // Hook for subclasses to handle reset\n protected onReset(): void {\n // Override in subclasses if needed\n }\n\n // Abstract properties for backpressure configuration\n protected abstract readonly highWaterMark: number;\n protected abstract readonly encodeQueueThreshold: number;\n\n /**\n * Create transform stream for encoding\n * Implements common stream logic with backpressure handling\n */\n createStream(): TransformStream<TInput, EncoderChunk> {\n return new TransformStream<TInput, EncoderChunk>(\n {\n start: async (controller) => {\n this.controller = controller;\n },\n\n transform: async (input) => {\n // Lazy init: create encoder on first frame or re-create after codec reclaim\n if (!this.isReady) {\n console.warn('Encoder is not ready, initializing...');\n await this.initialize();\n }\n if (!this.encoder || this.encoder.state !== 'configured') {\n console.error('Encoder not configured, throwing error');\n throw new Error('Encoder not configured');\n }\n\n // Check encoder queue pressure\n if (this.encoder.encodeQueueSize >= this.encodeQueueThreshold) {\n // Wait for queue to drain\n await new Promise<void>((resolve) => {\n const check = () => {\n if (!this.encoder || this.encoder.encodeQueueSize < this.encodeQueueThreshold - 1) {\n resolve();\n } else {\n setTimeout(check, 10);\n }\n };\n check();\n });\n }\n\n // Encode the input\n const frame = (input as any).frame || input;\n try {\n this.encode(frame);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('Encoder error:', msg);\n if (err instanceof DOMException && msg.includes('reclaimed')) {\n console.warn('Codec reclaimed due to inactivity, reset encoder...');\n this.encoder = undefined;\n await this.initialize();\n this.encode(frame);\n } else {\n throw err;\n }\n }\n },\n\n flush: async () => {\n await this.flush();\n },\n },\n // Queuing strategy with backpressure configuration\n {\n highWaterMark: this.highWaterMark,\n size: () => 1, // Count-based\n }\n );\n }\n\n // Abstract method for encoding\n abstract encode(input: TInput): void;\n}\n\ninterface EncoderInit {\n output: (chunk: any, metadata: any) => void;\n error: (error: DOMException) => void;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BaseEncoder.js","sources":["../../../src/stages/encode/BaseEncoder.ts"],"sourcesContent":["// Base encoder implementation\n\n/**\n * Base encoder class for both video and audio encoding\n * Handles common WebCodecs encoder operations\n */\n\nexport interface EncoderChunk {\n chunk: EncodedVideoChunk;\n metadata: EncodedVideoChunkMetadata;\n}\n\nexport abstract class BaseEncoder<\n TEncoder extends VideoEncoder | AudioEncoder,\n TConfig extends VideoEncoderConfig | AudioEncoderConfig,\n TInput extends VideoFrame | AudioData,\n TChunk extends EncodedVideoChunk | EncodedAudioChunk,\n TMetadata extends EncodedVideoChunkMetadata | EncodedAudioChunkMetadata,\n> {\n protected encoder?: TEncoder;\n protected config: TConfig;\n protected controller: TransformStreamDefaultController<EncoderChunk> | null = null;\n\n constructor(config: TConfig) {\n this.config = config;\n }\n\n getConfig(): TConfig {\n return { ...this.config };\n }\n\n protected get currentConfig(): TConfig {\n return this.config;\n }\n\n protected shouldReconfigure(partial: Partial<TConfig>): boolean {\n const next = { ...this.config, ...partial } as TConfig;\n const keys = Object.keys(partial ?? {}) as Array<keyof TConfig>;\n for (const key of keys) {\n if (partial[key] !== undefined && next[key] !== this.config[key]) {\n return true;\n }\n }\n return false;\n }\n\n protected hasConfigChanged(next: TConfig): boolean {\n const currentEntries = Object.entries(this.config) as Array<[keyof TConfig, any]>;\n for (const [key, value] of currentEntries) {\n if (next[key] !== value) {\n return true;\n }\n }\n\n for (const key of Object.keys(next) as Array<keyof TConfig>) {\n if (this.config[key] !== next[key]) {\n return true;\n }\n }\n\n return false;\n }\n\n protected configsEqual(a: TConfig, b: TConfig): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n }\n\n async initialize(): Promise<void> {\n console.info('Encoder is initializing, config:', this.config);\n if (this.encoder?.state === 'configured') {\n console.warn('Encoder is already initialized, skipping initialization');\n return;\n }\n\n const isSupported = await this.isConfigSupported(this.config);\n\n if (!isSupported.supported) {\n console.warn('Codec not supported:', JSON.stringify(this.config));\n throw new Error(`Codec not supported: ${JSON.stringify(this.config)}`);\n }\n\n this.encoder = this.createEncoder({\n output: this.handleOutput.bind(this),\n error: this.handleError.bind(this),\n });\n\n (this.encoder as any).configure(this.config);\n console.info('Encoder is initialized');\n }\n\n async reconfigure(config: Partial<TConfig>): Promise<void> {\n console.info('Encoder is reconfiguring...');\n if (!config || Object.keys(config).length === 0) {\n return;\n }\n\n const nextConfig = { ...this.config, ...config } as TConfig;\n\n if (this.configsEqual(this.config, nextConfig)) {\n return;\n }\n\n if (!this.encoder) {\n this.config = nextConfig;\n await this.initialize();\n return;\n }\n\n if (this.encoder.state === 'configured') {\n await this.encoder.flush();\n }\n\n const isSupported = await this.isConfigSupported(nextConfig);\n if (!isSupported.supported) {\n throw new Error(`New configuration not supported: ${nextConfig.codec}`);\n }\n\n this.config = nextConfig;\n (this.encoder as any).configure(this.config);\n console.info('Encoder is reconfigured');\n }\n\n async flush(): Promise<void> {\n if (!this.encoder) {\n return;\n }\n\n await this.encoder.flush();\n }\n\n async reset(): Promise<void> {\n console.info('Encoder is resetting...');\n if (!this.encoder) {\n console.warn('Encoder is not initialized, skipping reset');\n return;\n }\n\n this.encoder.reset();\n this.onReset();\n console.info('Encoder is resetted');\n }\n\n async close(): Promise<void> {\n console.info('Encoder is closing...');\n if (!this.encoder) {\n return;\n }\n\n if (this.encoder.state === 'configured') {\n await this.encoder.flush();\n }\n\n this.encoder.close();\n this.encoder = undefined;\n console.info('Encoder is closed');\n }\n\n get isReady(): boolean {\n return this.encoder?.state === 'configured';\n }\n\n get queueSize(): number {\n return this.encoder?.encodeQueueSize ?? 0;\n }\n\n protected handleOutput(chunk: TChunk, metadata: TMetadata): void {\n // Only enqueue if controller exists and stream is not closed\n if (this.controller) {\n try {\n this.controller.enqueue({ chunk, metadata });\n } catch (error) {\n console.error('Encoder output error:', error);\n // Stream may be closed during flush, ignore enqueue errors\n if (!(error instanceof TypeError && error.message.includes('closed'))) {\n throw error;\n }\n }\n }\n }\n\n protected handleError(error: DOMException): void {\n // Codec reclaimed by browser due to inactivity, let transform re-initialize on next frame\n if (error.message.includes('reclaimed')) {\n console.warn('Encoder reclaimed by browser due to inactivity, skipping error handling');\n return;\n }\n console.error(`[${this.getEncoderType()}Encoder] Encode error:`, {\n name: error.name,\n message: error.message,\n encoderState: this.encoder?.state,\n queueSize: this.queueSize,\n platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',\n });\n this.controller?.error(error);\n }\n\n // Abstract methods to be implemented by subclasses\n protected abstract isConfigSupported(config: TConfig): Promise<{ supported: boolean }>;\n protected abstract createEncoder(init: EncoderInit): TEncoder;\n protected abstract getEncoderType(): string;\n\n // Hook for subclasses to handle reset\n protected onReset(): void {\n // Override in subclasses if needed\n }\n\n // Abstract properties for backpressure configuration\n protected abstract readonly highWaterMark: number;\n protected abstract readonly encodeQueueThreshold: number;\n\n /**\n * Create transform stream for encoding\n * Implements common stream logic with backpressure handling\n */\n createStream(): TransformStream<TInput, EncoderChunk> {\n return new TransformStream<TInput, EncoderChunk>(\n {\n start: async (controller) => {\n this.controller = controller;\n },\n\n transform: async (input) => {\n // Lazy init: create encoder on first frame or re-create after codec reclaim\n if (!this.isReady) {\n console.warn('Encoder is not ready, initializing 1...');\n await this.initialize();\n }\n\n // Backpressure: wait for encoder queue to drain\n if (this.encoder && this.encoder.encodeQueueSize >= this.encodeQueueThreshold) {\n await new Promise<void>((resolve) => {\n const check = () => {\n if (!this.encoder || this.encoder.encodeQueueSize < this.encodeQueueThreshold - 1) {\n resolve();\n } else {\n setTimeout(check, 10);\n }\n };\n check();\n });\n }\n\n // Re-check after backpressure await: codec may be reclaimed during the wait\n if (!this.isReady) {\n console.warn('Encoder is not ready, initializing 2...');\n await this.initialize();\n }\n if (!this.encoder || this.encoder.state !== 'configured') {\n console.error('Encoder not configured, throwing error');\n throw new Error('Encoder not configured');\n }\n\n const frame = (input as any).frame || input;\n this.encode(frame);\n },\n\n flush: async () => {\n await this.flush();\n },\n },\n // Queuing strategy with backpressure configuration\n {\n highWaterMark: this.highWaterMark,\n size: () => 1, // Count-based\n }\n );\n }\n\n // Abstract method for encoding\n abstract encode(input: TInput): void;\n}\n\ninterface EncoderInit {\n output: (chunk: any, metadata: any) => void;\n error: (error: DOMException) => void;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -2322,7 +2322,7 @@ class BaseEncoder {
|
|
|
2322
2322
|
return JSON.stringify(a) === JSON.stringify(b);
|
|
2323
2323
|
}
|
|
2324
2324
|
async initialize() {
|
|
2325
|
-
console.info("Encoder is initializing
|
|
2325
|
+
console.info("Encoder is initializing, config:", this.config);
|
|
2326
2326
|
if (this.encoder?.state === "configured") {
|
|
2327
2327
|
console.warn("Encoder is already initialized, skipping initialization");
|
|
2328
2328
|
return;
|
|
@@ -2439,14 +2439,10 @@ class BaseEncoder {
|
|
|
2439
2439
|
},
|
|
2440
2440
|
transform: async (input) => {
|
|
2441
2441
|
if (!this.isReady) {
|
|
2442
|
-
console.warn("Encoder is not ready, initializing...");
|
|
2442
|
+
console.warn("Encoder is not ready, initializing 1...");
|
|
2443
2443
|
await this.initialize();
|
|
2444
2444
|
}
|
|
2445
|
-
if (
|
|
2446
|
-
console.error("Encoder not configured, throwing error");
|
|
2447
|
-
throw new Error("Encoder not configured");
|
|
2448
|
-
}
|
|
2449
|
-
if (this.encoder.encodeQueueSize >= this.encodeQueueThreshold) {
|
|
2445
|
+
if (this.encoder && this.encoder.encodeQueueSize >= this.encodeQueueThreshold) {
|
|
2450
2446
|
await new Promise((resolve) => {
|
|
2451
2447
|
const check = () => {
|
|
2452
2448
|
if (!this.encoder || this.encoder.encodeQueueSize < this.encodeQueueThreshold - 1) {
|
|
@@ -2458,21 +2454,16 @@ class BaseEncoder {
|
|
|
2458
2454
|
check();
|
|
2459
2455
|
});
|
|
2460
2456
|
}
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
this.
|
|
2464
|
-
}
|
|
2465
|
-
|
|
2466
|
-
console.error("Encoder error
|
|
2467
|
-
|
|
2468
|
-
console.warn("Codec reclaimed due to inactivity, reset encoder...");
|
|
2469
|
-
this.encoder = void 0;
|
|
2470
|
-
await this.initialize();
|
|
2471
|
-
this.encode(frame);
|
|
2472
|
-
} else {
|
|
2473
|
-
throw err;
|
|
2474
|
-
}
|
|
2457
|
+
if (!this.isReady) {
|
|
2458
|
+
console.warn("Encoder is not ready, initializing 2...");
|
|
2459
|
+
await this.initialize();
|
|
2460
|
+
}
|
|
2461
|
+
if (!this.encoder || this.encoder.state !== "configured") {
|
|
2462
|
+
console.error("Encoder not configured, throwing error");
|
|
2463
|
+
throw new Error("Encoder not configured");
|
|
2475
2464
|
}
|
|
2465
|
+
const frame = input.frame || input;
|
|
2466
|
+
this.encode(frame);
|
|
2476
2467
|
},
|
|
2477
2468
|
flush: async () => {
|
|
2478
2469
|
await this.flush();
|
|
@@ -3252,4 +3243,4 @@ const export_worker = null;
|
|
|
3252
3243
|
export {
|
|
3253
3244
|
export_worker as default
|
|
3254
3245
|
};
|
|
3255
|
-
//# sourceMappingURL=export.worker.
|
|
3246
|
+
//# sourceMappingURL=export.worker.DCStS1mL.js.map
|