avbridge 2.8.3 → 2.9.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 +165 -0
- package/README.md +74 -1
- package/dist/{avi-F6WZJK5T.cjs → avi-2ILLBNPQ.cjs} +8 -2
- package/dist/avi-2ILLBNPQ.cjs.map +1 -0
- package/dist/{avi-W6L3BTWU.cjs → avi-B5CQYB7L.cjs} +8 -2
- package/dist/avi-B5CQYB7L.cjs.map +1 -0
- package/dist/{avi-2JPBSHGA.js → avi-JXU4GQL2.js} +8 -2
- package/dist/avi-JXU4GQL2.js.map +1 -0
- package/dist/{avi-NJXAXUXK.js → avi-RWWPN2PR.js} +8 -2
- package/dist/avi-RWWPN2PR.js.map +1 -0
- package/dist/{chunk-X2K3GIWE.js → chunk-2NSOOMXW.js} +14 -3
- package/dist/chunk-2NSOOMXW.js.map +1 -0
- package/dist/{chunk-ZCUXHW55.cjs → chunk-BYGZN4Z5.cjs} +5 -5
- package/dist/{chunk-ZCUXHW55.cjs.map → chunk-BYGZN4Z5.cjs.map} +1 -1
- package/dist/{chunk-SMH6IOP2.js → chunk-CL6UEUQF.js} +4 -4
- package/dist/{chunk-SMH6IOP2.js.map → chunk-CL6UEUQF.js.map} +1 -1
- package/dist/{chunk-IUSFLVLJ.cjs → chunk-EY6DZEDT.cjs} +149 -24
- package/dist/chunk-EY6DZEDT.cjs.map +1 -0
- package/dist/{chunk-SR3MPV4D.js → chunk-GYIJU44C.js} +5 -5
- package/dist/{chunk-SR3MPV4D.js.map → chunk-GYIJU44C.js.map} +1 -1
- package/dist/{chunk-CPZ7PXAM.cjs → chunk-L7A3ECI2.cjs} +14 -2
- package/dist/chunk-L7A3ECI2.cjs.map +1 -0
- package/dist/{chunk-Q2VUO52Z.cjs → chunk-OTFS7DC4.cjs} +12 -12
- package/dist/{chunk-Q2VUO52Z.cjs.map → chunk-OTFS7DC4.cjs.map} +1 -1
- package/dist/{chunk-JSQOBUQB.js → chunk-SN4WZE24.js} +139 -14
- package/dist/chunk-SN4WZE24.js.map +1 -0
- package/dist/element-browser.js +164 -16
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +16 -10
- package/dist/element.cjs.map +1 -1
- package/dist/element.d.cts +11 -6
- package/dist/element.d.ts +11 -6
- package/dist/element.js +15 -9
- package/dist/element.js.map +1 -1
- package/dist/index.cjs +20 -20
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -8
- package/dist/libav-demux-3N5Y3VQA.cjs +31 -0
- package/dist/{libav-demux-H2GS46GH.cjs.map → libav-demux-3N5Y3VQA.cjs.map} +1 -1
- package/dist/libav-demux-JXD4OTLM.js +6 -0
- package/dist/{libav-demux-OWZ4T2YW.js.map → libav-demux-JXD4OTLM.js.map} +1 -1
- package/dist/{player-DXEKOky8.d.cts → player-DEcidWk6.d.cts} +8 -1
- package/dist/{player-DXEKOky8.d.ts → player-DEcidWk6.d.ts} +8 -1
- package/dist/player.cjs +266 -36
- package/dist/player.cjs.map +1 -1
- package/dist/player.d.cts +37 -11
- package/dist/player.d.ts +37 -11
- package/dist/player.js +266 -36
- package/dist/player.js.map +1 -1
- package/dist/{remux-WBYIZBBX.js → remux-56V7LDAD.js} +5 -5
- package/dist/{remux-WBYIZBBX.js.map → remux-56V7LDAD.js.map} +1 -1
- package/dist/{remux-OBSMIENG.cjs → remux-KUS5GIL6.cjs} +10 -10
- package/dist/{remux-OBSMIENG.cjs.map → remux-KUS5GIL6.cjs.map} +1 -1
- package/package.json +1 -1
- package/src/classify/rules.ts +11 -0
- package/src/element/avbridge-player.ts +22 -11
- package/src/element/avbridge-video.ts +22 -6
- package/src/element/player-styles.ts +68 -3
- package/src/player.ts +96 -8
- package/src/probe/avi.ts +2 -0
- package/src/strategies/fallback/decoder.ts +30 -0
- package/src/strategies/fallback/index.ts +30 -0
- package/src/strategies/hybrid/decoder.ts +35 -0
- package/src/strategies/hybrid/index.ts +17 -0
- package/src/strategies/remux/index.ts +8 -0
- package/src/types.ts +6 -0
- package/src/util/libav-demux.ts +26 -0
- package/dist/avi-2JPBSHGA.js.map +0 -1
- package/dist/avi-F6WZJK5T.cjs.map +0 -1
- package/dist/avi-NJXAXUXK.js.map +0 -1
- package/dist/avi-W6L3BTWU.cjs.map +0 -1
- package/dist/chunk-CPZ7PXAM.cjs.map +0 -1
- package/dist/chunk-IUSFLVLJ.cjs.map +0 -1
- package/dist/chunk-JSQOBUQB.js.map +0 -1
- package/dist/chunk-X2K3GIWE.js.map +0 -1
- package/dist/libav-demux-H2GS46GH.cjs +0 -27
- package/dist/libav-demux-OWZ4T2YW.js +0 -6
package/dist/element-browser.js
CHANGED
|
@@ -30030,6 +30030,12 @@ function ffmpegToAvbridgeVideo(name) {
|
|
|
30030
30030
|
return "rv30";
|
|
30031
30031
|
case "rv40":
|
|
30032
30032
|
return "rv40";
|
|
30033
|
+
case "dvvideo":
|
|
30034
|
+
return "dv";
|
|
30035
|
+
// DV / DVCPRO (camcorder, MiniDV)
|
|
30036
|
+
case "hq_hqa":
|
|
30037
|
+
return "hq_hqa";
|
|
30038
|
+
// Canopus HQ / HQA (Grass Valley)
|
|
30033
30039
|
default:
|
|
30034
30040
|
return name;
|
|
30035
30041
|
}
|
|
@@ -31564,7 +31570,13 @@ var FALLBACK_VIDEO_CODECS = /* @__PURE__ */ new Set([
|
|
|
31564
31570
|
"rv40",
|
|
31565
31571
|
"mpeg2",
|
|
31566
31572
|
"mpeg1",
|
|
31567
|
-
"theora"
|
|
31573
|
+
"theora",
|
|
31574
|
+
"dv",
|
|
31575
|
+
"hq_hqa",
|
|
31576
|
+
"rawvideo",
|
|
31577
|
+
"qtrle",
|
|
31578
|
+
"png",
|
|
31579
|
+
"vp6f"
|
|
31568
31580
|
]);
|
|
31569
31581
|
var FALLBACK_AUDIO_CODECS = /* @__PURE__ */ new Set([
|
|
31570
31582
|
"wmav2",
|
|
@@ -31705,10 +31717,12 @@ function classifyContext(ctx) {
|
|
|
31705
31717
|
reason: `${ctx.container} container with ${video.codec}${audio ? "/" + audio.codec : ""}; MSE rejects the remux target mime and WebCodecs is unavailable \u2014 falling back to WASM decode`
|
|
31706
31718
|
};
|
|
31707
31719
|
}
|
|
31720
|
+
const fallbackChain = webCodecsAvailable() ? ["hybrid", "fallback"] : ["fallback"];
|
|
31708
31721
|
return {
|
|
31709
31722
|
class: "REMUX_CANDIDATE",
|
|
31710
31723
|
strategy: "remux",
|
|
31711
|
-
reason: `${ctx.container} container with native-supported codecs \u2014 remux to fragmented MP4 for reliable playback
|
|
31724
|
+
reason: `${ctx.container} container with native-supported codecs \u2014 remux to fragmented MP4 for reliable playback`,
|
|
31725
|
+
fallbackChain
|
|
31712
31726
|
};
|
|
31713
31727
|
}
|
|
31714
31728
|
if (webCodecsAvailable()) {
|
|
@@ -32399,6 +32413,12 @@ async function createRemuxSession(context, video) {
|
|
|
32399
32413
|
}
|
|
32400
32414
|
const wasPlaying = !video.paused;
|
|
32401
32415
|
await pipeline.seek(time, wasPlaying || wantPlay);
|
|
32416
|
+
queueMicrotask(() => {
|
|
32417
|
+
try {
|
|
32418
|
+
video.dispatchEvent(new Event("seeked"));
|
|
32419
|
+
} catch {
|
|
32420
|
+
}
|
|
32421
|
+
});
|
|
32402
32422
|
},
|
|
32403
32423
|
async setAudioTrack(id) {
|
|
32404
32424
|
if (!context.audioTracks.some((t) => t.id === id)) {
|
|
@@ -33040,6 +33060,17 @@ function sanitizePacketTimestamp(pkt, nextUs, fallbackTimeBase) {
|
|
|
33040
33060
|
pkt.time_base_num = 1;
|
|
33041
33061
|
pkt.time_base_den = 1e6;
|
|
33042
33062
|
}
|
|
33063
|
+
function packetPtsSec(pkt, timeBase) {
|
|
33064
|
+
const lo = pkt.pts ?? 0;
|
|
33065
|
+
const hi = pkt.ptshi ?? 0;
|
|
33066
|
+
const isInvalid = hi === -2147483648 && lo === 0 || !Number.isFinite(lo);
|
|
33067
|
+
if (isInvalid) return null;
|
|
33068
|
+
const tb = timeBase ?? [1, 1e6];
|
|
33069
|
+
if (!tb[0] || !tb[1]) return null;
|
|
33070
|
+
const pts64 = hi * 4294967296 + lo;
|
|
33071
|
+
const sec = pts64 * tb[0] / tb[1];
|
|
33072
|
+
return Number.isFinite(sec) ? sec : null;
|
|
33073
|
+
}
|
|
33043
33074
|
var AV_SAMPLE_FMT_U8 = 0;
|
|
33044
33075
|
var AV_SAMPLE_FMT_S16 = 1;
|
|
33045
33076
|
var AV_SAMPLE_FMT_S32 = 2;
|
|
@@ -33300,6 +33331,7 @@ async function startHybridDecoder(opts) {
|
|
|
33300
33331
|
let videoFramesDecoded = 0;
|
|
33301
33332
|
let audioFramesDecoded = 0;
|
|
33302
33333
|
let videoChunksFed = 0;
|
|
33334
|
+
let bufferedUntilSec = 0;
|
|
33303
33335
|
let syntheticVideoUs = 0;
|
|
33304
33336
|
let syntheticAudioUs = 0;
|
|
33305
33337
|
const videoTrackInfo = opts.context.videoTracks.find((t) => t.id === videoStream?.index);
|
|
@@ -33320,6 +33352,18 @@ async function startHybridDecoder(opts) {
|
|
|
33320
33352
|
if (myToken !== pumpToken || destroyed) return;
|
|
33321
33353
|
const videoPackets = videoStream ? packets[videoStream.index] : void 0;
|
|
33322
33354
|
const audioPackets = audioStream ? packets[audioStream.index] : void 0;
|
|
33355
|
+
if (videoPackets && videoTimeBase) {
|
|
33356
|
+
for (const pkt of videoPackets) {
|
|
33357
|
+
const sec = packetPtsSec(pkt, videoTimeBase);
|
|
33358
|
+
if (sec != null && sec > bufferedUntilSec) bufferedUntilSec = sec;
|
|
33359
|
+
}
|
|
33360
|
+
}
|
|
33361
|
+
if (audioPackets && audioTimeBase) {
|
|
33362
|
+
for (const pkt of audioPackets) {
|
|
33363
|
+
const sec = packetPtsSec(pkt, audioTimeBase);
|
|
33364
|
+
if (sec != null && sec > bufferedUntilSec) bufferedUntilSec = sec;
|
|
33365
|
+
}
|
|
33366
|
+
}
|
|
33323
33367
|
if (audioDec && audioPackets && audioPackets.length > 0) {
|
|
33324
33368
|
await decodeAudioBatch(audioPackets, myToken);
|
|
33325
33369
|
}
|
|
@@ -33576,6 +33620,9 @@ async function startHybridDecoder(opts) {
|
|
|
33576
33620
|
(err) => console.error("[avbridge] hybrid pump failed (post-seek):", err)
|
|
33577
33621
|
);
|
|
33578
33622
|
},
|
|
33623
|
+
bufferedUntilSec() {
|
|
33624
|
+
return bufferedUntilSec;
|
|
33625
|
+
},
|
|
33579
33626
|
stats() {
|
|
33580
33627
|
return {
|
|
33581
33628
|
decoderType: "webcodecs-hybrid",
|
|
@@ -33703,6 +33750,13 @@ async function createHybridSession(ctx, target, transport) {
|
|
|
33703
33750
|
configurable: true,
|
|
33704
33751
|
get: () => makeTimeRanges(ctx.duration && Number.isFinite(ctx.duration) && ctx.duration > 0 ? [[0, ctx.duration]] : [])
|
|
33705
33752
|
});
|
|
33753
|
+
Object.defineProperty(target, "buffered", {
|
|
33754
|
+
configurable: true,
|
|
33755
|
+
get: () => {
|
|
33756
|
+
const end = handles.bufferedUntilSec();
|
|
33757
|
+
return makeTimeRanges(end > 0 ? [[0, end]] : []);
|
|
33758
|
+
}
|
|
33759
|
+
});
|
|
33706
33760
|
async function waitForBuffer() {
|
|
33707
33761
|
const start = performance.now();
|
|
33708
33762
|
while (true) {
|
|
@@ -33716,6 +33770,7 @@ async function createHybridSession(ctx, target, transport) {
|
|
|
33716
33770
|
}
|
|
33717
33771
|
async function doSeek(timeSec) {
|
|
33718
33772
|
const wasPlaying = audio.isPlaying();
|
|
33773
|
+
target.dispatchEvent(new Event("seeking"));
|
|
33719
33774
|
await audio.pause().catch(() => {
|
|
33720
33775
|
});
|
|
33721
33776
|
await handles.seek(timeSec).catch(
|
|
@@ -33727,7 +33782,14 @@ async function createHybridSession(ctx, target, transport) {
|
|
|
33727
33782
|
await waitForBuffer();
|
|
33728
33783
|
await audio.start();
|
|
33729
33784
|
}
|
|
33785
|
+
target.dispatchEvent(new Event("seeked"));
|
|
33730
33786
|
}
|
|
33787
|
+
queueMicrotask(() => {
|
|
33788
|
+
try {
|
|
33789
|
+
target.dispatchEvent(new Event("loadedmetadata"));
|
|
33790
|
+
} catch {
|
|
33791
|
+
}
|
|
33792
|
+
});
|
|
33731
33793
|
let fatalErrorHandler = null;
|
|
33732
33794
|
handles.onFatalError((reason) => fatalErrorHandler?.(reason));
|
|
33733
33795
|
return {
|
|
@@ -33914,6 +33976,7 @@ async function startDecoder(opts) {
|
|
|
33914
33976
|
let pumpRunning = null;
|
|
33915
33977
|
let packetsRead = 0;
|
|
33916
33978
|
let videoFramesDecoded = 0;
|
|
33979
|
+
let bufferedUntilSec = 0;
|
|
33917
33980
|
let audioFramesDecoded = 0;
|
|
33918
33981
|
let watchdogFirstFrameMs = 0;
|
|
33919
33982
|
let watchdogSlowSinceMs = 0;
|
|
@@ -33939,6 +34002,18 @@ async function startDecoder(opts) {
|
|
|
33939
34002
|
if (myToken !== pumpToken || destroyed) return;
|
|
33940
34003
|
const videoPackets = videoStream ? packets[videoStream.index] : void 0;
|
|
33941
34004
|
const audioPackets = audioStream ? packets[audioStream.index] : void 0;
|
|
34005
|
+
if (videoPackets && videoTimeBase) {
|
|
34006
|
+
for (const pkt of videoPackets) {
|
|
34007
|
+
const sec = packetPtsSec(pkt, videoTimeBase);
|
|
34008
|
+
if (sec != null && sec > bufferedUntilSec) bufferedUntilSec = sec;
|
|
34009
|
+
}
|
|
34010
|
+
}
|
|
34011
|
+
if (audioPackets && audioTimeBase) {
|
|
34012
|
+
for (const pkt of audioPackets) {
|
|
34013
|
+
const sec = packetPtsSec(pkt, audioTimeBase);
|
|
34014
|
+
if (sec != null && sec > bufferedUntilSec) bufferedUntilSec = sec;
|
|
34015
|
+
}
|
|
34016
|
+
}
|
|
33942
34017
|
if (audioDec && audioPackets && audioPackets.length > 0) {
|
|
33943
34018
|
await decodeAudioBatch(audioPackets, myToken);
|
|
33944
34019
|
}
|
|
@@ -34220,6 +34295,9 @@ async function startDecoder(opts) {
|
|
|
34220
34295
|
(err) => console.error("[avbridge] decoder pump failed (post-seek):", err)
|
|
34221
34296
|
);
|
|
34222
34297
|
},
|
|
34298
|
+
bufferedUntilSec() {
|
|
34299
|
+
return bufferedUntilSec;
|
|
34300
|
+
},
|
|
34223
34301
|
stats() {
|
|
34224
34302
|
return {
|
|
34225
34303
|
decoderType: "libav-wasm",
|
|
@@ -34320,6 +34398,13 @@ async function createFallbackSession(ctx, target, transport) {
|
|
|
34320
34398
|
configurable: true,
|
|
34321
34399
|
get: () => makeTimeRanges(ctx.duration && Number.isFinite(ctx.duration) && ctx.duration > 0 ? [[0, ctx.duration]] : [])
|
|
34322
34400
|
});
|
|
34401
|
+
Object.defineProperty(target, "buffered", {
|
|
34402
|
+
configurable: true,
|
|
34403
|
+
get: () => {
|
|
34404
|
+
const end = handles.bufferedUntilSec();
|
|
34405
|
+
return makeTimeRanges(end > 0 ? [[0, end]] : []);
|
|
34406
|
+
}
|
|
34407
|
+
});
|
|
34323
34408
|
async function waitForBuffer() {
|
|
34324
34409
|
const start = performance.now();
|
|
34325
34410
|
let firstFrameAtMs = 0;
|
|
@@ -34359,6 +34444,7 @@ async function createFallbackSession(ctx, target, transport) {
|
|
|
34359
34444
|
}
|
|
34360
34445
|
async function doSeek(timeSec) {
|
|
34361
34446
|
const wasPlaying = audio.isPlaying();
|
|
34447
|
+
target.dispatchEvent(new Event("seeking"));
|
|
34362
34448
|
await audio.pause().catch(() => {
|
|
34363
34449
|
});
|
|
34364
34450
|
await handles.seek(timeSec).catch(
|
|
@@ -34370,7 +34456,14 @@ async function createFallbackSession(ctx, target, transport) {
|
|
|
34370
34456
|
await waitForBuffer();
|
|
34371
34457
|
await audio.start();
|
|
34372
34458
|
}
|
|
34459
|
+
target.dispatchEvent(new Event("seeked"));
|
|
34373
34460
|
}
|
|
34461
|
+
queueMicrotask(() => {
|
|
34462
|
+
try {
|
|
34463
|
+
target.dispatchEvent(new Event("loadedmetadata"));
|
|
34464
|
+
} catch {
|
|
34465
|
+
}
|
|
34466
|
+
});
|
|
34374
34467
|
return {
|
|
34375
34468
|
strategy: "fallback",
|
|
34376
34469
|
async play() {
|
|
@@ -34465,6 +34558,29 @@ function registerBuiltins(registry) {
|
|
|
34465
34558
|
init_subtitles2();
|
|
34466
34559
|
init_debug();
|
|
34467
34560
|
init_errors();
|
|
34561
|
+
function readDecodedFrameCount(target) {
|
|
34562
|
+
if (typeof HTMLVideoElement === "undefined" || !(target instanceof HTMLVideoElement)) return 0;
|
|
34563
|
+
const vq = target.getVideoPlaybackQuality;
|
|
34564
|
+
if (typeof vq === "function") {
|
|
34565
|
+
try {
|
|
34566
|
+
return vq.call(target).totalVideoFrames;
|
|
34567
|
+
} catch {
|
|
34568
|
+
}
|
|
34569
|
+
}
|
|
34570
|
+
const legacy = target.webkitDecodedFrameCount;
|
|
34571
|
+
return typeof legacy === "number" ? legacy : 0;
|
|
34572
|
+
}
|
|
34573
|
+
function evaluateDecodeHealth(input) {
|
|
34574
|
+
const timeThreshold = input.timeStallThresholdMs ?? 5e3;
|
|
34575
|
+
const frameThreshold = input.frameStallThresholdMs ?? 3e3;
|
|
34576
|
+
if (!input.timeAdvanced && input.now - input.lastProgressTime > timeThreshold) {
|
|
34577
|
+
return { escalate: true, kind: "time-stall" };
|
|
34578
|
+
}
|
|
34579
|
+
if (input.hasVideoTrack && input.timeAdvanced && !input.framesAdvanced && input.now - input.lastFrameProgressTime > frameThreshold) {
|
|
34580
|
+
return { escalate: true, kind: "silent-video" };
|
|
34581
|
+
}
|
|
34582
|
+
return { escalate: false };
|
|
34583
|
+
}
|
|
34468
34584
|
var UnifiedPlayer = class _UnifiedPlayer {
|
|
34469
34585
|
/**
|
|
34470
34586
|
* @internal Use {@link createPlayer} or {@link UnifiedPlayer.create} instead.
|
|
@@ -34490,6 +34606,13 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
34490
34606
|
stallTimer = null;
|
|
34491
34607
|
lastProgressTime = 0;
|
|
34492
34608
|
lastProgressPosition = -1;
|
|
34609
|
+
/** Last observed `HTMLVideoElement.getVideoPlaybackQuality().totalVideoFrames`
|
|
34610
|
+
* (or `webkitDecodedFrameCount` fallback). Used by the silent-video
|
|
34611
|
+
* watchdog — catches cases where `currentTime` advances (audio plays)
|
|
34612
|
+
* but the decoder produces no frames, e.g. Firefox claiming `hev1.*`
|
|
34613
|
+
* via MSE when the decoder actually can't decode HEVC. */
|
|
34614
|
+
lastVideoFrameCount = 0;
|
|
34615
|
+
lastVideoFrameProgressTime = 0;
|
|
34493
34616
|
errorListener = null;
|
|
34494
34617
|
// Bound so we can removeEventListener in destroy(); without this the
|
|
34495
34618
|
// listener outlives the player and accumulates on elements that swap
|
|
@@ -34734,22 +34857,41 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
34734
34857
|
if (strategy === "native" || strategy === "remux") {
|
|
34735
34858
|
this.lastProgressPosition = this.options.target.currentTime;
|
|
34736
34859
|
this.lastProgressTime = performance.now();
|
|
34860
|
+
this.lastVideoFrameCount = readDecodedFrameCount(this.options.target);
|
|
34861
|
+
this.lastVideoFrameProgressTime = performance.now();
|
|
34862
|
+
const hasVideoTrack = (this.mediaContext?.videoTracks.length ?? 0) > 0;
|
|
34737
34863
|
this.stallTimer = setInterval(() => {
|
|
34738
34864
|
const t = this.options.target;
|
|
34865
|
+
const now = performance.now();
|
|
34739
34866
|
if (t.paused || t.ended || t.readyState < 2) {
|
|
34740
34867
|
this.lastProgressPosition = t.currentTime;
|
|
34741
|
-
this.lastProgressTime =
|
|
34868
|
+
this.lastProgressTime = now;
|
|
34869
|
+
this.lastVideoFrameCount = readDecodedFrameCount(t);
|
|
34870
|
+
this.lastVideoFrameProgressTime = now;
|
|
34742
34871
|
return;
|
|
34743
34872
|
}
|
|
34744
|
-
|
|
34873
|
+
const timeAdvanced = t.currentTime !== this.lastProgressPosition;
|
|
34874
|
+
const frames = readDecodedFrameCount(t);
|
|
34875
|
+
const framesAdvanced = frames > this.lastVideoFrameCount;
|
|
34876
|
+
const health = evaluateDecodeHealth({
|
|
34877
|
+
hasVideoTrack,
|
|
34878
|
+
timeAdvanced,
|
|
34879
|
+
framesAdvanced,
|
|
34880
|
+
now,
|
|
34881
|
+
lastProgressTime: this.lastProgressTime,
|
|
34882
|
+
lastFrameProgressTime: this.lastVideoFrameProgressTime
|
|
34883
|
+
});
|
|
34884
|
+
if (timeAdvanced) {
|
|
34745
34885
|
this.lastProgressPosition = t.currentTime;
|
|
34746
|
-
this.lastProgressTime =
|
|
34747
|
-
return;
|
|
34886
|
+
this.lastProgressTime = now;
|
|
34748
34887
|
}
|
|
34749
|
-
if (
|
|
34750
|
-
|
|
34751
|
-
|
|
34752
|
-
|
|
34888
|
+
if (framesAdvanced) {
|
|
34889
|
+
this.lastVideoFrameCount = frames;
|
|
34890
|
+
this.lastVideoFrameProgressTime = now;
|
|
34891
|
+
}
|
|
34892
|
+
if (health.escalate) {
|
|
34893
|
+
const reason = health.kind === "time-stall" ? `${strategy} strategy stalled for 5s at ${t.currentTime.toFixed(1)}s` : `${strategy} strategy: audio is advancing but the video decoder has produced no new frames for 3s \u2014 likely a silent codec failure`;
|
|
34894
|
+
void this.escalate(reason);
|
|
34753
34895
|
}
|
|
34754
34896
|
}, 1e3);
|
|
34755
34897
|
const onError = () => {
|
|
@@ -35404,9 +35546,10 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
|
|
|
35404
35546
|
else this.removeAttribute("autoplay");
|
|
35405
35547
|
}
|
|
35406
35548
|
get muted() {
|
|
35407
|
-
return this.
|
|
35549
|
+
return this._videoEl.muted;
|
|
35408
35550
|
}
|
|
35409
35551
|
set muted(value) {
|
|
35552
|
+
this._videoEl.muted = value;
|
|
35410
35553
|
if (value) this.setAttribute("muted", "");
|
|
35411
35554
|
else this.removeAttribute("muted");
|
|
35412
35555
|
}
|
|
@@ -35471,11 +35614,16 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
|
|
|
35471
35614
|
}
|
|
35472
35615
|
/**
|
|
35473
35616
|
* Buffered time ranges for the active source. Mirrors the standard
|
|
35474
|
-
* `<video>.buffered` `TimeRanges` API.
|
|
35475
|
-
*
|
|
35476
|
-
*
|
|
35477
|
-
*
|
|
35478
|
-
*
|
|
35617
|
+
* `<video>.buffered` `TimeRanges` API.
|
|
35618
|
+
*
|
|
35619
|
+
* - **Native / remux:** pass-through to the real `<video>.buffered`
|
|
35620
|
+
* (reflects the browser's SourceBuffer / progressive-download state).
|
|
35621
|
+
* - **Hybrid / fallback:** a single `[0, frontier]` range synthesized
|
|
35622
|
+
* from the demuxer's read progress — "how far libav has ever pumped
|
|
35623
|
+
* packets through." Monotonic; does not shrink on seek. This is an
|
|
35624
|
+
* approximation, not MSE-fidelity: decoded frames on canvas strategies
|
|
35625
|
+
* are consumed in flight, so we can't report per-range availability
|
|
35626
|
+
* the way MSE does. Enough for a seek-bar buffered indicator.
|
|
35479
35627
|
*/
|
|
35480
35628
|
get buffered() {
|
|
35481
35629
|
return this._videoEl.buffered;
|