avbridge 2.1.2 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +138 -0
- package/README.md +98 -71
- package/dist/{avi-GNTV5ZOH.cjs → avi-6SJLWIWW.cjs} +19 -4
- package/dist/avi-6SJLWIWW.cjs.map +1 -0
- package/dist/{avi-V6HYQVR2.js → avi-GCGM7OJI.js} +18 -3
- package/dist/avi-GCGM7OJI.js.map +1 -0
- package/dist/{chunk-EJH67FXG.js → chunk-5DMTJVIU.js} +99 -3
- package/dist/chunk-5DMTJVIU.js.map +1 -0
- package/dist/{chunk-3AUGRKPY.js → chunk-DMWARSEF.js} +160 -27
- package/dist/chunk-DMWARSEF.js.map +1 -0
- package/dist/{chunk-JQH6D4OE.cjs → chunk-G4APZMCP.cjs} +100 -3
- package/dist/chunk-G4APZMCP.cjs.map +1 -0
- package/dist/{chunk-Y5FYF5KG.cjs → chunk-HZLQNKFN.cjs} +5 -2
- package/dist/chunk-HZLQNKFN.cjs.map +1 -0
- package/dist/{chunk-PQTZS7OA.js → chunk-ILKDNBSE.js} +5 -2
- package/dist/chunk-ILKDNBSE.js.map +1 -0
- package/dist/{chunk-DPVIOYGC.cjs → chunk-UF2N5L63.cjs} +164 -31
- package/dist/chunk-UF2N5L63.cjs.map +1 -0
- package/dist/element-browser.js +276 -21
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +4 -4
- package/dist/element.d.cts +1 -1
- package/dist/element.d.ts +1 -1
- package/dist/element.js +3 -3
- package/dist/index.cjs +18 -18
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -5
- package/dist/libav-loader-27RDIN2I.js +3 -0
- package/dist/{libav-loader-XKH2TKUW.js.map → libav-loader-27RDIN2I.js.map} +1 -1
- package/dist/libav-loader-IV4AJ2HW.cjs +12 -0
- package/dist/{libav-loader-6APXVNIV.cjs.map → libav-loader-IV4AJ2HW.cjs.map} +1 -1
- package/dist/{player-BdtUG4rh.d.cts → player-U2NPmFvA.d.cts} +4 -3
- package/dist/{player-BdtUG4rh.d.ts → player-U2NPmFvA.d.ts} +4 -3
- package/dist/source-CN43EI7Z.cjs +28 -0
- package/dist/{source-SC6ZEQYR.cjs.map → source-CN43EI7Z.cjs.map} +1 -1
- package/dist/source-FFZ7TW2B.js +3 -0
- package/dist/{source-ZFS4H7J3.js.map → source-FFZ7TW2B.js.map} +1 -1
- package/package.json +1 -1
- package/src/classify/rules.ts +9 -2
- package/src/player.ts +46 -17
- package/src/probe/avi.ts +8 -1
- package/src/strategies/fallback/audio-output.ts +25 -3
- package/src/strategies/fallback/decoder.ts +96 -8
- package/src/strategies/fallback/index.ts +98 -6
- package/src/strategies/fallback/libav-loader.ts +12 -0
- package/src/strategies/fallback/video-renderer.ts +5 -1
- package/src/strategies/hybrid/index.ts +9 -1
- package/src/strategies/remux/index.ts +13 -1
- package/src/strategies/remux/pipeline.ts +6 -0
- package/src/types.ts +10 -1
- package/src/util/debug.ts +131 -0
- package/src/util/source.ts +4 -0
- package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.mjs +1 -1
- package/vendor/libav/avbridge/libav-6.8.8.0-avbridge.wasm.wasm +0 -0
- package/dist/avi-GNTV5ZOH.cjs.map +0 -1
- package/dist/avi-V6HYQVR2.js.map +0 -1
- package/dist/chunk-3AUGRKPY.js.map +0 -1
- package/dist/chunk-DPVIOYGC.cjs.map +0 -1
- package/dist/chunk-EJH67FXG.js.map +0 -1
- package/dist/chunk-JQH6D4OE.cjs.map +0 -1
- package/dist/chunk-PQTZS7OA.js.map +0 -1
- package/dist/chunk-Y5FYF5KG.cjs.map +0 -1
- package/dist/libav-loader-6APXVNIV.cjs +0 -12
- package/dist/libav-loader-XKH2TKUW.js +0 -3
- package/dist/source-SC6ZEQYR.cjs +0 -28
- package/dist/source-ZFS4H7J3.js +0 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var chunkHZLQNKFN_cjs = require('./chunk-HZLQNKFN.cjs');
|
|
4
|
+
var chunkG4APZMCP_cjs = require('./chunk-G4APZMCP.cjs');
|
|
5
5
|
var chunkNZU7W256_cjs = require('./chunk-NZU7W256.cjs');
|
|
6
6
|
|
|
7
7
|
// src/probe/mediabunny.ts
|
|
@@ -181,8 +181,8 @@ var MEDIABUNNY_CONTAINERS = /* @__PURE__ */ new Set([
|
|
|
181
181
|
"mpegts"
|
|
182
182
|
]);
|
|
183
183
|
async function probe(source) {
|
|
184
|
-
const normalized = await
|
|
185
|
-
const sniffed = await
|
|
184
|
+
const normalized = await chunkHZLQNKFN_cjs.normalizeSource(source);
|
|
185
|
+
const sniffed = await chunkHZLQNKFN_cjs.sniffNormalizedSource(normalized);
|
|
186
186
|
if (MEDIABUNNY_CONTAINERS.has(sniffed)) {
|
|
187
187
|
try {
|
|
188
188
|
return await probeWithMediabunny(normalized, sniffed);
|
|
@@ -192,7 +192,7 @@ async function probe(source) {
|
|
|
192
192
|
mediabunnyErr.message
|
|
193
193
|
);
|
|
194
194
|
try {
|
|
195
|
-
const { probeWithLibav } = await import('./avi-
|
|
195
|
+
const { probeWithLibav } = await import('./avi-6SJLWIWW.cjs');
|
|
196
196
|
return await probeWithLibav(normalized, sniffed);
|
|
197
197
|
} catch (libavErr) {
|
|
198
198
|
const mbMsg = mediabunnyErr.message || String(mediabunnyErr);
|
|
@@ -204,7 +204,7 @@ async function probe(source) {
|
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
try {
|
|
207
|
-
const { probeWithLibav } = await import('./avi-
|
|
207
|
+
const { probeWithLibav } = await import('./avi-6SJLWIWW.cjs');
|
|
208
208
|
return await probeWithLibav(normalized, sniffed);
|
|
209
209
|
} catch (err) {
|
|
210
210
|
const inner = err instanceof Error ? err.message : String(err);
|
|
@@ -290,8 +290,29 @@ var NATIVE_AUDIO_CODECS = /* @__PURE__ */ new Set([
|
|
|
290
290
|
"vorbis",
|
|
291
291
|
"flac"
|
|
292
292
|
]);
|
|
293
|
-
var FALLBACK_VIDEO_CODECS = /* @__PURE__ */ new Set([
|
|
294
|
-
|
|
293
|
+
var FALLBACK_VIDEO_CODECS = /* @__PURE__ */ new Set([
|
|
294
|
+
"wmv3",
|
|
295
|
+
"vc1",
|
|
296
|
+
"mpeg4",
|
|
297
|
+
"rv10",
|
|
298
|
+
"rv20",
|
|
299
|
+
"rv30",
|
|
300
|
+
"rv40",
|
|
301
|
+
"mpeg2",
|
|
302
|
+
"mpeg1",
|
|
303
|
+
"theora"
|
|
304
|
+
]);
|
|
305
|
+
var FALLBACK_AUDIO_CODECS = /* @__PURE__ */ new Set([
|
|
306
|
+
"wmav2",
|
|
307
|
+
"wmapro",
|
|
308
|
+
"ac3",
|
|
309
|
+
"eac3",
|
|
310
|
+
"cook",
|
|
311
|
+
"ra_144",
|
|
312
|
+
"ra_288",
|
|
313
|
+
"sipr",
|
|
314
|
+
"atrac3"
|
|
315
|
+
]);
|
|
295
316
|
var NATIVE_CONTAINERS = /* @__PURE__ */ new Set([
|
|
296
317
|
"mp4",
|
|
297
318
|
"mov",
|
|
@@ -1051,6 +1072,10 @@ async function createRemuxPipeline(ctx, video) {
|
|
|
1051
1072
|
console.error("[avbridge] remux pipeline reseek failed:", err);
|
|
1052
1073
|
});
|
|
1053
1074
|
},
|
|
1075
|
+
setAutoPlay(autoPlay) {
|
|
1076
|
+
pendingAutoPlay = autoPlay;
|
|
1077
|
+
if (sink) sink.setPlayOnSeek(autoPlay);
|
|
1078
|
+
},
|
|
1054
1079
|
async destroy() {
|
|
1055
1080
|
destroyed = true;
|
|
1056
1081
|
pumpToken++;
|
|
@@ -1091,7 +1116,11 @@ async function createRemuxSession(context, video) {
|
|
|
1091
1116
|
await pipeline.start(video.currentTime || 0, true);
|
|
1092
1117
|
return;
|
|
1093
1118
|
}
|
|
1094
|
-
|
|
1119
|
+
pipeline.setAutoPlay(true);
|
|
1120
|
+
try {
|
|
1121
|
+
await video.play();
|
|
1122
|
+
} catch {
|
|
1123
|
+
}
|
|
1095
1124
|
},
|
|
1096
1125
|
pause() {
|
|
1097
1126
|
wantPlay = false;
|
|
@@ -1139,7 +1168,7 @@ var VideoRenderer = class {
|
|
|
1139
1168
|
this.resolveFirstFrame = resolve;
|
|
1140
1169
|
});
|
|
1141
1170
|
this.canvas = document.createElement("canvas");
|
|
1142
|
-
this.canvas.style.cssText = "position:absolute;left:0;top:0;width:100%;height:100%;background:black;";
|
|
1171
|
+
this.canvas.style.cssText = "position:absolute;left:0;top:0;width:100%;height:100%;background:black;object-fit:contain;";
|
|
1143
1172
|
const parent = target.parentElement ?? target.parentNode;
|
|
1144
1173
|
if (parent && parent instanceof HTMLElement) {
|
|
1145
1174
|
if (getComputedStyle(parent).position === "static") {
|
|
@@ -1381,9 +1410,13 @@ var AudioOutput = class {
|
|
|
1381
1410
|
const node = this.ctx.createBufferSource();
|
|
1382
1411
|
node.buffer = buffer;
|
|
1383
1412
|
node.connect(this.gain);
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1413
|
+
let ctxStart = this.ctxTimeAtAnchor + (this.mediaTimeOfNext - this.mediaTimeOfAnchor);
|
|
1414
|
+
if (ctxStart < this.ctx.currentTime) {
|
|
1415
|
+
this.ctxTimeAtAnchor = this.ctx.currentTime;
|
|
1416
|
+
this.mediaTimeOfAnchor = this.mediaTimeOfNext;
|
|
1417
|
+
ctxStart = this.ctx.currentTime;
|
|
1418
|
+
}
|
|
1419
|
+
node.start(ctxStart);
|
|
1387
1420
|
this.mediaTimeOfNext += frameCount / sampleRate;
|
|
1388
1421
|
this.framesScheduled++;
|
|
1389
1422
|
}
|
|
@@ -1485,7 +1518,7 @@ var AudioOutput = class {
|
|
|
1485
1518
|
// src/strategies/hybrid/decoder.ts
|
|
1486
1519
|
async function startHybridDecoder(opts) {
|
|
1487
1520
|
const variant = chunkNZU7W256_cjs.pickLibavVariant(opts.context);
|
|
1488
|
-
const libav = await
|
|
1521
|
+
const libav = await chunkG4APZMCP_cjs.loadLibav(variant);
|
|
1489
1522
|
const bridge = await loadBridge();
|
|
1490
1523
|
const { prepareLibavInput } = await import('./libav-http-reader-FPYDBMYK.cjs');
|
|
1491
1524
|
const inputHandle = await prepareLibavInput(libav, opts.filename, opts.source);
|
|
@@ -1925,7 +1958,7 @@ async function loadBridge() {
|
|
|
1925
1958
|
var READY_AUDIO_BUFFER_SECONDS = 0.3;
|
|
1926
1959
|
var READY_TIMEOUT_SECONDS = 10;
|
|
1927
1960
|
async function createHybridSession(ctx, target) {
|
|
1928
|
-
const { normalizeSource: normalizeSource2 } = await import('./source-
|
|
1961
|
+
const { normalizeSource: normalizeSource2 } = await import('./source-CN43EI7Z.cjs');
|
|
1929
1962
|
const source = await normalizeSource2(ctx.source);
|
|
1930
1963
|
const fps = ctx.videoTracks[0]?.fps ?? 30;
|
|
1931
1964
|
const audio = new AudioOutput();
|
|
@@ -1951,6 +1984,10 @@ async function createHybridSession(ctx, target) {
|
|
|
1951
1984
|
void doSeek(v);
|
|
1952
1985
|
}
|
|
1953
1986
|
});
|
|
1987
|
+
Object.defineProperty(target, "paused", {
|
|
1988
|
+
configurable: true,
|
|
1989
|
+
get: () => !audio.isPlaying()
|
|
1990
|
+
});
|
|
1954
1991
|
if (ctx.duration && Number.isFinite(ctx.duration)) {
|
|
1955
1992
|
Object.defineProperty(target, "duration", {
|
|
1956
1993
|
configurable: true,
|
|
@@ -2015,6 +2052,7 @@ async function createHybridSession(ctx, target) {
|
|
|
2015
2052
|
try {
|
|
2016
2053
|
delete target.currentTime;
|
|
2017
2054
|
delete target.duration;
|
|
2055
|
+
delete target.paused;
|
|
2018
2056
|
} catch {
|
|
2019
2057
|
}
|
|
2020
2058
|
},
|
|
@@ -2027,7 +2065,7 @@ async function createHybridSession(ctx, target) {
|
|
|
2027
2065
|
// src/strategies/fallback/decoder.ts
|
|
2028
2066
|
async function startDecoder(opts) {
|
|
2029
2067
|
const variant = chunkNZU7W256_cjs.pickLibavVariant(opts.context);
|
|
2030
|
-
const libav = await
|
|
2068
|
+
const libav = await chunkG4APZMCP_cjs.loadLibav(variant);
|
|
2031
2069
|
const bridge = await loadBridge2();
|
|
2032
2070
|
const { prepareLibavInput } = await import('./libav-http-reader-FPYDBMYK.cjs');
|
|
2033
2071
|
const inputHandle = await prepareLibavInput(libav, opts.filename, opts.source);
|
|
@@ -2092,6 +2130,10 @@ async function startDecoder(opts) {
|
|
|
2092
2130
|
let packetsRead = 0;
|
|
2093
2131
|
let videoFramesDecoded = 0;
|
|
2094
2132
|
let audioFramesDecoded = 0;
|
|
2133
|
+
let watchdogFirstFrameMs = 0;
|
|
2134
|
+
let watchdogSlowSinceMs = 0;
|
|
2135
|
+
let watchdogSlowWarned = false;
|
|
2136
|
+
let watchdogOverflowWarned = false;
|
|
2095
2137
|
let syntheticVideoUs = 0;
|
|
2096
2138
|
let syntheticAudioUs = 0;
|
|
2097
2139
|
const videoTrackInfo = opts.context.videoTracks.find((t) => t.id === videoStream?.index);
|
|
@@ -2112,14 +2154,47 @@ async function startDecoder(opts) {
|
|
|
2112
2154
|
if (myToken !== pumpToken || destroyed) return;
|
|
2113
2155
|
const videoPackets = videoStream ? packets[videoStream.index] : void 0;
|
|
2114
2156
|
const audioPackets = audioStream ? packets[audioStream.index] : void 0;
|
|
2115
|
-
if (videoDec && videoPackets && videoPackets.length > 0) {
|
|
2116
|
-
await decodeVideoBatch(videoPackets, myToken);
|
|
2117
|
-
}
|
|
2118
|
-
if (myToken !== pumpToken || destroyed) return;
|
|
2119
2157
|
if (audioDec && audioPackets && audioPackets.length > 0) {
|
|
2120
2158
|
await decodeAudioBatch(audioPackets, myToken);
|
|
2121
2159
|
}
|
|
2160
|
+
if (myToken !== pumpToken || destroyed) return;
|
|
2161
|
+
if (videoDec && videoPackets && videoPackets.length > 0) {
|
|
2162
|
+
await decodeVideoBatch(videoPackets, myToken);
|
|
2163
|
+
}
|
|
2122
2164
|
packetsRead += (videoPackets?.length ?? 0) + (audioPackets?.length ?? 0);
|
|
2165
|
+
if (videoFramesDecoded > 0) {
|
|
2166
|
+
if (watchdogFirstFrameMs === 0) {
|
|
2167
|
+
watchdogFirstFrameMs = performance.now();
|
|
2168
|
+
}
|
|
2169
|
+
const elapsedSinceFirst = (performance.now() - watchdogFirstFrameMs) / 1e3;
|
|
2170
|
+
if (elapsedSinceFirst > 1 && !watchdogSlowWarned) {
|
|
2171
|
+
const expectedFrames = elapsedSinceFirst * videoFps;
|
|
2172
|
+
const ratio = videoFramesDecoded / expectedFrames;
|
|
2173
|
+
if (ratio < 0.6) {
|
|
2174
|
+
if (watchdogSlowSinceMs === 0) watchdogSlowSinceMs = performance.now();
|
|
2175
|
+
if ((performance.now() - watchdogSlowSinceMs) / 1e3 > 5) {
|
|
2176
|
+
watchdogSlowWarned = true;
|
|
2177
|
+
console.warn(
|
|
2178
|
+
"[avbridge:decode-rate]",
|
|
2179
|
+
`decoder is running slower than realtime: ${videoFramesDecoded} frames in ${elapsedSinceFirst.toFixed(1)}s (${(videoFramesDecoded / elapsedSinceFirst).toFixed(1)} fps vs ${videoFps} fps source \u2014 ${(ratio * 100).toFixed(0)}% of realtime). Playback will stutter. Typical causes: software decode of a codec with no WebCodecs support (rv40, mpeg4 @ 720p+, wmv3), or a resolution too large for single-threaded WASM to keep up with.`
|
|
2180
|
+
);
|
|
2181
|
+
}
|
|
2182
|
+
} else {
|
|
2183
|
+
watchdogSlowSinceMs = 0;
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
if (!watchdogOverflowWarned && videoFramesDecoded > 100) {
|
|
2187
|
+
const rendererStats = opts.renderer.stats();
|
|
2188
|
+
const overflow = rendererStats.framesDroppedOverflow ?? 0;
|
|
2189
|
+
if (overflow / videoFramesDecoded > 0.1) {
|
|
2190
|
+
watchdogOverflowWarned = true;
|
|
2191
|
+
console.warn(
|
|
2192
|
+
"[avbridge:overflow-drop]",
|
|
2193
|
+
`renderer is dropping ${overflow}/${videoFramesDecoded} frames (${(overflow / videoFramesDecoded * 100).toFixed(0)}%) because the decoder is producing bursts faster than the canvas can drain. Symptom: choppy playback despite decoder keeping up on average. Fix would be smaller read batches in the pump loop or a lower queueHighWater cap \u2014 see src/strategies/fallback/decoder.ts.`
|
|
2194
|
+
);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2123
2198
|
while (!destroyed && myToken === pumpToken && (opts.audio.bufferAhead() > 2 || opts.renderer.queueDepth() >= opts.renderer.queueHighWater)) {
|
|
2124
2199
|
await new Promise((r) => setTimeout(r, 50));
|
|
2125
2200
|
}
|
|
@@ -2446,10 +2521,10 @@ async function loadBridge2() {
|
|
|
2446
2521
|
}
|
|
2447
2522
|
|
|
2448
2523
|
// src/strategies/fallback/index.ts
|
|
2449
|
-
var READY_AUDIO_BUFFER_SECONDS2 = 0.
|
|
2450
|
-
var READY_TIMEOUT_SECONDS2 =
|
|
2524
|
+
var READY_AUDIO_BUFFER_SECONDS2 = 0.04;
|
|
2525
|
+
var READY_TIMEOUT_SECONDS2 = 3;
|
|
2451
2526
|
async function createFallbackSession(ctx, target) {
|
|
2452
|
-
const { normalizeSource: normalizeSource2 } = await import('./source-
|
|
2527
|
+
const { normalizeSource: normalizeSource2 } = await import('./source-CN43EI7Z.cjs');
|
|
2453
2528
|
const source = await normalizeSource2(ctx.source);
|
|
2454
2529
|
const fps = ctx.videoTracks[0]?.fps ?? 30;
|
|
2455
2530
|
const audio = new AudioOutput();
|
|
@@ -2475,6 +2550,10 @@ async function createFallbackSession(ctx, target) {
|
|
|
2475
2550
|
void doSeek(v);
|
|
2476
2551
|
}
|
|
2477
2552
|
});
|
|
2553
|
+
Object.defineProperty(target, "paused", {
|
|
2554
|
+
configurable: true,
|
|
2555
|
+
get: () => !audio.isPlaying()
|
|
2556
|
+
});
|
|
2478
2557
|
if (ctx.duration && Number.isFinite(ctx.duration)) {
|
|
2479
2558
|
Object.defineProperty(target, "duration", {
|
|
2480
2559
|
configurable: true,
|
|
@@ -2483,12 +2562,36 @@ async function createFallbackSession(ctx, target) {
|
|
|
2483
2562
|
}
|
|
2484
2563
|
async function waitForBuffer() {
|
|
2485
2564
|
const start = performance.now();
|
|
2565
|
+
let firstFrameAtMs = 0;
|
|
2566
|
+
chunkG4APZMCP_cjs.dbg.info(
|
|
2567
|
+
"cold-start",
|
|
2568
|
+
`gate entry: want audio \u2265 ${READY_AUDIO_BUFFER_SECONDS2 * 1e3}ms + 1 frame`
|
|
2569
|
+
);
|
|
2486
2570
|
while (true) {
|
|
2487
|
-
const
|
|
2488
|
-
|
|
2571
|
+
const audioAhead = audio.isNoAudio() ? Infinity : audio.bufferAhead();
|
|
2572
|
+
const audioReady = audio.isNoAudio() || audioAhead >= READY_AUDIO_BUFFER_SECONDS2;
|
|
2573
|
+
const hasFrames = renderer.hasFrames();
|
|
2574
|
+
const nowMs = performance.now();
|
|
2575
|
+
if (hasFrames && firstFrameAtMs === 0) firstFrameAtMs = nowMs;
|
|
2576
|
+
if (audioReady && hasFrames) {
|
|
2577
|
+
chunkG4APZMCP_cjs.dbg.info(
|
|
2578
|
+
"cold-start",
|
|
2579
|
+
`gate satisfied in ${(nowMs - start).toFixed(0)}ms (audio=${(audioAhead * 1e3).toFixed(0)}ms, frames=${renderer.queueDepth()})`
|
|
2580
|
+
);
|
|
2489
2581
|
return;
|
|
2490
2582
|
}
|
|
2491
|
-
if (
|
|
2583
|
+
if (hasFrames && firstFrameAtMs > 0 && nowMs - firstFrameAtMs >= 500) {
|
|
2584
|
+
chunkG4APZMCP_cjs.dbg.info(
|
|
2585
|
+
"cold-start",
|
|
2586
|
+
`gate released on video-only grace at ${(nowMs - start).toFixed(0)}ms (frames=${renderer.queueDepth()}, audio=${(audioAhead * 1e3).toFixed(0)}ms \u2014 demuxer hasn't delivered audio packets yet, starting anyway and letting the audio scheduler catch up at its media-time anchor)`
|
|
2587
|
+
);
|
|
2588
|
+
return;
|
|
2589
|
+
}
|
|
2590
|
+
if ((nowMs - start) / 1e3 > READY_TIMEOUT_SECONDS2) {
|
|
2591
|
+
chunkG4APZMCP_cjs.dbg.diag(
|
|
2592
|
+
"cold-start",
|
|
2593
|
+
`gate TIMEOUT after ${READY_TIMEOUT_SECONDS2}s \u2014 audio=${(audioAhead * 1e3).toFixed(0)}ms (needed ${READY_AUDIO_BUFFER_SECONDS2 * 1e3}ms), frames=${renderer.queueDepth()} (needed \u22651). Decoder produced nothing in ${READY_TIMEOUT_SECONDS2}s \u2014 either a corrupt source, a missing codec, or WASM is catastrophically slow on this file. Check getDiagnostics().runtime for decode counters.`
|
|
2594
|
+
);
|
|
2492
2595
|
return;
|
|
2493
2596
|
}
|
|
2494
2597
|
await new Promise((r) => setTimeout(r, 50));
|
|
@@ -2536,6 +2639,7 @@ async function createFallbackSession(ctx, target) {
|
|
|
2536
2639
|
try {
|
|
2537
2640
|
delete target.currentTime;
|
|
2538
2641
|
delete target.duration;
|
|
2642
|
+
delete target.paused;
|
|
2539
2643
|
} catch {
|
|
2540
2644
|
}
|
|
2541
2645
|
},
|
|
@@ -2678,6 +2782,10 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2678
2782
|
lastProgressTime = 0;
|
|
2679
2783
|
lastProgressPosition = -1;
|
|
2680
2784
|
errorListener = null;
|
|
2785
|
+
// Bound so we can removeEventListener in destroy(); without this the
|
|
2786
|
+
// listener outlives the player and accumulates on elements that swap
|
|
2787
|
+
// source (e.g. <avbridge-video>).
|
|
2788
|
+
endedListener = null;
|
|
2681
2789
|
// Serializes escalation / setStrategy calls
|
|
2682
2790
|
switchingPromise = Promise.resolve();
|
|
2683
2791
|
// Owns blob URLs created during sidecar discovery + SRT->VTT conversion.
|
|
@@ -2703,8 +2811,14 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2703
2811
|
return player;
|
|
2704
2812
|
}
|
|
2705
2813
|
async bootstrap() {
|
|
2814
|
+
const bootstrapStart = performance.now();
|
|
2706
2815
|
try {
|
|
2707
|
-
|
|
2816
|
+
chunkG4APZMCP_cjs.dbg.info("bootstrap", "start");
|
|
2817
|
+
const ctx = await chunkG4APZMCP_cjs.dbg.timed("probe", "probe", 3e3, () => probe(this.options.source));
|
|
2818
|
+
chunkG4APZMCP_cjs.dbg.info(
|
|
2819
|
+
"probe",
|
|
2820
|
+
`container=${ctx.container} video=${ctx.videoTracks[0]?.codec ?? "-"} audio=${ctx.audioTracks[0]?.codec ?? "-"} probedBy=${ctx.probedBy}`
|
|
2821
|
+
);
|
|
2708
2822
|
this.diag.recordProbe(ctx);
|
|
2709
2823
|
this.mediaContext = ctx;
|
|
2710
2824
|
if (this.options.subtitles) {
|
|
@@ -2730,6 +2844,10 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2730
2844
|
}
|
|
2731
2845
|
}
|
|
2732
2846
|
const decision = this.options.initialStrategy ? buildInitialDecision(this.options.initialStrategy, ctx) : classifyContext(ctx);
|
|
2847
|
+
chunkG4APZMCP_cjs.dbg.info(
|
|
2848
|
+
"classify",
|
|
2849
|
+
`strategy=${decision.strategy} class=${decision.class} reason="${decision.reason}"` + (decision.fallbackChain ? ` fallback=${decision.fallbackChain.join("\u2192")}` : "")
|
|
2850
|
+
);
|
|
2733
2851
|
this.classification = decision;
|
|
2734
2852
|
this.diag.recordClassification(decision);
|
|
2735
2853
|
this.emitter.emitSticky("strategy", {
|
|
@@ -2753,8 +2871,17 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2753
2871
|
subtitle: ctx.subtitleTracks
|
|
2754
2872
|
});
|
|
2755
2873
|
this.startTimeupdateLoop();
|
|
2756
|
-
this.
|
|
2874
|
+
this.endedListener = () => this.emitter.emit("ended", void 0);
|
|
2875
|
+
this.options.target.addEventListener("ended", this.endedListener);
|
|
2757
2876
|
this.emitter.emitSticky("ready", void 0);
|
|
2877
|
+
const bootstrapElapsed = performance.now() - bootstrapStart;
|
|
2878
|
+
chunkG4APZMCP_cjs.dbg.info("bootstrap", `ready in ${bootstrapElapsed.toFixed(0)}ms`);
|
|
2879
|
+
if (bootstrapElapsed > 5e3) {
|
|
2880
|
+
console.warn(
|
|
2881
|
+
"[avbridge:bootstrap]",
|
|
2882
|
+
`total bootstrap time ${bootstrapElapsed.toFixed(0)}ms \u2014 unusually slow. Enable globalThis.AVBRIDGE_DEBUG for a per-phase breakdown.`
|
|
2883
|
+
);
|
|
2884
|
+
}
|
|
2758
2885
|
} catch (err) {
|
|
2759
2886
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
2760
2887
|
this.diag.recordError(e);
|
|
@@ -3031,6 +3158,10 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
3031
3158
|
this.timeupdateInterval = null;
|
|
3032
3159
|
}
|
|
3033
3160
|
this.clearSupervisor();
|
|
3161
|
+
if (this.endedListener) {
|
|
3162
|
+
this.options.target.removeEventListener("ended", this.endedListener);
|
|
3163
|
+
this.endedListener = null;
|
|
3164
|
+
}
|
|
3034
3165
|
if (this.session) {
|
|
3035
3166
|
await this.session.destroy();
|
|
3036
3167
|
this.session = null;
|
|
@@ -3045,11 +3176,13 @@ async function createPlayer(options) {
|
|
|
3045
3176
|
function buildInitialDecision(initial, ctx) {
|
|
3046
3177
|
const natural = classifyContext(ctx);
|
|
3047
3178
|
const cls = strategyToClass(initial, natural);
|
|
3179
|
+
const inherited = natural.fallbackChain ?? defaultFallbackChain(initial);
|
|
3180
|
+
const fallbackChain = inherited.filter((s) => s !== initial);
|
|
3048
3181
|
return {
|
|
3049
3182
|
class: cls,
|
|
3050
3183
|
strategy: initial,
|
|
3051
3184
|
reason: `initial strategy "${initial}" requested via options.initialStrategy`,
|
|
3052
|
-
fallbackChain
|
|
3185
|
+
fallbackChain
|
|
3053
3186
|
};
|
|
3054
3187
|
}
|
|
3055
3188
|
function strategyToClass(strategy, natural) {
|
|
@@ -3086,5 +3219,5 @@ exports.classifyContext = classifyContext;
|
|
|
3086
3219
|
exports.createPlayer = createPlayer;
|
|
3087
3220
|
exports.probe = probe;
|
|
3088
3221
|
exports.srtToVtt = srtToVtt;
|
|
3089
|
-
//# sourceMappingURL=chunk-
|
|
3090
|
-
//# sourceMappingURL=chunk-
|
|
3222
|
+
//# sourceMappingURL=chunk-UF2N5L63.cjs.map
|
|
3223
|
+
//# sourceMappingURL=chunk-UF2N5L63.cjs.map
|