avbridge 2.3.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -0
- package/dist/{chunk-6UUT4BEA.cjs → chunk-2IJ66NTD.cjs} +13 -20
- package/dist/chunk-2IJ66NTD.cjs.map +1 -0
- package/dist/{chunk-XKPSTC34.cjs → chunk-2XW2O3YI.cjs} +5 -20
- package/dist/chunk-2XW2O3YI.cjs.map +1 -0
- package/dist/chunk-5KVLE6YI.js +167 -0
- package/dist/chunk-5KVLE6YI.js.map +1 -0
- package/dist/{chunk-7RGG6ME7.cjs → chunk-6SOFJV44.cjs} +422 -688
- package/dist/chunk-6SOFJV44.cjs.map +1 -0
- package/dist/{chunk-2PGRFCWB.js → chunk-CPJLFFCC.js} +8 -18
- package/dist/chunk-CPJLFFCC.js.map +1 -0
- package/dist/chunk-CPZ7PXAM.cjs +240 -0
- package/dist/chunk-CPZ7PXAM.cjs.map +1 -0
- package/dist/{chunk-QQXBPW72.js → chunk-E76AMWI4.js} +4 -18
- package/dist/chunk-E76AMWI4.js.map +1 -0
- package/dist/chunk-LUFA47FP.js +19 -0
- package/dist/chunk-LUFA47FP.js.map +1 -0
- package/dist/{chunk-NV7ILLWH.js → chunk-OGYHFY6K.js} +404 -665
- package/dist/chunk-OGYHFY6K.js.map +1 -0
- package/dist/chunk-Q2VUO52Z.cjs +374 -0
- package/dist/chunk-Q2VUO52Z.cjs.map +1 -0
- package/dist/chunk-QDJLQR53.cjs +22 -0
- package/dist/chunk-QDJLQR53.cjs.map +1 -0
- package/dist/chunk-S4WAZC2T.cjs +173 -0
- package/dist/chunk-S4WAZC2T.cjs.map +1 -0
- package/dist/chunk-SMH6IOP2.js +368 -0
- package/dist/chunk-SMH6IOP2.js.map +1 -0
- package/dist/chunk-SR3MPV4D.js +237 -0
- package/dist/chunk-SR3MPV4D.js.map +1 -0
- package/dist/chunk-X2K3GIWE.js +235 -0
- package/dist/chunk-X2K3GIWE.js.map +1 -0
- package/dist/chunk-ZCUXHW55.cjs +242 -0
- package/dist/chunk-ZCUXHW55.cjs.map +1 -0
- package/dist/element-browser.js +883 -492
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +88 -6
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.cts +51 -1
- package/dist/element.d.ts +51 -1
- package/dist/element.js +87 -5
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +523 -393
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +494 -366
- package/dist/index.js.map +1 -1
- package/dist/libav-demux-H2GS46GH.cjs +27 -0
- package/dist/libav-demux-H2GS46GH.cjs.map +1 -0
- package/dist/libav-demux-OWZ4T2YW.js +6 -0
- package/dist/libav-demux-OWZ4T2YW.js.map +1 -0
- package/dist/{libav-import-GST2AMPL.cjs → libav-import-2ZVKV2E7.cjs} +2 -2
- package/dist/{libav-import-GST2AMPL.cjs.map → libav-import-2ZVKV2E7.cjs.map} +1 -1
- package/dist/{libav-import-2JURFHEW.js → libav-import-6MGLCXVQ.js} +2 -2
- package/dist/{libav-import-2JURFHEW.js.map → libav-import-6MGLCXVQ.js.map} +1 -1
- package/dist/{player-B6WB74RD.d.ts → player-DGXeCNfD.d.cts} +41 -1
- package/dist/{player-B6WB74RD.d.cts → player-DGXeCNfD.d.ts} +41 -1
- package/dist/player.cjs +731 -472
- package/dist/player.cjs.map +1 -1
- package/dist/player.d.cts +229 -120
- package/dist/player.d.ts +229 -120
- package/dist/player.js +710 -451
- package/dist/player.js.map +1 -1
- package/dist/remux-OBSMIENG.cjs +35 -0
- package/dist/remux-OBSMIENG.cjs.map +1 -0
- package/dist/remux-WBYIZBBX.js +10 -0
- package/dist/remux-WBYIZBBX.js.map +1 -0
- package/dist/source-4TZ6KMNV.js +4 -0
- package/dist/{source-F656KYYV.js.map → source-4TZ6KMNV.js.map} +1 -1
- package/dist/source-7YLO6E7X.cjs +29 -0
- package/dist/{source-73CAH6HW.cjs.map → source-7YLO6E7X.cjs.map} +1 -1
- package/dist/source-MTX5ELUZ.js +4 -0
- package/dist/{source-QJR3OHTW.js.map → source-MTX5ELUZ.js.map} +1 -1
- package/dist/source-VFLXLOCN.cjs +29 -0
- package/dist/{source-VB74JQ7Z.cjs.map → source-VFLXLOCN.cjs.map} +1 -1
- package/dist/subtitles-4T74JRGT.js +4 -0
- package/dist/subtitles-4T74JRGT.js.map +1 -0
- package/dist/subtitles-QUH4LPI4.cjs +29 -0
- package/dist/subtitles-QUH4LPI4.cjs.map +1 -0
- package/package.json +1 -1
- package/src/convert/remux.ts +1 -35
- package/src/convert/transcode-libav.ts +691 -0
- package/src/convert/transcode.ts +12 -4
- package/src/element/avbridge-player.ts +100 -0
- package/src/element/avbridge-video.ts +140 -3
- package/src/element/player-styles.ts +12 -0
- package/src/errors.ts +6 -0
- package/src/player.ts +15 -16
- package/src/strategies/fallback/decoder.ts +96 -173
- package/src/strategies/fallback/index.ts +46 -2
- package/src/strategies/fallback/libav-import.ts +9 -1
- package/src/strategies/fallback/video-renderer.ts +107 -0
- package/src/strategies/hybrid/decoder.ts +88 -180
- package/src/strategies/hybrid/index.ts +35 -2
- package/src/strategies/native.ts +6 -3
- package/src/strategies/remux/index.ts +14 -2
- package/src/strategies/remux/pipeline.ts +72 -12
- package/src/subtitles/render.ts +8 -0
- package/src/types.ts +32 -0
- package/src/util/libav-demux.ts +405 -0
- package/src/util/time-ranges.ts +40 -0
- package/dist/chunk-2PGRFCWB.js.map +0 -1
- package/dist/chunk-6UUT4BEA.cjs.map +0 -1
- package/dist/chunk-7RGG6ME7.cjs.map +0 -1
- package/dist/chunk-NV7ILLWH.js.map +0 -1
- package/dist/chunk-QQXBPW72.js.map +0 -1
- package/dist/chunk-XKPSTC34.cjs.map +0 -1
- package/dist/source-73CAH6HW.cjs +0 -28
- package/dist/source-F656KYYV.js +0 -3
- package/dist/source-QJR3OHTW.js +0 -3
- package/dist/source-VB74JQ7Z.cjs +0 -28
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { normalizeSource, sniffNormalizedSource, AvbridgeError, ERR_PROBE_FAILED, ERR_PROBE_UNKNOWN_CONTAINER, ERR_LIBAV_NOT_REACHABLE } from './chunk-CPJLFFCC.js';
|
|
2
|
+
|
|
3
|
+
// src/probe/mediabunny.ts
|
|
4
|
+
async function probeWithMediabunny(source, sniffedContainer) {
|
|
5
|
+
const mb = await import('mediabunny');
|
|
6
|
+
const input = new mb.Input({
|
|
7
|
+
source: await buildMediabunnySource(mb, source),
|
|
8
|
+
formats: mb.ALL_FORMATS
|
|
9
|
+
});
|
|
10
|
+
const allTracks = await input.getTracks();
|
|
11
|
+
const duration = await safeNumber(() => input.computeDuration());
|
|
12
|
+
const videoTracks = [];
|
|
13
|
+
const audioTracks = [];
|
|
14
|
+
for (const track of allTracks) {
|
|
15
|
+
if (track.isVideoTrack()) {
|
|
16
|
+
const codecParam = await safe(() => track.getCodecParameterString());
|
|
17
|
+
videoTracks.push({
|
|
18
|
+
id: track.id,
|
|
19
|
+
codec: mediabunnyVideoToAvbridge(track.codec),
|
|
20
|
+
width: track.displayWidth ?? track.codedWidth ?? 0,
|
|
21
|
+
height: track.displayHeight ?? track.codedHeight ?? 0,
|
|
22
|
+
codecString: codecParam ?? void 0
|
|
23
|
+
});
|
|
24
|
+
} else if (track.isAudioTrack()) {
|
|
25
|
+
const codecParam = await safe(() => track.getCodecParameterString());
|
|
26
|
+
audioTracks.push({
|
|
27
|
+
id: track.id,
|
|
28
|
+
codec: mediabunnyAudioToAvbridge(track.codec),
|
|
29
|
+
channels: track.numberOfChannels ?? 0,
|
|
30
|
+
sampleRate: track.sampleRate ?? 0,
|
|
31
|
+
language: track.languageCode,
|
|
32
|
+
codecString: codecParam ?? void 0
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const format = await safe(() => input.getFormat());
|
|
37
|
+
const container = resolveContainer(format?.name, sniffedContainer);
|
|
38
|
+
return {
|
|
39
|
+
source: source.original,
|
|
40
|
+
name: source.name,
|
|
41
|
+
byteLength: source.byteLength,
|
|
42
|
+
container,
|
|
43
|
+
videoTracks,
|
|
44
|
+
audioTracks,
|
|
45
|
+
subtitleTracks: [],
|
|
46
|
+
probedBy: "mediabunny",
|
|
47
|
+
duration
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function buildMediabunnySource(mb, source) {
|
|
51
|
+
if (source.kind === "url") {
|
|
52
|
+
return new mb.UrlSource(source.url);
|
|
53
|
+
}
|
|
54
|
+
return new mb.BlobSource(source.blob);
|
|
55
|
+
}
|
|
56
|
+
async function buildMediabunnySourceFromInput(mb, source) {
|
|
57
|
+
if (typeof source === "string") return new mb.UrlSource(source);
|
|
58
|
+
if (source instanceof URL) return new mb.UrlSource(source.toString());
|
|
59
|
+
if (source instanceof Blob) return new mb.BlobSource(source);
|
|
60
|
+
if (source instanceof ArrayBuffer) return new mb.BlobSource(new Blob([source]));
|
|
61
|
+
if (source instanceof Uint8Array) return new mb.BlobSource(new Blob([source]));
|
|
62
|
+
throw new TypeError("unsupported source type for mediabunny");
|
|
63
|
+
}
|
|
64
|
+
function resolveContainer(formatName, sniffed) {
|
|
65
|
+
const name = (formatName ?? "").toLowerCase();
|
|
66
|
+
if (name.includes("matroska") || name.includes("mkv")) return "mkv";
|
|
67
|
+
if (name.includes("webm")) return "webm";
|
|
68
|
+
if (name.includes("mp4") || name.includes("isom")) return "mp4";
|
|
69
|
+
if (name.includes("mov") || name.includes("quicktime")) return "mov";
|
|
70
|
+
if (name.includes("ogg")) return "ogg";
|
|
71
|
+
if (name.includes("wav")) return "wav";
|
|
72
|
+
if (name.includes("flac")) return "flac";
|
|
73
|
+
if (name.includes("mp3")) return "mp3";
|
|
74
|
+
if (name.includes("adts") || name.includes("aac")) return "adts";
|
|
75
|
+
if (name.includes("mpegts") || name.includes("mpeg-ts") || name.includes("transport")) return "mpegts";
|
|
76
|
+
return sniffed;
|
|
77
|
+
}
|
|
78
|
+
function mediabunnyVideoToAvbridge(c) {
|
|
79
|
+
switch (c) {
|
|
80
|
+
case "avc":
|
|
81
|
+
return "h264";
|
|
82
|
+
case "hevc":
|
|
83
|
+
return "h265";
|
|
84
|
+
case "vp8":
|
|
85
|
+
return "vp8";
|
|
86
|
+
case "vp9":
|
|
87
|
+
return "vp9";
|
|
88
|
+
case "av1":
|
|
89
|
+
return "av1";
|
|
90
|
+
default:
|
|
91
|
+
return c ? c : "unknown";
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function avbridgeVideoToMediabunny(c) {
|
|
95
|
+
switch (c) {
|
|
96
|
+
case "h264":
|
|
97
|
+
return "avc";
|
|
98
|
+
case "h265":
|
|
99
|
+
return "hevc";
|
|
100
|
+
case "vp8":
|
|
101
|
+
return "vp8";
|
|
102
|
+
case "vp9":
|
|
103
|
+
return "vp9";
|
|
104
|
+
case "av1":
|
|
105
|
+
return "av1";
|
|
106
|
+
default:
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function mediabunnyAudioToAvbridge(c) {
|
|
111
|
+
switch (c) {
|
|
112
|
+
case "aac":
|
|
113
|
+
return "aac";
|
|
114
|
+
case "mp3":
|
|
115
|
+
return "mp3";
|
|
116
|
+
case "opus":
|
|
117
|
+
return "opus";
|
|
118
|
+
case "vorbis":
|
|
119
|
+
return "vorbis";
|
|
120
|
+
case "flac":
|
|
121
|
+
return "flac";
|
|
122
|
+
case "ac3":
|
|
123
|
+
return "ac3";
|
|
124
|
+
case "eac3":
|
|
125
|
+
return "eac3";
|
|
126
|
+
default:
|
|
127
|
+
return c ? c : "unknown";
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function avbridgeAudioToMediabunny(c) {
|
|
131
|
+
switch (c) {
|
|
132
|
+
case "aac":
|
|
133
|
+
return "aac";
|
|
134
|
+
case "mp3":
|
|
135
|
+
return "mp3";
|
|
136
|
+
case "opus":
|
|
137
|
+
return "opus";
|
|
138
|
+
case "vorbis":
|
|
139
|
+
return "vorbis";
|
|
140
|
+
case "flac":
|
|
141
|
+
return "flac";
|
|
142
|
+
case "ac3":
|
|
143
|
+
return "ac3";
|
|
144
|
+
case "eac3":
|
|
145
|
+
return "eac3";
|
|
146
|
+
default:
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function safeNumber(fn) {
|
|
151
|
+
try {
|
|
152
|
+
const v = await fn();
|
|
153
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
154
|
+
} catch {
|
|
155
|
+
return void 0;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function safe(fn) {
|
|
159
|
+
try {
|
|
160
|
+
return await fn();
|
|
161
|
+
} catch {
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/probe/index.ts
|
|
167
|
+
var MEDIABUNNY_CONTAINERS = /* @__PURE__ */ new Set([
|
|
168
|
+
"mp4",
|
|
169
|
+
"mov",
|
|
170
|
+
"mkv",
|
|
171
|
+
"webm",
|
|
172
|
+
"ogg",
|
|
173
|
+
"wav",
|
|
174
|
+
"mp3",
|
|
175
|
+
"flac",
|
|
176
|
+
"adts",
|
|
177
|
+
"mpegts"
|
|
178
|
+
]);
|
|
179
|
+
async function probe(source, transport) {
|
|
180
|
+
const normalized = await normalizeSource(source, transport);
|
|
181
|
+
const sniffed = await sniffNormalizedSource(normalized);
|
|
182
|
+
if (MEDIABUNNY_CONTAINERS.has(sniffed)) {
|
|
183
|
+
try {
|
|
184
|
+
const result = await probeWithMediabunny(normalized, sniffed);
|
|
185
|
+
const hasUnknownCodec = result.videoTracks.some((t) => t.codec === "unknown") || result.audioTracks.some((t) => t.codec === "unknown");
|
|
186
|
+
if (hasUnknownCodec) {
|
|
187
|
+
try {
|
|
188
|
+
const { probeWithLibav } = await import('./avi-NJXAXUXK.js');
|
|
189
|
+
return await probeWithLibav(normalized, sniffed);
|
|
190
|
+
} catch {
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
} catch (mediabunnyErr) {
|
|
196
|
+
console.warn(
|
|
197
|
+
`[avbridge] mediabunny rejected ${sniffed} file, falling back to libav:`,
|
|
198
|
+
mediabunnyErr.message
|
|
199
|
+
);
|
|
200
|
+
try {
|
|
201
|
+
const { probeWithLibav } = await import('./avi-NJXAXUXK.js');
|
|
202
|
+
return await probeWithLibav(normalized, sniffed);
|
|
203
|
+
} catch (libavErr) {
|
|
204
|
+
const mbMsg = mediabunnyErr.message || String(mediabunnyErr);
|
|
205
|
+
const lvMsg = libavErr instanceof Error ? libavErr.message : String(libavErr);
|
|
206
|
+
throw new AvbridgeError(
|
|
207
|
+
ERR_PROBE_FAILED,
|
|
208
|
+
`Failed to probe ${sniffed.toUpperCase()} file. mediabunny: ${mbMsg}. libav: ${lvMsg}.`,
|
|
209
|
+
"The file may be corrupt, truncated, or in an unsupported format. Enable AVBRIDGE_DEBUG for detailed logs."
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const { probeWithLibav } = await import('./avi-NJXAXUXK.js');
|
|
216
|
+
return await probeWithLibav(normalized, sniffed);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
const inner = err instanceof Error ? err.message : String(err);
|
|
219
|
+
console.error("[avbridge] libav probe failed for", sniffed, "file:", err);
|
|
220
|
+
if (sniffed === "unknown") {
|
|
221
|
+
throw new AvbridgeError(
|
|
222
|
+
ERR_PROBE_UNKNOWN_CONTAINER,
|
|
223
|
+
`Unable to probe source: container format could not be identified. libav fallback: ${inner || "(no details)"}`,
|
|
224
|
+
"The file may be corrupt or in a format avbridge doesn't recognize. Check the file plays in VLC or ffprobe."
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
throw new AvbridgeError(
|
|
228
|
+
ERR_LIBAV_NOT_REACHABLE,
|
|
229
|
+
`${sniffed.toUpperCase()} files require libav.js, which failed to load: ${inner || "(no details)"}`,
|
|
230
|
+
"Install @libav.js/variant-webcodecs, or check that AVBRIDGE_LIBAV_BASE points to the correct path."
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export { avbridgeAudioToMediabunny, avbridgeVideoToMediabunny, buildMediabunnySourceFromInput, probe };
|
|
236
|
+
//# sourceMappingURL=chunk-SR3MPV4D.js.map
|
|
237
|
+
//# sourceMappingURL=chunk-SR3MPV4D.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/probe/mediabunny.ts","../src/probe/index.ts"],"names":[],"mappings":"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","file":"chunk-SR3MPV4D.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"]}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { prepareLibavInput } from './chunk-DCSOQH2N.js';
|
|
2
|
+
import { loadLibav } from './chunk-5DMTJVIU.js';
|
|
3
|
+
import { pickLibavVariant } from './chunk-5YAWWKA3.js';
|
|
4
|
+
|
|
5
|
+
// src/util/libav-demux.ts
|
|
6
|
+
async function openLibavDemux(opts) {
|
|
7
|
+
const variant = opts.variant ?? pickLibavVariant(opts.context);
|
|
8
|
+
const libav = await loadLibav(variant);
|
|
9
|
+
const inputHandle = await prepareLibavInput(
|
|
10
|
+
libav,
|
|
11
|
+
opts.filename,
|
|
12
|
+
opts.source,
|
|
13
|
+
opts.transport
|
|
14
|
+
);
|
|
15
|
+
const readPkt = await libav.av_packet_alloc();
|
|
16
|
+
const [fmtCtx, streams] = await libav.ff_init_demuxer_file(opts.filename);
|
|
17
|
+
const videoStream = streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_VIDEO) ?? null;
|
|
18
|
+
const audioStream = streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_AUDIO) ?? null;
|
|
19
|
+
let destroyed = false;
|
|
20
|
+
async function pump(cb) {
|
|
21
|
+
while (!destroyed) {
|
|
22
|
+
if (cb.signal?.aborted) return;
|
|
23
|
+
let readErr;
|
|
24
|
+
let packets;
|
|
25
|
+
try {
|
|
26
|
+
[readErr, packets] = await libav.ff_read_frame_multi(fmtCtx, readPkt, {
|
|
27
|
+
// 16 KB batch — chosen so each read produces a handful of
|
|
28
|
+
// packets, keeping downstream queues bounded. Same rationale
|
|
29
|
+
// as the hybrid/fallback pumps (see CLAUDE.md note).
|
|
30
|
+
limit: 16 * 1024
|
|
31
|
+
});
|
|
32
|
+
} catch (err) {
|
|
33
|
+
throw new Error(`libav-demux: ff_read_frame_multi failed: ${err.message}`);
|
|
34
|
+
}
|
|
35
|
+
if (destroyed || cb.signal?.aborted) return;
|
|
36
|
+
const videoPackets = videoStream ? packets[videoStream.index] : void 0;
|
|
37
|
+
const audioPackets = audioStream ? packets[audioStream.index] : void 0;
|
|
38
|
+
if (cb.onAudioPackets && audioPackets && audioPackets.length > 0) {
|
|
39
|
+
await cb.onAudioPackets(audioPackets);
|
|
40
|
+
}
|
|
41
|
+
if (destroyed || cb.signal?.aborted) return;
|
|
42
|
+
if (cb.onVideoPackets && videoPackets && videoPackets.length > 0) {
|
|
43
|
+
await cb.onVideoPackets(videoPackets);
|
|
44
|
+
}
|
|
45
|
+
if (readErr === libav.AVERROR_EOF) {
|
|
46
|
+
if (cb.onEof) await cb.onEof();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (readErr && readErr !== 0 && readErr !== -libav.EAGAIN) {
|
|
50
|
+
throw new Error(`libav-demux: ff_read_frame_multi returned ${readErr}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async function destroy() {
|
|
55
|
+
destroyed = true;
|
|
56
|
+
try {
|
|
57
|
+
await libav.av_packet_free?.(readPkt);
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
await libav.avformat_close_input_js(fmtCtx);
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
await inputHandle.detach();
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
libav,
|
|
71
|
+
fmtCtx,
|
|
72
|
+
streams,
|
|
73
|
+
videoStream,
|
|
74
|
+
audioStream,
|
|
75
|
+
transport: inputHandle.transport,
|
|
76
|
+
pump,
|
|
77
|
+
destroy
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function sanitizePacketTimestamp(pkt, nextUs, fallbackTimeBase) {
|
|
81
|
+
const lo = pkt.pts ?? 0;
|
|
82
|
+
const hi = pkt.ptshi ?? 0;
|
|
83
|
+
const isInvalid = hi === -2147483648 && lo === 0 || !Number.isFinite(lo);
|
|
84
|
+
if (isInvalid) {
|
|
85
|
+
const us2 = nextUs();
|
|
86
|
+
pkt.pts = us2;
|
|
87
|
+
pkt.ptshi = 0;
|
|
88
|
+
pkt.time_base_num = 1;
|
|
89
|
+
pkt.time_base_den = 1e6;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const tb = fallbackTimeBase ?? [1, 1e6];
|
|
93
|
+
const pts64 = hi * 4294967296 + lo;
|
|
94
|
+
const us = Math.round(pts64 * 1e6 * tb[0] / tb[1]);
|
|
95
|
+
if (Number.isFinite(us) && Math.abs(us) <= Number.MAX_SAFE_INTEGER) {
|
|
96
|
+
pkt.pts = us;
|
|
97
|
+
pkt.ptshi = us < 0 ? -1 : 0;
|
|
98
|
+
pkt.time_base_num = 1;
|
|
99
|
+
pkt.time_base_den = 1e6;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const fallback = nextUs();
|
|
103
|
+
pkt.pts = fallback;
|
|
104
|
+
pkt.ptshi = 0;
|
|
105
|
+
pkt.time_base_num = 1;
|
|
106
|
+
pkt.time_base_den = 1e6;
|
|
107
|
+
}
|
|
108
|
+
var AV_SAMPLE_FMT_U8 = 0;
|
|
109
|
+
var AV_SAMPLE_FMT_S16 = 1;
|
|
110
|
+
var AV_SAMPLE_FMT_S32 = 2;
|
|
111
|
+
var AV_SAMPLE_FMT_FLT = 3;
|
|
112
|
+
var AV_SAMPLE_FMT_U8P = 5;
|
|
113
|
+
var AV_SAMPLE_FMT_S16P = 6;
|
|
114
|
+
var AV_SAMPLE_FMT_S32P = 7;
|
|
115
|
+
var AV_SAMPLE_FMT_FLTP = 8;
|
|
116
|
+
function libavFrameToInterleavedFloat32(frame) {
|
|
117
|
+
const channels = frame.channels ?? frame.ch_layout_nb_channels ?? 1;
|
|
118
|
+
const sampleRate = frame.sample_rate ?? 44100;
|
|
119
|
+
const nbSamples = frame.nb_samples ?? 0;
|
|
120
|
+
if (nbSamples === 0) return null;
|
|
121
|
+
const out = new Float32Array(nbSamples * channels);
|
|
122
|
+
switch (frame.format) {
|
|
123
|
+
case AV_SAMPLE_FMT_FLTP: {
|
|
124
|
+
const planes = ensurePlanes(frame.data, channels);
|
|
125
|
+
for (let ch = 0; ch < channels; ch++) {
|
|
126
|
+
const plane = asFloat32(planes[ch]);
|
|
127
|
+
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i];
|
|
128
|
+
}
|
|
129
|
+
return { data: out, channels, sampleRate };
|
|
130
|
+
}
|
|
131
|
+
case AV_SAMPLE_FMT_FLT: {
|
|
132
|
+
const flat = asFloat32(frame.data);
|
|
133
|
+
for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i];
|
|
134
|
+
return { data: out, channels, sampleRate };
|
|
135
|
+
}
|
|
136
|
+
case AV_SAMPLE_FMT_S16P: {
|
|
137
|
+
const planes = ensurePlanes(frame.data, channels);
|
|
138
|
+
for (let ch = 0; ch < channels; ch++) {
|
|
139
|
+
const plane = asInt16(planes[ch]);
|
|
140
|
+
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 32768;
|
|
141
|
+
}
|
|
142
|
+
return { data: out, channels, sampleRate };
|
|
143
|
+
}
|
|
144
|
+
case AV_SAMPLE_FMT_S16: {
|
|
145
|
+
const flat = asInt16(frame.data);
|
|
146
|
+
for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 32768;
|
|
147
|
+
return { data: out, channels, sampleRate };
|
|
148
|
+
}
|
|
149
|
+
case AV_SAMPLE_FMT_S32P: {
|
|
150
|
+
const planes = ensurePlanes(frame.data, channels);
|
|
151
|
+
for (let ch = 0; ch < channels; ch++) {
|
|
152
|
+
const plane = asInt32(planes[ch]);
|
|
153
|
+
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 2147483648;
|
|
154
|
+
}
|
|
155
|
+
return { data: out, channels, sampleRate };
|
|
156
|
+
}
|
|
157
|
+
case AV_SAMPLE_FMT_S32: {
|
|
158
|
+
const flat = asInt32(frame.data);
|
|
159
|
+
for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 2147483648;
|
|
160
|
+
return { data: out, channels, sampleRate };
|
|
161
|
+
}
|
|
162
|
+
case AV_SAMPLE_FMT_U8P: {
|
|
163
|
+
const planes = ensurePlanes(frame.data, channels);
|
|
164
|
+
for (let ch = 0; ch < channels; ch++) {
|
|
165
|
+
const plane = asUint8(planes[ch]);
|
|
166
|
+
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = (plane[i] - 128) / 128;
|
|
167
|
+
}
|
|
168
|
+
return { data: out, channels, sampleRate };
|
|
169
|
+
}
|
|
170
|
+
case AV_SAMPLE_FMT_U8: {
|
|
171
|
+
const flat = asUint8(frame.data);
|
|
172
|
+
for (let i = 0; i < nbSamples * channels; i++) out[i] = (flat[i] - 128) / 128;
|
|
173
|
+
return { data: out, channels, sampleRate };
|
|
174
|
+
}
|
|
175
|
+
default:
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function ensurePlanes(data, channels) {
|
|
180
|
+
if (Array.isArray(data)) return data;
|
|
181
|
+
const arr = data;
|
|
182
|
+
const len = arr.length;
|
|
183
|
+
const perChannel = Math.floor(len / channels);
|
|
184
|
+
const planes = [];
|
|
185
|
+
for (let ch = 0; ch < channels; ch++) {
|
|
186
|
+
planes.push(arr.subarray ? arr.subarray(ch * perChannel, (ch + 1) * perChannel) : arr);
|
|
187
|
+
}
|
|
188
|
+
return planes;
|
|
189
|
+
}
|
|
190
|
+
function asFloat32(x) {
|
|
191
|
+
if (x instanceof Float32Array) return x;
|
|
192
|
+
const ta = x;
|
|
193
|
+
return new Float32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);
|
|
194
|
+
}
|
|
195
|
+
function asInt16(x) {
|
|
196
|
+
if (x instanceof Int16Array) return x;
|
|
197
|
+
const ta = x;
|
|
198
|
+
return new Int16Array(ta.buffer, ta.byteOffset, ta.byteLength / 2);
|
|
199
|
+
}
|
|
200
|
+
function asInt32(x) {
|
|
201
|
+
if (x instanceof Int32Array) return x;
|
|
202
|
+
const ta = x;
|
|
203
|
+
return new Int32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);
|
|
204
|
+
}
|
|
205
|
+
function asUint8(x) {
|
|
206
|
+
if (x instanceof Uint8Array) return x;
|
|
207
|
+
const ta = x;
|
|
208
|
+
return new Uint8Array(ta.buffer, ta.byteOffset, ta.byteLength);
|
|
209
|
+
}
|
|
210
|
+
function sanitizeFrameTimestamp(frame, nextUs, fallbackTimeBase) {
|
|
211
|
+
const lo = frame.pts ?? 0;
|
|
212
|
+
const hi = frame.ptshi ?? 0;
|
|
213
|
+
const isInvalid = hi === -2147483648 && lo === 0 || !Number.isFinite(lo);
|
|
214
|
+
if (isInvalid) {
|
|
215
|
+
const us2 = nextUs();
|
|
216
|
+
frame.pts = us2;
|
|
217
|
+
frame.ptshi = 0;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const tb = fallbackTimeBase ?? [1, 1e6];
|
|
221
|
+
const pts64 = hi * 4294967296 + lo;
|
|
222
|
+
const us = Math.round(pts64 * 1e6 * tb[0] / tb[1]);
|
|
223
|
+
if (Number.isFinite(us) && Math.abs(us) <= Number.MAX_SAFE_INTEGER) {
|
|
224
|
+
frame.pts = us;
|
|
225
|
+
frame.ptshi = us < 0 ? -1 : 0;
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const fallback = nextUs();
|
|
229
|
+
frame.pts = fallback;
|
|
230
|
+
frame.ptshi = 0;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export { libavFrameToInterleavedFloat32, openLibavDemux, sanitizeFrameTimestamp, sanitizePacketTimestamp };
|
|
234
|
+
//# sourceMappingURL=chunk-X2K3GIWE.js.map
|
|
235
|
+
//# sourceMappingURL=chunk-X2K3GIWE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/util/libav-demux.ts"],"names":["us"],"mappings":"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","file":"chunk-X2K3GIWE.js","sourcesContent":["/**\n * Shared libav demux session. Opens a libav demuxer over a NormalizedSource\n * and provides a linear, cancellable packet pump.\n *\n * Phase 1 API: deliberately minimal. The first consumer is the AVI/ASF/FLV\n * transcode path (src/convert/transcode-libav.ts), which is strictly linear.\n * No seek, no track swapping — those were added to hybrid/fallback's\n * private pumps for playback reasons. When those paths migrate here, the\n * API will grow to cover their needs.\n *\n * The shared timestamp sanitizers (sanitizePacketTimestamp,\n * sanitizeFrameTimestamp) also live here. They were previously duplicated\n * in convert/remux.ts and strategies/hybrid/decoder.ts. The duplicates\n * stay put in Phase 1 with TODO pointers; migration is a follow-up.\n */\n\nimport { loadLibav, type LibavVariant } from \"../strategies/fallback/libav-loader.js\";\nimport { pickLibavVariant } from \"../strategies/fallback/variant-routing.js\";\nimport { prepareLibavInput } from \"./libav-http-reader.js\";\nimport type { MediaContext, TransportConfig } from \"../types.js\";\nimport type { NormalizedSource } from \"./source.js\";\n\n// ─────────────────────────────────────────────────────────────────────────\n// Structural types (mirror libav.js' shape without dragging in its types)\n// ─────────────────────────────────────────────────────────────────────────\n\nexport interface LibavStream {\n index: number;\n codec_type: number;\n codec_id: number;\n codecpar: number;\n time_base_num?: number;\n time_base_den?: number;\n}\n\nexport interface LibavPacket {\n data: Uint8Array;\n pts: number;\n ptshi?: number;\n duration?: number;\n durationhi?: number;\n flags: number;\n stream_index: number;\n time_base_num?: number;\n time_base_den?: number;\n}\n\nexport interface LibavFrame {\n data: unknown;\n format: number;\n channels?: number;\n ch_layout_nb_channels?: number;\n sample_rate?: number;\n nb_samples?: number;\n pts?: number;\n ptshi?: number;\n width?: number;\n height?: number;\n}\n\ninterface LibavRuntime {\n AVMEDIA_TYPE_VIDEO: number;\n AVMEDIA_TYPE_AUDIO: number;\n AVERROR_EOF: number;\n EAGAIN: number;\n\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_read_frame_multi(\n fmt_ctx: number,\n pkt: number,\n opts?: { limit?: number },\n ): Promise<[number, Record<number, LibavPacket[]>]>;\n av_packet_alloc(): Promise<number>;\n av_packet_free?(pkt: number): Promise<void>;\n avformat_close_input_js(ctx: number): Promise<void>;\n f64toi64?(val: number): [number, number];\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Session\n// ─────────────────────────────────────────────────────────────────────────\n\nexport interface LibavDemuxSession {\n readonly libav: LibavRuntime;\n readonly fmtCtx: number;\n readonly streams: LibavStream[];\n readonly videoStream: LibavStream | null;\n readonly audioStream: LibavStream | null;\n /** True when the input is being streamed via HTTP Range requests. */\n readonly transport: \"http-range\" | \"blob\";\n /**\n * Linear read-to-EOF pump. Invokes the callbacks for each\n * ff_read_frame_multi batch (audio is handed over before video per\n * batch, matching the audio-first ordering that the hybrid/fallback\n * playback pumps use — see POSTMORTEMS.md entry 1).\n *\n * Honors the AbortSignal between batches. Invokes `onEof` once when\n * the demuxer returns EOF. Does NOT handle seek.\n */\n pump(cb: {\n onVideoPackets?: (pkts: LibavPacket[]) => Promise<void>;\n onAudioPackets?: (pkts: LibavPacket[]) => Promise<void>;\n onEof?: () => Promise<void>;\n signal?: AbortSignal;\n }): Promise<void>;\n destroy(): Promise<void>;\n}\n\nexport interface OpenLibavDemuxOptions {\n source: NormalizedSource;\n filename: string;\n context: MediaContext;\n transport?: TransportConfig;\n /** Override automatic variant picking. Defaults to pickLibavVariant(context). */\n variant?: LibavVariant;\n}\n\nexport async function openLibavDemux(opts: OpenLibavDemuxOptions): Promise<LibavDemuxSession> {\n const variant: LibavVariant = opts.variant ?? pickLibavVariant(opts.context);\n const libav = (await loadLibav(variant)) as unknown as LibavRuntime;\n\n const inputHandle = await prepareLibavInput(\n libav as unknown as Parameters<typeof prepareLibavInput>[0],\n opts.filename,\n opts.source,\n opts.transport,\n );\n\n const readPkt = await libav.av_packet_alloc();\n const [fmtCtx, streams] = await libav.ff_init_demuxer_file(opts.filename);\n const videoStream = streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_VIDEO) ?? null;\n const audioStream = streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_AUDIO) ?? null;\n\n let destroyed = false;\n\n async function pump(cb: Parameters<LibavDemuxSession[\"pump\"]>[0]): Promise<void> {\n while (!destroyed) {\n if (cb.signal?.aborted) return;\n\n let readErr: number;\n let packets: Record<number, LibavPacket[]>;\n try {\n [readErr, packets] = await libav.ff_read_frame_multi(fmtCtx, readPkt, {\n // 16 KB batch — chosen so each read produces a handful of\n // packets, keeping downstream queues bounded. Same rationale\n // as the hybrid/fallback pumps (see CLAUDE.md note).\n limit: 16 * 1024,\n });\n } catch (err) {\n throw new Error(`libav-demux: ff_read_frame_multi failed: ${(err as Error).message}`);\n }\n\n if (destroyed || cb.signal?.aborted) return;\n\n const videoPackets = videoStream ? packets[videoStream.index] : undefined;\n const audioPackets = audioStream ? packets[audioStream.index] : undefined;\n\n // Audio-first ordering. Audio decode is cheap; video decode can\n // be expensive. Feeding audio first ensures the audio consumer\n // has samples to work with before any long video-decode block.\n if (cb.onAudioPackets && audioPackets && audioPackets.length > 0) {\n await cb.onAudioPackets(audioPackets);\n }\n if (destroyed || cb.signal?.aborted) return;\n if (cb.onVideoPackets && videoPackets && videoPackets.length > 0) {\n await cb.onVideoPackets(videoPackets);\n }\n\n if (readErr === libav.AVERROR_EOF) {\n if (cb.onEof) await cb.onEof();\n return;\n }\n if (readErr && readErr !== 0 && readErr !== -libav.EAGAIN) {\n throw new Error(`libav-demux: ff_read_frame_multi returned ${readErr}`);\n }\n }\n }\n\n async function destroy(): Promise<void> {\n destroyed = true;\n try { await libav.av_packet_free?.(readPkt); } catch { /* ignore */ }\n try { await libav.avformat_close_input_js(fmtCtx); } catch { /* ignore */ }\n try { await inputHandle.detach(); } catch { /* ignore */ }\n }\n\n return {\n libav,\n fmtCtx,\n streams,\n videoStream,\n audioStream,\n transport: inputHandle.transport,\n pump,\n destroy,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Timestamp sanitizers (extracted from convert/remux.ts + hybrid/decoder.ts)\n//\n// libav can hand us packets/frames with pts = AV_NOPTS_VALUE (encoded as\n// ptshi = -2147483648, pts = 0) for inputs whose demuxer can't determine\n// presentation times. AVI is the canonical example. Downstream consumers\n// that treat pts as int64 overflow and throw.\n//\n// The sanitizer replaces invalid pts with a synthetic microsecond counter,\n// and normalizes valid pts to a 1/1e6 time_base so consumers don't need\n// to track the source time_base per packet.\n// ─────────────────────────────────────────────────────────────────────────\n\n/**\n * Sanitize a libav packet's timestamp. Mutates `pkt` in place.\n * If the packet has AV_NOPTS_VALUE, replaces pts with `nextUs()`.\n * Otherwise normalizes to µs with time_base = 1/1_000_000.\n */\nexport function sanitizePacketTimestamp(\n pkt: LibavPacket,\n nextUs: () => number,\n fallbackTimeBase?: [number, number],\n): void {\n const lo = pkt.pts ?? 0;\n const hi = pkt.ptshi ?? 0;\n const isInvalid = (hi === -2147483648 && lo === 0) || !Number.isFinite(lo);\n if (isInvalid) {\n const us = nextUs();\n pkt.pts = us;\n pkt.ptshi = 0;\n pkt.time_base_num = 1;\n pkt.time_base_den = 1_000_000;\n return;\n }\n const tb = fallbackTimeBase ?? [1, 1_000_000];\n const pts64 = hi * 0x100000000 + lo;\n const us = Math.round((pts64 * 1_000_000 * tb[0]) / tb[1]);\n if (Number.isFinite(us) && Math.abs(us) <= Number.MAX_SAFE_INTEGER) {\n pkt.pts = us;\n pkt.ptshi = us < 0 ? -1 : 0;\n pkt.time_base_num = 1;\n pkt.time_base_den = 1_000_000;\n return;\n }\n const fallback = nextUs();\n pkt.pts = fallback;\n pkt.ptshi = 0;\n pkt.time_base_num = 1;\n pkt.time_base_den = 1_000_000;\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// Audio frame → interleaved Float32 (extracted from\n// strategies/hybrid/decoder.ts + strategies/fallback/decoder.ts).\n//\n// libav hands us decoded audio frames in whichever sample format the codec\n// uses (FLTP, S16P, etc.). Most downstream consumers (Web Audio, WebCodecs\n// AudioEncoder) want interleaved Float32. This does the conversion without\n// any dependencies.\n// ─────────────────────────────────────────────────────────────────────────\n\nconst AV_SAMPLE_FMT_U8 = 0;\nconst AV_SAMPLE_FMT_S16 = 1;\nconst AV_SAMPLE_FMT_S32 = 2;\nconst AV_SAMPLE_FMT_FLT = 3;\nconst AV_SAMPLE_FMT_U8P = 5;\nconst AV_SAMPLE_FMT_S16P = 6;\nconst AV_SAMPLE_FMT_S32P = 7;\nconst AV_SAMPLE_FMT_FLTP = 8;\n\nexport interface InterleavedSamples {\n data: Float32Array;\n channels: number;\n sampleRate: number;\n}\n\nexport function libavFrameToInterleavedFloat32(frame: LibavFrame): InterleavedSamples | null {\n const channels = frame.channels ?? frame.ch_layout_nb_channels ?? 1;\n const sampleRate = frame.sample_rate ?? 44100;\n const nbSamples = frame.nb_samples ?? 0;\n if (nbSamples === 0) return null;\n\n const out = new Float32Array(nbSamples * channels);\n\n switch (frame.format) {\n case AV_SAMPLE_FMT_FLTP: {\n const planes = ensurePlanes(frame.data, channels);\n for (let ch = 0; ch < channels; ch++) {\n const plane = asFloat32(planes[ch]);\n for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i];\n }\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_FLT: {\n const flat = asFloat32(frame.data);\n for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i];\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_S16P: {\n const planes = ensurePlanes(frame.data, channels);\n for (let ch = 0; ch < channels; ch++) {\n const plane = asInt16(planes[ch]);\n for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 32768;\n }\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_S16: {\n const flat = asInt16(frame.data);\n for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 32768;\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_S32P: {\n const planes = ensurePlanes(frame.data, channels);\n for (let ch = 0; ch < channels; ch++) {\n const plane = asInt32(planes[ch]);\n for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 2147483648;\n }\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_S32: {\n const flat = asInt32(frame.data);\n for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 2147483648;\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_U8P: {\n const planes = ensurePlanes(frame.data, channels);\n for (let ch = 0; ch < channels; ch++) {\n const plane = asUint8(planes[ch]);\n for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = (plane[i] - 128) / 128;\n }\n return { data: out, channels, sampleRate };\n }\n case AV_SAMPLE_FMT_U8: {\n const flat = asUint8(frame.data);\n for (let i = 0; i < nbSamples * channels; i++) out[i] = (flat[i] - 128) / 128;\n return { data: out, channels, sampleRate };\n }\n default:\n return null;\n }\n}\n\nfunction ensurePlanes(data: unknown, channels: number): unknown[] {\n if (Array.isArray(data)) return data;\n const arr = data as { length: number; subarray?: (a: number, b: number) => unknown };\n const len = arr.length;\n const perChannel = Math.floor(len / channels);\n const planes: unknown[] = [];\n for (let ch = 0; ch < channels; ch++) {\n planes.push(arr.subarray ? arr.subarray(ch * perChannel, (ch + 1) * perChannel) : arr);\n }\n return planes;\n}\n\nfunction asFloat32(x: unknown): Float32Array {\n if (x instanceof Float32Array) return x;\n const ta = x as { buffer: ArrayBuffer; byteOffset: number; byteLength: number };\n return new Float32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);\n}\nfunction asInt16(x: unknown): Int16Array {\n if (x instanceof Int16Array) return x;\n const ta = x as { buffer: ArrayBuffer; byteOffset: number; byteLength: number };\n return new Int16Array(ta.buffer, ta.byteOffset, ta.byteLength / 2);\n}\nfunction asInt32(x: unknown): Int32Array {\n if (x instanceof Int32Array) return x;\n const ta = x as { buffer: ArrayBuffer; byteOffset: number; byteLength: number };\n return new Int32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);\n}\nfunction asUint8(x: unknown): Uint8Array {\n if (x instanceof Uint8Array) return x;\n const ta = x as { buffer: ArrayBuffer; byteOffset: number; byteLength: number };\n return new Uint8Array(ta.buffer, ta.byteOffset, ta.byteLength);\n}\n\n/**\n * Sanitize a decoded frame's timestamp. Mutates `frame` in place.\n * Returns nothing; callers that want derived metadata (e.g. a\n * VideoFrame timestamp in µs) should read `frame.pts` after calling.\n */\nexport function sanitizeFrameTimestamp(\n frame: LibavFrame,\n nextUs: () => number,\n fallbackTimeBase?: [number, number],\n): void {\n const lo = frame.pts ?? 0;\n const hi = frame.ptshi ?? 0;\n const isInvalid = (hi === -2147483648 && lo === 0) || !Number.isFinite(lo);\n if (isInvalid) {\n const us = nextUs();\n frame.pts = us;\n frame.ptshi = 0;\n return;\n }\n const tb = fallbackTimeBase ?? [1, 1_000_000];\n const pts64 = hi * 0x100000000 + lo;\n const us = Math.round((pts64 * 1_000_000 * tb[0]) / tb[1]);\n if (Number.isFinite(us) && Math.abs(us) <= Number.MAX_SAFE_INTEGER) {\n frame.pts = us;\n frame.ptshi = us < 0 ? -1 : 0;\n return;\n }\n const fallback = nextUs();\n frame.pts = fallback;\n frame.ptshi = 0;\n}\n"]}
|