avbridge 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/dist/{avi-GNTV5ZOH.cjs → avi-6SJLWIWW.cjs} +19 -4
  3. package/dist/avi-6SJLWIWW.cjs.map +1 -0
  4. package/dist/{avi-V6HYQVR2.js → avi-GCGM7OJI.js} +18 -3
  5. package/dist/avi-GCGM7OJI.js.map +1 -0
  6. package/dist/{chunk-EJH67FXG.js → chunk-5DMTJVIU.js} +99 -3
  7. package/dist/chunk-5DMTJVIU.js.map +1 -0
  8. package/dist/{chunk-CUQD23WO.js → chunk-C5VA5U5O.js} +122 -23
  9. package/dist/chunk-C5VA5U5O.js.map +1 -0
  10. package/dist/{chunk-JQH6D4OE.cjs → chunk-G4APZMCP.cjs} +100 -3
  11. package/dist/chunk-G4APZMCP.cjs.map +1 -0
  12. package/dist/{chunk-Y5FYF5KG.cjs → chunk-HZLQNKFN.cjs} +5 -2
  13. package/dist/chunk-HZLQNKFN.cjs.map +1 -0
  14. package/dist/{chunk-PQTZS7OA.js → chunk-ILKDNBSE.js} +5 -2
  15. package/dist/chunk-ILKDNBSE.js.map +1 -0
  16. package/dist/{chunk-O34444ID.cjs → chunk-OE66B34H.cjs} +126 -27
  17. package/dist/chunk-OE66B34H.cjs.map +1 -0
  18. package/dist/element-browser.js +244 -19
  19. package/dist/element-browser.js.map +1 -1
  20. package/dist/element.cjs +9 -5
  21. package/dist/element.cjs.map +1 -1
  22. package/dist/element.d.cts +1 -1
  23. package/dist/element.d.ts +1 -1
  24. package/dist/element.js +8 -4
  25. package/dist/element.js.map +1 -1
  26. package/dist/index.cjs +18 -18
  27. package/dist/index.d.cts +12 -8
  28. package/dist/index.d.ts +12 -8
  29. package/dist/index.js +5 -5
  30. package/dist/libav-loader-27RDIN2I.js +3 -0
  31. package/dist/{libav-loader-XKH2TKUW.js.map → libav-loader-27RDIN2I.js.map} +1 -1
  32. package/dist/libav-loader-IV4AJ2HW.cjs +12 -0
  33. package/dist/{libav-loader-6APXVNIV.cjs.map → libav-loader-IV4AJ2HW.cjs.map} +1 -1
  34. package/dist/{player-BdtUG4rh.d.cts → player-DUyvltvy.d.cts} +3 -3
  35. package/dist/{player-BdtUG4rh.d.ts → player-DUyvltvy.d.ts} +3 -3
  36. package/dist/source-CN43EI7Z.cjs +28 -0
  37. package/dist/{source-SC6ZEQYR.cjs.map → source-CN43EI7Z.cjs.map} +1 -1
  38. package/dist/source-FFZ7TW2B.js +3 -0
  39. package/dist/{source-ZFS4H7J3.js.map → source-FFZ7TW2B.js.map} +1 -1
  40. package/package.json +1 -1
  41. package/src/classify/rules.ts +9 -2
  42. package/src/element/avbridge-video.ts +12 -1
  43. package/src/player.ts +22 -1
  44. package/src/probe/avi.ts +8 -1
  45. package/src/probe/index.ts +30 -9
  46. package/src/strategies/fallback/audio-output.ts +25 -3
  47. package/src/strategies/fallback/decoder.ts +96 -8
  48. package/src/strategies/fallback/index.ts +90 -6
  49. package/src/strategies/fallback/libav-loader.ts +12 -0
  50. package/src/strategies/fallback/video-renderer.ts +29 -4
  51. package/src/strategies/remux/pipeline.ts +10 -1
  52. package/src/types.ts +10 -1
  53. package/src/util/debug.ts +131 -0
  54. package/src/util/source.ts +4 -0
  55. package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.mjs +1 -1
  56. package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.wasm +0 -0
  57. package/dist/avi-GNTV5ZOH.cjs.map +0 -1
  58. package/dist/avi-V6HYQVR2.js.map +0 -1
  59. package/dist/chunk-CUQD23WO.js.map +0 -1
  60. package/dist/chunk-EJH67FXG.js.map +0 -1
  61. package/dist/chunk-JQH6D4OE.cjs.map +0 -1
  62. package/dist/chunk-O34444ID.cjs.map +0 -1
  63. package/dist/chunk-PQTZS7OA.js.map +0 -1
  64. package/dist/chunk-Y5FYF5KG.cjs.map +0 -1
  65. package/dist/libav-loader-6APXVNIV.cjs +0 -12
  66. package/dist/libav-loader-XKH2TKUW.js +0 -3
  67. package/dist/source-SC6ZEQYR.cjs +0 -28
  68. package/dist/source-ZFS4H7J3.js +0 -3
@@ -1,6 +1,93 @@
1
1
  'use strict';
2
2
 
3
3
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
4
+ // src/util/debug.ts
5
+ function isDebugEnabled() {
6
+ if (typeof globalThis === "undefined") return false;
7
+ const g = globalThis;
8
+ if (g.AVBRIDGE_DEBUG === true) return true;
9
+ if (typeof location !== "undefined" && typeof URLSearchParams !== "undefined") {
10
+ try {
11
+ const p = new URLSearchParams(location.search);
12
+ if (p.has("avbridge_debug")) {
13
+ g.AVBRIDGE_DEBUG = true;
14
+ return true;
15
+ }
16
+ } catch {
17
+ }
18
+ }
19
+ return false;
20
+ }
21
+ function fmt(tag) {
22
+ return `[avbridge:${tag}]`;
23
+ }
24
+ var dbg = {
25
+ /** Verbose — only when debug is enabled. The hot-path normal case. */
26
+ info(tag, ...args) {
27
+ if (isDebugEnabled()) console.info(fmt(tag), ...args);
28
+ },
29
+ /** Warning — only when debug is enabled. Non-fatal oddities. */
30
+ warn(tag, ...args) {
31
+ if (isDebugEnabled()) console.warn(fmt(tag), ...args);
32
+ },
33
+ /**
34
+ * Self-diagnosis warning. **Always** emits regardless of debug flag.
35
+ * Use this only for conditions that mean something is actually wrong
36
+ * or degraded — not for routine chatter.
37
+ */
38
+ diag(tag, ...args) {
39
+ console.warn(fmt(tag), ...args);
40
+ },
41
+ /**
42
+ * Timing helper: wraps an async call and logs its elapsed time when
43
+ * debug is on. The callback runs whether debug is on or off — this is
44
+ * just for the `dbg.info` at the end.
45
+ *
46
+ * Also unconditionally fires `dbg.diag` if the elapsed time exceeds
47
+ * `slowMs`, so "the bootstrap took 8 seconds" shows up even without
48
+ * debug mode enabled.
49
+ */
50
+ async timed(tag, label, slowMs, fn) {
51
+ const start = performance.now();
52
+ try {
53
+ const result = await fn();
54
+ const elapsed = performance.now() - start;
55
+ if (isDebugEnabled()) {
56
+ console.info(fmt(tag), `${label} ${elapsed.toFixed(0)}ms`);
57
+ }
58
+ if (elapsed > slowMs) {
59
+ console.warn(
60
+ fmt(tag),
61
+ `${label} took ${elapsed.toFixed(0)}ms (>${slowMs}ms expected) \u2014 this is unusually slow; possible causes: ${hintForTag(tag)}`
62
+ );
63
+ }
64
+ return result;
65
+ } catch (err) {
66
+ const elapsed = performance.now() - start;
67
+ console.warn(
68
+ fmt(tag),
69
+ `${label} FAILED after ${elapsed.toFixed(0)}ms:`,
70
+ err
71
+ );
72
+ throw err;
73
+ }
74
+ }
75
+ };
76
+ function hintForTag(tag) {
77
+ switch (tag) {
78
+ case "probe":
79
+ return "slow network (range request), large sniff window, or libav cold-start";
80
+ case "libav-load":
81
+ return "large .wasm download, misconfigured AVBRIDGE_LIBAV_BASE, or server-side MIME type";
82
+ case "bootstrap":
83
+ return "probe+classify+strategy-init chain; enable AVBRIDGE_DEBUG for a phase breakdown";
84
+ case "cold-start":
85
+ return "decoder is producing output slower than realtime \u2014 check framesDecoded in getDiagnostics()";
86
+ default:
87
+ return "unknown stage \u2014 enable globalThis.AVBRIDGE_DEBUG for more detail";
88
+ }
89
+ }
90
+
4
91
  // src/strategies/fallback/libav-loader.ts
5
92
  var cache = /* @__PURE__ */ new Map();
