@meframe/core 0.0.46 → 0.0.47
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 +7 -1
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +13 -0
- package/dist/Meframe.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/errors.d.ts +25 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +11 -0
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/platform-utils.d.ts +15 -0
- package/dist/utils/platform-utils.d.ts.map +1 -1
- package/dist/utils/platform-utils.js +68 -0
- package/dist/utils/platform-utils.js.map +1 -1
- package/dist/workers/stages/decode/video-decode.worker.BQtw6eWn.js.map +1 -1
- package/package.json +1 -1
package/dist/Meframe.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { CompositionModel } from './model/CompositionModel';
|
|
|
5
5
|
import { PluginManager } from './plugins/PluginManager';
|
|
6
6
|
import { EventBus } from './event/EventBus';
|
|
7
7
|
import { EventPayloadMap } from './event/events';
|
|
8
|
+
import { checkBrowserCompatibility } from './utils/platform-utils';
|
|
8
9
|
|
|
9
10
|
export declare class Meframe {
|
|
10
11
|
/** Current state - managed internally via setState() */
|
|
@@ -22,6 +23,11 @@ export declare class Meframe {
|
|
|
22
23
|
private playbackController;
|
|
23
24
|
private exportController;
|
|
24
25
|
private canvas?;
|
|
26
|
+
/**
|
|
27
|
+
* Check if current browser is compatible with Meframe
|
|
28
|
+
* Returns compatibility info including browser name, version, and missing features
|
|
29
|
+
*/
|
|
30
|
+
static checkCompatibility(): ReturnType<typeof checkBrowserCompatibility>;
|
|
25
31
|
private constructor();
|
|
26
32
|
/**
|
|
27
33
|
* Create a new Meframe instance
|
|
@@ -43,7 +49,7 @@ export declare class Meframe {
|
|
|
43
49
|
* Start preview and return a handle for control
|
|
44
50
|
*/
|
|
45
51
|
startPreview(options?: {
|
|
46
|
-
canvas?: HTMLCanvasElement
|
|
52
|
+
canvas?: HTMLCanvasElement;
|
|
47
53
|
startUs?: TimeUs;
|
|
48
54
|
autoStart?: boolean;
|
|
49
55
|
}): PreviewHandle;
|
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;
|
|
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;IAuBxF;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAcxD;;OAEG;IACH,YAAY,CAAC,OAAO,CAAC,EAAE;QACrB,MAAM,CAAC,EAAE,iBAAiB,CAAC;QAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,GAAG,aAAa;IA6BjB;;;OAGG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IA+CnD;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAYjC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB9B;;OAEG;IACH,OAAO,CAAC,QAAQ;IAYhB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;OAEG;IACH,OAAO,CAAC,WAAW;CAWpB"}
|
package/dist/Meframe.js
CHANGED
|
@@ -7,6 +7,8 @@ import { ExportController } from "./controllers/ExportController.js";
|
|
|
7
7
|
import { PluginManager } from "./plugins/PluginManager.js";
|
|
8
8
|
import { EventBus } from "./event/EventBus.js";
|
|
9
9
|
import { MeframeEvent } from "./event/events.js";
|
|
10
|
+
import { checkBrowserCompatibility } from "./utils/platform-utils.js";
|
|
11
|
+
import { BrowserCompatibilityError } from "./utils/errors.js";
|
|
10
12
|
class Meframe {
|
|
11
13
|
/** Current state - managed internally via setState() */
|
|
12
14
|
state = "idle";
|
|
@@ -23,6 +25,13 @@ class Meframe {
|
|
|
23
25
|
playbackController = null;
|
|
24
26
|
exportController;
|
|
25
27
|
canvas;
|
|
28
|
+
/**
|
|
29
|
+
* Check if current browser is compatible with Meframe
|
|
30
|
+
* Returns compatibility info including browser name, version, and missing features
|
|
31
|
+
*/
|
|
32
|
+
static checkCompatibility() {
|
|
33
|
+
return checkBrowserCompatibility();
|
|
34
|
+
}
|
|
26
35
|
constructor(config) {
|
|
27
36
|
this.config = config;
|
|
28
37
|
this.eventBus = new EventBus();
|
|
@@ -42,6 +51,10 @@ class Meframe {
|
|
|
42
51
|
* Create a new Meframe instance
|
|
43
52
|
*/
|
|
44
53
|
static async create(config = {}) {
|
|
54
|
+
const compatibility = checkBrowserCompatibility();
|
|
55
|
+
if (!compatibility.webCodecsAvailable || !compatibility.opfsAvailable) {
|
|
56
|
+
throw new BrowserCompatibilityError(compatibility);
|
|
57
|
+
}
|
|
45
58
|
const resolvedConfig = await loadConfig({ override: config });
|
|
46
59
|
const instance = new Meframe(resolvedConfig);
|
|
47
60
|
await instance.initialize();
|
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';\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 | OffscreenCanvas;\n\n private constructor(config: ResolvedConfig) {\n this.config = config;\n this.eventBus = new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n this.pluginManager = new PluginManager(this);\n\n // Initialize orchestrator with configuration and shared eventBus\n // Worker paths are passed via config\n this.orchestrator = new Orchestrator({\n projectId: config.global.projectId,\n maxWorkers: (config as any).maxWorkers,\n cacheConfig: config.cache as any,\n eventBus: this.eventBus,\n workerPath: config.global.workerPath,\n workerExtension: config.global.workerExtension,\n });\n\n this.exportController = new ExportController(this.orchestrator);\n }\n\n /**\n * Create a new Meframe instance\n */\n static async create(config: MeframeConfig = {}): Promise<Meframe> {\n // Load and resolve configuration using ConfigLoader\n const resolvedConfig = await loadConfig({ override: config });\n const instance = new Meframe(resolvedConfig);\n await instance.initialize();\n return instance;\n }\n\n /**\n * Initialize the core engine\n */\n private async initialize(): Promise<void> {\n this.setState('loading');\n\n try {\n // Initialize orchestrator (sets up workers and cache)\n await this.orchestrator.initialize();\n\n // Initialize plugins if provided\n const plugins = (this.config as any).plugins;\n if (plugins && Array.isArray(plugins)) {\n for (const plugin of plugins) {\n // Ensure plugin type matches the Plugin interface\n if (plugin && typeof plugin === 'object' && 'name' in plugin && 'install' in plugin) {\n this.pluginManager.register(plugin as Plugin);\n }\n }\n }\n\n this.setState('idle');\n } catch (error) {\n this.setState('error');\n throw error;\n }\n }\n\n /**\n * Set the composition model\n */\n async setCompositionModel(model: CompositionModel | CompositionModelData): Promise<void> {\n this.ensureNotDestroyed();\n\n // Convert plain object to CompositionModel instance if needed\n const compositionModel =\n model instanceof CompositionModel ? model : new CompositionModel(model);\n\n // Set the model in orchestrator\n await this.orchestrator.setCompositionModel(compositionModel);\n this.model = compositionModel;\n\n this.setState('ready');\n this.eventBus.emit(MeframeEvent.Ready, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce(\n (acc: number, track: any) => acc + track.clips?.length || 0,\n 0\n ),\n durationUs: model.durationUs,\n });\n // await this.playbackController?.renderCover();\n }\n\n /**\n * Apply a patch to the composition model\n */\n async applyPatch(patch: CompositionPatch): Promise<void> {\n this.ensureNotDestroyed();\n\n if (!this.model) {\n throw new Error('No composition model set');\n }\n\n // Apply patch through orchestrator\n await this.orchestrator.applyPatch(patch);\n\n // Patch is applied to the model by orchestrator\n this.model = this.orchestrator.compositionModel!;\n }\n\n /**\n * Start preview and return a handle for control\n */\n startPreview(options?: {\n canvas?: HTMLCanvasElement | OffscreenCanvas;\n startUs?: TimeUs;\n 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): Promise<Blob> {\n this.ensureReady();\n this.playbackController?.pause();\n this.setState('exporting');\n\n try {\n const model = this.model;\n if (!model) {\n throw new Error('No composition model set');\n }\n\n const width = options.width || (model as any).renderConfig?.width || 720;\n const height = options.height || (model as any).renderConfig?.height || 1280;\n const fps = options.fps || model.fps || 30;\n\n this.eventBus.emit(MeframeEvent.ExportStart, {\n format: options.format || 'mp4',\n width,\n height,\n fps,\n durationUs: model.durationUs,\n });\n\n // Delegate to ExportController\n const blob = await this.exportController.export(model, options);\n\n this.setState('ready');\n this.eventBus.emit(MeframeEvent.ExportComplete, {\n size: blob.size,\n durationMs: model.durationUs / 1000,\n format: options.format || 'mp4',\n });\n this.eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 1,\n });\n\n return blob;\n } catch (error) {\n this.setState('error');\n this.eventBus.emit(MeframeEvent.ExportError, {\n error: error as Error,\n stage: 'export',\n });\n throw error;\n }\n }\n\n /**\n * Get current playback time\n */\n getCurrentTime(): number {\n return this.playbackController?.currentTimeUs || 0;\n }\n\n /**\n * Clear all L1/L2 cache data\n * Useful for debugging or forcing re-render\n */\n async clearCache(): Promise<void> {\n this.ensureReady();\n\n // Stop playback first\n this.playbackController?.stop();\n\n // Clear cache through CacheManager\n await this.orchestrator.cacheManager.clear();\n\n console.log('[Meframe] Cache cleared successfully');\n }\n\n /**\n * Clean up and destroy the instance\n */\n async dispose(): Promise<void> {\n if (this.state === 'destroyed') return;\n\n // Stop playback\n this.playbackController?.stop();\n this.playbackController?.dispose();\n this.playbackController = null;\n\n // Cleanup plugins\n await this.pluginManager.disposeAll();\n\n // Dispose orchestrator (stops workers and clears cache)\n await this.orchestrator.dispose();\n\n // Clear event bus\n this.eventBus.dispose();\n\n this.setState('destroyed');\n }\n\n /**\n * Set internal state\n */\n private setState(state: MeframeState): void {\n const oldState = this.state;\n this.state = state;\n\n // Emit state change event\n // Use a generic event for now, can be added to MeframeEvent enum later\n this.eventBus.emit('state:changed' as any, {\n oldState,\n newState: state,\n });\n }\n\n /**\n * Ensure the instance is not destroyed\n */\n private ensureNotDestroyed(): void {\n if (this.state === 'destroyed') {\n throw new Error('Core instance is destroyed');\n }\n }\n\n /**\n * Ensure the instance is ready\n */\n private ensureReady(): void {\n this.ensureNotDestroyed();\n\n if (!this.model) {\n throw new Error('No composition model set');\n }\n\n if (this.state === 'loading' || this.state === 'idle') {\n throw new Error('Core is not ready');\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
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 // 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): Promise<Blob> {\n this.ensureReady();\n this.playbackController?.pause();\n this.setState('exporting');\n\n try {\n const model = this.model;\n if (!model) {\n throw new Error('No composition model set');\n }\n\n const width = options.width || (model as any).renderConfig?.width || 720;\n const height = options.height || (model as any).renderConfig?.height || 1280;\n const fps = options.fps || model.fps || 30;\n\n this.eventBus.emit(MeframeEvent.ExportStart, {\n format: options.format || 'mp4',\n width,\n height,\n fps,\n durationUs: model.durationUs,\n });\n\n // Delegate to ExportController\n const blob = await this.exportController.export(model, options);\n\n this.setState('ready');\n this.eventBus.emit(MeframeEvent.ExportComplete, {\n size: blob.size,\n durationMs: model.durationUs / 1000,\n format: options.format || 'mp4',\n });\n this.eventBus.emit(MeframeEvent.ExportProgress, {\n progress: 1,\n });\n\n return blob;\n } catch (error) {\n this.setState('error');\n this.eventBus.emit(MeframeEvent.ExportError, {\n error: error as Error,\n stage: 'export',\n });\n throw error;\n }\n }\n\n /**\n * Get current playback time\n */\n getCurrentTime(): number {\n return this.playbackController?.currentTimeUs || 0;\n }\n\n /**\n * Clear all L1/L2 cache data\n * Useful for debugging or forcing re-render\n */\n async clearCache(): Promise<void> {\n this.ensureReady();\n\n // Stop playback first\n this.playbackController?.stop();\n\n // Clear cache through CacheManager\n await this.orchestrator.cacheManager.clear();\n\n console.log('[Meframe] Cache cleared successfully');\n }\n\n /**\n * Clean up and destroy the instance\n */\n async dispose(): Promise<void> {\n if (this.state === 'destroyed') return;\n\n // Stop playback\n this.playbackController?.stop();\n this.playbackController?.dispose();\n this.playbackController = null;\n\n // Cleanup plugins\n await this.pluginManager.disposeAll();\n\n // Dispose orchestrator (stops workers and clears cache)\n await this.orchestrator.dispose();\n\n // Clear event bus\n this.eventBus.dispose();\n\n this.setState('destroyed');\n }\n\n /**\n * Set internal state\n */\n private setState(state: MeframeState): void {\n const oldState = this.state;\n this.state = state;\n\n // Emit state change event\n // Use a generic event for now, can be added to MeframeEvent enum later\n this.eventBus.emit('state:changed' as any, {\n oldState,\n newState: state,\n });\n }\n\n /**\n * Ensure the instance is not destroyed\n */\n private ensureNotDestroyed(): void {\n if (this.state === 'destroyed') {\n throw new Error('Core instance is destroyed');\n }\n }\n\n /**\n * Ensure the instance is ready\n */\n private ensureReady(): void {\n this.ensureNotDestroyed();\n\n if (!this.model) {\n throw new Error('No composition model set');\n }\n\n if (this.state === 'loading' || this.state === 'idle') {\n throw new Error('Core is not ready');\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,5 +10,6 @@ export type { CompositionModelData, CompositionPatch, DirtyRange, TimeUs, Track,
|
|
|
10
10
|
export type { CacheConfig, CacheStats } from './cache/types';
|
|
11
11
|
export type { Plugin, PluginHook } from './plugins/types';
|
|
12
12
|
export { setupCanvasDPI, createHiDPICanvas, checkCanvasDPI } from './utils/canvas-utils';
|
|
13
|
+
export { BrowserCompatibilityError } from './utils/errors';
|
|
13
14
|
export declare const VERSION = "0.0.1";
|
|
14
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,YAAY,EACV,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,MAAM,EACN,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,UAAU,EACV,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,WAAW,GACZ,MAAM,eAAe,CAAC;AAGvB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG7D,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,YAAY,EACV,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,EACV,MAAM,EACN,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,UAAU,EACV,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,WAAW,GACZ,MAAM,eAAe,CAAC;AAGvB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAG7D,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEzF,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAG3D,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,10 @@ import { Meframe } from "./Meframe.js";
|
|
|
2
2
|
import { MeframeEvent } from "./event/events.js";
|
|
3
3
|
import { CompositionModel } from "./model/CompositionModel.js";
|
|
4
4
|
import { checkCanvasDPI, createHiDPICanvas, setupCanvasDPI } from "./utils/canvas-utils.js";
|
|
5
|
+
import { BrowserCompatibilityError } from "./utils/errors.js";
|
|
5
6
|
const VERSION = "0.0.1";
|
|
6
7
|
export {
|
|
8
|
+
BrowserCompatibilityError,
|
|
7
9
|
CompositionModel,
|
|
8
10
|
Meframe,
|
|
9
11
|
MeframeEvent,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * @meframe/core-next - Next generation media processing framework\n * Based on WebCodecs API for high-performance video/audio processing\n */\n\n// Core exports\nexport { Meframe } from './Meframe';\nexport type { MeframeConfig, MeframeState } from './types';\nexport { MeframeEvent } from './event/events';\n\n// Model exports\nexport { CompositionModel } from './model/CompositionModel';\nexport type {\n CompositionModelData,\n CompositionPatch,\n DirtyRange,\n TimeUs,\n Track,\n Clip,\n Resource,\n Effect,\n Transition,\n Attachment,\n AnimationEffect,\n AnimationKeyframe,\n Transform2D,\n} from './model/types';\n\n// Cache exports\nexport type { CacheConfig, CacheStats } from './cache/types';\n\n// Plugin exports\nexport type { Plugin, PluginHook } from './plugins/types';\n\n// Utility exports\nexport { setupCanvasDPI, createHiDPICanvas, checkCanvasDPI } from './utils/canvas-utils';\n\n// Re-export version\nexport const VERSION = '0.0.1';\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/**\n * @meframe/core-next - Next generation media processing framework\n * Based on WebCodecs API for high-performance video/audio processing\n */\n\n// Core exports\nexport { Meframe } from './Meframe';\nexport type { MeframeConfig, MeframeState } from './types';\nexport { MeframeEvent } from './event/events';\n\n// Model exports\nexport { CompositionModel } from './model/CompositionModel';\nexport type {\n CompositionModelData,\n CompositionPatch,\n DirtyRange,\n TimeUs,\n Track,\n Clip,\n Resource,\n Effect,\n Transition,\n Attachment,\n AnimationEffect,\n AnimationKeyframe,\n Transform2D,\n} from './model/types';\n\n// Cache exports\nexport type { CacheConfig, CacheStats } from './cache/types';\n\n// Plugin exports\nexport type { Plugin, PluginHook } from './plugins/types';\n\n// Utility exports\nexport { setupCanvasDPI, createHiDPICanvas, checkCanvasDPI } from './utils/canvas-utils';\n\nexport { BrowserCompatibilityError } from './utils/errors';\n\n// Re-export version\nexport const VERSION = '0.0.1';\n"],"names":[],"mappings":";;;;;AAwCO,MAAM,UAAU;"}
|
package/dist/utils/errors.d.ts
CHANGED
|
@@ -36,4 +36,29 @@ export declare class OPFSQuotaExceededError extends Error {
|
|
|
36
36
|
readonly retryable: boolean;
|
|
37
37
|
constructor(projectId: string, prefix: string, retryable: boolean);
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Error thrown when browser doesn't meet minimum requirements
|
|
41
|
+
*/
|
|
42
|
+
export declare class BrowserCompatibilityError extends Error {
|
|
43
|
+
readonly compatibility: {
|
|
44
|
+
webCodecsAvailable: boolean;
|
|
45
|
+
opfsAvailable: boolean;
|
|
46
|
+
missingFeatures: string[];
|
|
47
|
+
browserInfo: {
|
|
48
|
+
name: string;
|
|
49
|
+
version: string;
|
|
50
|
+
recommended: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
constructor(compatibility: {
|
|
54
|
+
webCodecsAvailable: boolean;
|
|
55
|
+
opfsAvailable: boolean;
|
|
56
|
+
missingFeatures: string[];
|
|
57
|
+
browserInfo: {
|
|
58
|
+
name: string;
|
|
59
|
+
version: string;
|
|
60
|
+
recommended: string;
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
}
|
|
39
64
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;aAChB,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CAI3C;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,UAAU,EAAE,MAAM;aAClB,UAAU,EAAE,MAAM;gBADlB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM;CAQrC;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM;gBADd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM;CAKjC;AAED;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,SAAS,EAAE,MAAM;aACjB,MAAM,EAAE,MAAM;aACd,SAAS,EAAE,OAAO;gBAFlB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO;CAQrC"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;aAChB,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM;CAI3C;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,UAAU,EAAE,MAAM;aAClB,UAAU,EAAE,MAAM;gBADlB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM;CAQrC;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM;gBADd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM;CAKjC;AAED;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,SAAS,EAAE,MAAM;aACjB,MAAM,EAAE,MAAM;aACd,SAAS,EAAE,OAAO;gBAFlB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,OAAO;CAQrC;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;aAEhC,aAAa,EAAE;QAC7B,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE;gBALe,aAAa,EAAE;QAC7B,kBAAkB,EAAE,OAAO,CAAC;QAC5B,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC;KACrE;CAYJ"}
|
package/dist/utils/errors.js
CHANGED
|
@@ -34,7 +34,18 @@ class OPFSQuotaExceededError extends Error {
|
|
|
34
34
|
this.name = "OPFSQuotaExceededError";
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
class BrowserCompatibilityError extends Error {
|
|
38
|
+
constructor(compatibility) {
|
|
39
|
+
const { browserInfo, missingFeatures } = compatibility;
|
|
40
|
+
super(
|
|
41
|
+
`Browser not supported: ${browserInfo.name} ${browserInfo.version}. Required: ${browserInfo.recommended}. Missing: ${missingFeatures.join(", ")}`
|
|
42
|
+
);
|
|
43
|
+
this.compatibility = compatibility;
|
|
44
|
+
this.name = "BrowserCompatibilityError";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
37
47
|
export {
|
|
48
|
+
BrowserCompatibilityError,
|
|
38
49
|
EmptyStreamError,
|
|
39
50
|
OPFSQuotaExceededError,
|
|
40
51
|
ResourceCorruptedError,
|
package/dist/utils/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sources":["../../src/utils/errors.ts"],"sourcesContent":["/**\n * Custom error types for Meframe\n */\n\n/**\n * Error thrown when a clip ready waiter is replaced by a newer one\n * This happens during rapid seeks and should be ignored by callers\n */\nexport class WaiterReplacedError extends Error {\n constructor(public readonly clipId: string) {\n super(`Waiter for clip ${clipId} was replaced by a newer waiter`);\n this.name = 'WaiterReplacedError';\n }\n}\n\n/**\n * Error thrown when reading an empty or incomplete OPFS file\n * Usually indicates a race condition between write and read operations\n */\nexport class EmptyStreamError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly fileOffset: number\n ) {\n super(\n `Empty stream received for resource ${resourceId}. ` +\n `File offset: ${fileOffset}. This indicates the file is being written or is corrupted.`\n );\n this.name = 'EmptyStreamError';\n }\n}\n\n/**\n * Error thrown when a cached resource file is corrupted or incomplete\n */\nexport class ResourceCorruptedError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly reason: string\n ) {\n super(`Resource ${resourceId} is corrupted: ${reason}`);\n this.name = 'ResourceCorruptedError';\n }\n}\n\n/**\n * Error thrown when OPFS quota is exceeded\n * Used for project-level LRU eviction coordination\n */\nexport class OPFSQuotaExceededError extends Error {\n constructor(\n public readonly projectId: string,\n public readonly prefix: string,\n public readonly retryable: boolean\n ) {\n super(\n `OPFS quota exceeded for ${prefix}-${projectId}. ` +\n (retryable ? 'Old projects evicted, please retry.' : 'No space available.')\n );\n this.name = 'OPFSQuotaExceededError';\n }\n}\n"],"names":[],"mappings":"AAQO,MAAM,4BAA4B,MAAM;AAAA,EAC7C,YAA4B,QAAgB;AAC1C,UAAM,mBAAmB,MAAM,iCAAiC;AADtC,SAAA,SAAA;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,yBAAyB,MAAM;AAAA,EAC1C,YACkB,YACA,YAChB;AACA;AAAA,MACE,sCAAsC,UAAU,kBAC9B,UAAU;AAAA,IAAA;AALd,SAAA,aAAA;AACA,SAAA,aAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,YACA,QAChB;AACA,UAAM,YAAY,UAAU,kBAAkB,MAAM,EAAE;AAHtC,SAAA,aAAA;AACA,SAAA,SAAA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,WACA,QACA,WAChB;AACA;AAAA,MACE,2BAA2B,MAAM,IAAI,SAAS,QAC3C,YAAY,wCAAwC;AAAA,IAAA;AANzC,SAAA,YAAA;AACA,SAAA,SAAA;AACA,SAAA,YAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;"}
|
|
1
|
+
{"version":3,"file":"errors.js","sources":["../../src/utils/errors.ts"],"sourcesContent":["/**\n * Custom error types for Meframe\n */\n\n/**\n * Error thrown when a clip ready waiter is replaced by a newer one\n * This happens during rapid seeks and should be ignored by callers\n */\nexport class WaiterReplacedError extends Error {\n constructor(public readonly clipId: string) {\n super(`Waiter for clip ${clipId} was replaced by a newer waiter`);\n this.name = 'WaiterReplacedError';\n }\n}\n\n/**\n * Error thrown when reading an empty or incomplete OPFS file\n * Usually indicates a race condition between write and read operations\n */\nexport class EmptyStreamError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly fileOffset: number\n ) {\n super(\n `Empty stream received for resource ${resourceId}. ` +\n `File offset: ${fileOffset}. This indicates the file is being written or is corrupted.`\n );\n this.name = 'EmptyStreamError';\n }\n}\n\n/**\n * Error thrown when a cached resource file is corrupted or incomplete\n */\nexport class ResourceCorruptedError extends Error {\n constructor(\n public readonly resourceId: string,\n public readonly reason: string\n ) {\n super(`Resource ${resourceId} is corrupted: ${reason}`);\n this.name = 'ResourceCorruptedError';\n }\n}\n\n/**\n * Error thrown when OPFS quota is exceeded\n * Used for project-level LRU eviction coordination\n */\nexport class OPFSQuotaExceededError extends Error {\n constructor(\n public readonly projectId: string,\n public readonly prefix: string,\n public readonly retryable: boolean\n ) {\n super(\n `OPFS quota exceeded for ${prefix}-${projectId}. ` +\n (retryable ? 'Old projects evicted, please retry.' : 'No space available.')\n );\n this.name = 'OPFSQuotaExceededError';\n }\n}\n\n/**\n * Error thrown when browser doesn't meet minimum requirements\n */\nexport class BrowserCompatibilityError extends Error {\n constructor(\n public readonly compatibility: {\n webCodecsAvailable: boolean;\n opfsAvailable: boolean;\n missingFeatures: string[];\n browserInfo: { name: string; version: string; recommended: string };\n }\n ) {\n const { browserInfo, missingFeatures } = compatibility;\n\n super(\n `Browser not supported: ${browserInfo.name} ${browserInfo.version}. ` +\n `Required: ${browserInfo.recommended}. ` +\n `Missing: ${missingFeatures.join(', ')}`\n );\n\n this.name = 'BrowserCompatibilityError';\n }\n}\n"],"names":[],"mappings":"AAQO,MAAM,4BAA4B,MAAM;AAAA,EAC7C,YAA4B,QAAgB;AAC1C,UAAM,mBAAmB,MAAM,iCAAiC;AADtC,SAAA,SAAA;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,yBAAyB,MAAM;AAAA,EAC1C,YACkB,YACA,YAChB;AACA;AAAA,MACE,sCAAsC,UAAU,kBAC9B,UAAU;AAAA,IAAA;AALd,SAAA,aAAA;AACA,SAAA,aAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,YACA,QAChB;AACA,UAAM,YAAY,UAAU,kBAAkB,MAAM,EAAE;AAHtC,SAAA,aAAA;AACA,SAAA,SAAA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAMO,MAAM,+BAA+B,MAAM;AAAA,EAChD,YACkB,WACA,QACA,WAChB;AACA;AAAA,MACE,2BAA2B,MAAM,IAAI,SAAS,QAC3C,YAAY,wCAAwC;AAAA,IAAA;AANzC,SAAA,YAAA;AACA,SAAA,SAAA;AACA,SAAA,YAAA;AAMhB,SAAK,OAAO;AAAA,EACd;AACF;AAKO,MAAM,kCAAkC,MAAM;AAAA,EACnD,YACkB,eAMhB;AACA,UAAM,EAAE,aAAa,gBAAA,IAAoB;AAEzC;AAAA,MACE,0BAA0B,YAAY,IAAI,IAAI,YAAY,OAAO,eAClD,YAAY,WAAW,cACxB,gBAAgB,KAAK,IAAI,CAAC;AAAA,IAAA;AAZ1B,SAAA,gBAAA;AAehB,SAAK,OAAO;AAAA,EACd;AACF;"}
|
|
@@ -43,4 +43,19 @@ export declare function isLinux(): boolean;
|
|
|
43
43
|
* @returns Platform-recommended hardware acceleration setting
|
|
44
44
|
*/
|
|
45
45
|
export declare function getRecommendedHardwareAcceleration(): HardwareAcceleration;
|
|
46
|
+
export interface BrowserCompatibility {
|
|
47
|
+
webCodecsAvailable: boolean;
|
|
48
|
+
opfsAvailable: boolean;
|
|
49
|
+
missingFeatures: string[];
|
|
50
|
+
browserInfo: {
|
|
51
|
+
name: string;
|
|
52
|
+
version: string;
|
|
53
|
+
recommended: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check browser compatibility for WebCodecs and OPFS (sync, < 1ms, cached)
|
|
58
|
+
* Only checks API existence, no file operations
|
|
59
|
+
*/
|
|
60
|
+
export declare function checkBrowserCompatibility(): BrowserCompatibility;
|
|
46
61
|
//# sourceMappingURL=platform-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-utils.d.ts","sourceRoot":"","sources":["../../src/utils/platform-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD;;GAEG;AACH,wBAAgB,cAAc,IAAI,YAAY,CAiB7C;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,OAAO,CAEjC;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,OAAO,CAEjC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kCAAkC,IAAI,oBAAoB,CAQzE"}
|
|
1
|
+
{"version":3,"file":"platform-utils.d.ts","sourceRoot":"","sources":["../../src/utils/platform-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD;;GAEG;AACH,wBAAgB,cAAc,IAAI,YAAY,CAiB7C;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,OAAO,CAEjC;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,OAAO,CAEjC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kCAAkC,IAAI,oBAAoB,CAQzE;AAED,MAAM,WAAW,oBAAoB;IACnC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAID;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,oBAAoB,CAoFhE"}
|
|
@@ -23,7 +23,75 @@ function getRecommendedHardwareAcceleration() {
|
|
|
23
23
|
}
|
|
24
24
|
return "no-preference";
|
|
25
25
|
}
|
|
26
|
+
let cachedCompatibility = null;
|
|
27
|
+
function checkBrowserCompatibility() {
|
|
28
|
+
if (cachedCompatibility) return cachedCompatibility;
|
|
29
|
+
const missingFeatures = [];
|
|
30
|
+
let webCodecsAvailable = true;
|
|
31
|
+
let opfsAvailable = false;
|
|
32
|
+
if (typeof VideoDecoder === "undefined") {
|
|
33
|
+
missingFeatures.push("VideoDecoder");
|
|
34
|
+
webCodecsAvailable = false;
|
|
35
|
+
}
|
|
36
|
+
if (typeof VideoEncoder === "undefined") {
|
|
37
|
+
missingFeatures.push("VideoEncoder");
|
|
38
|
+
webCodecsAvailable = false;
|
|
39
|
+
}
|
|
40
|
+
if (typeof VideoFrame === "undefined") {
|
|
41
|
+
missingFeatures.push("VideoFrame");
|
|
42
|
+
webCodecsAvailable = false;
|
|
43
|
+
}
|
|
44
|
+
if (typeof EncodedVideoChunk === "undefined") {
|
|
45
|
+
missingFeatures.push("EncodedVideoChunk");
|
|
46
|
+
webCodecsAvailable = false;
|
|
47
|
+
}
|
|
48
|
+
const userAgent = typeof navigator !== "undefined" ? navigator.userAgent : "";
|
|
49
|
+
let browserName = "Unknown";
|
|
50
|
+
let browserVersion = "Unknown";
|
|
51
|
+
let recommended = "Chrome 94+ / Edge 94+ / Safari 26+";
|
|
52
|
+
if (userAgent.includes("Edg/")) {
|
|
53
|
+
browserName = "Edge";
|
|
54
|
+
const match = userAgent.match(/Edg\/(\d+)/);
|
|
55
|
+
browserVersion = match?.[1] ?? "Unknown";
|
|
56
|
+
recommended = "Edge 94+";
|
|
57
|
+
} else if (userAgent.includes("Chrome/")) {
|
|
58
|
+
browserName = "Chrome";
|
|
59
|
+
const match = userAgent.match(/Chrome\/(\d+)/);
|
|
60
|
+
browserVersion = match?.[1] ?? "Unknown";
|
|
61
|
+
recommended = "Chrome 94+";
|
|
62
|
+
} else if (userAgent.includes("Safari/") && !userAgent.includes("Chrome")) {
|
|
63
|
+
browserName = "Safari";
|
|
64
|
+
const match = userAgent.match(/Version\/(\d+)/);
|
|
65
|
+
browserVersion = match?.[1] ?? "Unknown";
|
|
66
|
+
recommended = "Safari 26+";
|
|
67
|
+
} else if (userAgent.includes("Firefox/")) {
|
|
68
|
+
browserName = "Firefox";
|
|
69
|
+
const match = userAgent.match(/Firefox\/(\d+)/);
|
|
70
|
+
browserVersion = match?.[1] ?? "Unknown";
|
|
71
|
+
recommended = "Not supported";
|
|
72
|
+
}
|
|
73
|
+
if (webCodecsAvailable) {
|
|
74
|
+
if (typeof navigator !== "undefined" && navigator.storage && typeof navigator.storage.getDirectory === "function") {
|
|
75
|
+
if (browserName === "Safari") {
|
|
76
|
+
missingFeatures.push("Safari not supported (compatibility issues)");
|
|
77
|
+
opfsAvailable = false;
|
|
78
|
+
} else {
|
|
79
|
+
opfsAvailable = true;
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
missingFeatures.push("OPFS");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
cachedCompatibility = {
|
|
86
|
+
webCodecsAvailable,
|
|
87
|
+
opfsAvailable,
|
|
88
|
+
missingFeatures,
|
|
89
|
+
browserInfo: { name: browserName, version: browserVersion, recommended }
|
|
90
|
+
};
|
|
91
|
+
return cachedCompatibility;
|
|
92
|
+
}
|
|
26
93
|
export {
|
|
94
|
+
checkBrowserCompatibility,
|
|
27
95
|
detectPlatform,
|
|
28
96
|
getRecommendedHardwareAcceleration,
|
|
29
97
|
isWindows
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-utils.js","sources":["../../src/utils/platform-utils.ts"],"sourcesContent":["/**\n * Platform detection utilities with caching for performance\n */\n\nexport interface PlatformInfo {\n isWindows: boolean;\n isMacOS: boolean;\n isLinux: boolean;\n platform: string;\n userAgent: string;\n}\n\n// Cache platform detection result (only detect once)\nlet cachedPlatformInfo: PlatformInfo | null = null;\n\n/**\n * Detect current platform (cached after first call)\n */\nexport function detectPlatform(): PlatformInfo {\n if (cachedPlatformInfo) {\n return cachedPlatformInfo;\n }\n\n const platform = typeof navigator !== 'undefined' ? navigator.platform : '';\n const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n\n cachedPlatformInfo = {\n isWindows: /Win/i.test(platform) || /Win/i.test(userAgent),\n isMacOS: /Mac/i.test(platform),\n isLinux: /Linux/i.test(platform),\n platform,\n userAgent,\n };\n\n return cachedPlatformInfo;\n}\n\n/**\n * Check if current platform is Windows (cached)\n */\nexport function isWindows(): boolean {\n return detectPlatform().isWindows;\n}\n\n/**\n * Check if current platform is macOS (cached)\n */\nexport function isMacOS(): boolean {\n return detectPlatform().isMacOS;\n}\n\n/**\n * Check if current platform is Linux (cached)\n */\nexport function isLinux(): boolean {\n return detectPlatform().isLinux;\n}\n\n/**\n * Get platform-recommended hardware acceleration setting for video decoding\n *\n * Background:\n * Windows hardware video decoders (especially with DXVA2/D3D11) may hang indefinitely\n * when calling VideoDecoder.flush() in certain scenarios (frequent seeks, large GOPs).\n * This appears to be a driver/platform-specific issue affecting Chromium's WebCodecs.\n *\n * Related discussions:\n * - https://github.com/w3c/webcodecs/issues\n * - Observed in production on Windows 10/11 with various GPU vendors\n *\n * Workaround:\n * Use software decoding on Windows to avoid flush() hangs, with ~4x slower decode\n * but reliable operation. Other platforms use hardware acceleration by default.\n *\n * @returns Platform-recommended hardware acceleration setting\n */\nexport function getRecommendedHardwareAcceleration(): HardwareAcceleration {\n // Windows: prefer software to avoid flush hang in hardware decoders\n if (isWindows()) {\n return 'prefer-software';\n }\n\n // Other platforms: no preference (let browser choose)\n return 'no-preference';\n}\n"],"names":[],"mappings":"AAaA,IAAI,qBAA0C;AAKvC,SAAS,iBAA+B;AAC7C,MAAI,oBAAoB;AACtB,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,OAAO,cAAc,cAAc,UAAU,WAAW;AACzE,QAAM,YAAY,OAAO,cAAc,cAAc,UAAU,YAAY;AAE3E,uBAAqB;AAAA,IACnB,WAAW,OAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,SAAS;AAAA,IACzD,SAAS,OAAO,KAAK,QAAQ;AAAA,IAC7B,SAAS,SAAS,KAAK,QAAQ;AAAA,IAC/B;AAAA,IACA;AAAA,EAAA;AAGF,SAAO;AACT;AAKO,SAAS,YAAqB;AACnC,SAAO,iBAAiB;AAC1B;AAkCO,SAAS,qCAA2D;AAEzE,MAAI,aAAa;AACf,WAAO;AAAA,EACT;AAGA,SAAO;AACT;"}
|
|
1
|
+
{"version":3,"file":"platform-utils.js","sources":["../../src/utils/platform-utils.ts"],"sourcesContent":["/**\n * Platform detection utilities with caching for performance\n */\n\nexport interface PlatformInfo {\n isWindows: boolean;\n isMacOS: boolean;\n isLinux: boolean;\n platform: string;\n userAgent: string;\n}\n\n// Cache platform detection result (only detect once)\nlet cachedPlatformInfo: PlatformInfo | null = null;\n\n/**\n * Detect current platform (cached after first call)\n */\nexport function detectPlatform(): PlatformInfo {\n if (cachedPlatformInfo) {\n return cachedPlatformInfo;\n }\n\n const platform = typeof navigator !== 'undefined' ? navigator.platform : '';\n const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n\n cachedPlatformInfo = {\n isWindows: /Win/i.test(platform) || /Win/i.test(userAgent),\n isMacOS: /Mac/i.test(platform),\n isLinux: /Linux/i.test(platform),\n platform,\n userAgent,\n };\n\n return cachedPlatformInfo;\n}\n\n/**\n * Check if current platform is Windows (cached)\n */\nexport function isWindows(): boolean {\n return detectPlatform().isWindows;\n}\n\n/**\n * Check if current platform is macOS (cached)\n */\nexport function isMacOS(): boolean {\n return detectPlatform().isMacOS;\n}\n\n/**\n * Check if current platform is Linux (cached)\n */\nexport function isLinux(): boolean {\n return detectPlatform().isLinux;\n}\n\n/**\n * Get platform-recommended hardware acceleration setting for video decoding\n *\n * Background:\n * Windows hardware video decoders (especially with DXVA2/D3D11) may hang indefinitely\n * when calling VideoDecoder.flush() in certain scenarios (frequent seeks, large GOPs).\n * This appears to be a driver/platform-specific issue affecting Chromium's WebCodecs.\n *\n * Related discussions:\n * - https://github.com/w3c/webcodecs/issues\n * - Observed in production on Windows 10/11 with various GPU vendors\n *\n * Workaround:\n * Use software decoding on Windows to avoid flush() hangs, with ~4x slower decode\n * but reliable operation. Other platforms use hardware acceleration by default.\n *\n * @returns Platform-recommended hardware acceleration setting\n */\nexport function getRecommendedHardwareAcceleration(): HardwareAcceleration {\n // Windows: prefer software to avoid flush hang in hardware decoders\n if (isWindows()) {\n return 'prefer-software';\n }\n\n // Other platforms: no preference (let browser choose)\n return 'no-preference';\n}\n\nexport interface BrowserCompatibility {\n webCodecsAvailable: boolean;\n opfsAvailable: boolean;\n missingFeatures: string[];\n browserInfo: {\n name: string;\n version: string;\n recommended: string;\n };\n}\n\nlet cachedCompatibility: BrowserCompatibility | null = null;\n\n/**\n * Check browser compatibility for WebCodecs and OPFS (sync, < 1ms, cached)\n * Only checks API existence, no file operations\n */\nexport function checkBrowserCompatibility(): BrowserCompatibility {\n if (cachedCompatibility) return cachedCompatibility;\n\n const missingFeatures: string[] = [];\n let webCodecsAvailable = true;\n let opfsAvailable = false;\n\n // Check WebCodecs APIs (sync, < 1ms)\n if (typeof VideoDecoder === 'undefined') {\n missingFeatures.push('VideoDecoder');\n webCodecsAvailable = false;\n }\n if (typeof VideoEncoder === 'undefined') {\n missingFeatures.push('VideoEncoder');\n webCodecsAvailable = false;\n }\n if (typeof VideoFrame === 'undefined') {\n missingFeatures.push('VideoFrame');\n webCodecsAvailable = false;\n }\n if (typeof EncodedVideoChunk === 'undefined') {\n missingFeatures.push('EncodedVideoChunk');\n webCodecsAvailable = false;\n }\n\n // Detect browser (sync, < 1ms)\n const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n let browserName = 'Unknown';\n let browserVersion = 'Unknown';\n let recommended = 'Chrome 94+ / Edge 94+ / Safari 26+';\n\n if (userAgent.includes('Edg/')) {\n browserName = 'Edge';\n const match = userAgent.match(/Edg\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Edge 94+';\n } else if (userAgent.includes('Chrome/')) {\n browserName = 'Chrome';\n const match = userAgent.match(/Chrome\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Chrome 94+';\n } else if (userAgent.includes('Safari/') && !userAgent.includes('Chrome')) {\n browserName = 'Safari';\n const match = userAgent.match(/Version\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Safari 26+';\n } else if (userAgent.includes('Firefox/')) {\n browserName = 'Firefox';\n const match = userAgent.match(/Firefox\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Not supported';\n }\n\n // Check OPFS API (sync, no file operations)\n if (webCodecsAvailable) {\n if (\n typeof navigator !== 'undefined' &&\n navigator.storage &&\n typeof navigator.storage.getDirectory === 'function'\n ) {\n // Safari: Not supported due to compatibility issues\n // - Audio playback issues (only first window plays correctly)\n // - Export issues (video output has stuttering and no audio)\n // - ReadableStream transfer not supported\n if (browserName === 'Safari') {\n missingFeatures.push('Safari not supported (compatibility issues)');\n opfsAvailable = false;\n } else {\n // Chrome/Edge: assume support if API exists\n opfsAvailable = true;\n }\n } else {\n missingFeatures.push('OPFS');\n }\n }\n\n cachedCompatibility = {\n webCodecsAvailable,\n opfsAvailable,\n missingFeatures,\n browserInfo: { name: browserName, version: browserVersion, recommended },\n };\n\n return cachedCompatibility;\n}\n"],"names":[],"mappings":"AAaA,IAAI,qBAA0C;AAKvC,SAAS,iBAA+B;AAC7C,MAAI,oBAAoB;AACtB,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,OAAO,cAAc,cAAc,UAAU,WAAW;AACzE,QAAM,YAAY,OAAO,cAAc,cAAc,UAAU,YAAY;AAE3E,uBAAqB;AAAA,IACnB,WAAW,OAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,SAAS;AAAA,IACzD,SAAS,OAAO,KAAK,QAAQ;AAAA,IAC7B,SAAS,SAAS,KAAK,QAAQ;AAAA,IAC/B;AAAA,IACA;AAAA,EAAA;AAGF,SAAO;AACT;AAKO,SAAS,YAAqB;AACnC,SAAO,iBAAiB;AAC1B;AAkCO,SAAS,qCAA2D;AAEzE,MAAI,aAAa;AACf,WAAO;AAAA,EACT;AAGA,SAAO;AACT;AAaA,IAAI,sBAAmD;AAMhD,SAAS,4BAAkD;AAChE,MAAI,oBAAqB,QAAO;AAEhC,QAAM,kBAA4B,CAAA;AAClC,MAAI,qBAAqB;AACzB,MAAI,gBAAgB;AAGpB,MAAI,OAAO,iBAAiB,aAAa;AACvC,oBAAgB,KAAK,cAAc;AACnC,yBAAqB;AAAA,EACvB;AACA,MAAI,OAAO,iBAAiB,aAAa;AACvC,oBAAgB,KAAK,cAAc;AACnC,yBAAqB;AAAA,EACvB;AACA,MAAI,OAAO,eAAe,aAAa;AACrC,oBAAgB,KAAK,YAAY;AACjC,yBAAqB;AAAA,EACvB;AACA,MAAI,OAAO,sBAAsB,aAAa;AAC5C,oBAAgB,KAAK,mBAAmB;AACxC,yBAAqB;AAAA,EACvB;AAGA,QAAM,YAAY,OAAO,cAAc,cAAc,UAAU,YAAY;AAC3E,MAAI,cAAc;AAClB,MAAI,iBAAiB;AACrB,MAAI,cAAc;AAElB,MAAI,UAAU,SAAS,MAAM,GAAG;AAC9B,kBAAc;AACd,UAAM,QAAQ,UAAU,MAAM,YAAY;AAC1C,qBAAiB,QAAQ,CAAC,KAAK;AAC/B,kBAAc;AAAA,EAChB,WAAW,UAAU,SAAS,SAAS,GAAG;AACxC,kBAAc;AACd,UAAM,QAAQ,UAAU,MAAM,eAAe;AAC7C,qBAAiB,QAAQ,CAAC,KAAK;AAC/B,kBAAc;AAAA,EAChB,WAAW,UAAU,SAAS,SAAS,KAAK,CAAC,UAAU,SAAS,QAAQ,GAAG;AACzE,kBAAc;AACd,UAAM,QAAQ,UAAU,MAAM,gBAAgB;AAC9C,qBAAiB,QAAQ,CAAC,KAAK;AAC/B,kBAAc;AAAA,EAChB,WAAW,UAAU,SAAS,UAAU,GAAG;AACzC,kBAAc;AACd,UAAM,QAAQ,UAAU,MAAM,gBAAgB;AAC9C,qBAAiB,QAAQ,CAAC,KAAK;AAC/B,kBAAc;AAAA,EAChB;AAGA,MAAI,oBAAoB;AACtB,QACE,OAAO,cAAc,eACrB,UAAU,WACV,OAAO,UAAU,QAAQ,iBAAiB,YAC1C;AAKA,UAAI,gBAAgB,UAAU;AAC5B,wBAAgB,KAAK,6CAA6C;AAClE,wBAAgB;AAAA,MAClB,OAAO;AAEL,wBAAgB;AAAA,MAClB;AAAA,IACF,OAAO;AACL,sBAAgB,KAAK,MAAM;AAAA,IAC7B;AAAA,EACF;AAEA,wBAAsB;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,EAAE,MAAM,aAAa,SAAS,gBAAgB,YAAA;AAAA,EAAY;AAGzE,SAAO;AACT;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-decode.worker.BQtw6eWn.js","sources":["../../../../src/utils/platform-utils.ts","../../../../src/stages/decode/VideoChunkDecoder.ts","../../../../src/stages/decode/video-decode.worker.ts"],"sourcesContent":["/**\n * Platform detection utilities with caching for performance\n */\n\nexport interface PlatformInfo {\n isWindows: boolean;\n isMacOS: boolean;\n isLinux: boolean;\n platform: string;\n userAgent: string;\n}\n\n// Cache platform detection result (only detect once)\nlet cachedPlatformInfo: PlatformInfo | null = null;\n\n/**\n * Detect current platform (cached after first call)\n */\nexport function detectPlatform(): PlatformInfo {\n if (cachedPlatformInfo) {\n return cachedPlatformInfo;\n }\n\n const platform = typeof navigator !== 'undefined' ? navigator.platform : '';\n const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n\n cachedPlatformInfo = {\n isWindows: /Win/i.test(platform) || /Win/i.test(userAgent),\n isMacOS: /Mac/i.test(platform),\n isLinux: /Linux/i.test(platform),\n platform,\n userAgent,\n };\n\n return cachedPlatformInfo;\n}\n\n/**\n * Check if current platform is Windows (cached)\n */\nexport function isWindows(): boolean {\n return detectPlatform().isWindows;\n}\n\n/**\n * Check if current platform is macOS (cached)\n */\nexport function isMacOS(): boolean {\n return detectPlatform().isMacOS;\n}\n\n/**\n * Check if current platform is Linux (cached)\n */\nexport function isLinux(): boolean {\n return detectPlatform().isLinux;\n}\n\n/**\n * Get platform-recommended hardware acceleration setting for video decoding\n *\n * Background:\n * Windows hardware video decoders (especially with DXVA2/D3D11) may hang indefinitely\n * when calling VideoDecoder.flush() in certain scenarios (frequent seeks, large GOPs).\n * This appears to be a driver/platform-specific issue affecting Chromium's WebCodecs.\n *\n * Related discussions:\n * - https://github.com/w3c/webcodecs/issues\n * - Observed in production on Windows 10/11 with various GPU vendors\n *\n * Workaround:\n * Use software decoding on Windows to avoid flush() hangs, with ~4x slower decode\n * but reliable operation. Other platforms use hardware acceleration by default.\n *\n * @returns Platform-recommended hardware acceleration setting\n */\nexport function getRecommendedHardwareAcceleration(): HardwareAcceleration {\n // Windows: prefer software to avoid flush hang in hardware decoders\n if (isWindows()) {\n return 'prefer-software';\n }\n\n // Other platforms: no preference (let browser choose)\n return 'no-preference';\n}\n","import { VideoDecoderConfig } from './types';\nimport { BaseDecoder } from './BaseDecoder';\nimport { getRecommendedHardwareAcceleration } from '../../utils/platform-utils';\n\n/**\n * Video decoder with GOP tracking\n * Tracks keyframe boundaries and attaches GOP metadata to decoded frames\n */\nexport class VideoChunkDecoder extends BaseDecoder<\n VideoDecoder,\n VideoDecoderConfig,\n EncodedVideoChunk,\n VideoFrame\n> {\n private static readonly DEFAULT_HIGH_WATER_MARK = 4;\n private static readonly DEFAULT_DECODE_QUEUE_THRESHOLD = 16;\n\n readonly trackId: string;\n\n protected readonly highWaterMark: number;\n protected readonly decodeQueueThreshold: number;\n\n // Buffering support for delayed configuration\n private bufferedChunks: EncodedVideoChunk[] = [];\n private isProcessingBuffer: boolean = false;\n\n constructor(trackId: string, config?: Partial<VideoDecoderConfig>) {\n super((config || {}) as VideoDecoderConfig);\n\n this.trackId = trackId;\n this.highWaterMark =\n config?.backpressure?.highWaterMark ?? VideoChunkDecoder.DEFAULT_HIGH_WATER_MARK;\n this.decodeQueueThreshold =\n config?.backpressure?.decodeQueueThreshold ??\n VideoChunkDecoder.DEFAULT_DECODE_QUEUE_THRESHOLD;\n }\n\n // Computed properties\n get isConfigured(): boolean {\n return this.isReady;\n }\n\n get state(): string {\n return this.decoder?.state || 'unconfigured';\n }\n\n /**\n * Update configuration - can be called before or after initialization\n */\n async updateConfig(config: Partial<VideoDecoderConfig>): Promise<void> {\n if (!this.isReady && config.codec) {\n await this.configure(config as VideoDecoderConfig);\n await this.processBufferedChunks();\n }\n }\n\n // Override createStream to handle GOP tracking and buffering\n // Always create new stream for each clip (ReadableStreams can only be consumed once)\n override createStream(): TransformStream<EncodedVideoChunk, VideoFrame> {\n return new TransformStream<EncodedVideoChunk, VideoFrame>(\n {\n start: async (controller) => {\n this.controller = controller;\n // Don't initialize if no codec config yet\n if (this.config?.codec && this.config?.description && !this.isReady) {\n await this.initialize();\n }\n },\n\n transform: async (chunk) => {\n // If not configured yet, buffer the chunk\n if (!this.isReady) {\n this.bufferedChunks.push(chunk);\n return; // Don't process yet\n }\n\n // If we're processing buffered chunks, add to buffer to maintain order\n if (this.isProcessingBuffer) {\n this.bufferedChunks.push(chunk);\n return;\n }\n\n // Normal processing\n await this.processChunk(chunk);\n },\n\n flush: async () => {\n if (this.isReady) {\n await this.flush();\n }\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: () => 1,\n }\n );\n }\n\n /**\n * Process a single chunk (extracted from transform for reuse)\n */\n private async processChunk(chunk: EncodedVideoChunk): Promise<void> {\n if (!this.decoder) {\n throw new Error('Decoder not initialized');\n }\n\n if (this.decoder.state !== 'configured') {\n console.error('[VideoChunkDecoder] Decoder in unexpected state:', this.decoder.state);\n throw new Error(`Decoder not configured, state: ${this.decoder.state}`);\n }\n\n this.decode(chunk);\n }\n\n // Override handleOutput to enqueue decoded frames\n protected override handleOutput(frame: VideoFrame): void {\n super.handleOutput(frame);\n }\n\n // Implement abstract methods\n protected async isConfigSupported(config: VideoDecoderConfig): Promise<{ supported: boolean }> {\n const result = await VideoDecoder.isConfigSupported({\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n });\n return { supported: result.supported ?? false };\n }\n\n protected createDecoder(init: {\n output: (frame: VideoFrame) => void;\n error: (error: DOMException) => void;\n }): VideoDecoder {\n return new VideoDecoder(init);\n }\n\n protected getDecoderType(): string {\n return 'Video';\n }\n\n protected async configureDecoder(config: VideoDecoderConfig): Promise<void> {\n if (!this.decoder) return;\n\n const hardwareAcceleration =\n config.hardwareAcceleration ?? getRecommendedHardwareAcceleration();\n\n const decoderConfig = {\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n hardwareAcceleration,\n optimizeForLatency: true,\n ...(config.description && { description: config.description }),\n ...(config.displayAspectWidth && { displayAspectWidth: config.displayAspectWidth }),\n ...(config.displayAspectHeight && { displayAspectHeight: config.displayAspectHeight }),\n };\n\n this.decoder.configure(decoderConfig as any);\n\n // Log when using software decoding for debugging\n if (hardwareAcceleration === 'prefer-software') {\n console.info('[VideoChunkDecoder] Using software decoding for platform compatibility');\n }\n }\n\n protected decode(chunk: EncodedVideoChunk): void {\n this.decoder?.decode(chunk);\n }\n\n /**\n * Configure the decoder with codec info (can be called after creation)\n */\n async configure(config: VideoDecoderConfig): Promise<void> {\n // console.log('[VideoChunkDecoder] Configuring with:', config);\n\n if (this.isReady) {\n // If already configured, reconfigure\n await this.reconfigure(config);\n return;\n }\n\n this.config = config as any;\n\n // Initialize decoder with new config\n await this.initialize();\n }\n\n /**\n * Process any buffered chunks after configuration\n */\n async processBufferedChunks(): Promise<void> {\n if (!this.isReady || this.bufferedChunks.length === 0) {\n return;\n }\n\n // console.log('[VideoChunkDecoder] Processing', this.bufferedChunks.length, 'buffered chunks');\n this.isProcessingBuffer = true;\n\n // Process buffered chunks in order\n const chunks = [...this.bufferedChunks];\n this.bufferedChunks = [];\n\n for (const chunk of chunks) {\n try {\n await this.processChunk(chunk);\n } catch (error) {\n console.error('[VideoChunkDecoder] Error processing buffered chunk:', error);\n }\n }\n\n this.isProcessingBuffer = false;\n\n // Process any new chunks that arrived while processing buffer\n if (this.bufferedChunks.length > 0) {\n await this.processBufferedChunks();\n }\n }\n\n // Override close to clean up buffered chunks\n override async close(): Promise<void> {\n // Clear buffered chunks\n this.bufferedChunks = [];\n\n // Call parent close\n await super.close();\n }\n}\n","/**\n * @deprecated VideoDecodeWorker is deprecated and will be removed in a future version.\n *\n * Reason: Export pipeline now uses OnDemandVideoSession (main thread) for decoding.\n * This eliminates the need for EncodedVideoChunk serialization and provides better\n * code reuse with preview's decoding logic.\n *\n * Replacement: OnDemandVideoSession (packages/core/src/orchestrator/OnDemandVideoSession.ts)\n *\n * New pipeline: IndexedVideoSource → OnDemandVideoSession (decode) → ComposeWorker → EncodeWorker\n * All in main thread before ComposeWorker, avoiding worker communication overhead.\n *\n * This file is kept for backward compatibility only and may be removed in the future.\n */\n\nimport { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { VideoChunkDecoder } from './VideoChunkDecoder';\nimport { VideoDecoderConfig } from './types';\n\nconst normalizeDescription = (desc?: ArrayBuffer | ArrayBufferView): ArrayBuffer | undefined => {\n if (!desc) return undefined;\n\n if (desc instanceof ArrayBuffer) return desc;\n\n const view = desc as ArrayBufferView;\n return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength) as ArrayBuffer;\n};\n\n/**\n * VideoDecodeWorker (Clip Local) - Decodes video for a single clip\n * Receives encoded video chunks from VideoDemuxWorker and outputs decoded frames\n *\n * Pipeline: VideoDemuxWorker → VideoDecodeWorker → VideoComposeWorker\n *\n * Features:\n * - Single clip, single VideoDecoder instance (no routing)\n * - GOP-based decoding with metadata\n * - Stream-based processing with backpressure\n * - Lifecycle tied to clip pipeline\n */\nexport class VideoDecodeWorker {\n private channel: WorkerChannel;\n private decoder: VideoChunkDecoder | null = null;\n private clipId: string = '';\n\n private defaultConfig: Partial<VideoDecoderConfig> = {};\n\n private upstreamPort: MessagePort | null = null;\n private downstreamPort: MessagePort | null = null;\n\n constructor() {\n this.channel = new WorkerChannel(self as any, {\n name: 'VideoDecodeWorker',\n timeout: 30000,\n });\n\n this.setupHandlers();\n }\n\n private setupHandlers(): void {\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n this.channel.registerHandler('connect', this.handleConnect.bind(this));\n this.channel.registerHandler('flush', this.handleFlush.bind(this));\n this.channel.registerHandler('reset', this.handleReset.bind(this));\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n }\n\n private async handleConnect(payload: {\n direction: 'upstream' | 'downstream';\n port: MessagePort;\n streamType?: 'video';\n sessionId?: string;\n clipStartUs?: number;\n clipDurationUs?: number;\n }): Promise<{ success: boolean }> {\n const { port, direction, sessionId } = payload;\n\n if (direction === 'upstream') {\n this.upstreamPort = port;\n this.clipId = sessionId || 'default';\n\n const channel = new WorkerChannel(port, {\n name: 'Demux-VideoDecode',\n timeout: 30000,\n });\n\n channel.receiveStream((stream, metadata) => {\n this.handleReceiveStream(stream, {\n ...metadata,\n clipStartUs: payload.clipStartUs,\n clipDurationUs: payload.clipDurationUs,\n });\n });\n\n channel.registerHandler('configure', this.handleConfigure.bind(this));\n }\n\n if (direction === 'downstream') {\n this.downstreamPort = port;\n }\n\n return { success: true };\n }\n\n private async handleConfigure(payload: {\n config?: { video?: Partial<VideoDecoderConfig> };\n sessionId?: string;\n streamType?: 'video';\n codec?: string;\n width?: number;\n height?: number;\n description?: ArrayBuffer | Uint8Array;\n }): Promise<{ success: boolean }> {\n const { sessionId, streamType, codec, width, height, description, config } = payload;\n\n if (sessionId && streamType === 'video') {\n if (this.decoder) {\n await this.decoder.updateConfig({\n codec,\n width,\n height,\n description: normalizeDescription(description),\n });\n }\n return { success: true };\n }\n\n if (config?.video) {\n Object.assign(this.defaultConfig, config.video);\n\n if (this.decoder) {\n await this.decoder.updateConfig(config.video);\n }\n }\n\n this.channel.state = WorkerState.Ready;\n\n return { success: true };\n }\n\n private async handleReceiveStream(\n stream: ReadableStream,\n metadata?: Record<string, any>\n ): Promise<void> {\n const sessionId = metadata?.sessionId || this.clipId;\n\n if (!this.decoder) {\n this.decoder = new VideoChunkDecoder(sessionId, {\n ...this.defaultConfig,\n codec: metadata?.codec,\n width: metadata?.width,\n height: metadata?.height,\n description: normalizeDescription(metadata?.description),\n });\n }\n\n const transform = this.decoder.createStream();\n\n if (this.downstreamPort) {\n const channel = new WorkerChannel(this.downstreamPort, {\n name: 'VideoDecode-Compose',\n timeout: 30000,\n });\n\n channel.sendStream(transform.readable as ReadableStream, {\n streamType: 'video',\n sessionId,\n });\n\n stream\n .pipeTo(transform.writable)\n .catch((error) =>\n console.error('[VideoDecodeWorker] Video stream pipe error:', sessionId, error)\n );\n }\n }\n\n private async handleFlush(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.flush();\n }\n return { success: true };\n }\n\n private async handleReset(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.reset();\n }\n\n this.channel.notify('reset_complete', {\n type: 'video',\n });\n\n return { success: true };\n }\n\n private async handleGetStats(): Promise<{ video?: any }> {\n if (!this.decoder) {\n return {};\n }\n\n return {\n video: {\n clipId: this.clipId,\n configured: this.decoder.isConfigured,\n queueSize: this.decoder.queueSize,\n state: this.decoder.state,\n },\n };\n }\n\n private async handleDispose(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.close();\n this.decoder = null;\n }\n\n this.upstreamPort?.close();\n this.upstreamPort = null;\n\n this.downstreamPort?.close();\n this.downstreamPort = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n}\n\nconst worker = new VideoDecodeWorker();\n\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"video-decode.worker.BQtw6eWn.js","sources":["../../../../src/utils/platform-utils.ts","../../../../src/stages/decode/VideoChunkDecoder.ts","../../../../src/stages/decode/video-decode.worker.ts"],"sourcesContent":["/**\n * Platform detection utilities with caching for performance\n */\n\nexport interface PlatformInfo {\n isWindows: boolean;\n isMacOS: boolean;\n isLinux: boolean;\n platform: string;\n userAgent: string;\n}\n\n// Cache platform detection result (only detect once)\nlet cachedPlatformInfo: PlatformInfo | null = null;\n\n/**\n * Detect current platform (cached after first call)\n */\nexport function detectPlatform(): PlatformInfo {\n if (cachedPlatformInfo) {\n return cachedPlatformInfo;\n }\n\n const platform = typeof navigator !== 'undefined' ? navigator.platform : '';\n const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n\n cachedPlatformInfo = {\n isWindows: /Win/i.test(platform) || /Win/i.test(userAgent),\n isMacOS: /Mac/i.test(platform),\n isLinux: /Linux/i.test(platform),\n platform,\n userAgent,\n };\n\n return cachedPlatformInfo;\n}\n\n/**\n * Check if current platform is Windows (cached)\n */\nexport function isWindows(): boolean {\n return detectPlatform().isWindows;\n}\n\n/**\n * Check if current platform is macOS (cached)\n */\nexport function isMacOS(): boolean {\n return detectPlatform().isMacOS;\n}\n\n/**\n * Check if current platform is Linux (cached)\n */\nexport function isLinux(): boolean {\n return detectPlatform().isLinux;\n}\n\n/**\n * Get platform-recommended hardware acceleration setting for video decoding\n *\n * Background:\n * Windows hardware video decoders (especially with DXVA2/D3D11) may hang indefinitely\n * when calling VideoDecoder.flush() in certain scenarios (frequent seeks, large GOPs).\n * This appears to be a driver/platform-specific issue affecting Chromium's WebCodecs.\n *\n * Related discussions:\n * - https://github.com/w3c/webcodecs/issues\n * - Observed in production on Windows 10/11 with various GPU vendors\n *\n * Workaround:\n * Use software decoding on Windows to avoid flush() hangs, with ~4x slower decode\n * but reliable operation. Other platforms use hardware acceleration by default.\n *\n * @returns Platform-recommended hardware acceleration setting\n */\nexport function getRecommendedHardwareAcceleration(): HardwareAcceleration {\n // Windows: prefer software to avoid flush hang in hardware decoders\n if (isWindows()) {\n return 'prefer-software';\n }\n\n // Other platforms: no preference (let browser choose)\n return 'no-preference';\n}\n\nexport interface BrowserCompatibility {\n webCodecsAvailable: boolean;\n opfsAvailable: boolean;\n missingFeatures: string[];\n browserInfo: {\n name: string;\n version: string;\n recommended: string;\n };\n}\n\nlet cachedCompatibility: BrowserCompatibility | null = null;\n\n/**\n * Check browser compatibility for WebCodecs and OPFS (sync, < 1ms, cached)\n * Only checks API existence, no file operations\n */\nexport function checkBrowserCompatibility(): BrowserCompatibility {\n if (cachedCompatibility) return cachedCompatibility;\n\n const missingFeatures: string[] = [];\n let webCodecsAvailable = true;\n let opfsAvailable = false;\n\n // Check WebCodecs APIs (sync, < 1ms)\n if (typeof VideoDecoder === 'undefined') {\n missingFeatures.push('VideoDecoder');\n webCodecsAvailable = false;\n }\n if (typeof VideoEncoder === 'undefined') {\n missingFeatures.push('VideoEncoder');\n webCodecsAvailable = false;\n }\n if (typeof VideoFrame === 'undefined') {\n missingFeatures.push('VideoFrame');\n webCodecsAvailable = false;\n }\n if (typeof EncodedVideoChunk === 'undefined') {\n missingFeatures.push('EncodedVideoChunk');\n webCodecsAvailable = false;\n }\n\n // Detect browser (sync, < 1ms)\n const userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n let browserName = 'Unknown';\n let browserVersion = 'Unknown';\n let recommended = 'Chrome 94+ / Edge 94+ / Safari 26+';\n\n if (userAgent.includes('Edg/')) {\n browserName = 'Edge';\n const match = userAgent.match(/Edg\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Edge 94+';\n } else if (userAgent.includes('Chrome/')) {\n browserName = 'Chrome';\n const match = userAgent.match(/Chrome\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Chrome 94+';\n } else if (userAgent.includes('Safari/') && !userAgent.includes('Chrome')) {\n browserName = 'Safari';\n const match = userAgent.match(/Version\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Safari 26+';\n } else if (userAgent.includes('Firefox/')) {\n browserName = 'Firefox';\n const match = userAgent.match(/Firefox\\/(\\d+)/);\n browserVersion = match?.[1] ?? 'Unknown';\n recommended = 'Not supported';\n }\n\n // Check OPFS API (sync, no file operations)\n if (webCodecsAvailable) {\n if (\n typeof navigator !== 'undefined' &&\n navigator.storage &&\n typeof navigator.storage.getDirectory === 'function'\n ) {\n // Safari: Not supported due to compatibility issues\n // - Audio playback issues (only first window plays correctly)\n // - Export issues (video output has stuttering and no audio)\n // - ReadableStream transfer not supported\n if (browserName === 'Safari') {\n missingFeatures.push('Safari not supported (compatibility issues)');\n opfsAvailable = false;\n } else {\n // Chrome/Edge: assume support if API exists\n opfsAvailable = true;\n }\n } else {\n missingFeatures.push('OPFS');\n }\n }\n\n cachedCompatibility = {\n webCodecsAvailable,\n opfsAvailable,\n missingFeatures,\n browserInfo: { name: browserName, version: browserVersion, recommended },\n };\n\n return cachedCompatibility;\n}\n","import { VideoDecoderConfig } from './types';\nimport { BaseDecoder } from './BaseDecoder';\nimport { getRecommendedHardwareAcceleration } from '../../utils/platform-utils';\n\n/**\n * Video decoder with GOP tracking\n * Tracks keyframe boundaries and attaches GOP metadata to decoded frames\n */\nexport class VideoChunkDecoder extends BaseDecoder<\n VideoDecoder,\n VideoDecoderConfig,\n EncodedVideoChunk,\n VideoFrame\n> {\n private static readonly DEFAULT_HIGH_WATER_MARK = 4;\n private static readonly DEFAULT_DECODE_QUEUE_THRESHOLD = 16;\n\n readonly trackId: string;\n\n protected readonly highWaterMark: number;\n protected readonly decodeQueueThreshold: number;\n\n // Buffering support for delayed configuration\n private bufferedChunks: EncodedVideoChunk[] = [];\n private isProcessingBuffer: boolean = false;\n\n constructor(trackId: string, config?: Partial<VideoDecoderConfig>) {\n super((config || {}) as VideoDecoderConfig);\n\n this.trackId = trackId;\n this.highWaterMark =\n config?.backpressure?.highWaterMark ?? VideoChunkDecoder.DEFAULT_HIGH_WATER_MARK;\n this.decodeQueueThreshold =\n config?.backpressure?.decodeQueueThreshold ??\n VideoChunkDecoder.DEFAULT_DECODE_QUEUE_THRESHOLD;\n }\n\n // Computed properties\n get isConfigured(): boolean {\n return this.isReady;\n }\n\n get state(): string {\n return this.decoder?.state || 'unconfigured';\n }\n\n /**\n * Update configuration - can be called before or after initialization\n */\n async updateConfig(config: Partial<VideoDecoderConfig>): Promise<void> {\n if (!this.isReady && config.codec) {\n await this.configure(config as VideoDecoderConfig);\n await this.processBufferedChunks();\n }\n }\n\n // Override createStream to handle GOP tracking and buffering\n // Always create new stream for each clip (ReadableStreams can only be consumed once)\n override createStream(): TransformStream<EncodedVideoChunk, VideoFrame> {\n return new TransformStream<EncodedVideoChunk, VideoFrame>(\n {\n start: async (controller) => {\n this.controller = controller;\n // Don't initialize if no codec config yet\n if (this.config?.codec && this.config?.description && !this.isReady) {\n await this.initialize();\n }\n },\n\n transform: async (chunk) => {\n // If not configured yet, buffer the chunk\n if (!this.isReady) {\n this.bufferedChunks.push(chunk);\n return; // Don't process yet\n }\n\n // If we're processing buffered chunks, add to buffer to maintain order\n if (this.isProcessingBuffer) {\n this.bufferedChunks.push(chunk);\n return;\n }\n\n // Normal processing\n await this.processChunk(chunk);\n },\n\n flush: async () => {\n if (this.isReady) {\n await this.flush();\n }\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: () => 1,\n }\n );\n }\n\n /**\n * Process a single chunk (extracted from transform for reuse)\n */\n private async processChunk(chunk: EncodedVideoChunk): Promise<void> {\n if (!this.decoder) {\n throw new Error('Decoder not initialized');\n }\n\n if (this.decoder.state !== 'configured') {\n console.error('[VideoChunkDecoder] Decoder in unexpected state:', this.decoder.state);\n throw new Error(`Decoder not configured, state: ${this.decoder.state}`);\n }\n\n this.decode(chunk);\n }\n\n // Override handleOutput to enqueue decoded frames\n protected override handleOutput(frame: VideoFrame): void {\n super.handleOutput(frame);\n }\n\n // Implement abstract methods\n protected async isConfigSupported(config: VideoDecoderConfig): Promise<{ supported: boolean }> {\n const result = await VideoDecoder.isConfigSupported({\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n });\n return { supported: result.supported ?? false };\n }\n\n protected createDecoder(init: {\n output: (frame: VideoFrame) => void;\n error: (error: DOMException) => void;\n }): VideoDecoder {\n return new VideoDecoder(init);\n }\n\n protected getDecoderType(): string {\n return 'Video';\n }\n\n protected async configureDecoder(config: VideoDecoderConfig): Promise<void> {\n if (!this.decoder) return;\n\n const hardwareAcceleration =\n config.hardwareAcceleration ?? getRecommendedHardwareAcceleration();\n\n const decoderConfig = {\n codec: config.codec,\n codedWidth: config.width,\n codedHeight: config.height,\n hardwareAcceleration,\n optimizeForLatency: true,\n ...(config.description && { description: config.description }),\n ...(config.displayAspectWidth && { displayAspectWidth: config.displayAspectWidth }),\n ...(config.displayAspectHeight && { displayAspectHeight: config.displayAspectHeight }),\n };\n\n this.decoder.configure(decoderConfig as any);\n\n // Log when using software decoding for debugging\n if (hardwareAcceleration === 'prefer-software') {\n console.info('[VideoChunkDecoder] Using software decoding for platform compatibility');\n }\n }\n\n protected decode(chunk: EncodedVideoChunk): void {\n this.decoder?.decode(chunk);\n }\n\n /**\n * Configure the decoder with codec info (can be called after creation)\n */\n async configure(config: VideoDecoderConfig): Promise<void> {\n // console.log('[VideoChunkDecoder] Configuring with:', config);\n\n if (this.isReady) {\n // If already configured, reconfigure\n await this.reconfigure(config);\n return;\n }\n\n this.config = config as any;\n\n // Initialize decoder with new config\n await this.initialize();\n }\n\n /**\n * Process any buffered chunks after configuration\n */\n async processBufferedChunks(): Promise<void> {\n if (!this.isReady || this.bufferedChunks.length === 0) {\n return;\n }\n\n // console.log('[VideoChunkDecoder] Processing', this.bufferedChunks.length, 'buffered chunks');\n this.isProcessingBuffer = true;\n\n // Process buffered chunks in order\n const chunks = [...this.bufferedChunks];\n this.bufferedChunks = [];\n\n for (const chunk of chunks) {\n try {\n await this.processChunk(chunk);\n } catch (error) {\n console.error('[VideoChunkDecoder] Error processing buffered chunk:', error);\n }\n }\n\n this.isProcessingBuffer = false;\n\n // Process any new chunks that arrived while processing buffer\n if (this.bufferedChunks.length > 0) {\n await this.processBufferedChunks();\n }\n }\n\n // Override close to clean up buffered chunks\n override async close(): Promise<void> {\n // Clear buffered chunks\n this.bufferedChunks = [];\n\n // Call parent close\n await super.close();\n }\n}\n","/**\n * @deprecated VideoDecodeWorker is deprecated and will be removed in a future version.\n *\n * Reason: Export pipeline now uses OnDemandVideoSession (main thread) for decoding.\n * This eliminates the need for EncodedVideoChunk serialization and provides better\n * code reuse with preview's decoding logic.\n *\n * Replacement: OnDemandVideoSession (packages/core/src/orchestrator/OnDemandVideoSession.ts)\n *\n * New pipeline: IndexedVideoSource → OnDemandVideoSession (decode) → ComposeWorker → EncodeWorker\n * All in main thread before ComposeWorker, avoiding worker communication overhead.\n *\n * This file is kept for backward compatibility only and may be removed in the future.\n */\n\nimport { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { VideoChunkDecoder } from './VideoChunkDecoder';\nimport { VideoDecoderConfig } from './types';\n\nconst normalizeDescription = (desc?: ArrayBuffer | ArrayBufferView): ArrayBuffer | undefined => {\n if (!desc) return undefined;\n\n if (desc instanceof ArrayBuffer) return desc;\n\n const view = desc as ArrayBufferView;\n return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength) as ArrayBuffer;\n};\n\n/**\n * VideoDecodeWorker (Clip Local) - Decodes video for a single clip\n * Receives encoded video chunks from VideoDemuxWorker and outputs decoded frames\n *\n * Pipeline: VideoDemuxWorker → VideoDecodeWorker → VideoComposeWorker\n *\n * Features:\n * - Single clip, single VideoDecoder instance (no routing)\n * - GOP-based decoding with metadata\n * - Stream-based processing with backpressure\n * - Lifecycle tied to clip pipeline\n */\nexport class VideoDecodeWorker {\n private channel: WorkerChannel;\n private decoder: VideoChunkDecoder | null = null;\n private clipId: string = '';\n\n private defaultConfig: Partial<VideoDecoderConfig> = {};\n\n private upstreamPort: MessagePort | null = null;\n private downstreamPort: MessagePort | null = null;\n\n constructor() {\n this.channel = new WorkerChannel(self as any, {\n name: 'VideoDecodeWorker',\n timeout: 30000,\n });\n\n this.setupHandlers();\n }\n\n private setupHandlers(): void {\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n this.channel.registerHandler('connect', this.handleConnect.bind(this));\n this.channel.registerHandler('flush', this.handleFlush.bind(this));\n this.channel.registerHandler('reset', this.handleReset.bind(this));\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n }\n\n private async handleConnect(payload: {\n direction: 'upstream' | 'downstream';\n port: MessagePort;\n streamType?: 'video';\n sessionId?: string;\n clipStartUs?: number;\n clipDurationUs?: number;\n }): Promise<{ success: boolean }> {\n const { port, direction, sessionId } = payload;\n\n if (direction === 'upstream') {\n this.upstreamPort = port;\n this.clipId = sessionId || 'default';\n\n const channel = new WorkerChannel(port, {\n name: 'Demux-VideoDecode',\n timeout: 30000,\n });\n\n channel.receiveStream((stream, metadata) => {\n this.handleReceiveStream(stream, {\n ...metadata,\n clipStartUs: payload.clipStartUs,\n clipDurationUs: payload.clipDurationUs,\n });\n });\n\n channel.registerHandler('configure', this.handleConfigure.bind(this));\n }\n\n if (direction === 'downstream') {\n this.downstreamPort = port;\n }\n\n return { success: true };\n }\n\n private async handleConfigure(payload: {\n config?: { video?: Partial<VideoDecoderConfig> };\n sessionId?: string;\n streamType?: 'video';\n codec?: string;\n width?: number;\n height?: number;\n description?: ArrayBuffer | Uint8Array;\n }): Promise<{ success: boolean }> {\n const { sessionId, streamType, codec, width, height, description, config } = payload;\n\n if (sessionId && streamType === 'video') {\n if (this.decoder) {\n await this.decoder.updateConfig({\n codec,\n width,\n height,\n description: normalizeDescription(description),\n });\n }\n return { success: true };\n }\n\n if (config?.video) {\n Object.assign(this.defaultConfig, config.video);\n\n if (this.decoder) {\n await this.decoder.updateConfig(config.video);\n }\n }\n\n this.channel.state = WorkerState.Ready;\n\n return { success: true };\n }\n\n private async handleReceiveStream(\n stream: ReadableStream,\n metadata?: Record<string, any>\n ): Promise<void> {\n const sessionId = metadata?.sessionId || this.clipId;\n\n if (!this.decoder) {\n this.decoder = new VideoChunkDecoder(sessionId, {\n ...this.defaultConfig,\n codec: metadata?.codec,\n width: metadata?.width,\n height: metadata?.height,\n description: normalizeDescription(metadata?.description),\n });\n }\n\n const transform = this.decoder.createStream();\n\n if (this.downstreamPort) {\n const channel = new WorkerChannel(this.downstreamPort, {\n name: 'VideoDecode-Compose',\n timeout: 30000,\n });\n\n channel.sendStream(transform.readable as ReadableStream, {\n streamType: 'video',\n sessionId,\n });\n\n stream\n .pipeTo(transform.writable)\n .catch((error) =>\n console.error('[VideoDecodeWorker] Video stream pipe error:', sessionId, error)\n );\n }\n }\n\n private async handleFlush(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.flush();\n }\n return { success: true };\n }\n\n private async handleReset(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.reset();\n }\n\n this.channel.notify('reset_complete', {\n type: 'video',\n });\n\n return { success: true };\n }\n\n private async handleGetStats(): Promise<{ video?: any }> {\n if (!this.decoder) {\n return {};\n }\n\n return {\n video: {\n clipId: this.clipId,\n configured: this.decoder.isConfigured,\n queueSize: this.decoder.queueSize,\n state: this.decoder.state,\n },\n };\n }\n\n private async handleDispose(): Promise<{ success: boolean }> {\n if (this.decoder) {\n await this.decoder.close();\n this.decoder = null;\n }\n\n this.upstreamPort?.close();\n this.upstreamPort = null;\n\n this.downstreamPort?.close();\n this.downstreamPort = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n}\n\nconst worker = new VideoDecodeWorker();\n\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null;\n"],"names":[],"mappings":"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"}
|