avbridge 2.1.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +136 -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-CUQD23WO.js → chunk-C5VA5U5O.js} +122 -23
- 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-O34444ID.cjs → chunk-OE66B34H.cjs} +126 -27
- package/dist/chunk-OE66B34H.cjs.map +1 -0
- package/dist/element-browser.js +244 -19
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +9 -5
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.cts +1 -1
- package/dist/element.d.ts +1 -1
- package/dist/element.js +8 -4
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +18 -18
- package/dist/index.d.cts +12 -8
- package/dist/index.d.ts +12 -8
- 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/element/avbridge-video.ts +12 -1
- package/src/player.ts +22 -1
- package/src/probe/avi.ts +8 -1
- package/src/probe/index.ts +30 -9
- 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/strategies/fallback/video-renderer.ts +29 -4
- package/src/strategies/remux/pipeline.ts +10 -1
- 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-CUQD23WO.js.map +0 -1
- package/dist/chunk-EJH67FXG.js.map +0 -1
- package/dist/chunk-JQH6D4OE.cjs.map +0 -1
- package/dist/chunk-O34444ID.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,142 @@ 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
|
+
|
|
100
|
+
## [2.1.2]
|
|
101
|
+
|
|
102
|
+
### Fixed
|
|
103
|
+
|
|
104
|
+
- **Fallback strategy video now visible inside `<avbridge-video>`.** The
|
|
105
|
+
fallback renderer attaches its canvas overlay via `target.parentElement`,
|
|
106
|
+
but when the `<video>` lives inside a `ShadowRoot` (as it does in the
|
|
107
|
+
custom element), `parentElement` is `null` because `ShadowRoot` is not
|
|
108
|
+
an `Element`. The canvas silently never got attached to the DOM, so
|
|
109
|
+
frames were decoded and "painted" (the stats counters incremented) but
|
|
110
|
+
nothing was ever visible — just audio. Fixed by wrapping the shadow
|
|
111
|
+
`<video>` in a positioned `<div part="stage">` and hardening the
|
|
112
|
+
renderer's parent lookup to use `parentNode` as a fallback with a loud
|
|
113
|
+
warning if no parent exists at all.
|
|
114
|
+
|
|
115
|
+
- **Remux strategy reseek no longer fails with "First packet must be a
|
|
116
|
+
key packet".** When `setStrategy("remux")` is invoked mid-playback, the
|
|
117
|
+
pipeline recreates mediabunny's `Output` (required because mediabunny's
|
|
118
|
+
fMP4 muxer is one-shot streaming). The pump's packet race could then
|
|
119
|
+
emit an audio packet first on the fresh muxer, which mediabunny rejects
|
|
120
|
+
because the first packet of any muxer run must be a key packet. The
|
|
121
|
+
pump now forces the first video packet — which we fetch via
|
|
122
|
+
`getKeyPacket()` and is guaranteed to be a keyframe — out before any
|
|
123
|
+
audio.
|
|
124
|
+
|
|
125
|
+
- **`mp4v`-in-MP4 files now fall through to libav probing.** mediabunny's
|
|
126
|
+
MP4 demuxer asserts on files whose video sample entry type isn't one
|
|
127
|
+
it recognizes (`mp4v` for MPEG-4 Part 2 / DivX / Xvid packaged in
|
|
128
|
+
ISOBMFF is the common case). Previously the probe rethrew the
|
|
129
|
+
assertion and gave up. The probe now escalates to libav when
|
|
130
|
+
mediabunny fails on any mediabunny-targeted container (mp4, mkv,
|
|
131
|
+
webm, …), which handles the long tail of codec combinations
|
|
132
|
+
mediabunny's pure-JS parser doesn't cover. If libav also fails, both
|
|
133
|
+
errors are surfaced together.
|
|
134
|
+
|
|
135
|
+
- **Demo dev server serves libav binaries via a Vite middleware plugin**
|
|
136
|
+
instead of `demo/public/libav/`. Recent Vite versions refuse to let
|
|
137
|
+
source code `import()` files out of `public/`, which broke the libav
|
|
138
|
+
loader's dynamic import. `serveVendorLibav()` in `vite.config.ts` now
|
|
139
|
+
streams files directly out of `vendor/libav/` at the same `/libav/*`
|
|
140
|
+
URL, bypassing the restriction. `scripts/copy-libav.mjs` is
|
|
141
|
+
simplified — it no longer mirrors to the demo's public tree.
|
|
142
|
+
|
|
7
143
|
## [2.1.1]
|
|
8
144
|
|
|
9
145
|
### 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":";;;;;;AAuBA,eAAsB,cAAA,CACpB,QACA,OAAA,EACuB;AAKvB,EAAA,MAAM,KAAA,GAAS,MAAMA,2BAAA,CAAU,UAAU,CAAA;AAEzC,EAAA,MAAM,WAAW,MAAA,CAAO,IAAA,IAAQ,SAAS,OAAA,KAAY,SAAA,GAAY,QAAQ,OAAO,CAAA,CAAA;AAIhF,EAAA,MAAM,MAAA,GAA2B,MAAMC,mCAAA,CAAkB,KAAA,EAA6D,UAAU,MAAM,CAAA;AAEtI,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI,UAAyB,EAAC;AAC9B,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,oBAAA,CAAqB,QAAQ,CAAA;AACxD,IAAA,OAAA,GAAU,OAAO,CAAC,CAAA;AAClB,IAAA,OAAA,GAAU,OAAO,CAAC,CAAA;AAAA,EACpB,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,MAAA,CAAO,MAAA,EAAO,CAAE,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAKpC,IAAA,MAAM,KAAA,GACJ,GAAA,YAAe,KAAA,GACX,GAAA,CAAI,UACJ,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,KAAQ,OACjC,IAAA,CAAK,SAAA,CAAU,GAAG,CAAA,GAClB,OAAO,GAAG,CAAA;AAElB,IAAA,OAAA,CAAQ,KAAA,CAAM,8CAA8C,GAAG,CAAA;AAC/D,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,yBAAA,EAA4B,QAAQ,CAAA,8HAAA,EAAiI,KAAA,IAAS,6CAAwC,CAAA,CAAA;AAAA,KACxN;AAAA,EACF;AAEA,EAAA,MAAM,cAAgC,EAAC;AACvC,EAAA,MAAM,cAAgC,EAAC;AAEvC,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,MAAM,SAAA,GAAa,MAAM,IAAA,CAAK,MAAM,KAAA,CAAM,gBAAA,CAAiB,MAAA,CAAO,QAAQ,CAAC,CAAA,IAAM,CAAA,QAAA,EAAW,MAAA,CAAO,QAAQ,CAAA,CAAA,CAAA;AAG3G,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,MAAM,MAAM,mBAAA,CAAoB,MAAA,CAAO,QAAQ,CAAC,CAAA;AAE5E,IAAA,IAAI,MAAA,CAAO,UAAA,KAAe,KAAA,CAAM,kBAAA,EAAoB;AAClD,MAAA,WAAA,CAAY,IAAA,CAAK;AAAA,QACf,IAAI,MAAA,CAAO,KAAA;AAAA,QACX,KAAA,EAAO,sBAAsB,SAAS,CAAA;AAAA,QACtC,KAAA,EAAO,UAAU,KAAA,IAAS,CAAA;AAAA,QAC1B,MAAA,EAAQ,UAAU,MAAA,IAAU,CAAA;AAAA,QAC5B,GAAA,EAAK,UAAU,MAAM;AAAA,OACtB,CAAA;AAAA,IACH,CAAA,MAAA,IAAW,MAAA,CAAO,UAAA,KAAe,KAAA,CAAM,kBAAA,EAAoB;AACzD,MAAA,WAAA,CAAY,IAAA,CAAK;AAAA,QACf,IAAI,MAAA,CAAO,KAAA;AAAA,QACX,KAAA,EAAO,sBAAsB,SAAS,CAAA;AAAA,QACtC,QAAA,EAAU,QAAA,EAAU,QAAA,IAAY,QAAA,EAAU,qBAAA,IAAyB,CAAA;AAAA,QACnE,UAAA,EAAY,UAAU,WAAA,IAAe;AAAA,OACtC,CAAA;AAAA,IACH;AAAA,EACF;AAIA,EAAA,MAAM,QAAA,GAAW,MAAM,YAAA,CAAa,KAAA,EAAO,OAAQ,CAAA;AAInD,EAAA,MAAM,KAAA,CAAM,uBAAA,CAAwB,OAAQ,CAAA,CAAE,MAAM,MAAM;AAAA,EAAC,CAAC,CAAA;AAC5D,EAAA,MAAM,MAAA,CAAO,MAAA,EAAO,CAAE,KAAA,CAAM,MAAM;AAAA,EAAC,CAAC,CAAA;AAEpC,EAAA,OAAO;AAAA,IACL,QAAQ,MAAA,CAAO,QAAA;AAAA,IACf,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,YAAY,MAAA,CAAO,UAAA;AAAA,IACnB,SAAA,EAAW,OAAA,KAAY,SAAA,GAAY,SAAA,GAAY,OAAA;AAAA,IAC/C,WAAA;AAAA,IACA,WAAA;AAAA,IACA,gBAAgB,EAAC;AAAA,IACjB,QAAA,EAAU,OAAA;AAAA,IACV;AAAA,GACF;AACF;AAEA,SAAS,UAAU,MAAA,EAAyC;AAC1D,EAAA,IAAI,OAAO,MAAA,CAAO,kBAAA,KAAuB,QAAA,IAAY,OAAO,kBAAA,EAAoB;AAC9E,IAAA,OAAO,MAAA,CAAO,qBAAqB,MAAA,CAAO,kBAAA;AAAA,EAC5C;AACA,EAAA,IAAI,MAAA,CAAO,cAAA,IAAkB,OAAO,MAAA,CAAO,mBAAmB,QAAA,EAAU;AACtE,IAAA,IAAI,MAAA,CAAO,cAAA,CAAe,GAAA,KAAQ,CAAA,EAAG,OAAO,MAAA;AAC5C,IAAA,OAAO,MAAA,CAAO,cAAA,CAAe,GAAA,GAAM,MAAA,CAAO,cAAA,CAAe,GAAA;AAAA,EAC3D;AACA,EAAA,OAAO,MAAA;AACT;AAEA,eAAe,YAAA,CAAa,OAAsB,OAAA,EAA8C;AAC9F,EAAA,IAAI;AAQF,IAAA,MAAM,EAAA,GAAK,MAAM,KAAA,CAAM,wBAAA,GAA2B,OAAO,CAAA;AACzD,IAAA,MAAM,EAAA,GAAK,MAAM,KAAA,CAAM,0BAAA,GAA6B,OAAO,CAAA;AAC3D,IAAA,IAAI,OAAO,EAAA,KAAO,QAAA,IAAY,OAAO,EAAA,KAAO,UAAU,OAAO,KAAA,CAAA;AAG7D,IAAA,IAAI,EAAA,KAAO,CAAA,UAAA,IAAe,EAAA,KAAO,CAAA,EAAG,OAAO,KAAA,CAAA;AAI3C,IAAA,MAAM,EAAA,GACJ,OAAO,KAAA,CAAM,QAAA,KAAa,aACtB,KAAA,CAAM,QAAA,CAAS,EAAA,EAAI,EAAE,IACrB,EAAA,GAAK,UAAA,GAAc,EAAA,IAAM,EAAA,GAAK,IAAI,UAAA,GAAc,CAAA,CAAA;AAEtD,IAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,EAAE,CAAA,IAAK,EAAA,IAAM,GAAG,OAAO,KAAA,CAAA;AAC5C,IAAA,OAAO,EAAA,GAAK,GAAA;AAAA,EACd,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAEA,eAAe,KAAQ,EAAA,EAAkD;AACvE,EAAA,IAAI;AAAE,IAAA,OAAO,MAAM,EAAA,EAAG;AAAA,EAAG,CAAA,CAAA,MAAQ;AAAE,IAAA,OAAO,MAAA;AAAA,EAAW;AACvD;AAGA,SAAS,sBAAsB,IAAA,EAA0B;AACvD,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,OAAA;AAAU,MAAA,OAAO,OAAA;AAAA;AAAA,IACtB,KAAK,WAAA;AAAA,IACL,KAAK,WAAA;AAAA,IACL,KAAK,WAAA;AACH,MAAA,OAAO,OAAA;AAAA,IACT,KAAK,MAAA;AAAA,IACL,KAAK,MAAA;AAAA,IACL,KAAK,MAAA;AACH,MAAA,OAAO,MAAA;AAAA,IACT,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,YAAA;AAAc,MAAA,OAAO,OAAA;AAAA,IAC1B,KAAK,YAAA;AAAc,MAAA,OAAO,OAAA;AAAA,IAC1B,KAAK,QAAA;AAAU,MAAA,OAAO,QAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB;AAAe,MAAA,OAAO,IAAA;AAAA;AAE1B;AAEA,SAAS,sBAAsB,IAAA,EAA0B;AACvD,EAAA,QAAQ,IAAA;AAAM,IACZ,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,KAAA;AAAA,IACL,KAAK,UAAA;AACH,MAAA,OAAO,KAAA;AAAA,IACT,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,QAAA;AAAU,MAAA,OAAO,QAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,OAAA;AAAA,IACL,KAAK,OAAA;AAAU,MAAA,OAAO,OAAA;AAAA,IACtB,KAAK,QAAA;AAAU,MAAA,OAAO,QAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,QAAA;AAAU,MAAA,OAAO,QAAA;AAAA,IACtB,KAAK,QAAA;AAAU,MAAA,OAAO,QAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,QAAA;AAAU,MAAA,OAAO,QAAA;AAAA,IACtB;AAAe,MAAA,OAAO,IAAA;AAAA;AAE1B","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":"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","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"]}
|