weblet-convert 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -100,6 +100,8 @@ Transcodes videos to WebM.
100
100
 
101
101
  - `ffmpeg.baseURL` (auto-derives core/wasm/worker URLs)
102
102
  - or explicit `ffmpeg.coreURL`, `ffmpeg.wasmURL`, `ffmpeg.workerURL`
103
+ - `enableLowMemoryFallback` (default `true`) retries with reduced memory profile
104
+ - `fallbackMaxWidth` (default `960`) output width cap used by fallback
103
105
 
104
106
  ### `videoToWebmDebug(input, options?, onEvent?)` (Ultimate debug mode)
105
107
 
@@ -144,7 +144,9 @@ var VIDEO_DEFAULTS = {
144
144
  crf: 32,
145
145
  deadline: "good",
146
146
  returnFile: false,
147
- maxInputBytes: 64 * 1024 * 1024
147
+ maxInputBytes: 0,
148
+ enableLowMemoryFallback: true,
149
+ fallbackMaxWidth: 960
148
150
  };
149
151
  function assertVideoInputGuardrails(blob, opts) {
150
152
  if (opts.maxInputBytes > 0 && blob.size > opts.maxInputBytes) {
@@ -284,6 +286,68 @@ ${logs.join("\n")}` : "ffmpeg_logs: <none>"
284
286
  });
285
287
  return new Blob([outBytes], { type: "video/webm" });
286
288
  }
289
+ async function runVideoTranscodeLowMemoryJob(ffmpeg, blob, opts) {
290
+ const jobId = createFsSafeId();
291
+ const inName = `input-${jobId}`;
292
+ const outName = `output-${jobId}.webm`;
293
+ const safeWidth = Math.max(320, Math.round(opts.fallbackMaxWidth));
294
+ const outBytes = await runWithFfmpegLock(async () => {
295
+ await ffmpeg.writeFile(inName, await util.fetchFile(blob));
296
+ const args = [
297
+ ...opts.maxDurationSeconds && opts.maxDurationSeconds > 0 ? ["-t", String(opts.maxDurationSeconds)] : [],
298
+ "-i",
299
+ inName,
300
+ "-map",
301
+ "0:v:0",
302
+ "-map",
303
+ "0:a?",
304
+ "-vf",
305
+ `scale='min(${safeWidth},iw)':-2:flags=bilinear`,
306
+ "-c:v",
307
+ "libvpx-vp9",
308
+ "-b:v",
309
+ "0",
310
+ "-crf",
311
+ String(Math.max(34, opts.crf)),
312
+ "-deadline",
313
+ "realtime",
314
+ "-cpu-used",
315
+ "8",
316
+ "-row-mt",
317
+ "1",
318
+ "-c:a",
319
+ "libopus",
320
+ outName
321
+ ];
322
+ const logs = [];
323
+ const onLog = ({ message }) => {
324
+ if (typeof message !== "string" || message.length === 0) return;
325
+ logs.push(message);
326
+ if (logs.length > 60) logs.shift();
327
+ };
328
+ try {
329
+ ffmpeg.on("log", onLog);
330
+ await ffmpeg.exec(args);
331
+ const outData = await ffmpeg.readFile(outName);
332
+ return outData instanceof Uint8Array ? outData : new Uint8Array(outData);
333
+ } catch (err) {
334
+ const detail = [
335
+ `videoToWebm low-memory fallback failed.`,
336
+ `input=${inName}`,
337
+ `output=${outName}`,
338
+ `args=${formatArgs(args)}`,
339
+ `error=${toErrorMessage(err)}`,
340
+ logs.length ? `ffmpeg_logs:
341
+ ${logs.join("\n")}` : "ffmpeg_logs: <none>"
342
+ ].join("\n");
343
+ throw new Error(detail);
344
+ } finally {
345
+ ffmpeg.off("log", onLog);
346
+ await Promise.allSettled([ffmpeg.deleteFile(inName), ffmpeg.deleteFile(outName)]);
347
+ }
348
+ });
349
+ return new Blob([outBytes], { type: "video/webm" });
350
+ }
287
351
  async function videoToWebm(input, options = {}) {
288
352
  const opts = { ...VIDEO_DEFAULTS, ...options };
289
353
  const blob = await inputToBlob(input);
@@ -296,7 +360,14 @@ async function videoToWebm(input, options = {}) {
296
360
  if (!isRecoverableWasmError(err)) throw err;
297
361
  await resetFfmpegRuntime();
298
362
  const ffmpeg = await getFfmpeg(opts.ffmpeg);
299
- outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts);
363
+ try {
364
+ outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts);
365
+ } catch (retryErr) {
366
+ if (!isRecoverableWasmError(retryErr) || !opts.enableLowMemoryFallback) throw retryErr;
367
+ await resetFfmpegRuntime();
368
+ const ffmpegFallback = await getFfmpeg(opts.ffmpeg);
369
+ outBlob = await runVideoTranscodeLowMemoryJob(ffmpegFallback, blob, opts);
370
+ }
300
371
  }
301
372
  const file = opts.returnFile ? new File([outBlob], defaultVideoName(input, opts.fileName), { type: outBlob.type }) : void 0;
302
373
  return { blob: outBlob, file, isWebm: true };
@@ -336,7 +407,19 @@ async function videoToWebmDebug(input, options = {}, onEvent) {
336
407
  emit({ stage: "retry-init", message: "Reinitializing ffmpeg.wasm runtime." });
337
408
  const ffmpeg = await getFfmpeg(opts.ffmpeg);
338
409
  emit({ stage: "retry-transcode", message: "Retrying video transcode job." });
339
- outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts);
410
+ try {
411
+ outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts);
412
+ } catch (retryErr) {
413
+ if (!isRecoverableWasmError(retryErr) || !opts.enableLowMemoryFallback) throw retryErr;
414
+ emit({
415
+ stage: "retry-transcode",
416
+ message: "Running low-memory fallback transcode profile.",
417
+ detail: `fallback_max_width=${opts.fallbackMaxWidth}`
418
+ });
419
+ await resetFfmpegRuntime();
420
+ const ffmpegFallback = await getFfmpeg(opts.ffmpeg);
421
+ outBlob = await runVideoTranscodeLowMemoryJob(ffmpegFallback, blob, opts);
422
+ }
340
423
  }
341
424
  const file = opts.returnFile ? new File([outBlob], defaultVideoName(input, opts.fileName), { type: outBlob.type }) : void 0;
342
425
  emit({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/browser/index.ts"],"names":["FFmpeg","fetchFile","fileTypeFromBuffer","canvas","ctx","fileName","file"],"mappings":"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","file":"index.cjs","sourcesContent":["import { FFmpeg } from \"@ffmpeg/ffmpeg\"\r\nimport { fetchFile } from \"@ffmpeg/util\"\r\nimport { fileTypeFromBuffer } from \"file-type\"\r\n\r\nimport type { ConvertResult } from \"../shared/types.js\"\r\n\r\nexport type ImageToWebpInput = Blob | File | string\r\n\r\nexport type ImageToWebpOptions = {\r\n /**\r\n * Hard cap for output dimensions, preserving aspect ratio.\r\n * If the input is larger, it will be downscaled.\r\n */\r\n maxWidth?: number\r\n maxHeight?: number\r\n\r\n /**\r\n * Target maximum output size in bytes.\r\n * If provided, the encoder will try to reach <= targetBytes by reducing quality.\r\n */\r\n targetBytes?: number\r\n\r\n /**\r\n * Quality search range (0..1). Higher is better quality, larger file.\r\n */\r\n maxQuality?: number\r\n minQuality?: number\r\n\r\n /**\r\n * If true, always return WebP even if it can't reach targetBytes.\r\n * If false, will return the original input when WebP isn't supported.\r\n */\r\n force?: boolean\r\n\r\n /**\r\n * Optional output filename (only used when returning a File).\r\n * If omitted and input is a File, the name is derived from it.\r\n */\r\n fileName?: string\r\n\r\n /**\r\n * Return a File instead of a Blob.\r\n */\r\n returnFile?: boolean\r\n}\r\n\r\nexport type ImageToWebpResult = {\r\n blob: Blob\r\n file?: File\r\n width: number\r\n height: number\r\n quality: number\r\n /**\r\n * True if output is `image/webp`.\r\n */\r\n isWebp: boolean\r\n}\r\n\r\nconst DEFAULTS: Required<\r\n Pick<\r\n ImageToWebpOptions,\r\n \"maxWidth\" | \"maxHeight\" | \"maxQuality\" | \"minQuality\" | \"force\" | \"returnFile\"\r\n >\r\n> = {\r\n maxWidth: 2048,\r\n maxHeight: 2048,\r\n maxQuality: 0.82,\r\n minQuality: 0.45,\r\n force: true,\r\n returnFile: false,\r\n}\r\n\r\nfunction clamp01(n: number) {\r\n return Math.max(0, Math.min(1, n))\r\n}\r\n\r\nfunction toWebpName(name: string) {\r\n const base = name.replace(/\\.[^/.]+$/, \"\")\r\n return `${base || \"image\"}.webp`\r\n}\r\n\r\nfunction getInputName(input: ImageToWebpInput) {\r\n if (typeof input === \"string\") return \"image.webp\"\r\n if (input instanceof File && input.name) return toWebpName(input.name)\r\n return \"image.webp\"\r\n}\r\n\r\nasync function inputToBlob(input: ImageToWebpInput): Promise<Blob> {\r\n if (typeof input === \"string\") {\r\n const res = await fetch(input)\r\n if (!res.ok) throw new Error(`Failed to fetch: ${res.status} ${res.statusText}`)\r\n return await res.blob()\r\n }\r\n return input\r\n}\r\n\r\nfunction toWebmName(name: string) {\r\n const base = name.replace(/\\.[^/.]+$/, \"\")\r\n return `${base || \"video\"}.webm`\r\n}\r\n\r\nfunction defaultVideoName(input: Blob | File | string, fileName?: string) {\r\n if (fileName && fileName.trim().length > 0) return fileName\r\n if (typeof input === \"string\") return \"video.webm\"\r\n if (input instanceof File && input.name) return toWebmName(input.name)\r\n return \"video.webm\"\r\n}\r\n\r\nlet ffmpegSingleton: FFmpeg | null = null\r\nlet ffmpegLoadPromise: Promise<FFmpeg> | null = null\r\nlet ffmpegJobQueue: Promise<void> = Promise.resolve()\r\nlet ffmpegConfigKey = \"{}\"\r\n\r\nfunction normalizeBrowserFfmpegLoadOptions(input?: BrowserFfmpegLoadOptions): BrowserFfmpegLoadOptions | undefined {\r\n if (!input) return undefined\r\n const baseURL = input.baseURL?.trim()\r\n const normalize = (v?: string) => v?.trim() || undefined\r\n const make = (suffix: string) => {\r\n if (!baseURL) return undefined\r\n return `${baseURL.replace(/\\/+$/, \"\")}/${suffix}`\r\n }\r\n return {\r\n baseURL,\r\n coreURL: normalize(input.coreURL) ?? make(\"ffmpeg-core.js\"),\r\n wasmURL: normalize(input.wasmURL) ?? make(\"ffmpeg-core.wasm\"),\r\n workerURL: normalize(input.workerURL) ?? make(\"ffmpeg-core.worker.js\"),\r\n }\r\n}\r\n\r\nfunction getFfmpegConfigKey(input?: BrowserFfmpegLoadOptions): string {\r\n return JSON.stringify(normalizeBrowserFfmpegLoadOptions(input) ?? {})\r\n}\r\n\r\nasync function getFfmpeg(loadOptions?: BrowserFfmpegLoadOptions): Promise<FFmpeg> {\r\n const nextKey = getFfmpegConfigKey(loadOptions)\r\n if (nextKey !== ffmpegConfigKey) {\r\n await resetFfmpegRuntime()\r\n ffmpegConfigKey = nextKey\r\n }\r\n if (ffmpegSingleton) return ffmpegSingleton\r\n if (ffmpegLoadPromise) return ffmpegLoadPromise\r\n const ff = new FFmpeg()\r\n const normalized = normalizeBrowserFfmpegLoadOptions(loadOptions)\r\n const loadParams =\r\n normalized && (normalized.coreURL || normalized.wasmURL || normalized.workerURL)\r\n ? {\r\n ...(normalized.coreURL ? { coreURL: normalized.coreURL } : {}),\r\n ...(normalized.wasmURL ? { wasmURL: normalized.wasmURL } : {}),\r\n ...(normalized.workerURL ? { workerURL: normalized.workerURL } : {}),\r\n }\r\n : undefined\r\n ffmpegLoadPromise = (async () => {\r\n try {\r\n await ff.load(loadParams as any)\r\n ffmpegSingleton = ff\r\n return ff\r\n } catch (err) {\r\n ffmpegLoadPromise = null\r\n const detail = [\r\n \"Failed to initialize ffmpeg.wasm.\",\r\n `error=${toErrorMessage(err)}`,\r\n \"hint=Ensure ffmpeg core/worker assets are reachable by your bundler/runtime and not blocked by CSP/CORS.\",\r\n ].join(\"\\n\")\r\n throw new Error(detail)\r\n }\r\n })()\r\n return ffmpegLoadPromise\r\n}\r\n\r\nasync function resetFfmpegRuntime(): Promise<void> {\r\n const current = ffmpegSingleton\r\n ffmpegSingleton = null\r\n ffmpegLoadPromise = null\r\n if (current && typeof (current as any).terminate === \"function\") {\r\n try {\r\n await (current as any).terminate()\r\n } catch {\r\n // Best-effort reset only.\r\n }\r\n }\r\n}\r\n\r\nasync function runWithFfmpegLock<T>(job: () => Promise<T>): Promise<T> {\r\n const previous = ffmpegJobQueue\r\n let release!: () => void\r\n ffmpegJobQueue = new Promise<void>((resolve) => {\r\n release = resolve\r\n })\r\n await previous\r\n try {\r\n return await job()\r\n } finally {\r\n release()\r\n }\r\n}\r\n\r\nfunction createFsSafeId(): string {\r\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`\r\n}\r\n\r\nfunction toErrorMessage(err: unknown): string {\r\n if (err instanceof Error && err.message) return err.message\r\n if (typeof err === \"string\") return err\r\n try {\r\n return JSON.stringify(err)\r\n } catch {\r\n return String(err)\r\n }\r\n}\r\n\r\nfunction formatArgs(args: string[]): string {\r\n return args.map((v) => (/\\s/.test(v) ? `\"${v}\"` : v)).join(\" \")\r\n}\r\n\r\nfunction isRecoverableWasmError(err: unknown): boolean {\r\n const msg = toErrorMessage(err).toLowerCase()\r\n return (\r\n msg.includes(\"memory access out of bounds\") ||\r\n msg.includes(\"runtimeerror\") ||\r\n msg.includes(\"aborted\") ||\r\n msg.includes(\"wasm\")\r\n )\r\n}\r\n\r\nexport type VideoToWebmInput = Blob | File | string\r\n\r\nexport type BrowserFfmpegLoadOptions = {\r\n /**\r\n * Base URL where ffmpeg.wasm assets live.\r\n * Missing specific URLs are derived from this.\r\n */\r\n baseURL?: string\r\n coreURL?: string\r\n wasmURL?: string\r\n workerURL?: string\r\n}\r\n\r\nexport type VideoToWebmOptions = {\r\n crf?: number\r\n deadline?: \"good\" | \"best\" | \"realtime\"\r\n maxDurationSeconds?: number\r\n /**\r\n * Guardrail for browser wasm memory pressure.\r\n * Throws before transcoding when input exceeds this limit.\r\n */\r\n maxInputBytes?: number\r\n /**\r\n * Optional browser ffmpeg.wasm asset URL configuration.\r\n */\r\n ffmpeg?: BrowserFfmpegLoadOptions\r\n fileName?: string\r\n returnFile?: boolean\r\n}\r\n\r\nexport type VideoToWebmResult = {\r\n blob: Blob\r\n file?: File\r\n isWebm: boolean\r\n}\r\n\r\nexport type VideoToWebmDebugStage =\r\n | \"input\"\r\n | \"guardrail\"\r\n | \"init\"\r\n | \"transcode\"\r\n | \"retry-reset\"\r\n | \"retry-init\"\r\n | \"retry-transcode\"\r\n | \"done\"\r\n\r\nexport type VideoToWebmDebugEvent = {\r\n stage: VideoToWebmDebugStage\r\n message: string\r\n detail?: string\r\n}\r\n\r\nexport type VideoToWebmDebugCause =\r\n | \"none\"\r\n | \"input-fetch-failed\"\r\n | \"input-too-large\"\r\n | \"ffmpeg-init-failed\"\r\n | \"ffmpeg-transcode-failed\"\r\n | \"ffmpeg-wasm-runtime-crash\"\r\n | \"unsupported-input\"\r\n | \"unknown\"\r\n\r\nexport type VideoToWebmDiagnostic = {\r\n cause: VideoToWebmDebugCause\r\n stage: VideoToWebmDebugStage\r\n rawError?: string\r\n recoverable: boolean\r\n hints: string[]\r\n}\r\n\r\nexport type VideoToWebmDebugResult =\r\n | ({ ok: true } & VideoToWebmResult & { events: VideoToWebmDebugEvent[]; diagnostic: VideoToWebmDiagnostic })\r\n | { ok: false; error: Error; events: VideoToWebmDebugEvent[]; diagnostic: VideoToWebmDiagnostic }\r\n\r\nconst VIDEO_DEFAULTS: Required<Pick<VideoToWebmOptions, \"crf\" | \"deadline\" | \"returnFile\" | \"maxInputBytes\">> = {\r\n crf: 32,\r\n deadline: \"good\",\r\n returnFile: false,\r\n maxInputBytes: 64 * 1024 * 1024,\r\n}\r\n\r\nfunction assertVideoInputGuardrails(blob: Blob, opts: Required<Pick<VideoToWebmOptions, \"maxInputBytes\">>) {\r\n if (opts.maxInputBytes > 0 && blob.size > opts.maxInputBytes) {\r\n throw new Error(\r\n [\r\n \"videoToWebm guardrail: input too large for browser ffmpeg.wasm.\",\r\n `input_bytes=${blob.size}`,\r\n `max_input_bytes=${opts.maxInputBytes}`,\r\n \"hint=Lower source resolution/bitrate, trim video, or increase maxInputBytes if your environment has enough memory.\",\r\n ].join(\"\\n\")\r\n )\r\n }\r\n}\r\n\r\ntype NormalizedVideoOptions = Required<Pick<VideoToWebmOptions, \"crf\" | \"deadline\" | \"returnFile\" | \"maxInputBytes\">> &\r\n Pick<VideoToWebmOptions, \"maxDurationSeconds\" | \"fileName\" | \"ffmpeg\">\r\n\r\nfunction buildVideoDiagnostic(stage: VideoToWebmDebugStage, err: unknown): VideoToWebmDiagnostic {\r\n const raw = toErrorMessage(err)\r\n const lower = raw.toLowerCase()\r\n const recoverable = isRecoverableWasmError(err)\r\n\r\n if (lower.includes(\"failed to fetch\")) {\r\n return {\r\n cause: \"input-fetch-failed\",\r\n stage,\r\n rawError: raw,\r\n recoverable: false,\r\n hints: [\"Verify URL reachability and CORS policy for browser fetch.\"],\r\n }\r\n }\r\n\r\n if (lower.includes(\"input too large\") || lower.includes(\"max_input_bytes\")) {\r\n return {\r\n cause: \"input-too-large\",\r\n stage,\r\n rawError: raw,\r\n recoverable: false,\r\n hints: [\"Increase maxInputBytes or reduce source size/resolution/duration.\"],\r\n }\r\n }\r\n\r\n if (lower.includes(\"failed to initialize ffmpeg.wasm\")) {\r\n return {\r\n cause: \"ffmpeg-init-failed\",\r\n stage,\r\n rawError: raw,\r\n recoverable,\r\n hints: [\r\n \"Provide video.ffmpeg.baseURL or explicit coreURL/wasmURL/workerURL.\",\r\n \"Ensure CSP/CORS allows ffmpeg worker + wasm assets.\",\r\n ],\r\n }\r\n }\r\n\r\n if (lower.includes(\"memory access out of bounds\") || lower.includes(\"runtimeerror\") || lower.includes(\"aborted\")) {\r\n return {\r\n cause: \"ffmpeg-wasm-runtime-crash\",\r\n stage,\r\n rawError: raw,\r\n recoverable: true,\r\n hints: [\"Reduce workload: trim duration, lower resolution/bitrate, or split video.\"],\r\n }\r\n }\r\n\r\n if (lower.includes(\"videotowebm failed\")) {\r\n return {\r\n cause: \"ffmpeg-transcode-failed\",\r\n stage,\r\n rawError: raw,\r\n recoverable,\r\n hints: [\"Inspect ffmpeg_logs in rawError for codec/container-specific details.\"],\r\n }\r\n }\r\n\r\n if (lower.includes(\"unsupported input type\")) {\r\n return {\r\n cause: \"unsupported-input\",\r\n stage,\r\n rawError: raw,\r\n recoverable: false,\r\n hints: [\"Use a valid image/* or video/* input and ensure proper MIME/magic bytes.\"],\r\n }\r\n }\r\n\r\n return {\r\n cause: \"unknown\",\r\n stage,\r\n rawError: raw,\r\n recoverable,\r\n hints: [\"Use videoToWebmDebug() output to trace failing stage and share rawError.\"],\r\n }\r\n}\r\n\r\nasync function runVideoTranscodeJob(ffmpeg: FFmpeg, blob: Blob, opts: NormalizedVideoOptions) {\r\n const jobId = createFsSafeId()\r\n const inName = `input-${jobId}`\r\n const outName = `output-${jobId}.webm`\r\n\r\n const outBytes = await runWithFfmpegLock(async () => {\r\n await ffmpeg.writeFile(inName, await fetchFile(blob))\r\n\r\n const args: string[] = [\r\n ...(opts.maxDurationSeconds && opts.maxDurationSeconds > 0 ? [\"-t\", String(opts.maxDurationSeconds)] : []),\r\n \"-i\",\r\n inName,\r\n \"-map\",\r\n \"0:v:0\",\r\n \"-map\",\r\n \"0:a?\",\r\n \"-c:v\",\r\n \"libvpx-vp9\",\r\n \"-b:v\",\r\n \"0\",\r\n \"-crf\",\r\n String(opts.crf),\r\n \"-deadline\",\r\n opts.deadline,\r\n \"-row-mt\",\r\n \"1\",\r\n \"-c:a\",\r\n \"libopus\",\r\n outName,\r\n ]\r\n\r\n const logs: string[] = []\r\n const onLog = ({ message }: { message: string }) => {\r\n if (typeof message !== \"string\" || message.length === 0) return\r\n logs.push(message)\r\n if (logs.length > 60) logs.shift()\r\n }\r\n\r\n try {\r\n ffmpeg.on(\"log\", onLog)\r\n await ffmpeg.exec(args)\r\n const outData = await ffmpeg.readFile(outName)\r\n return outData instanceof Uint8Array ? outData : new Uint8Array(outData as any)\r\n } catch (err) {\r\n const detail = [\r\n `videoToWebm failed.`,\r\n `input=${inName}`,\r\n `output=${outName}`,\r\n `args=${formatArgs(args)}`,\r\n `error=${toErrorMessage(err)}`,\r\n logs.length ? `ffmpeg_logs:\\n${logs.join(\"\\n\")}` : \"ffmpeg_logs: <none>\",\r\n ].join(\"\\n\")\r\n throw new Error(detail)\r\n } finally {\r\n ffmpeg.off(\"log\", onLog)\r\n await Promise.allSettled([ffmpeg.deleteFile(inName), ffmpeg.deleteFile(outName)])\r\n }\r\n })\r\n\r\n return new Blob([outBytes], { type: \"video/webm\" })\r\n}\r\n\r\nexport async function videoToWebm(\r\n input: VideoToWebmInput,\r\n options: VideoToWebmOptions = {}\r\n): Promise<VideoToWebmResult> {\r\n const opts = { ...VIDEO_DEFAULTS, ...options }\r\n const blob = await inputToBlob(input)\r\n assertVideoInputGuardrails(blob, opts)\r\n\r\n let outBlob: Blob\r\n try {\r\n const ffmpeg = await getFfmpeg(opts.ffmpeg)\r\n outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts)\r\n } catch (err) {\r\n if (!isRecoverableWasmError(err)) throw err\r\n await resetFfmpegRuntime()\r\n const ffmpeg = await getFfmpeg(opts.ffmpeg)\r\n outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts)\r\n }\r\n\r\n const file = opts.returnFile ? new File([outBlob], defaultVideoName(input, opts.fileName), { type: outBlob.type }) : undefined\r\n return { blob: outBlob, file, isWebm: true }\r\n}\r\n\r\nexport async function videoToWebmDebug(\r\n input: VideoToWebmInput,\r\n options: VideoToWebmOptions = {},\r\n onEvent?: (event: VideoToWebmDebugEvent) => void\r\n): Promise<VideoToWebmDebugResult> {\r\n const events: VideoToWebmDebugEvent[] = []\r\n let lastStage: VideoToWebmDebugStage = \"input\"\r\n const emit = (event: VideoToWebmDebugEvent) => {\r\n lastStage = event.stage\r\n events.push(event)\r\n onEvent?.(event)\r\n }\r\n\r\n try {\r\n emit({ stage: \"input\", message: \"Reading input as Blob.\" })\r\n const opts = { ...VIDEO_DEFAULTS, ...options }\r\n const blob = await inputToBlob(input)\r\n\r\n emit({\r\n stage: \"guardrail\",\r\n message: \"Checking browser ffmpeg.wasm memory guardrail.\",\r\n detail: `input_bytes=${blob.size};max_input_bytes=${opts.maxInputBytes}`,\r\n })\r\n assertVideoInputGuardrails(blob, opts)\r\n\r\n let outBlob: Blob\r\n try {\r\n emit({ stage: \"init\", message: \"Initializing ffmpeg.wasm runtime.\" })\r\n const ffmpeg = await getFfmpeg(opts.ffmpeg)\r\n emit({ stage: \"transcode\", message: \"Running video transcode job (VP9 + Opus).\" })\r\n outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts)\r\n } catch (err) {\r\n if (!isRecoverableWasmError(err)) throw err\r\n emit({\r\n stage: \"retry-reset\",\r\n message: \"Recoverable wasm error detected, resetting runtime.\",\r\n detail: toErrorMessage(err),\r\n })\r\n await resetFfmpegRuntime()\r\n emit({ stage: \"retry-init\", message: \"Reinitializing ffmpeg.wasm runtime.\" })\r\n const ffmpeg = await getFfmpeg(opts.ffmpeg)\r\n emit({ stage: \"retry-transcode\", message: \"Retrying video transcode job.\" })\r\n outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts)\r\n }\r\n\r\n const file = opts.returnFile ? new File([outBlob], defaultVideoName(input, opts.fileName), { type: outBlob.type }) : undefined\r\n emit({\r\n stage: \"done\",\r\n message: \"Video converted to WebM.\",\r\n detail: `output_bytes=${outBlob.size};mime=${outBlob.type}`,\r\n })\r\n return {\r\n ok: true,\r\n blob: outBlob,\r\n file,\r\n isWebm: true,\r\n events,\r\n diagnostic: { cause: \"none\", stage: \"done\", recoverable: false, hints: [] },\r\n }\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(toErrorMessage(err))\r\n const diagnostic = buildVideoDiagnostic(lastStage, error)\r\n emit({\r\n stage: \"done\",\r\n message: \"Video conversion failed.\",\r\n detail: `cause=${diagnostic.cause};${error.message}`,\r\n })\r\n return { ok: false, error, events, diagnostic }\r\n }\r\n}\r\n\r\nexport type ConvertInput = ImageToWebpInput\r\n\r\nexport type ConvertOptions = {\r\n image?: ImageToWebpOptions\r\n video?: VideoToWebmOptions\r\n returnFile?: boolean\r\n fileName?: string\r\n}\r\n\r\nasync function detectKindBrowser(input: ConvertInput): Promise<\"image\" | \"video\" | \"unknown\"> {\r\n if (typeof input !== \"string\") {\r\n const t = (input.type || \"\").toLowerCase()\r\n if (t.startsWith(\"image/\")) return \"image\"\r\n if (t.startsWith(\"video/\")) return \"video\"\r\n }\r\n\r\n const blob = await inputToBlob(input)\r\n const buf = new Uint8Array(await blob.slice(0, 4100).arrayBuffer())\r\n const ft = await fileTypeFromBuffer(buf)\r\n if (!ft) return \"unknown\"\r\n if (ft.mime.startsWith(\"image/\")) return \"image\"\r\n if (ft.mime.startsWith(\"video/\")) return \"video\"\r\n return \"unknown\"\r\n}\r\n\r\nexport async function convert(input: ConvertInput, options: ConvertOptions = {}): Promise<ConvertResult> {\r\n const kind = await detectKindBrowser(input)\r\n const returnFile = options.returnFile ?? false\r\n\r\n if (kind === \"image\") {\r\n const res = await imageToWebp(input, { ...options.image, returnFile, fileName: options.fileName })\r\n return { kind: \"image\", ...res }\r\n }\r\n\r\n if (kind === \"video\") {\r\n const res = await videoToWebm(input, { ...options.video, returnFile, fileName: options.fileName })\r\n return { kind: \"video\", ...res }\r\n }\r\n\r\n throw new Error(\"Unsupported input type. Expected an image/* or video/* asset.\")\r\n}\r\n\r\nfunction computeTargetSize(\r\n srcW: number,\r\n srcH: number,\r\n maxW: number,\r\n maxH: number\r\n): { width: number; height: number } {\r\n if (srcW <= 0 || srcH <= 0) return { width: srcW, height: srcH }\r\n const scale = Math.min(1, maxW / srcW, maxH / srcH)\r\n return {\r\n width: Math.max(1, Math.round(srcW * scale)),\r\n height: Math.max(1, Math.round(srcH * scale)),\r\n }\r\n}\r\n\r\nasync function decodeImage(blob: Blob): Promise<{ bitmap: ImageBitmap; width: number; height: number }> {\r\n if (typeof createImageBitmap === \"function\") {\r\n const bitmap = await createImageBitmap(blob, { imageOrientation: \"from-image\" as any })\r\n return { bitmap, width: bitmap.width, height: bitmap.height }\r\n }\r\n\r\n const url = URL.createObjectURL(blob)\r\n try {\r\n const img = await new Promise<HTMLImageElement>((resolve, reject) => {\r\n const el = new Image()\r\n el.onload = () => resolve(el)\r\n el.onerror = () => reject(new Error(\"Failed to decode image\"))\r\n el.src = url\r\n })\r\n\r\n const canvas = document.createElement(\"canvas\")\r\n canvas.width = img.naturalWidth || img.width\r\n canvas.height = img.naturalHeight || img.height\r\n const ctx = canvas.getContext(\"2d\")\r\n if (!ctx) throw new Error(\"Canvas 2D context not available\")\r\n ctx.drawImage(img, 0, 0)\r\n\r\n const dataUrl = canvas.toDataURL(\"image/png\")\r\n const pngBlob = await (await fetch(dataUrl)).blob()\r\n const bitmap = await createImageBitmap(pngBlob)\r\n return { bitmap, width: bitmap.width, height: bitmap.height }\r\n } finally {\r\n URL.revokeObjectURL(url)\r\n }\r\n}\r\n\r\nasync function encodeWebpFromBitmap(\r\n bitmap: ImageBitmap,\r\n width: number,\r\n height: number,\r\n quality: number\r\n): Promise<Blob | null> {\r\n const q = clamp01(quality)\r\n\r\n const hasOffscreen = typeof OffscreenCanvas !== \"undefined\"\r\n if (hasOffscreen) {\r\n const canvas = new OffscreenCanvas(width, height)\r\n const ctx = canvas.getContext(\"2d\")\r\n if (!ctx) return null\r\n ctx.drawImage(bitmap, 0, 0, width, height)\r\n try {\r\n return await canvas.convertToBlob({ type: \"image/webp\", quality: q })\r\n } catch {\r\n return null\r\n }\r\n }\r\n\r\n const canvas = document.createElement(\"canvas\")\r\n canvas.width = width\r\n canvas.height = height\r\n const ctx = canvas.getContext(\"2d\")\r\n if (!ctx) return null\r\n ctx.drawImage(bitmap, 0, 0, width, height)\r\n\r\n const blob = await new Promise<Blob | null>((resolve) => {\r\n canvas.toBlob((b) => resolve(b), \"image/webp\", q)\r\n })\r\n return blob\r\n}\r\n\r\n/**\r\n * Convert an image (png/jpg/etc) to WebP and shrink size while keeping similar quality.\r\n *\r\n * - Downscales to `maxWidth`/`maxHeight` (keeps aspect ratio).\r\n * - Encodes as WebP with a quality search to try meeting `targetBytes` (if provided).\r\n */\r\nexport async function imageToWebp(\r\n input: ImageToWebpInput,\r\n options: ImageToWebpOptions = {}\r\n): Promise<ImageToWebpResult> {\r\n const opts = { ...DEFAULTS, ...options }\r\n const blob = await inputToBlob(input)\r\n\r\n const { bitmap, width: srcW, height: srcH } = await decodeImage(blob)\r\n try {\r\n const { width, height } = computeTargetSize(srcW, srcH, opts.maxWidth, opts.maxHeight)\r\n\r\n if (!opts.targetBytes || opts.targetBytes <= 0) {\r\n const out = await encodeWebpFromBitmap(bitmap, width, height, opts.maxQuality)\r\n if (!out) {\r\n if (!opts.force) return { blob, width: srcW, height: srcH, quality: 1, isWebp: false }\r\n return { blob, width: srcW, height: srcH, quality: 1, isWebp: false }\r\n }\r\n\r\n const fileName = opts.fileName ?? getInputName(input)\r\n const file = opts.returnFile ? new File([out], fileName, { type: out.type }) : undefined\r\n return { blob: out, file, width, height, quality: opts.maxQuality, isWebp: true }\r\n }\r\n\r\n const maxQ = clamp01(opts.maxQuality)\r\n const minQ = clamp01(Math.min(opts.minQuality, maxQ))\r\n\r\n const atMax = await encodeWebpFromBitmap(bitmap, width, height, maxQ)\r\n if (!atMax) {\r\n if (!opts.force) return { blob, width: srcW, height: srcH, quality: 1, isWebp: false }\r\n return { blob, width: srcW, height: srcH, quality: 1, isWebp: false }\r\n }\r\n if (atMax.size <= opts.targetBytes) {\r\n const fileName = opts.fileName ?? getInputName(input)\r\n const file = opts.returnFile ? new File([atMax], fileName, { type: atMax.type }) : undefined\r\n return { blob: atMax, file, width, height, quality: maxQ, isWebp: true }\r\n }\r\n\r\n const atMin = await encodeWebpFromBitmap(bitmap, width, height, minQ)\r\n if (!atMin) {\r\n const fileName = opts.fileName ?? getInputName(input)\r\n const file = opts.returnFile ? new File([atMax], fileName, { type: atMax.type }) : undefined\r\n return { blob: atMax, file, width, height, quality: maxQ, isWebp: true }\r\n }\r\n if (atMin.size > opts.targetBytes) {\r\n const fileName = opts.fileName ?? getInputName(input)\r\n const file = opts.returnFile ? new File([atMin], fileName, { type: atMin.type }) : undefined\r\n return { blob: atMin, file, width, height, quality: minQ, isWebp: true }\r\n }\r\n\r\n let lo = minQ\r\n let hi = maxQ\r\n let bestBlob: Blob = atMin\r\n let bestQ = minQ\r\n\r\n for (let i = 0; i < 7; i++) {\r\n const mid = (lo + hi) / 2\r\n const enc = await encodeWebpFromBitmap(bitmap, width, height, mid)\r\n if (!enc) break\r\n\r\n if (enc.size <= opts.targetBytes) {\r\n bestBlob = enc\r\n bestQ = mid\r\n lo = mid\r\n } else {\r\n hi = mid\r\n }\r\n }\r\n\r\n const fileName = opts.fileName ?? getInputName(input)\r\n const file = opts.returnFile ? new File([bestBlob], fileName, { type: bestBlob.type }) : undefined\r\n return { blob: bestBlob, file, width, height, quality: bestQ, isWebp: true }\r\n } finally {\r\n bitmap.close()\r\n }\r\n}\r\n\r\n"]}
1
+ {"version":3,"sources":["../../src/browser/index.ts"],"names":["FFmpeg","fetchFile","fileTypeFromBuffer","canvas","ctx","fileName","file"],"mappings":"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","file":"index.cjs","sourcesContent":["import { FFmpeg } from \"@ffmpeg/ffmpeg\"\r\nimport { fetchFile } from \"@ffmpeg/util\"\r\nimport { fileTypeFromBuffer } from \"file-type\"\r\n\r\nimport type { ConvertResult } from \"../shared/types.js\"\r\n\r\nexport type ImageToWebpInput = Blob | File | string\r\n\r\nexport type ImageToWebpOptions = {\r\n /**\r\n * Hard cap for output dimensions, preserving aspect ratio.\r\n * If the input is larger, it will be downscaled.\r\n */\r\n maxWidth?: number\r\n maxHeight?: number\r\n\r\n /**\r\n * Target maximum output size in bytes.\r\n * If provided, the encoder will try to reach <= targetBytes by reducing quality.\r\n */\r\n targetBytes?: number\r\n\r\n /**\r\n * Quality search range (0..1). Higher is better quality, larger file.\r\n */\r\n maxQuality?: number\r\n minQuality?: number\r\n\r\n /**\r\n * If true, always return WebP even if it can't reach targetBytes.\r\n * If false, will return the original input when WebP isn't supported.\r\n */\r\n force?: boolean\r\n\r\n /**\r\n * Optional output filename (only used when returning a File).\r\n * If omitted and input is a File, the name is derived from it.\r\n */\r\n fileName?: string\r\n\r\n /**\r\n * Return a File instead of a Blob.\r\n */\r\n returnFile?: boolean\r\n}\r\n\r\nexport type ImageToWebpResult = {\r\n blob: Blob\r\n file?: File\r\n width: number\r\n height: number\r\n quality: number\r\n /**\r\n * True if output is `image/webp`.\r\n */\r\n isWebp: boolean\r\n}\r\n\r\nconst DEFAULTS: Required<\r\n Pick<\r\n ImageToWebpOptions,\r\n \"maxWidth\" | \"maxHeight\" | \"maxQuality\" | \"minQuality\" | \"force\" | \"returnFile\"\r\n >\r\n> = {\r\n maxWidth: 2048,\r\n maxHeight: 2048,\r\n maxQuality: 0.82,\r\n minQuality: 0.45,\r\n force: true,\r\n returnFile: false,\r\n}\r\n\r\nfunction clamp01(n: number) {\r\n return Math.max(0, Math.min(1, n))\r\n}\r\n\r\nfunction toWebpName(name: string) {\r\n const base = name.replace(/\\.[^/.]+$/, \"\")\r\n return `${base || \"image\"}.webp`\r\n}\r\n\r\nfunction getInputName(input: ImageToWebpInput) {\r\n if (typeof input === \"string\") return \"image.webp\"\r\n if (input instanceof File && input.name) return toWebpName(input.name)\r\n return \"image.webp\"\r\n}\r\n\r\nasync function inputToBlob(input: ImageToWebpInput): Promise<Blob> {\r\n if (typeof input === \"string\") {\r\n const res = await fetch(input)\r\n if (!res.ok) throw new Error(`Failed to fetch: ${res.status} ${res.statusText}`)\r\n return await res.blob()\r\n }\r\n return input\r\n}\r\n\r\nfunction toWebmName(name: string) {\r\n const base = name.replace(/\\.[^/.]+$/, \"\")\r\n return `${base || \"video\"}.webm`\r\n}\r\n\r\nfunction defaultVideoName(input: Blob | File | string, fileName?: string) {\r\n if (fileName && fileName.trim().length > 0) return fileName\r\n if (typeof input === \"string\") return \"video.webm\"\r\n if (input instanceof File && input.name) return toWebmName(input.name)\r\n return \"video.webm\"\r\n}\r\n\r\nlet ffmpegSingleton: FFmpeg | null = null\r\nlet ffmpegLoadPromise: Promise<FFmpeg> | null = null\r\nlet ffmpegJobQueue: Promise<void> = Promise.resolve()\r\nlet ffmpegConfigKey = \"{}\"\r\n\r\nfunction normalizeBrowserFfmpegLoadOptions(input?: BrowserFfmpegLoadOptions): BrowserFfmpegLoadOptions | undefined {\r\n if (!input) return undefined\r\n const baseURL = input.baseURL?.trim()\r\n const normalize = (v?: string) => v?.trim() || undefined\r\n const make = (suffix: string) => {\r\n if (!baseURL) return undefined\r\n return `${baseURL.replace(/\\/+$/, \"\")}/${suffix}`\r\n }\r\n return {\r\n baseURL,\r\n coreURL: normalize(input.coreURL) ?? make(\"ffmpeg-core.js\"),\r\n wasmURL: normalize(input.wasmURL) ?? make(\"ffmpeg-core.wasm\"),\r\n workerURL: normalize(input.workerURL) ?? make(\"ffmpeg-core.worker.js\"),\r\n }\r\n}\r\n\r\nfunction getFfmpegConfigKey(input?: BrowserFfmpegLoadOptions): string {\r\n return JSON.stringify(normalizeBrowserFfmpegLoadOptions(input) ?? {})\r\n}\r\n\r\nasync function getFfmpeg(loadOptions?: BrowserFfmpegLoadOptions): Promise<FFmpeg> {\r\n const nextKey = getFfmpegConfigKey(loadOptions)\r\n if (nextKey !== ffmpegConfigKey) {\r\n await resetFfmpegRuntime()\r\n ffmpegConfigKey = nextKey\r\n }\r\n if (ffmpegSingleton) return ffmpegSingleton\r\n if (ffmpegLoadPromise) return ffmpegLoadPromise\r\n const ff = new FFmpeg()\r\n const normalized = normalizeBrowserFfmpegLoadOptions(loadOptions)\r\n const loadParams =\r\n normalized && (normalized.coreURL || normalized.wasmURL || normalized.workerURL)\r\n ? {\r\n ...(normalized.coreURL ? { coreURL: normalized.coreURL } : {}),\r\n ...(normalized.wasmURL ? { wasmURL: normalized.wasmURL } : {}),\r\n ...(normalized.workerURL ? { workerURL: normalized.workerURL } : {}),\r\n }\r\n : undefined\r\n ffmpegLoadPromise = (async () => {\r\n try {\r\n await ff.load(loadParams as any)\r\n ffmpegSingleton = ff\r\n return ff\r\n } catch (err) {\r\n ffmpegLoadPromise = null\r\n const detail = [\r\n \"Failed to initialize ffmpeg.wasm.\",\r\n `error=${toErrorMessage(err)}`,\r\n \"hint=Ensure ffmpeg core/worker assets are reachable by your bundler/runtime and not blocked by CSP/CORS.\",\r\n ].join(\"\\n\")\r\n throw new Error(detail)\r\n }\r\n })()\r\n return ffmpegLoadPromise\r\n}\r\n\r\nasync function resetFfmpegRuntime(): Promise<void> {\r\n const current = ffmpegSingleton\r\n ffmpegSingleton = null\r\n ffmpegLoadPromise = null\r\n if (current && typeof (current as any).terminate === \"function\") {\r\n try {\r\n await (current as any).terminate()\r\n } catch {\r\n // Best-effort reset only.\r\n }\r\n }\r\n}\r\n\r\nasync function runWithFfmpegLock<T>(job: () => Promise<T>): Promise<T> {\r\n const previous = ffmpegJobQueue\r\n let release!: () => void\r\n ffmpegJobQueue = new Promise<void>((resolve) => {\r\n release = resolve\r\n })\r\n await previous\r\n try {\r\n return await job()\r\n } finally {\r\n release()\r\n }\r\n}\r\n\r\nfunction createFsSafeId(): string {\r\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`\r\n}\r\n\r\nfunction toErrorMessage(err: unknown): string {\r\n if (err instanceof Error && err.message) return err.message\r\n if (typeof err === \"string\") return err\r\n try {\r\n return JSON.stringify(err)\r\n } catch {\r\n return String(err)\r\n }\r\n}\r\n\r\nfunction formatArgs(args: string[]): string {\r\n return args.map((v) => (/\\s/.test(v) ? `\"${v}\"` : v)).join(\" \")\r\n}\r\n\r\nfunction isRecoverableWasmError(err: unknown): boolean {\r\n const msg = toErrorMessage(err).toLowerCase()\r\n return (\r\n msg.includes(\"memory access out of bounds\") ||\r\n msg.includes(\"runtimeerror\") ||\r\n msg.includes(\"aborted\") ||\r\n msg.includes(\"wasm\")\r\n )\r\n}\r\n\r\nexport type VideoToWebmInput = Blob | File | string\r\n\r\nexport type BrowserFfmpegLoadOptions = {\r\n /**\r\n * Base URL where ffmpeg.wasm assets live.\r\n * Missing specific URLs are derived from this.\r\n */\r\n baseURL?: string\r\n coreURL?: string\r\n wasmURL?: string\r\n workerURL?: string\r\n}\r\n\r\nexport type VideoToWebmOptions = {\r\n crf?: number\r\n deadline?: \"good\" | \"best\" | \"realtime\"\r\n maxDurationSeconds?: number\r\n /**\r\n * Guardrail for browser wasm memory pressure.\r\n * Throws before transcoding when input exceeds this limit.\r\n */\r\n maxInputBytes?: number\r\n /**\r\n * On recoverable wasm crashes, retry with lower-memory ffmpeg args.\r\n */\r\n enableLowMemoryFallback?: boolean\r\n /**\r\n * Max output width used by low-memory fallback.\r\n */\r\n fallbackMaxWidth?: number\r\n /**\r\n * Optional browser ffmpeg.wasm asset URL configuration.\r\n */\r\n ffmpeg?: BrowserFfmpegLoadOptions\r\n fileName?: string\r\n returnFile?: boolean\r\n}\r\n\r\nexport type VideoToWebmResult = {\r\n blob: Blob\r\n file?: File\r\n isWebm: boolean\r\n}\r\n\r\nexport type VideoToWebmDebugStage =\r\n | \"input\"\r\n | \"guardrail\"\r\n | \"init\"\r\n | \"transcode\"\r\n | \"retry-reset\"\r\n | \"retry-init\"\r\n | \"retry-transcode\"\r\n | \"done\"\r\n\r\nexport type VideoToWebmDebugEvent = {\r\n stage: VideoToWebmDebugStage\r\n message: string\r\n detail?: string\r\n}\r\n\r\nexport type VideoToWebmDebugCause =\r\n | \"none\"\r\n | \"input-fetch-failed\"\r\n | \"input-too-large\"\r\n | \"ffmpeg-init-failed\"\r\n | \"ffmpeg-transcode-failed\"\r\n | \"ffmpeg-wasm-runtime-crash\"\r\n | \"unsupported-input\"\r\n | \"unknown\"\r\n\r\nexport type VideoToWebmDiagnostic = {\r\n cause: VideoToWebmDebugCause\r\n stage: VideoToWebmDebugStage\r\n rawError?: string\r\n recoverable: boolean\r\n hints: string[]\r\n}\r\n\r\nexport type VideoToWebmDebugResult =\r\n | ({ ok: true } & VideoToWebmResult & { events: VideoToWebmDebugEvent[]; diagnostic: VideoToWebmDiagnostic })\r\n | { ok: false; error: Error; events: VideoToWebmDebugEvent[]; diagnostic: VideoToWebmDiagnostic }\r\n\r\nconst VIDEO_DEFAULTS: Required<\r\n Pick<\r\n VideoToWebmOptions,\r\n \"crf\" | \"deadline\" | \"returnFile\" | \"maxInputBytes\" | \"enableLowMemoryFallback\" | \"fallbackMaxWidth\"\r\n >\r\n> = {\r\n crf: 32,\r\n deadline: \"good\",\r\n returnFile: false,\r\n maxInputBytes: 0,\r\n enableLowMemoryFallback: true,\r\n fallbackMaxWidth: 960,\r\n}\r\n\r\nfunction assertVideoInputGuardrails(blob: Blob, opts: Required<Pick<VideoToWebmOptions, \"maxInputBytes\">>) {\r\n if (opts.maxInputBytes > 0 && blob.size > opts.maxInputBytes) {\r\n throw new Error(\r\n [\r\n \"videoToWebm guardrail: input too large for browser ffmpeg.wasm.\",\r\n `input_bytes=${blob.size}`,\r\n `max_input_bytes=${opts.maxInputBytes}`,\r\n \"hint=Lower source resolution/bitrate, trim video, or increase maxInputBytes if your environment has enough memory.\",\r\n ].join(\"\\n\")\r\n )\r\n }\r\n}\r\n\r\ntype NormalizedVideoOptions = Required<\r\n Pick<\r\n VideoToWebmOptions,\r\n \"crf\" | \"deadline\" | \"returnFile\" | \"maxInputBytes\" | \"enableLowMemoryFallback\" | \"fallbackMaxWidth\"\r\n >\r\n> &\r\n Pick<VideoToWebmOptions, \"maxDurationSeconds\" | \"fileName\" | \"ffmpeg\">\r\n\r\nfunction buildVideoDiagnostic(stage: VideoToWebmDebugStage, err: unknown): VideoToWebmDiagnostic {\r\n const raw = toErrorMessage(err)\r\n const lower = raw.toLowerCase()\r\n const recoverable = isRecoverableWasmError(err)\r\n\r\n if (lower.includes(\"failed to fetch\")) {\r\n return {\r\n cause: \"input-fetch-failed\",\r\n stage,\r\n rawError: raw,\r\n recoverable: false,\r\n hints: [\"Verify URL reachability and CORS policy for browser fetch.\"],\r\n }\r\n }\r\n\r\n if (lower.includes(\"input too large\") || lower.includes(\"max_input_bytes\")) {\r\n return {\r\n cause: \"input-too-large\",\r\n stage,\r\n rawError: raw,\r\n recoverable: false,\r\n hints: [\"Increase maxInputBytes or reduce source size/resolution/duration.\"],\r\n }\r\n }\r\n\r\n if (lower.includes(\"failed to initialize ffmpeg.wasm\")) {\r\n return {\r\n cause: \"ffmpeg-init-failed\",\r\n stage,\r\n rawError: raw,\r\n recoverable,\r\n hints: [\r\n \"Provide video.ffmpeg.baseURL or explicit coreURL/wasmURL/workerURL.\",\r\n \"Ensure CSP/CORS allows ffmpeg worker + wasm assets.\",\r\n ],\r\n }\r\n }\r\n\r\n if (lower.includes(\"memory access out of bounds\") || lower.includes(\"runtimeerror\") || lower.includes(\"aborted\")) {\r\n return {\r\n cause: \"ffmpeg-wasm-runtime-crash\",\r\n stage,\r\n rawError: raw,\r\n recoverable: true,\r\n hints: [\"Reduce workload: trim duration, lower resolution/bitrate, or split video.\"],\r\n }\r\n }\r\n\r\n if (lower.includes(\"videotowebm failed\")) {\r\n return {\r\n cause: \"ffmpeg-transcode-failed\",\r\n stage,\r\n rawError: raw,\r\n recoverable,\r\n hints: [\"Inspect ffmpeg_logs in rawError for codec/container-specific details.\"],\r\n }\r\n }\r\n\r\n if (lower.includes(\"unsupported input type\")) {\r\n return {\r\n cause: \"unsupported-input\",\r\n stage,\r\n rawError: raw,\r\n recoverable: false,\r\n hints: [\"Use a valid image/* or video/* input and ensure proper MIME/magic bytes.\"],\r\n }\r\n }\r\n\r\n return {\r\n cause: \"unknown\",\r\n stage,\r\n rawError: raw,\r\n recoverable,\r\n hints: [\"Use videoToWebmDebug() output to trace failing stage and share rawError.\"],\r\n }\r\n}\r\n\r\nasync function runVideoTranscodeJob(ffmpeg: FFmpeg, blob: Blob, opts: NormalizedVideoOptions) {\r\n const jobId = createFsSafeId()\r\n const inName = `input-${jobId}`\r\n const outName = `output-${jobId}.webm`\r\n\r\n const outBytes = await runWithFfmpegLock(async () => {\r\n await ffmpeg.writeFile(inName, await fetchFile(blob))\r\n\r\n const args: string[] = [\r\n ...(opts.maxDurationSeconds && opts.maxDurationSeconds > 0 ? [\"-t\", String(opts.maxDurationSeconds)] : []),\r\n \"-i\",\r\n inName,\r\n \"-map\",\r\n \"0:v:0\",\r\n \"-map\",\r\n \"0:a?\",\r\n \"-c:v\",\r\n \"libvpx-vp9\",\r\n \"-b:v\",\r\n \"0\",\r\n \"-crf\",\r\n String(opts.crf),\r\n \"-deadline\",\r\n opts.deadline,\r\n \"-row-mt\",\r\n \"1\",\r\n \"-c:a\",\r\n \"libopus\",\r\n outName,\r\n ]\r\n\r\n const logs: string[] = []\r\n const onLog = ({ message }: { message: string }) => {\r\n if (typeof message !== \"string\" || message.length === 0) return\r\n logs.push(message)\r\n if (logs.length > 60) logs.shift()\r\n }\r\n\r\n try {\r\n ffmpeg.on(\"log\", onLog)\r\n await ffmpeg.exec(args)\r\n const outData = await ffmpeg.readFile(outName)\r\n return outData instanceof Uint8Array ? outData : new Uint8Array(outData as any)\r\n } catch (err) {\r\n const detail = [\r\n `videoToWebm failed.`,\r\n `input=${inName}`,\r\n `output=${outName}`,\r\n `args=${formatArgs(args)}`,\r\n `error=${toErrorMessage(err)}`,\r\n logs.length ? `ffmpeg_logs:\\n${logs.join(\"\\n\")}` : \"ffmpeg_logs: <none>\",\r\n ].join(\"\\n\")\r\n throw new Error(detail)\r\n } finally {\r\n ffmpeg.off(\"log\", onLog)\r\n await Promise.allSettled([ffmpeg.deleteFile(inName), ffmpeg.deleteFile(outName)])\r\n }\r\n })\r\n\r\n return new Blob([outBytes], { type: \"video/webm\" })\r\n}\r\n\r\nasync function runVideoTranscodeLowMemoryJob(ffmpeg: FFmpeg, blob: Blob, opts: NormalizedVideoOptions) {\r\n const jobId = createFsSafeId()\r\n const inName = `input-${jobId}`\r\n const outName = `output-${jobId}.webm`\r\n const safeWidth = Math.max(320, Math.round(opts.fallbackMaxWidth))\r\n\r\n const outBytes = await runWithFfmpegLock(async () => {\r\n await ffmpeg.writeFile(inName, await fetchFile(blob))\r\n\r\n const args: string[] = [\r\n ...(opts.maxDurationSeconds && opts.maxDurationSeconds > 0 ? [\"-t\", String(opts.maxDurationSeconds)] : []),\r\n \"-i\",\r\n inName,\r\n \"-map\",\r\n \"0:v:0\",\r\n \"-map\",\r\n \"0:a?\",\r\n \"-vf\",\r\n `scale='min(${safeWidth},iw)':-2:flags=bilinear`,\r\n \"-c:v\",\r\n \"libvpx-vp9\",\r\n \"-b:v\",\r\n \"0\",\r\n \"-crf\",\r\n String(Math.max(34, opts.crf)),\r\n \"-deadline\",\r\n \"realtime\",\r\n \"-cpu-used\",\r\n \"8\",\r\n \"-row-mt\",\r\n \"1\",\r\n \"-c:a\",\r\n \"libopus\",\r\n outName,\r\n ]\r\n\r\n const logs: string[] = []\r\n const onLog = ({ message }: { message: string }) => {\r\n if (typeof message !== \"string\" || message.length === 0) return\r\n logs.push(message)\r\n if (logs.length > 60) logs.shift()\r\n }\r\n\r\n try {\r\n ffmpeg.on(\"log\", onLog)\r\n await ffmpeg.exec(args)\r\n const outData = await ffmpeg.readFile(outName)\r\n return outData instanceof Uint8Array ? outData : new Uint8Array(outData as any)\r\n } catch (err) {\r\n const detail = [\r\n `videoToWebm low-memory fallback failed.`,\r\n `input=${inName}`,\r\n `output=${outName}`,\r\n `args=${formatArgs(args)}`,\r\n `error=${toErrorMessage(err)}`,\r\n logs.length ? `ffmpeg_logs:\\n${logs.join(\"\\n\")}` : \"ffmpeg_logs: <none>\",\r\n ].join(\"\\n\")\r\n throw new Error(detail)\r\n } finally {\r\n ffmpeg.off(\"log\", onLog)\r\n await Promise.allSettled([ffmpeg.deleteFile(inName), ffmpeg.deleteFile(outName)])\r\n }\r\n })\r\n\r\n return new Blob([outBytes], { type: \"video/webm\" })\r\n}\r\n\r\nexport async function videoToWebm(\r\n input: VideoToWebmInput,\r\n options: VideoToWebmOptions = {}\r\n): Promise<VideoToWebmResult> {\r\n const opts = { ...VIDEO_DEFAULTS, ...options }\r\n const blob = await inputToBlob(input)\r\n assertVideoInputGuardrails(blob, opts)\r\n\r\n let outBlob: Blob\r\n try {\r\n const ffmpeg = await getFfmpeg(opts.ffmpeg)\r\n outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts)\r\n } catch (err) {\r\n if (!isRecoverableWasmError(err)) throw err\r\n await resetFfmpegRuntime()\r\n const ffmpeg = await getFfmpeg(opts.ffmpeg)\r\n try {\r\n outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts)\r\n } catch (retryErr) {\r\n if (!isRecoverableWasmError(retryErr) || !opts.enableLowMemoryFallback) throw retryErr\r\n await resetFfmpegRuntime()\r\n const ffmpegFallback = await getFfmpeg(opts.ffmpeg)\r\n outBlob = await runVideoTranscodeLowMemoryJob(ffmpegFallback, blob, opts)\r\n }\r\n }\r\n\r\n const file = opts.returnFile ? new File([outBlob], defaultVideoName(input, opts.fileName), { type: outBlob.type }) : undefined\r\n return { blob: outBlob, file, isWebm: true }\r\n}\r\n\r\nexport async function videoToWebmDebug(\r\n input: VideoToWebmInput,\r\n options: VideoToWebmOptions = {},\r\n onEvent?: (event: VideoToWebmDebugEvent) => void\r\n): Promise<VideoToWebmDebugResult> {\r\n const events: VideoToWebmDebugEvent[] = []\r\n let lastStage: VideoToWebmDebugStage = \"input\"\r\n const emit = (event: VideoToWebmDebugEvent) => {\r\n lastStage = event.stage\r\n events.push(event)\r\n onEvent?.(event)\r\n }\r\n\r\n try {\r\n emit({ stage: \"input\", message: \"Reading input as Blob.\" })\r\n const opts = { ...VIDEO_DEFAULTS, ...options }\r\n const blob = await inputToBlob(input)\r\n\r\n emit({\r\n stage: \"guardrail\",\r\n message: \"Checking browser ffmpeg.wasm memory guardrail.\",\r\n detail: `input_bytes=${blob.size};max_input_bytes=${opts.maxInputBytes}`,\r\n })\r\n assertVideoInputGuardrails(blob, opts)\r\n\r\n let outBlob: Blob\r\n try {\r\n emit({ stage: \"init\", message: \"Initializing ffmpeg.wasm runtime.\" })\r\n const ffmpeg = await getFfmpeg(opts.ffmpeg)\r\n emit({ stage: \"transcode\", message: \"Running video transcode job (VP9 + Opus).\" })\r\n outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts)\r\n } catch (err) {\r\n if (!isRecoverableWasmError(err)) throw err\r\n emit({\r\n stage: \"retry-reset\",\r\n message: \"Recoverable wasm error detected, resetting runtime.\",\r\n detail: toErrorMessage(err),\r\n })\r\n await resetFfmpegRuntime()\r\n emit({ stage: \"retry-init\", message: \"Reinitializing ffmpeg.wasm runtime.\" })\r\n const ffmpeg = await getFfmpeg(opts.ffmpeg)\r\n emit({ stage: \"retry-transcode\", message: \"Retrying video transcode job.\" })\r\n try {\r\n outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts)\r\n } catch (retryErr) {\r\n if (!isRecoverableWasmError(retryErr) || !opts.enableLowMemoryFallback) throw retryErr\r\n emit({\r\n stage: \"retry-transcode\",\r\n message: \"Running low-memory fallback transcode profile.\",\r\n detail: `fallback_max_width=${opts.fallbackMaxWidth}`,\r\n })\r\n await resetFfmpegRuntime()\r\n const ffmpegFallback = await getFfmpeg(opts.ffmpeg)\r\n outBlob = await runVideoTranscodeLowMemoryJob(ffmpegFallback, blob, opts)\r\n }\r\n }\r\n\r\n const file = opts.returnFile ? new File([outBlob], defaultVideoName(input, opts.fileName), { type: outBlob.type }) : undefined\r\n emit({\r\n stage: \"done\",\r\n message: \"Video converted to WebM.\",\r\n detail: `output_bytes=${outBlob.size};mime=${outBlob.type}`,\r\n })\r\n return {\r\n ok: true,\r\n blob: outBlob,\r\n file,\r\n isWebm: true,\r\n events,\r\n diagnostic: { cause: \"none\", stage: \"done\", recoverable: false, hints: [] },\r\n }\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(toErrorMessage(err))\r\n const diagnostic = buildVideoDiagnostic(lastStage, error)\r\n emit({\r\n stage: \"done\",\r\n message: \"Video conversion failed.\",\r\n detail: `cause=${diagnostic.cause};${error.message}`,\r\n })\r\n return { ok: false, error, events, diagnostic }\r\n }\r\n}\r\n\r\nexport type ConvertInput = ImageToWebpInput\r\n\r\nexport type ConvertOptions = {\r\n image?: ImageToWebpOptions\r\n video?: VideoToWebmOptions\r\n returnFile?: boolean\r\n fileName?: string\r\n}\r\n\r\nasync function detectKindBrowser(input: ConvertInput): Promise<\"image\" | \"video\" | \"unknown\"> {\r\n if (typeof input !== \"string\") {\r\n const t = (input.type || \"\").toLowerCase()\r\n if (t.startsWith(\"image/\")) return \"image\"\r\n if (t.startsWith(\"video/\")) return \"video\"\r\n }\r\n\r\n const blob = await inputToBlob(input)\r\n const buf = new Uint8Array(await blob.slice(0, 4100).arrayBuffer())\r\n const ft = await fileTypeFromBuffer(buf)\r\n if (!ft) return \"unknown\"\r\n if (ft.mime.startsWith(\"image/\")) return \"image\"\r\n if (ft.mime.startsWith(\"video/\")) return \"video\"\r\n return \"unknown\"\r\n}\r\n\r\nexport async function convert(input: ConvertInput, options: ConvertOptions = {}): Promise<ConvertResult> {\r\n const kind = await detectKindBrowser(input)\r\n const returnFile = options.returnFile ?? false\r\n\r\n if (kind === \"image\") {\r\n const res = await imageToWebp(input, { ...options.image, returnFile, fileName: options.fileName })\r\n return { kind: \"image\", ...res }\r\n }\r\n\r\n if (kind === \"video\") {\r\n const res = await videoToWebm(input, { ...options.video, returnFile, fileName: options.fileName })\r\n return { kind: \"video\", ...res }\r\n }\r\n\r\n throw new Error(\"Unsupported input type. Expected an image/* or video/* asset.\")\r\n}\r\n\r\nfunction computeTargetSize(\r\n srcW: number,\r\n srcH: number,\r\n maxW: number,\r\n maxH: number\r\n): { width: number; height: number } {\r\n if (srcW <= 0 || srcH <= 0) return { width: srcW, height: srcH }\r\n const scale = Math.min(1, maxW / srcW, maxH / srcH)\r\n return {\r\n width: Math.max(1, Math.round(srcW * scale)),\r\n height: Math.max(1, Math.round(srcH * scale)),\r\n }\r\n}\r\n\r\nasync function decodeImage(blob: Blob): Promise<{ bitmap: ImageBitmap; width: number; height: number }> {\r\n if (typeof createImageBitmap === \"function\") {\r\n const bitmap = await createImageBitmap(blob, { imageOrientation: \"from-image\" as any })\r\n return { bitmap, width: bitmap.width, height: bitmap.height }\r\n }\r\n\r\n const url = URL.createObjectURL(blob)\r\n try {\r\n const img = await new Promise<HTMLImageElement>((resolve, reject) => {\r\n const el = new Image()\r\n el.onload = () => resolve(el)\r\n el.onerror = () => reject(new Error(\"Failed to decode image\"))\r\n el.src = url\r\n })\r\n\r\n const canvas = document.createElement(\"canvas\")\r\n canvas.width = img.naturalWidth || img.width\r\n canvas.height = img.naturalHeight || img.height\r\n const ctx = canvas.getContext(\"2d\")\r\n if (!ctx) throw new Error(\"Canvas 2D context not available\")\r\n ctx.drawImage(img, 0, 0)\r\n\r\n const dataUrl = canvas.toDataURL(\"image/png\")\r\n const pngBlob = await (await fetch(dataUrl)).blob()\r\n const bitmap = await createImageBitmap(pngBlob)\r\n return { bitmap, width: bitmap.width, height: bitmap.height }\r\n } finally {\r\n URL.revokeObjectURL(url)\r\n }\r\n}\r\n\r\nasync function encodeWebpFromBitmap(\r\n bitmap: ImageBitmap,\r\n width: number,\r\n height: number,\r\n quality: number\r\n): Promise<Blob | null> {\r\n const q = clamp01(quality)\r\n\r\n const hasOffscreen = typeof OffscreenCanvas !== \"undefined\"\r\n if (hasOffscreen) {\r\n const canvas = new OffscreenCanvas(width, height)\r\n const ctx = canvas.getContext(\"2d\")\r\n if (!ctx) return null\r\n ctx.drawImage(bitmap, 0, 0, width, height)\r\n try {\r\n return await canvas.convertToBlob({ type: \"image/webp\", quality: q })\r\n } catch {\r\n return null\r\n }\r\n }\r\n\r\n const canvas = document.createElement(\"canvas\")\r\n canvas.width = width\r\n canvas.height = height\r\n const ctx = canvas.getContext(\"2d\")\r\n if (!ctx) return null\r\n ctx.drawImage(bitmap, 0, 0, width, height)\r\n\r\n const blob = await new Promise<Blob | null>((resolve) => {\r\n canvas.toBlob((b) => resolve(b), \"image/webp\", q)\r\n })\r\n return blob\r\n}\r\n\r\n/**\r\n * Convert an image (png/jpg/etc) to WebP and shrink size while keeping similar quality.\r\n *\r\n * - Downscales to `maxWidth`/`maxHeight` (keeps aspect ratio).\r\n * - Encodes as WebP with a quality search to try meeting `targetBytes` (if provided).\r\n */\r\nexport async function imageToWebp(\r\n input: ImageToWebpInput,\r\n options: ImageToWebpOptions = {}\r\n): Promise<ImageToWebpResult> {\r\n const opts = { ...DEFAULTS, ...options }\r\n const blob = await inputToBlob(input)\r\n\r\n const { bitmap, width: srcW, height: srcH } = await decodeImage(blob)\r\n try {\r\n const { width, height } = computeTargetSize(srcW, srcH, opts.maxWidth, opts.maxHeight)\r\n\r\n if (!opts.targetBytes || opts.targetBytes <= 0) {\r\n const out = await encodeWebpFromBitmap(bitmap, width, height, opts.maxQuality)\r\n if (!out) {\r\n if (!opts.force) return { blob, width: srcW, height: srcH, quality: 1, isWebp: false }\r\n return { blob, width: srcW, height: srcH, quality: 1, isWebp: false }\r\n }\r\n\r\n const fileName = opts.fileName ?? getInputName(input)\r\n const file = opts.returnFile ? new File([out], fileName, { type: out.type }) : undefined\r\n return { blob: out, file, width, height, quality: opts.maxQuality, isWebp: true }\r\n }\r\n\r\n const maxQ = clamp01(opts.maxQuality)\r\n const minQ = clamp01(Math.min(opts.minQuality, maxQ))\r\n\r\n const atMax = await encodeWebpFromBitmap(bitmap, width, height, maxQ)\r\n if (!atMax) {\r\n if (!opts.force) return { blob, width: srcW, height: srcH, quality: 1, isWebp: false }\r\n return { blob, width: srcW, height: srcH, quality: 1, isWebp: false }\r\n }\r\n if (atMax.size <= opts.targetBytes) {\r\n const fileName = opts.fileName ?? getInputName(input)\r\n const file = opts.returnFile ? new File([atMax], fileName, { type: atMax.type }) : undefined\r\n return { blob: atMax, file, width, height, quality: maxQ, isWebp: true }\r\n }\r\n\r\n const atMin = await encodeWebpFromBitmap(bitmap, width, height, minQ)\r\n if (!atMin) {\r\n const fileName = opts.fileName ?? getInputName(input)\r\n const file = opts.returnFile ? new File([atMax], fileName, { type: atMax.type }) : undefined\r\n return { blob: atMax, file, width, height, quality: maxQ, isWebp: true }\r\n }\r\n if (atMin.size > opts.targetBytes) {\r\n const fileName = opts.fileName ?? getInputName(input)\r\n const file = opts.returnFile ? new File([atMin], fileName, { type: atMin.type }) : undefined\r\n return { blob: atMin, file, width, height, quality: minQ, isWebp: true }\r\n }\r\n\r\n let lo = minQ\r\n let hi = maxQ\r\n let bestBlob: Blob = atMin\r\n let bestQ = minQ\r\n\r\n for (let i = 0; i < 7; i++) {\r\n const mid = (lo + hi) / 2\r\n const enc = await encodeWebpFromBitmap(bitmap, width, height, mid)\r\n if (!enc) break\r\n\r\n if (enc.size <= opts.targetBytes) {\r\n bestBlob = enc\r\n bestQ = mid\r\n lo = mid\r\n } else {\r\n hi = mid\r\n }\r\n }\r\n\r\n const fileName = opts.fileName ?? getInputName(input)\r\n const file = opts.returnFile ? new File([bestBlob], fileName, { type: bestBlob.type }) : undefined\r\n return { blob: bestBlob, file, width, height, quality: bestQ, isWebp: true }\r\n } finally {\r\n bitmap.close()\r\n }\r\n}\r\n\r\n"]}
@@ -81,6 +81,14 @@ type VideoToWebmOptions = {
81
81
  * Throws before transcoding when input exceeds this limit.
82
82
  */
83
83
  maxInputBytes?: number;
84
+ /**
85
+ * On recoverable wasm crashes, retry with lower-memory ffmpeg args.
86
+ */
87
+ enableLowMemoryFallback?: boolean;
88
+ /**
89
+ * Max output width used by low-memory fallback.
90
+ */
91
+ fallbackMaxWidth?: number;
84
92
  /**
85
93
  * Optional browser ffmpeg.wasm asset URL configuration.
86
94
  */
@@ -81,6 +81,14 @@ type VideoToWebmOptions = {
81
81
  * Throws before transcoding when input exceeds this limit.
82
82
  */
83
83
  maxInputBytes?: number;
84
+ /**
85
+ * On recoverable wasm crashes, retry with lower-memory ffmpeg args.
86
+ */
87
+ enableLowMemoryFallback?: boolean;
88
+ /**
89
+ * Max output width used by low-memory fallback.
90
+ */
91
+ fallbackMaxWidth?: number;
84
92
  /**
85
93
  * Optional browser ffmpeg.wasm asset URL configuration.
86
94
  */
@@ -142,7 +142,9 @@ var VIDEO_DEFAULTS = {
142
142
  crf: 32,
143
143
  deadline: "good",
144
144
  returnFile: false,
145
- maxInputBytes: 64 * 1024 * 1024
145
+ maxInputBytes: 0,
146
+ enableLowMemoryFallback: true,
147
+ fallbackMaxWidth: 960
146
148
  };
147
149
  function assertVideoInputGuardrails(blob, opts) {
148
150
  if (opts.maxInputBytes > 0 && blob.size > opts.maxInputBytes) {
@@ -282,6 +284,68 @@ ${logs.join("\n")}` : "ffmpeg_logs: <none>"
282
284
  });
283
285
  return new Blob([outBytes], { type: "video/webm" });
284
286
  }
287
+ async function runVideoTranscodeLowMemoryJob(ffmpeg, blob, opts) {
288
+ const jobId = createFsSafeId();
289
+ const inName = `input-${jobId}`;
290
+ const outName = `output-${jobId}.webm`;
291
+ const safeWidth = Math.max(320, Math.round(opts.fallbackMaxWidth));
292
+ const outBytes = await runWithFfmpegLock(async () => {
293
+ await ffmpeg.writeFile(inName, await fetchFile(blob));
294
+ const args = [
295
+ ...opts.maxDurationSeconds && opts.maxDurationSeconds > 0 ? ["-t", String(opts.maxDurationSeconds)] : [],
296
+ "-i",
297
+ inName,
298
+ "-map",
299
+ "0:v:0",
300
+ "-map",
301
+ "0:a?",
302
+ "-vf",
303
+ `scale='min(${safeWidth},iw)':-2:flags=bilinear`,
304
+ "-c:v",
305
+ "libvpx-vp9",
306
+ "-b:v",
307
+ "0",
308
+ "-crf",
309
+ String(Math.max(34, opts.crf)),
310
+ "-deadline",
311
+ "realtime",
312
+ "-cpu-used",
313
+ "8",
314
+ "-row-mt",
315
+ "1",
316
+ "-c:a",
317
+ "libopus",
318
+ outName
319
+ ];
320
+ const logs = [];
321
+ const onLog = ({ message }) => {
322
+ if (typeof message !== "string" || message.length === 0) return;
323
+ logs.push(message);
324
+ if (logs.length > 60) logs.shift();
325
+ };
326
+ try {
327
+ ffmpeg.on("log", onLog);
328
+ await ffmpeg.exec(args);
329
+ const outData = await ffmpeg.readFile(outName);
330
+ return outData instanceof Uint8Array ? outData : new Uint8Array(outData);
331
+ } catch (err) {
332
+ const detail = [
333
+ `videoToWebm low-memory fallback failed.`,
334
+ `input=${inName}`,
335
+ `output=${outName}`,
336
+ `args=${formatArgs(args)}`,
337
+ `error=${toErrorMessage(err)}`,
338
+ logs.length ? `ffmpeg_logs:
339
+ ${logs.join("\n")}` : "ffmpeg_logs: <none>"
340
+ ].join("\n");
341
+ throw new Error(detail);
342
+ } finally {
343
+ ffmpeg.off("log", onLog);
344
+ await Promise.allSettled([ffmpeg.deleteFile(inName), ffmpeg.deleteFile(outName)]);
345
+ }
346
+ });
347
+ return new Blob([outBytes], { type: "video/webm" });
348
+ }
285
349
  async function videoToWebm(input, options = {}) {
286
350
  const opts = { ...VIDEO_DEFAULTS, ...options };
287
351
  const blob = await inputToBlob(input);
@@ -294,7 +358,14 @@ async function videoToWebm(input, options = {}) {
294
358
  if (!isRecoverableWasmError(err)) throw err;
295
359
  await resetFfmpegRuntime();
296
360
  const ffmpeg = await getFfmpeg(opts.ffmpeg);
297
- outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts);
361
+ try {
362
+ outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts);
363
+ } catch (retryErr) {
364
+ if (!isRecoverableWasmError(retryErr) || !opts.enableLowMemoryFallback) throw retryErr;
365
+ await resetFfmpegRuntime();
366
+ const ffmpegFallback = await getFfmpeg(opts.ffmpeg);
367
+ outBlob = await runVideoTranscodeLowMemoryJob(ffmpegFallback, blob, opts);
368
+ }
298
369
  }
299
370
  const file = opts.returnFile ? new File([outBlob], defaultVideoName(input, opts.fileName), { type: outBlob.type }) : void 0;
300
371
  return { blob: outBlob, file, isWebm: true };
@@ -334,7 +405,19 @@ async function videoToWebmDebug(input, options = {}, onEvent) {
334
405
  emit({ stage: "retry-init", message: "Reinitializing ffmpeg.wasm runtime." });
335
406
  const ffmpeg = await getFfmpeg(opts.ffmpeg);
336
407
  emit({ stage: "retry-transcode", message: "Retrying video transcode job." });
337
- outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts);
408
+ try {
409
+ outBlob = await runVideoTranscodeJob(ffmpeg, blob, opts);
410
+ } catch (retryErr) {
411
+ if (!isRecoverableWasmError(retryErr) || !opts.enableLowMemoryFallback) throw retryErr;
412
+ emit({
413
+ stage: "retry-transcode",
414
+ message: "Running low-memory fallback transcode profile.",
415
+ detail: `fallback_max_width=${opts.fallbackMaxWidth}`
416
+ });
417
+ await resetFfmpegRuntime();
418
+ const ffmpegFallback = await getFfmpeg(opts.ffmpeg);
419
+ outBlob = await runVideoTranscodeLowMemoryJob(ffmpegFallback, blob, opts);
420
+ }
338
421
  }
339
422
  const file = opts.returnFile ? new File([outBlob], defaultVideoName(input, opts.fileName), { type: outBlob.type }) : void 0;
340
423
  emit({