avbridge 2.12.0 → 2.12.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.
- package/CHANGELOG.md +76 -0
- package/dist/{avi-EQE6AR75.cjs → avi-32UABODO.cjs} +12 -4
- package/dist/avi-32UABODO.cjs.map +1 -0
- package/dist/{avi-Y3N325WZ.cjs → avi-5BPR6QUX.cjs} +12 -4
- package/dist/avi-5BPR6QUX.cjs.map +1 -0
- package/dist/{avi-NNHH4AAA.js → avi-BLIH7KKV.js} +12 -4
- package/dist/avi-BLIH7KKV.js.map +1 -0
- package/dist/{avi-S7EY54YA.js → avi-GX2H34IQ.js} +12 -4
- package/dist/avi-GX2H34IQ.js.map +1 -0
- package/dist/{chunk-2LNXMGT6.js → chunk-5CX7BVVV.js} +5 -5
- package/dist/{chunk-2LNXMGT6.js.map → chunk-5CX7BVVV.js.map} +1 -1
- package/dist/{chunk-5Y5BTB5D.js → chunk-B76QWPFM.js} +3 -3
- package/dist/{chunk-5Y5BTB5D.js.map → chunk-B76QWPFM.js.map} +1 -1
- package/dist/{chunk-Z26PXRUY.js → chunk-BN7BRTLY.js} +137 -26
- package/dist/chunk-BN7BRTLY.js.map +1 -0
- package/dist/{chunk-GJBNLPGI.cjs → chunk-E5MAM2P4.cjs} +9 -9
- package/dist/{chunk-GJBNLPGI.cjs.map → chunk-E5MAM2P4.cjs.map} +1 -1
- package/dist/{chunk-7EF4VTUS.cjs → chunk-UM6WCSGL.cjs} +141 -30
- package/dist/chunk-UM6WCSGL.cjs.map +1 -0
- package/dist/{chunk-HBHSUGNI.cjs → chunk-VLI3Y6IJ.cjs} +5 -5
- package/dist/{chunk-HBHSUGNI.cjs.map → chunk-VLI3Y6IJ.cjs.map} +1 -1
- package/dist/element-browser.js +144 -25
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +3 -3
- package/dist/element.js +2 -2
- package/dist/index.cjs +18 -18
- package/dist/index.js +6 -6
- package/dist/player.cjs +207 -41
- package/dist/player.cjs.map +1 -1
- package/dist/player.d.cts +1 -0
- package/dist/player.d.ts +1 -0
- package/dist/player.js +207 -41
- package/dist/player.js.map +1 -1
- package/dist/{remux-VPKCLHHM.cjs → remux-NSBJFMLG.cjs} +9 -9
- package/dist/{remux-VPKCLHHM.cjs.map → remux-NSBJFMLG.cjs.map} +1 -1
- package/dist/{remux-7TA4FKTY.js → remux-PHUHO3VV.js} +4 -4
- package/dist/{remux-7TA4FKTY.js.map → remux-PHUHO3VV.js.map} +1 -1
- package/package.json +1 -1
- package/src/element/avbridge-player.ts +87 -15
- package/src/probe/avi.ts +34 -2
- package/src/strategies/fallback/decoder.ts +148 -19
- package/src/strategies/fallback/video-renderer.ts +41 -3
- package/src/strategies/hybrid/decoder.ts +34 -9
- 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/vendor/libav/avbridge/libav-avbridge.mjs +1 -1
- package/dist/avi-EQE6AR75.cjs.map +0 -1
- package/dist/avi-NNHH4AAA.js.map +0 -1
- package/dist/avi-S7EY54YA.js.map +0 -1
- package/dist/avi-Y3N325WZ.cjs.map +0 -1
- package/dist/chunk-7EF4VTUS.cjs.map +0 -1
- package/dist/chunk-Z26PXRUY.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,82 @@ 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.12.1]
|
|
8
|
+
|
|
9
|
+
DivX/Xvid AVI playback reliability — four latent bugs fixed. Content
|
|
10
|
+
that had been stuttering and freezing for months now plays smoothly
|
|
11
|
+
and seeks cleanly.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Stats-for-Nerds overhaul** with live deltas: per-second decode
|
|
16
|
+
fps (% of realtime), paint fps, drops/sec, decode-ms breakdown
|
|
17
|
+
(avg/batch, % of wall, slowest), producer throttle %, queue span
|
|
18
|
+
+ head/tail PTS, newest-decoded PTS, and explicit `PTS REGRESSIONS`
|
|
19
|
+
/ `BSF MISSING` warning lines. Designed to answer "why is playback
|
|
20
|
+
stuttering" at a glance.
|
|
21
|
+
- **Decoder throughput instrumentation** in the fallback pipeline:
|
|
22
|
+
`videoDecodeMsTotal`, `videoDecodeBatches`, `audioDecodeMsTotal`,
|
|
23
|
+
`audioDecodeBatches`, `readMsTotal`, `pumpThrottleMsTotal`,
|
|
24
|
+
`slowestVideoBatchMs`, `newestVideoPtsMs`, `ptsRegressions`,
|
|
25
|
+
`worstPtsRegressionMs` all exposed in `getDiagnostics().runtime`.
|
|
26
|
+
- **`avbsf` fragment** in `scripts/build-libav.sh` + libav variant
|
|
27
|
+
rebuild. The BSF C code (`bsf-mpeg4_unpack_bframes`) was shipped
|
|
28
|
+
since 2.2.0 but the JS wrappers it needs
|
|
29
|
+
(`av_bsf_list_parse_str_js`, `av_bsf_init`, `av_bsf_send_packet`,
|
|
30
|
+
`av_bsf_receive_packet`) were never exported — the fixup was dead
|
|
31
|
+
at runtime. The BSF now actually runs, and its absence (if ever
|
|
32
|
+
rebuilt without `avbsf`) surfaces as `bsfMissing` in diagnostics
|
|
33
|
+
and a loud `console.error`.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- **DivX/Xvid AVI stuttering** — large, clearly visible frame drops
|
|
38
|
+
(42 % of frames on a typical 25 fps 624×352 episode). Three
|
|
39
|
+
independent bugs stacked; all fixed now:
|
|
40
|
+
1. *Synthetic PTS counter ignored valid neighbors.* When libav
|
|
41
|
+
emitted a frame with `AV_NOPTS_VALUE`, the fallback callback
|
|
42
|
+
assigned it a timestamp from a counter that started at 0 and
|
|
43
|
+
only advanced on invalid frames. At minute 5 of playback, one
|
|
44
|
+
bad frame would be tagged as PTS ≈ 0, jumping the renderer
|
|
45
|
+
wildly backwards. Now anchored to the last emitted frame's
|
|
46
|
+
PTS + one frame step, so invalid frames interpolate cleanly.
|
|
47
|
+
2. *`mpeg4_unpack_bframes` BSF was compiled but unreachable from
|
|
48
|
+
JS* (see `avbsf` fragment addition above).
|
|
49
|
+
3. *FPS probe gap for AVI.* `avg_frame_rate_num/_den` don't exist
|
|
50
|
+
as properties on libav.js stream records; they must be read
|
|
51
|
+
via `AVCodecParameters_framerate_num/_den`. Every AVI was
|
|
52
|
+
falling back to a 30 fps default, narrowing the renderer's
|
|
53
|
+
tolerance window and pushing 25 fps content off-cadence. Now
|
|
54
|
+
reads the real rate.
|
|
55
|
+
- **Seek freeze in the fallback strategy** — video stayed on the
|
|
56
|
+
pre-seek frame while audio continued from the new position. Root
|
|
57
|
+
cause: `hasFrames()` checked `framesPainted > 0`, which is
|
|
58
|
+
cumulative, so `waitForBuffer()` returned immediately after flush
|
|
59
|
+
even with an empty queue; audio started before any post-seek
|
|
60
|
+
frame had been decoded. Fixed with a `hasEverEnqueuedSinceFlush`
|
|
61
|
+
flag that resets on every `flush()`.
|
|
62
|
+
- **`DataCloneError` after seek** — the post-seek pump hammered the
|
|
63
|
+
console with `An ArrayBuffer is detached and could not be cloned`
|
|
64
|
+
on every decode batch. `flushBSF()` was sending a NULL packet as
|
|
65
|
+
its flush signal, but NULL is the EOF signal — it locked the BSF
|
|
66
|
+
into EOF state, so every subsequent `av_bsf_send_packet` failed
|
|
67
|
+
and `applyBSF` fell through to pushing the original input packet,
|
|
68
|
+
whose buffer had already been transferred to WASM by
|
|
69
|
+
`ff_copyin_packet`. Now uses `av_bsf_flush` (the actual flush
|
|
70
|
+
API) and defensively drops rejected packets instead of passing
|
|
71
|
+
their detached buffers through to the decoder.
|
|
72
|
+
- **Post-seek out-of-order frames from the mpeg4 decoder** —
|
|
73
|
+
`avcodec_flush_buffers` doesn't always clear the B-frame reorder
|
|
74
|
+
tail on mpeg4, so the first post-seek batch could contain frames
|
|
75
|
+
from before the seek mixed with frames from after, in arbitrary
|
|
76
|
+
order. The renderer's paint loop assumes monotonic queue and
|
|
77
|
+
breaks (head stuck, newer frames age out to late-drop) when that
|
|
78
|
+
invariant fails. The decoder now drops any frame whose PTS is
|
|
79
|
+
less than the previously emitted frame, with a console warning.
|
|
80
|
+
Counter resets at every seek so a legitimate large jump in PTS
|
|
81
|
+
after seek is always accepted.
|
|
82
|
+
|
|
7
83
|
## [2.12.0]
|
|
8
84
|
|
|
9
85
|
Network playback performance + seek-bar polish.
|
|
@@ -34,7 +34,7 @@ async function probeWithLibav(source, sniffed) {
|
|
|
34
34
|
codec: ffmpegToAvbridgeVideo(codecName),
|
|
35
35
|
width: codecpar?.width ?? 0,
|
|
36
36
|
height: codecpar?.height ?? 0,
|
|
37
|
-
fps: framerate(stream)
|
|
37
|
+
fps: await framerate(libav, stream)
|
|
38
38
|
});
|
|
39
39
|
} else if (stream.codec_type === libav.AVMEDIA_TYPE_AUDIO) {
|
|
40
40
|
audioTracks.push({
|
|
@@ -62,7 +62,7 @@ async function probeWithLibav(source, sniffed) {
|
|
|
62
62
|
duration
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
-
function framerate(stream) {
|
|
65
|
+
async function framerate(libav, stream) {
|
|
66
66
|
if (typeof stream.avg_frame_rate_num === "number" && stream.avg_frame_rate_den) {
|
|
67
67
|
return stream.avg_frame_rate_num / stream.avg_frame_rate_den;
|
|
68
68
|
}
|
|
@@ -70,6 +70,14 @@ function framerate(stream) {
|
|
|
70
70
|
if (stream.avg_frame_rate.den === 0) return void 0;
|
|
71
71
|
return stream.avg_frame_rate.num / stream.avg_frame_rate.den;
|
|
72
72
|
}
|
|
73
|
+
try {
|
|
74
|
+
const num = await libav.AVCodecParameters_framerate_num?.(stream.codecpar);
|
|
75
|
+
const den = await libav.AVCodecParameters_framerate_den?.(stream.codecpar);
|
|
76
|
+
if (typeof num === "number" && typeof den === "number" && den > 0 && num > 0) {
|
|
77
|
+
return num / den;
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
73
81
|
return void 0;
|
|
74
82
|
}
|
|
75
83
|
async function safeDuration(libav, fmt_ctx) {
|
|
@@ -187,5 +195,5 @@ function ffmpegToAvbridgeAudio(name) {
|
|
|
187
195
|
}
|
|
188
196
|
|
|
189
197
|
exports.probeWithLibav = probeWithLibav;
|
|
190
|
-
//# sourceMappingURL=avi-
|
|
191
|
-
//# sourceMappingURL=avi-
|
|
198
|
+
//# sourceMappingURL=avi-32UABODO.cjs.map
|
|
199
|
+
//# sourceMappingURL=avi-32UABODO.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/probe/avi.ts"],"names":["loadLibav","prepareLibavInput"],"mappings":"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","file":"avi-32UABODO.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: await framerate(libav, 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\n/**\n * Read frame rate from the stream's `AVCodecParameters.framerate`.\n *\n * `avg_frame_rate` / `r_frame_rate` live on the AVStream in C but libav.js\n * doesn't expose them as JS properties on the stream record — only via\n * dedicated accessor functions we don't ship. `codecpar.framerate` IS\n * accessible via `AVCodecParameters_framerate_num/_den` and is populated\n * for most containers (for AVI it's parsed from `dwRate`/`dwScale` in the\n * stream header).\n *\n * Returns undefined if unavailable so the caller can fall back to a\n * container-appropriate default (currently 30 fps, which is wrong for\n * 25 fps PAL and 23.976 fps film content — hence the importance of\n * reading this correctly).\n */\nasync function framerate(\n libav: LibavInstance,\n stream: LibavStream,\n): Promise<number | undefined> {\n // The stream record may still carry these (new libav.js versions) —\n // prefer them when present.\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 try {\n const num = await libav.AVCodecParameters_framerate_num?.(stream.codecpar);\n const den = await libav.AVCodecParameters_framerate_den?.(stream.codecpar);\n if (typeof num === \"number\" && typeof den === \"number\" && den > 0 && num > 0) {\n return num / den;\n }\n } catch {\n // Fall through.\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 case \"dvvideo\": return \"dv\"; // DV / DVCPRO (camcorder, MiniDV)\n case \"hq_hqa\": return \"hq_hqa\"; // Canopus HQ / HQA (Grass Valley)\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 AVCodecParameters_framerate_num?(codecpar: number): Promise<number>;\n AVCodecParameters_framerate_den?(codecpar: number): Promise<number>;\n\n AVMEDIA_TYPE_VIDEO: number;\n AVMEDIA_TYPE_AUDIO: number;\n}\n"]}
|
|
@@ -34,7 +34,7 @@ async function probeWithLibav(source, sniffed) {
|
|
|
34
34
|
codec: ffmpegToAvbridgeVideo(codecName),
|
|
35
35
|
width: codecpar?.width ?? 0,
|
|
36
36
|
height: codecpar?.height ?? 0,
|
|
37
|
-
fps: framerate(stream)
|
|
37
|
+
fps: await framerate(libav, stream)
|
|
38
38
|
});
|
|
39
39
|
} else if (stream.codec_type === libav.AVMEDIA_TYPE_AUDIO) {
|
|
40
40
|
audioTracks.push({
|
|
@@ -62,7 +62,7 @@ async function probeWithLibav(source, sniffed) {
|
|
|
62
62
|
duration
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
-
function framerate(stream) {
|
|
65
|
+
async function framerate(libav, stream) {
|
|
66
66
|
if (typeof stream.avg_frame_rate_num === "number" && stream.avg_frame_rate_den) {
|
|
67
67
|
return stream.avg_frame_rate_num / stream.avg_frame_rate_den;
|
|
68
68
|
}
|
|
@@ -70,6 +70,14 @@ function framerate(stream) {
|
|
|
70
70
|
if (stream.avg_frame_rate.den === 0) return void 0;
|
|
71
71
|
return stream.avg_frame_rate.num / stream.avg_frame_rate.den;
|
|
72
72
|
}
|
|
73
|
+
try {
|
|
74
|
+
const num = await libav.AVCodecParameters_framerate_num?.(stream.codecpar);
|
|
75
|
+
const den = await libav.AVCodecParameters_framerate_den?.(stream.codecpar);
|
|
76
|
+
if (typeof num === "number" && typeof den === "number" && den > 0 && num > 0) {
|
|
77
|
+
return num / den;
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
73
81
|
return void 0;
|
|
74
82
|
}
|
|
75
83
|
async function safeDuration(libav, fmt_ctx) {
|
|
@@ -187,5 +195,5 @@ function ffmpegToAvbridgeAudio(name) {
|
|
|
187
195
|
}
|
|
188
196
|
|
|
189
197
|
exports.probeWithLibav = probeWithLibav;
|
|
190
|
-
//# sourceMappingURL=avi-
|
|
191
|
-
//# sourceMappingURL=avi-
|
|
198
|
+
//# sourceMappingURL=avi-5BPR6QUX.cjs.map
|
|
199
|
+
//# sourceMappingURL=avi-5BPR6QUX.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/probe/avi.ts"],"names":["loadLibav","prepareLibavInput"],"mappings":"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","file":"avi-5BPR6QUX.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: await framerate(libav, 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\n/**\n * Read frame rate from the stream's `AVCodecParameters.framerate`.\n *\n * `avg_frame_rate` / `r_frame_rate` live on the AVStream in C but libav.js\n * doesn't expose them as JS properties on the stream record — only via\n * dedicated accessor functions we don't ship. `codecpar.framerate` IS\n * accessible via `AVCodecParameters_framerate_num/_den` and is populated\n * for most containers (for AVI it's parsed from `dwRate`/`dwScale` in the\n * stream header).\n *\n * Returns undefined if unavailable so the caller can fall back to a\n * container-appropriate default (currently 30 fps, which is wrong for\n * 25 fps PAL and 23.976 fps film content — hence the importance of\n * reading this correctly).\n */\nasync function framerate(\n libav: LibavInstance,\n stream: LibavStream,\n): Promise<number | undefined> {\n // The stream record may still carry these (new libav.js versions) —\n // prefer them when present.\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 try {\n const num = await libav.AVCodecParameters_framerate_num?.(stream.codecpar);\n const den = await libav.AVCodecParameters_framerate_den?.(stream.codecpar);\n if (typeof num === \"number\" && typeof den === \"number\" && den > 0 && num > 0) {\n return num / den;\n }\n } catch {\n // Fall through.\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 case \"dvvideo\": return \"dv\"; // DV / DVCPRO (camcorder, MiniDV)\n case \"hq_hqa\": return \"hq_hqa\"; // Canopus HQ / HQA (Grass Valley)\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 AVCodecParameters_framerate_num?(codecpar: number): Promise<number>;\n AVCodecParameters_framerate_den?(codecpar: number): Promise<number>;\n\n AVMEDIA_TYPE_VIDEO: number;\n AVMEDIA_TYPE_AUDIO: number;\n}\n"]}
|
|
@@ -32,7 +32,7 @@ async function probeWithLibav(source, sniffed) {
|
|
|
32
32
|
codec: ffmpegToAvbridgeVideo(codecName),
|
|
33
33
|
width: codecpar?.width ?? 0,
|
|
34
34
|
height: codecpar?.height ?? 0,
|
|
35
|
-
fps: framerate(stream)
|
|
35
|
+
fps: await framerate(libav, stream)
|
|
36
36
|
});
|
|
37
37
|
} else if (stream.codec_type === libav.AVMEDIA_TYPE_AUDIO) {
|
|
38
38
|
audioTracks.push({
|
|
@@ -60,7 +60,7 @@ async function probeWithLibav(source, sniffed) {
|
|
|
60
60
|
duration
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
-
function framerate(stream) {
|
|
63
|
+
async function framerate(libav, stream) {
|
|
64
64
|
if (typeof stream.avg_frame_rate_num === "number" && stream.avg_frame_rate_den) {
|
|
65
65
|
return stream.avg_frame_rate_num / stream.avg_frame_rate_den;
|
|
66
66
|
}
|
|
@@ -68,6 +68,14 @@ function framerate(stream) {
|
|
|
68
68
|
if (stream.avg_frame_rate.den === 0) return void 0;
|
|
69
69
|
return stream.avg_frame_rate.num / stream.avg_frame_rate.den;
|
|
70
70
|
}
|
|
71
|
+
try {
|
|
72
|
+
const num = await libav.AVCodecParameters_framerate_num?.(stream.codecpar);
|
|
73
|
+
const den = await libav.AVCodecParameters_framerate_den?.(stream.codecpar);
|
|
74
|
+
if (typeof num === "number" && typeof den === "number" && den > 0 && num > 0) {
|
|
75
|
+
return num / den;
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
71
79
|
return void 0;
|
|
72
80
|
}
|
|
73
81
|
async function safeDuration(libav, fmt_ctx) {
|
|
@@ -185,5 +193,5 @@ function ffmpegToAvbridgeAudio(name) {
|
|
|
185
193
|
}
|
|
186
194
|
|
|
187
195
|
export { probeWithLibav };
|
|
188
|
-
//# sourceMappingURL=avi-
|
|
189
|
-
//# sourceMappingURL=avi-
|
|
196
|
+
//# sourceMappingURL=avi-BLIH7KKV.js.map
|
|
197
|
+
//# sourceMappingURL=avi-BLIH7KKV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/probe/avi.ts"],"names":[],"mappings":"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","file":"avi-BLIH7KKV.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: await framerate(libav, 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\n/**\n * Read frame rate from the stream's `AVCodecParameters.framerate`.\n *\n * `avg_frame_rate` / `r_frame_rate` live on the AVStream in C but libav.js\n * doesn't expose them as JS properties on the stream record — only via\n * dedicated accessor functions we don't ship. `codecpar.framerate` IS\n * accessible via `AVCodecParameters_framerate_num/_den` and is populated\n * for most containers (for AVI it's parsed from `dwRate`/`dwScale` in the\n * stream header).\n *\n * Returns undefined if unavailable so the caller can fall back to a\n * container-appropriate default (currently 30 fps, which is wrong for\n * 25 fps PAL and 23.976 fps film content — hence the importance of\n * reading this correctly).\n */\nasync function framerate(\n libav: LibavInstance,\n stream: LibavStream,\n): Promise<number | undefined> {\n // The stream record may still carry these (new libav.js versions) —\n // prefer them when present.\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 try {\n const num = await libav.AVCodecParameters_framerate_num?.(stream.codecpar);\n const den = await libav.AVCodecParameters_framerate_den?.(stream.codecpar);\n if (typeof num === \"number\" && typeof den === \"number\" && den > 0 && num > 0) {\n return num / den;\n }\n } catch {\n // Fall through.\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 case \"dvvideo\": return \"dv\"; // DV / DVCPRO (camcorder, MiniDV)\n case \"hq_hqa\": return \"hq_hqa\"; // Canopus HQ / HQA (Grass Valley)\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 AVCodecParameters_framerate_num?(codecpar: number): Promise<number>;\n AVCodecParameters_framerate_den?(codecpar: number): Promise<number>;\n\n AVMEDIA_TYPE_VIDEO: number;\n AVMEDIA_TYPE_AUDIO: number;\n}\n"]}
|
|
@@ -32,7 +32,7 @@ async function probeWithLibav(source, sniffed) {
|
|
|
32
32
|
codec: ffmpegToAvbridgeVideo(codecName),
|
|
33
33
|
width: codecpar?.width ?? 0,
|
|
34
34
|
height: codecpar?.height ?? 0,
|
|
35
|
-
fps: framerate(stream)
|
|
35
|
+
fps: await framerate(libav, stream)
|
|
36
36
|
});
|
|
37
37
|
} else if (stream.codec_type === libav.AVMEDIA_TYPE_AUDIO) {
|
|
38
38
|
audioTracks.push({
|
|
@@ -60,7 +60,7 @@ async function probeWithLibav(source, sniffed) {
|
|
|
60
60
|
duration
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
|
-
function framerate(stream) {
|
|
63
|
+
async function framerate(libav, stream) {
|
|
64
64
|
if (typeof stream.avg_frame_rate_num === "number" && stream.avg_frame_rate_den) {
|
|
65
65
|
return stream.avg_frame_rate_num / stream.avg_frame_rate_den;
|
|
66
66
|
}
|
|
@@ -68,6 +68,14 @@ function framerate(stream) {
|
|
|
68
68
|
if (stream.avg_frame_rate.den === 0) return void 0;
|
|
69
69
|
return stream.avg_frame_rate.num / stream.avg_frame_rate.den;
|
|
70
70
|
}
|
|
71
|
+
try {
|
|
72
|
+
const num = await libav.AVCodecParameters_framerate_num?.(stream.codecpar);
|
|
73
|
+
const den = await libav.AVCodecParameters_framerate_den?.(stream.codecpar);
|
|
74
|
+
if (typeof num === "number" && typeof den === "number" && den > 0 && num > 0) {
|
|
75
|
+
return num / den;
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
71
79
|
return void 0;
|
|
72
80
|
}
|
|
73
81
|
async function safeDuration(libav, fmt_ctx) {
|
|
@@ -185,5 +193,5 @@ function ffmpegToAvbridgeAudio(name) {
|
|
|
185
193
|
}
|
|
186
194
|
|
|
187
195
|
export { probeWithLibav };
|
|
188
|
-
//# sourceMappingURL=avi-
|
|
189
|
-
//# sourceMappingURL=avi-
|
|
196
|
+
//# sourceMappingURL=avi-GX2H34IQ.js.map
|
|
197
|
+
//# sourceMappingURL=avi-GX2H34IQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/probe/avi.ts"],"names":[],"mappings":"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","file":"avi-GX2H34IQ.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: await framerate(libav, 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\n/**\n * Read frame rate from the stream's `AVCodecParameters.framerate`.\n *\n * `avg_frame_rate` / `r_frame_rate` live on the AVStream in C but libav.js\n * doesn't expose them as JS properties on the stream record — only via\n * dedicated accessor functions we don't ship. `codecpar.framerate` IS\n * accessible via `AVCodecParameters_framerate_num/_den` and is populated\n * for most containers (for AVI it's parsed from `dwRate`/`dwScale` in the\n * stream header).\n *\n * Returns undefined if unavailable so the caller can fall back to a\n * container-appropriate default (currently 30 fps, which is wrong for\n * 25 fps PAL and 23.976 fps film content — hence the importance of\n * reading this correctly).\n */\nasync function framerate(\n libav: LibavInstance,\n stream: LibavStream,\n): Promise<number | undefined> {\n // The stream record may still carry these (new libav.js versions) —\n // prefer them when present.\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 try {\n const num = await libav.AVCodecParameters_framerate_num?.(stream.codecpar);\n const den = await libav.AVCodecParameters_framerate_den?.(stream.codecpar);\n if (typeof num === \"number\" && typeof den === \"number\" && den > 0 && num > 0) {\n return num / den;\n }\n } catch {\n // Fall through.\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 case \"dvvideo\": return \"dv\"; // DV / DVCPRO (camcorder, MiniDV)\n case \"hq_hqa\": return \"hq_hqa\"; // Canopus HQ / HQA (Grass Valley)\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 AVCodecParameters_framerate_num?(codecpar: number): Promise<number>;\n AVCodecParameters_framerate_den?(codecpar: number): Promise<number>;\n\n AVMEDIA_TYPE_VIDEO: number;\n AVMEDIA_TYPE_AUDIO: number;\n}\n"]}
|
|
@@ -185,7 +185,7 @@ async function probe(source, transport) {
|
|
|
185
185
|
const hasUnknownCodec = result.videoTracks.some((t) => t.codec === "unknown") || result.audioTracks.some((t) => t.codec === "unknown");
|
|
186
186
|
if (hasUnknownCodec) {
|
|
187
187
|
try {
|
|
188
|
-
const { probeWithLibav } = await import('./avi-
|
|
188
|
+
const { probeWithLibav } = await import('./avi-BLIH7KKV.js');
|
|
189
189
|
return await probeWithLibav(normalized, sniffed);
|
|
190
190
|
} catch {
|
|
191
191
|
return result;
|
|
@@ -198,7 +198,7 @@ async function probe(source, transport) {
|
|
|
198
198
|
mediabunnyErr.message
|
|
199
199
|
);
|
|
200
200
|
try {
|
|
201
|
-
const { probeWithLibav } = await import('./avi-
|
|
201
|
+
const { probeWithLibav } = await import('./avi-BLIH7KKV.js');
|
|
202
202
|
return await probeWithLibav(normalized, sniffed);
|
|
203
203
|
} catch (libavErr) {
|
|
204
204
|
const mbMsg = mediabunnyErr.message || String(mediabunnyErr);
|
|
@@ -212,7 +212,7 @@ async function probe(source, transport) {
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
try {
|
|
215
|
-
const { probeWithLibav } = await import('./avi-
|
|
215
|
+
const { probeWithLibav } = await import('./avi-BLIH7KKV.js');
|
|
216
216
|
return await probeWithLibav(normalized, sniffed);
|
|
217
217
|
} catch (err) {
|
|
218
218
|
const inner = err instanceof Error ? err.message : String(err);
|
|
@@ -233,5 +233,5 @@ async function probe(source, transport) {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
export { avbridgeAudioToMediabunny, avbridgeVideoToMediabunny, buildMediabunnySourceFromInput, probe };
|
|
236
|
-
//# sourceMappingURL=chunk-
|
|
237
|
-
//# sourceMappingURL=chunk-
|
|
236
|
+
//# sourceMappingURL=chunk-5CX7BVVV.js.map
|
|
237
|
+
//# sourceMappingURL=chunk-5CX7BVVV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/probe/mediabunny.ts","../src/probe/index.ts"],"names":[],"mappings":"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","file":"chunk-2LNXMGT6.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\";\n\n/**\n * Probe via mediabunny. Built against the real (typed) API exported by\n * `mediabunny.d.ts`:\n *\n * - `Input.getTracks()` returns `InputTrack[]`; each track has `isVideoTrack()`\n * / `isAudioTrack()` type guards plus a `codec` getter that returns one of\n * the enum strings (`\"avc\"|\"hevc\"|\"vp9\"|\"vp8\"|\"av1\"` for video,\n * `\"aac\"|\"opus\"|...` for audio).\n * - For decoder metadata + codec parameter strings we call\n * `getDecoderConfig()` and `getCodecParameterString()` on the typed track.\n *\n * The bridging back to avbridge's own codec naming (`h264` instead of mediabunny's\n * `avc`) happens here so the rest of the codebase keeps a single vocabulary.\n */\nexport async function probeWithMediabunny(\n source: NormalizedSource,\n sniffedContainer: ContainerKind,\n): Promise<MediaContext> {\n const mb = await import(\"mediabunny\");\n const input = new mb.Input({\n source: await buildMediabunnySource(mb, source),\n formats: mb.ALL_FORMATS,\n });\n\n const allTracks = await input.getTracks();\n const duration = await safeNumber(() => input.computeDuration());\n\n const videoTracks: VideoTrackInfo[] = [];\n const audioTracks: AudioTrackInfo[] = [];\n\n for (const track of allTracks) {\n if (track.isVideoTrack()) {\n const codecParam = await safe(() => track.getCodecParameterString());\n videoTracks.push({\n id: track.id,\n codec: mediabunnyVideoToAvbridge(track.codec),\n width: track.displayWidth ?? track.codedWidth ?? 0,\n height: track.displayHeight ?? track.codedHeight ?? 0,\n codecString: codecParam ?? undefined,\n });\n } else if (track.isAudioTrack()) {\n const codecParam = await safe(() => track.getCodecParameterString());\n audioTracks.push({\n id: track.id,\n codec: mediabunnyAudioToAvbridge(track.codec),\n channels: track.numberOfChannels ?? 0,\n sampleRate: track.sampleRate ?? 0,\n language: track.languageCode,\n codecString: codecParam ?? undefined,\n });\n }\n }\n\n const format = await safe(() => input.getFormat());\n const container = resolveContainer(format?.name, sniffedContainer);\n\n return {\n source: source.original,\n name: source.name,\n byteLength: source.byteLength,\n container,\n videoTracks,\n audioTracks,\n subtitleTracks: [],\n probedBy: \"mediabunny\",\n duration,\n };\n}\n\n/**\n * Build the right mediabunny `Source` for a normalized input. URL sources\n * use `UrlSource` (Range requests, prefetch, parallelism) so we don't\n * buffer the whole file into memory. Blob/File sources use `BlobSource`.\n *\n * Exported so the remux strategy can use the same routing logic.\n */\nexport async function buildMediabunnySource(\n mb: typeof import(\"mediabunny\"),\n source: NormalizedSource,\n): Promise<InstanceType<typeof mb.BlobSource> | InstanceType<typeof mb.UrlSource>> {\n if (source.kind === \"url\") {\n return new mb.UrlSource(source.url);\n }\n return new mb.BlobSource(source.blob);\n}\n\n/**\n * Build a mediabunny `Source` directly from a raw `MediaInput`, bypassing\n * `normalizeSource`. Used by strategies that already have the original\n * input on hand (via `MediaContext.source`) and don't need a sniff window.\n *\n * This is the routing point that decides \"stream from URL via Range\n * requests\" vs \"wrap in-memory bytes as BlobSource\". Always prefer\n * `UrlSource` for URL inputs so we don't accidentally buffer the file.\n */\nexport async function buildMediabunnySourceFromInput(\n mb: typeof import(\"mediabunny\"),\n source: import(\"../types.js\").MediaInput,\n): Promise<InstanceType<typeof mb.BlobSource> | InstanceType<typeof mb.UrlSource>> {\n if (typeof source === \"string\") return new mb.UrlSource(source);\n if (source instanceof URL) return new mb.UrlSource(source.toString());\n if (source instanceof Blob) return new mb.BlobSource(source);\n if (source instanceof ArrayBuffer) return new mb.BlobSource(new Blob([source]));\n if (source instanceof Uint8Array) return new mb.BlobSource(new Blob([source as BlobPart]));\n throw new TypeError(\"unsupported source type for mediabunny\");\n}\n\nfunction resolveContainer(formatName: string | undefined, sniffed: ContainerKind): ContainerKind {\n const name = (formatName ?? \"\").toLowerCase();\n if (name.includes(\"matroska\") || name.includes(\"mkv\")) return \"mkv\";\n if (name.includes(\"webm\")) return \"webm\";\n if (name.includes(\"mp4\") || name.includes(\"isom\")) return \"mp4\";\n if (name.includes(\"mov\") || name.includes(\"quicktime\")) return \"mov\";\n if (name.includes(\"ogg\")) return \"ogg\";\n if (name.includes(\"wav\")) return \"wav\";\n if (name.includes(\"flac\")) return \"flac\";\n if (name.includes(\"mp3\")) return \"mp3\";\n if (name.includes(\"adts\") || name.includes(\"aac\")) return \"adts\";\n if (name.includes(\"mpegts\") || name.includes(\"mpeg-ts\") || name.includes(\"transport\")) return \"mpegts\";\n return sniffed;\n}\n\n/** Mediabunny video codec → avbridge video codec. */\nexport function mediabunnyVideoToAvbridge(c: string | null | undefined): VideoCodec {\n switch (c) {\n case \"avc\": return \"h264\";\n case \"hevc\": return \"h265\";\n case \"vp8\": return \"vp8\";\n case \"vp9\": return \"vp9\";\n case \"av1\": return \"av1\";\n default:\n // Preserve the original codec string when mediabunny gave us something\n // we don't recognize. The classifier checks `NATIVE_VIDEO_CODECS.has()`\n // and routes anything outside that set through the fallback chain — so\n // returning the unknown name (instead of silently relabeling it as\n // \"h264\") gets correct routing AND honest diagnostics.\n return c ? (c as VideoCodec) : \"unknown\";\n }\n}\n\n/** avbridge video codec → mediabunny video codec (for output sources). */\nexport function avbridgeVideoToMediabunny(c: VideoCodec): \"avc\" | \"hevc\" | \"vp9\" | \"vp8\" | \"av1\" | null {\n switch (c) {\n case \"h264\": return \"avc\";\n case \"h265\": return \"hevc\";\n case \"vp8\": return \"vp8\";\n case \"vp9\": return \"vp9\";\n case \"av1\": return \"av1\";\n default: return null;\n }\n}\n\nexport function mediabunnyAudioToAvbridge(c: string | null | undefined): AudioCodec {\n switch (c) {\n case \"aac\": return \"aac\";\n case \"mp3\": 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 default: return c ? (c as AudioCodec) : \"unknown\";\n }\n}\n\nexport function avbridgeAudioToMediabunny(c: AudioCodec): string | null {\n switch (c) {\n case \"aac\": return \"aac\";\n case \"mp3\": 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 default: return null;\n }\n}\n\nasync function safeNumber(fn: () => Promise<number> | number): Promise<number | undefined> {\n try {\n const v = await fn();\n return typeof v === \"number\" && Number.isFinite(v) ? v : undefined;\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","import type { ContainerKind, MediaContext, MediaInput, TransportConfig } from \"../types.js\";\nimport { normalizeSource, sniffNormalizedSource } from \"../util/source.js\";\nimport { probeWithMediabunny } from \"./mediabunny.js\";\nimport { AvbridgeError, ERR_PROBE_FAILED, ERR_PROBE_UNKNOWN_CONTAINER, ERR_LIBAV_NOT_REACHABLE } from \"../errors.js\";\n\n/** Containers mediabunny can demux. Sniff results outside this set go straight to libav. */\nconst MEDIABUNNY_CONTAINERS = new Set<ContainerKind>([\n \"mp4\",\n \"mov\",\n \"mkv\",\n \"webm\",\n \"ogg\",\n \"wav\",\n \"mp3\",\n \"flac\",\n \"adts\",\n \"mpegts\",\n]);\n\n/**\n * Probe a source and produce a {@link MediaContext}.\n *\n * Routing:\n * 1. Sniff the magic header. Cheap, deterministic, ignores file extensions.\n * 2. If the container is one mediabunny supports → try mediabunny first\n * (fast path — it's a single pass of WASM-free JS parsing). If mediabunny\n * throws (e.g. an assertion on an unsupported sample entry like `mp4v`\n * for MPEG-4 Part 2 in ISOBMFF, or an exotic MKV codec), fall through to\n * libav.js which handles the long tail of codecs mediabunny doesn't.\n * The combined-error case surfaces *both* failures so the user sees\n * which path each step took.\n * 3. If sniffing identifies AVI/ASF/FLV (or `unknown`) → libav.js directly.\n * mediabunny can't read those containers at all, so there's no fast path\n * to try.\n */\nexport async function probe(\n source: MediaInput,\n transport?: TransportConfig,\n): Promise<MediaContext> {\n const normalized = await normalizeSource(source, transport);\n const sniffed = await sniffNormalizedSource(normalized);\n\n if (MEDIABUNNY_CONTAINERS.has(sniffed)) {\n try {\n const result = await probeWithMediabunny(normalized, sniffed);\n // If mediabunny returned unknown codecs, try libav which recognizes\n // a wider range (DTS, TrueHD, etc.). Only escalate if there ARE\n // tracks with unknown codecs — otherwise mediabunny's result is fine.\n const hasUnknownCodec =\n result.videoTracks.some((t) => t.codec === \"unknown\") ||\n result.audioTracks.some((t) => t.codec === \"unknown\");\n if (hasUnknownCodec) {\n try {\n const { probeWithLibav } = await import(\"./avi.js\");\n return await probeWithLibav(normalized, sniffed);\n } catch {\n // libav also failed — return mediabunny's result (unknown codecs\n // are better than no result at all)\n return result;\n }\n }\n return result;\n } catch (mediabunnyErr) {\n // mediabunny rejected the file. Before giving up, try libav — it can\n // demux a much wider range of codec combinations in ISOBMFF/MKV/etc.\n // than mediabunny's pure-JS parser (e.g. mp4v, wmv3-in-asf, flac in\n // an MP4 container). This is \"escalation\", not \"masking\": if libav\n // also fails we surface both errors below.\n // eslint-disable-next-line no-console\n console.warn(\n `[avbridge] mediabunny rejected ${sniffed} file, falling back to libav:`,\n (mediabunnyErr as Error).message,\n );\n try {\n const { probeWithLibav } = await import(\"./avi.js\");\n return await probeWithLibav(normalized, sniffed);\n } catch (libavErr) {\n const mbMsg = (mediabunnyErr as Error).message || String(mediabunnyErr);\n const lvMsg = libavErr instanceof Error ? libavErr.message : String(libavErr);\n throw new AvbridgeError(\n ERR_PROBE_FAILED,\n `Failed to probe ${sniffed.toUpperCase()} file. mediabunny: ${mbMsg}. libav: ${lvMsg}.`,\n \"The file may be corrupt, truncated, or in an unsupported format. Enable AVBRIDGE_DEBUG for detailed logs.\",\n );\n }\n }\n }\n\n // sniffed === avi | asf | flv | unknown — try libav.\n try {\n const { probeWithLibav } = await import(\"./avi.js\");\n return await probeWithLibav(normalized, sniffed);\n } catch (err) {\n const inner = err instanceof Error ? err.message : String(err);\n // eslint-disable-next-line no-console\n console.error(\"[avbridge] libav probe failed for\", sniffed, \"file:\", err);\n if (sniffed === \"unknown\") {\n throw new AvbridgeError(\n ERR_PROBE_UNKNOWN_CONTAINER,\n `Unable to probe source: container format could not be identified. libav fallback: ${inner || \"(no details)\"}`,\n \"The file may be corrupt or in a format avbridge doesn't recognize. Check the file plays in VLC or ffprobe.\",\n );\n }\n throw new AvbridgeError(\n ERR_LIBAV_NOT_REACHABLE,\n `${sniffed.toUpperCase()} files require libav.js, which failed to load: ${inner || \"(no details)\"}`,\n \"Install @libav.js/variant-webcodecs, or check that AVBRIDGE_LIBAV_BASE points to the correct path.\",\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/probe/mediabunny.ts","../src/probe/index.ts"],"names":[],"mappings":";;;AAwBA,eAAsB,mBAAA,CACpB,QACA,gBAAA,EACuB;AACvB,EAAA,MAAM,EAAA,GAAK,MAAM,OAAO,YAAY,CAAA;AACpC,EAAA,MAAM,KAAA,GAAQ,IAAI,EAAA,CAAG,KAAA,CAAM;AAAA,IACzB,MAAA,EAAQ,MAAM,qBAAA,CAAsB,EAAA,EAAI,MAAM,CAAA;AAAA,IAC9C,SAAS,EAAA,CAAG;AAAA,GACb,CAAA;AAED,EAAA,MAAM,SAAA,GAAY,MAAM,KAAA,CAAM,SAAA,EAAU;AACxC,EAAA,MAAM,WAAW,MAAM,UAAA,CAAW,MAAM,KAAA,CAAM,iBAAiB,CAAA;AAE/D,EAAA,MAAM,cAAgC,EAAC;AACvC,EAAA,MAAM,cAAgC,EAAC;AAEvC,EAAA,KAAA,MAAW,SAAS,SAAA,EAAW;AAC7B,IAAA,IAAI,KAAA,CAAM,cAAa,EAAG;AACxB,MAAA,MAAM,aAAa,MAAM,IAAA,CAAK,MAAM,KAAA,CAAM,yBAAyB,CAAA;AACnE,MAAA,WAAA,CAAY,IAAA,CAAK;AAAA,QACf,IAAI,KAAA,CAAM,EAAA;AAAA,QACV,KAAA,EAAO,yBAAA,CAA0B,KAAA,CAAM,KAAK,CAAA;AAAA,QAC5C,KAAA,EAAO,KAAA,CAAM,YAAA,IAAgB,KAAA,CAAM,UAAA,IAAc,CAAA;AAAA,QACjD,MAAA,EAAQ,KAAA,CAAM,aAAA,IAAiB,KAAA,CAAM,WAAA,IAAe,CAAA;AAAA,QACpD,aAAa,UAAA,IAAc;AAAA,OAC5B,CAAA;AAAA,IACH,CAAA,MAAA,IAAW,KAAA,CAAM,YAAA,EAAa,EAAG;AAC/B,MAAA,MAAM,aAAa,MAAM,IAAA,CAAK,MAAM,KAAA,CAAM,yBAAyB,CAAA;AACnE,MAAA,WAAA,CAAY,IAAA,CAAK;AAAA,QACf,IAAI,KAAA,CAAM,EAAA;AAAA,QACV,KAAA,EAAO,yBAAA,CAA0B,KAAA,CAAM,KAAK,CAAA;AAAA,QAC5C,QAAA,EAAU,MAAM,gBAAA,IAAoB,CAAA;AAAA,QACpC,UAAA,EAAY,MAAM,UAAA,IAAc,CAAA;AAAA,QAChC,UAAU,KAAA,CAAM,YAAA;AAAA,QAChB,aAAa,UAAA,IAAc;AAAA,OAC5B,CAAA;AAAA,IACH;AAAA,EACF;AAEA,EAAA,MAAM,SAAS,MAAM,IAAA,CAAK,MAAM,KAAA,CAAM,WAAW,CAAA;AACjD,EAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,MAAA,EAAQ,IAAA,EAAM,gBAAgB,CAAA;AAEjE,EAAA,OAAO;AAAA,IACL,QAAQ,MAAA,CAAO,QAAA;AAAA,IACf,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,YAAY,MAAA,CAAO,UAAA;AAAA,IACnB,SAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA,gBAAgB,EAAC;AAAA,IACjB,QAAA,EAAU,YAAA;AAAA,IACV;AAAA,GACF;AACF;AASA,eAAsB,qBAAA,CACpB,IACA,MAAA,EACiF;AACjF,EAAA,IAAI,MAAA,CAAO,SAAS,KAAA,EAAO;AACzB,IAAA,OAAO,IAAI,EAAA,CAAG,SAAA,CAAU,MAAA,CAAO,GAAG,CAAA;AAAA,EACpC;AACA,EAAA,OAAO,IAAI,EAAA,CAAG,UAAA,CAAW,MAAA,CAAO,IAAI,CAAA;AACtC;AAWA,eAAsB,8BAAA,CACpB,IACA,MAAA,EACiF;AACjF,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,SAAiB,IAAI,EAAA,CAAG,UAAU,MAAM,CAAA;AAC9D,EAAA,IAAI,MAAA,YAAkB,KAAK,OAAO,IAAI,GAAG,SAAA,CAAU,MAAA,CAAO,UAAU,CAAA;AACpE,EAAA,IAAI,kBAAkB,IAAA,EAAM,OAAO,IAAI,EAAA,CAAG,WAAW,MAAM,CAAA;AAC3D,EAAA,IAAI,MAAA,YAAkB,WAAA,EAAa,OAAO,IAAI,EAAA,CAAG,UAAA,CAAW,IAAI,IAAA,CAAK,CAAC,MAAM,CAAC,CAAC,CAAA;AAC9E,EAAA,IAAI,MAAA,YAAkB,UAAA,EAAY,OAAO,IAAI,EAAA,CAAG,UAAA,CAAW,IAAI,IAAA,CAAK,CAAC,MAAkB,CAAC,CAAC,CAAA;AACzF,EAAA,MAAM,IAAI,UAAU,wCAAwC,CAAA;AAC9D;AAEA,SAAS,gBAAA,CAAiB,YAAgC,OAAA,EAAuC;AAC/F,EAAA,MAAM,IAAA,GAAA,CAAQ,UAAA,IAAc,EAAA,EAAI,WAAA,EAAY;AAC5C,EAAA,IAAI,IAAA,CAAK,SAAS,UAAU,CAAA,IAAK,KAAK,QAAA,CAAS,KAAK,GAAG,OAAO,KAAA;AAC9D,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,EAAG,OAAO,MAAA;AAClC,EAAA,IAAI,IAAA,CAAK,SAAS,KAAK,CAAA,IAAK,KAAK,QAAA,CAAS,MAAM,GAAG,OAAO,KAAA;AAC1D,EAAA,IAAI,IAAA,CAAK,SAAS,KAAK,CAAA,IAAK,KAAK,QAAA,CAAS,WAAW,GAAG,OAAO,KAAA;AAC/D,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,KAAK,CAAA,EAAG,OAAO,KAAA;AACjC,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,KAAK,CAAA,EAAG,OAAO,KAAA;AACjC,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA,EAAG,OAAO,MAAA;AAClC,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,KAAK,CAAA,EAAG,OAAO,KAAA;AACjC,EAAA,IAAI,IAAA,CAAK,SAAS,MAAM,CAAA,IAAK,KAAK,QAAA,CAAS,KAAK,GAAG,OAAO,MAAA;AAC1D,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA,IAAK,IAAA,CAAK,QAAA,CAAS,SAAS,CAAA,IAAK,IAAA,CAAK,QAAA,CAAS,WAAW,CAAA,EAAG,OAAO,QAAA;AAC9F,EAAA,OAAO,OAAA;AACT;AAGO,SAAS,0BAA0B,CAAA,EAA0C;AAClF,EAAA,QAAQ,CAAA;AAAG,IACT,KAAK,KAAA;AAAQ,MAAA,OAAO,MAAA;AAAA,IACpB,KAAK,MAAA;AAAQ,MAAA,OAAO,MAAA;AAAA,IACpB,KAAK,KAAA;AAAQ,MAAA,OAAO,KAAA;AAAA,IACpB,KAAK,KAAA;AAAQ,MAAA,OAAO,KAAA;AAAA,IACpB,KAAK,KAAA;AAAQ,MAAA,OAAO,KAAA;AAAA,IACpB;AAME,MAAA,OAAO,IAAK,CAAA,GAAmB,SAAA;AAAA;AAErC;AAGO,SAAS,0BAA0B,CAAA,EAA8D;AACtG,EAAA,QAAQ,CAAA;AAAG,IACT,KAAK,MAAA;AAAQ,MAAA,OAAO,KAAA;AAAA,IACpB,KAAK,MAAA;AAAQ,MAAA,OAAO,MAAA;AAAA,IACpB,KAAK,KAAA;AAAQ,MAAA,OAAO,KAAA;AAAA,IACpB,KAAK,KAAA;AAAQ,MAAA,OAAO,KAAA;AAAA,IACpB,KAAK,KAAA;AAAQ,MAAA,OAAO,KAAA;AAAA,IACpB;AAAa,MAAA,OAAO,IAAA;AAAA;AAExB;AAEO,SAAS,0BAA0B,CAAA,EAA0C;AAClF,EAAA,QAAQ,CAAA;AAAG,IACT,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,QAAA;AAAU,MAAA,OAAO,QAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB;AAAe,MAAA,OAAO,IAAK,CAAA,GAAmB,SAAA;AAAA;AAElD;AAEO,SAAS,0BAA0B,CAAA,EAA8B;AACtE,EAAA,QAAQ,CAAA;AAAG,IACT,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,QAAA;AAAU,MAAA,OAAO,QAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB,KAAK,KAAA;AAAU,MAAA,OAAO,KAAA;AAAA,IACtB,KAAK,MAAA;AAAU,MAAA,OAAO,MAAA;AAAA,IACtB;AAAe,MAAA,OAAO,IAAA;AAAA;AAE1B;AAEA,eAAe,WAAW,EAAA,EAAiE;AACzF,EAAA,IAAI;AACF,IAAA,MAAM,CAAA,GAAI,MAAM,EAAA,EAAG;AACnB,IAAA,OAAO,OAAO,CAAA,KAAM,QAAA,IAAY,OAAO,QAAA,CAAS,CAAC,IAAI,CAAA,GAAI,KAAA,CAAA;AAAA,EAC3D,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAEA,eAAe,KAAQ,EAAA,EAAkD;AACvE,EAAA,IAAI;AAAE,IAAA,OAAO,MAAM,EAAA,EAAG;AAAA,EAAG,CAAA,CAAA,MAAQ;AAAE,IAAA,OAAO,MAAA;AAAA,EAAW;AACvD;;;ACjMA,IAAM,qBAAA,uBAA4B,GAAA,CAAmB;AAAA,EACnD,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAC,CAAA;AAkBD,eAAsB,KAAA,CACpB,QACA,SAAA,EACuB;AACvB,EAAA,MAAM,UAAA,GAAa,MAAM,eAAA,CAAgB,MAAA,EAAQ,SAAS,CAAA;AAC1D,EAAA,MAAM,OAAA,GAAU,MAAM,qBAAA,CAAsB,UAAU,CAAA;AAEtD,EAAA,IAAI,qBAAA,CAAsB,GAAA,CAAI,OAAO,CAAA,EAAG;AACtC,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,mBAAA,CAAoB,UAAA,EAAY,OAAO,CAAA;AAI5D,MAAA,MAAM,kBACJ,MAAA,CAAO,WAAA,CAAY,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,KAAA,KAAU,SAAS,CAAA,IACpD,OAAO,WAAA,CAAY,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,UAAU,SAAS,CAAA;AACtD,MAAA,IAAI,eAAA,EAAiB;AACnB,QAAA,IAAI;AACF,UAAA,MAAM,EAAE,cAAA,EAAe,GAAI,MAAM,OAAO,mBAAU,CAAA;AAClD,UAAA,OAAO,MAAM,cAAA,CAAe,UAAA,EAAY,OAAO,CAAA;AAAA,QACjD,CAAA,CAAA,MAAQ;AAGN,UAAA,OAAO,MAAA;AAAA,QACT;AAAA,MACF;AACA,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,aAAA,EAAe;AAOtB,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN,kCAAkC,OAAO,CAAA,6BAAA,CAAA;AAAA,QACxC,aAAA,CAAwB;AAAA,OAC3B;AACA,MAAA,IAAI;AACF,QAAA,MAAM,EAAE,cAAA,EAAe,GAAI,MAAM,OAAO,mBAAU,CAAA;AAClD,QAAA,OAAO,MAAM,cAAA,CAAe,UAAA,EAAY,OAAO,CAAA;AAAA,MACjD,SAAS,QAAA,EAAU;AACjB,QAAA,MAAM,KAAA,GAAS,aAAA,CAAwB,OAAA,IAAW,MAAA,CAAO,aAAa,CAAA;AACtE,QAAA,MAAM,QAAQ,QAAA,YAAoB,KAAA,GAAQ,QAAA,CAAS,OAAA,GAAU,OAAO,QAAQ,CAAA;AAC5E,QAAA,MAAM,IAAI,aAAA;AAAA,UACR,gBAAA;AAAA,UACA,mBAAmB,OAAA,CAAQ,WAAA,EAAa,CAAA,mBAAA,EAAsB,KAAK,YAAY,KAAK,CAAA,CAAA,CAAA;AAAA,UACpF;AAAA,SACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,cAAA,EAAe,GAAI,MAAM,OAAO,mBAAU,CAAA;AAClD,IAAA,OAAO,MAAM,cAAA,CAAe,UAAA,EAAY,OAAO,CAAA;AAAA,EACjD,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,QAAQ,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU,OAAO,GAAG,CAAA;AAE7D,IAAA,OAAA,CAAQ,KAAA,CAAM,mCAAA,EAAqC,OAAA,EAAS,OAAA,EAAS,GAAG,CAAA;AACxE,IAAA,IAAI,YAAY,SAAA,EAAW;AACzB,MAAA,MAAM,IAAI,aAAA;AAAA,QACR,2BAAA;AAAA,QACA,CAAA,kFAAA,EAAqF,SAAS,cAAc,CAAA,CAAA;AAAA,QAC5G;AAAA,OACF;AAAA,IACF;AACA,IAAA,MAAM,IAAI,aAAA;AAAA,MACR,uBAAA;AAAA,MACA,GAAG,OAAA,CAAQ,WAAA,EAAa,CAAA,+CAAA,EAAkD,SAAS,cAAc,CAAA,CAAA;AAAA,MACjG;AAAA,KACF;AAAA,EACF;AACF","file":"chunk-5CX7BVVV.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\";\n\n/**\n * Probe via mediabunny. Built against the real (typed) API exported by\n * `mediabunny.d.ts`:\n *\n * - `Input.getTracks()` returns `InputTrack[]`; each track has `isVideoTrack()`\n * / `isAudioTrack()` type guards plus a `codec` getter that returns one of\n * the enum strings (`\"avc\"|\"hevc\"|\"vp9\"|\"vp8\"|\"av1\"` for video,\n * `\"aac\"|\"opus\"|...` for audio).\n * - For decoder metadata + codec parameter strings we call\n * `getDecoderConfig()` and `getCodecParameterString()` on the typed track.\n *\n * The bridging back to avbridge's own codec naming (`h264` instead of mediabunny's\n * `avc`) happens here so the rest of the codebase keeps a single vocabulary.\n */\nexport async function probeWithMediabunny(\n source: NormalizedSource,\n sniffedContainer: ContainerKind,\n): Promise<MediaContext> {\n const mb = await import(\"mediabunny\");\n const input = new mb.Input({\n source: await buildMediabunnySource(mb, source),\n formats: mb.ALL_FORMATS,\n });\n\n const allTracks = await input.getTracks();\n const duration = await safeNumber(() => input.computeDuration());\n\n const videoTracks: VideoTrackInfo[] = [];\n const audioTracks: AudioTrackInfo[] = [];\n\n for (const track of allTracks) {\n if (track.isVideoTrack()) {\n const codecParam = await safe(() => track.getCodecParameterString());\n videoTracks.push({\n id: track.id,\n codec: mediabunnyVideoToAvbridge(track.codec),\n width: track.displayWidth ?? track.codedWidth ?? 0,\n height: track.displayHeight ?? track.codedHeight ?? 0,\n codecString: codecParam ?? undefined,\n });\n } else if (track.isAudioTrack()) {\n const codecParam = await safe(() => track.getCodecParameterString());\n audioTracks.push({\n id: track.id,\n codec: mediabunnyAudioToAvbridge(track.codec),\n channels: track.numberOfChannels ?? 0,\n sampleRate: track.sampleRate ?? 0,\n language: track.languageCode,\n codecString: codecParam ?? undefined,\n });\n }\n }\n\n const format = await safe(() => input.getFormat());\n const container = resolveContainer(format?.name, sniffedContainer);\n\n return {\n source: source.original,\n name: source.name,\n byteLength: source.byteLength,\n container,\n videoTracks,\n audioTracks,\n subtitleTracks: [],\n probedBy: \"mediabunny\",\n duration,\n };\n}\n\n/**\n * Build the right mediabunny `Source` for a normalized input. URL sources\n * use `UrlSource` (Range requests, prefetch, parallelism) so we don't\n * buffer the whole file into memory. Blob/File sources use `BlobSource`.\n *\n * Exported so the remux strategy can use the same routing logic.\n */\nexport async function buildMediabunnySource(\n mb: typeof import(\"mediabunny\"),\n source: NormalizedSource,\n): Promise<InstanceType<typeof mb.BlobSource> | InstanceType<typeof mb.UrlSource>> {\n if (source.kind === \"url\") {\n return new mb.UrlSource(source.url);\n }\n return new mb.BlobSource(source.blob);\n}\n\n/**\n * Build a mediabunny `Source` directly from a raw `MediaInput`, bypassing\n * `normalizeSource`. Used by strategies that already have the original\n * input on hand (via `MediaContext.source`) and don't need a sniff window.\n *\n * This is the routing point that decides \"stream from URL via Range\n * requests\" vs \"wrap in-memory bytes as BlobSource\". Always prefer\n * `UrlSource` for URL inputs so we don't accidentally buffer the file.\n */\nexport async function buildMediabunnySourceFromInput(\n mb: typeof import(\"mediabunny\"),\n source: import(\"../types.js\").MediaInput,\n): Promise<InstanceType<typeof mb.BlobSource> | InstanceType<typeof mb.UrlSource>> {\n if (typeof source === \"string\") return new mb.UrlSource(source);\n if (source instanceof URL) return new mb.UrlSource(source.toString());\n if (source instanceof Blob) return new mb.BlobSource(source);\n if (source instanceof ArrayBuffer) return new mb.BlobSource(new Blob([source]));\n if (source instanceof Uint8Array) return new mb.BlobSource(new Blob([source as BlobPart]));\n throw new TypeError(\"unsupported source type for mediabunny\");\n}\n\nfunction resolveContainer(formatName: string | undefined, sniffed: ContainerKind): ContainerKind {\n const name = (formatName ?? \"\").toLowerCase();\n if (name.includes(\"matroska\") || name.includes(\"mkv\")) return \"mkv\";\n if (name.includes(\"webm\")) return \"webm\";\n if (name.includes(\"mp4\") || name.includes(\"isom\")) return \"mp4\";\n if (name.includes(\"mov\") || name.includes(\"quicktime\")) return \"mov\";\n if (name.includes(\"ogg\")) return \"ogg\";\n if (name.includes(\"wav\")) return \"wav\";\n if (name.includes(\"flac\")) return \"flac\";\n if (name.includes(\"mp3\")) return \"mp3\";\n if (name.includes(\"adts\") || name.includes(\"aac\")) return \"adts\";\n if (name.includes(\"mpegts\") || name.includes(\"mpeg-ts\") || name.includes(\"transport\")) return \"mpegts\";\n return sniffed;\n}\n\n/** Mediabunny video codec → avbridge video codec. */\nexport function mediabunnyVideoToAvbridge(c: string | null | undefined): VideoCodec {\n switch (c) {\n case \"avc\": return \"h264\";\n case \"hevc\": return \"h265\";\n case \"vp8\": return \"vp8\";\n case \"vp9\": return \"vp9\";\n case \"av1\": return \"av1\";\n default:\n // Preserve the original codec string when mediabunny gave us something\n // we don't recognize. The classifier checks `NATIVE_VIDEO_CODECS.has()`\n // and routes anything outside that set through the fallback chain — so\n // returning the unknown name (instead of silently relabeling it as\n // \"h264\") gets correct routing AND honest diagnostics.\n return c ? (c as VideoCodec) : \"unknown\";\n }\n}\n\n/** avbridge video codec → mediabunny video codec (for output sources). */\nexport function avbridgeVideoToMediabunny(c: VideoCodec): \"avc\" | \"hevc\" | \"vp9\" | \"vp8\" | \"av1\" | null {\n switch (c) {\n case \"h264\": return \"avc\";\n case \"h265\": return \"hevc\";\n case \"vp8\": return \"vp8\";\n case \"vp9\": return \"vp9\";\n case \"av1\": return \"av1\";\n default: return null;\n }\n}\n\nexport function mediabunnyAudioToAvbridge(c: string | null | undefined): AudioCodec {\n switch (c) {\n case \"aac\": return \"aac\";\n case \"mp3\": 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 default: return c ? (c as AudioCodec) : \"unknown\";\n }\n}\n\nexport function avbridgeAudioToMediabunny(c: AudioCodec): string | null {\n switch (c) {\n case \"aac\": return \"aac\";\n case \"mp3\": 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 default: return null;\n }\n}\n\nasync function safeNumber(fn: () => Promise<number> | number): Promise<number | undefined> {\n try {\n const v = await fn();\n return typeof v === \"number\" && Number.isFinite(v) ? v : undefined;\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","import type { ContainerKind, MediaContext, MediaInput, TransportConfig } from \"../types.js\";\nimport { normalizeSource, sniffNormalizedSource } from \"../util/source.js\";\nimport { probeWithMediabunny } from \"./mediabunny.js\";\nimport { AvbridgeError, ERR_PROBE_FAILED, ERR_PROBE_UNKNOWN_CONTAINER, ERR_LIBAV_NOT_REACHABLE } from \"../errors.js\";\n\n/** Containers mediabunny can demux. Sniff results outside this set go straight to libav. */\nconst MEDIABUNNY_CONTAINERS = new Set<ContainerKind>([\n \"mp4\",\n \"mov\",\n \"mkv\",\n \"webm\",\n \"ogg\",\n \"wav\",\n \"mp3\",\n \"flac\",\n \"adts\",\n \"mpegts\",\n]);\n\n/**\n * Probe a source and produce a {@link MediaContext}.\n *\n * Routing:\n * 1. Sniff the magic header. Cheap, deterministic, ignores file extensions.\n * 2. If the container is one mediabunny supports → try mediabunny first\n * (fast path — it's a single pass of WASM-free JS parsing). If mediabunny\n * throws (e.g. an assertion on an unsupported sample entry like `mp4v`\n * for MPEG-4 Part 2 in ISOBMFF, or an exotic MKV codec), fall through to\n * libav.js which handles the long tail of codecs mediabunny doesn't.\n * The combined-error case surfaces *both* failures so the user sees\n * which path each step took.\n * 3. If sniffing identifies AVI/ASF/FLV (or `unknown`) → libav.js directly.\n * mediabunny can't read those containers at all, so there's no fast path\n * to try.\n */\nexport async function probe(\n source: MediaInput,\n transport?: TransportConfig,\n): Promise<MediaContext> {\n const normalized = await normalizeSource(source, transport);\n const sniffed = await sniffNormalizedSource(normalized);\n\n if (MEDIABUNNY_CONTAINERS.has(sniffed)) {\n try {\n const result = await probeWithMediabunny(normalized, sniffed);\n // If mediabunny returned unknown codecs, try libav which recognizes\n // a wider range (DTS, TrueHD, etc.). Only escalate if there ARE\n // tracks with unknown codecs — otherwise mediabunny's result is fine.\n const hasUnknownCodec =\n result.videoTracks.some((t) => t.codec === \"unknown\") ||\n result.audioTracks.some((t) => t.codec === \"unknown\");\n if (hasUnknownCodec) {\n try {\n const { probeWithLibav } = await import(\"./avi.js\");\n return await probeWithLibav(normalized, sniffed);\n } catch {\n // libav also failed — return mediabunny's result (unknown codecs\n // are better than no result at all)\n return result;\n }\n }\n return result;\n } catch (mediabunnyErr) {\n // mediabunny rejected the file. Before giving up, try libav — it can\n // demux a much wider range of codec combinations in ISOBMFF/MKV/etc.\n // than mediabunny's pure-JS parser (e.g. mp4v, wmv3-in-asf, flac in\n // an MP4 container). This is \"escalation\", not \"masking\": if libav\n // also fails we surface both errors below.\n // eslint-disable-next-line no-console\n console.warn(\n `[avbridge] mediabunny rejected ${sniffed} file, falling back to libav:`,\n (mediabunnyErr as Error).message,\n );\n try {\n const { probeWithLibav } = await import(\"./avi.js\");\n return await probeWithLibav(normalized, sniffed);\n } catch (libavErr) {\n const mbMsg = (mediabunnyErr as Error).message || String(mediabunnyErr);\n const lvMsg = libavErr instanceof Error ? libavErr.message : String(libavErr);\n throw new AvbridgeError(\n ERR_PROBE_FAILED,\n `Failed to probe ${sniffed.toUpperCase()} file. mediabunny: ${mbMsg}. libav: ${lvMsg}.`,\n \"The file may be corrupt, truncated, or in an unsupported format. Enable AVBRIDGE_DEBUG for detailed logs.\",\n );\n }\n }\n }\n\n // sniffed === avi | asf | flv | unknown — try libav.\n try {\n const { probeWithLibav } = await import(\"./avi.js\");\n return await probeWithLibav(normalized, sniffed);\n } catch (err) {\n const inner = err instanceof Error ? err.message : String(err);\n // eslint-disable-next-line no-console\n console.error(\"[avbridge] libav probe failed for\", sniffed, \"file:\", err);\n if (sniffed === \"unknown\") {\n throw new AvbridgeError(\n ERR_PROBE_UNKNOWN_CONTAINER,\n `Unable to probe source: container format could not be identified. libav fallback: ${inner || \"(no details)\"}`,\n \"The file may be corrupt or in a format avbridge doesn't recognize. Check the file plays in VLC or ffprobe.\",\n );\n }\n throw new AvbridgeError(\n ERR_LIBAV_NOT_REACHABLE,\n `${sniffed.toUpperCase()} files require libav.js, which failed to load: ${inner || \"(no details)\"}`,\n \"Install @libav.js/variant-webcodecs, or check that AVBRIDGE_LIBAV_BASE points to the correct path.\",\n );\n }\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { probe, avbridgeVideoToMediabunny, avbridgeAudioToMediabunny, buildMediabunnySourceFromInput } from './chunk-
|
|
1
|
+
import { probe, avbridgeVideoToMediabunny, avbridgeAudioToMediabunny, buildMediabunnySourceFromInput } from './chunk-5CX7BVVV.js';
|
|
2
2
|
import { normalizeSource } from './chunk-CPJLFFCC.js';
|
|
3
3
|
import { sanitizePacketTimestamp } from './chunk-3YKWU4FM.js';
|
|
4
4
|
import { prepareLibavInput } from './chunk-3AI5WFFN.js';
|
|
@@ -364,5 +364,5 @@ function buildAudioDecoderConfig(track) {
|
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
export { createOutputFormat, generateFilename, mimeForFormat, remux, validateRemuxEligibility };
|
|
367
|
-
//# sourceMappingURL=chunk-
|
|
368
|
-
//# sourceMappingURL=chunk-
|
|
367
|
+
//# sourceMappingURL=chunk-B76QWPFM.js.map
|
|
368
|
+
//# sourceMappingURL=chunk-B76QWPFM.js.map
|