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
|
@@ -1,241 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkS4WAZC2T_cjs = require('./chunk-S4WAZC2T.cjs');
|
|
4
|
+
var chunkZCUXHW55_cjs = require('./chunk-ZCUXHW55.cjs');
|
|
5
|
+
var chunk2IJ66NTD_cjs = require('./chunk-2IJ66NTD.cjs');
|
|
6
|
+
var chunkCPZ7PXAM_cjs = require('./chunk-CPZ7PXAM.cjs');
|
|
4
7
|
var chunkG4APZMCP_cjs = require('./chunk-G4APZMCP.cjs');
|
|
5
8
|
var chunkF3LQJKXK_cjs = require('./chunk-F3LQJKXK.cjs');
|
|
6
9
|
|
|
7
|
-
// src/probe/mediabunny.ts
|
|
8
|
-
async function probeWithMediabunny(source, sniffedContainer) {
|
|
9
|
-
const mb = await import('mediabunny');
|
|
10
|
-
const input = new mb.Input({
|
|
11
|
-
source: await buildMediabunnySource(mb, source),
|
|
12
|
-
formats: mb.ALL_FORMATS
|
|
13
|
-
});
|
|
14
|
-
const allTracks = await input.getTracks();
|
|
15
|
-
const duration = await safeNumber(() => input.computeDuration());
|
|
16
|
-
const videoTracks = [];
|
|
17
|
-
const audioTracks = [];
|
|
18
|
-
for (const track of allTracks) {
|
|
19
|
-
if (track.isVideoTrack()) {
|
|
20
|
-
const codecParam = await safe(() => track.getCodecParameterString());
|
|
21
|
-
videoTracks.push({
|
|
22
|
-
id: track.id,
|
|
23
|
-
codec: mediabunnyVideoToAvbridge(track.codec),
|
|
24
|
-
width: track.displayWidth ?? track.codedWidth ?? 0,
|
|
25
|
-
height: track.displayHeight ?? track.codedHeight ?? 0,
|
|
26
|
-
codecString: codecParam ?? void 0
|
|
27
|
-
});
|
|
28
|
-
} else if (track.isAudioTrack()) {
|
|
29
|
-
const codecParam = await safe(() => track.getCodecParameterString());
|
|
30
|
-
audioTracks.push({
|
|
31
|
-
id: track.id,
|
|
32
|
-
codec: mediabunnyAudioToAvbridge(track.codec),
|
|
33
|
-
channels: track.numberOfChannels ?? 0,
|
|
34
|
-
sampleRate: track.sampleRate ?? 0,
|
|
35
|
-
language: track.languageCode,
|
|
36
|
-
codecString: codecParam ?? void 0
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const format = await safe(() => input.getFormat());
|
|
41
|
-
const container = resolveContainer(format?.name, sniffedContainer);
|
|
42
|
-
return {
|
|
43
|
-
source: source.original,
|
|
44
|
-
name: source.name,
|
|
45
|
-
byteLength: source.byteLength,
|
|
46
|
-
container,
|
|
47
|
-
videoTracks,
|
|
48
|
-
audioTracks,
|
|
49
|
-
subtitleTracks: [],
|
|
50
|
-
probedBy: "mediabunny",
|
|
51
|
-
duration
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
async function buildMediabunnySource(mb, source) {
|
|
55
|
-
if (source.kind === "url") {
|
|
56
|
-
return new mb.UrlSource(source.url);
|
|
57
|
-
}
|
|
58
|
-
return new mb.BlobSource(source.blob);
|
|
59
|
-
}
|
|
60
|
-
async function buildMediabunnySourceFromInput(mb, source) {
|
|
61
|
-
if (typeof source === "string") return new mb.UrlSource(source);
|
|
62
|
-
if (source instanceof URL) return new mb.UrlSource(source.toString());
|
|
63
|
-
if (source instanceof Blob) return new mb.BlobSource(source);
|
|
64
|
-
if (source instanceof ArrayBuffer) return new mb.BlobSource(new Blob([source]));
|
|
65
|
-
if (source instanceof Uint8Array) return new mb.BlobSource(new Blob([source]));
|
|
66
|
-
throw new TypeError("unsupported source type for mediabunny");
|
|
67
|
-
}
|
|
68
|
-
function resolveContainer(formatName, sniffed) {
|
|
69
|
-
const name = (formatName ?? "").toLowerCase();
|
|
70
|
-
if (name.includes("matroska") || name.includes("mkv")) return "mkv";
|
|
71
|
-
if (name.includes("webm")) return "webm";
|
|
72
|
-
if (name.includes("mp4") || name.includes("isom")) return "mp4";
|
|
73
|
-
if (name.includes("mov") || name.includes("quicktime")) return "mov";
|
|
74
|
-
if (name.includes("ogg")) return "ogg";
|
|
75
|
-
if (name.includes("wav")) return "wav";
|
|
76
|
-
if (name.includes("flac")) return "flac";
|
|
77
|
-
if (name.includes("mp3")) return "mp3";
|
|
78
|
-
if (name.includes("adts") || name.includes("aac")) return "adts";
|
|
79
|
-
if (name.includes("mpegts") || name.includes("mpeg-ts") || name.includes("transport")) return "mpegts";
|
|
80
|
-
return sniffed;
|
|
81
|
-
}
|
|
82
|
-
function mediabunnyVideoToAvbridge(c) {
|
|
83
|
-
switch (c) {
|
|
84
|
-
case "avc":
|
|
85
|
-
return "h264";
|
|
86
|
-
case "hevc":
|
|
87
|
-
return "h265";
|
|
88
|
-
case "vp8":
|
|
89
|
-
return "vp8";
|
|
90
|
-
case "vp9":
|
|
91
|
-
return "vp9";
|
|
92
|
-
case "av1":
|
|
93
|
-
return "av1";
|
|
94
|
-
default:
|
|
95
|
-
return c ? c : "unknown";
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
function avbridgeVideoToMediabunny(c) {
|
|
99
|
-
switch (c) {
|
|
100
|
-
case "h264":
|
|
101
|
-
return "avc";
|
|
102
|
-
case "h265":
|
|
103
|
-
return "hevc";
|
|
104
|
-
case "vp8":
|
|
105
|
-
return "vp8";
|
|
106
|
-
case "vp9":
|
|
107
|
-
return "vp9";
|
|
108
|
-
case "av1":
|
|
109
|
-
return "av1";
|
|
110
|
-
default:
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
function mediabunnyAudioToAvbridge(c) {
|
|
115
|
-
switch (c) {
|
|
116
|
-
case "aac":
|
|
117
|
-
return "aac";
|
|
118
|
-
case "mp3":
|
|
119
|
-
return "mp3";
|
|
120
|
-
case "opus":
|
|
121
|
-
return "opus";
|
|
122
|
-
case "vorbis":
|
|
123
|
-
return "vorbis";
|
|
124
|
-
case "flac":
|
|
125
|
-
return "flac";
|
|
126
|
-
case "ac3":
|
|
127
|
-
return "ac3";
|
|
128
|
-
case "eac3":
|
|
129
|
-
return "eac3";
|
|
130
|
-
default:
|
|
131
|
-
return c ? c : "unknown";
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
function avbridgeAudioToMediabunny(c) {
|
|
135
|
-
switch (c) {
|
|
136
|
-
case "aac":
|
|
137
|
-
return "aac";
|
|
138
|
-
case "mp3":
|
|
139
|
-
return "mp3";
|
|
140
|
-
case "opus":
|
|
141
|
-
return "opus";
|
|
142
|
-
case "vorbis":
|
|
143
|
-
return "vorbis";
|
|
144
|
-
case "flac":
|
|
145
|
-
return "flac";
|
|
146
|
-
case "ac3":
|
|
147
|
-
return "ac3";
|
|
148
|
-
case "eac3":
|
|
149
|
-
return "eac3";
|
|
150
|
-
default:
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
async function safeNumber(fn) {
|
|
155
|
-
try {
|
|
156
|
-
const v = await fn();
|
|
157
|
-
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
158
|
-
} catch {
|
|
159
|
-
return void 0;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
async function safe(fn) {
|
|
163
|
-
try {
|
|
164
|
-
return await fn();
|
|
165
|
-
} catch {
|
|
166
|
-
return void 0;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// src/probe/index.ts
|
|
171
|
-
var MEDIABUNNY_CONTAINERS = /* @__PURE__ */ new Set([
|
|
172
|
-
"mp4",
|
|
173
|
-
"mov",
|
|
174
|
-
"mkv",
|
|
175
|
-
"webm",
|
|
176
|
-
"ogg",
|
|
177
|
-
"wav",
|
|
178
|
-
"mp3",
|
|
179
|
-
"flac",
|
|
180
|
-
"adts",
|
|
181
|
-
"mpegts"
|
|
182
|
-
]);
|
|
183
|
-
async function probe(source, transport) {
|
|
184
|
-
const normalized = await chunk6UUT4BEA_cjs.normalizeSource(source, transport);
|
|
185
|
-
const sniffed = await chunk6UUT4BEA_cjs.sniffNormalizedSource(normalized);
|
|
186
|
-
if (MEDIABUNNY_CONTAINERS.has(sniffed)) {
|
|
187
|
-
try {
|
|
188
|
-
const result = await probeWithMediabunny(normalized, sniffed);
|
|
189
|
-
const hasUnknownCodec = result.videoTracks.some((t) => t.codec === "unknown") || result.audioTracks.some((t) => t.codec === "unknown");
|
|
190
|
-
if (hasUnknownCodec) {
|
|
191
|
-
try {
|
|
192
|
-
const { probeWithLibav } = await import('./avi-W6L3BTWU.cjs');
|
|
193
|
-
return await probeWithLibav(normalized, sniffed);
|
|
194
|
-
} catch {
|
|
195
|
-
return result;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return result;
|
|
199
|
-
} catch (mediabunnyErr) {
|
|
200
|
-
console.warn(
|
|
201
|
-
`[avbridge] mediabunny rejected ${sniffed} file, falling back to libav:`,
|
|
202
|
-
mediabunnyErr.message
|
|
203
|
-
);
|
|
204
|
-
try {
|
|
205
|
-
const { probeWithLibav } = await import('./avi-W6L3BTWU.cjs');
|
|
206
|
-
return await probeWithLibav(normalized, sniffed);
|
|
207
|
-
} catch (libavErr) {
|
|
208
|
-
const mbMsg = mediabunnyErr.message || String(mediabunnyErr);
|
|
209
|
-
const lvMsg = libavErr instanceof Error ? libavErr.message : String(libavErr);
|
|
210
|
-
throw new chunk6UUT4BEA_cjs.AvbridgeError(
|
|
211
|
-
chunk6UUT4BEA_cjs.ERR_PROBE_FAILED,
|
|
212
|
-
`Failed to probe ${sniffed.toUpperCase()} file. mediabunny: ${mbMsg}. libav: ${lvMsg}.`,
|
|
213
|
-
"The file may be corrupt, truncated, or in an unsupported format. Enable AVBRIDGE_DEBUG for detailed logs."
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
try {
|
|
219
|
-
const { probeWithLibav } = await import('./avi-W6L3BTWU.cjs');
|
|
220
|
-
return await probeWithLibav(normalized, sniffed);
|
|
221
|
-
} catch (err) {
|
|
222
|
-
const inner = err instanceof Error ? err.message : String(err);
|
|
223
|
-
console.error("[avbridge] libav probe failed for", sniffed, "file:", err);
|
|
224
|
-
if (sniffed === "unknown") {
|
|
225
|
-
throw new chunk6UUT4BEA_cjs.AvbridgeError(
|
|
226
|
-
chunk6UUT4BEA_cjs.ERR_PROBE_UNKNOWN_CONTAINER,
|
|
227
|
-
`Unable to probe source: container format could not be identified. libav fallback: ${inner || "(no details)"}`,
|
|
228
|
-
"The file may be corrupt or in a format avbridge doesn't recognize. Check the file plays in VLC or ffprobe."
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
throw new chunk6UUT4BEA_cjs.AvbridgeError(
|
|
232
|
-
chunk6UUT4BEA_cjs.ERR_LIBAV_NOT_REACHABLE,
|
|
233
|
-
`${sniffed.toUpperCase()} files require libav.js, which failed to load: ${inner || "(no details)"}`,
|
|
234
|
-
"Install @libav.js/variant-webcodecs, or check that AVBRIDGE_LIBAV_BASE points to the correct path."
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
10
|
// src/util/codec-strings.ts
|
|
240
11
|
function videoCodecString(track) {
|
|
241
12
|
if (track.codecString) return track.codecString;
|
|
@@ -485,36 +256,6 @@ function isRiskyNative(video) {
|
|
|
485
256
|
return false;
|
|
486
257
|
}
|
|
487
258
|
|
|
488
|
-
// src/subtitles/srt.ts
|
|
489
|
-
function srtToVtt(srt) {
|
|
490
|
-
if (srt.charCodeAt(0) === 65279) srt = srt.slice(1);
|
|
491
|
-
const normalized = srt.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim();
|
|
492
|
-
const blocks = normalized.split(/\n{2,}/);
|
|
493
|
-
const out = ["WEBVTT", ""];
|
|
494
|
-
for (const block of blocks) {
|
|
495
|
-
const lines = block.split("\n");
|
|
496
|
-
if (lines.length > 0 && /^\d+$/.test(lines[0].trim())) {
|
|
497
|
-
lines.shift();
|
|
498
|
-
}
|
|
499
|
-
if (lines.length === 0) continue;
|
|
500
|
-
const timing = lines.shift();
|
|
501
|
-
const vttTiming = convertTiming(timing);
|
|
502
|
-
if (!vttTiming) continue;
|
|
503
|
-
out.push(vttTiming);
|
|
504
|
-
for (const l of lines) out.push(l);
|
|
505
|
-
out.push("");
|
|
506
|
-
}
|
|
507
|
-
return out.join("\n");
|
|
508
|
-
}
|
|
509
|
-
function convertTiming(line) {
|
|
510
|
-
const m = /^(\d{1,2}):(\d{2}):(\d{2})[,.](\d{1,3})\s*-->\s*(\d{1,2}):(\d{2}):(\d{2})[,.](\d{1,3})(.*)$/.exec(
|
|
511
|
-
line.trim()
|
|
512
|
-
);
|
|
513
|
-
if (!m) return null;
|
|
514
|
-
const fmt = (h, mm, s, ms) => `${h.padStart(2, "0")}:${mm}:${s}.${ms.padEnd(3, "0").slice(0, 3)}`;
|
|
515
|
-
return `${fmt(m[1], m[2], m[3], m[4])} --> ${fmt(m[5], m[6], m[7], m[8])}${m[9] ?? ""}`;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
259
|
// src/events.ts
|
|
519
260
|
var TypedEmitter = class {
|
|
520
261
|
listeners = {};
|
|
@@ -720,7 +461,7 @@ async function createNativeSession(context, video) {
|
|
|
720
461
|
},
|
|
721
462
|
async setAudioTrack(id) {
|
|
722
463
|
const tracks = video.audioTracks;
|
|
723
|
-
if (!tracks) return;
|
|
464
|
+
if (!tracks || tracks.length === 0) return;
|
|
724
465
|
for (let i = 0; i < tracks.length; i++) {
|
|
725
466
|
tracks[i].enabled = tracks[i].id === String(id) || i === id;
|
|
726
467
|
}
|
|
@@ -772,15 +513,15 @@ var MseSink = class {
|
|
|
772
513
|
constructor(options) {
|
|
773
514
|
this.options = options;
|
|
774
515
|
if (typeof MediaSource === "undefined") {
|
|
775
|
-
throw new
|
|
776
|
-
|
|
516
|
+
throw new chunk2IJ66NTD_cjs.AvbridgeError(
|
|
517
|
+
chunk2IJ66NTD_cjs.ERR_MSE_NOT_SUPPORTED,
|
|
777
518
|
"MediaSource Extensions (MSE) are not supported in this environment.",
|
|
778
519
|
"MSE is required for the remux strategy. Use a browser that supports MSE, or try the fallback strategy."
|
|
779
520
|
);
|
|
780
521
|
}
|
|
781
522
|
if (!MediaSource.isTypeSupported(options.mime)) {
|
|
782
|
-
throw new
|
|
783
|
-
|
|
523
|
+
throw new chunk2IJ66NTD_cjs.AvbridgeError(
|
|
524
|
+
chunk2IJ66NTD_cjs.ERR_MSE_CODEC_NOT_SUPPORTED,
|
|
784
525
|
`This browser's MSE does not support "${options.mime}".`,
|
|
785
526
|
"The codec combination can't be played via remux in this browser. The player will try the next strategy automatically."
|
|
786
527
|
);
|
|
@@ -968,30 +709,49 @@ var MseSink = class {
|
|
|
968
709
|
async function createRemuxPipeline(ctx, video) {
|
|
969
710
|
const mb = await import('mediabunny');
|
|
970
711
|
const videoTrackInfo = ctx.videoTracks[0];
|
|
971
|
-
const audioTrackInfo = ctx.audioTracks[0];
|
|
972
712
|
if (!videoTrackInfo) throw new Error("remux: source has no video track");
|
|
973
|
-
const mbVideoCodec = avbridgeVideoToMediabunny(videoTrackInfo.codec);
|
|
713
|
+
const mbVideoCodec = chunkZCUXHW55_cjs.avbridgeVideoToMediabunny(videoTrackInfo.codec);
|
|
974
714
|
if (!mbVideoCodec) {
|
|
975
715
|
throw new Error(`remux: video codec "${videoTrackInfo.codec}" is not supported by mediabunny output`);
|
|
976
716
|
}
|
|
977
|
-
const mbAudioCodec = audioTrackInfo ? avbridgeAudioToMediabunny(audioTrackInfo.codec) : null;
|
|
978
717
|
const input = new mb.Input({
|
|
979
|
-
source: await buildMediabunnySourceFromInput(mb, ctx.source),
|
|
718
|
+
source: await chunkZCUXHW55_cjs.buildMediabunnySourceFromInput(mb, ctx.source),
|
|
980
719
|
formats: mb.ALL_FORMATS
|
|
981
720
|
});
|
|
982
721
|
const allTracks = await input.getTracks();
|
|
983
722
|
const inputVideo = allTracks.find((t) => t.id === videoTrackInfo.id && t.isVideoTrack());
|
|
984
|
-
const inputAudio = audioTrackInfo ? allTracks.find((t) => t.id === audioTrackInfo.id && t.isAudioTrack()) : null;
|
|
985
723
|
if (!inputVideo || !inputVideo.isVideoTrack()) {
|
|
986
724
|
throw new Error("remux: video track not found in input");
|
|
987
725
|
}
|
|
988
|
-
if (audioTrackInfo && (!inputAudio || !inputAudio.isAudioTrack())) {
|
|
989
|
-
throw new Error("remux: audio track not found in input");
|
|
990
|
-
}
|
|
991
726
|
const videoConfig = await inputVideo.getDecoderConfig();
|
|
992
|
-
const audioConfig = inputAudio && inputAudio.isAudioTrack() ? await inputAudio.getDecoderConfig() : null;
|
|
993
727
|
const videoSink = new mb.EncodedPacketSink(inputVideo);
|
|
994
|
-
|
|
728
|
+
let selectedAudioTrackId = ctx.audioTracks[0]?.id ?? null;
|
|
729
|
+
let inputAudio = null;
|
|
730
|
+
let mbAudioCodec = null;
|
|
731
|
+
let audioSink = null;
|
|
732
|
+
let audioConfig = null;
|
|
733
|
+
async function rebuildAudio() {
|
|
734
|
+
if (selectedAudioTrackId == null) {
|
|
735
|
+
inputAudio = null;
|
|
736
|
+
mbAudioCodec = null;
|
|
737
|
+
audioSink = null;
|
|
738
|
+
audioConfig = null;
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
const trackInfo = ctx.audioTracks.find((t) => t.id === selectedAudioTrackId);
|
|
742
|
+
if (!trackInfo) {
|
|
743
|
+
throw new Error(`remux: no audio track with id ${selectedAudioTrackId}`);
|
|
744
|
+
}
|
|
745
|
+
const newInput = allTracks.find((t) => t.id === trackInfo.id && t.isAudioTrack());
|
|
746
|
+
if (!newInput || !newInput.isAudioTrack()) {
|
|
747
|
+
throw new Error("remux: audio track not found in input");
|
|
748
|
+
}
|
|
749
|
+
inputAudio = newInput;
|
|
750
|
+
mbAudioCodec = chunkZCUXHW55_cjs.avbridgeAudioToMediabunny(trackInfo.codec);
|
|
751
|
+
audioSink = new mb.EncodedPacketSink(newInput);
|
|
752
|
+
audioConfig = await newInput.getDecoderConfig();
|
|
753
|
+
}
|
|
754
|
+
await rebuildAudio();
|
|
995
755
|
let sink = null;
|
|
996
756
|
const stats = { videoPackets: 0, audioPackets: 0, bytesWritten: 0, fragments: 0 };
|
|
997
757
|
let destroyed = false;
|
|
@@ -1116,6 +876,30 @@ async function createRemuxPipeline(ctx, video) {
|
|
|
1116
876
|
pendingAutoPlay = autoPlay;
|
|
1117
877
|
if (sink) sink.setPlayOnSeek(autoPlay);
|
|
1118
878
|
},
|
|
879
|
+
async setAudioTrack(trackId, time, autoPlay) {
|
|
880
|
+
if (selectedAudioTrackId === trackId) return;
|
|
881
|
+
if (!ctx.audioTracks.some((t) => t.id === trackId)) {
|
|
882
|
+
console.warn("[avbridge] remux: setAudioTrack \u2014 unknown track id", trackId);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
pumpToken++;
|
|
886
|
+
selectedAudioTrackId = trackId;
|
|
887
|
+
await rebuildAudio().catch((err) => {
|
|
888
|
+
console.warn("[avbridge] remux: rebuildAudio failed:", err.message);
|
|
889
|
+
});
|
|
890
|
+
if (sink) {
|
|
891
|
+
try {
|
|
892
|
+
sink.destroy();
|
|
893
|
+
} catch {
|
|
894
|
+
}
|
|
895
|
+
sink = null;
|
|
896
|
+
}
|
|
897
|
+
pendingAutoPlay = autoPlay;
|
|
898
|
+
pendingStartTime = time;
|
|
899
|
+
pumpLoop(++pumpToken, time).catch((err) => {
|
|
900
|
+
console.error("[avbridge] remux pipeline setAudioTrack pump failed:", err);
|
|
901
|
+
});
|
|
902
|
+
},
|
|
1119
903
|
async destroy() {
|
|
1120
904
|
destroyed = true;
|
|
1121
905
|
pumpToken++;
|
|
@@ -1175,7 +959,19 @@ async function createRemuxSession(context, video) {
|
|
|
1175
959
|
const wasPlaying = !video.paused;
|
|
1176
960
|
await pipeline.seek(time, wasPlaying || wantPlay);
|
|
1177
961
|
},
|
|
1178
|
-
async setAudioTrack(
|
|
962
|
+
async setAudioTrack(id) {
|
|
963
|
+
if (!context.audioTracks.some((t) => t.id === id)) {
|
|
964
|
+
console.warn("[avbridge] remux: setAudioTrack \u2014 unknown track id", id);
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
const wasPlaying = !video.paused;
|
|
968
|
+
const time = video.currentTime || 0;
|
|
969
|
+
if (!started) {
|
|
970
|
+
started = true;
|
|
971
|
+
await pipeline.setAudioTrack(id, time, wantPlay || wasPlaying);
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
await pipeline.setAudioTrack(id, time, wasPlaying || wantPlay);
|
|
1179
975
|
},
|
|
1180
976
|
async setSubtitleTrack(id) {
|
|
1181
977
|
const tracks = video.textTracks;
|
|
@@ -1228,6 +1024,9 @@ var VideoRenderer = class {
|
|
|
1228
1024
|
document.body.appendChild(this.canvas);
|
|
1229
1025
|
}
|
|
1230
1026
|
target.style.visibility = "hidden";
|
|
1027
|
+
const overlayParent = parent instanceof HTMLElement ? parent : document.body;
|
|
1028
|
+
this.subtitleOverlay = new chunkS4WAZC2T_cjs.SubtitleOverlay(overlayParent);
|
|
1029
|
+
this.watchTextTracks(target);
|
|
1231
1030
|
const ctx = this.canvas.getContext("2d");
|
|
1232
1031
|
if (!ctx) throw new Error("video renderer: failed to acquire 2D context");
|
|
1233
1032
|
this.ctx = ctx;
|
|
@@ -1253,6 +1052,15 @@ var VideoRenderer = class {
|
|
|
1253
1052
|
ticksWaiting = 0;
|
|
1254
1053
|
/** Cumulative count of ticks where PTS mode painted a frame. */
|
|
1255
1054
|
ticksPainted = 0;
|
|
1055
|
+
/**
|
|
1056
|
+
* Subtitle overlay div attached to the stage wrapper alongside the
|
|
1057
|
+
* canvas. Created lazily when subtitle tracks are attached via the
|
|
1058
|
+
* target's `<track>` children. Canvas strategies (hybrid, fallback)
|
|
1059
|
+
* hide the <video>, so we can't rely on the browser's native cue
|
|
1060
|
+
* rendering; we read TextTrack.cues and render into this overlay.
|
|
1061
|
+
*/
|
|
1062
|
+
subtitleOverlay = null;
|
|
1063
|
+
subtitleTrack = null;
|
|
1256
1064
|
/**
|
|
1257
1065
|
* Calibration offset (microseconds) between video PTS and audio clock.
|
|
1258
1066
|
* Video PTS and AudioContext.currentTime can drift ~0.1% relative to
|
|
@@ -1296,9 +1104,80 @@ var VideoRenderer = class {
|
|
|
1296
1104
|
this.framesDroppedOverflow++;
|
|
1297
1105
|
}
|
|
1298
1106
|
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Watch the target <video>'s textTracks list. When a track is added,
|
|
1109
|
+
* grab it and start polling cues on each render tick. Existing tracks
|
|
1110
|
+
* (if any) are picked up immediately.
|
|
1111
|
+
*/
|
|
1112
|
+
watchTextTracks(target) {
|
|
1113
|
+
const pick = () => {
|
|
1114
|
+
if (this.subtitleTrack) return;
|
|
1115
|
+
const tracks = target.textTracks;
|
|
1116
|
+
if (isDebug()) {
|
|
1117
|
+
console.log(`[avbridge:subs] watchTextTracks pick() \u2014 ${tracks.length} tracks`);
|
|
1118
|
+
}
|
|
1119
|
+
for (let i = 0; i < tracks.length; i++) {
|
|
1120
|
+
const t = tracks[i];
|
|
1121
|
+
if (isDebug()) {
|
|
1122
|
+
console.log(`[avbridge:subs] track ${i}: kind=${t.kind} mode=${t.mode} cues=${t.cues?.length ?? 0}`);
|
|
1123
|
+
}
|
|
1124
|
+
if (t.kind === "subtitles" || t.kind === "captions") {
|
|
1125
|
+
this.subtitleTrack = t;
|
|
1126
|
+
t.mode = "hidden";
|
|
1127
|
+
if (isDebug()) {
|
|
1128
|
+
console.log(`[avbridge:subs] picked track, mode=hidden`);
|
|
1129
|
+
}
|
|
1130
|
+
const trackEl = target.querySelector(`track[srclang="${t.language}"]`);
|
|
1131
|
+
if (trackEl) {
|
|
1132
|
+
trackEl.addEventListener("load", () => {
|
|
1133
|
+
if (isDebug()) {
|
|
1134
|
+
console.log(`[avbridge:subs] track element loaded, cues=${t.cues?.length ?? 0}`);
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
trackEl.addEventListener("error", (ev) => {
|
|
1138
|
+
console.warn(`[avbridge:subs] track element error:`, ev);
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
break;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
pick();
|
|
1146
|
+
if (typeof target.textTracks.addEventListener === "function") {
|
|
1147
|
+
target.textTracks.addEventListener("addtrack", (e) => {
|
|
1148
|
+
if (isDebug()) {
|
|
1149
|
+
console.log("[avbridge:subs] addtrack event fired");
|
|
1150
|
+
}
|
|
1151
|
+
pick();
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
_loggedCues = false;
|
|
1156
|
+
/** Find the active cue (if any) for the given media time. */
|
|
1157
|
+
updateSubtitles() {
|
|
1158
|
+
if (!this.subtitleOverlay || !this.subtitleTrack) return;
|
|
1159
|
+
const cues = this.subtitleTrack.cues;
|
|
1160
|
+
if (!cues || cues.length === 0) return;
|
|
1161
|
+
if (isDebug() && !this._loggedCues) {
|
|
1162
|
+
this._loggedCues = true;
|
|
1163
|
+
console.log(`[avbridge:subs] cues available: ${cues.length}, first start=${cues[0].startTime}, last end=${cues[cues.length - 1].endTime}`);
|
|
1164
|
+
}
|
|
1165
|
+
const t = this.clock.now();
|
|
1166
|
+
let activeText = "";
|
|
1167
|
+
for (let i = 0; i < cues.length; i++) {
|
|
1168
|
+
const c = cues[i];
|
|
1169
|
+
if (t >= c.startTime && t <= c.endTime) {
|
|
1170
|
+
const vttCue = c;
|
|
1171
|
+
activeText = vttCue.text ?? "";
|
|
1172
|
+
break;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
this.subtitleOverlay.setText(activeText.replace(/<[^>]+>/g, ""));
|
|
1176
|
+
}
|
|
1299
1177
|
tick() {
|
|
1300
1178
|
if (this.destroyed) return;
|
|
1301
1179
|
this.rafHandle = requestAnimationFrame(this.tick);
|
|
1180
|
+
this.updateSubtitles();
|
|
1302
1181
|
if (this.queue.length === 0) return;
|
|
1303
1182
|
const playing = this.clock.isPlaying();
|
|
1304
1183
|
if (!playing) {
|
|
@@ -1427,6 +1306,11 @@ var VideoRenderer = class {
|
|
|
1427
1306
|
this.destroyed = true;
|
|
1428
1307
|
if (this.rafHandle != null) cancelAnimationFrame(this.rafHandle);
|
|
1429
1308
|
this.flush();
|
|
1309
|
+
if (this.subtitleOverlay) {
|
|
1310
|
+
this.subtitleOverlay.destroy();
|
|
1311
|
+
this.subtitleOverlay = null;
|
|
1312
|
+
}
|
|
1313
|
+
this.subtitleTrack = null;
|
|
1430
1314
|
this.canvas.remove();
|
|
1431
1315
|
this.target.style.visibility = "";
|
|
1432
1316
|
}
|
|
@@ -1676,7 +1560,8 @@ async function startHybridDecoder(opts) {
|
|
|
1676
1560
|
const readPkt = await libav.av_packet_alloc();
|
|
1677
1561
|
const [fmt_ctx, streams] = await libav.ff_init_demuxer_file(opts.filename);
|
|
1678
1562
|
const videoStream = streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_VIDEO) ?? null;
|
|
1679
|
-
const
|
|
1563
|
+
const firstAudioTrackId = opts.context.audioTracks[0]?.id;
|
|
1564
|
+
let audioStream = (firstAudioTrackId != null ? streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_AUDIO && s.index === firstAudioTrackId) : void 0) ?? streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_AUDIO) ?? null;
|
|
1680
1565
|
if (!videoStream && !audioStream) {
|
|
1681
1566
|
throw new Error("hybrid decoder: file has no decodable streams");
|
|
1682
1567
|
}
|
|
@@ -1830,7 +1715,7 @@ async function startHybridDecoder(opts) {
|
|
|
1830
1715
|
const processed = await applyBSF(videoPackets);
|
|
1831
1716
|
for (const pkt of processed) {
|
|
1832
1717
|
if (myToken !== pumpToken || destroyed) return;
|
|
1833
|
-
sanitizePacketTimestamp(pkt, () => {
|
|
1718
|
+
chunkCPZ7PXAM_cjs.sanitizePacketTimestamp(pkt, () => {
|
|
1834
1719
|
const ts = syntheticVideoUs;
|
|
1835
1720
|
syntheticVideoUs += videoFrameStepUs;
|
|
1836
1721
|
return ts;
|
|
@@ -1909,7 +1794,7 @@ async function startHybridDecoder(opts) {
|
|
|
1909
1794
|
const frames = allFrames;
|
|
1910
1795
|
for (const f of frames) {
|
|
1911
1796
|
if (myToken !== pumpToken || destroyed) return;
|
|
1912
|
-
sanitizeFrameTimestamp(
|
|
1797
|
+
chunkCPZ7PXAM_cjs.sanitizeFrameTimestamp(
|
|
1913
1798
|
f,
|
|
1914
1799
|
() => {
|
|
1915
1800
|
const ts = syntheticAudioUs;
|
|
@@ -1920,7 +1805,7 @@ async function startHybridDecoder(opts) {
|
|
|
1920
1805
|
},
|
|
1921
1806
|
audioTimeBase
|
|
1922
1807
|
);
|
|
1923
|
-
const samples = libavFrameToInterleavedFloat32(f);
|
|
1808
|
+
const samples = chunkCPZ7PXAM_cjs.libavFrameToInterleavedFloat32(f);
|
|
1924
1809
|
if (samples) {
|
|
1925
1810
|
opts.audio.schedule(samples.data, samples.channels, samples.sampleRate);
|
|
1926
1811
|
audioFramesDecoded++;
|
|
@@ -1972,6 +1857,71 @@ async function startHybridDecoder(opts) {
|
|
|
1972
1857
|
} catch {
|
|
1973
1858
|
}
|
|
1974
1859
|
},
|
|
1860
|
+
async setAudioTrack(trackId, timeSec) {
|
|
1861
|
+
if (audioStream && audioStream.index === trackId) return;
|
|
1862
|
+
const newStream = streams.find(
|
|
1863
|
+
(s) => s.codec_type === libav.AVMEDIA_TYPE_AUDIO && s.index === trackId
|
|
1864
|
+
);
|
|
1865
|
+
if (!newStream) {
|
|
1866
|
+
console.warn("[avbridge] hybrid: setAudioTrack \u2014 no stream with id", trackId);
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1869
|
+
const newToken = ++pumpToken;
|
|
1870
|
+
if (pumpRunning) {
|
|
1871
|
+
try {
|
|
1872
|
+
await pumpRunning;
|
|
1873
|
+
} catch {
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
if (destroyed) return;
|
|
1877
|
+
if (audioDec) {
|
|
1878
|
+
try {
|
|
1879
|
+
await libav.ff_free_decoder?.(audioDec.c, audioDec.pkt, audioDec.frame);
|
|
1880
|
+
} catch {
|
|
1881
|
+
}
|
|
1882
|
+
audioDec = null;
|
|
1883
|
+
}
|
|
1884
|
+
try {
|
|
1885
|
+
const [, c, pkt, frame] = await libav.ff_init_decoder(newStream.codec_id, {
|
|
1886
|
+
codecpar: newStream.codecpar
|
|
1887
|
+
});
|
|
1888
|
+
audioDec = { c, pkt, frame };
|
|
1889
|
+
audioTimeBase = newStream.time_base_num && newStream.time_base_den ? [newStream.time_base_num, newStream.time_base_den] : void 0;
|
|
1890
|
+
} catch (err) {
|
|
1891
|
+
console.warn(
|
|
1892
|
+
"[avbridge] hybrid: setAudioTrack init failed \u2014 switching to no-audio:",
|
|
1893
|
+
err.message
|
|
1894
|
+
);
|
|
1895
|
+
audioDec = null;
|
|
1896
|
+
opts.audio.setNoAudio();
|
|
1897
|
+
}
|
|
1898
|
+
audioStream = newStream;
|
|
1899
|
+
try {
|
|
1900
|
+
const tsUs = Math.floor(timeSec * 1e6);
|
|
1901
|
+
const [tsLo, tsHi] = libav.f64toi64 ? libav.f64toi64(tsUs) : [tsUs | 0, Math.floor(tsUs / 4294967296)];
|
|
1902
|
+
await libav.av_seek_frame(
|
|
1903
|
+
fmt_ctx,
|
|
1904
|
+
-1,
|
|
1905
|
+
tsLo,
|
|
1906
|
+
tsHi,
|
|
1907
|
+
libav.AVSEEK_FLAG_BACKWARD ?? 0
|
|
1908
|
+
);
|
|
1909
|
+
} catch (err) {
|
|
1910
|
+
console.warn("[avbridge] hybrid: setAudioTrack seek failed:", err);
|
|
1911
|
+
}
|
|
1912
|
+
try {
|
|
1913
|
+
if (videoDecoder && videoDecoder.state === "configured") {
|
|
1914
|
+
await videoDecoder.flush();
|
|
1915
|
+
}
|
|
1916
|
+
} catch {
|
|
1917
|
+
}
|
|
1918
|
+
await flushBSF();
|
|
1919
|
+
syntheticVideoUs = Math.round(timeSec * 1e6);
|
|
1920
|
+
syntheticAudioUs = Math.round(timeSec * 1e6);
|
|
1921
|
+
pumpRunning = pumpLoop(newToken).catch(
|
|
1922
|
+
(err) => console.error("[avbridge] hybrid pump failed (post-setAudioTrack):", err)
|
|
1923
|
+
);
|
|
1924
|
+
},
|
|
1975
1925
|
async seek(timeSec) {
|
|
1976
1926
|
const newToken = ++pumpToken;
|
|
1977
1927
|
if (pumpRunning) {
|
|
@@ -2029,161 +1979,9 @@ async function startHybridDecoder(opts) {
|
|
|
2029
1979
|
}
|
|
2030
1980
|
};
|
|
2031
1981
|
}
|
|
2032
|
-
function sanitizePacketTimestamp(pkt, nextUs, fallbackTimeBase) {
|
|
2033
|
-
const lo = pkt.pts ?? 0;
|
|
2034
|
-
const hi = pkt.ptshi ?? 0;
|
|
2035
|
-
const isInvalid = hi === -2147483648 && lo === 0 || !Number.isFinite(lo);
|
|
2036
|
-
if (isInvalid) {
|
|
2037
|
-
const us2 = nextUs();
|
|
2038
|
-
pkt.pts = us2;
|
|
2039
|
-
pkt.ptshi = 0;
|
|
2040
|
-
pkt.time_base_num = 1;
|
|
2041
|
-
pkt.time_base_den = 1e6;
|
|
2042
|
-
return;
|
|
2043
|
-
}
|
|
2044
|
-
const tb = fallbackTimeBase ?? [1, 1e6];
|
|
2045
|
-
const pts64 = hi * 4294967296 + lo;
|
|
2046
|
-
const us = Math.round(pts64 * 1e6 * tb[0] / tb[1]);
|
|
2047
|
-
if (Number.isFinite(us) && Math.abs(us) <= Number.MAX_SAFE_INTEGER) {
|
|
2048
|
-
pkt.pts = us;
|
|
2049
|
-
pkt.ptshi = us < 0 ? -1 : 0;
|
|
2050
|
-
pkt.time_base_num = 1;
|
|
2051
|
-
pkt.time_base_den = 1e6;
|
|
2052
|
-
return;
|
|
2053
|
-
}
|
|
2054
|
-
const fallback = nextUs();
|
|
2055
|
-
pkt.pts = fallback;
|
|
2056
|
-
pkt.ptshi = 0;
|
|
2057
|
-
pkt.time_base_num = 1;
|
|
2058
|
-
pkt.time_base_den = 1e6;
|
|
2059
|
-
}
|
|
2060
|
-
function sanitizeFrameTimestamp(frame, nextUs, fallbackTimeBase) {
|
|
2061
|
-
const lo = frame.pts ?? 0;
|
|
2062
|
-
const hi = frame.ptshi ?? 0;
|
|
2063
|
-
const isInvalid = hi === -2147483648 && lo === 0 || !Number.isFinite(lo);
|
|
2064
|
-
if (isInvalid) {
|
|
2065
|
-
const us2 = nextUs();
|
|
2066
|
-
frame.pts = us2;
|
|
2067
|
-
frame.ptshi = 0;
|
|
2068
|
-
return;
|
|
2069
|
-
}
|
|
2070
|
-
const tb = fallbackTimeBase ?? [1, 1e6];
|
|
2071
|
-
const pts64 = hi * 4294967296 + lo;
|
|
2072
|
-
const us = Math.round(pts64 * 1e6 * tb[0] / tb[1]);
|
|
2073
|
-
if (Number.isFinite(us) && Math.abs(us) <= Number.MAX_SAFE_INTEGER) {
|
|
2074
|
-
frame.pts = us;
|
|
2075
|
-
frame.ptshi = us < 0 ? -1 : 0;
|
|
2076
|
-
return;
|
|
2077
|
-
}
|
|
2078
|
-
const fallback = nextUs();
|
|
2079
|
-
frame.pts = fallback;
|
|
2080
|
-
frame.ptshi = 0;
|
|
2081
|
-
}
|
|
2082
|
-
var AV_SAMPLE_FMT_U8 = 0;
|
|
2083
|
-
var AV_SAMPLE_FMT_S16 = 1;
|
|
2084
|
-
var AV_SAMPLE_FMT_S32 = 2;
|
|
2085
|
-
var AV_SAMPLE_FMT_FLT = 3;
|
|
2086
|
-
var AV_SAMPLE_FMT_U8P = 5;
|
|
2087
|
-
var AV_SAMPLE_FMT_S16P = 6;
|
|
2088
|
-
var AV_SAMPLE_FMT_S32P = 7;
|
|
2089
|
-
var AV_SAMPLE_FMT_FLTP = 8;
|
|
2090
|
-
function libavFrameToInterleavedFloat32(frame) {
|
|
2091
|
-
const channels = frame.channels ?? frame.ch_layout_nb_channels ?? 1;
|
|
2092
|
-
const sampleRate = frame.sample_rate ?? 44100;
|
|
2093
|
-
const nbSamples = frame.nb_samples ?? 0;
|
|
2094
|
-
if (nbSamples === 0) return null;
|
|
2095
|
-
const out = new Float32Array(nbSamples * channels);
|
|
2096
|
-
switch (frame.format) {
|
|
2097
|
-
case AV_SAMPLE_FMT_FLTP: {
|
|
2098
|
-
const planes = ensurePlanes(frame.data, channels);
|
|
2099
|
-
for (let ch = 0; ch < channels; ch++) {
|
|
2100
|
-
const plane = asFloat32(planes[ch]);
|
|
2101
|
-
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i];
|
|
2102
|
-
}
|
|
2103
|
-
return { data: out, channels, sampleRate };
|
|
2104
|
-
}
|
|
2105
|
-
case AV_SAMPLE_FMT_FLT: {
|
|
2106
|
-
const flat = asFloat32(frame.data);
|
|
2107
|
-
for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i];
|
|
2108
|
-
return { data: out, channels, sampleRate };
|
|
2109
|
-
}
|
|
2110
|
-
case AV_SAMPLE_FMT_S16P: {
|
|
2111
|
-
const planes = ensurePlanes(frame.data, channels);
|
|
2112
|
-
for (let ch = 0; ch < channels; ch++) {
|
|
2113
|
-
const plane = asInt16(planes[ch]);
|
|
2114
|
-
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 32768;
|
|
2115
|
-
}
|
|
2116
|
-
return { data: out, channels, sampleRate };
|
|
2117
|
-
}
|
|
2118
|
-
case AV_SAMPLE_FMT_S16: {
|
|
2119
|
-
const flat = asInt16(frame.data);
|
|
2120
|
-
for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 32768;
|
|
2121
|
-
return { data: out, channels, sampleRate };
|
|
2122
|
-
}
|
|
2123
|
-
case AV_SAMPLE_FMT_S32P: {
|
|
2124
|
-
const planes = ensurePlanes(frame.data, channels);
|
|
2125
|
-
for (let ch = 0; ch < channels; ch++) {
|
|
2126
|
-
const plane = asInt32(planes[ch]);
|
|
2127
|
-
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 2147483648;
|
|
2128
|
-
}
|
|
2129
|
-
return { data: out, channels, sampleRate };
|
|
2130
|
-
}
|
|
2131
|
-
case AV_SAMPLE_FMT_S32: {
|
|
2132
|
-
const flat = asInt32(frame.data);
|
|
2133
|
-
for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 2147483648;
|
|
2134
|
-
return { data: out, channels, sampleRate };
|
|
2135
|
-
}
|
|
2136
|
-
case AV_SAMPLE_FMT_U8P: {
|
|
2137
|
-
const planes = ensurePlanes(frame.data, channels);
|
|
2138
|
-
for (let ch = 0; ch < channels; ch++) {
|
|
2139
|
-
const plane = asUint8(planes[ch]);
|
|
2140
|
-
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = (plane[i] - 128) / 128;
|
|
2141
|
-
}
|
|
2142
|
-
return { data: out, channels, sampleRate };
|
|
2143
|
-
}
|
|
2144
|
-
case AV_SAMPLE_FMT_U8: {
|
|
2145
|
-
const flat = asUint8(frame.data);
|
|
2146
|
-
for (let i = 0; i < nbSamples * channels; i++) out[i] = (flat[i] - 128) / 128;
|
|
2147
|
-
return { data: out, channels, sampleRate };
|
|
2148
|
-
}
|
|
2149
|
-
default:
|
|
2150
|
-
return null;
|
|
2151
|
-
}
|
|
2152
|
-
}
|
|
2153
|
-
function ensurePlanes(data, channels) {
|
|
2154
|
-
if (Array.isArray(data)) return data;
|
|
2155
|
-
const arr = data;
|
|
2156
|
-
const len = arr.length;
|
|
2157
|
-
const perChannel = Math.floor(len / channels);
|
|
2158
|
-
const planes = [];
|
|
2159
|
-
for (let ch = 0; ch < channels; ch++) {
|
|
2160
|
-
planes.push(arr.subarray ? arr.subarray(ch * perChannel, (ch + 1) * perChannel) : arr);
|
|
2161
|
-
}
|
|
2162
|
-
return planes;
|
|
2163
|
-
}
|
|
2164
|
-
function asFloat32(x) {
|
|
2165
|
-
if (x instanceof Float32Array) return x;
|
|
2166
|
-
const ta = x;
|
|
2167
|
-
return new Float32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);
|
|
2168
|
-
}
|
|
2169
|
-
function asInt16(x) {
|
|
2170
|
-
if (x instanceof Int16Array) return x;
|
|
2171
|
-
const ta = x;
|
|
2172
|
-
return new Int16Array(ta.buffer, ta.byteOffset, ta.byteLength / 2);
|
|
2173
|
-
}
|
|
2174
|
-
function asInt32(x) {
|
|
2175
|
-
if (x instanceof Int32Array) return x;
|
|
2176
|
-
const ta = x;
|
|
2177
|
-
return new Int32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);
|
|
2178
|
-
}
|
|
2179
|
-
function asUint8(x) {
|
|
2180
|
-
if (x instanceof Uint8Array) return x;
|
|
2181
|
-
const ta = x;
|
|
2182
|
-
return new Uint8Array(ta.buffer, ta.byteOffset, ta.byteLength);
|
|
2183
|
-
}
|
|
2184
1982
|
async function loadBridge() {
|
|
2185
1983
|
try {
|
|
2186
|
-
const wrapper = await import('./libav-import-
|
|
1984
|
+
const wrapper = await import('./libav-import-2ZVKV2E7.cjs');
|
|
2187
1985
|
return wrapper.libavBridge;
|
|
2188
1986
|
} catch (err) {
|
|
2189
1987
|
throw new Error(
|
|
@@ -2192,12 +1990,41 @@ async function loadBridge() {
|
|
|
2192
1990
|
}
|
|
2193
1991
|
}
|
|
2194
1992
|
|
|
1993
|
+
// src/util/time-ranges.ts
|
|
1994
|
+
function makeTimeRanges(ranges) {
|
|
1995
|
+
const frozen = ranges.slice();
|
|
1996
|
+
const impl = {
|
|
1997
|
+
get length() {
|
|
1998
|
+
return frozen.length;
|
|
1999
|
+
},
|
|
2000
|
+
start(index) {
|
|
2001
|
+
if (index < 0 || index >= frozen.length) {
|
|
2002
|
+
throw new DOMException(
|
|
2003
|
+
`TimeRanges.start: index ${index} out of range (length=${frozen.length})`,
|
|
2004
|
+
"IndexSizeError"
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
return frozen[index][0];
|
|
2008
|
+
},
|
|
2009
|
+
end(index) {
|
|
2010
|
+
if (index < 0 || index >= frozen.length) {
|
|
2011
|
+
throw new DOMException(
|
|
2012
|
+
`TimeRanges.end: index ${index} out of range (length=${frozen.length})`,
|
|
2013
|
+
"IndexSizeError"
|
|
2014
|
+
);
|
|
2015
|
+
}
|
|
2016
|
+
return frozen[index][1];
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
return impl;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2195
2022
|
// src/strategies/hybrid/index.ts
|
|
2196
2023
|
var READY_AUDIO_BUFFER_SECONDS = 0.3;
|
|
2197
2024
|
var READY_TIMEOUT_SECONDS = 10;
|
|
2198
2025
|
async function createHybridSession(ctx, target, transport) {
|
|
2199
|
-
const { normalizeSource
|
|
2200
|
-
const source = await
|
|
2026
|
+
const { normalizeSource } = await import('./source-VFLXLOCN.cjs');
|
|
2027
|
+
const source = await normalizeSource(ctx.source);
|
|
2201
2028
|
const fps = ctx.videoTracks[0]?.fps ?? 30;
|
|
2202
2029
|
const audio = new AudioOutput();
|
|
2203
2030
|
const renderer = new VideoRenderer(target, audio, fps);
|
|
@@ -2249,6 +2076,18 @@ async function createHybridSession(ctx, target, transport) {
|
|
|
2249
2076
|
get: () => ctx.duration ?? NaN
|
|
2250
2077
|
});
|
|
2251
2078
|
}
|
|
2079
|
+
Object.defineProperty(target, "readyState", {
|
|
2080
|
+
configurable: true,
|
|
2081
|
+
get: () => {
|
|
2082
|
+
if (!renderer.hasFrames()) return 0;
|
|
2083
|
+
if (!audio.isPlaying() && audio.bufferAhead() <= 0 && !audio.isNoAudio()) return 1;
|
|
2084
|
+
return 2;
|
|
2085
|
+
}
|
|
2086
|
+
});
|
|
2087
|
+
Object.defineProperty(target, "seekable", {
|
|
2088
|
+
configurable: true,
|
|
2089
|
+
get: () => makeTimeRanges(ctx.duration && Number.isFinite(ctx.duration) && ctx.duration > 0 ? [[0, ctx.duration]] : [])
|
|
2090
|
+
});
|
|
2252
2091
|
async function waitForBuffer() {
|
|
2253
2092
|
const start = performance.now();
|
|
2254
2093
|
while (true) {
|
|
@@ -2293,7 +2132,24 @@ async function createHybridSession(ctx, target, transport) {
|
|
|
2293
2132
|
async seek(time) {
|
|
2294
2133
|
await doSeek(time);
|
|
2295
2134
|
},
|
|
2296
|
-
async setAudioTrack(
|
|
2135
|
+
async setAudioTrack(id) {
|
|
2136
|
+
if (!ctx.audioTracks.some((t) => t.id === id)) {
|
|
2137
|
+
console.warn("[avbridge] hybrid: setAudioTrack \u2014 unknown track id", id);
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
const wasPlaying = audio.isPlaying();
|
|
2141
|
+
const currentTime = audio.now();
|
|
2142
|
+
await audio.pause().catch(() => {
|
|
2143
|
+
});
|
|
2144
|
+
await handles.setAudioTrack(id, currentTime).catch(
|
|
2145
|
+
(err) => console.warn("[avbridge] hybrid: handles.setAudioTrack failed:", err)
|
|
2146
|
+
);
|
|
2147
|
+
await audio.reset(currentTime);
|
|
2148
|
+
renderer.flush();
|
|
2149
|
+
if (wasPlaying) {
|
|
2150
|
+
await waitForBuffer();
|
|
2151
|
+
await audio.start();
|
|
2152
|
+
}
|
|
2297
2153
|
},
|
|
2298
2154
|
async setSubtitleTrack(_id) {
|
|
2299
2155
|
},
|
|
@@ -2313,6 +2169,8 @@ async function createHybridSession(ctx, target, transport) {
|
|
|
2313
2169
|
delete target.paused;
|
|
2314
2170
|
delete target.volume;
|
|
2315
2171
|
delete target.muted;
|
|
2172
|
+
delete target.readyState;
|
|
2173
|
+
delete target.seekable;
|
|
2316
2174
|
} catch {
|
|
2317
2175
|
}
|
|
2318
2176
|
},
|
|
@@ -2332,7 +2190,8 @@ async function startDecoder(opts) {
|
|
|
2332
2190
|
const readPkt = await libav.av_packet_alloc();
|
|
2333
2191
|
const [fmt_ctx, streams] = await libav.ff_init_demuxer_file(opts.filename);
|
|
2334
2192
|
const videoStream = streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_VIDEO) ?? null;
|
|
2335
|
-
const
|
|
2193
|
+
const firstAudioTrackId = opts.context.audioTracks[0]?.id;
|
|
2194
|
+
let audioStream = (firstAudioTrackId != null ? streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_AUDIO && s.index === firstAudioTrackId) : void 0) ?? streams.find((s) => s.codec_type === libav.AVMEDIA_TYPE_AUDIO) ?? null;
|
|
2336
2195
|
if (!videoStream && !audioStream) {
|
|
2337
2196
|
throw new Error("fallback decoder: file has no decodable streams");
|
|
2338
2197
|
}
|
|
@@ -2548,7 +2407,7 @@ async function startDecoder(opts) {
|
|
|
2548
2407
|
if (myToken !== pumpToken || destroyed) return;
|
|
2549
2408
|
for (const f of frames) {
|
|
2550
2409
|
if (myToken !== pumpToken || destroyed) return;
|
|
2551
|
-
|
|
2410
|
+
chunkCPZ7PXAM_cjs.sanitizeFrameTimestamp(
|
|
2552
2411
|
f,
|
|
2553
2412
|
() => {
|
|
2554
2413
|
const ts = syntheticVideoUs;
|
|
@@ -2558,7 +2417,7 @@ async function startDecoder(opts) {
|
|
|
2558
2417
|
videoTimeBase
|
|
2559
2418
|
);
|
|
2560
2419
|
try {
|
|
2561
|
-
const vf = bridge.laFrameToVideoFrame(f,
|
|
2420
|
+
const vf = bridge.laFrameToVideoFrame(f, { timeBase: [1, 1e6] });
|
|
2562
2421
|
opts.renderer.enqueue(vf);
|
|
2563
2422
|
videoFramesDecoded++;
|
|
2564
2423
|
} catch (err) {
|
|
@@ -2586,7 +2445,7 @@ async function startDecoder(opts) {
|
|
|
2586
2445
|
if (myToken !== pumpToken || destroyed) return;
|
|
2587
2446
|
for (const f of frames) {
|
|
2588
2447
|
if (myToken !== pumpToken || destroyed) return;
|
|
2589
|
-
|
|
2448
|
+
chunkCPZ7PXAM_cjs.sanitizeFrameTimestamp(
|
|
2590
2449
|
f,
|
|
2591
2450
|
() => {
|
|
2592
2451
|
const ts = syntheticAudioUs;
|
|
@@ -2597,7 +2456,7 @@ async function startDecoder(opts) {
|
|
|
2597
2456
|
},
|
|
2598
2457
|
audioTimeBase
|
|
2599
2458
|
);
|
|
2600
|
-
const samples =
|
|
2459
|
+
const samples = chunkCPZ7PXAM_cjs.libavFrameToInterleavedFloat32(f);
|
|
2601
2460
|
if (samples) {
|
|
2602
2461
|
opts.audio.schedule(samples.data, samples.channels, samples.sampleRate);
|
|
2603
2462
|
audioFramesDecoded++;
|
|
@@ -2645,6 +2504,69 @@ async function startDecoder(opts) {
|
|
|
2645
2504
|
} catch {
|
|
2646
2505
|
}
|
|
2647
2506
|
},
|
|
2507
|
+
async setAudioTrack(trackId, timeSec) {
|
|
2508
|
+
if (audioStream && audioStream.index === trackId) return;
|
|
2509
|
+
const newStream = streams.find(
|
|
2510
|
+
(s) => s.codec_type === libav.AVMEDIA_TYPE_AUDIO && s.index === trackId
|
|
2511
|
+
);
|
|
2512
|
+
if (!newStream) {
|
|
2513
|
+
console.warn("[avbridge] fallback: setAudioTrack \u2014 no stream with id", trackId);
|
|
2514
|
+
return;
|
|
2515
|
+
}
|
|
2516
|
+
const newToken = ++pumpToken;
|
|
2517
|
+
if (pumpRunning) {
|
|
2518
|
+
try {
|
|
2519
|
+
await pumpRunning;
|
|
2520
|
+
} catch {
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
if (destroyed) return;
|
|
2524
|
+
if (audioDec) {
|
|
2525
|
+
try {
|
|
2526
|
+
await libav.ff_free_decoder?.(audioDec.c, audioDec.pkt, audioDec.frame);
|
|
2527
|
+
} catch {
|
|
2528
|
+
}
|
|
2529
|
+
audioDec = null;
|
|
2530
|
+
}
|
|
2531
|
+
try {
|
|
2532
|
+
const [, c, pkt, frame] = await libav.ff_init_decoder(newStream.codec_id, {
|
|
2533
|
+
codecpar: newStream.codecpar
|
|
2534
|
+
});
|
|
2535
|
+
audioDec = { c, pkt, frame };
|
|
2536
|
+
audioTimeBase = newStream.time_base_num && newStream.time_base_den ? [newStream.time_base_num, newStream.time_base_den] : void 0;
|
|
2537
|
+
} catch (err) {
|
|
2538
|
+
console.warn(
|
|
2539
|
+
"[avbridge] fallback: setAudioTrack init failed \u2014 falling back to no-audio mode:",
|
|
2540
|
+
err.message
|
|
2541
|
+
);
|
|
2542
|
+
audioDec = null;
|
|
2543
|
+
opts.audio.setNoAudio();
|
|
2544
|
+
}
|
|
2545
|
+
audioStream = newStream;
|
|
2546
|
+
try {
|
|
2547
|
+
const tsUs = Math.floor(timeSec * 1e6);
|
|
2548
|
+
const [tsLo, tsHi] = libav.f64toi64 ? libav.f64toi64(tsUs) : [tsUs | 0, Math.floor(tsUs / 4294967296)];
|
|
2549
|
+
await libav.av_seek_frame(
|
|
2550
|
+
fmt_ctx,
|
|
2551
|
+
-1,
|
|
2552
|
+
tsLo,
|
|
2553
|
+
tsHi,
|
|
2554
|
+
libav.AVSEEK_FLAG_BACKWARD ?? 0
|
|
2555
|
+
);
|
|
2556
|
+
} catch (err) {
|
|
2557
|
+
console.warn("[avbridge] fallback: setAudioTrack seek failed:", err);
|
|
2558
|
+
}
|
|
2559
|
+
try {
|
|
2560
|
+
if (videoDec) await libav.avcodec_flush_buffers?.(videoDec.c);
|
|
2561
|
+
} catch {
|
|
2562
|
+
}
|
|
2563
|
+
await flushBSF();
|
|
2564
|
+
syntheticVideoUs = Math.round(timeSec * 1e6);
|
|
2565
|
+
syntheticAudioUs = Math.round(timeSec * 1e6);
|
|
2566
|
+
pumpRunning = pumpLoop(newToken).catch(
|
|
2567
|
+
(err) => console.error("[avbridge] fallback pump failed (post-setAudioTrack):", err)
|
|
2568
|
+
);
|
|
2569
|
+
},
|
|
2648
2570
|
async seek(timeSec) {
|
|
2649
2571
|
const newToken = ++pumpToken;
|
|
2650
2572
|
if (pumpRunning) {
|
|
@@ -2701,138 +2623,9 @@ async function startDecoder(opts) {
|
|
|
2701
2623
|
}
|
|
2702
2624
|
};
|
|
2703
2625
|
}
|
|
2704
|
-
function sanitizeFrameTimestamp2(frame, nextUs, fallbackTimeBase) {
|
|
2705
|
-
const lo = frame.pts ?? 0;
|
|
2706
|
-
const hi = frame.ptshi ?? 0;
|
|
2707
|
-
const isInvalid = hi === -2147483648 && lo === 0 || !Number.isFinite(lo);
|
|
2708
|
-
if (isInvalid) {
|
|
2709
|
-
const us2 = nextUs();
|
|
2710
|
-
frame.pts = us2;
|
|
2711
|
-
frame.ptshi = 0;
|
|
2712
|
-
return { timeBase: [1, 1e6] };
|
|
2713
|
-
}
|
|
2714
|
-
const tb = fallbackTimeBase ?? [1, 1e6];
|
|
2715
|
-
const pts64 = hi * 4294967296 + lo;
|
|
2716
|
-
const us = Math.round(pts64 * 1e6 * tb[0] / tb[1]);
|
|
2717
|
-
if (Number.isFinite(us) && Math.abs(us) <= Number.MAX_SAFE_INTEGER) {
|
|
2718
|
-
frame.pts = us;
|
|
2719
|
-
frame.ptshi = us < 0 ? -1 : 0;
|
|
2720
|
-
return { timeBase: [1, 1e6] };
|
|
2721
|
-
}
|
|
2722
|
-
const fallback = nextUs();
|
|
2723
|
-
frame.pts = fallback;
|
|
2724
|
-
frame.ptshi = 0;
|
|
2725
|
-
return { timeBase: [1, 1e6] };
|
|
2726
|
-
}
|
|
2727
|
-
var AV_SAMPLE_FMT_U82 = 0;
|
|
2728
|
-
var AV_SAMPLE_FMT_S162 = 1;
|
|
2729
|
-
var AV_SAMPLE_FMT_S322 = 2;
|
|
2730
|
-
var AV_SAMPLE_FMT_FLT2 = 3;
|
|
2731
|
-
var AV_SAMPLE_FMT_U8P2 = 5;
|
|
2732
|
-
var AV_SAMPLE_FMT_S16P2 = 6;
|
|
2733
|
-
var AV_SAMPLE_FMT_S32P2 = 7;
|
|
2734
|
-
var AV_SAMPLE_FMT_FLTP2 = 8;
|
|
2735
|
-
function libavFrameToInterleavedFloat322(frame) {
|
|
2736
|
-
const channels = frame.channels ?? frame.ch_layout_nb_channels ?? 1;
|
|
2737
|
-
const sampleRate = frame.sample_rate ?? 44100;
|
|
2738
|
-
const nbSamples = frame.nb_samples ?? 0;
|
|
2739
|
-
if (nbSamples === 0) return null;
|
|
2740
|
-
const out = new Float32Array(nbSamples * channels);
|
|
2741
|
-
switch (frame.format) {
|
|
2742
|
-
case AV_SAMPLE_FMT_FLTP2: {
|
|
2743
|
-
const planes = ensurePlanes2(frame.data, channels);
|
|
2744
|
-
for (let ch = 0; ch < channels; ch++) {
|
|
2745
|
-
const plane = asFloat322(planes[ch]);
|
|
2746
|
-
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i];
|
|
2747
|
-
}
|
|
2748
|
-
return { data: out, channels, sampleRate };
|
|
2749
|
-
}
|
|
2750
|
-
case AV_SAMPLE_FMT_FLT2: {
|
|
2751
|
-
const flat = asFloat322(frame.data);
|
|
2752
|
-
for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i];
|
|
2753
|
-
return { data: out, channels, sampleRate };
|
|
2754
|
-
}
|
|
2755
|
-
case AV_SAMPLE_FMT_S16P2: {
|
|
2756
|
-
const planes = ensurePlanes2(frame.data, channels);
|
|
2757
|
-
for (let ch = 0; ch < channels; ch++) {
|
|
2758
|
-
const plane = asInt162(planes[ch]);
|
|
2759
|
-
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 32768;
|
|
2760
|
-
}
|
|
2761
|
-
return { data: out, channels, sampleRate };
|
|
2762
|
-
}
|
|
2763
|
-
case AV_SAMPLE_FMT_S162: {
|
|
2764
|
-
const flat = asInt162(frame.data);
|
|
2765
|
-
for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 32768;
|
|
2766
|
-
return { data: out, channels, sampleRate };
|
|
2767
|
-
}
|
|
2768
|
-
case AV_SAMPLE_FMT_S32P2: {
|
|
2769
|
-
const planes = ensurePlanes2(frame.data, channels);
|
|
2770
|
-
for (let ch = 0; ch < channels; ch++) {
|
|
2771
|
-
const plane = asInt322(planes[ch]);
|
|
2772
|
-
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = plane[i] / 2147483648;
|
|
2773
|
-
}
|
|
2774
|
-
return { data: out, channels, sampleRate };
|
|
2775
|
-
}
|
|
2776
|
-
case AV_SAMPLE_FMT_S322: {
|
|
2777
|
-
const flat = asInt322(frame.data);
|
|
2778
|
-
for (let i = 0; i < nbSamples * channels; i++) out[i] = flat[i] / 2147483648;
|
|
2779
|
-
return { data: out, channels, sampleRate };
|
|
2780
|
-
}
|
|
2781
|
-
case AV_SAMPLE_FMT_U8P2: {
|
|
2782
|
-
const planes = ensurePlanes2(frame.data, channels);
|
|
2783
|
-
for (let ch = 0; ch < channels; ch++) {
|
|
2784
|
-
const plane = asUint82(planes[ch]);
|
|
2785
|
-
for (let i = 0; i < nbSamples; i++) out[i * channels + ch] = (plane[i] - 128) / 128;
|
|
2786
|
-
}
|
|
2787
|
-
return { data: out, channels, sampleRate };
|
|
2788
|
-
}
|
|
2789
|
-
case AV_SAMPLE_FMT_U82: {
|
|
2790
|
-
const flat = asUint82(frame.data);
|
|
2791
|
-
for (let i = 0; i < nbSamples * channels; i++) out[i] = (flat[i] - 128) / 128;
|
|
2792
|
-
return { data: out, channels, sampleRate };
|
|
2793
|
-
}
|
|
2794
|
-
default:
|
|
2795
|
-
if (!globalThis.__avbridgeLoggedSampleFmt) {
|
|
2796
|
-
globalThis.__avbridgeLoggedSampleFmt = frame.format;
|
|
2797
|
-
console.warn(`[avbridge] unsupported audio sample format from libav: ${frame.format}`);
|
|
2798
|
-
}
|
|
2799
|
-
return null;
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2802
|
-
function ensurePlanes2(data, channels) {
|
|
2803
|
-
if (Array.isArray(data)) return data;
|
|
2804
|
-
const arr = data;
|
|
2805
|
-
const len = arr.length;
|
|
2806
|
-
const perChannel = Math.floor(len / channels);
|
|
2807
|
-
const planes = [];
|
|
2808
|
-
for (let ch = 0; ch < channels; ch++) {
|
|
2809
|
-
planes.push(arr.subarray ? arr.subarray(ch * perChannel, (ch + 1) * perChannel) : arr);
|
|
2810
|
-
}
|
|
2811
|
-
return planes;
|
|
2812
|
-
}
|
|
2813
|
-
function asFloat322(x) {
|
|
2814
|
-
if (x instanceof Float32Array) return x;
|
|
2815
|
-
const ta = x;
|
|
2816
|
-
return new Float32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);
|
|
2817
|
-
}
|
|
2818
|
-
function asInt162(x) {
|
|
2819
|
-
if (x instanceof Int16Array) return x;
|
|
2820
|
-
const ta = x;
|
|
2821
|
-
return new Int16Array(ta.buffer, ta.byteOffset, ta.byteLength / 2);
|
|
2822
|
-
}
|
|
2823
|
-
function asInt322(x) {
|
|
2824
|
-
if (x instanceof Int32Array) return x;
|
|
2825
|
-
const ta = x;
|
|
2826
|
-
return new Int32Array(ta.buffer, ta.byteOffset, ta.byteLength / 4);
|
|
2827
|
-
}
|
|
2828
|
-
function asUint82(x) {
|
|
2829
|
-
if (x instanceof Uint8Array) return x;
|
|
2830
|
-
const ta = x;
|
|
2831
|
-
return new Uint8Array(ta.buffer, ta.byteOffset, ta.byteLength);
|
|
2832
|
-
}
|
|
2833
2626
|
async function loadBridge2() {
|
|
2834
2627
|
try {
|
|
2835
|
-
const wrapper = await import('./libav-import-
|
|
2628
|
+
const wrapper = await import('./libav-import-2ZVKV2E7.cjs');
|
|
2836
2629
|
return wrapper.libavBridge;
|
|
2837
2630
|
} catch (err) {
|
|
2838
2631
|
throw new Error(
|
|
@@ -2845,8 +2638,8 @@ async function loadBridge2() {
|
|
|
2845
2638
|
var READY_AUDIO_BUFFER_SECONDS2 = 0.04;
|
|
2846
2639
|
var READY_TIMEOUT_SECONDS2 = 3;
|
|
2847
2640
|
async function createFallbackSession(ctx, target, transport) {
|
|
2848
|
-
const { normalizeSource
|
|
2849
|
-
const source = await
|
|
2641
|
+
const { normalizeSource } = await import('./source-VFLXLOCN.cjs');
|
|
2642
|
+
const source = await normalizeSource(ctx.source);
|
|
2850
2643
|
const fps = ctx.videoTracks[0]?.fps ?? 30;
|
|
2851
2644
|
const audio = new AudioOutput();
|
|
2852
2645
|
const renderer = new VideoRenderer(target, audio, fps);
|
|
@@ -2898,6 +2691,18 @@ async function createFallbackSession(ctx, target, transport) {
|
|
|
2898
2691
|
get: () => ctx.duration ?? NaN
|
|
2899
2692
|
});
|
|
2900
2693
|
}
|
|
2694
|
+
Object.defineProperty(target, "readyState", {
|
|
2695
|
+
configurable: true,
|
|
2696
|
+
get: () => {
|
|
2697
|
+
if (!renderer.hasFrames()) return 0;
|
|
2698
|
+
if (!audio.isPlaying() && audio.bufferAhead() <= 0 && !audio.isNoAudio()) return 1;
|
|
2699
|
+
return 2;
|
|
2700
|
+
}
|
|
2701
|
+
});
|
|
2702
|
+
Object.defineProperty(target, "seekable", {
|
|
2703
|
+
configurable: true,
|
|
2704
|
+
get: () => makeTimeRanges(ctx.duration && Number.isFinite(ctx.duration) && ctx.duration > 0 ? [[0, ctx.duration]] : [])
|
|
2705
|
+
});
|
|
2901
2706
|
async function waitForBuffer() {
|
|
2902
2707
|
const start = performance.now();
|
|
2903
2708
|
let firstFrameAtMs = 0;
|
|
@@ -2966,7 +2771,24 @@ async function createFallbackSession(ctx, target, transport) {
|
|
|
2966
2771
|
async seek(time) {
|
|
2967
2772
|
await doSeek(time);
|
|
2968
2773
|
},
|
|
2969
|
-
async setAudioTrack(
|
|
2774
|
+
async setAudioTrack(id) {
|
|
2775
|
+
if (!ctx.audioTracks.some((t) => t.id === id)) {
|
|
2776
|
+
console.warn("[avbridge] fallback: setAudioTrack \u2014 unknown track id", id);
|
|
2777
|
+
return;
|
|
2778
|
+
}
|
|
2779
|
+
const wasPlaying = audio.isPlaying();
|
|
2780
|
+
const currentTime = audio.now();
|
|
2781
|
+
await audio.pause().catch(() => {
|
|
2782
|
+
});
|
|
2783
|
+
await handles.setAudioTrack(id, currentTime).catch(
|
|
2784
|
+
(err) => console.warn("[avbridge] fallback: handles.setAudioTrack failed:", err)
|
|
2785
|
+
);
|
|
2786
|
+
await audio.reset(currentTime);
|
|
2787
|
+
renderer.flush();
|
|
2788
|
+
if (wasPlaying) {
|
|
2789
|
+
await waitForBuffer();
|
|
2790
|
+
await audio.start();
|
|
2791
|
+
}
|
|
2970
2792
|
},
|
|
2971
2793
|
async setSubtitleTrack(_id) {
|
|
2972
2794
|
},
|
|
@@ -2983,6 +2805,8 @@ async function createFallbackSession(ctx, target, transport) {
|
|
|
2983
2805
|
delete target.paused;
|
|
2984
2806
|
delete target.volume;
|
|
2985
2807
|
delete target.muted;
|
|
2808
|
+
delete target.readyState;
|
|
2809
|
+
delete target.seekable;
|
|
2986
2810
|
} catch {
|
|
2987
2811
|
}
|
|
2988
2812
|
},
|
|
@@ -3020,89 +2844,6 @@ function registerBuiltins(registry) {
|
|
|
3020
2844
|
registry.register(fallbackPlugin);
|
|
3021
2845
|
}
|
|
3022
2846
|
|
|
3023
|
-
// src/subtitles/vtt.ts
|
|
3024
|
-
function isVtt(text) {
|
|
3025
|
-
const trimmed = text.replace(/^\ufeff/, "").trimStart();
|
|
3026
|
-
return trimmed.startsWith("WEBVTT");
|
|
3027
|
-
}
|
|
3028
|
-
|
|
3029
|
-
// src/subtitles/index.ts
|
|
3030
|
-
async function discoverSidecars(file, directory) {
|
|
3031
|
-
const baseName = file.name.replace(/\.[^.]+$/, "");
|
|
3032
|
-
const found = [];
|
|
3033
|
-
for await (const [name, handle] of directory) {
|
|
3034
|
-
if (handle.kind !== "file") continue;
|
|
3035
|
-
if (!name.startsWith(baseName)) continue;
|
|
3036
|
-
const lower = name.toLowerCase();
|
|
3037
|
-
let format = null;
|
|
3038
|
-
if (lower.endsWith(".srt")) format = "srt";
|
|
3039
|
-
else if (lower.endsWith(".vtt")) format = "vtt";
|
|
3040
|
-
if (!format) continue;
|
|
3041
|
-
const sidecarFile = await handle.getFile();
|
|
3042
|
-
const url = URL.createObjectURL(sidecarFile);
|
|
3043
|
-
const langMatch = name.slice(baseName.length).match(/[._-]([a-z]{2,3})(?:[._-]|\.)/i);
|
|
3044
|
-
found.push({
|
|
3045
|
-
url,
|
|
3046
|
-
format,
|
|
3047
|
-
language: langMatch?.[1]
|
|
3048
|
-
});
|
|
3049
|
-
}
|
|
3050
|
-
return found;
|
|
3051
|
-
}
|
|
3052
|
-
var SubtitleResourceBag = class {
|
|
3053
|
-
urls = /* @__PURE__ */ new Set();
|
|
3054
|
-
/** Track an externally-created blob URL (e.g. from `discoverSidecars`). */
|
|
3055
|
-
track(url) {
|
|
3056
|
-
this.urls.add(url);
|
|
3057
|
-
}
|
|
3058
|
-
/** Convenience: create a blob URL and track it in one call. */
|
|
3059
|
-
createObjectURL(blob) {
|
|
3060
|
-
const url = URL.createObjectURL(blob);
|
|
3061
|
-
this.urls.add(url);
|
|
3062
|
-
return url;
|
|
3063
|
-
}
|
|
3064
|
-
/** Revoke every tracked URL. Idempotent — safe to call multiple times. */
|
|
3065
|
-
revokeAll() {
|
|
3066
|
-
for (const u of this.urls) URL.revokeObjectURL(u);
|
|
3067
|
-
this.urls.clear();
|
|
3068
|
-
}
|
|
3069
|
-
};
|
|
3070
|
-
async function attachSubtitleTracks(video, tracks, bag, onError, transport) {
|
|
3071
|
-
const doFetch = chunk6UUT4BEA_cjs.fetchWith(transport);
|
|
3072
|
-
for (const t of Array.from(video.querySelectorAll("track[data-avbridge]"))) {
|
|
3073
|
-
t.remove();
|
|
3074
|
-
}
|
|
3075
|
-
for (const t of tracks) {
|
|
3076
|
-
if (!t.sidecarUrl) continue;
|
|
3077
|
-
try {
|
|
3078
|
-
let url = t.sidecarUrl;
|
|
3079
|
-
if (t.format === "srt") {
|
|
3080
|
-
const res = await doFetch(t.sidecarUrl, transport?.requestInit);
|
|
3081
|
-
const text = await res.text();
|
|
3082
|
-
const vtt = srtToVtt(text);
|
|
3083
|
-
const blob = new Blob([vtt], { type: "text/vtt" });
|
|
3084
|
-
url = bag ? bag.createObjectURL(blob) : URL.createObjectURL(blob);
|
|
3085
|
-
} else if (t.format === "vtt") {
|
|
3086
|
-
const res = await doFetch(t.sidecarUrl, transport?.requestInit);
|
|
3087
|
-
const text = await res.text();
|
|
3088
|
-
if (!isVtt(text)) {
|
|
3089
|
-
console.warn("[avbridge] subtitle missing WEBVTT header:", t.sidecarUrl);
|
|
3090
|
-
}
|
|
3091
|
-
}
|
|
3092
|
-
const trackEl = document.createElement("track");
|
|
3093
|
-
trackEl.kind = "subtitles";
|
|
3094
|
-
trackEl.src = url;
|
|
3095
|
-
trackEl.srclang = t.language ?? "und";
|
|
3096
|
-
trackEl.label = t.language ?? `Subtitle ${t.id}`;
|
|
3097
|
-
trackEl.dataset.avbridge = "true";
|
|
3098
|
-
video.appendChild(trackEl);
|
|
3099
|
-
} catch (err) {
|
|
3100
|
-
const e = err instanceof Error ? err : new Error(String(err));
|
|
3101
|
-
onError?.(e, t);
|
|
3102
|
-
}
|
|
3103
|
-
}
|
|
3104
|
-
}
|
|
3105
|
-
|
|
3106
2847
|
// src/player.ts
|
|
3107
2848
|
var UnifiedPlayer = class _UnifiedPlayer {
|
|
3108
2849
|
/**
|
|
@@ -3146,7 +2887,7 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
3146
2887
|
switchingPromise = Promise.resolve();
|
|
3147
2888
|
// Owns blob URLs created during sidecar discovery + SRT->VTT conversion.
|
|
3148
2889
|
// Revoked at destroy() so repeated source swaps don't leak.
|
|
3149
|
-
subtitleResources = new SubtitleResourceBag();
|
|
2890
|
+
subtitleResources = new chunkS4WAZC2T_cjs.SubtitleResourceBag();
|
|
3150
2891
|
// Transport config extracted from CreatePlayerOptions. Threaded to probe,
|
|
3151
2892
|
// subtitle fetches, and strategy session creators. Not stored on MediaContext
|
|
3152
2893
|
// because it's runtime config, not media analysis.
|
|
@@ -3174,7 +2915,7 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
3174
2915
|
const bootstrapStart = performance.now();
|
|
3175
2916
|
try {
|
|
3176
2917
|
chunkG4APZMCP_cjs.dbg.info("bootstrap", "start");
|
|
3177
|
-
const ctx = await chunkG4APZMCP_cjs.dbg.timed("probe", "probe", 3e3, () => probe(this.options.source, this.transport));
|
|
2918
|
+
const ctx = await chunkG4APZMCP_cjs.dbg.timed("probe", "probe", 3e3, () => chunkZCUXHW55_cjs.probe(this.options.source, this.transport));
|
|
3178
2919
|
chunkG4APZMCP_cjs.dbg.info(
|
|
3179
2920
|
"probe",
|
|
3180
2921
|
`container=${ctx.container} video=${ctx.videoTracks[0]?.codec ?? "-"} audio=${ctx.audioTracks[0]?.codec ?? "-"} probedBy=${ctx.probedBy}`
|
|
@@ -3192,7 +2933,7 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
3192
2933
|
}
|
|
3193
2934
|
}
|
|
3194
2935
|
if (this.options.directory && this.options.source instanceof File) {
|
|
3195
|
-
const found = await discoverSidecars(this.options.source, this.options.directory);
|
|
2936
|
+
const found = await chunkS4WAZC2T_cjs.discoverSidecars(this.options.source, this.options.directory);
|
|
3196
2937
|
for (const s of found) {
|
|
3197
2938
|
this.subtitleResources.track(s.url);
|
|
3198
2939
|
ctx.subtitleTracks.push({
|
|
@@ -3215,17 +2956,15 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
3215
2956
|
reason: decision.reason
|
|
3216
2957
|
});
|
|
3217
2958
|
await this.startSession(decision.strategy, decision.reason);
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
(
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
);
|
|
3228
|
-
}
|
|
2959
|
+
await chunkS4WAZC2T_cjs.attachSubtitleTracks(
|
|
2960
|
+
this.options.target,
|
|
2961
|
+
ctx.subtitleTracks,
|
|
2962
|
+
this.subtitleResources,
|
|
2963
|
+
(err, track) => {
|
|
2964
|
+
console.warn(`[avbridge] subtitle ${track.id} failed: ${err.message}`);
|
|
2965
|
+
},
|
|
2966
|
+
this.transport
|
|
2967
|
+
);
|
|
3229
2968
|
this.emitter.emitSticky("tracks", {
|
|
3230
2969
|
video: ctx.videoTracks,
|
|
3231
2970
|
audio: ctx.audioTracks,
|
|
@@ -3360,8 +3099,8 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
3360
3099
|
}
|
|
3361
3100
|
return;
|
|
3362
3101
|
}
|
|
3363
|
-
this.emitter.emit("error", new
|
|
3364
|
-
|
|
3102
|
+
this.emitter.emit("error", new chunk2IJ66NTD_cjs.AvbridgeError(
|
|
3103
|
+
chunk2IJ66NTD_cjs.ERR_ALL_STRATEGIES_EXHAUSTED,
|
|
3365
3104
|
`All playback strategies failed: ${errors.join("; ")}`,
|
|
3366
3105
|
"This file may require a codec or container that isn't available in this browser. Try the fallback strategy or check browser codec support."
|
|
3367
3106
|
));
|
|
@@ -3415,7 +3154,7 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
3415
3154
|
// ── Public: manual strategy switch ────────────────────────────────────
|
|
3416
3155
|
/** Manually switch to a different playback strategy. Preserves current position and play/pause state. Concurrent calls are serialized. */
|
|
3417
3156
|
async setStrategy(strategy, reason) {
|
|
3418
|
-
if (!this.mediaContext) throw new
|
|
3157
|
+
if (!this.mediaContext) throw new chunk2IJ66NTD_cjs.AvbridgeError(chunk2IJ66NTD_cjs.ERR_PLAYER_NOT_READY, "Player not ready \u2014 wait for the 'ready' event before calling playback methods.", "Await the 'ready' event or check player.readyState before calling play/pause/seek.");
|
|
3419
3158
|
if (this.session?.strategy === strategy) return;
|
|
3420
3159
|
this.switchingPromise = this.switchingPromise.then(
|
|
3421
3160
|
() => this.doSetStrategy(strategy, reason)
|
|
@@ -3478,7 +3217,7 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
3478
3217
|
}
|
|
3479
3218
|
/** Begin or resume playback. Throws if the player is not ready. */
|
|
3480
3219
|
async play() {
|
|
3481
|
-
if (!this.session) throw new
|
|
3220
|
+
if (!this.session) throw new chunk2IJ66NTD_cjs.AvbridgeError(chunk2IJ66NTD_cjs.ERR_PLAYER_NOT_READY, "Player not ready \u2014 wait for the 'ready' event before calling playback methods.", "Await the 'ready' event or check player.readyState before calling play/pause/seek.");
|
|
3482
3221
|
this.userIntent = "play";
|
|
3483
3222
|
this.autoPausedForVisibility = false;
|
|
3484
3223
|
await this.session.play();
|
|
@@ -3517,17 +3256,17 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
3517
3256
|
}
|
|
3518
3257
|
/** Seek to the given time in seconds. Throws if the player is not ready. */
|
|
3519
3258
|
async seek(time) {
|
|
3520
|
-
if (!this.session) throw new
|
|
3259
|
+
if (!this.session) throw new chunk2IJ66NTD_cjs.AvbridgeError(chunk2IJ66NTD_cjs.ERR_PLAYER_NOT_READY, "Player not ready \u2014 wait for the 'ready' event before calling playback methods.", "Await the 'ready' event or check player.readyState before calling play/pause/seek.");
|
|
3521
3260
|
await this.session.seek(time);
|
|
3522
3261
|
}
|
|
3523
3262
|
/** Switch the active audio track by track ID. Throws if the player is not ready. */
|
|
3524
3263
|
async setAudioTrack(id) {
|
|
3525
|
-
if (!this.session) throw new
|
|
3264
|
+
if (!this.session) throw new chunk2IJ66NTD_cjs.AvbridgeError(chunk2IJ66NTD_cjs.ERR_PLAYER_NOT_READY, "Player not ready \u2014 wait for the 'ready' event before calling playback methods.", "Await the 'ready' event or check player.readyState before calling play/pause/seek.");
|
|
3526
3265
|
await this.session.setAudioTrack(id);
|
|
3527
3266
|
}
|
|
3528
3267
|
/** Switch the active subtitle track by track ID, or pass `null` to disable subtitles. */
|
|
3529
3268
|
async setSubtitleTrack(id) {
|
|
3530
|
-
if (!this.session) throw new
|
|
3269
|
+
if (!this.session) throw new chunk2IJ66NTD_cjs.AvbridgeError(chunk2IJ66NTD_cjs.ERR_PLAYER_NOT_READY, "Player not ready \u2014 wait for the 'ready' event before calling playback methods.", "Await the 'ready' event or check player.readyState before calling play/pause/seek.");
|
|
3531
3270
|
await this.session.setSubtitleTrack(id);
|
|
3532
3271
|
}
|
|
3533
3272
|
/** Return a snapshot of current diagnostics: container, codecs, strategy, runtime stats, and strategy history. */
|
|
@@ -3625,12 +3364,7 @@ exports.FALLBACK_VIDEO_CODECS = FALLBACK_VIDEO_CODECS;
|
|
|
3625
3364
|
exports.NATIVE_AUDIO_CODECS = NATIVE_AUDIO_CODECS;
|
|
3626
3365
|
exports.NATIVE_VIDEO_CODECS = NATIVE_VIDEO_CODECS;
|
|
3627
3366
|
exports.UnifiedPlayer = UnifiedPlayer;
|
|
3628
|
-
exports.avbridgeAudioToMediabunny = avbridgeAudioToMediabunny;
|
|
3629
|
-
exports.avbridgeVideoToMediabunny = avbridgeVideoToMediabunny;
|
|
3630
|
-
exports.buildMediabunnySourceFromInput = buildMediabunnySourceFromInput;
|
|
3631
3367
|
exports.classifyContext = classifyContext;
|
|
3632
3368
|
exports.createPlayer = createPlayer;
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
//# sourceMappingURL=chunk-7RGG6ME7.cjs.map
|
|
3636
|
-
//# sourceMappingURL=chunk-7RGG6ME7.cjs.map
|
|
3369
|
+
//# sourceMappingURL=chunk-6SOFJV44.cjs.map
|
|
3370
|
+
//# sourceMappingURL=chunk-6SOFJV44.cjs.map
|