avbridge 2.1.2 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/README.md +98 -71
  3. package/dist/{avi-GNTV5ZOH.cjs → avi-6SJLWIWW.cjs} +19 -4
  4. package/dist/avi-6SJLWIWW.cjs.map +1 -0
  5. package/dist/{avi-V6HYQVR2.js → avi-GCGM7OJI.js} +18 -3
  6. package/dist/avi-GCGM7OJI.js.map +1 -0
  7. package/dist/{chunk-EJH67FXG.js → chunk-5DMTJVIU.js} +99 -3
  8. package/dist/chunk-5DMTJVIU.js.map +1 -0
  9. package/dist/{chunk-3AUGRKPY.js → chunk-DMWARSEF.js} +160 -27
  10. package/dist/chunk-DMWARSEF.js.map +1 -0
  11. package/dist/{chunk-JQH6D4OE.cjs → chunk-G4APZMCP.cjs} +100 -3
  12. package/dist/chunk-G4APZMCP.cjs.map +1 -0
  13. package/dist/{chunk-Y5FYF5KG.cjs → chunk-HZLQNKFN.cjs} +5 -2
  14. package/dist/chunk-HZLQNKFN.cjs.map +1 -0
  15. package/dist/{chunk-PQTZS7OA.js → chunk-ILKDNBSE.js} +5 -2
  16. package/dist/chunk-ILKDNBSE.js.map +1 -0
  17. package/dist/{chunk-DPVIOYGC.cjs → chunk-UF2N5L63.cjs} +164 -31
  18. package/dist/chunk-UF2N5L63.cjs.map +1 -0
  19. package/dist/element-browser.js +276 -21
  20. package/dist/element-browser.js.map +1 -1
  21. package/dist/element.cjs +4 -4
  22. package/dist/element.d.cts +1 -1
  23. package/dist/element.d.ts +1 -1
  24. package/dist/element.js +3 -3
  25. package/dist/index.cjs +18 -18
  26. package/dist/index.d.cts +2 -2
  27. package/dist/index.d.ts +2 -2
  28. package/dist/index.js +5 -5
  29. package/dist/libav-loader-27RDIN2I.js +3 -0
  30. package/dist/{libav-loader-XKH2TKUW.js.map → libav-loader-27RDIN2I.js.map} +1 -1
  31. package/dist/libav-loader-IV4AJ2HW.cjs +12 -0
  32. package/dist/{libav-loader-6APXVNIV.cjs.map → libav-loader-IV4AJ2HW.cjs.map} +1 -1
  33. package/dist/{player-BdtUG4rh.d.cts → player-U2NPmFvA.d.cts} +4 -3
  34. package/dist/{player-BdtUG4rh.d.ts → player-U2NPmFvA.d.ts} +4 -3
  35. package/dist/source-CN43EI7Z.cjs +28 -0
  36. package/dist/{source-SC6ZEQYR.cjs.map → source-CN43EI7Z.cjs.map} +1 -1
  37. package/dist/source-FFZ7TW2B.js +3 -0
  38. package/dist/{source-ZFS4H7J3.js.map → source-FFZ7TW2B.js.map} +1 -1
  39. package/package.json +1 -1
  40. package/src/classify/rules.ts +9 -2
  41. package/src/player.ts +46 -17
  42. package/src/probe/avi.ts +8 -1
  43. package/src/strategies/fallback/audio-output.ts +25 -3
  44. package/src/strategies/fallback/decoder.ts +96 -8
  45. package/src/strategies/fallback/index.ts +98 -6
  46. package/src/strategies/fallback/libav-loader.ts +12 -0
  47. package/src/strategies/fallback/video-renderer.ts +5 -1
  48. package/src/strategies/hybrid/index.ts +9 -1
  49. package/src/strategies/remux/index.ts +13 -1
  50. package/src/strategies/remux/pipeline.ts +6 -0
  51. package/src/types.ts +10 -1
  52. package/src/util/debug.ts +131 -0
  53. package/src/util/source.ts +4 -0
  54. package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.mjs +1 -1
  55. package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.wasm +0 -0
  56. package/dist/avi-GNTV5ZOH.cjs.map +0 -1
  57. package/dist/avi-V6HYQVR2.js.map +0 -1
  58. package/dist/chunk-3AUGRKPY.js.map +0 -1
  59. package/dist/chunk-DPVIOYGC.cjs.map +0 -1
  60. package/dist/chunk-EJH67FXG.js.map +0 -1
  61. package/dist/chunk-JQH6D4OE.cjs.map +0 -1
  62. package/dist/chunk-PQTZS7OA.js.map +0 -1
  63. package/dist/chunk-Y5FYF5KG.cjs.map +0 -1
  64. package/dist/libav-loader-6APXVNIV.cjs +0 -12
  65. package/dist/libav-loader-XKH2TKUW.js +0 -3
  66. package/dist/source-SC6ZEQYR.cjs +0 -28
  67. package/dist/source-ZFS4H7J3.js +0 -3
