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
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,99 @@ All notable changes to **avbridge** are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project
|
|
5
5
|
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [2.2.0]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **RealMedia playback support** (`.rm`, `.rmvb`). The custom `avbridge`
|
|
12
|
+
libav variant now includes the `rm` demuxer and every RealVideo /
|
|
13
|
+
RealAudio decoder family:
|
|
14
|
+
- Video: `rv10`, `rv20`, `rv30`, `rv40`
|
|
15
|
+
- Audio: `cook`, `ra_144`, `ra_288`, `sipr`, `atrac3`
|
|
16
|
+
These codecs have no browser decoder, so classification routes them
|
|
17
|
+
to the fallback WASM strategy. The custom variant grows by a few
|
|
18
|
+
hundred KB of WASM; the webcodecs variant is unchanged.
|
|
19
|
+
- **Sniff layer recognizes `.RMF` magic bytes** and returns the new
|
|
20
|
+
`"rm"` `ContainerKind`. Probing for a `.rm` or `.rmvb` file goes
|
|
21
|
+
through libav directly (mediabunny doesn't handle RealMedia).
|
|
22
|
+
- **File-picker accept list** in `demo/index.html` and
|
|
23
|
+
`demo/convert.html` now includes `.rm` and `.rmvb`.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **`VideoCodec`** gained `rv10`, `rv20`, `rv30` (existing `rv40` kept).
|
|
28
|
+
- **`AudioCodec`** gained `cook`, `ra_144`, `ra_288`, `sipr`, `atrac3`.
|
|
29
|
+
- **`ContainerKind`** gained `rm`.
|
|
30
|
+
- **Classifier** (`src/classify/rules.ts`) — all new RV/RA codecs added
|
|
31
|
+
to `FALLBACK_VIDEO_CODECS` / `FALLBACK_AUDIO_CODECS`.
|
|
32
|
+
|
|
33
|
+
### Fallback strategy performance tuning
|
|
34
|
+
|
|
35
|
+
These are general-purpose improvements motivated by RealMedia testing
|
|
36
|
+
but also benefit MPEG-4 Part 2 (DivX/Xvid), WMV3, and other
|
|
37
|
+
software-decoded content:
|
|
38
|
+
|
|
39
|
+
- **Cold-start pre-roll gate lowered 300 ms → 40 ms, timeout 10 s → 3 s.**
|
|
40
|
+
The gate used to wait for 300 ms of buffered audio before starting
|
|
41
|
+
playback. On software-decode-bound content (rv40, mpeg4 @ 720p+),
|
|
42
|
+
the decoder produces output slower than realtime, so 300 ms is
|
|
43
|
+
unreachable — the gate would sit out its 10 s timeout before the
|
|
44
|
+
first frame appeared, which the user experienced as a silent 10-
|
|
45
|
+
second hang after clicking Play. The gate now starts on 40 ms
|
|
46
|
+
audio + first frame, and the safety timeout is 3 s. A diagnostic
|
|
47
|
+
warning fires loudly if the timeout is ever hit.
|
|
48
|
+
- **Decoder read batch size raised 16 KB → 64 KB.** Fewer JS↔WASM
|
|
49
|
+
`ff_read_frame_multi` / `ff_decode_multi` round trips per unit of
|
|
50
|
+
video, which measurably speeds up software decode on slow devices.
|
|
51
|
+
Queue burstiness is unchanged because the existing
|
|
52
|
+
`queueHighWater = 30` backpressure still applies.
|
|
53
|
+
|
|
54
|
+
### Debug + self-diagnosis layer
|
|
55
|
+
|
|
56
|
+
New: **`src/util/debug.ts`** — a runtime-toggleable verbose logging
|
|
57
|
+
channel, plus unconditional warnings for suspicious conditions. The
|
|
58
|
+
goal is that subtle issues self-identify in the console instead of
|
|
59
|
+
requiring 10 minutes of reading diagnostics JSON.
|
|
60
|
+
|
|
61
|
+
- Set `globalThis.AVBRIDGE_DEBUG = true` (or append `?avbridge_debug`
|
|
62
|
+
to a demo page URL) to enable verbose logging. Every log is
|
|
63
|
+
prefixed `[avbridge:<tag>]` so you can filter.
|
|
64
|
+
- When debug is **off**, the following conditions still emit an
|
|
65
|
+
unconditional `console.warn`:
|
|
66
|
+
- **`[avbridge:cold-start] gate TIMEOUT…`** — the fallback
|
|
67
|
+
strategy's `waitForBuffer` hit its 3 s timeout with a
|
|
68
|
+
specific underflow (e.g. "audio=0 ms, frames=0"). This used to
|
|
69
|
+
silently hang playback for 10 seconds.
|
|
70
|
+
- **`[avbridge:decode-rate] decoder is running slower than
|
|
71
|
+
realtime…`** — watchdog in the fallback pump loop; fires once
|
|
72
|
+
per stall when framesDecoded/s stays below 60% of source fps
|
|
73
|
+
for ≥5 s after the first frame. Tells you the exact fps
|
|
74
|
+
ratio and names the likely cause.
|
|
75
|
+
- **`[avbridge:bootstrap] total bootstrap time <N>ms — unusually
|
|
76
|
+
slow…`** — bootstrap took >5 s end-to-end.
|
|
77
|
+
- **`[avbridge:probe] probe took <N>ms (>3000ms expected)…`** —
|
|
78
|
+
slow probe (usually a slow Range request or libav cold-start).
|
|
79
|
+
- **`[avbridge:libav-load] load "<variant>" took <N>ms
|
|
80
|
+
(>5000ms expected)…`** — slow WASM download or wrong base
|
|
81
|
+
path.
|
|
82
|
+
|
|
83
|
+
### Known limitations
|
|
84
|
+
|
|
85
|
+
- **rv40 / rv30 at 720p+ may still stutter** on modest CPUs. Single-
|
|
86
|
+
threaded WASM software decode of RealVideo's motion compensation is
|
|
87
|
+
fundamentally slower than realtime on many files. libav.js pthreads
|
|
88
|
+
and a WebGL YUV→RGBA upload path are both plausible follow-ups but
|
|
89
|
+
not in 2.2.0. For reference: a 1024×768 rv40 file plays at roughly
|
|
90
|
+
0.5-2× realtime on an M-series Mac depending on the bitrate. The
|
|
91
|
+
new `[avbridge:decode-rate]` watchdog flags this condition in the
|
|
92
|
+
console so the symptom is never a silent stutter.
|
|
93
|
+
|
|
94
|
+
### Tests
|
|
95
|
+
|
|
96
|
+
- New sniff test for `.RMF` magic bytes (`tests/sniff.test.ts`).
|
|
97
|
+
- Two new classify tests for RealMedia routing (rv40+cook, rv30+ra_288).
|
|
98
|
+
- Test count: 115 → **118**.
|
|
99
|
+
|
|
7
100
|
## [2.1.2]
|
|
8
101
|
|
|
9
102
|
### Fixed
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunkL4NPOJ36_cjs = require('./chunk-L4NPOJ36.cjs');
|
|
4
|
-
var
|
|
4
|
+
var chunkG4APZMCP_cjs = require('./chunk-G4APZMCP.cjs');
|
|
5
5
|
|
|
6
6
|
// src/probe/avi.ts
|
|
7
7
|
async function probeWithLibav(source, sniffed) {
|
|
8
|
-
const libav = await
|
|
8
|
+
const libav = await chunkG4APZMCP_cjs.loadLibav("avbridge");
|
|
9
9
|
const filename = source.name ?? `input.${sniffed === "unknown" ? "bin" : sniffed}`;
|
|
10
10
|
const handle = await chunkL4NPOJ36_cjs.prepareLibavInput(libav, filename, source);
|
|
11
11
|
let fmt_ctx;
|
|
@@ -123,7 +123,12 @@ function ffmpegToAvbridgeVideo(name) {
|
|
|
123
123
|
return "mpeg1";
|
|
124
124
|
case "theora":
|
|
125
125
|
return "theora";
|
|
126
|
+
case "rv10":
|
|
127
|
+
return "rv10";
|
|
128
|
+
case "rv20":
|
|
129
|
+
return "rv20";
|
|
126
130
|
case "rv30":
|
|
131
|
+
return "rv30";
|
|
127
132
|
case "rv40":
|
|
128
133
|
return "rv40";
|
|
129
134
|
default:
|
|
@@ -154,11 +159,21 @@ function ffmpegToAvbridgeAudio(name) {
|
|
|
154
159
|
return "wmapro";
|
|
155
160
|
case "alac":
|
|
156
161
|
return "alac";
|
|
162
|
+
case "cook":
|
|
163
|
+
return "cook";
|
|
164
|
+
case "ra_144":
|
|
165
|
+
return "ra_144";
|
|
166
|
+
case "ra_288":
|
|
167
|
+
return "ra_288";
|
|
168
|
+
case "sipr":
|
|
169
|
+
return "sipr";
|
|
170
|
+
case "atrac3":
|
|
171
|
+
return "atrac3";
|
|
157
172
|
default:
|
|
158
173
|
return name;
|
|
159
174
|
}
|
|
160
175
|
}
|
|
161
176
|
|
|
162
177
|
exports.probeWithLibav = probeWithLibav;
|
|
163
|
-
//# sourceMappingURL=avi-
|
|
164
|
-
//# sourceMappingURL=avi-
|
|
178
|
+
//# sourceMappingURL=avi-6SJLWIWW.cjs.map
|
|
179
|
+
//# sourceMappingURL=avi-6SJLWIWW.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/probe/avi.ts"],"names":["loadLibav","prepareLibavInput"],"mappings":"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","file":"avi-6SJLWIWW.cjs","sourcesContent":["import type {\n AudioCodec,\n AudioTrackInfo,\n ContainerKind,\n MediaContext,\n VideoCodec,\n VideoTrackInfo,\n} from \"../types.js\";\nimport type { NormalizedSource } from \"../util/source.js\";\nimport { prepareLibavInput, type LibavInputHandle } from \"../util/libav-http-reader.js\";\nimport { loadLibav } from \"../strategies/fallback/libav-loader.js\";\n\n/**\n * Probe AVI/ASF/FLV (and any other format mediabunny doesn't speak) via\n * libav.js. This module is `import()`-ed only when sniffing identifies one of\n * those containers.\n *\n * Critical: codec identification goes through `libav.avcodec_get_name(id)`\n * which returns the FFmpeg codec name as a string (e.g. \"h264\", \"mpeg4\",\n * \"wmv3\"). The numeric AV_CODEC_ID_* enum is *not* exposed on the libav\n * instance (only AVMEDIA_TYPE_*, AV_PIX_FMT_*, AV_SAMPLE_FMT_* and a handful\n * of others are), so comparing codec_ids against constants does not work.\n */\nexport async function probeWithLibav(\n source: NormalizedSource,\n sniffed: ContainerKind,\n): Promise<MediaContext> {\n // AVI/ASF/FLV demuxers are not in any libav.js npm variant — they live in\n // the custom \"avbridge\" build produced by `scripts/build-libav.sh`. The loader\n // emits an actionable error if the build hasn't been run yet. Threading\n // is OFF by default in `loadLibav` (see the comment there for why).\n const libav = (await loadLibav(\"avbridge\")) as unknown as LibavInstance;\n\n const filename = source.name ?? `input.${sniffed === \"unknown\" ? \"bin\" : sniffed}`;\n // For Blob/File sources we use libav's in-memory readahead file. For URL\n // sources we attach an HTTP block reader so libav demuxes via Range\n // requests instead of buffering the whole file.\n const handle: LibavInputHandle = await prepareLibavInput(libav as unknown as Parameters<typeof prepareLibavInput>[0], filename, source);\n\n let fmt_ctx: number | undefined;\n let streams: LibavStream[] = [];\n try {\n const result = await libav.ff_init_demuxer_file(filename);\n fmt_ctx = result[0];\n streams = result[1];\n } catch (err) {\n await handle.detach().catch(() => {});\n // Errors thrown across the libav.js worker/pthread boundary aren't\n // always Error instances — they can be plain objects, numbers (errno\n // codes), or strings. Stringify defensively so the user-facing message\n // never has `(undefined)` in it.\n const inner =\n err instanceof Error\n ? err.message\n : typeof err === \"object\" && err !== null\n ? JSON.stringify(err)\n : String(err);\n // eslint-disable-next-line no-console\n console.error(\"[avbridge] ff_init_demuxer_file raw error:\", err);\n throw new Error(\n `libav.js could not demux ${filename}. The current libav variant likely lacks the required demuxer (e.g. AVI). See vendor/libav/README.md for build instructions. (${inner || \"no message — see console for raw error\"})`,\n );\n }\n\n const videoTracks: VideoTrackInfo[] = [];\n const audioTracks: AudioTrackInfo[] = [];\n\n for (const stream of streams) {\n const codecName = (await safe(() => libav.avcodec_get_name(stream.codec_id))) ?? `unknown(${stream.codec_id})`;\n // codecpar holds width/height/channels/sample_rate/profile/level/extradata\n // for the actual stream. We have to copy it out of WASM memory.\n const codecpar = await safe(() => libav.ff_copyout_codecpar(stream.codecpar));\n\n if (stream.codec_type === libav.AVMEDIA_TYPE_VIDEO) {\n videoTracks.push({\n id: stream.index,\n codec: ffmpegToAvbridgeVideo(codecName),\n width: codecpar?.width ?? 0,\n height: codecpar?.height ?? 0,\n fps: framerate(stream),\n });\n } else if (stream.codec_type === libav.AVMEDIA_TYPE_AUDIO) {\n audioTracks.push({\n id: stream.index,\n codec: ffmpegToAvbridgeAudio(codecName),\n channels: codecpar?.channels ?? codecpar?.ch_layout_nb_channels ?? 0,\n sampleRate: codecpar?.sample_rate ?? 0,\n });\n }\n }\n\n // We need this duration but cannot reliably get it from the streams alone\n // for AVI; libav.js exposes it via the AVFormatContext duration helper.\n const duration = await safeDuration(libav, fmt_ctx!);\n\n // Close the demuxer; the strategy will reopen it later if it ends up being\n // chosen. Probing should not pin native resources.\n await libav.avformat_close_input_js(fmt_ctx!).catch(() => {});\n await handle.detach().catch(() => {});\n\n return {\n source: source.original,\n name: source.name,\n byteLength: source.byteLength,\n container: sniffed === \"unknown\" ? \"unknown\" : sniffed,\n videoTracks,\n audioTracks,\n subtitleTracks: [],\n probedBy: \"libav\",\n duration,\n };\n}\n\nfunction framerate(stream: LibavStream): number | undefined {\n if (typeof stream.avg_frame_rate_num === \"number\" && stream.avg_frame_rate_den) {\n return stream.avg_frame_rate_num / stream.avg_frame_rate_den;\n }\n if (stream.avg_frame_rate && typeof stream.avg_frame_rate === \"object\") {\n if (stream.avg_frame_rate.den === 0) return undefined;\n return stream.avg_frame_rate.num / stream.avg_frame_rate.den;\n }\n return undefined;\n}\n\nasync function safeDuration(libav: LibavInstance, fmt_ctx: number): Promise<number | undefined> {\n try {\n // `AVFormatContext.duration` is an int64 in microseconds (AV_TIME_BASE).\n // libav.js exposes it as a split lo/hi pair the same way it does for\n // packet pts — `AVFormatContext_duration(ctx)` returns the low 32 bits,\n // `AVFormatContext_durationhi(ctx)` returns the high 32 bits. Reading\n // only the low half (the previous bug) gave garbage for any file whose\n // duration > ~35 minutes, and zero for shorter files where the value\n // happened to live in the high half.\n const lo = await libav.AVFormatContext_duration?.(fmt_ctx);\n const hi = await libav.AVFormatContext_durationhi?.(fmt_ctx);\n if (typeof lo !== \"number\" || typeof hi !== \"number\") return undefined;\n\n // AV_NOPTS_VALUE = -2^63 → ptshi = -2147483648, pts = 0. Means \"unknown\".\n if (hi === -2147483648 && lo === 0) return undefined;\n\n // Reconstruct the 64-bit value. Prefer libav's helper when available\n // because it correctly handles signed 32-bit two's complement.\n const us =\n typeof libav.i64tof64 === \"function\"\n ? libav.i64tof64(lo, hi)\n : hi * 0x100000000 + lo + (lo < 0 ? 0x100000000 : 0);\n\n if (!Number.isFinite(us) || us <= 0) return undefined;\n return us / 1_000_000;\n } catch {\n return undefined;\n }\n}\n\nasync function safe<T>(fn: () => Promise<T> | T): Promise<T | undefined> {\n try { return await fn(); } catch { return undefined; }\n}\n\n/** Map FFmpeg codec names to avbridge video codec identifiers. */\nfunction ffmpegToAvbridgeVideo(name: string): VideoCodec {\n switch (name) {\n case \"h264\": return \"h264\";\n case \"hevc\": return \"h265\";\n case \"vp8\": return \"vp8\";\n case \"vp9\": return \"vp9\";\n case \"av1\": return \"av1\";\n case \"mpeg4\": return \"mpeg4\"; // MPEG-4 Part 2 / DivX / Xvid\n case \"msmpeg4v1\":\n case \"msmpeg4v2\":\n case \"msmpeg4v3\": // a.k.a. DIV3\n return \"mpeg4\";\n case \"wmv1\":\n case \"wmv2\":\n case \"wmv3\":\n return \"wmv3\";\n case \"vc1\": return \"vc1\";\n case \"mpeg2video\": return \"mpeg2\";\n case \"mpeg1video\": return \"mpeg1\";\n case \"theora\": return \"theora\";\n case \"rv10\": return \"rv10\";\n case \"rv20\": return \"rv20\";\n case \"rv30\": return \"rv30\";\n case \"rv40\": return \"rv40\";\n default: return name as VideoCodec;\n }\n}\n\nfunction ffmpegToAvbridgeAudio(name: string): AudioCodec {\n switch (name) {\n case \"aac\": return \"aac\";\n case \"mp3\":\n case \"mp3float\":\n return \"mp3\";\n case \"opus\": return \"opus\";\n case \"vorbis\": return \"vorbis\";\n case \"flac\": return \"flac\";\n case \"ac3\": return \"ac3\";\n case \"eac3\": return \"eac3\";\n case \"wmav1\":\n case \"wmav2\": return \"wmav2\";\n case \"wmapro\": return \"wmapro\";\n case \"alac\": return \"alac\";\n case \"cook\": return \"cook\";\n case \"ra_144\": return \"ra_144\";\n case \"ra_288\": return \"ra_288\";\n case \"sipr\": return \"sipr\";\n case \"atrac3\": return \"atrac3\";\n default: return name as AudioCodec;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Minimal structural types for the slice of libav.js we touch.\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface LibavStream {\n index: number;\n codec_type: number;\n codec_id: number;\n codecpar: number;\n avg_frame_rate?: { num: number; den: number };\n avg_frame_rate_num?: number;\n avg_frame_rate_den?: number;\n}\n\ninterface LibavCodecpar {\n width?: number;\n height?: number;\n channels?: number;\n ch_layout_nb_channels?: number;\n sample_rate?: number;\n profile?: number;\n level?: number;\n}\n\ninterface LibavInstance {\n mkreadaheadfile(name: string, blob: Blob): Promise<void>;\n unlinkreadaheadfile(name: string): Promise<void>;\n ff_init_demuxer_file(name: string): Promise<[number, LibavStream[]]>;\n ff_copyout_codecpar(codecpar: number): Promise<LibavCodecpar>;\n avcodec_get_name(codec_id: number): Promise<string>;\n avformat_close_input_js(ctx: number): Promise<void>;\n AVFormatContext_duration?(ctx: number): Promise<number>;\n AVFormatContext_durationhi?(ctx: number): Promise<number>;\n i64tof64?(lo: number, hi: number): number;\n\n AVMEDIA_TYPE_VIDEO: number;\n AVMEDIA_TYPE_AUDIO: number;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { prepareLibavInput } from './chunk-WD2ZNQA7.js';
|
|
2
|
-
import { loadLibav } from './chunk-
|
|
2
|
+
import { loadLibav } from './chunk-5DMTJVIU.js';
|
|
3
3
|
|
|
4
4
|
// src/probe/avi.ts
|
|
5
5
|
async function probeWithLibav(source, sniffed) {
|
|
@@ -121,7 +121,12 @@ function ffmpegToAvbridgeVideo(name) {
|
|
|
121
121
|
return "mpeg1";
|
|
122
122
|
case "theora":
|
|
123
123
|
return "theora";
|
|
124
|
+
case "rv10":
|
|
125
|
+
return "rv10";
|
|
126
|
+
case "rv20":
|
|
127
|
+
return "rv20";
|
|
124
128
|
case "rv30":
|
|
129
|
+
return "rv30";
|
|
125
130
|
case "rv40":
|
|
126
131
|
return "rv40";
|
|
127
132
|
default:
|
|
@@ -152,11 +157,21 @@ function ffmpegToAvbridgeAudio(name) {
|
|
|
152
157
|
return "wmapro";
|
|
153
158
|
case "alac":
|
|
154
159
|
return "alac";
|
|
160
|
+
case "cook":
|
|
161
|
+
return "cook";
|
|
162
|
+
case "ra_144":
|
|
163
|
+
return "ra_144";
|
|
164
|
+
case "ra_288":
|
|
165
|
+
return "ra_288";
|
|
166
|
+
case "sipr":
|
|
167
|
+
return "sipr";
|
|
168
|
+
case "atrac3":
|
|
169
|
+
return "atrac3";
|
|
155
170
|
default:
|
|
156
171
|
return name;
|
|
157
172
|
}
|
|
158
173
|
}
|
|
159
174
|
|
|
160
175
|
export { probeWithLibav };
|
|
161
|
-
//# sourceMappingURL=avi-
|
|
162
|
-
//# sourceMappingURL=avi-
|
|
176
|
+
//# sourceMappingURL=avi-GCGM7OJI.js.map
|
|
177
|
+
//# sourceMappingURL=avi-GCGM7OJI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/probe/avi.ts"],"names":[],"mappings":"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","file":"avi-GCGM7OJI.js","sourcesContent":["import type {\n AudioCodec,\n AudioTrackInfo,\n ContainerKind,\n MediaContext,\n VideoCodec,\n VideoTrackInfo,\n} from \"../types.js\";\nimport type { NormalizedSource } from \"../util/source.js\";\nimport { prepareLibavInput, type LibavInputHandle } from \"../util/libav-http-reader.js\";\nimport { loadLibav } from \"../strategies/fallback/libav-loader.js\";\n\n/**\n * Probe AVI/ASF/FLV (and any other format mediabunny doesn't speak) via\n * libav.js. This module is `import()`-ed only when sniffing identifies one of\n * those containers.\n *\n * Critical: codec identification goes through `libav.avcodec_get_name(id)`\n * which returns the FFmpeg codec name as a string (e.g. \"h264\", \"mpeg4\",\n * \"wmv3\"). The numeric AV_CODEC_ID_* enum is *not* exposed on the libav\n * instance (only AVMEDIA_TYPE_*, AV_PIX_FMT_*, AV_SAMPLE_FMT_* and a handful\n * of others are), so comparing codec_ids against constants does not work.\n */\nexport async function probeWithLibav(\n source: NormalizedSource,\n sniffed: ContainerKind,\n): Promise<MediaContext> {\n // AVI/ASF/FLV demuxers are not in any libav.js npm variant — they live in\n // the custom \"avbridge\" build produced by `scripts/build-libav.sh`. The loader\n // emits an actionable error if the build hasn't been run yet. Threading\n // is OFF by default in `loadLibav` (see the comment there for why).\n const libav = (await loadLibav(\"avbridge\")) as unknown as LibavInstance;\n\n const filename = source.name ?? `input.${sniffed === \"unknown\" ? \"bin\" : sniffed}`;\n // For Blob/File sources we use libav's in-memory readahead file. For URL\n // sources we attach an HTTP block reader so libav demuxes via Range\n // requests instead of buffering the whole file.\n const handle: LibavInputHandle = await prepareLibavInput(libav as unknown as Parameters<typeof prepareLibavInput>[0], filename, source);\n\n let fmt_ctx: number | undefined;\n let streams: LibavStream[] = [];\n try {\n const result = await libav.ff_init_demuxer_file(filename);\n fmt_ctx = result[0];\n streams = result[1];\n } catch (err) {\n await handle.detach().catch(() => {});\n // Errors thrown across the libav.js worker/pthread boundary aren't\n // always Error instances — they can be plain objects, numbers (errno\n // codes), or strings. Stringify defensively so the user-facing message\n // never has `(undefined)` in it.\n const inner =\n err instanceof Error\n ? err.message\n : typeof err === \"object\" && err !== null\n ? JSON.stringify(err)\n : String(err);\n // eslint-disable-next-line no-console\n console.error(\"[avbridge] ff_init_demuxer_file raw error:\", err);\n throw new Error(\n `libav.js could not demux ${filename}. The current libav variant likely lacks the required demuxer (e.g. AVI). See vendor/libav/README.md for build instructions. (${inner || \"no message — see console for raw error\"})`,\n );\n }\n\n const videoTracks: VideoTrackInfo[] = [];\n const audioTracks: AudioTrackInfo[] = [];\n\n for (const stream of streams) {\n const codecName = (await safe(() => libav.avcodec_get_name(stream.codec_id))) ?? `unknown(${stream.codec_id})`;\n // codecpar holds width/height/channels/sample_rate/profile/level/extradata\n // for the actual stream. We have to copy it out of WASM memory.\n const codecpar = await safe(() => libav.ff_copyout_codecpar(stream.codecpar));\n\n if (stream.codec_type === libav.AVMEDIA_TYPE_VIDEO) {\n videoTracks.push({\n id: stream.index,\n codec: ffmpegToAvbridgeVideo(codecName),\n width: codecpar?.width ?? 0,\n height: codecpar?.height ?? 0,\n fps: framerate(stream),\n });\n } else if (stream.codec_type === libav.AVMEDIA_TYPE_AUDIO) {\n audioTracks.push({\n id: stream.index,\n codec: ffmpegToAvbridgeAudio(codecName),\n channels: codecpar?.channels ?? codecpar?.ch_layout_nb_channels ?? 0,\n sampleRate: codecpar?.sample_rate ?? 0,\n });\n }\n }\n\n // We need this duration but cannot reliably get it from the streams alone\n // for AVI; libav.js exposes it via the AVFormatContext duration helper.\n const duration = await safeDuration(libav, fmt_ctx!);\n\n // Close the demuxer; the strategy will reopen it later if it ends up being\n // chosen. Probing should not pin native resources.\n await libav.avformat_close_input_js(fmt_ctx!).catch(() => {});\n await handle.detach().catch(() => {});\n\n return {\n source: source.original,\n name: source.name,\n byteLength: source.byteLength,\n container: sniffed === \"unknown\" ? \"unknown\" : sniffed,\n videoTracks,\n audioTracks,\n subtitleTracks: [],\n probedBy: \"libav\",\n duration,\n };\n}\n\nfunction framerate(stream: LibavStream): number | undefined {\n if (typeof stream.avg_frame_rate_num === \"number\" && stream.avg_frame_rate_den) {\n return stream.avg_frame_rate_num / stream.avg_frame_rate_den;\n }\n if (stream.avg_frame_rate && typeof stream.avg_frame_rate === \"object\") {\n if (stream.avg_frame_rate.den === 0) return undefined;\n return stream.avg_frame_rate.num / stream.avg_frame_rate.den;\n }\n return undefined;\n}\n\nasync function safeDuration(libav: LibavInstance, fmt_ctx: number): Promise<number | undefined> {\n try {\n // `AVFormatContext.duration` is an int64 in microseconds (AV_TIME_BASE).\n // libav.js exposes it as a split lo/hi pair the same way it does for\n // packet pts — `AVFormatContext_duration(ctx)` returns the low 32 bits,\n // `AVFormatContext_durationhi(ctx)` returns the high 32 bits. Reading\n // only the low half (the previous bug) gave garbage for any file whose\n // duration > ~35 minutes, and zero for shorter files where the value\n // happened to live in the high half.\n const lo = await libav.AVFormatContext_duration?.(fmt_ctx);\n const hi = await libav.AVFormatContext_durationhi?.(fmt_ctx);\n if (typeof lo !== \"number\" || typeof hi !== \"number\") return undefined;\n\n // AV_NOPTS_VALUE = -2^63 → ptshi = -2147483648, pts = 0. Means \"unknown\".\n if (hi === -2147483648 && lo === 0) return undefined;\n\n // Reconstruct the 64-bit value. Prefer libav's helper when available\n // because it correctly handles signed 32-bit two's complement.\n const us =\n typeof libav.i64tof64 === \"function\"\n ? libav.i64tof64(lo, hi)\n : hi * 0x100000000 + lo + (lo < 0 ? 0x100000000 : 0);\n\n if (!Number.isFinite(us) || us <= 0) return undefined;\n return us / 1_000_000;\n } catch {\n return undefined;\n }\n}\n\nasync function safe<T>(fn: () => Promise<T> | T): Promise<T | undefined> {\n try { return await fn(); } catch { return undefined; }\n}\n\n/** Map FFmpeg codec names to avbridge video codec identifiers. */\nfunction ffmpegToAvbridgeVideo(name: string): VideoCodec {\n switch (name) {\n case \"h264\": return \"h264\";\n case \"hevc\": return \"h265\";\n case \"vp8\": return \"vp8\";\n case \"vp9\": return \"vp9\";\n case \"av1\": return \"av1\";\n case \"mpeg4\": return \"mpeg4\"; // MPEG-4 Part 2 / DivX / Xvid\n case \"msmpeg4v1\":\n case \"msmpeg4v2\":\n case \"msmpeg4v3\": // a.k.a. DIV3\n return \"mpeg4\";\n case \"wmv1\":\n case \"wmv2\":\n case \"wmv3\":\n return \"wmv3\";\n case \"vc1\": return \"vc1\";\n case \"mpeg2video\": return \"mpeg2\";\n case \"mpeg1video\": return \"mpeg1\";\n case \"theora\": return \"theora\";\n case \"rv10\": return \"rv10\";\n case \"rv20\": return \"rv20\";\n case \"rv30\": return \"rv30\";\n case \"rv40\": return \"rv40\";\n default: return name as VideoCodec;\n }\n}\n\nfunction ffmpegToAvbridgeAudio(name: string): AudioCodec {\n switch (name) {\n case \"aac\": return \"aac\";\n case \"mp3\":\n case \"mp3float\":\n return \"mp3\";\n case \"opus\": return \"opus\";\n case \"vorbis\": return \"vorbis\";\n case \"flac\": return \"flac\";\n case \"ac3\": return \"ac3\";\n case \"eac3\": return \"eac3\";\n case \"wmav1\":\n case \"wmav2\": return \"wmav2\";\n case \"wmapro\": return \"wmapro\";\n case \"alac\": return \"alac\";\n case \"cook\": return \"cook\";\n case \"ra_144\": return \"ra_144\";\n case \"ra_288\": return \"ra_288\";\n case \"sipr\": return \"sipr\";\n case \"atrac3\": return \"atrac3\";\n default: return name as AudioCodec;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Minimal structural types for the slice of libav.js we touch.\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface LibavStream {\n index: number;\n codec_type: number;\n codec_id: number;\n codecpar: number;\n avg_frame_rate?: { num: number; den: number };\n avg_frame_rate_num?: number;\n avg_frame_rate_den?: number;\n}\n\ninterface LibavCodecpar {\n width?: number;\n height?: number;\n channels?: number;\n ch_layout_nb_channels?: number;\n sample_rate?: number;\n profile?: number;\n level?: number;\n}\n\ninterface LibavInstance {\n mkreadaheadfile(name: string, blob: Blob): Promise<void>;\n unlinkreadaheadfile(name: string): Promise<void>;\n ff_init_demuxer_file(name: string): Promise<[number, LibavStream[]]>;\n ff_copyout_codecpar(codecpar: number): Promise<LibavCodecpar>;\n avcodec_get_name(codec_id: number): Promise<string>;\n avformat_close_input_js(ctx: number): Promise<void>;\n AVFormatContext_duration?(ctx: number): Promise<number>;\n AVFormatContext_durationhi?(ctx: number): Promise<number>;\n i64tof64?(lo: number, hi: number): number;\n\n AVMEDIA_TYPE_VIDEO: number;\n AVMEDIA_TYPE_AUDIO: number;\n}\n"]}
|
|
@@ -1,3 +1,90 @@
|
|
|
1
|
+
// src/util/debug.ts
|
|
2
|
+
function isDebugEnabled() {
|
|
3
|
+
if (typeof globalThis === "undefined") return false;
|
|
4
|
+
const g = globalThis;
|
|
5
|
+
if (g.AVBRIDGE_DEBUG === true) return true;
|
|
6
|
+
if (typeof location !== "undefined" && typeof URLSearchParams !== "undefined") {
|
|
7
|
+
try {
|
|
8
|
+
const p = new URLSearchParams(location.search);
|
|
9
|
+
if (p.has("avbridge_debug")) {
|
|
10
|
+
g.AVBRIDGE_DEBUG = true;
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
} catch {
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
function fmt(tag) {
|
|
19
|
+
return `[avbridge:${tag}]`;
|
|
20
|
+
}
|
|
21
|
+
var dbg = {
|
|
22
|
+
/** Verbose — only when debug is enabled. The hot-path normal case. */
|
|
23
|
+
info(tag, ...args) {
|
|
24
|
+
if (isDebugEnabled()) console.info(fmt(tag), ...args);
|
|
25
|
+
},
|
|
26
|
+
/** Warning — only when debug is enabled. Non-fatal oddities. */
|
|
27
|
+
warn(tag, ...args) {
|
|
28
|
+
if (isDebugEnabled()) console.warn(fmt(tag), ...args);
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* Self-diagnosis warning. **Always** emits regardless of debug flag.
|
|
32
|
+
* Use this only for conditions that mean something is actually wrong
|
|
33
|
+
* or degraded — not for routine chatter.
|
|
34
|
+
*/
|
|
35
|
+
diag(tag, ...args) {
|
|
36
|
+
console.warn(fmt(tag), ...args);
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* Timing helper: wraps an async call and logs its elapsed time when
|
|
40
|
+
* debug is on. The callback runs whether debug is on or off — this is
|
|
41
|
+
* just for the `dbg.info` at the end.
|
|
42
|
+
*
|
|
43
|
+
* Also unconditionally fires `dbg.diag` if the elapsed time exceeds
|
|
44
|
+
* `slowMs`, so "the bootstrap took 8 seconds" shows up even without
|
|
45
|
+
* debug mode enabled.
|
|
46
|
+
*/
|
|
47
|
+
async timed(tag, label, slowMs, fn) {
|
|
48
|
+
const start = performance.now();
|
|
49
|
+
try {
|
|
50
|
+
const result = await fn();
|
|
51
|
+
const elapsed = performance.now() - start;
|
|
52
|
+
if (isDebugEnabled()) {
|
|
53
|
+
console.info(fmt(tag), `${label} ${elapsed.toFixed(0)}ms`);
|
|
54
|
+
}
|
|
55
|
+
if (elapsed > slowMs) {
|
|
56
|
+
console.warn(
|
|
57
|
+
fmt(tag),
|
|
58
|
+
`${label} took ${elapsed.toFixed(0)}ms (>${slowMs}ms expected) \u2014 this is unusually slow; possible causes: ${hintForTag(tag)}`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const elapsed = performance.now() - start;
|
|
64
|
+
console.warn(
|
|
65
|
+
fmt(tag),
|
|
66
|
+
`${label} FAILED after ${elapsed.toFixed(0)}ms:`,
|
|
67
|
+
err
|
|
68
|
+
);
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
function hintForTag(tag) {
|
|
74
|
+
switch (tag) {
|
|
75
|
+
case "probe":
|
|
76
|
+
return "slow network (range request), large sniff window, or libav cold-start";
|
|
77
|
+
case "libav-load":
|
|
78
|
+
return "large .wasm download, misconfigured AVBRIDGE_LIBAV_BASE, or server-side MIME type";
|
|
79
|
+
case "bootstrap":
|
|
80
|
+
return "probe+classify+strategy-init chain; enable AVBRIDGE_DEBUG for a phase breakdown";
|
|
81
|
+
case "cold-start":
|
|
82
|
+
return "decoder is producing output slower than realtime \u2014 check framesDecoded in getDiagnostics()";
|
|
83
|
+
default:
|
|
84
|
+
return "unknown stage \u2014 enable globalThis.AVBRIDGE_DEBUG for more detail";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
1
88
|
// src/strategies/fallback/libav-loader.ts
|
|
2
89
|
var cache = /* @__PURE__ */ new Map();
|
|
3
90
|
function cacheKey(variant, threads) {
|
|
@@ -15,9 +102,18 @@ function loadLibav(variant = "webcodecs", opts = {}) {
|
|
|
15
102
|
return entry;
|
|
16
103
|
}
|
|
17
104
|
async function loadVariant(variant, wantThreads) {
|
|
105
|
+
return dbg.timed(
|
|
106
|
+
"libav-load",
|
|
107
|
+
`load "${variant}" (threads=${wantThreads})`,
|
|
108
|
+
5e3,
|
|
109
|
+
() => loadVariantInner(variant, wantThreads)
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
async function loadVariantInner(variant, wantThreads) {
|
|
18
113
|
const key = cacheKey(variant, wantThreads);
|
|
19
114
|
const base = `${libavBaseUrl()}/${variant}`;
|
|
20
115
|
const variantUrl = `${base}/libav-${variant}.mjs`;
|
|
116
|
+
dbg.info("libav-load", `fetching ${variantUrl}`);
|
|
21
117
|
if (typeof fetch === "function") {
|
|
22
118
|
try {
|
|
23
119
|
const head = await fetch(variantUrl, { method: "GET", headers: { Range: "bytes=0-0" } });
|
|
@@ -99,6 +195,6 @@ function chain(message, err) {
|
|
|
99
195
|
return new Error(`${message}: ${inner || "(no message \u2014 see browser console)"}`);
|
|
100
196
|
}
|
|
101
197
|
|
|
102
|
-
export { loadLibav };
|
|
103
|
-
//# sourceMappingURL=chunk-
|
|
104
|
-
//# sourceMappingURL=chunk-
|
|
198
|
+
export { dbg, loadLibav };
|
|
199
|
+
//# sourceMappingURL=chunk-5DMTJVIU.js.map
|
|
200
|
+
//# sourceMappingURL=chunk-5DMTJVIU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/util/debug.ts","../src/strategies/fallback/libav-loader.ts"],"names":[],"mappings":";AA8BA,SAAS,cAAA,GAA0B;AACjC,EAAA,IAAI,OAAO,UAAA,KAAe,WAAA,EAAa,OAAO,KAAA;AAC9C,EAAA,MAAM,CAAA,GAAI,UAAA;AACV,EAAA,IAAI,CAAA,CAAE,cAAA,KAAmB,IAAA,EAAM,OAAO,IAAA;AAItC,EAAA,IAAI,OAAO,QAAA,KAAa,WAAA,IAAe,OAAO,oBAAoB,WAAA,EAAa;AAC7E,IAAA,IAAI;AACF,MAAA,MAAM,CAAA,GAAI,IAAI,eAAA,CAAgB,QAAA,CAAS,MAAM,CAAA;AAC7C,MAAA,IAAI,CAAA,CAAE,GAAA,CAAI,gBAAgB,CAAA,EAAG;AAC3B,QAAA,CAAA,CAAE,cAAA,GAAiB,IAAA;AACnB,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAAe;AAAA,EACzB;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,IAAI,GAAA,EAAqB;AAChC,EAAA,OAAO,aAAa,GAAG,CAAA,CAAA,CAAA;AACzB;AAIO,IAAM,GAAA,GAAM;AAAA;AAAA,EAEjB,IAAA,CAAK,QAAgB,IAAA,EAAuB;AAC1C,IAAA,IAAI,cAAA,IAAkB,OAAA,CAAQ,IAAA,CAAK,IAAI,GAAG,CAAA,EAAG,GAAG,IAAI,CAAA;AAAA,EACtD,CAAA;AAAA;AAAA,EAGA,IAAA,CAAK,QAAgB,IAAA,EAAuB;AAC1C,IAAA,IAAI,cAAA,IAAkB,OAAA,CAAQ,IAAA,CAAK,IAAI,GAAG,CAAA,EAAG,GAAG,IAAI,CAAA;AAAA,EACtD,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAA,CAAK,QAAgB,IAAA,EAAuB;AAC1C,IAAA,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,EAAG,GAAG,IAAI,CAAA;AAAA,EAChC,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,KAAA,CACJ,GAAA,EACA,KAAA,EACA,QACA,EAAA,EACY;AACZ,IAAA,MAAM,KAAA,GAAQ,YAAY,GAAA,EAAI;AAC9B,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,EAAA,EAAG;AACxB,MAAA,MAAM,OAAA,GAAU,WAAA,CAAY,GAAA,EAAI,GAAI,KAAA;AACpC,MAAA,IAAI,gBAAe,EAAG;AACpB,QAAA,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,EAAG,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAC,CAAA,EAAA,CAAI,CAAA;AAAA,MAC3D;AACA,MAAA,IAAI,UAAU,MAAA,EAAQ;AACpB,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,IAAI,GAAG,CAAA;AAAA,UACP,CAAA,EAAG,KAAK,CAAA,MAAA,EAAS,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAC,CAAA,KAAA,EAAQ,MAAM,CAAA,6DAAA,EACL,UAAA,CAAW,GAAG,CAAC,CAAA;AAAA,SAC7D;AAAA,MACF;AACA,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,GAAA,EAAK;AACZ,MAAA,MAAM,OAAA,GAAU,WAAA,CAAY,GAAA,EAAI,GAAI,KAAA;AACpC,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN,IAAI,GAAG,CAAA;AAAA,QACP,GAAG,KAAK,CAAA,cAAA,EAAiB,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAC,CAAA,GAAA,CAAA;AAAA,QAC3C;AAAA,OACF;AACA,MAAA,MAAM,GAAA;AAAA,IACR;AAAA,EACF;AACF;AAEA,SAAS,WAAW,GAAA,EAAqB;AACvC,EAAA,QAAQ,GAAA;AAAK,IACX,KAAK,OAAA;AACH,MAAA,OAAO,uEAAA;AAAA,IACT,KAAK,YAAA;AACH,MAAA,OAAO,mFAAA;AAAA,IACT,KAAK,WAAA;AACH,MAAA,OAAO,iFAAA;AAAA,IACT,KAAK,YAAA;AACH,MAAA,OAAO,iGAAA;AAAA,IACT;AACE,MAAA,OAAO,uEAAA;AAAA;AAEb;;;ACzFA,IAAM,KAAA,uBAAiD,GAAA,EAAI;AAE3D,SAAS,QAAA,CAAS,SAAuB,OAAA,EAA0B;AACjE,EAAA,OAAO,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,OAAA,GAAU,QAAQ,MAAM,CAAA,CAAA;AAC/C;AAOO,SAAS,SAAA,CACd,OAAA,GAAwB,WAAA,EACxB,IAAA,GAAyB,EAAC,EACF;AAgBxB,EAAA,MAAM,GAAA,GAAM,UAAA;AACZ,EAAA,MAAM,cACJ,IAAA,CAAK,OAAA,KAAY,SACb,IAAA,CAAK,OAAA,GACL,IAAI,sBAAA,KAA2B,IAAA;AAErC,EAAA,MAAM,GAAA,GAAM,QAAA,CAAS,OAAA,EAAS,WAAW,CAAA;AACzC,EAAA,IAAI,KAAA,GAAQ,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AACzB,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,KAAA,GAAQ,WAAA,CAAY,SAAS,WAAW,CAAA;AACxC,IAAA,KAAA,CAAM,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,EACtB;AACA,EAAA,OAAO,KAAA;AACT;AAEA,eAAe,WAAA,CACb,SACA,WAAA,EACwB;AACxB,EAAA,OAAO,GAAA,CAAI,KAAA;AAAA,IAAM,YAAA;AAAA,IAAc,CAAA,MAAA,EAAS,OAAO,CAAA,WAAA,EAAc,WAAW,CAAA,CAAA,CAAA;AAAA,IAAK,GAAA;AAAA,IAAM,MACjF,gBAAA,CAAiB,OAAA,EAAS,WAAW;AAAA,GACvC;AACF;AAEA,eAAe,gBAAA,CACb,SACA,WAAA,EACwB;AACxB,EAAA,MAAM,GAAA,GAAM,QAAA,CAAS,OAAA,EAAS,WAAW,CAAA;AACzC,EAAA,MAAM,IAAA,GAAO,CAAA,EAAG,YAAA,EAAc,IAAI,OAAO,CAAA,CAAA;AAGzC,EAAA,MAAM,UAAA,GAAa,CAAA,EAAG,IAAI,CAAA,OAAA,EAAU,OAAO,CAAA,IAAA,CAAA;AAC3C,EAAA,GAAA,CAAI,IAAA,CAAK,YAAA,EAAc,CAAA,SAAA,EAAY,UAAU,CAAA,CAAE,CAAA;AAO/C,EAAA,IAAI,OAAO,UAAU,UAAA,EAAY;AAC/B,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,UAAA,EAAY,EAAE,MAAA,EAAQ,KAAA,EAAO,OAAA,EAAS,EAAE,KAAA,EAAO,WAAA,EAAY,EAAG,CAAA;AACvF,MAAA,IAAI,CAAC,IAAA,CAAK,EAAA,IAAM,IAAA,CAAK,WAAW,GAAA,EAAK;AACnC,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,QAAQ,IAAA,CAAK,MAAM,IAAI,IAAA,CAAK,UAAU,gDAChC,IAAI,CAAA,+CAAA;AAAA,SACZ;AAAA,MACF;AAEA,MAAA,IAAI;AAAE,QAAA,MAAM,KAAK,WAAA,EAAY;AAAA,MAAG,CAAA,CAAA,MAAQ;AAAA,MAAe;AAAA,IACzD,SAAS,GAAA,EAAK;AACZ,MAAA,KAAA,CAAM,OAAO,GAAG,CAAA;AAChB,MAAA,MAAM,KAAA;AAAA,QACJ,CAAA,UAAA,EAAa,OAAO,CAAA,2BAAA,EAA8B,UAAU,CAAA,CAAA;AAAA,QAC5D;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AAEF,IAAA,MAAM,WAAoB,MAAM;AAAA;AAAA,MAA0B;AAAA,KAAA;AAC1D,IAAA,IAAI,CAAC,QAAA,IAAY,OAAQ,QAAA,CAAiC,UAAU,UAAA,EAAY;AAC9E,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,UAAA,EAAa,UAAU,CAAA,qBAAA,CAAuB,CAAA;AAAA,IAChE;AACA,IAAA,GAAA,GAAM,QAAA;AAAA,EACR,SAAS,GAAA,EAAK;AACZ,IAAA,KAAA,CAAM,OAAO,GAAG,CAAA;AAChB,IAAA,MAAM,IAAA,GACJ,OAAA,KAAY,UAAA,GACR,CAAA,gJAAA,CAAA,GAEA,8CAA8C,IAAI,CAAA,2EAAA,CAAA;AAExD,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,yBAAA,EAA4B,OAAO,CAAA,eAAA,EAAkB,UAAU,CAAA,EAAA,EAAK,IAAI,CAAA,iBAAA,EAClD,GAAA,CAAc,OAAA,IAAW,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,KAC5D;AAAA,EACF;AAEA,EAAA,IAAI;AACF,IAAA,MAAM,OAAQ,MAAM,GAAA,CAAI,MAAM,SAAA,CAAU,IAAA,EAAM,WAAW,CAAC,CAAA;AAC1D,IAAA,MAAM,iBAAiB,IAAI,CAAA;AAC3B,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,GAAA,EAAK;AACZ,IAAA,KAAA,CAAM,OAAO,GAAG,CAAA;AAChB,IAAA,MAAM,MAAM,CAAA,4BAAA,EAA+B,OAAO,CAAA,mBAAA,EAAsB,WAAW,KAAK,GAAG,CAAA;AAAA,EAC7F;AACF;AAYA,eAAe,iBAAiB,IAAA,EAAoC;AAClE,EAAA,IAAI;AACF,IAAA,MAAM,WAAY,IAAA,CACf,gBAAA;AACH,IAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,MAAA,MAAM,KAAA,GAAS,KAAmC,YAAA,IAAgB,CAAA,CAAA;AAClE,MAAA,MAAM,SAAS,KAAK,CAAA;AAAA,IACtB;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAEA,SAAS,SAAA,CAAU,MAAc,WAAA,EAA+C;AAK9E,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,WAAW,CAAC,WAAA;AAAA,IACZ,UAAA,EAAY;AAAA,GACd;AACF;AAEA,SAAS,YAAA,GAAuB;AAK9B,EAAA,MAAM,QAAA,GACJ,OAAO,UAAA,KAAe,WAAA,GACjB,WAAgD,mBAAA,GACjD,MAAA;AACN,EAAA,IAAI,UAAU,OAAO,QAAA;AAWrB,EAAA,IAAI;AACF,IAAA,OAAO,IAAI,GAAA,CAAI,iBAAA,EAAmB,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA,CAAE,IAAA;AAAA,EACrD,CAAA,CAAA,MAAQ;AACN,IAAA,IAAI,OAAO,QAAA,KAAa,WAAA,IAAe,SAAS,QAAA,CAAS,UAAA,CAAW,MAAM,CAAA,EAAG;AAC3E,MAAA,OAAO,CAAA,EAAG,SAAS,MAAM,CAAA,MAAA,CAAA;AAAA,IAC3B;AACA,IAAA,OAAO,QAAA;AAAA,EACT;AACF;AAEA,SAAS,KAAA,CAAM,SAAiB,GAAA,EAAqB;AACnD,EAAA,MAAM,QAAQ,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAE7D,EAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,WAAA,EAAc,OAAO,CAAA,CAAA,CAAA,EAAK,GAAG,CAAA;AAC3C,EAAA,OAAO,IAAI,KAAA,CAAM,CAAA,EAAG,OAAO,CAAA,EAAA,EAAK,KAAA,IAAS,yCAAoC,CAAA,CAAE,CAAA;AACjF","file":"chunk-5DMTJVIU.js","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"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { normalizeSource, sniffNormalizedSource } from './chunk-
|
|
2
|
-
import { loadLibav } from './chunk-
|
|
1
|
+
import { normalizeSource, sniffNormalizedSource } from './chunk-ILKDNBSE.js';
|
|
2
|
+
import { dbg, loadLibav } from './chunk-5DMTJVIU.js';
|
|
3
3
|
import { pickLibavVariant } from './chunk-J5MCMN3S.js';
|
|
4
4
|
|
|
5
5
|
// src/probe/mediabunny.ts
|
|
@@ -190,7 +190,7 @@ async function probe(source) {
|
|
|
190
190
|
mediabunnyErr.message
|
|
191
191
|
);
|
|
192
192
|
try {
|
|
193
|
-
const { probeWithLibav } = await import('./avi-
|
|
193
|
+
const { probeWithLibav } = await import('./avi-GCGM7OJI.js');
|
|
194
194
|
return await probeWithLibav(normalized, sniffed);
|
|
195
195
|
} catch (libavErr) {
|
|
196
196
|
const mbMsg = mediabunnyErr.message || String(mediabunnyErr);
|
|
@@ -202,7 +202,7 @@ async function probe(source) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
try {
|
|
205
|
-
const { probeWithLibav } = await import('./avi-
|
|
205
|
+
const { probeWithLibav } = await import('./avi-GCGM7OJI.js');
|
|
206
206
|
return await probeWithLibav(normalized, sniffed);
|
|
207
207
|
} catch (err) {
|
|
208
208
|
const inner = err instanceof Error ? err.message : String(err);
|
|
@@ -288,8 +288,29 @@ var NATIVE_AUDIO_CODECS = /* @__PURE__ */ new Set([
|
|
|
288
288
|
"vorbis",
|
|
289
289
|
"flac"
|
|
290
290
|
]);
|
|
291
|
-
var FALLBACK_VIDEO_CODECS = /* @__PURE__ */ new Set([
|
|
292
|
-
|
|
291
|
+
var FALLBACK_VIDEO_CODECS = /* @__PURE__ */ new Set([
|
|
292
|
+
"wmv3",
|
|
293
|
+
"vc1",
|
|
294
|
+
"mpeg4",
|
|
295
|
+
"rv10",
|
|
296
|
+
"rv20",
|
|
297
|
+
"rv30",
|
|
298
|
+
"rv40",
|
|
299
|
+
"mpeg2",
|
|
300
|
+
"mpeg1",
|
|
301
|
+
"theora"
|
|
302
|
+
]);
|
|
303
|
+
var FALLBACK_AUDIO_CODECS = /* @__PURE__ */ new Set([
|
|
304
|
+
"wmav2",
|
|
305
|
+
"wmapro",
|
|
306
|
+
"ac3",
|
|
307
|
+
"eac3",
|
|
308
|
+
"cook",
|
|
309
|
+
"ra_144",
|
|
310
|
+
"ra_288",
|
|
311
|
+
"sipr",
|
|
312
|
+
"atrac3"
|
|
313
|
+
]);
|
|
293
314
|
var NATIVE_CONTAINERS = /* @__PURE__ */ new Set([
|
|
294
315
|
"mp4",
|
|
295
316
|
"mov",
|
|
@@ -1923,7 +1944,7 @@ async function loadBridge() {
|
|
|
1923
1944
|
var READY_AUDIO_BUFFER_SECONDS = 0.3;
|
|
1924
1945
|
var READY_TIMEOUT_SECONDS = 10;
|
|
1925
1946
|
async function createHybridSession(ctx, target) {
|
|
1926
|
-
const { normalizeSource: normalizeSource2 } = await import('./source-
|
|
1947
|
+
const { normalizeSource: normalizeSource2 } = await import('./source-FFZ7TW2B.js');
|
|
1927
1948
|
const source = await normalizeSource2(ctx.source);
|
|
1928
1949
|
const fps = ctx.videoTracks[0]?.fps ?? 30;
|
|
1929
1950
|
const audio = new AudioOutput();
|
|
@@ -2090,6 +2111,9 @@ async function startDecoder(opts) {
|
|
|
2090
2111
|
let packetsRead = 0;
|
|
2091
2112
|
let videoFramesDecoded = 0;
|
|
2092
2113
|
let audioFramesDecoded = 0;
|
|
2114
|
+
let watchdogFirstFrameMs = 0;
|
|
2115
|
+
let watchdogSlowSinceMs = 0;
|
|
2116
|
+
let watchdogWarned = false;
|
|
2093
2117
|
let syntheticVideoUs = 0;
|
|
2094
2118
|
let syntheticAudioUs = 0;
|
|
2095
2119
|
const videoTrackInfo = opts.context.videoTracks.find((t) => t.id === videoStream?.index);
|
|
@@ -2101,7 +2125,7 @@ async function startDecoder(opts) {
|
|
|
2101
2125
|
let packets;
|
|
2102
2126
|
try {
|
|
2103
2127
|
[readErr, packets] = await libav.ff_read_frame_multi(fmt_ctx, readPkt, {
|
|
2104
|
-
limit:
|
|
2128
|
+
limit: 64 * 1024
|
|
2105
2129
|
});
|
|
2106
2130
|
} catch (err) {
|
|
2107
2131
|
console.error("[avbridge] ff_read_frame_multi failed:", err);
|
|
@@ -2118,6 +2142,28 @@ async function startDecoder(opts) {
|
|
|
2118
2142
|
await decodeAudioBatch(audioPackets, myToken);
|
|
2119
2143
|
}
|
|
2120
2144
|
packetsRead += (videoPackets?.length ?? 0) + (audioPackets?.length ?? 0);
|
|
2145
|
+
if (videoFramesDecoded > 0) {
|
|
2146
|
+
if (watchdogFirstFrameMs === 0) {
|
|
2147
|
+
watchdogFirstFrameMs = performance.now();
|
|
2148
|
+
}
|
|
2149
|
+
const elapsedSinceFirst = (performance.now() - watchdogFirstFrameMs) / 1e3;
|
|
2150
|
+
if (elapsedSinceFirst > 1 && !watchdogWarned) {
|
|
2151
|
+
const expectedFrames = elapsedSinceFirst * videoFps;
|
|
2152
|
+
const ratio = videoFramesDecoded / expectedFrames;
|
|
2153
|
+
if (ratio < 0.6) {
|
|
2154
|
+
if (watchdogSlowSinceMs === 0) watchdogSlowSinceMs = performance.now();
|
|
2155
|
+
if ((performance.now() - watchdogSlowSinceMs) / 1e3 > 5) {
|
|
2156
|
+
watchdogWarned = true;
|
|
2157
|
+
console.warn(
|
|
2158
|
+
"[avbridge:decode-rate]",
|
|
2159
|
+
`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.`
|
|
2160
|
+
);
|
|
2161
|
+
}
|
|
2162
|
+
} else {
|
|
2163
|
+
watchdogSlowSinceMs = 0;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2121
2167
|
while (!destroyed && myToken === pumpToken && (opts.audio.bufferAhead() > 2 || opts.renderer.queueDepth() >= opts.renderer.queueHighWater)) {
|
|
2122
2168
|
await new Promise((r) => setTimeout(r, 50));
|
|
2123
2169
|
}
|
|
@@ -2444,10 +2490,10 @@ async function loadBridge2() {
|
|
|
2444
2490
|
}
|
|
2445
2491
|
|
|
2446
2492
|
// src/strategies/fallback/index.ts
|
|
2447
|
-
var READY_AUDIO_BUFFER_SECONDS2 = 0.
|
|
2448
|
-
var READY_TIMEOUT_SECONDS2 =
|
|
2493
|
+
var READY_AUDIO_BUFFER_SECONDS2 = 0.04;
|
|
2494
|
+
var READY_TIMEOUT_SECONDS2 = 3;
|
|
2449
2495
|
async function createFallbackSession(ctx, target) {
|
|
2450
|
-
const { normalizeSource: normalizeSource2 } = await import('./source-
|
|
2496
|
+
const { normalizeSource: normalizeSource2 } = await import('./source-FFZ7TW2B.js');
|
|
2451
2497
|
const source = await normalizeSource2(ctx.source);
|
|
2452
2498
|
const fps = ctx.videoTracks[0]?.fps ?? 30;
|
|
2453
2499
|
const audio = new AudioOutput();
|
|
@@ -2481,12 +2527,26 @@ async function createFallbackSession(ctx, target) {
|
|
|
2481
2527
|
}
|
|
2482
2528
|
async function waitForBuffer() {
|
|
2483
2529
|
const start = performance.now();
|
|
2530
|
+
dbg.info(
|
|
2531
|
+
"cold-start",
|
|
2532
|
+
`gate entry: need audio >= ${READY_AUDIO_BUFFER_SECONDS2 * 1e3}ms + 1 frame`
|
|
2533
|
+
);
|
|
2484
2534
|
while (true) {
|
|
2485
|
-
const
|
|
2486
|
-
|
|
2535
|
+
const audioAhead = audio.isNoAudio() ? Infinity : audio.bufferAhead();
|
|
2536
|
+
const audioReady = audio.isNoAudio() || audioAhead >= READY_AUDIO_BUFFER_SECONDS2;
|
|
2537
|
+
const hasFrames = renderer.hasFrames();
|
|
2538
|
+
if (audioReady && hasFrames) {
|
|
2539
|
+
dbg.info(
|
|
2540
|
+
"cold-start",
|
|
2541
|
+
`gate satisfied in ${(performance.now() - start).toFixed(0)}ms (audio=${(audioAhead * 1e3).toFixed(0)}ms, frames=${renderer.queueDepth()})`
|
|
2542
|
+
);
|
|
2487
2543
|
return;
|
|
2488
2544
|
}
|
|
2489
2545
|
if ((performance.now() - start) / 1e3 > READY_TIMEOUT_SECONDS2) {
|
|
2546
|
+
dbg.diag(
|
|
2547
|
+
"cold-start",
|
|
2548
|
+
`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.`
|
|
2549
|
+
);
|
|
2490
2550
|
return;
|
|
2491
2551
|
}
|
|
2492
2552
|
await new Promise((r) => setTimeout(r, 50));
|
|
@@ -2701,8 +2761,14 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2701
2761
|
return player;
|
|
2702
2762
|
}
|
|
2703
2763
|
async bootstrap() {
|
|
2764
|
+
const bootstrapStart = performance.now();
|
|
2704
2765
|
try {
|
|
2705
|
-
|
|
2766
|
+
dbg.info("bootstrap", "start");
|
|
2767
|
+
const ctx = await dbg.timed("probe", "probe", 3e3, () => probe(this.options.source));
|
|
2768
|
+
dbg.info(
|
|
2769
|
+
"probe",
|
|
2770
|
+
`container=${ctx.container} video=${ctx.videoTracks[0]?.codec ?? "-"} audio=${ctx.audioTracks[0]?.codec ?? "-"} probedBy=${ctx.probedBy}`
|
|
2771
|
+
);
|
|
2706
2772
|
this.diag.recordProbe(ctx);
|
|
2707
2773
|
this.mediaContext = ctx;
|
|
2708
2774
|
if (this.options.subtitles) {
|
|
@@ -2728,6 +2794,10 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2728
2794
|
}
|
|
2729
2795
|
}
|
|
2730
2796
|
const decision = this.options.initialStrategy ? buildInitialDecision(this.options.initialStrategy, ctx) : classifyContext(ctx);
|
|
2797
|
+
dbg.info(
|
|
2798
|
+
"classify",
|
|
2799
|
+
`strategy=${decision.strategy} class=${decision.class} reason="${decision.reason}"` + (decision.fallbackChain ? ` fallback=${decision.fallbackChain.join("\u2192")}` : "")
|
|
2800
|
+
);
|
|
2731
2801
|
this.classification = decision;
|
|
2732
2802
|
this.diag.recordClassification(decision);
|
|
2733
2803
|
this.emitter.emitSticky("strategy", {
|
|
@@ -2753,6 +2823,14 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2753
2823
|
this.startTimeupdateLoop();
|
|
2754
2824
|
this.options.target.addEventListener("ended", () => this.emitter.emit("ended", void 0));
|
|
2755
2825
|
this.emitter.emitSticky("ready", void 0);
|
|
2826
|
+
const bootstrapElapsed = performance.now() - bootstrapStart;
|
|
2827
|
+
dbg.info("bootstrap", `ready in ${bootstrapElapsed.toFixed(0)}ms`);
|
|
2828
|
+
if (bootstrapElapsed > 5e3) {
|
|
2829
|
+
console.warn(
|
|
2830
|
+
"[avbridge:bootstrap]",
|
|
2831
|
+
`total bootstrap time ${bootstrapElapsed.toFixed(0)}ms \u2014 unusually slow. Enable globalThis.AVBRIDGE_DEBUG for a per-phase breakdown.`
|
|
2832
|
+
);
|
|
2833
|
+
}
|
|
2756
2834
|
} catch (err) {
|
|
2757
2835
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
2758
2836
|
this.diag.recordError(e);
|
|
@@ -3077,5 +3155,5 @@ function defaultFallbackChain(strategy) {
|
|
|
3077
3155
|
}
|
|
3078
3156
|
|
|
3079
3157
|
export { UnifiedPlayer, avbridgeAudioToMediabunny, avbridgeVideoToMediabunny, buildMediabunnySourceFromInput, classifyContext, createPlayer, probe, srtToVtt };
|
|
3080
|
-
//# sourceMappingURL=chunk-
|
|
3081
|
-
//# sourceMappingURL=chunk-
|
|
3158
|
+
//# sourceMappingURL=chunk-C5VA5U5O.js.map
|
|
3159
|
+
//# sourceMappingURL=chunk-C5VA5U5O.js.map
|