avbridge 2.1.1 → 2.2.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 +136 -0
- 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-CUQD23WO.js → chunk-C5VA5U5O.js} +122 -23
- package/dist/chunk-C5VA5U5O.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-O34444ID.cjs → chunk-OE66B34H.cjs} +126 -27
- package/dist/chunk-OE66B34H.cjs.map +1 -0
- package/dist/element-browser.js +244 -19
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +9 -5
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.cts +1 -1
- package/dist/element.d.ts +1 -1
- package/dist/element.js +8 -4
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +18 -18
- package/dist/index.d.cts +12 -8
- package/dist/index.d.ts +12 -8
- 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-DUyvltvy.d.cts} +3 -3
- package/dist/{player-BdtUG4rh.d.ts → player-DUyvltvy.d.ts} +3 -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/element/avbridge-video.ts +12 -1
- package/src/player.ts +22 -1
- package/src/probe/avi.ts +8 -1
- package/src/probe/index.ts +30 -9
- package/src/strategies/fallback/audio-output.ts +25 -3
- package/src/strategies/fallback/decoder.ts +96 -8
- package/src/strategies/fallback/index.ts +90 -6
- package/src/strategies/fallback/libav-loader.ts +12 -0
- package/src/strategies/fallback/video-renderer.ts +29 -4
- package/src/strategies/remux/pipeline.ts +10 -1
- 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-CUQD23WO.js.map +0 -1
- package/dist/chunk-EJH67FXG.js.map +0 -1
- package/dist/chunk-JQH6D4OE.cjs.map +0 -1
- package/dist/chunk-O34444ID.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,5 +1,5 @@
|
|
|
1
|
-
import { normalizeSource, sniffNormalizedSource } from './chunk-
|
|
2
|
-
import { loadLibav } from './chunk-
|
|
1
|
+
import { normalizeSource, sniffNormalizedSource } from './chunk-ILKDNBSE.js';
|
|
2
|
+
import { dbg, loadLibav } from './chunk-5DMTJVIU.js';
|
|
3
3
|
import { pickLibavVariant } from './chunk-J5MCMN3S.js';
|
|
4
4
|
|
|
5
5
|
// src/probe/mediabunny.ts
|
|
@@ -184,14 +184,25 @@ async function probe(source) {
|
|
|
184
184
|
if (MEDIABUNNY_CONTAINERS.has(sniffed)) {
|
|
185
185
|
try {
|
|
186
186
|
return await probeWithMediabunny(normalized, sniffed);
|
|
187
|
-
} catch (
|
|
188
|
-
|
|
189
|
-
`mediabunny
|
|
187
|
+
} catch (mediabunnyErr) {
|
|
188
|
+
console.warn(
|
|
189
|
+
`[avbridge] mediabunny rejected ${sniffed} file, falling back to libav:`,
|
|
190
|
+
mediabunnyErr.message
|
|
190
191
|
);
|
|
192
|
+
try {
|
|
193
|
+
const { probeWithLibav } = await import('./avi-GCGM7OJI.js');
|
|
194
|
+
return await probeWithLibav(normalized, sniffed);
|
|
195
|
+
} catch (libavErr) {
|
|
196
|
+
const mbMsg = mediabunnyErr.message || String(mediabunnyErr);
|
|
197
|
+
const lvMsg = libavErr instanceof Error ? libavErr.message : String(libavErr);
|
|
198
|
+
throw new Error(
|
|
199
|
+
`failed to probe ${sniffed} file. mediabunny: ${mbMsg}. libav fallback: ${lvMsg}.`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
191
202
|
}
|
|
192
203
|
}
|
|
193
204
|
try {
|
|
194
|
-
const { probeWithLibav } = await import('./avi-
|
|
205
|
+
const { probeWithLibav } = await import('./avi-GCGM7OJI.js');
|
|
195
206
|
return await probeWithLibav(normalized, sniffed);
|
|
196
207
|
} catch (err) {
|
|
197
208
|
const inner = err instanceof Error ? err.message : String(err);
|
|
@@ -277,8 +288,29 @@ var NATIVE_AUDIO_CODECS = /* @__PURE__ */ new Set([
|
|
|
277
288
|
"vorbis",
|
|
278
289
|
"flac"
|
|
279
290
|
]);
|
|
280
|
-
var FALLBACK_VIDEO_CODECS = /* @__PURE__ */ new Set([
|
|
281
|
-
|
|
291
|
+
var FALLBACK_VIDEO_CODECS = /* @__PURE__ */ new Set([
|
|
292
|
+
"wmv3",
|
|
293
|
+
"vc1",
|
|
294
|
+
"mpeg4",
|
|
295
|
+
"rv10",
|
|
296
|
+
"rv20",
|
|
297
|
+
"rv30",
|
|
298
|
+
"rv40",
|
|
299
|
+
"mpeg2",
|
|
300
|
+
"mpeg1",
|
|
301
|
+
"theora"
|
|
302
|
+
]);
|
|
303
|
+
var FALLBACK_AUDIO_CODECS = /* @__PURE__ */ new Set([
|
|
304
|
+
"wmav2",
|
|
305
|
+
"wmapro",
|
|
306
|
+
"ac3",
|
|
307
|
+
"eac3",
|
|
308
|
+
"cook",
|
|
309
|
+
"ra_144",
|
|
310
|
+
"ra_288",
|
|
311
|
+
"sipr",
|
|
312
|
+
"atrac3"
|
|
313
|
+
]);
|
|
282
314
|
var NATIVE_CONTAINERS = /* @__PURE__ */ new Set([
|
|
283
315
|
"mp4",
|
|
284
316
|
"mov",
|
|
@@ -988,7 +1020,8 @@ async function createRemuxPipeline(ctx, video) {
|
|
|
988
1020
|
if (destroyed || pumpToken !== token) break;
|
|
989
1021
|
const vTs = !vNext.done ? vNext.value.timestamp : Number.POSITIVE_INFINITY;
|
|
990
1022
|
const aTs = !aNext.done ? aNext.value.timestamp : Number.POSITIVE_INFINITY;
|
|
991
|
-
|
|
1023
|
+
const forceVideoFirst = firstVideo && !vNext.done;
|
|
1024
|
+
if (!vNext.done && (forceVideoFirst || vTs <= aTs)) {
|
|
992
1025
|
await videoSource.add(
|
|
993
1026
|
vNext.value,
|
|
994
1027
|
firstVideo && videoConfig ? { decoderConfig: videoConfig } : void 0
|
|
@@ -1126,11 +1159,20 @@ var VideoRenderer = class {
|
|
|
1126
1159
|
});
|
|
1127
1160
|
this.canvas = document.createElement("canvas");
|
|
1128
1161
|
this.canvas.style.cssText = "position:absolute;left:0;top:0;width:100%;height:100%;background:black;";
|
|
1129
|
-
const parent = target.parentElement;
|
|
1130
|
-
if (parent &&
|
|
1131
|
-
parent.
|
|
1162
|
+
const parent = target.parentElement ?? target.parentNode;
|
|
1163
|
+
if (parent && parent instanceof HTMLElement) {
|
|
1164
|
+
if (getComputedStyle(parent).position === "static") {
|
|
1165
|
+
parent.style.position = "relative";
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
if (parent) {
|
|
1169
|
+
parent.insertBefore(this.canvas, target);
|
|
1170
|
+
} else {
|
|
1171
|
+
console.warn(
|
|
1172
|
+
"[avbridge] fallback renderer: target <video> has no parent; appending canvas to document.body as a fallback."
|
|
1173
|
+
);
|
|
1174
|
+
document.body.appendChild(this.canvas);
|
|
1132
1175
|
}
|
|
1133
|
-
parent?.insertBefore(this.canvas, target);
|
|
1134
1176
|
target.style.visibility = "hidden";
|
|
1135
1177
|
const ctx = this.canvas.getContext("2d");
|
|
1136
1178
|
if (!ctx) throw new Error("video renderer: failed to acquire 2D context");
|
|
@@ -1902,7 +1944,7 @@ async function loadBridge() {
|
|
|
1902
1944
|
var READY_AUDIO_BUFFER_SECONDS = 0.3;
|
|
1903
1945
|
var READY_TIMEOUT_SECONDS = 10;
|
|
1904
1946
|
async function createHybridSession(ctx, target) {
|
|
1905
|
-
const { normalizeSource: normalizeSource2 } = await import('./source-
|
|
1947
|
+
const { normalizeSource: normalizeSource2 } = await import('./source-FFZ7TW2B.js');
|
|
1906
1948
|
const source = await normalizeSource2(ctx.source);
|
|
1907
1949
|
const fps = ctx.videoTracks[0]?.fps ?? 30;
|
|
1908
1950
|
const audio = new AudioOutput();
|
|
@@ -2069,6 +2111,9 @@ async function startDecoder(opts) {
|
|
|
2069
2111
|
let packetsRead = 0;
|
|
2070
2112
|
let videoFramesDecoded = 0;
|
|
2071
2113
|
let audioFramesDecoded = 0;
|
|
2114
|
+
let watchdogFirstFrameMs = 0;
|
|
2115
|
+
let watchdogSlowSinceMs = 0;
|
|
2116
|
+
let watchdogWarned = false;
|
|
2072
2117
|
let syntheticVideoUs = 0;
|
|
2073
2118
|
let syntheticAudioUs = 0;
|
|
2074
2119
|
const videoTrackInfo = opts.context.videoTracks.find((t) => t.id === videoStream?.index);
|
|
@@ -2080,7 +2125,7 @@ async function startDecoder(opts) {
|
|
|
2080
2125
|
let packets;
|
|
2081
2126
|
try {
|
|
2082
2127
|
[readErr, packets] = await libav.ff_read_frame_multi(fmt_ctx, readPkt, {
|
|
2083
|
-
limit:
|
|
2128
|
+
limit: 64 * 1024
|
|
2084
2129
|
});
|
|
2085
2130
|
} catch (err) {
|
|
2086
2131
|
console.error("[avbridge] ff_read_frame_multi failed:", err);
|
|
@@ -2097,6 +2142,28 @@ async function startDecoder(opts) {
|
|
|
2097
2142
|
await decodeAudioBatch(audioPackets, myToken);
|
|
2098
2143
|
}
|
|
2099
2144
|
packetsRead += (videoPackets?.length ?? 0) + (audioPackets?.length ?? 0);
|
|
2145
|
+
if (videoFramesDecoded > 0) {
|
|
2146
|
+
if (watchdogFirstFrameMs === 0) {
|
|
2147
|
+
watchdogFirstFrameMs = performance.now();
|
|
2148
|
+
}
|
|
2149
|
+
const elapsedSinceFirst = (performance.now() - watchdogFirstFrameMs) / 1e3;
|
|
2150
|
+
if (elapsedSinceFirst > 1 && !watchdogWarned) {
|
|
2151
|
+
const expectedFrames = elapsedSinceFirst * videoFps;
|
|
2152
|
+
const ratio = videoFramesDecoded / expectedFrames;
|
|
2153
|
+
if (ratio < 0.6) {
|
|
2154
|
+
if (watchdogSlowSinceMs === 0) watchdogSlowSinceMs = performance.now();
|
|
2155
|
+
if ((performance.now() - watchdogSlowSinceMs) / 1e3 > 5) {
|
|
2156
|
+
watchdogWarned = true;
|
|
2157
|
+
console.warn(
|
|
2158
|
+
"[avbridge:decode-rate]",
|
|
2159
|
+
`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.`
|
|
2160
|
+
);
|
|
2161
|
+
}
|
|
2162
|
+
} else {
|
|
2163
|
+
watchdogSlowSinceMs = 0;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2100
2167
|
while (!destroyed && myToken === pumpToken && (opts.audio.bufferAhead() > 2 || opts.renderer.queueDepth() >= opts.renderer.queueHighWater)) {
|
|
2101
2168
|
await new Promise((r) => setTimeout(r, 50));
|
|
2102
2169
|
}
|
|
@@ -2423,10 +2490,10 @@ async function loadBridge2() {
|
|
|
2423
2490
|
}
|
|
2424
2491
|
|
|
2425
2492
|
// src/strategies/fallback/index.ts
|
|
2426
|
-
var READY_AUDIO_BUFFER_SECONDS2 = 0.
|
|
2427
|
-
var READY_TIMEOUT_SECONDS2 =
|
|
2493
|
+
var READY_AUDIO_BUFFER_SECONDS2 = 0.04;
|
|
2494
|
+
var READY_TIMEOUT_SECONDS2 = 3;
|
|
2428
2495
|
async function createFallbackSession(ctx, target) {
|
|
2429
|
-
const { normalizeSource: normalizeSource2 } = await import('./source-
|
|
2496
|
+
const { normalizeSource: normalizeSource2 } = await import('./source-FFZ7TW2B.js');
|
|
2430
2497
|
const source = await normalizeSource2(ctx.source);
|
|
2431
2498
|
const fps = ctx.videoTracks[0]?.fps ?? 30;
|
|
2432
2499
|
const audio = new AudioOutput();
|
|
@@ -2460,12 +2527,26 @@ async function createFallbackSession(ctx, target) {
|
|
|
2460
2527
|
}
|
|
2461
2528
|
async function waitForBuffer() {
|
|
2462
2529
|
const start = performance.now();
|
|
2530
|
+
dbg.info(
|
|
2531
|
+
"cold-start",
|
|
2532
|
+
`gate entry: need audio >= ${READY_AUDIO_BUFFER_SECONDS2 * 1e3}ms + 1 frame`
|
|
2533
|
+
);
|
|
2463
2534
|
while (true) {
|
|
2464
|
-
const
|
|
2465
|
-
|
|
2535
|
+
const audioAhead = audio.isNoAudio() ? Infinity : audio.bufferAhead();
|
|
2536
|
+
const audioReady = audio.isNoAudio() || audioAhead >= READY_AUDIO_BUFFER_SECONDS2;
|
|
2537
|
+
const hasFrames = renderer.hasFrames();
|
|
2538
|
+
if (audioReady && hasFrames) {
|
|
2539
|
+
dbg.info(
|
|
2540
|
+
"cold-start",
|
|
2541
|
+
`gate satisfied in ${(performance.now() - start).toFixed(0)}ms (audio=${(audioAhead * 1e3).toFixed(0)}ms, frames=${renderer.queueDepth()})`
|
|
2542
|
+
);
|
|
2466
2543
|
return;
|
|
2467
2544
|
}
|
|
2468
2545
|
if ((performance.now() - start) / 1e3 > READY_TIMEOUT_SECONDS2) {
|
|
2546
|
+
dbg.diag(
|
|
2547
|
+
"cold-start",
|
|
2548
|
+
`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). Software decoder is producing output slower than realtime \u2014 playback will stutter. Check getDiagnostics().runtime for the decode rate.`
|
|
2549
|
+
);
|
|
2469
2550
|
return;
|
|
2470
2551
|
}
|
|
2471
2552
|
await new Promise((r) => setTimeout(r, 50));
|
|
@@ -2680,8 +2761,14 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2680
2761
|
return player;
|
|
2681
2762
|
}
|
|
2682
2763
|
async bootstrap() {
|
|
2764
|
+
const bootstrapStart = performance.now();
|
|
2683
2765
|
try {
|
|
2684
|
-
|
|
2766
|
+
dbg.info("bootstrap", "start");
|
|
2767
|
+
const ctx = await dbg.timed("probe", "probe", 3e3, () => probe(this.options.source));
|
|
2768
|
+
dbg.info(
|
|
2769
|
+
"probe",
|
|
2770
|
+
`container=${ctx.container} video=${ctx.videoTracks[0]?.codec ?? "-"} audio=${ctx.audioTracks[0]?.codec ?? "-"} probedBy=${ctx.probedBy}`
|
|
2771
|
+
);
|
|
2685
2772
|
this.diag.recordProbe(ctx);
|
|
2686
2773
|
this.mediaContext = ctx;
|
|
2687
2774
|
if (this.options.subtitles) {
|
|
@@ -2707,6 +2794,10 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2707
2794
|
}
|
|
2708
2795
|
}
|
|
2709
2796
|
const decision = this.options.initialStrategy ? buildInitialDecision(this.options.initialStrategy, ctx) : classifyContext(ctx);
|
|
2797
|
+
dbg.info(
|
|
2798
|
+
"classify",
|
|
2799
|
+
`strategy=${decision.strategy} class=${decision.class} reason="${decision.reason}"` + (decision.fallbackChain ? ` fallback=${decision.fallbackChain.join("\u2192")}` : "")
|
|
2800
|
+
);
|
|
2710
2801
|
this.classification = decision;
|
|
2711
2802
|
this.diag.recordClassification(decision);
|
|
2712
2803
|
this.emitter.emitSticky("strategy", {
|
|
@@ -2732,6 +2823,14 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
2732
2823
|
this.startTimeupdateLoop();
|
|
2733
2824
|
this.options.target.addEventListener("ended", () => this.emitter.emit("ended", void 0));
|
|
2734
2825
|
this.emitter.emitSticky("ready", void 0);
|
|
2826
|
+
const bootstrapElapsed = performance.now() - bootstrapStart;
|
|
2827
|
+
dbg.info("bootstrap", `ready in ${bootstrapElapsed.toFixed(0)}ms`);
|
|
2828
|
+
if (bootstrapElapsed > 5e3) {
|
|
2829
|
+
console.warn(
|
|
2830
|
+
"[avbridge:bootstrap]",
|
|
2831
|
+
`total bootstrap time ${bootstrapElapsed.toFixed(0)}ms \u2014 unusually slow. Enable globalThis.AVBRIDGE_DEBUG for a per-phase breakdown.`
|
|
2832
|
+
);
|
|
2833
|
+
}
|
|
2735
2834
|
} catch (err) {
|
|
2736
2835
|
const e = err instanceof Error ? err : new Error(String(err));
|
|
2737
2836
|
this.diag.recordError(e);
|
|
@@ -3056,5 +3155,5 @@ function defaultFallbackChain(strategy) {
|
|
|
3056
3155
|
}
|
|
3057
3156
|
|
|
3058
3157
|
export { UnifiedPlayer, avbridgeAudioToMediabunny, avbridgeVideoToMediabunny, buildMediabunnySourceFromInput, classifyContext, createPlayer, probe, srtToVtt };
|
|
3059
|
-
//# sourceMappingURL=chunk-
|
|
3060
|
-
//# sourceMappingURL=chunk-
|
|
3158
|
+
//# sourceMappingURL=chunk-C5VA5U5O.js.map
|
|
3159
|
+
//# sourceMappingURL=chunk-C5VA5U5O.js.map
|