avbridge 2.2.1 → 2.5.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 +153 -1
- package/NOTICE.md +2 -2
- package/README.md +2 -3
- package/THIRD_PARTY_LICENSES.md +2 -2
- package/dist/avi-2JPBSHGA.js +183 -0
- package/dist/avi-2JPBSHGA.js.map +1 -0
- package/dist/avi-F6WZJK5T.cjs +185 -0
- package/dist/avi-F6WZJK5T.cjs.map +1 -0
- package/dist/{avi-GCGM7OJI.js → avi-NJXAXUXK.js} +9 -3
- package/dist/avi-NJXAXUXK.js.map +1 -0
- package/dist/{avi-6SJLWIWW.cjs → avi-W6L3BTWU.cjs} +10 -4
- package/dist/avi-W6L3BTWU.cjs.map +1 -0
- package/dist/chunk-2IJ66NTD.cjs +212 -0
- package/dist/chunk-2IJ66NTD.cjs.map +1 -0
- package/dist/{chunk-ILKDNBSE.js → chunk-2XW2O3YI.cjs} +55 -10
- package/dist/chunk-2XW2O3YI.cjs.map +1 -0
- package/dist/chunk-5KVLE6YI.js +167 -0
- package/dist/chunk-5KVLE6YI.js.map +1 -0
- package/dist/chunk-5YAWWKA3.js +18 -0
- package/dist/chunk-5YAWWKA3.js.map +1 -0
- package/dist/chunk-CPJLFFCC.js +189 -0
- package/dist/chunk-CPJLFFCC.js.map +1 -0
- package/dist/chunk-CPZ7PXAM.cjs +240 -0
- package/dist/chunk-CPZ7PXAM.cjs.map +1 -0
- package/dist/{chunk-WD2ZNQA7.js → chunk-DCSOQH2N.js} +7 -4
- package/dist/chunk-DCSOQH2N.js.map +1 -0
- package/dist/{chunk-HZLQNKFN.cjs → chunk-E76AMWI4.js} +40 -15
- package/dist/chunk-E76AMWI4.js.map +1 -0
- package/dist/chunk-F3LQJKXK.cjs +20 -0
- package/dist/chunk-F3LQJKXK.cjs.map +1 -0
- package/dist/chunk-IAYKFGFG.js +200 -0
- package/dist/chunk-IAYKFGFG.js.map +1 -0
- package/dist/{chunk-DMWARSEF.js → chunk-KY2GPCT7.js} +788 -697
- package/dist/chunk-KY2GPCT7.js.map +1 -0
- package/dist/chunk-LUFA47FP.js +19 -0
- package/dist/chunk-LUFA47FP.js.map +1 -0
- package/dist/chunk-NNVOHKXJ.cjs +204 -0
- package/dist/chunk-NNVOHKXJ.cjs.map +1 -0
- package/dist/chunk-Q2VUO52Z.cjs +374 -0
- package/dist/chunk-Q2VUO52Z.cjs.map +1 -0
- package/dist/chunk-QDJLQR53.cjs +22 -0
- package/dist/chunk-QDJLQR53.cjs.map +1 -0
- package/dist/chunk-S4WAZC2T.cjs +173 -0
- package/dist/chunk-S4WAZC2T.cjs.map +1 -0
- package/dist/chunk-SMH6IOP2.js +368 -0
- package/dist/chunk-SMH6IOP2.js.map +1 -0
- package/dist/chunk-SR3MPV4D.js +237 -0
- package/dist/chunk-SR3MPV4D.js.map +1 -0
- package/dist/{chunk-UF2N5L63.cjs → chunk-TBW26OPP.cjs} +800 -710
- package/dist/chunk-TBW26OPP.cjs.map +1 -0
- package/dist/chunk-X2K3GIWE.js +235 -0
- package/dist/chunk-X2K3GIWE.js.map +1 -0
- package/dist/{chunk-L4NPOJ36.cjs → chunk-Z33SBWL5.cjs} +7 -4
- package/dist/chunk-Z33SBWL5.cjs.map +1 -0
- package/dist/chunk-ZCUXHW55.cjs +242 -0
- package/dist/chunk-ZCUXHW55.cjs.map +1 -0
- package/dist/element-browser.js +1282 -503
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +59 -5
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.cts +39 -1
- package/dist/element.d.ts +39 -1
- package/dist/element.js +58 -4
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +605 -327
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -4
- package/dist/index.d.ts +48 -4
- package/dist/index.js +528 -319
- package/dist/index.js.map +1 -1
- package/dist/libav-demux-H2GS46GH.cjs +27 -0
- package/dist/{libav-http-reader-NQJVY273.js.map → libav-demux-H2GS46GH.cjs.map} +1 -1
- package/dist/libav-demux-OWZ4T2YW.js +6 -0
- package/dist/{libav-http-reader-FPYDBMYK.cjs.map → libav-demux-OWZ4T2YW.js.map} +1 -1
- package/dist/libav-http-reader-AZLE7YFS.cjs +16 -0
- package/dist/libav-http-reader-AZLE7YFS.cjs.map +1 -0
- package/dist/libav-http-reader-WXG3Z7AI.js +3 -0
- package/dist/libav-http-reader-WXG3Z7AI.js.map +1 -0
- package/dist/{libav-import-GST2AMPL.cjs → libav-import-2ZVKV2E7.cjs} +2 -2
- package/dist/{libav-import-GST2AMPL.cjs.map → libav-import-2ZVKV2E7.cjs.map} +1 -1
- package/dist/{libav-import-2JURFHEW.js → libav-import-6MGLCXVQ.js} +2 -2
- package/dist/{libav-import-2JURFHEW.js.map → libav-import-6MGLCXVQ.js.map} +1 -1
- package/dist/{player-U2NPmFvA.d.cts → player-B6WB74RD.d.cts} +62 -3
- package/dist/{player-U2NPmFvA.d.ts → player-B6WB74RD.d.ts} +62 -3
- package/dist/player.cjs +5631 -0
- package/dist/player.cjs.map +1 -0
- package/dist/player.d.cts +699 -0
- package/dist/player.d.ts +699 -0
- package/dist/player.js +5629 -0
- package/dist/player.js.map +1 -0
- package/dist/remux-OBSMIENG.cjs +35 -0
- package/dist/remux-OBSMIENG.cjs.map +1 -0
- package/dist/remux-WBYIZBBX.js +10 -0
- package/dist/remux-WBYIZBBX.js.map +1 -0
- package/dist/source-4TZ6KMNV.js +4 -0
- package/dist/{source-FFZ7TW2B.js.map → source-4TZ6KMNV.js.map} +1 -1
- package/dist/source-7YLO6E7X.cjs +29 -0
- package/dist/{source-CN43EI7Z.cjs.map → source-7YLO6E7X.cjs.map} +1 -1
- package/dist/source-MTX5ELUZ.js +4 -0
- package/dist/source-MTX5ELUZ.js.map +1 -0
- package/dist/source-VFLXLOCN.cjs +29 -0
- package/dist/source-VFLXLOCN.cjs.map +1 -0
- package/dist/subtitles-4T74JRGT.js +4 -0
- package/dist/subtitles-4T74JRGT.js.map +1 -0
- package/dist/subtitles-QUH4LPI4.cjs +29 -0
- package/dist/subtitles-QUH4LPI4.cjs.map +1 -0
- package/dist/variant-routing-434STYAB.js +3 -0
- package/dist/{variant-routing-JOBWXYKD.js.map → variant-routing-434STYAB.js.map} +1 -1
- package/dist/variant-routing-HONNAA6R.cjs +12 -0
- package/dist/{variant-routing-GOHB2RZN.cjs.map → variant-routing-HONNAA6R.cjs.map} +1 -1
- package/package.json +9 -1
- package/src/classify/rules.ts +27 -5
- package/src/convert/remux.ts +9 -35
- package/src/convert/transcode-libav.ts +691 -0
- package/src/convert/transcode.ts +53 -12
- package/src/element/avbridge-player.ts +861 -0
- package/src/element/avbridge-video.ts +54 -0
- package/src/element/player-icons.ts +25 -0
- package/src/element/player-styles.ts +472 -0
- package/src/errors.ts +53 -0
- package/src/index.ts +23 -0
- package/src/player-element.ts +18 -0
- package/src/player.ts +118 -27
- package/src/plugins/builtin.ts +2 -2
- package/src/probe/avi.ts +4 -0
- package/src/probe/index.ts +40 -10
- package/src/strategies/fallback/audio-output.ts +31 -0
- package/src/strategies/fallback/decoder.ts +179 -175
- package/src/strategies/fallback/index.ts +48 -6
- package/src/strategies/fallback/libav-import.ts +9 -1
- package/src/strategies/fallback/variant-routing.ts +7 -13
- package/src/strategies/fallback/video-renderer.ts +231 -32
- package/src/strategies/hybrid/decoder.ts +219 -200
- package/src/strategies/hybrid/index.ts +48 -7
- package/src/strategies/native.ts +6 -3
- package/src/strategies/remux/index.ts +14 -2
- package/src/strategies/remux/mse.ts +12 -2
- package/src/strategies/remux/pipeline.ts +72 -12
- package/src/subtitles/index.ts +7 -3
- package/src/subtitles/render.ts +8 -0
- package/src/types.ts +53 -1
- package/src/util/libav-demux.ts +405 -0
- package/src/util/libav-http-reader.ts +5 -1
- package/src/util/source.ts +28 -8
- package/src/util/transport.ts +26 -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-6SJLWIWW.cjs.map +0 -1
- package/dist/avi-GCGM7OJI.js.map +0 -1
- package/dist/chunk-DMWARSEF.js.map +0 -1
- package/dist/chunk-HZLQNKFN.cjs.map +0 -1
- package/dist/chunk-ILKDNBSE.js.map +0 -1
- package/dist/chunk-J5MCMN3S.js +0 -27
- package/dist/chunk-J5MCMN3S.js.map +0 -1
- package/dist/chunk-L4NPOJ36.cjs.map +0 -1
- package/dist/chunk-NZU7W256.cjs +0 -29
- package/dist/chunk-NZU7W256.cjs.map +0 -1
- package/dist/chunk-UF2N5L63.cjs.map +0 -1
- package/dist/chunk-WD2ZNQA7.js.map +0 -1
- package/dist/libav-http-reader-FPYDBMYK.cjs +0 -16
- package/dist/libav-http-reader-NQJVY273.js +0 -3
- package/dist/source-CN43EI7Z.cjs +0 -28
- package/dist/source-FFZ7TW2B.js +0 -3
- package/dist/variant-routing-GOHB2RZN.cjs +0 -12
- package/dist/variant-routing-JOBWXYKD.js +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,161 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
All notable changes to **avbridge** are documented here. The format follows
|
|
3
|
+
All notable changes to **avbridge.js** 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.5.0]
|
|
8
|
+
|
|
9
|
+
The "legacy transcode breadth" release. avbridge.js can now transcode
|
|
10
|
+
from legacy containers (AVI, ASF, FLV, RealMedia) to any modern output
|
|
11
|
+
container (MP4, WebM, MKV) in a single one-pass pipeline. Reinforces
|
|
12
|
+
the engine-first positioning: if avbridge can *play* it, avbridge can
|
|
13
|
+
now generally *convert* it too.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **rm/rmvb transcode input.** The legacy-container transcode pipeline
|
|
18
|
+
now handles RealMedia. Video codecs WebCodecs doesn't support
|
|
19
|
+
(rv10/20/30/40) go through a libav software video decoder whose
|
|
20
|
+
decoded frames are bridged to `VideoFrame` via `laFrameToVideoFrame` —
|
|
21
|
+
same downstream queue+drain → mediabunny encode+mux. Audio codecs
|
|
22
|
+
(cook, ra_144/288, sipr, atrac3) already decode via libav.
|
|
23
|
+
- **WebM and MKV output from legacy containers.** The libav-demux
|
|
24
|
+
transcode path previously only emitted MP4. Any supported output
|
|
25
|
+
format (mp4, webm, mkv) now works. Default codecs adapt per output:
|
|
26
|
+
mp4/mkv → h264/aac, webm → vp9/opus. The existing
|
|
27
|
+
`validateCodecCompatibility` gate (in `transcode()`) still catches
|
|
28
|
+
nonsense combos like webm + h264.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- Removed the hard "WebCodecs doesn't support this video codec" throw
|
|
33
|
+
in the transcode-libav setup. The setup now tries WebCodecs first
|
|
34
|
+
and falls back to libav software decode silently. The only path
|
|
35
|
+
that throws at setup is when *both* fail — a codec neither WebCodecs
|
|
36
|
+
nor the avbridge libav variant can decode.
|
|
37
|
+
- Migrated hybrid/fallback/remux from their duplicated copies of
|
|
38
|
+
`sanitizePacketTimestamp` / `sanitizeFrameTimestamp` /
|
|
39
|
+
`libavFrameToInterleavedFloat32` to the shared helpers in
|
|
40
|
+
`src/util/libav-demux.ts`. No behavioral change; bundle sizes
|
|
41
|
+
decreased slightly as a side effect (duplicates weren't tree-shaking
|
|
42
|
+
across strategy boundaries).
|
|
43
|
+
|
|
44
|
+
### Known caveats (out of scope for this release)
|
|
45
|
+
|
|
46
|
+
- **10-bit video transcode** — source 10-bit video throws with a
|
|
47
|
+
clear error. Needs pixel-format conversion before encode.
|
|
48
|
+
- **Streaming output** (`outputStream`) is not yet supported for the
|
|
49
|
+
libav-backed transcode path. Output goes through an in-memory
|
|
50
|
+
`BufferTarget`. Large files are limited by available memory.
|
|
51
|
+
- **Multi-track output** remains deferred — extra input tracks are
|
|
52
|
+
silently dropped.
|
|
53
|
+
|
|
54
|
+
## [2.4.0]
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
|
|
58
|
+
- **Multi-audio track selection across all strategies.** The
|
|
59
|
+
`<avbridge-player>` audio track menu previously rendered the list but
|
|
60
|
+
`setAudioTrack(id)` was a no-op in every strategy. Fallback and hybrid
|
|
61
|
+
rebuild the libav audio decoder and reseek. Remux rebuilds the
|
|
62
|
+
mediabunny Output (MSE SourceBuffer mime can change across tracks).
|
|
63
|
+
Common pain for anime/movie rips with dual-language audio.
|
|
64
|
+
- **AVI/ASF/FLV input support for MP4 transcoding.** New libav-demux-backed
|
|
65
|
+
transcode pipeline: libav demux → WebCodecs `VideoDecoder` + libav
|
|
66
|
+
software audio decode → mediabunny `VideoSampleSource` /
|
|
67
|
+
`AudioSampleSource` → MP4 Blob. Phase 1 scope: MP4 output only, single
|
|
68
|
+
video + single audio track, 8-bit video. Extra tracks are silently
|
|
69
|
+
dropped. 10-bit sources throw with a clear error. rm/rmvb, WebM output,
|
|
70
|
+
and multi-track output remain on the roadmap.
|
|
71
|
+
- Shared `src/util/libav-demux.ts` helper (`openLibavDemux`,
|
|
72
|
+
`sanitizePacketTimestamp`, `sanitizeFrameTimestamp`,
|
|
73
|
+
`libavFrameToInterleavedFloat32`). Phase 1 only consumed by the new
|
|
74
|
+
transcode path; hybrid/fallback/remux keep their own copies and migrate
|
|
75
|
+
in a follow-up.
|
|
76
|
+
- New error codes: `ERR_AVBRIDGE_TRANSCODE_ABORTED`,
|
|
77
|
+
`ERR_AVBRIDGE_TRANSCODE_UNSUPPORTED_COMBO`,
|
|
78
|
+
`ERR_AVBRIDGE_TRANSCODE_DECODE`, `ERR_AVBRIDGE_CONTAINER_NOT_SUPPORTED`.
|
|
79
|
+
|
|
80
|
+
## [2.3.0]
|
|
81
|
+
|
|
82
|
+
This release makes avbridge.js production-ready for authenticated
|
|
83
|
+
remote media, legacy codecs, and end-user embedding.
|
|
84
|
+
|
|
85
|
+
### Added
|
|
86
|
+
|
|
87
|
+
- **`<avbridge-player>` controls-bearing element** (new subpath:
|
|
88
|
+
`avbridge/player`). Full YouTube-style player UI — play/pause, seek
|
|
89
|
+
bar, time display, volume/mute, settings menu (playback speed,
|
|
90
|
+
subtitle + audio tracks, Stats for Nerds), fullscreen. Auto-hide
|
|
91
|
+
controls, keyboard shortcuts (space/k, f, m, j/l, arrows, >/<, Esc),
|
|
92
|
+
touch gestures (tap-to-toggle, double-tap ±10s with ripple,
|
|
93
|
+
tap-and-hold for 2x speed). `::part()` hooks on every control.
|
|
94
|
+
- **Transport configurability** — `requestInit` and `fetchFn` on
|
|
95
|
+
`CreatePlayerOptions`; `probe()` takes a transport argument.
|
|
96
|
+
Threaded through probe Range requests, subtitle fetches, and the
|
|
97
|
+
libav HTTP reader. Unblocks signed URLs and custom auth headers.
|
|
98
|
+
- **Bitstream fixups** — `mpeg4_unpack_bframes` BSF wired into the
|
|
99
|
+
fallback and hybrid decoders for DivX packed-B-frame files.
|
|
100
|
+
Annex B → AVCC normalization in the libav remux path. Applied
|
|
101
|
+
filters visible in diagnostics as `bsfApplied`.
|
|
102
|
+
- **Structured errors** — new `AvbridgeError` class with
|
|
103
|
+
machine-readable `code` (`ERR_AVBRIDGE_*`) and human-readable
|
|
104
|
+
`recovery` hints. Applied to probe, codec, MSE, player-readiness,
|
|
105
|
+
and strategy-exhaustion paths.
|
|
106
|
+
- **DTS, TrueHD, Theora decoder support** in the custom libav
|
|
107
|
+
variant. Probe re-runs via libav when mediabunny returns unknown
|
|
108
|
+
codecs. Hybrid strategy now used for "native video + fallback
|
|
109
|
+
audio" combos (e.g. H.264 + DTS in Blu-ray MKV rips) instead of
|
|
110
|
+
the much slower full WASM fallback.
|
|
111
|
+
- **Streaming transcode output** via `outputStream` option. Pairs
|
|
112
|
+
with `showSaveFilePicker()` for multi-GB file transcoding without
|
|
113
|
+
loading the entire output into memory.
|
|
114
|
+
- **Background tab pause/resume** — auto-pause on `visibilitychange`
|
|
115
|
+
when hidden, auto-resume on return. Prevents degraded playback
|
|
116
|
+
from Chrome's rAF/setTimeout throttling. Configurable via
|
|
117
|
+
`backgroundBehavior: "pause" | "continue"`.
|
|
118
|
+
- **GitHub Pages demo** — deployed at `keishi.github.io/avbridge/`.
|
|
119
|
+
COOP/COEP service worker enables SharedArrayBuffer on static
|
|
120
|
+
hosting.
|
|
121
|
+
- **Consolidated rebrand** — public-facing name is now "avbridge.js"
|
|
122
|
+
across README, docs, demos. npm package name unchanged (`avbridge`).
|
|
123
|
+
|
|
124
|
+
### Fixed
|
|
125
|
+
|
|
126
|
+
- **A/V sync for long-running hybrid playback** — video PTS and
|
|
127
|
+
`AudioContext.currentTime` drift ~7ms/s (different clock domains).
|
|
128
|
+
Now periodically re-snaps calibration every 10 seconds, keeping
|
|
129
|
+
max drift under 70ms (below human lip-sync threshold). See
|
|
130
|
+
`docs/dev/POSTMORTEMS.md` for the full investigation.
|
|
131
|
+
- **Hybrid pump ordering** — audio decoded before video, with
|
|
132
|
+
sub-batch yields during heavy audio decode (DTS) to prevent rAF
|
|
133
|
+
starvation that caused visible stutter.
|
|
134
|
+
- **Probe regression** for MKV files with unrecognized codecs —
|
|
135
|
+
now falls back to libav probe instead of returning "unknown".
|
|
136
|
+
- **Variant picker** rewritten to use an allowlist (codecs
|
|
137
|
+
webcodecs variant can handle) instead of a denylist. New codecs
|
|
138
|
+
automatically route to the avbridge variant.
|
|
139
|
+
- **Hybrid + fallback preserve HTMLMediaElement contract** —
|
|
140
|
+
dispatch standard `play`/`pause`/`volumechange` events and patch
|
|
141
|
+
`target.volume`/`muted` as getter/setters so `<avbridge-player>`'s
|
|
142
|
+
controls reflect real state.
|
|
143
|
+
- **Seek bar click position** — custom pointer handler replaces
|
|
144
|
+
native range-input click math, eliminating the thumb-vs-cursor
|
|
145
|
+
offset at the track edges.
|
|
146
|
+
|
|
147
|
+
### Changed
|
|
148
|
+
|
|
149
|
+
- Classification now routes native-video + fallback-audio combos
|
|
150
|
+
to hybrid (WebCodecs video + libav audio) instead of full
|
|
151
|
+
fallback. Previously a Blu-ray MKV with H.264 + DTS went straight
|
|
152
|
+
to WASM software decode (unwatchable at 1080p).
|
|
153
|
+
- Annex B → AVCC conversion is now applied during libav remux to
|
|
154
|
+
produce correct fMP4 output.
|
|
155
|
+
- 269 unit tests across 17 files (up from 119 across 9). Three
|
|
156
|
+
testing tiers documented: unit (vitest + jsdom), browser
|
|
157
|
+
integration (Puppeteer), and strategy-to-element contract.
|
|
158
|
+
|
|
7
159
|
## [2.2.1]
|
|
8
160
|
|
|
9
161
|
### Fixed
|
package/NOTICE.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Third-party notices
|
|
2
2
|
|
|
3
|
-
avbridge is distributed under the MIT License (see [LICENSE](./LICENSE)).
|
|
3
|
+
avbridge.js is distributed under the MIT License (see [LICENSE](./LICENSE)).
|
|
4
4
|
|
|
5
5
|
This package **bundles binary builds** of third-party libraries. Each
|
|
6
6
|
bundled library retains its original license, which you must honor if you
|
|
7
|
-
redistribute avbridge or works that include it.
|
|
7
|
+
redistribute avbridge.js or works that include it.
|
|
8
8
|
|
|
9
9
|
See [THIRD_PARTY_LICENSES.md](./THIRD_PARTY_LICENSES.md) for the full
|
|
10
10
|
license texts. This file is a short index.
|
package/README.md
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
# avbridge
|
|
1
|
+
# avbridge.js
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/avbridge)
|
|
4
|
-
[](https://bundlephobia.com/package/avbridge)
|
|
5
4
|
[](./LICENSE)
|
|
6
5
|
[](https://github.com/keishi/avbridge/actions/workflows/ci.yml)
|
|
7
6
|
|
|
8
|
-
> **Play and convert arbitrary video files
|
|
7
|
+
> **VLC-style media playback for the browser. Play and convert arbitrary video files — local or remote.**
|
|
9
8
|
|
|
10
9
|
A media compatibility layer for the web. Drop in any file — MP4, MKV, AVI,
|
|
11
10
|
WMV, FLV, MPEG-TS, DivX, RMVB — and avbridge picks the best path: native
|
package/THIRD_PARTY_LICENSES.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Third-party license texts
|
|
2
2
|
|
|
3
3
|
This document contains the full license text of every third-party
|
|
4
|
-
component bundled with avbridge. The short overview lives in
|
|
4
|
+
component bundled with avbridge.js. The short overview lives in
|
|
5
5
|
[NOTICE.md](./NOTICE.md); this file is what you need if you redistribute
|
|
6
|
-
avbridge and have to comply with the underlying licenses.
|
|
6
|
+
avbridge.js and have to comply with the underlying licenses.
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { loadLibav } from './chunk-IAYKFGFG.js';
|
|
2
|
+
import { prepareLibavInput } from './chunk-DCSOQH2N.js';
|
|
3
|
+
|
|
4
|
+
// src/probe/avi.ts
|
|
5
|
+
async function probeWithLibav(source, sniffed) {
|
|
6
|
+
const libav = await loadLibav("avbridge");
|
|
7
|
+
const filename = source.name ?? `input.${sniffed === "unknown" ? "bin" : sniffed}`;
|
|
8
|
+
const handle = await prepareLibavInput(libav, filename, source);
|
|
9
|
+
let fmt_ctx;
|
|
10
|
+
let streams = [];
|
|
11
|
+
try {
|
|
12
|
+
const result = await libav.ff_init_demuxer_file(filename);
|
|
13
|
+
fmt_ctx = result[0];
|
|
14
|
+
streams = result[1];
|
|
15
|
+
} catch (err) {
|
|
16
|
+
await handle.detach().catch(() => {
|
|
17
|
+
});
|
|
18
|
+
const inner = err instanceof Error ? err.message : typeof err === "object" && err !== null ? JSON.stringify(err) : String(err);
|
|
19
|
+
console.error("[avbridge] ff_init_demuxer_file raw error:", err);
|
|
20
|
+
throw new Error(
|
|
21
|
+
`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 \u2014 see console for raw error"})`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
const videoTracks = [];
|
|
25
|
+
const audioTracks = [];
|
|
26
|
+
for (const stream of streams) {
|
|
27
|
+
const codecName = await safe(() => libav.avcodec_get_name(stream.codec_id)) ?? `unknown(${stream.codec_id})`;
|
|
28
|
+
const codecpar = await safe(() => libav.ff_copyout_codecpar(stream.codecpar));
|
|
29
|
+
if (stream.codec_type === libav.AVMEDIA_TYPE_VIDEO) {
|
|
30
|
+
videoTracks.push({
|
|
31
|
+
id: stream.index,
|
|
32
|
+
codec: ffmpegToAvbridgeVideo(codecName),
|
|
33
|
+
width: codecpar?.width ?? 0,
|
|
34
|
+
height: codecpar?.height ?? 0,
|
|
35
|
+
fps: framerate(stream)
|
|
36
|
+
});
|
|
37
|
+
} else if (stream.codec_type === libav.AVMEDIA_TYPE_AUDIO) {
|
|
38
|
+
audioTracks.push({
|
|
39
|
+
id: stream.index,
|
|
40
|
+
codec: ffmpegToAvbridgeAudio(codecName),
|
|
41
|
+
channels: codecpar?.channels ?? codecpar?.ch_layout_nb_channels ?? 0,
|
|
42
|
+
sampleRate: codecpar?.sample_rate ?? 0
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const duration = await safeDuration(libav, fmt_ctx);
|
|
47
|
+
await libav.avformat_close_input_js(fmt_ctx).catch(() => {
|
|
48
|
+
});
|
|
49
|
+
await handle.detach().catch(() => {
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
source: source.original,
|
|
53
|
+
name: source.name,
|
|
54
|
+
byteLength: source.byteLength,
|
|
55
|
+
container: sniffed === "unknown" ? "unknown" : sniffed,
|
|
56
|
+
videoTracks,
|
|
57
|
+
audioTracks,
|
|
58
|
+
subtitleTracks: [],
|
|
59
|
+
probedBy: "libav",
|
|
60
|
+
duration
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function framerate(stream) {
|
|
64
|
+
if (typeof stream.avg_frame_rate_num === "number" && stream.avg_frame_rate_den) {
|
|
65
|
+
return stream.avg_frame_rate_num / stream.avg_frame_rate_den;
|
|
66
|
+
}
|
|
67
|
+
if (stream.avg_frame_rate && typeof stream.avg_frame_rate === "object") {
|
|
68
|
+
if (stream.avg_frame_rate.den === 0) return void 0;
|
|
69
|
+
return stream.avg_frame_rate.num / stream.avg_frame_rate.den;
|
|
70
|
+
}
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
async function safeDuration(libav, fmt_ctx) {
|
|
74
|
+
try {
|
|
75
|
+
const lo = await libav.AVFormatContext_duration?.(fmt_ctx);
|
|
76
|
+
const hi = await libav.AVFormatContext_durationhi?.(fmt_ctx);
|
|
77
|
+
if (typeof lo !== "number" || typeof hi !== "number") return void 0;
|
|
78
|
+
if (hi === -2147483648 && lo === 0) return void 0;
|
|
79
|
+
const us = typeof libav.i64tof64 === "function" ? libav.i64tof64(lo, hi) : hi * 4294967296 + lo + (lo < 0 ? 4294967296 : 0);
|
|
80
|
+
if (!Number.isFinite(us) || us <= 0) return void 0;
|
|
81
|
+
return us / 1e6;
|
|
82
|
+
} catch {
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async function safe(fn) {
|
|
87
|
+
try {
|
|
88
|
+
return await fn();
|
|
89
|
+
} catch {
|
|
90
|
+
return void 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function ffmpegToAvbridgeVideo(name) {
|
|
94
|
+
switch (name) {
|
|
95
|
+
case "h264":
|
|
96
|
+
return "h264";
|
|
97
|
+
case "hevc":
|
|
98
|
+
return "h265";
|
|
99
|
+
case "vp8":
|
|
100
|
+
return "vp8";
|
|
101
|
+
case "vp9":
|
|
102
|
+
return "vp9";
|
|
103
|
+
case "av1":
|
|
104
|
+
return "av1";
|
|
105
|
+
case "mpeg4":
|
|
106
|
+
return "mpeg4";
|
|
107
|
+
// MPEG-4 Part 2 / DivX / Xvid
|
|
108
|
+
case "msmpeg4v1":
|
|
109
|
+
case "msmpeg4v2":
|
|
110
|
+
case "msmpeg4v3":
|
|
111
|
+
return "mpeg4";
|
|
112
|
+
case "wmv1":
|
|
113
|
+
case "wmv2":
|
|
114
|
+
case "wmv3":
|
|
115
|
+
return "wmv3";
|
|
116
|
+
case "vc1":
|
|
117
|
+
return "vc1";
|
|
118
|
+
case "mpeg2video":
|
|
119
|
+
return "mpeg2";
|
|
120
|
+
case "mpeg1video":
|
|
121
|
+
return "mpeg1";
|
|
122
|
+
case "theora":
|
|
123
|
+
return "theora";
|
|
124
|
+
case "rv10":
|
|
125
|
+
return "rv10";
|
|
126
|
+
case "rv20":
|
|
127
|
+
return "rv20";
|
|
128
|
+
case "rv30":
|
|
129
|
+
return "rv30";
|
|
130
|
+
case "rv40":
|
|
131
|
+
return "rv40";
|
|
132
|
+
default:
|
|
133
|
+
return name;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function ffmpegToAvbridgeAudio(name) {
|
|
137
|
+
switch (name) {
|
|
138
|
+
case "aac":
|
|
139
|
+
return "aac";
|
|
140
|
+
case "mp3":
|
|
141
|
+
case "mp3float":
|
|
142
|
+
return "mp3";
|
|
143
|
+
case "opus":
|
|
144
|
+
return "opus";
|
|
145
|
+
case "vorbis":
|
|
146
|
+
return "vorbis";
|
|
147
|
+
case "flac":
|
|
148
|
+
return "flac";
|
|
149
|
+
case "ac3":
|
|
150
|
+
return "ac3";
|
|
151
|
+
case "eac3":
|
|
152
|
+
return "eac3";
|
|
153
|
+
case "wmav1":
|
|
154
|
+
case "wmav2":
|
|
155
|
+
return "wmav2";
|
|
156
|
+
case "wmapro":
|
|
157
|
+
return "wmapro";
|
|
158
|
+
case "alac":
|
|
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";
|
|
170
|
+
case "dca":
|
|
171
|
+
case "dts":
|
|
172
|
+
return "dts";
|
|
173
|
+
case "truehd":
|
|
174
|
+
case "mlp":
|
|
175
|
+
return "truehd";
|
|
176
|
+
default:
|
|
177
|
+
return name;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export { probeWithLibav };
|
|
182
|
+
//# sourceMappingURL=avi-2JPBSHGA.js.map
|
|
183
|
+
//# sourceMappingURL=avi-2JPBSHGA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/probe/avi.ts"],"names":[],"mappings":"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","file":"avi-2JPBSHGA.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 case \"dca\":\n case \"dts\": return \"dts\";\n case \"truehd\":\n case \"mlp\": return \"truehd\";\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"]}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkNNVOHKXJ_cjs = require('./chunk-NNVOHKXJ.cjs');
|
|
4
|
+
var chunkZ33SBWL5_cjs = require('./chunk-Z33SBWL5.cjs');
|
|
5
|
+
|
|
6
|
+
// src/probe/avi.ts
|
|
7
|
+
async function probeWithLibav(source, sniffed) {
|
|
8
|
+
const libav = await chunkNNVOHKXJ_cjs.loadLibav("avbridge");
|
|
9
|
+
const filename = source.name ?? `input.${sniffed === "unknown" ? "bin" : sniffed}`;
|
|
10
|
+
const handle = await chunkZ33SBWL5_cjs.prepareLibavInput(libav, filename, source);
|
|
11
|
+
let fmt_ctx;
|
|
12
|
+
let streams = [];
|
|
13
|
+
try {
|
|
14
|
+
const result = await libav.ff_init_demuxer_file(filename);
|
|
15
|
+
fmt_ctx = result[0];
|
|
16
|
+
streams = result[1];
|
|
17
|
+
} catch (err) {
|
|
18
|
+
await handle.detach().catch(() => {
|
|
19
|
+
});
|
|
20
|
+
const inner = err instanceof Error ? err.message : typeof err === "object" && err !== null ? JSON.stringify(err) : String(err);
|
|
21
|
+
console.error("[avbridge] ff_init_demuxer_file raw error:", err);
|
|
22
|
+
throw new Error(
|
|
23
|
+
`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 \u2014 see console for raw error"})`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
const videoTracks = [];
|
|
27
|
+
const audioTracks = [];
|
|
28
|
+
for (const stream of streams) {
|
|
29
|
+
const codecName = await safe(() => libav.avcodec_get_name(stream.codec_id)) ?? `unknown(${stream.codec_id})`;
|
|
30
|
+
const codecpar = await safe(() => libav.ff_copyout_codecpar(stream.codecpar));
|
|
31
|
+
if (stream.codec_type === libav.AVMEDIA_TYPE_VIDEO) {
|
|
32
|
+
videoTracks.push({
|
|
33
|
+
id: stream.index,
|
|
34
|
+
codec: ffmpegToAvbridgeVideo(codecName),
|
|
35
|
+
width: codecpar?.width ?? 0,
|
|
36
|
+
height: codecpar?.height ?? 0,
|
|
37
|
+
fps: framerate(stream)
|
|
38
|
+
});
|
|
39
|
+
} else if (stream.codec_type === libav.AVMEDIA_TYPE_AUDIO) {
|
|
40
|
+
audioTracks.push({
|
|
41
|
+
id: stream.index,
|
|
42
|
+
codec: ffmpegToAvbridgeAudio(codecName),
|
|
43
|
+
channels: codecpar?.channels ?? codecpar?.ch_layout_nb_channels ?? 0,
|
|
44
|
+
sampleRate: codecpar?.sample_rate ?? 0
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const duration = await safeDuration(libav, fmt_ctx);
|
|
49
|
+
await libav.avformat_close_input_js(fmt_ctx).catch(() => {
|
|
50
|
+
});
|
|
51
|
+
await handle.detach().catch(() => {
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
source: source.original,
|
|
55
|
+
name: source.name,
|
|
56
|
+
byteLength: source.byteLength,
|
|
57
|
+
container: sniffed === "unknown" ? "unknown" : sniffed,
|
|
58
|
+
videoTracks,
|
|
59
|
+
audioTracks,
|
|
60
|
+
subtitleTracks: [],
|
|
61
|
+
probedBy: "libav",
|
|
62
|
+
duration
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function framerate(stream) {
|
|
66
|
+
if (typeof stream.avg_frame_rate_num === "number" && stream.avg_frame_rate_den) {
|
|
67
|
+
return stream.avg_frame_rate_num / stream.avg_frame_rate_den;
|
|
68
|
+
}
|
|
69
|
+
if (stream.avg_frame_rate && typeof stream.avg_frame_rate === "object") {
|
|
70
|
+
if (stream.avg_frame_rate.den === 0) return void 0;
|
|
71
|
+
return stream.avg_frame_rate.num / stream.avg_frame_rate.den;
|
|
72
|
+
}
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
async function safeDuration(libav, fmt_ctx) {
|
|
76
|
+
try {
|
|
77
|
+
const lo = await libav.AVFormatContext_duration?.(fmt_ctx);
|
|
78
|
+
const hi = await libav.AVFormatContext_durationhi?.(fmt_ctx);
|
|
79
|
+
if (typeof lo !== "number" || typeof hi !== "number") return void 0;
|
|
80
|
+
if (hi === -2147483648 && lo === 0) return void 0;
|
|
81
|
+
const us = typeof libav.i64tof64 === "function" ? libav.i64tof64(lo, hi) : hi * 4294967296 + lo + (lo < 0 ? 4294967296 : 0);
|
|
82
|
+
if (!Number.isFinite(us) || us <= 0) return void 0;
|
|
83
|
+
return us / 1e6;
|
|
84
|
+
} catch {
|
|
85
|
+
return void 0;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function safe(fn) {
|
|
89
|
+
try {
|
|
90
|
+
return await fn();
|
|
91
|
+
} catch {
|
|
92
|
+
return void 0;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function ffmpegToAvbridgeVideo(name) {
|
|
96
|
+
switch (name) {
|
|
97
|
+
case "h264":
|
|
98
|
+
return "h264";
|
|
99
|
+
case "hevc":
|
|
100
|
+
return "h265";
|
|
101
|
+
case "vp8":
|
|
102
|
+
return "vp8";
|
|
103
|
+
case "vp9":
|
|
104
|
+
return "vp9";
|
|
105
|
+
case "av1":
|
|
106
|
+
return "av1";
|
|
107
|
+
case "mpeg4":
|
|
108
|
+
return "mpeg4";
|
|
109
|
+
// MPEG-4 Part 2 / DivX / Xvid
|
|
110
|
+
case "msmpeg4v1":
|
|
111
|
+
case "msmpeg4v2":
|
|
112
|
+
case "msmpeg4v3":
|
|
113
|
+
return "mpeg4";
|
|
114
|
+
case "wmv1":
|
|
115
|
+
case "wmv2":
|
|
116
|
+
case "wmv3":
|
|
117
|
+
return "wmv3";
|
|
118
|
+
case "vc1":
|
|
119
|
+
return "vc1";
|
|
120
|
+
case "mpeg2video":
|
|
121
|
+
return "mpeg2";
|
|
122
|
+
case "mpeg1video":
|
|
123
|
+
return "mpeg1";
|
|
124
|
+
case "theora":
|
|
125
|
+
return "theora";
|
|
126
|
+
case "rv10":
|
|
127
|
+
return "rv10";
|
|
128
|
+
case "rv20":
|
|
129
|
+
return "rv20";
|
|
130
|
+
case "rv30":
|
|
131
|
+
return "rv30";
|
|
132
|
+
case "rv40":
|
|
133
|
+
return "rv40";
|
|
134
|
+
default:
|
|
135
|
+
return name;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function ffmpegToAvbridgeAudio(name) {
|
|
139
|
+
switch (name) {
|
|
140
|
+
case "aac":
|
|
141
|
+
return "aac";
|
|
142
|
+
case "mp3":
|
|
143
|
+
case "mp3float":
|
|
144
|
+
return "mp3";
|
|
145
|
+
case "opus":
|
|
146
|
+
return "opus";
|
|
147
|
+
case "vorbis":
|
|
148
|
+
return "vorbis";
|
|
149
|
+
case "flac":
|
|
150
|
+
return "flac";
|
|
151
|
+
case "ac3":
|
|
152
|
+
return "ac3";
|
|
153
|
+
case "eac3":
|
|
154
|
+
return "eac3";
|
|
155
|
+
case "wmav1":
|
|
156
|
+
case "wmav2":
|
|
157
|
+
return "wmav2";
|
|
158
|
+
case "wmapro":
|
|
159
|
+
return "wmapro";
|
|
160
|
+
case "alac":
|
|
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";
|
|
172
|
+
case "dca":
|
|
173
|
+
case "dts":
|
|
174
|
+
return "dts";
|
|
175
|
+
case "truehd":
|
|
176
|
+
case "mlp":
|
|
177
|
+
return "truehd";
|
|
178
|
+
default:
|
|
179
|
+
return name;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
exports.probeWithLibav = probeWithLibav;
|
|
184
|
+
//# sourceMappingURL=avi-F6WZJK5T.cjs.map
|
|
185
|
+
//# sourceMappingURL=avi-F6WZJK5T.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/probe/avi.ts"],"names":["loadLibav","prepareLibavInput"],"mappings":"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","file":"avi-F6WZJK5T.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 case \"dca\":\n case \"dts\": return \"dts\";\n case \"truehd\":\n case \"mlp\": return \"truehd\";\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"]}
|