avbridge 2.1.2 → 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.
- package/CHANGELOG.md +93 -0
- package/dist/{avi-GNTV5ZOH.cjs → avi-6SJLWIWW.cjs} +19 -4
- package/dist/avi-6SJLWIWW.cjs.map +1 -0
- package/dist/{avi-V6HYQVR2.js → avi-GCGM7OJI.js} +18 -3
- package/dist/avi-GCGM7OJI.js.map +1 -0
- package/dist/{chunk-EJH67FXG.js → chunk-5DMTJVIU.js} +99 -3
- package/dist/chunk-5DMTJVIU.js.map +1 -0
- package/dist/{chunk-3AUGRKPY.js → chunk-C5VA5U5O.js} +94 -16
- package/dist/chunk-C5VA5U5O.js.map +1 -0
- package/dist/{chunk-JQH6D4OE.cjs → chunk-G4APZMCP.cjs} +100 -3
- package/dist/chunk-G4APZMCP.cjs.map +1 -0
- package/dist/{chunk-Y5FYF5KG.cjs → chunk-HZLQNKFN.cjs} +5 -2
- package/dist/chunk-HZLQNKFN.cjs.map +1 -0
- package/dist/{chunk-PQTZS7OA.js → chunk-ILKDNBSE.js} +5 -2
- package/dist/chunk-ILKDNBSE.js.map +1 -0
- package/dist/{chunk-DPVIOYGC.cjs → chunk-OE66B34H.cjs} +98 -20
- package/dist/chunk-OE66B34H.cjs.map +1 -0
- package/dist/element-browser.js +210 -10
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +4 -4
- package/dist/element.d.cts +1 -1
- package/dist/element.d.ts +1 -1
- package/dist/element.js +3 -3
- package/dist/index.cjs +18 -18
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -5
- package/dist/libav-loader-27RDIN2I.js +3 -0
- package/dist/{libav-loader-XKH2TKUW.js.map → libav-loader-27RDIN2I.js.map} +1 -1
- package/dist/libav-loader-IV4AJ2HW.cjs +12 -0
- package/dist/{libav-loader-6APXVNIV.cjs.map → libav-loader-IV4AJ2HW.cjs.map} +1 -1
- package/dist/{player-BdtUG4rh.d.cts → player-DUyvltvy.d.cts} +3 -3
- package/dist/{player-BdtUG4rh.d.ts → player-DUyvltvy.d.ts} +3 -3
- package/dist/source-CN43EI7Z.cjs +28 -0
- package/dist/{source-SC6ZEQYR.cjs.map → source-CN43EI7Z.cjs.map} +1 -1
- package/dist/source-FFZ7TW2B.js +3 -0
- package/dist/{source-ZFS4H7J3.js.map → source-FFZ7TW2B.js.map} +1 -1
- package/package.json +1 -1
- package/src/classify/rules.ts +9 -2
- package/src/player.ts +22 -1
- package/src/probe/avi.ts +8 -1
- package/src/strategies/fallback/audio-output.ts +25 -3
- package/src/strategies/fallback/decoder.ts +96 -8
- package/src/strategies/fallback/index.ts +90 -6
- package/src/strategies/fallback/libav-loader.ts +12 -0
- package/src/types.ts +10 -1
- package/src/util/debug.ts +131 -0
- package/src/util/source.ts +4 -0
- package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.mjs +1 -1
- package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.wasm +0 -0
- package/dist/avi-GNTV5ZOH.cjs.map +0 -1
- package/dist/avi-V6HYQVR2.js.map +0 -1
- package/dist/chunk-3AUGRKPY.js.map +0 -1
- package/dist/chunk-DPVIOYGC.cjs.map +0 -1
- package/dist/chunk-EJH67FXG.js.map +0 -1
- package/dist/chunk-JQH6D4OE.cjs.map +0 -1
- package/dist/chunk-PQTZS7OA.js.map +0 -1
- package/dist/chunk-Y5FYF5KG.cjs.map +0 -1
- package/dist/libav-loader-6APXVNIV.cjs +0 -12
- package/dist/libav-loader-XKH2TKUW.js +0 -3
- package/dist/source-SC6ZEQYR.cjs +0 -28
- 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-
|
|
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-
|
|
107
|
-
//# sourceMappingURL=chunk-
|
|
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-
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
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-
|
|
147
|
-
//# sourceMappingURL=chunk-
|
|
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
|
|
4
|
-
var
|
|
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,8 +181,8 @@ var MEDIABUNNY_CONTAINERS = /* @__PURE__ */ new Set([
|
|
|
181
181
|
"mpegts"
|
|
182
182
|
]);
|
|
183
183
|
async function probe(source) {
|
|
184
|
-
const normalized = await
|
|
185
|
-
const sniffed = await
|
|
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);
|
|
@@ -192,7 +192,7 @@ async function probe(source) {
|
|
|
192
192
|
mediabunnyErr.message
|
|
193
193
|
);
|
|
194
194
|
try {
|
|
195
|
-
const { probeWithLibav } = await import('./avi-
|
|
195
|
+
const { probeWithLibav } = await import('./avi-6SJLWIWW.cjs');
|
|
196
196
|
return await probeWithLibav(normalized, sniffed);
|
|
197
197
|
} catch (libavErr) {
|
|
198
198
|
const mbMsg = mediabunnyErr.message || String(mediabunnyErr);
|
|
@@ -204,7 +204,7 @@ async function probe(source) {
|
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
try {
|
|
207
|
-
const { probeWithLibav } = await import('./avi-
|
|
207
|
+
const { probeWithLibav } = await import('./avi-6SJLWIWW.cjs');
|
|
208
208
|
return await probeWithLibav(normalized, sniffed);
|
|
209
209
|
} catch (err) {
|
|
210
210
|
const inner = err instanceof Error ? err.message : String(err);
|
|
@@ -290,8 +290,29 @@ var NATIVE_AUDIO_CODECS = /* @__PURE__ */ new Set([
|
|
|
290
290
|
"vorbis",
|
|
291
291
|
"flac"
|
|
292
292
|
]);
|
|
293
|
-
var FALLBACK_VIDEO_CODECS = /* @__PURE__ */ new Set([
|
|
294
|
-
|
|
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
|
+
]);
|
|
295
316
|
var NATIVE_CONTAINERS = /* @__PURE__ */ new Set([
|
|
296
317
|
"mp4",
|
|
297
318
|
"mov",
|
|
@@ -1485,7 +1506,7 @@ var AudioOutput = class {
|
|
|
1485
1506
|
// src/strategies/hybrid/decoder.ts
|
|
1486
1507
|
async function startHybridDecoder(opts) {
|
|
1487
1508
|
const variant = chunkNZU7W256_cjs.pickLibavVariant(opts.context);
|
|
1488
|
-
const libav = await
|
|
1509
|
+
const libav = await chunkG4APZMCP_cjs.loadLibav(variant);
|
|
1489
1510
|
const bridge = await loadBridge();
|
|
1490
1511
|
const { prepareLibavInput } = await import('./libav-http-reader-FPYDBMYK.cjs');
|
|
1491
1512
|
const inputHandle = await prepareLibavInput(libav, opts.filename, opts.source);
|
|
@@ -1925,7 +1946,7 @@ async function loadBridge() {
|
|
|
1925
1946
|
var READY_AUDIO_BUFFER_SECONDS = 0.3;
|
|
1926
1947
|
var READY_TIMEOUT_SECONDS = 10;
|
|
1927
1948
|
async function createHybridSession(ctx, target) {
|
|
1928
|
-
const { normalizeSource: normalizeSource2 } = await import('./source-
|
|
1949
|
+
const { normalizeSource: normalizeSource2 } = await import('./source-CN43EI7Z.cjs');
|
|
1929
1950
|
const source = await normalizeSource2(ctx.source);
|
|
1930
1951
|
const fps = ctx.videoTracks[0]?.fps ?? 30;
|
|
1931
1952
|
const audio = new AudioOutput();
|
|
@@ -2027,7 +2048,7 @@ async function createHybridSession(ctx, target) {
|
|
|
2027
2048
|
// src/strategies/fallback/decoder.ts
|
|
2028
2049
|
async function startDecoder(opts) {
|
|
2029
2050
|
const variant = chunkNZU7W256_cjs.pickLibavVariant(opts.context);
|
|
2030
|
-
const libav = await
|
|
2051
|
+
const libav = await chunkG4APZMCP_cjs.loadLibav(variant);
|
|
2031
2052
|
const bridge = await loadBridge2();
|
|
2032
2053
|
const { prepareLibavInput } = await import('./libav-http-reader-FPYDBMYK.cjs');
|
|
2033
2054
|
const inputHandle = await prepareLibavInput(libav, opts.filename, opts.source);
|
|
@@ -2092,6 +2113,9 @@ async function startDecoder(opts) {
|
|
|
2092
2113
|
let packetsRead = 0;
|
|
2093
2114
|
let videoFramesDecoded = 0;
|
|
2094
2115
|
let audioFramesDecoded = 0;
|
|
2116
|
+
let watchdogFirstFrameMs = 0;
|
|
2117
|
+
let watchdogSlowSinceMs = 0;
|
|
2118
|
+
let watchdogWarned = false;
|
|
2095
2119
|
let syntheticVideoUs = 0;
|
|
2096
2120
|
let syntheticAudioUs = 0;
|
|
2097
2121
|
const videoTrackInfo = opts.context.videoTracks.find((t) => t.id === videoStream?.index);
|
|
@@ -2103,7 +2127,7 @@ async function startDecoder(opts) {
|
|
|
2103
2127
|
let packets;
|
|
2104
2128
|
try {
|
|
2105
2129
|
[readErr, packets] = await libav.ff_read_frame_multi(fmt_ctx, readPkt, {
|
|
2106
|
-
limit:
|
|
2130
|
+
limit: 64 * 1024
|
|
2107
2131
|
});
|
|
2108
2132
|
} catch (err) {
|
|
2109
2133
|
console.error("[avbridge] ff_read_frame_multi failed:", err);
|
|
@@ -2120,6 +2144,28 @@ async function startDecoder(opts) {
|
|
|
2120
2144
|
await decodeAudioBatch(audioPackets, myToken);
|
|
2121
2145
|
}
|
|
2122
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
|
+
}
|
|
2123
2169
|
while (!destroyed && myToken === pumpToken && (opts.audio.bufferAhead() > 2 || opts.renderer.queueDepth() >= opts.renderer.queueHighWater)) {
|
|
2124
2170
|
await new Promise((r) => setTimeout(r, 50));
|
|
2125
2171
|
}
|
|
@@ -2446,10 +2492,10 @@ async function loadBridge2() {
|
|
|
2446
2492
|
}
|
|
2447
2493
|
|
|
2448
2494
|
// src/strategies/fallback/index.ts
|
|
2449
|
-
var READY_AUDIO_BUFFER_SECONDS2 = 0.
|
|
2450
|
-
var READY_TIMEOUT_SECONDS2 =
|
|
2495
|
+
var READY_AUDIO_BUFFER_SECONDS2 = 0.04;
|
|
2496
|
+
var READY_TIMEOUT_SECONDS2 = 3;
|
|
2451
2497
|
async function createFallbackSession(ctx, target) {
|
|
2452
|
-
const { normalizeSource: normalizeSource2 } = await import('./source-
|
|
2498
|
+
const { normalizeSource: normalizeSource2 } = await import('./source-CN43EI7Z.cjs');
|
|
2453
2499
|
const source = await normalizeSource2(ctx.source);
|
|
2454
2500
|
const fps = ctx.videoTracks[0]?.fps ?? 30;
|
|
2455
2501
|
const audio = new AudioOutput();
|
|
@@ -2483,12 +2529,26 @@ async function createFallbackSession(ctx, target) {
|
|
|
2483
2529
|
}
|
|
2484
2530
|
async function waitForBuffer() {
|
|
2485
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
|
+
);
|
|
2486
2536
|
while (true) {
|
|
2487
|
-
const
|
|
2488
|
-
|
|
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
|
+
);
|
|
2489
2545
|
return;
|
|
2490
2546
|
}
|
|
2491
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
|
+
);
|
|
2492
2552
|
return;
|
|
2493
2553
|
}
|
|
2494
2554
|
await new Promise((r) => setTimeout(r, 50));
|
|
@@ -2703,8 +2763,14 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2703
2763
|
return player;
|
|
2704
2764
|
}
|
|
2705
2765
|
async bootstrap() {
|
|
2766
|
+
const bootstrapStart = performance.now();
|
|
2706
2767
|
try {
|
|
2707
|
-
|
|
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
|
+
);
|
|
2708
2774
|
this.diag.recordProbe(ctx);
|
|
2709
2775
|
this.mediaContext = ctx;
|
|
2710
2776
|
if (this.options.subtitles) {
|
|
@@ -2730,6 +2796,10 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2730
2796
|
}
|
|
2731
2797
|
}
|
|
2732
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
|
+
);
|
|
2733
2803
|
this.classification = decision;
|
|
2734
2804
|
this.diag.recordClassification(decision);
|
|
2735
2805
|
this.emitter.emitSticky("strategy", {
|
|
@@ -2755,6 +2825,14 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2755
2825
|
this.startTimeupdateLoop();
|
|
2756
2826
|
this.options.target.addEventListener("ended", () => this.emitter.emit("ended", void 0));
|
|
2757
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
|
+
}
|
|
2758
2836
|
} catch (err) {
|
|
2759
2837
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
2760
2838
|
this.diag.recordError(e);
|
|
@@ -3086,5 +3164,5 @@ exports.classifyContext = classifyContext;
|
|
|
3086
3164
|
exports.createPlayer = createPlayer;
|
|
3087
3165
|
exports.probe = probe;
|
|
3088
3166
|
exports.srtToVtt = srtToVtt;
|
|
3089
|
-
//# sourceMappingURL=chunk-
|
|
3090
|
-
//# sourceMappingURL=chunk-
|
|
3167
|
+
//# sourceMappingURL=chunk-OE66B34H.cjs.map
|
|
3168
|
+
//# sourceMappingURL=chunk-OE66B34H.cjs.map
|