diff --git a/web/app/src/sequencer/SequencerCodecProbe.ts b/web/app/src/sequencer/SequencerCodecProbe.ts index faa58274f..4355a8cbc 100644 --- a/web/app/src/sequencer/SequencerCodecProbe.ts +++ b/web/app/src/sequencer/SequencerCodecProbe.ts @@ -129,6 +129,19 @@ export async function probeSequencerCodec( return probeMovie(request, source); } +/** Probe a movie with the browser decoder first, then use the local ffmpeg.wasm + * Worker when Chromium cannot decode the container/codec. */ +export async function probeSequencerMovieCodecWithFallback( + requestValue: unknown, + source: ArrayBuffer, + signal?: AbortSignal, +): Promise { + const browserResult = await probeSequencerCodec(requestValue, source); + if (browserResult.status === "READY") return browserResult; + const { probeMovieWithFFmpeg } = await import("./FFmpegMovieProbe"); + return probeMovieWithFFmpeg(requestValue, source, signal); +} + export function createSequencerCodecProbeRequest( stripType: SequencerCodecProbeRequestIR["stripType"], mimeType: string, diff --git a/web/app/src/sequencer/SequencerMediaProxyCache.ts b/web/app/src/sequencer/SequencerMediaProxyCache.ts index 6d188e1e2..97ad390c0 100644 --- a/web/app/src/sequencer/SequencerMediaProxyCache.ts +++ b/web/app/src/sequencer/SequencerMediaProxyCache.ts @@ -88,6 +88,21 @@ export async function generateInitialSequencerMovieProxyFrame( throw new SequencerMediaCacheValidationError("SEQUENCER_CACHE_SOURCE_MISMATCH", "Movie proxy source bytes failed identity verification"); } cancelled(signal); + if (capability.backend === "FFMPEG_WASM") { + try { + const { decodeMovieFrameWithFFmpeg } = await import("./FFmpegMovieProbe"); + const frame = await decodeMovieFrameWithFFmpeg(source, sourceData, profile.width, profile.height, signal); + cancelled(signal); + return { + manifest: await createSequencerMediaCacheManifest(source, capability, profile, 0, frame.rgba), + data: frame.rgba, + }; + } + catch (error) { + if (error instanceof DOMException && error.name === "AbortError") throw new SequencerMediaCacheValidationError("SEQUENCER_CANCELLED", "Movie proxy generation was cancelled"); + throw new SequencerMediaCacheValidationError("SEQUENCER_CODEC_UNSUPPORTED", "ffmpeg.wasm movie proxy frame decode failed"); + } + } if (typeof document === "undefined" || typeof HTMLMediaElement === "undefined") { throw new SequencerMediaCacheValidationError("SEQUENCER_CODEC_UNSUPPORTED", "HTML media proxy generation is unavailable"); }