6
93
  function cacheKey(variant, threads) {
@@ -18,9 +105,18 @@ function loadLibav(variant = "webcodecs", opts = {}) {
18
105
  return entry;
19
106
  }
20
107
  async function loadVariant(variant, wantThreads) {
108
+ return dbg.timed(
109
+ "libav-load",
110
+ `load "${variant}" (threads=${wantThreads})`,
111
+ 5e3,
112
+ () => loadVariantInner(variant, wantThreads)
113
+ );
114
+ }
115
+ async function loadVariantInner(variant, wantThreads) {
21
116
  const key = cacheKey(variant, wantThreads);
22
117
  const base = `${libavBaseUrl()}/${variant}`;
23
118
  const variantUrl = `${base}/libav-${variant}.mjs`;
119
+ dbg.info("libav-load", `fetching ${variantUrl}`);
24
120
  if (typeof fetch === "function") {
25
121
  try {
26
122
  const head = await fetch(variantUrl, { method: "GET", headers: { Range: "bytes=0-0" } });
@@ -88,7 +184,7 @@ function libavBaseUrl() {
88
184
  const override = typeof globalThis !== "undefined" ? globalThis.AVBRIDGE_LIBAV_BASE : void 0;
89
185
  if (override) return override;
90
186
  try {
91
- return new URL("../vendor/libav", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-JQH6D4OE.cjs', document.baseURI).href))).href;
187
+ return new URL("../vendor/libav", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-G4APZMCP.cjs', document.baseURI).href))).href;
92
188
  } catch {
93
189
  if (typeof location !== "undefined" && location.protocol.startsWith("http")) {
94
190
  return `${location.origin}/libav`;
@@ -102,6 +198,7 @@ function chain(message, err) {
102
198
  return new Error(`${message}: ${inner || "(no message \u2014 see browser console)"}`);
103
199
  }
104
200
 
201
+ exports.dbg = dbg;
105
202
  exports.loadLibav = loadLibav;
106
- //# sourceMappingURL=chunk-JQH6D4OE.cjs.map
107
- //# sourceMappingURL=chunk-JQH6D4OE.cjs.map
203
+ //# sourceMappingURL=chunk-G4APZMCP.cjs.map
204
+ //# sourceMappingURL=chunk-G4APZMCP.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/util/debug.ts","../src/strategies/fallback/libav-loader.ts"],"names":[],"mappings":"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","file":"chunk-G4APZMCP.cjs","sourcesContent":["/**\n * Debug + self-diagnosis helper.\n *\n * avbridge has a lot of async stages (probe → classify → libav load →\n * strategy.execute → decoder pump → cold-start gate → first paint) and\n * when something's slow or wrong the symptom — \"it hangs\", \"it stutters\",\n * \"it plays audio without video\" — is usually nowhere near the actual\n * cause. This module gives us two things:\n *\n * 1. **Gated verbose logging.** `dbg.info(tag, ...)` etc. are no-ops\n * unless the consumer sets `globalThis.AVBRIDGE_DEBUG = true` (or\n * uses the matching `?avbridge_debug` URL search param at dev time).\n * When enabled, every log is prefixed with `[avbridge:<tag>]` so the\n * console is filterable.\n *\n * 2. **Unconditional self-diagnosis.** `dbg.warnIf(cond, tag, ...)`\n * always fires when a suspicious condition is detected, even with\n * debug off. These are the things we *know* mean something is\n * broken or degraded and the user would want to know about — e.g.\n * the cold-start gate timing out, the decoder running slower than\n * realtime, a libav variant taking longer to load than any network\n * should take, >20% of packets getting rejected.\n *\n * The guiding principle: **if a symptom caused more than 10 minutes of\n * human debugging once, add a targeted warning so the next instance\n * self-identifies in the console.** This module is where those\n * warnings live.\n */\n\n/** Read the debug flag fresh on every call so it's runtime-toggleable. */\nfunction isDebugEnabled(): boolean {\n if (typeof globalThis === \"undefined\") return false;\n const g = globalThis as { AVBRIDGE_DEBUG?: unknown };\n if (g.AVBRIDGE_DEBUG === true) return true;\n // Convenience: if running in a browser with a `?avbridge_debug` search\n // param, flip the flag on automatically. Useful for demos and quick\n // user reproduction without editing code.\n if (typeof location !== \"undefined\" && typeof URLSearchParams !== \"undefined\") {\n try {\n const p = new URLSearchParams(location.search);\n if (p.has(\"avbridge_debug\")) {\n g.AVBRIDGE_DEBUG = true;\n return true;\n }\n } catch { /* ignore */ }\n }\n return false;\n}\n\nfunction fmt(tag: string): string {\n return `[avbridge:${tag}]`;\n}\n\n/* eslint-disable no-console */\n\nexport const dbg = {\n /** Verbose — only when debug is enabled. The hot-path normal case. */\n info(tag: string, ...args: unknown[]): void {\n if (isDebugEnabled()) console.info(fmt(tag), ...args);\n },\n\n /** Warning — only when debug is enabled. Non-fatal oddities. */\n warn(tag: string, ...args: unknown[]): void {\n if (isDebugEnabled()) console.warn(fmt(tag), ...args);\n },\n\n /**\n * Self-diagnosis warning. **Always** emits regardless of debug flag.\n * Use this only for conditions that mean something is actually wrong\n * or degraded — not for routine chatter.\n */\n diag(tag: string, ...args: unknown[]): void {\n console.warn(fmt(tag), ...args);\n },\n\n /**\n * Timing helper: wraps an async call and logs its elapsed time when\n * debug is on. The callback runs whether debug is on or off — this is\n * just for the `dbg.info` at the end.\n *\n * Also unconditionally fires `dbg.diag` if the elapsed time exceeds\n * `slowMs`, so \"the bootstrap took 8 seconds\" shows up even without\n * debug mode enabled.\n */\n async timed<T>(\n tag: string,\n label: string,\n slowMs: number,\n fn: () => Promise<T>,\n ): Promise<T> {\n const start = performance.now();\n try {\n const result = await fn();\n const elapsed = performance.now() - start;\n if (isDebugEnabled()) {\n console.info(fmt(tag), `${label} ${elapsed.toFixed(0)}ms`);\n }\n if (elapsed > slowMs) {\n console.warn(\n fmt(tag),\n `${label} took ${elapsed.toFixed(0)}ms (>${slowMs}ms expected) — ` +\n `this is unusually slow; possible causes: ${hintForTag(tag)}`,\n );\n }\n return result;\n } catch (err) {\n const elapsed = performance.now() - start;\n console.warn(\n fmt(tag),\n `${label} FAILED after ${elapsed.toFixed(0)}ms:`,\n err,\n );\n throw err;\n }\n },\n};\n\nfunction hintForTag(tag: string): string {\n switch (tag) {\n case \"probe\":\n return \"slow network (range request), large sniff window, or libav cold-start\";\n case \"libav-load\":\n return \"large .wasm download, misconfigured AVBRIDGE_LIBAV_BASE, or server-side MIME type\";\n case \"bootstrap\":\n return \"probe+classify+strategy-init chain; enable AVBRIDGE_DEBUG for a phase breakdown\";\n case \"cold-start\":\n return \"decoder is producing output slower than realtime — check framesDecoded in getDiagnostics()\";\n default:\n return \"unknown stage — enable globalThis.AVBRIDGE_DEBUG for more detail\";\n }\n}\n","/**\n * Lazy libav.js loader supporting multiple variants.\n *\n * avbridge recognises three libav variants:\n *\n * - **webcodecs** — npm `@libav.js/variant-webcodecs`, ~5 MB. Modern formats\n * only (mp4/mkv/webm/ogg/wav/...) — designed to bridge to WebCodecs.\n *\n * - **default** — npm `@libav.js/variant-default`, ~12 MB. Audio-only build\n * (Opus, FLAC, WAV) despite the name. Useful for audio fallback.\n *\n * - **avbridge** — a custom build produced by `scripts/build-libav.sh` and\n * landing in `vendor/libav/`. Includes the AVI/ASF/FLV/MKV demuxers plus\n * the legacy decoders (WMV3, MPEG-4 Part 2, MS-MPEG4 v1/2/3, VC-1, MPEG-1/2,\n * AC-3/E-AC-3, WMAv1/v2/Pro). This is the only variant that can read AVI;\n * the npm variants are intentionally minimal and ship none of the legacy\n * demuxers.\n *\n * Variant resolution always goes through a runtime URL + `/* @vite-ignore *\\/`\n * dynamic import. Static imports trigger Vite's optimized-deps pipeline,\n * which rewrites `import.meta.url` away from the real `dist/` directory and\n * breaks libav's sibling-binary loading.\n */\n\nimport { dbg } from \"../../util/debug.js\";\n\nexport type LibavVariant = \"webcodecs\" | \"default\" | \"avbridge\";\n\nexport interface LoadLibavOptions {\n /**\n * Force threading on/off for this load. If unspecified, defaults to\n * \"true if `crossOriginIsolated`, otherwise false\". Some libav.js code\n * paths (notably the cross-thread reader-device protocol used during\n * `avformat_find_stream_info` for AVI) are unreliable in threaded mode,\n * so probing forces this to `false` while decode keeps it default.\n */\n threads?: boolean;\n}\n\n// Cache key includes both variant and threading mode so probe and decode\n// can run different libav instances of the same variant.\nconst cache: Map<string, Promise<LibavInstance>> = new Map();\n\nfunction cacheKey(variant: LibavVariant, threads: boolean): string {\n return `${variant}:${threads ? \"thr\" : \"wasm\"}`;\n}\n\n/**\n * Load (and cache) a libav.js variant. Pass `\"webcodecs\"` for the small\n * default; pass `\"default\"` for the audio fallback; pass `\"avbridge\"` for the\n * custom build that supports AVI/WMV/legacy codecs.\n */\nexport function loadLibav(\n variant: LibavVariant = \"webcodecs\",\n opts: LoadLibavOptions = {},\n): Promise<LibavInstance> {\n // Threading is OFF by default. The threaded libav.js variant is too\n // fragile in practice for our usage:\n // - Probe (`avformat_find_stream_info` for AVI) throws an `undefined`\n // exception out of `ff_init_demuxer_file`, apparently due to the\n // cross-thread reader-device protocol racing with the main thread.\n // - Decode hits a `TypeError: Cannot read properties of undefined\n // (reading 'apply')` inside libav.js's own worker message handler\n // within seconds of starting — a bug in libav.js's threaded message\n // dispatch that we can't fix from outside.\n //\n // Performance work for the fallback strategy needs to come from elsewhere\n // (WASM SIMD, OffscreenCanvas, larger decode batches) instead of libav's\n // pthreads. Threading can still be force-enabled with\n // `globalThis.AVBRIDGE_LIBAV_THREADS = true` for testing if libav.js fixes\n // those bugs in a future release.\n const env = globalThis as { AVBRIDGE_LIBAV_THREADS?: boolean };\n const wantThreads =\n opts.threads !== undefined\n ? opts.threads\n : env.AVBRIDGE_LIBAV_THREADS === true;\n\n const key = cacheKey(variant, wantThreads);\n let entry = cache.get(key);\n if (!entry) {\n entry = loadVariant(variant, wantThreads);\n cache.set(key, entry);\n }\n return entry;\n}\n\nasync function loadVariant(\n variant: LibavVariant,\n wantThreads: boolean,\n): Promise<LibavInstance> {\n return dbg.timed(\"libav-load\", `load \"${variant}\" (threads=${wantThreads})`, 5000, () =>\n loadVariantInner(variant, wantThreads),\n );\n}\n\nasync function loadVariantInner(\n variant: LibavVariant,\n wantThreads: boolean,\n): Promise<LibavInstance> {\n const key = cacheKey(variant, wantThreads);\n const base = `${libavBaseUrl()}/${variant}`;\n // The custom variant is named `libav-avbridge.mjs`; the npm variants follow\n // the same convention (`libav-webcodecs.mjs`, `libav-default.mjs`).\n const variantUrl = `${base}/libav-${variant}.mjs`;\n dbg.info(\"libav-load\", `fetching ${variantUrl}`);\n\n // Preflight HEAD-ish check: issue a bytes=0-0 range request so a missing\n // file fails fast with a clear error instead of hanging deep inside the\n // dynamic import or inside libav's own WASM instantiation. Surfaces the\n // most common mistake (\"libav base path is wrong\") in <100 ms instead of\n // an indeterminate stall.\n if (typeof fetch === \"function\") {\n try {\n const head = await fetch(variantUrl, { method: \"GET\", headers: { Range: \"bytes=0-0\" } });\n if (!head.ok && head.status !== 206) {\n throw new Error(\n `HTTP ${head.status} ${head.statusText} — check that libav files are served ` +\n `at ${base}/ (override via globalThis.AVBRIDGE_LIBAV_BASE)`,\n );\n }\n // Drain the tiny response so the connection can be reused.\n try { await head.arrayBuffer(); } catch { /* ignore */ }\n } catch (err) {\n cache.delete(key);\n throw chain(\n `libav.js \"${variant}\" variant not reachable at ${variantUrl}`,\n err,\n );\n }\n }\n\n let mod: LoadedVariant;\n try {\n // @ts-ignore runtime URL\n const imported: unknown = await import(/* @vite-ignore */ variantUrl);\n if (!imported || typeof (imported as { LibAV?: unknown }).LibAV !== \"function\") {\n throw new Error(`module at ${variantUrl} did not export LibAV`);\n }\n mod = imported as LoadedVariant;\n } catch (err) {\n cache.delete(key);\n const hint =\n variant === \"avbridge\"\n ? `The \"avbridge\" variant is a custom local build. Run \\`./scripts/build-libav.sh\\` ` +\n `to produce it (requires Emscripten; ~15-30 min the first time).`\n : `Make sure the variant files are present at ${base}/ (set ` +\n `globalThis.AVBRIDGE_LIBAV_BASE to override the default lookup path).`;\n throw new Error(\n `failed to load libav.js \"${variant}\" variant from ${variantUrl}. ${hint} ` +\n `Original error: ${(err as Error).message || String(err)}`,\n );\n }\n\n try {\n const inst = (await mod.LibAV(buildOpts(base, wantThreads))) as LibavInstance;\n await silenceLibavLogs(inst);\n return inst;\n } catch (err) {\n cache.delete(key);\n throw chain(`LibAV() factory failed for \"${variant}\" variant (threads=${wantThreads})`, err);\n }\n}\n\n/**\n * Lower libav's internal log level so the console doesn't get flooded with\n * `[mp3 @ ...] Header missing` and `Video uses a non-standard and wasteful\n * way to store B-frames` warnings on every legacy file. We still get any\n * actual JS-level errors via the normal Error path; this only affects\n * libav's own ffmpeg log channel.\n *\n * AV_LOG_QUIET = -8 (no output at all). If you want to keep fatal errors,\n * use AV_LOG_FATAL = 8 instead.\n */\nasync function silenceLibavLogs(inst: LibavInstance): Promise<void> {\n try {\n const setLevel = (inst as { av_log_set_level?: (n: number) => Promise<void> })\n .av_log_set_level;\n if (typeof setLevel === \"function\") {\n const quiet = (inst as { AV_LOG_QUIET?: number }).AV_LOG_QUIET ?? -8;\n await setLevel(quiet);\n }\n } catch {\n /* not fatal — verbose logs are noise, not an error */\n }\n}\n\nfunction buildOpts(base: string, wantThreads: boolean): Record<string, unknown> {\n // The wantThreads decision is made by `loadLibav()` so callers (probe,\n // decoder) can override per-load. Decode wants pthreads for speed; probe\n // forces them off because libav.js's cross-thread reader-device protocol\n // is unreliable mid-`avformat_find_stream_info` for some AVI files.\n return {\n base,\n nothreads: !wantThreads,\n yesthreads: wantThreads,\n };\n}\n\nfunction libavBaseUrl(): string {\n // Consumer override — the documented \"LGPL replaceability\" hook.\n // Setting `globalThis.AVBRIDGE_LIBAV_BASE = \"/my/path\"` lets anyone swap\n // in a different libav build (custom fragments, security patches, etc.)\n // without rebuilding avbridge.\n const override =\n typeof globalThis !== \"undefined\"\n ? (globalThis as { AVBRIDGE_LIBAV_BASE?: string }).AVBRIDGE_LIBAV_BASE\n : undefined;\n if (override) return override;\n\n // Default: resolve relative to this module's URL. When avbridge is installed\n // under `node_modules/avbridge/`, this module lives at `dist/chunk-*.js` (or\n // `dist/element-browser.js` for the browser entry) and `../vendor/libav`\n // resolves to `node_modules/avbridge/vendor/libav`, where the build step\n // vendored every variant's binaries. That's the zero-config path.\n //\n // `import.meta.url` throws in some synthetic environments (CJS tests, some\n // SSR evaluators). If it fails, fall back to the legacy `/libav` path so\n // consumers who relied on the pre-2.1 behavior still work.\n try {\n return new URL(\"../vendor/libav\", import.meta.url).href;\n } catch {\n if (typeof location !== \"undefined\" && location.protocol.startsWith(\"http\")) {\n return `${location.origin}/libav`;\n }\n return \"/libav\";\n }\n}\n\nfunction chain(message: string, err: unknown): Error {\n const inner = err instanceof Error ? err.message : String(err);\n // eslint-disable-next-line no-console\n console.error(`[avbridge] ${message}:`, err);\n return new Error(`${message}: ${inner || \"(no message — see browser console)\"}`);\n}\n\ninterface LoadedVariant {\n LibAV: (opts?: Record<string, unknown>) => Promise<Record<string, unknown>>;\n}\n\n/** Loose structural type — the AVI probe and the fallback decoder add fields. */\nexport type LibavInstance = Record<string, unknown> & {\n mkreadaheadfile(name: string, blob: Blob): Promise<void>;\n unlinkreadaheadfile(name: string): Promise<void>;\n};\n"]}
@@ -108,6 +108,9 @@ function sniffContainerFromBytes(head) {
108
108
  }
109
109
  if (head[0] === 48 && head[1] === 38 && head[2] === 178 && head[3] === 117 && head[4] === 142 && head[5] === 102 && head[6] === 207 && head[7] === 17) return "asf";
110
110
  if (head[0] === 70 && head[1] === 76 && head[2] === 86) return "flv";
111
+ if (head[0] === 46 && head[1] === 82 && head[2] === 77 && head[3] === 70) {
112
+ return "rm";
113
+ }
111
114
  if (head[0] === 79 && head[1] === 103 && head[2] === 103 && head[3] === 83) return "ogg";
112
115
  if (head[0] === 102 && head[1] === 76 && head[2] === 97 && head[3] === 67) return "flac";
113
116
  if (head[0] === 73 && head[1] === 68 && head[2] === 51) return "mp3";
@@ -149,5 +152,5 @@ exports.normalizeSource = normalizeSource;
149
152
  exports.sniffContainer = sniffContainer;
150
153
  exports.sniffContainerFromBytes = sniffContainerFromBytes;
151
154
  exports.sniffNormalizedSource = sniffNormalizedSource;
152
- //# sourceMappingURL=chunk-Y5FYF5KG.cjs.map
153
- //# sourceMappingURL=chunk-Y5FYF5KG.cjs.map
155
+ //# sourceMappingURL=chunk-HZLQNKFN.cjs.map
156
+ //# sourceMappingURL=chunk-HZLQNKFN.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/util/source.ts"],"names":["sniffBytes"],"mappings":"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","file":"chunk-HZLQNKFN.cjs","sourcesContent":["import type { ContainerKind, MediaInput } from \"../types.js\";\n\n/**\n * Bytes needed by the sniffer to identify every container we recognize.\n * MPEG-TS needs the most: a sync byte at offset 0 *and* offset 188 (one TS\n * packet apart). Allow a little extra for the M2TS variant (offset 4/192).\n */\nconst SNIFF_BYTES_NEEDED = 380;\n\n/**\n * Bytes to fetch from a URL during the initial sniff. We grab a slightly\n * larger range than `SNIFF_BYTES_NEEDED` so the cache has some headroom for\n * the demuxer's first read after sniffing, in case it wants to look at\n * a few extra bytes (e.g. mp4 ftyp + first moov box).\n */\nconst URL_SNIFF_RANGE_BYTES = 32 * 1024;\n\n/**\n * `NormalizedSource` is a discriminated union: every consumer (probe,\n * strategies) decides what to do based on `kind`. URL sources are NOT\n * fetched eagerly; we only do a Range request for the first ~32 KB so the\n * sniffer has bytes to look at. The strategies are then handed the URL\n * directly so they can stream the rest via Range requests.\n *\n * For File / Blob / ArrayBuffer / Uint8Array sources, the bytes are\n * already in memory, so we wrap them as a `blob` variant.\n */\nexport type NormalizedSource =\n | {\n kind: \"blob\";\n blob: Blob;\n name?: string;\n byteLength: number;\n original: MediaInput;\n }\n | {\n kind: \"url\";\n url: string;\n /** Bytes pulled via Range request for the sniffer. NOT the full file. */\n sniffBytes: Uint8Array;\n name?: string;\n /** Total file size from Content-Length / Content-Range. May be undefined. */\n byteLength: number | undefined;\n original: MediaInput;\n };\n\n/** True if this source carries the entire file's bytes (vs. streaming). */\nexport function isInMemorySource(source: NormalizedSource): source is Extract<NormalizedSource, { kind: \"blob\" }> {\n return source.kind === \"blob\";\n}\n\n\n/**\n * Normalize a `MediaInput` for the probe + strategy layers. **Does not**\n * download URL sources in full — only fetches the first ~32 KB via a\n * Range request, which is enough for the sniffer to identify the\n * container. The strategies are then expected to stream the rest via\n * mediabunny's `UrlSource` (Range requests, prefetch, parallelism, cache).\n *\n * For non-URL inputs, the bytes are already in memory and we just wrap them.\n */\nexport async function normalizeSource(source: MediaInput): Promise<NormalizedSource> {\n if (source instanceof File) {\n return {\n kind: \"blob\",\n blob: source,\n name: source.name,\n byteLength: source.size,\n original: source,\n };\n }\n if (source instanceof Blob) {\n return { kind: \"blob\", blob: source, byteLength: source.size, original: source };\n }\n if (source instanceof ArrayBuffer) {\n const blob = new Blob([source]);\n return { kind: \"blob\", blob, byteLength: blob.size, original: source };\n }\n if (source instanceof Uint8Array) {\n const blob = new Blob([source as BlobPart]);\n return { kind: \"blob\", blob, byteLength: blob.size, original: source };\n }\n if (typeof source === \"string\" || source instanceof URL) {\n const url = source instanceof URL ? source.toString() : source;\n return await fetchUrlForSniff(url, source);\n }\n throw new TypeError(\"unsupported source type\");\n}\n\n/**\n * Fetch the first ~32 KB of a URL via a Range request. Falls back to a\n * full GET if the server doesn't support range requests, but in that case\n * we only read the first 32 KB and abort the rest of the response so we\n * don't accidentally buffer a large file.\n */\nasync function fetchUrlForSniff(url: string, originalSource: MediaInput): Promise<NormalizedSource> {\n const name = url.split(\"/\").pop()?.split(\"?\")[0] ?? undefined;\n\n // First attempt: Range request for the sniff window.\n let res: Response;\n try {\n res = await fetch(url, {\n headers: { Range: `bytes=0-${URL_SNIFF_RANGE_BYTES - 1}` },\n });\n } catch (err) {\n throw new Error(`failed to fetch source ${url}: ${(err as Error).message}`);\n }\n if (!res.ok && res.status !== 206) {\n throw new Error(`failed to fetch source ${url}: ${res.status} ${res.statusText}`);\n }\n\n // Determine the total file size from Content-Range (preferred) or Content-Length.\n let byteLength: number | undefined;\n const contentRange = res.headers.get(\"content-range\");\n if (contentRange) {\n // \"bytes 0-32767/12345678\" — parse the part after the slash\n const m = contentRange.match(/\\/(\\d+)$/);\n if (m) byteLength = parseInt(m[1], 10);\n }\n if (byteLength === undefined) {\n const cl = res.headers.get(\"content-length\");\n if (cl) {\n const n = parseInt(cl, 10);\n if (Number.isFinite(n)) {\n // If the server returned 200 (full body), Content-Length is the\n // FILE size. If 206 (partial), it's the chunk size — only use it\n // as a total if no Content-Range was present (server doesn't do\n // ranges) AND the full response is smaller than our sniff window.\n if (res.status === 200) byteLength = n;\n else if (res.status === 206 && !contentRange) byteLength = n;\n }\n }\n }\n\n // Read the sniff bytes. If the server ignored the Range header and is\n // streaming the full file, only read the first window and let the rest\n // be GC'd. We use a reader so we can stop early.\n const reader = res.body?.getReader();\n if (!reader) {\n // No streamed body (some test environments). Fall back to .arrayBuffer()\n // and slice — this might pull more than we wanted, but only for the\n // initial sniff, not the full file.\n const buf = new Uint8Array(await res.arrayBuffer());\n const sniffBytes = buf.slice(0, URL_SNIFF_RANGE_BYTES);\n return { kind: \"url\", url, sniffBytes, name, byteLength, original: originalSource };\n }\n\n const chunks: Uint8Array[] = [];\n let collected = 0;\n while (collected < URL_SNIFF_RANGE_BYTES) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n collected += value.byteLength;\n }\n // Cancel the response so we don't keep downloading.\n await reader.cancel().catch(() => { /* ignore */ });\n\n // Concatenate up to URL_SNIFF_RANGE_BYTES.\n const total = Math.min(collected, URL_SNIFF_RANGE_BYTES);\n const sniffBytes = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n if (offset >= total) break;\n const room = total - offset;\n sniffBytes.set(chunk.subarray(0, Math.min(chunk.byteLength, room)), offset);\n offset += chunk.byteLength;\n }\n\n return { kind: \"url\", url, sniffBytes, name, byteLength, original: originalSource };\n}\n\n/**\n * Identify the container family from a small byte buffer. Used by the\n * probe layer for both file (Blob → first 380 bytes) and URL (Range\n * request → first 32 KB) inputs.\n *\n * Sniffing intentionally does not trust file extensions.\n */\nexport function sniffContainerFromBytes(head: Uint8Array): ContainerKind {\n // MPEG-TS: sync byte 0x47 every 188 bytes. Verify at least two sync\n // bytes in the right places to avoid false positives. Some captures\n // start with a few junk bytes — also try offsets 4 and 192 (M2TS).\n if (head.length >= 376 && head[0] === 0x47 && head[188] === 0x47) {\n return \"mpegts\";\n }\n if (head.length >= 380 && head[4] === 0x47 && head[192] === 0x47) {\n return \"mpegts\"; // M2TS — 4-byte timestamp prefix per packet\n }\n // RIFF....AVI → AVI\n if (\n head[0] === 0x52 && head[1] === 0x49 && head[2] === 0x46 && head[3] === 0x46 &&\n head[8] === 0x41 && head[9] === 0x56 && head[10] === 0x49\n ) return \"avi\";\n // RIFF....WAVE → WAV\n if (\n head[0] === 0x52 && head[1] === 0x49 && head[2] === 0x46 && head[3] === 0x46 &&\n head[8] === 0x57 && head[9] === 0x41 && head[10] === 0x56 && head[11] === 0x45\n ) return \"wav\";\n // EBML start: 1A 45 DF A3 → MKV/WebM. Distinguish later via DocType.\n if (head[0] === 0x1a && head[1] === 0x45 && head[2] === 0xdf && head[3] === 0xa3) {\n return \"mkv\";\n }\n // ftyp at offset 4 → MP4 family\n if (head[4] === 0x66 && head[5] === 0x74 && head[6] === 0x79 && head[7] === 0x70) {\n // brand at bytes 8..11\n const brand = String.fromCharCode(head[8], head[9], head[10], head[11]);\n if (brand.startsWith(\"qt\")) return \"mov\";\n return \"mp4\";\n }\n // ASF / WMV: 30 26 B2 75 8E 66 CF 11\n if (\n head[0] === 0x30 && head[1] === 0x26 && head[2] === 0xb2 && head[3] === 0x75 &&\n head[4] === 0x8e && head[5] === 0x66 && head[6] === 0xcf && head[7] === 0x11\n ) return \"asf\";\n // FLV: 46 4C 56\n if (head[0] === 0x46 && head[1] === 0x4c && head[2] === 0x56) return \"flv\";\n // RealMedia (.rm / .rmvb): \".RMF\" — 2E 52 4D 46\n if (head[0] === 0x2e && head[1] === 0x52 && head[2] === 0x4d && head[3] === 0x46) {\n return \"rm\";\n }\n // OggS: 4F 67 67 53\n if (head[0] === 0x4f && head[1] === 0x67 && head[2] === 0x67 && head[3] === 0x53) return \"ogg\";\n // FLAC: 66 4C 61 43\n if (head[0] === 0x66 && head[1] === 0x4c && head[2] === 0x61 && head[3] === 0x43) return \"flac\";\n // ID3v2: 49 44 33 → MP3 (with id3)\n if (head[0] === 0x49 && head[1] === 0x44 && head[2] === 0x33) return \"mp3\";\n // MPEG audio frame sync: FF Fx\n if (head[0] === 0xff && (head[1] & 0xe0) === 0xe0) {\n // ADTS: FF F1 / FF F9\n if ((head[1] & 0xf6) === 0xf0) return \"adts\";\n return \"mp3\";\n }\n return \"unknown\";\n}\n\n/**\n * Convenience: sniff a `NormalizedSource` regardless of kind. For URL\n * sources, uses the pre-fetched `sniffBytes`. For blob sources, reads the\n * first 380 bytes.\n */\nexport async function sniffNormalizedSource(source: NormalizedSource): Promise<ContainerKind> {\n if (source.kind === \"url\") {\n return sniffContainerFromBytes(source.sniffBytes);\n }\n const buf = await readBlobBytes(source.blob, SNIFF_BYTES_NEEDED);\n return sniffContainerFromBytes(new Uint8Array(buf));\n}\n\n/**\n * Backwards-compatible wrapper for code that still passes a Blob directly.\n * Prefer `sniffNormalizedSource` going forward.\n */\nexport async function sniffContainer(blob: Blob): Promise<ContainerKind> {\n const buf = await readBlobBytes(blob, SNIFF_BYTES_NEEDED);\n return sniffContainerFromBytes(new Uint8Array(buf));\n}\n\n/**\n * Read up to `limit` bytes from a Blob. Tries `Blob.arrayBuffer()` first\n * (modern browsers), then falls back to `FileReader` (works under jsdom).\n */\nasync function readBlobBytes(blob: Blob, limit: number): Promise<ArrayBuffer> {\n const slice = blob.slice(0, limit);\n if (typeof (slice as Blob & { arrayBuffer?: () => Promise<ArrayBuffer> }).arrayBuffer === \"function\") {\n try {\n return await (slice as Blob & { arrayBuffer: () => Promise<ArrayBuffer> }).arrayBuffer();\n } catch {\n /* fall through to FileReader */\n }\n }\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(reader.error ?? new Error(\"FileReader failed\"));\n reader.readAsArrayBuffer(slice);\n });\n}\n"]}
@@ -106,6 +106,9 @@ function sniffContainerFromBytes(head) {
106
106
  }
107
107
  if (head[0] === 48 && head[1] === 38 && head[2] === 178 && head[3] === 117 && head[4] === 142 && head[5] === 102 && head[6] === 207 && head[7] === 17) return "asf";
108
108
  if (head[0] === 70 && head[1] === 76 && head[2] === 86) return "flv";
109
+ if (head[0] === 46 && head[1] === 82 && head[2] === 77 && head[3] === 70) {
110
+ return "rm";
111
+ }
109
112
  if (head[0] === 79 && head[1] === 103 && head[2] === 103 && head[3] === 83) return "ogg";
110
113
  if (head[0] === 102 && head[1] === 76 && head[2] === 97 && head[3] === 67) return "flac";
111
114
  if (head[0] === 73 && head[1] === 68 && head[2] === 51) return "mp3";
@@ -143,5 +146,5 @@ async function readBlobBytes(blob, limit) {
143
146
  }
144
147
 
145
148
  export { isInMemorySource, normalizeSource, sniffContainer, sniffContainerFromBytes, sniffNormalizedSource };
146
- //# sourceMappingURL=chunk-PQTZS7OA.js.map
147
- //# sourceMappingURL=chunk-PQTZS7OA.js.map
149
+ //# sourceMappingURL=chunk-ILKDNBSE.js.map
150
+ //# sourceMappingURL=chunk-ILKDNBSE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/util/source.ts"],"names":["sniffBytes"],"mappings":"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","file":"chunk-ILKDNBSE.js","sourcesContent":["import type { ContainerKind, MediaInput } from \"../types.js\";\n\n/**\n * Bytes needed by the sniffer to identify every container we recognize.\n * MPEG-TS needs the most: a sync byte at offset 0 *and* offset 188 (one TS\n * packet apart). Allow a little extra for the M2TS variant (offset 4/192).\n */\nconst SNIFF_BYTES_NEEDED = 380;\n\n/**\n * Bytes to fetch from a URL during the initial sniff. We grab a slightly\n * larger range than `SNIFF_BYTES_NEEDED` so the cache has some headroom for\n * the demuxer's first read after sniffing, in case it wants to look at\n * a few extra bytes (e.g. mp4 ftyp + first moov box).\n */\nconst URL_SNIFF_RANGE_BYTES = 32 * 1024;\n\n/**\n * `NormalizedSource` is a discriminated union: every consumer (probe,\n * strategies) decides what to do based on `kind`. URL sources are NOT\n * fetched eagerly; we only do a Range request for the first ~32 KB so the\n * sniffer has bytes to look at. The strategies are then handed the URL\n * directly so they can stream the rest via Range requests.\n *\n * For File / Blob / ArrayBuffer / Uint8Array sources, the bytes are\n * already in memory, so we wrap them as a `blob` variant.\n */\nexport type NormalizedSource =\n | {\n kind: \"blob\";\n blob: Blob;\n name?: string;\n byteLength: number;\n original: MediaInput;\n }\n | {\n kind: \"url\";\n url: string;\n /** Bytes pulled via Range request for the sniffer. NOT the full file. */\n sniffBytes: Uint8Array;\n name?: string;\n /** Total file size from Content-Length / Content-Range. May be undefined. */\n byteLength: number | undefined;\n original: MediaInput;\n };\n\n/** True if this source carries the entire file's bytes (vs. streaming). */\nexport function isInMemorySource(source: NormalizedSource): source is Extract<NormalizedSource, { kind: \"blob\" }> {\n return source.kind === \"blob\";\n}\n\n\n/**\n * Normalize a `MediaInput` for the probe + strategy layers. **Does not**\n * download URL sources in full — only fetches the first ~32 KB via a\n * Range request, which is enough for the sniffer to identify the\n * container. The strategies are then expected to stream the rest via\n * mediabunny's `UrlSource` (Range requests, prefetch, parallelism, cache).\n *\n * For non-URL inputs, the bytes are already in memory and we just wrap them.\n */\nexport async function normalizeSource(source: MediaInput): Promise<NormalizedSource> {\n if (source instanceof File) {\n return {\n kind: \"blob\",\n blob: source,\n name: source.name,\n byteLength: source.size,\n original: source,\n };\n }\n if (source instanceof Blob) {\n return { kind: \"blob\", blob: source, byteLength: source.size, original: source };\n }\n if (source instanceof ArrayBuffer) {\n const blob = new Blob([source]);\n return { kind: \"blob\", blob, byteLength: blob.size, original: source };\n }\n if (source instanceof Uint8Array) {\n const blob = new Blob([source as BlobPart]);\n return { kind: \"blob\", blob, byteLength: blob.size, original: source };\n }\n if (typeof source === \"string\" || source instanceof URL) {\n const url = source instanceof URL ? source.toString() : source;\n return await fetchUrlForSniff(url, source);\n }\n throw new TypeError(\"unsupported source type\");\n}\n\n/**\n * Fetch the first ~32 KB of a URL via a Range request. Falls back to a\n * full GET if the server doesn't support range requests, but in that case\n * we only read the first 32 KB and abort the rest of the response so we\n * don't accidentally buffer a large file.\n */\nasync function fetchUrlForSniff(url: string, originalSource: MediaInput): Promise<NormalizedSource> {\n const name = url.split(\"/\").pop()?.split(\"?\")[0] ?? undefined;\n\n // First attempt: Range request for the sniff window.\n let res: Response;\n try {\n res = await fetch(url, {\n headers: { Range: `bytes=0-${URL_SNIFF_RANGE_BYTES - 1}` },\n });\n } catch (err) {\n throw new Error(`failed to fetch source ${url}: ${(err as Error).message}`);\n }\n if (!res.ok && res.status !== 206) {\n throw new Error(`failed to fetch source ${url}: ${res.status} ${res.statusText}`);\n }\n\n // Determine the total file size from Content-Range (preferred) or Content-Length.\n let byteLength: number | undefined;\n const contentRange = res.headers.get(\"content-range\");\n if (contentRange) {\n // \"bytes 0-32767/12345678\" — parse the part after the slash\n const m = contentRange.match(/\\/(\\d+)$/);\n if (m) byteLength = parseInt(m[1], 10);\n }\n if (byteLength === undefined) {\n const cl = res.headers.get(\"content-length\");\n if (cl) {\n const n = parseInt(cl, 10);\n if (Number.isFinite(n)) {\n // If the server returned 200 (full body), Content-Length is the\n // FILE size. If 206 (partial), it's the chunk size — only use it\n // as a total if no Content-Range was present (server doesn't do\n // ranges) AND the full response is smaller than our sniff window.\n if (res.status === 200) byteLength = n;\n else if (res.status === 206 && !contentRange) byteLength = n;\n }\n }\n }\n\n // Read the sniff bytes. If the server ignored the Range header and is\n // streaming the full file, only read the first window and let the rest\n // be GC'd. We use a reader so we can stop early.\n const reader = res.body?.getReader();\n if (!reader) {\n // No streamed body (some test environments). Fall back to .arrayBuffer()\n // and slice — this might pull more than we wanted, but only for the\n // initial sniff, not the full file.\n const buf = new Uint8Array(await res.arrayBuffer());\n const sniffBytes = buf.slice(0, URL_SNIFF_RANGE_BYTES);\n return { kind: \"url\", url, sniffBytes, name, byteLength, original: originalSource };\n }\n\n const chunks: Uint8Array[] = [];\n let collected = 0;\n while (collected < URL_SNIFF_RANGE_BYTES) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n collected += value.byteLength;\n }\n // Cancel the response so we don't keep downloading.\n await reader.cancel().catch(() => { /* ignore */ });\n\n // Concatenate up to URL_SNIFF_RANGE_BYTES.\n const total = Math.min(collected, URL_SNIFF_RANGE_BYTES);\n const sniffBytes = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n if (offset >= total) break;\n const room = total - offset;\n sniffBytes.set(chunk.subarray(0, Math.min(chunk.byteLength, room)), offset);\n offset += chunk.byteLength;\n }\n\n return { kind: \"url\", url, sniffBytes, name, byteLength, original: originalSource };\n}\n\n/**\n * Identify the container family from a small byte buffer. Used by the\n * probe layer for both file (Blob → first 380 bytes) and URL (Range\n * request → first 32 KB) inputs.\n *\n * Sniffing intentionally does not trust file extensions.\n */\nexport function sniffContainerFromBytes(head: Uint8Array): ContainerKind {\n // MPEG-TS: sync byte 0x47 every 188 bytes. Verify at least two sync\n // bytes in the right places to avoid false positives. Some captures\n // start with a few junk bytes — also try offsets 4 and 192 (M2TS).\n if (head.length >= 376 && head[0] === 0x47 && head[188] === 0x47) {\n return \"mpegts\";\n }\n if (head.length >= 380 && head[4] === 0x47 && head[192] === 0x47) {\n return \"mpegts\"; // M2TS — 4-byte timestamp prefix per packet\n }\n // RIFF....AVI → AVI\n if (\n head[0] === 0x52 && head[1] === 0x49 && head[2] === 0x46 && head[3] === 0x46 &&\n head[8] === 0x41 && head[9] === 0x56 && head[10] === 0x49\n ) return \"avi\";\n // RIFF....WAVE → WAV\n if (\n head[0] === 0x52 && head[1] === 0x49 && head[2] === 0x46 && head[3] === 0x46 &&\n head[8] === 0x57 && head[9] === 0x41 && head[10] === 0x56 && head[11] === 0x45\n ) return \"wav\";\n // EBML start: 1A 45 DF A3 → MKV/WebM. Distinguish later via DocType.\n if (head[0] === 0x1a && head[1] === 0x45 && head[2] === 0xdf && head[3] === 0xa3) {\n return \"mkv\";\n }\n // ftyp at offset 4 → MP4 family\n if (head[4] === 0x66 && head[5] === 0x74 && head[6] === 0x79 && head[7] === 0x70) {\n // brand at bytes 8..11\n const brand = String.fromCharCode(head[8], head[9], head[10], head[11]);\n if (brand.startsWith(\"qt\")) return \"mov\";\n return \"mp4\";\n }\n // ASF / WMV: 30 26 B2 75 8E 66 CF 11\n if (\n head[0] === 0x30 && head[1] === 0x26 && head[2] === 0xb2 && head[3] === 0x75 &&\n head[4] === 0x8e && head[5] === 0x66 && head[6] === 0xcf && head[7] === 0x11\n ) return \"asf\";\n // FLV: 46 4C 56\n if (head[0] === 0x46 && head[1] === 0x4c && head[2] === 0x56) return \"flv\";\n // RealMedia (.rm / .rmvb): \".RMF\" — 2E 52 4D 46\n if (head[0] === 0x2e && head[1] === 0x52 && head[2] === 0x4d && head[3] === 0x46) {\n return \"rm\";\n }\n // OggS: 4F 67 67 53\n if (head[0] === 0x4f && head[1] === 0x67 && head[2] === 0x67 && head[3] === 0x53) return \"ogg\";\n // FLAC: 66 4C 61 43\n if (head[0] === 0x66 && head[1] === 0x4c && head[2] === 0x61 && head[3] === 0x43) return \"flac\";\n // ID3v2: 49 44 33 → MP3 (with id3)\n if (head[0] === 0x49 && head[1] === 0x44 && head[2] === 0x33) return \"mp3\";\n // MPEG audio frame sync: FF Fx\n if (head[0] === 0xff && (head[1] & 0xe0) === 0xe0) {\n // ADTS: FF F1 / FF F9\n if ((head[1] & 0xf6) === 0xf0) return \"adts\";\n return \"mp3\";\n }\n return \"unknown\";\n}\n\n/**\n * Convenience: sniff a `NormalizedSource` regardless of kind. For URL\n * sources, uses the pre-fetched `sniffBytes`. For blob sources, reads the\n * first 380 bytes.\n */\nexport async function sniffNormalizedSource(source: NormalizedSource): Promise<ContainerKind> {\n if (source.kind === \"url\") {\n return sniffContainerFromBytes(source.sniffBytes);\n }\n const buf = await readBlobBytes(source.blob, SNIFF_BYTES_NEEDED);\n return sniffContainerFromBytes(new Uint8Array(buf));\n}\n\n/**\n * Backwards-compatible wrapper for code that still passes a Blob directly.\n * Prefer `sniffNormalizedSource` going forward.\n */\nexport async function sniffContainer(blob: Blob): Promise<ContainerKind> {\n const buf = await readBlobBytes(blob, SNIFF_BYTES_NEEDED);\n return sniffContainerFromBytes(new Uint8Array(buf));\n}\n\n/**\n * Read up to `limit` bytes from a Blob. Tries `Blob.arrayBuffer()` first\n * (modern browsers), then falls back to `FileReader` (works under jsdom).\n */\nasync function readBlobBytes(blob: Blob, limit: number): Promise<ArrayBuffer> {\n const slice = blob.slice(0, limit);\n if (typeof (slice as Blob & { arrayBuffer?: () => Promise<ArrayBuffer> }).arrayBuffer === \"function\") {\n try {\n return await (slice as Blob & { arrayBuffer: () => Promise<ArrayBuffer> }).arrayBuffer();\n } catch {\n /* fall through to FileReader */\n }\n }\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => resolve(reader.result as ArrayBuffer);\n reader.onerror = () => reject(reader.error ?? new Error(\"FileReader failed\"));\n reader.readAsArrayBuffer(slice);\n });\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkY5FYF5KG_cjs = require('./chunk-Y5FYF5KG.cjs');
4
- var chunkJQH6D4OE_cjs = require('./chunk-JQH6D4OE.cjs');
3
+ var chunkHZLQNKFN_cjs = require('./chunk-HZLQNKFN.cjs');
4
+ var chunkG4APZMCP_cjs = require('./chunk-G4APZMCP.cjs');
5
5
  var chunkNZU7W256_cjs = require('./chunk-NZU7W256.cjs');
6
6
 
7
7
  // src/probe/mediabunny.ts
@@ -181,19 +181,30 @@ var MEDIABUNNY_CONTAINERS = /* @__PURE__ */ new Set([
181
181
  "mpegts"
182
182
  ]);
183
183
  async function probe(source) {
184
- const normalized = await chunkY5FYF5KG_cjs.normalizeSource(source);
185
- const sniffed = await chunkY5FYF5KG_cjs.sniffNormalizedSource(normalized);
184
+ const normalized = await chunkHZLQNKFN_cjs.normalizeSource(source);
185
+ const sniffed = await chunkHZLQNKFN_cjs.sniffNormalizedSource(normalized);
186
186
  if (MEDIABUNNY_CONTAINERS.has(sniffed)) {
187
187
  try {
188
188
  return await probeWithMediabunny(normalized, sniffed);
189
- } catch (err) {
190
- throw new Error(
191
- `mediabunny failed to probe a ${sniffed} file: ${err.message}`
189
+ } catch (mediabunnyErr) {
190
+ console.warn(
191
+ `[avbridge] mediabunny rejected ${sniffed} file, falling back to libav:`,
192
+ mediabunnyErr.message
192
193
  );
194
+ try {
195
+ const { probeWithLibav } = await import('./avi-6SJLWIWW.cjs');
196
+ return await probeWithLibav(normalized, sniffed);
197
+ } catch (libavErr) {
198
+ const mbMsg = mediabunnyErr.message || String(mediabunnyErr);
199
+ const lvMsg = libavErr instanceof Error ? libavErr.message : String(libavErr);
200
+ throw new Error(
201
+ `failed to probe ${sniffed} file. mediabunny: ${mbMsg}. libav fallback: ${lvMsg}.`
202
+ );
203
+ }
193
204
  }
194
205
  }
195
206
  try {
196
- const { probeWithLibav } = await import('./avi-GNTV5ZOH.cjs');
207
+ const { probeWithLibav } = await import('./avi-6SJLWIWW.cjs');
197
208
  return await probeWithLibav(normalized, sniffed);
198
209
  } catch (err) {
199
210
  const inner = err instanceof Error ? err.message : String(err);
@@ -279,8 +290,29 @@ var NATIVE_AUDIO_CODECS = /* @__PURE__ */ new Set([
279
290
  "vorbis",
280
291
  "flac"
281
292
  ]);
282
- var FALLBACK_VIDEO_CODECS = /* @__PURE__ */ new Set(["wmv3", "vc1", "mpeg4", "rv40", "mpeg2", "mpeg1", "theora"]);
283
- var FALLBACK_AUDIO_CODECS = /* @__PURE__ */ new Set(["wmav2", "wmapro", "ac3", "eac3"]);
293
+ var FALLBACK_VIDEO_CODECS = /* @__PURE__ */ new Set([
294
+ "wmv3",
295
+ "vc1",
296
+ "mpeg4",
297
+ "rv10",
298
+ "rv20",
299
+ "rv30",
300
+ "rv40",
301
+ "mpeg2",
302
+ "mpeg1",
303
+ "theora"
304
+ ]);
305
+ var FALLBACK_AUDIO_CODECS = /* @__PURE__ */ new Set([
306
+ "wmav2",
307
+ "wmapro",
308
+ "ac3",
309
+ "eac3",
310
+ "cook",
311
+ "ra_144",
312
+ "ra_288",
313
+ "sipr",
314
+ "atrac3"
315
+ ]);
284
316
  var NATIVE_CONTAINERS = /* @__PURE__ */ new Set([
285
317
  "mp4",
286
318
  "mov",
@@ -990,7 +1022,8 @@ async function createRemuxPipeline(ctx, video) {
990
1022
  if (destroyed || pumpToken !== token) break;
991
1023
  const vTs = !vNext.done ? vNext.value.timestamp : Number.POSITIVE_INFINITY;
992
1024
  const aTs = !aNext.done ? aNext.value.timestamp : Number.POSITIVE_INFINITY;
993
- if (!vNext.done && vTs <= aTs) {
1025
+ const forceVideoFirst = firstVideo && !vNext.done;
1026
+ if (!vNext.done && (forceVideoFirst || vTs <= aTs)) {
994
1027
  await videoSource.add(
995
1028
  vNext.value,
996
1029
  firstVideo && videoConfig ? { decoderConfig: videoConfig } : void 0
@@ -1128,11 +1161,20 @@ var VideoRenderer = class {
1128
1161
  });
1129
1162
  this.canvas = document.createElement("canvas");
1130
1163
  this.canvas.style.cssText = "position:absolute;left:0;top:0;width:100%;height:100%;background:black;";
1131
- const parent = target.parentElement;
1132
- if (parent && getComputedStyle(parent).position === "static") {
1133
- parent.style.position = "relative";
1164
+ const parent = target.parentElement ?? target.parentNode;
1165
+ if (parent && parent instanceof HTMLElement) {
1166
+ if (getComputedStyle(parent).position === "static") {
1167
+ parent.style.position = "relative";
1168
+ }
1169
+ }
1170
+ if (parent) {
1171
+ parent.insertBefore(this.canvas, target);
1172
+ } else {
1173
+ console.warn(
1174
+ "[avbridge] fallback renderer: target <video> has no parent; appending canvas to document.body as a fallback."
1175
+ );
1176
+ document.body.appendChild(this.canvas);
1134
1177
  }
1135
- parent?.insertBefore(this.canvas, target);
1136
1178
  target.style.visibility = "hidden";
1137
1179
  const ctx = this.canvas.getContext("2d");
1138
1180
  if (!ctx) throw new Error("video renderer: failed to acquire 2D context");
@@ -1464,7 +1506,7 @@ var AudioOutput = class {
1464
1506
  // src/strategies/hybrid/decoder.ts
1465
1507
  async function startHybridDecoder(opts) {
1466
1508
  const variant = chunkNZU7W256_cjs.pickLibavVariant(opts.context);
1467
- const libav = await chunkJQH6D4OE_cjs.loadLibav(variant);
1509
+ const libav = await chunkG4APZMCP_cjs.loadLibav(variant);
1468
1510
  const bridge = await loadBridge();
1469
1511
  const { prepareLibavInput } = await import('./libav-http-reader-FPYDBMYK.cjs');
1470
1512
  const inputHandle = await prepareLibavInput(libav, opts.filename, opts.source);
@@ -1904,7 +1946,7 @@ async function loadBridge() {
1904
1946
  var READY_AUDIO_BUFFER_SECONDS = 0.3;
1905
1947
  var READY_TIMEOUT_SECONDS = 10;
1906
1948
  async function createHybridSession(ctx, target) {
1907
- const { normalizeSource: normalizeSource2 } = await import('./source-SC6ZEQYR.cjs');
1949
+ const { normalizeSource: normalizeSource2 } = await import('./source-CN43EI7Z.cjs');
1908
1950
  const source = await normalizeSource2(ctx.source);
1909
1951
  const fps = ctx.videoTracks[0]?.fps ?? 30;
1910
1952
  const audio = new AudioOutput();
@@ -2006,7 +2048,7 @@ async function createHybridSession(ctx, target) {
2006
2048
  // src/strategies/fallback/decoder.ts
2007
2049
  async function startDecoder(opts) {
2008
2050
  const variant = chunkNZU7W256_cjs.pickLibavVariant(opts.context);
2009
- const libav = await chunkJQH6D4OE_cjs.loadLibav(variant);
2051
+ const libav = await chunkG4APZMCP_cjs.loadLibav(variant);
2010
2052
  const bridge = await loadBridge2();
2011
2053
  const { prepareLibavInput } = await import('./libav-http-reader-FPYDBMYK.cjs');
2012
2054
  const inputHandle = await prepareLibavInput(libav, opts.filename, opts.source);
@@ -2071,6 +2113,9 @@ async function startDecoder(opts) {
2071
2113
  let packetsRead = 0;
2072
2114
  let videoFramesDecoded = 0;
2073
2115
  let audioFramesDecoded = 0;
2116
+ let watchdogFirstFrameMs = 0;
2117
+ let watchdogSlowSinceMs = 0;
2118
+ let watchdogWarned = false;
2074
2119
  let syntheticVideoUs = 0;
2075
2120
  let syntheticAudioUs = 0;
2076
2121
  const videoTrackInfo = opts.context.videoTracks.find((t) => t.id === videoStream?.index);
@@ -2082,7 +2127,7 @@ async function startDecoder(opts) {
2082
2127
  let packets;
2083
2128
  try {
2084
2129
  [readErr, packets] = await libav.ff_read_frame_multi(fmt_ctx, readPkt, {
2085
- limit: 16 * 1024
2130
+ limit: 64 * 1024
2086
2131
  });
2087
2132
  } catch (err) {
2088
2133
  console.error("[avbridge] ff_read_frame_multi failed:", err);
@@ -2099,6 +2144,28 @@ async function startDecoder(opts) {
2099
2144
  await decodeAudioBatch(audioPackets, myToken);
2100
2145
  }
2101
2146
  packetsRead += (videoPackets?.length ?? 0) + (audioPackets?.length ?? 0);
2147
+ if (videoFramesDecoded > 0) {
2148
+ if (watchdogFirstFrameMs === 0) {
2149
+ watchdogFirstFrameMs = performance.now();
2150
+ }
2151
+ const elapsedSinceFirst = (performance.now() - watchdogFirstFrameMs) / 1e3;
2152
+ if (elapsedSinceFirst > 1 && !watchdogWarned) {
2153
+ const expectedFrames = elapsedSinceFirst * videoFps;
2154
+ const ratio = videoFramesDecoded / expectedFrames;
2155
+ if (ratio < 0.6) {
2156
+ if (watchdogSlowSinceMs === 0) watchdogSlowSinceMs = performance.now();
2157
+ if ((performance.now() - watchdogSlowSinceMs) / 1e3 > 5) {
2158
+ watchdogWarned = true;
2159
+ console.warn(
2160
+ "[avbridge:decode-rate]",
2161
+ `decoder is running slower than realtime: ${videoFramesDecoded} frames in ${elapsedSinceFirst.toFixed(1)}s (${(videoFramesDecoded / elapsedSinceFirst).toFixed(1)} fps vs ${videoFps} fps source \u2014 ${(ratio * 100).toFixed(0)}% of realtime). Playback will stutter. Typical causes: software decode of a codec with no WebCodecs support (rv40, mpeg4 @ 720p+, wmv3), or a resolution too large for single-threaded WASM to keep up with.`
2162
+ );
2163
+ }
2164
+ } else {
2165
+ watchdogSlowSinceMs = 0;
2166
+ }
2167
+ }
2168
+ }
2102
2169
  while (!destroyed && myToken === pumpToken && (opts.audio.bufferAhead() > 2 || opts.renderer.queueDepth() >= opts.renderer.queueHighWater)) {
2103
2170
  await new Promise((r) => setTimeout(r, 50));
2104
2171
  }
@@ -2425,10 +2492,10 @@ async function loadBridge2() {
2425
2492
  }
2426
2493
 
2427
2494
  // src/strategies/fallback/index.ts
2428
- var READY_AUDIO_BUFFER_SECONDS2 = 0.3;
2429
- var READY_TIMEOUT_SECONDS2 = 10;
2495
+ var READY_AUDIO_BUFFER_SECONDS2 = 0.04;
2496
+ var READY_TIMEOUT_SECONDS2 = 3;
2430
2497
  async function createFallbackSession(ctx, target) {
2431
- const { normalizeSource: normalizeSource2 } = await import('./source-SC6ZEQYR.cjs');
2498
+ const { normalizeSource: normalizeSource2 } = await import('./source-CN43EI7Z.cjs');
2432
2499
  const source = await normalizeSource2(ctx.source);
2433
2500
  const fps = ctx.videoTracks[0]?.fps ?? 30;
2434
2501
  const audio = new AudioOutput();
@@ -2462,12 +2529,26 @@ async function createFallbackSession(ctx, target) {
2462
2529
  }
2463
2530
  async function waitForBuffer() {
2464
2531
  const start = performance.now();
2532
+ chunkG4APZMCP_cjs.dbg.info(
2533
+ "cold-start",
2534
+ `gate entry: need audio >= ${READY_AUDIO_BUFFER_SECONDS2 * 1e3}ms + 1 frame`
2535
+ );
2465
2536
  while (true) {
2466
- const audioReady = audio.isNoAudio() || audio.bufferAhead() >= READY_AUDIO_BUFFER_SECONDS2;
2467
- if (audioReady && renderer.hasFrames()) {
2537
+ const audioAhead = audio.isNoAudio() ? Infinity : audio.bufferAhead();
2538
+ const audioReady = audio.isNoAudio() || audioAhead >= READY_AUDIO_BUFFER_SECONDS2;
2539
+ const hasFrames = renderer.hasFrames();
2540
+ if (audioReady && hasFrames) {
2541
+ chunkG4APZMCP_cjs.dbg.info(
2542
+ "cold-start",
2543
+ `gate satisfied in ${(performance.now() - start).toFixed(0)}ms (audio=${(audioAhead * 1e3).toFixed(0)}ms, frames=${renderer.queueDepth()})`
2544
+ );
2468
2545
  return;
2469
2546
  }
2470
2547
  if ((performance.now() - start) / 1e3 > READY_TIMEOUT_SECONDS2) {
2548
+ chunkG4APZMCP_cjs.dbg.diag(
2549
+ "cold-start",
2550
+ `gate TIMEOUT after ${READY_TIMEOUT_SECONDS2}s \u2014 audio=${(audioAhead * 1e3).toFixed(0)}ms (needed ${READY_AUDIO_BUFFER_SECONDS2 * 1e3}ms), frames=${renderer.queueDepth()} (needed \u22651). Software decoder is producing output slower than realtime \u2014 playback will stutter. Check getDiagnostics().runtime for the decode rate.`
2551
+ );
2471
2552
  return;
2472
2553
  }
2473
2554
  await new Promise((r) => setTimeout(r, 50));
@@ -2682,8 +2763,14 @@ var UnifiedPlayer = class _UnifiedPlayer {
2682
2763
  return player;
2683
2764
  }
2684
2765
  async bootstrap() {
2766
+ const bootstrapStart = performance.now();
2685
2767
  try {
2686
- const ctx = await probe(this.options.source);
2768
+ chunkG4APZMCP_cjs.dbg.info("bootstrap", "start");
2769
+ const ctx = await chunkG4APZMCP_cjs.dbg.timed("probe", "probe", 3e3, () => probe(this.options.source));
2770
+ chunkG4APZMCP_cjs.dbg.info(
2771
+ "probe",
2772
+ `container=${ctx.container} video=${ctx.videoTracks[0]?.codec ?? "-"} audio=${ctx.audioTracks[0]?.codec ?? "-"} probedBy=${ctx.probedBy}`
2773
+ );
2687
2774
  this.diag.recordProbe(ctx);
2688
2775
  this.mediaContext = ctx;
2689
2776
  if (this.options.subtitles) {
@@ -2709,6 +2796,10 @@ var UnifiedPlayer = class _UnifiedPlayer {
2709
2796
  }
2710
2797
  }
2711
2798
  const decision = this.options.initialStrategy ? buildInitialDecision(this.options.initialStrategy, ctx) : classifyContext(ctx);
2799
+ chunkG4APZMCP_cjs.dbg.info(
2800
+ "classify",
2801
+ `strategy=${decision.strategy} class=${decision.class} reason="${decision.reason}"` + (decision.fallbackChain ? ` fallback=${decision.fallbackChain.join("\u2192")}` : "")
2802
+ );
2712
2803
  this.classification = decision;
2713
2804
  this.diag.recordClassification(decision);
2714
2805
  this.emitter.emitSticky("strategy", {
@@ -2734,6 +2825,14 @@ var UnifiedPlayer = class _UnifiedPlayer {
2734
2825
  this.startTimeupdateLoop();
2735
2826
  this.options.target.addEventListener("ended", () => this.emitter.emit("ended", void 0));
2736
2827
  this.emitter.emitSticky("ready", void 0);
2828
+ const bootstrapElapsed = performance.now() - bootstrapStart;
2829
+ chunkG4APZMCP_cjs.dbg.info("bootstrap", `ready in ${bootstrapElapsed.toFixed(0)}ms`);
2830
+ if (bootstrapElapsed > 5e3) {
2831
+ console.warn(
2832
+ "[avbridge:bootstrap]",
2833
+ `total bootstrap time ${bootstrapElapsed.toFixed(0)}ms \u2014 unusually slow. Enable globalThis.AVBRIDGE_DEBUG for a per-phase breakdown.`
2834
+ );
2835
+ }
2737
2836
  } catch (err) {
2738
2837
  const e = err instanceof Error ? err : new Error(String(err));
2739
2838
  this.diag.recordError(e);
@@ -3065,5 +3164,5 @@ exports.classifyContext = classifyContext;
3065
3164
  exports.createPlayer = createPlayer;
3066
3165
  exports.probe = probe;
3067
3166
  exports.srtToVtt = srtToVtt;
3068
- //# sourceMappingURL=chunk-O34444ID.cjs.map
3069
- //# sourceMappingURL=chunk-O34444ID.cjs.map
3167
+ //# sourceMappingURL=chunk-OE66B34H.cjs.map
3168
+ //# sourceMappingURL=chunk-OE66B34H.cjs.map