package/CHANGELOG.md CHANGED
@@ -4,6 +4,144 @@ 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.1]
8
+
9
+ ### Fixed
10
+
11
+ - **Canvas renderer no longer stretches non-stage-aspect video.** The
12
+ fallback + hybrid renderer's canvas sat at `width:100%;height:100%`
13
+ with no `object-fit`, so portrait or otherwise non-matching content
14
+ was stretched to fill the stage. Now uses `object-fit: contain` to
15
+ letterbox the bitmap inside the stage.
16
+ - **Strategy switch to `remux` while playing now resumes playback.**
17
+ `doSetStrategy` calls `session.seek()` before `session.play()`, so
18
+ the remux pipeline used to start with `pendingAutoPlay=false`; the
19
+ subsequent `video.play()` would then hit an element whose `src`
20
+ wasn't yet assigned (the MseSink constructs lazily on first write)
21
+ and silently reject. `RemuxPipeline` gained `setAutoPlay()` so
22
+ `session.play()` can flip `pendingAutoPlay=true` mid-flight; the
23
+ MseSink fires `video.play()` as soon as buffered data lands.
24
+ - **Strategy switch from `hybrid` / `fallback` to another backend now
25
+ preserves play state.** Those strategies hide the `<video>` and
26
+ drive playback from their own Web Audio clock, so the underlying
27
+ element's native `paused` was always `true`. `doSetStrategy` read
28
+ `!target.paused` and captured `wasPlaying=false`, skipping the
29
+ restore on the new session. Both strategies now patch a
30
+ configurable `paused` getter on the target that mirrors
31
+ `audio.isPlaying()`, and clean it up on `destroy()`.
32
+ - **`initialStrategy` no longer retries the same failing strategy.**
33
+ `buildInitialDecision` inherited `natural.fallbackChain` verbatim,
34
+ so for a `RISKY_NATIVE` file with `initialStrategy: "remux"` the
35
+ chain still contained `"remux"` — on failure, `startSession` would
36
+ shift it off and retry `remux` before escalating. The synthetic
37
+ decision now filters `initial` out of the inherited chain.
38
+ - **`UnifiedPlayer.destroy()` removes the `ended` listener it
39
+ attached during `bootstrap()`.** Previously the anonymous handler
40
+ leaked across player lifecycles on long-lived target elements
41
+ (e.g. `<avbridge-video>` swapping source), causing gradual
42
+ accumulation and duplicate `ended` events after source reloads.
43
+
44
+ ### Changed
45
+
46
+ - Bundle audit ceiling for the `element-only` scenario raised to
47
+ 20 KB eager gzip. The budget's purpose is catching
48
+ order-of-magnitude regressions (e.g. libav accidentally eager-
49
+ imported), not policing ±200 bytes; realistic first-play cost is
50
+ dominated by the multi-megabyte lazy wasm load.
51
+
52
+ ## [2.2.0]
53
+
54
+ ### Added
55
+
56
+ - **RealMedia playback support** (`.rm`, `.rmvb`). The custom `avbridge`
57
+ libav variant now includes the `rm` demuxer and every RealVideo /
58
+ RealAudio decoder family:
59
+ - Video: `rv10`, `rv20`, `rv30`, `rv40`
60
+ - Audio: `cook`, `ra_144`, `ra_288`, `sipr`, `atrac3`
61
+ These codecs have no browser decoder, so classification routes them
62
+ to the fallback WASM strategy. The custom variant grows by a few
63
+ hundred KB of WASM; the webcodecs variant is unchanged.
64
+ - **Sniff layer recognizes `.RMF` magic bytes** and returns the new
65
+ `"rm"` `ContainerKind`. Probing for a `.rm` or `.rmvb` file goes
66
+ through libav directly (mediabunny doesn't handle RealMedia).
67
+ - **File-picker accept list** in `demo/index.html` and
68
+ `demo/convert.html` now includes `.rm` and `.rmvb`.
69
+
70
+ ### Changed
71
+
72
+ - **`VideoCodec`** gained `rv10`, `rv20`, `rv30` (existing `rv40` kept).
73
+ - **`AudioCodec`** gained `cook`, `ra_144`, `ra_288`, `sipr`, `atrac3`.
74
+ - **`ContainerKind`** gained `rm`.
75
+ - **Classifier** (`src/classify/rules.ts`) — all new RV/RA codecs added
76
+ to `FALLBACK_VIDEO_CODECS` / `FALLBACK_AUDIO_CODECS`.
77
+
78
+ ### Fallback strategy performance tuning
79
+
80
+ These are general-purpose improvements motivated by RealMedia testing
81
+ but also benefit MPEG-4 Part 2 (DivX/Xvid), WMV3, and other
82
+ software-decoded content:
83
+
84
+ - **Cold-start pre-roll gate lowered 300 ms → 40 ms, timeout 10 s → 3 s.**
85
+ The gate used to wait for 300 ms of buffered audio before starting
86
+ playback. On software-decode-bound content (rv40, mpeg4 @ 720p+),
87
+ the decoder produces output slower than realtime, so 300 ms is
88
+ unreachable — the gate would sit out its 10 s timeout before the
89
+ first frame appeared, which the user experienced as a silent 10-
90
+ second hang after clicking Play. The gate now starts on 40 ms
91
+ audio + first frame, and the safety timeout is 3 s. A diagnostic
92
+ warning fires loudly if the timeout is ever hit.
93
+ - **Decoder read batch size raised 16 KB → 64 KB.** Fewer JS↔WASM
94
+ `ff_read_frame_multi` / `ff_decode_multi` round trips per unit of
95
+ video, which measurably speeds up software decode on slow devices.
96
+ Queue burstiness is unchanged because the existing
97
+ `queueHighWater = 30` backpressure still applies.
98
+
99
+ ### Debug + self-diagnosis layer
100
+
101
+ New: **`src/util/debug.ts`** — a runtime-toggleable verbose logging
102
+ channel, plus unconditional warnings for suspicious conditions. The
103
+ goal is that subtle issues self-identify in the console instead of
104
+ requiring 10 minutes of reading diagnostics JSON.
105
+
106
+ - Set `globalThis.AVBRIDGE_DEBUG = true` (or append `?avbridge_debug`
107
+ to a demo page URL) to enable verbose logging. Every log is
108
+ prefixed `[avbridge:<tag>]` so you can filter.
109
+ - When debug is **off**, the following conditions still emit an
110
+ unconditional `console.warn`:
111
+ - **`[avbridge:cold-start] gate TIMEOUT…`** — the fallback
112
+ strategy's `waitForBuffer` hit its 3 s timeout with a
113
+ specific underflow (e.g. "audio=0 ms, frames=0"). This used to
114
+ silently hang playback for 10 seconds.
115
+ - **`[avbridge:decode-rate] decoder is running slower than
116
+ realtime…`** — watchdog in the fallback pump loop; fires once
117
+ per stall when framesDecoded/s stays below 60% of source fps
118
+ for ≥5 s after the first frame. Tells you the exact fps
119
+ ratio and names the likely cause.
120
+ - **`[avbridge:bootstrap] total bootstrap time <N>ms — unusually
121
+ slow…`** — bootstrap took >5 s end-to-end.
122
+ - **`[avbridge:probe] probe took <N>ms (>3000ms expected)…`** —
123
+ slow probe (usually a slow Range request or libav cold-start).
124
+ - **`[avbridge:libav-load] load "<variant>" took <N>ms
125
+ (>5000ms expected)…`** — slow WASM download or wrong base
126
+ path.
127
+
128
+ ### Known limitations
129
+
130
+ - **rv40 / rv30 at 720p+ may still stutter** on modest CPUs. Single-
131
+ threaded WASM software decode of RealVideo's motion compensation is
132
+ fundamentally slower than realtime on many files. libav.js pthreads
133
+ and a WebGL YUV→RGBA upload path are both plausible follow-ups but
134
+ not in 2.2.0. For reference: a 1024×768 rv40 file plays at roughly
135
+ 0.5-2× realtime on an M-series Mac depending on the bitrate. The
136
+ new `[avbridge:decode-rate]` watchdog flags this condition in the
137
+ console so the symptom is never a silent stutter.
138
+
139
+ ### Tests
140
+
141
+ - New sniff test for `.RMF` magic bytes (`tests/sniff.test.ts`).
142
+ - Two new classify tests for RealMedia routing (rv40+cook, rv30+ra_288).
143
+ - Test count: 115 → **118**.
144
+
7
145
  ## [2.1.2]
8
146
 
9
147
  ### Fixed
package/README.md CHANGED
@@ -8,9 +8,10 @@
8
8
  > **Play and convert arbitrary video files in the browser. Local files or remote URLs.**
9
9
 
10
10
  A media compatibility layer for the web. Drop in any file — MP4, MKV, AVI,
11
- WMV, FLV, MPEG-TS, DivX — and avbridge picks the best path: native `<video>`
12
- playback, mediabunny remux to fragmented MP4, libav.js demux + WebCodecs
13
- hardware decode, or full WASM software decode. Same API for all of them.
11
+ WMV, FLV, MPEG-TS, DivX, RMVB — and avbridge picks the best path: native
12
+ `<video>` playback, mediabunny remux to fragmented MP4, libav.js demux +
13
+ WebCodecs hardware decode, or full WASM software decode. Same API for all
14
+ of them.
14
15
 
15
16
  **Streaming-first.** Remote URLs are read via HTTP Range requests across all
16
17
  strategies — even AVI/WMV/FLV — so a 4 GB file plays without buffering 4 GB
@@ -46,6 +47,7 @@ MKV (H.264/AAC) → remux → fragmented MP4 via MSE
46
47
  MPEG-TS (H.264) → remux → fragmented MP4 via MSE
47
48
  AVI (H.264) → hybrid → libav demux + hardware decode
48
49
  AVI (DivX) → fallback → smooth software decode
50
+ RMVB (rv40/cook) → fallback → libav software decode
49
51
  ```
50
52
 
51
53
  ## Quick start
@@ -246,107 +248,132 @@ player.getDiagnostics();
246
248
  // reason: "avi container requires libav demux; codecs are hardware-decodable",
247
249
  // width: 1920, height: 1080, duration: 5400,
248
250
  // probedBy: "libav",
251
+ // transport: "http-range",
252
+ // rangeSupported: true,
253
+ // runtime: { decoderType: "webcodecs-hybrid", videoFramesDecoded: 5432, ... },
249
254
  // strategyHistory: [{ strategy: "hybrid", reason: "...", at: 1712764800000 }]
250
255
  // }
251
256
  ```
252
257
 
258
+ ### Debug logging
259
+
260
+ Enable verbose per-stage logging for hard-to-diagnose issues:
261
+
262
+ ```js
263
+ // In the browser console, or before avbridge loads:
264
+ globalThis.AVBRIDGE_DEBUG = true;
265
+ ```
266
+
267
+ The demo pages also accept `?avbridge_debug` in the URL. When enabled,
268
+ every decision point emits a `[avbridge:<tag>]` log covering probe,
269
+ classify, libav load, bootstrap, strategy execute, and cold-start gate
270
+ timings.
271
+
272
+ The following **unconditional diagnostics** also fire — even without the
273
+ flag — when something smells off:
274
+
275
+ - `[avbridge:bootstrap]` — bootstrap chain took >5 s end-to-end
276
+ - `[avbridge:probe]` — probe took >3 s
277
+ - `[avbridge:libav-load]` — libav variant load took >5 s (usually a
278
+ misconfigured base path or server MIME type)
279
+ - `[avbridge:cold-start]` — fallback cold-start gate timed out or
280
+ released on video-only grace after waiting for audio
281
+ - `[avbridge:decode-rate]` — fallback decoder is running under 60% of
282
+ realtime fps for more than 5 seconds (one-shot per session)
283
+ - `[avbridge:overflow-drop]` — renderer is dropping more than 10% of
284
+ decoded frames because the decoder is bursting faster than the
285
+ canvas can drain (one-shot per session)
286
+
287
+ These are designed so "it works on my machine but stutters on your
288
+ file" surfaces the specific reason in the console instead of requiring
289
+ a live debug session.
290
+
253
291
  ## Install
254
292
 
255
293
  ```bash
256
294
  npm install avbridge
257
295
  ```
258
296
 
259
- This gives you the **core package**: probe, classify, native playback, remux,
260
- transcode, and subtitles. No WASM. The full library is ~17 KB gzipped, but
261
- tree-shaking is aggressive what you actually pay for depends on which
262
- exports you import:
297
+ That's it. **No optional peers to install, no binaries to build, no static
298
+ file path to configure.** Both libav.js variants (the 5 MB webcodecs build
299
+ and the 6.5 MB custom avbridge build with AVI/WMV/DivX/rv40 decoders) ship
300
+ inside the tarball under `node_modules/avbridge/vendor/libav/` and are
301
+ lazy-loaded at runtime only if a file actually needs them.
263
302
 
264
- | Import | Eager (gzip) |
265
- |---|---|
266
- | `srtToVtt` | **0.5 KB** |
267
- | `probe`, `classify` | **3 KB** |
268
- | `transcode` | **3.3 KB** |
269
- | `remux` | **4.1 KB** |
270
- | `createPlayer` | **14 KB** |
271
- | `*` (everything) | **17 KB** |
303
+ Packed tarball is **~4 MB**, unpacked **~15 MB** (mostly the two WASM
304
+ binaries). If you only ever play native MP4, you never download a single
305
+ byte of the libav WASM — the loader is behind a dynamic `import()` that
306
+ never fires.
272
307
 
273
- The libav-loader path is split into a lazy chunk (~5 KB extra) that only
274
- loads when a consumer actually invokes the AVI/ASF/FLV remux path.
308
+ ### Two ways to consume
275
309
 
276
- Run `npm run audit:bundle` to verify these numbers in your fork.
310
+ **Bundler (Vite, webpack, Rollup, esbuild):**
277
311
 
278
- ### Optional: fallback / hybrid strategies
312
+ ```ts
313
+ import { createPlayer, remux, transcode, probe, classify } from "avbridge";
314
+ // or
315
+ import "avbridge/element"; // registers <avbridge-video> custom element
316
+ ```
279
317
 
280
- For files that need software decode or libav.js demux (AVI, WMV, FLV,
281
- legacy codecs):
318
+ The tree-shaking budgets below apply to this path. Your bundler resolves
319
+ `mediabunny` and `libavjs-webcodecs-bridge` through normal dependency
320
+ resolution. libav.js binaries live at
321
+ `node_modules/avbridge/vendor/libav/` — the loader finds them
322
+ automatically via `import.meta.url` in the generated chunk.
282
323
 
283
- ```bash
284
- npm install @libav.js/variant-webcodecs libavjs-webcodecs-bridge
285
- ```
324
+ **Plain `<script type="module">` (no bundler):**
286
325
 
287
- This handles MKV/WebM/MP4 containers via the hybrid/fallback strategies.
326
+ ```html
327
+ <script type="module"
328
+ src="/node_modules/avbridge/dist/element-browser.js"></script>
288
329
 
289
- ### Optional: AVI, WMV3, DivX, and other legacy formats
330
+ <avbridge-video src="/video.mkv" autoplay playsinline></avbridge-video>
331
+ ```
290
332
 
291
- For **AVI, WMV3, MPEG-4 Part 2, DivX**, and other legacy formats, you need
292
- a custom libav.js build see [`vendor/libav/README.md`](./vendor/libav/README.md)
293
- for the build recipe.
333
+ This is a second tsup entry (`dist/element-browser.js`) that inlines
334
+ mediabunny + libavjs-webcodecs-bridge into a single ~1.3 MB file with
335
+ zero bare specifiers at runtime. Perfect for self-hosted tools or static
336
+ sites that don't want a build step. It loads libav.js from the same
337
+ co-located `vendor/libav/` tree.
294
338
 
295
- ### Package boundary summary
339
+ ### Bundle sizes (bundler path)
296
340
 
297
- | What you need | What to install |
341
+ | Import | Eager (gzip) |
298
342
  |---|---|
299
- | Playback of MP4/MKV/WebM/**MPEG-TS** + remux/transcode export | `avbridge` (core, no WASM) |
300
- | Fallback/hybrid decode for modern codecs in legacy containers (AVI/ASF/FLV) | + `@libav.js/variant-webcodecs` + `libavjs-webcodecs-bridge` |
301
- | AVI, WMV3, DivX, MPEG-4 Part 2, VC-1 | + custom libav build (`scripts/build-libav.sh`) |
302
-
303
- ### Serving the libav.js binaries
343
+ | `srtToVtt` | **0.5 KB** |
344
+ | `probe`, `classify` | **2.5 KB** |
345
+ | `transcode` | **3 KB** |
346
+ | `remux` | **3.7 KB** |
347
+ | `createPlayer` | **15 KB** |
348
+ | `*` (everything) | **17.5 KB** |
349
+ | `avbridge/element` | **17 KB** |
304
350
 
305
- The optional libav variants ship as `.wasm` + `.mjs` files that need to be
306
- served by your app at a known URL. avbridge looks for them at
307
- `/libav/<variant>/libav-<variant>.mjs` (where `<variant>` is `webcodecs` or
308
- `avbridge`). You can override the base URL with
309
- `globalThis.AVBRIDGE_LIBAV_BASE = "/my-static-path"` before any avbridge
310
- code runs.
351
+ Run `npm run audit:bundle` to verify in your fork.
311
352
 
312
- #### Vite
353
+ ### Overriding the libav path (advanced)
313
354
 
314
- Copy the variant binaries into your `public/libav/` directory at build
315
- time. The avbridge demo does this via `scripts/copy-libav.mjs`:
355
+ If you want to host the libav binaries somewhere other than
356
+ `node_modules/avbridge/vendor/libav/` for example a CDN, a custom
357
+ libav build, or a patched version — set `AVBRIDGE_LIBAV_BASE` **before**
358
+ any avbridge code runs:
316
359
 
317
- ```bash
318
- # In your project, after npm install:
319
- mkdir -p public/libav/webcodecs
320
- cp node_modules/@libav.js/variant-webcodecs/dist/* public/libav/webcodecs/
360
+ ```html
361
+ <script>globalThis.AVBRIDGE_LIBAV_BASE = "https://cdn.example.com/libav";</script>
362
+ <script type="module" src="..."></script>
321
363
  ```
322
364
 
323
- For the custom `avbridge` variant, after running `./scripts/build-libav.sh`
324
- in the avbridge repo, copy `vendor/libav/*` into `public/libav/avbridge/`.
325
-
326
- #### Webpack
327
-
328
- Use `copy-webpack-plugin` to ship the binaries to your output directory at
329
- the same `libav/<variant>/` path.
330
-
331
- #### Plain `<script>` / no bundler
332
-
333
- Drop the variant directory anywhere on your origin and set
334
- `globalThis.AVBRIDGE_LIBAV_BASE` to the matching URL before importing
335
- avbridge.
336
-
337
- If a libav-backed strategy is selected and the binary isn't reachable,
338
- avbridge throws a clear error mentioning the URL it tried to load. The
339
- core (native + remux for modern containers) doesn't need any of this.
365
+ The loader will then fetch `<base>/<variant>/libav-<variant>.mjs` and its
366
+ sibling `.wasm` files. This is the documented replaceability hook for
367
+ LGPL compliance — see [`NOTICE.md`](./NOTICE.md) and
368
+ [`THIRD_PARTY_LICENSES.md`](./THIRD_PARTY_LICENSES.md).
340
369
 
341
370
  ## Known limitations
342
371
 
343
- - The **fallback strategy** uses WASM software decoding and is CPU-intensive, especially for HD video on mobile devices.
344
- - **Remux of AVI/ASF/FLV** requires libav.js — the core package cannot demux these containers.
372
+ - The **fallback strategy** uses WASM software decoding and is CPU-intensive, especially for HD video on mobile devices. The `[avbridge:decode-rate]` diagnostic fires if the decoder falls below 60% of realtime so you know that's what's happening. Codecs with no WebCodecs support (rv40, mpeg4 @ 720p+, wmv3, vc1 at high resolutions) are the usual suspects.
345
373
  - **Remote URL playback requires HTTP Range requests.** Servers that don't support `Range: bytes=...` will fail fast with a clear error rather than silently downloading the whole file. This applies to all strategies.
346
374
  - **H.264 + MP3 in MP4** is a best-effort combination that may produce playback issues in some browsers. Use `strict: true` to reject it, or re-encode audio to AAC via `transcode()`.
347
- - AVI files with **packed B-frames** (some DivX encodes) may have timing issues until the `mpeg4_unpack_bframes` BSF is wired in.
348
- - libav.js **threading is disabled** due to bugs in v6.8.8 decode runs single-threaded with SIMD acceleration.
349
- - `transcode()` v1 only accepts mediabunny-readable inputs (MP4/MKV/WebM/OGG/MOV/MP3/FLAC/WAV). AVI/ASF/FLV transcoding is planned for v1.1.
375
+ - libav.js **threading is disabled** due to known runtime bugs in the v6.8.8 pthreads build decode runs single-threaded with WASM SIMD acceleration.
376
+ - `transcode()` only accepts mediabunny-readable inputs (MP4/MKV/WebM/OGG/MOV/MP3/FLAC/WAV). AVI/ASF/FLV/RM transcoding means "play it first, record the output" — not yet plumbed.
350
377
  - `transcode()` uses **WebCodecs encoders only** — codec availability depends on the browser. AV1 encoding is not yet universal.
351
378
  - For the **hybrid and fallback strategies**, `<avbridge-video>.buffered` returns an empty `TimeRanges` because the canvas-based renderers don't track buffered ranges yet. Native and remux strategies expose the full `<video>.buffered` set as expected.
352
379
 
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkL4NPOJ36_cjs = require('./chunk-L4NPOJ36.cjs');
4
- var chunkJQH6D4OE_cjs = require('./chunk-JQH6D4OE.cjs');
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 chunkJQH6D4OE_cjs.loadLibav("avbridge");
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-GNTV5ZOH.cjs.map
164
- //# sourceMappingURL=avi-GNTV5ZOH.cjs.map
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-EJH67FXG.js';
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-V6HYQVR2.js.map
162
- //# sourceMappingURL=avi-V6HYQVR2.js.map
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"]}