avbridge 2.8.3 → 2.8.4
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 +32 -0
- package/dist/{chunk-JSQOBUQB.js → chunk-KBWQRGHS.js} +62 -11
- package/dist/chunk-KBWQRGHS.js.map +1 -0
- package/dist/{chunk-IUSFLVLJ.cjs → chunk-YX4AGLNF.cjs} +62 -11
- package/dist/chunk-YX4AGLNF.cjs.map +1 -0
- package/dist/element-browser.js +60 -9
- package/dist/element-browser.js.map +1 -1
- package/dist/element.cjs +2 -2
- package/dist/element.d.cts +1 -1
- package/dist/element.d.ts +1 -1
- package/dist/element.js +1 -1
- package/dist/index.cjs +8 -8
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{player-DXEKOky8.d.cts → player-BptSJPfn.d.cts} +7 -0
- package/dist/{player-DXEKOky8.d.ts → player-BptSJPfn.d.ts} +7 -0
- package/dist/player.cjs +79 -13
- package/dist/player.cjs.map +1 -1
- package/dist/player.d.cts +20 -0
- package/dist/player.d.ts +20 -0
- package/dist/player.js +79 -13
- package/dist/player.js.map +1 -1
- package/package.json +1 -1
- package/src/classify/rules.ts +9 -0
- package/src/player.ts +96 -8
- package/dist/chunk-IUSFLVLJ.cjs.map +0 -1
- package/dist/chunk-JSQOBUQB.js.map +0 -1
package/dist/element-browser.js
CHANGED
|
@@ -31705,10 +31705,12 @@ function classifyContext(ctx) {
|
|
|
31705
31705
|
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
31706
|
};
|
|
31707
31707
|
}
|
|
31708
|
+
const fallbackChain = webCodecsAvailable() ? ["hybrid", "fallback"] : ["fallback"];
|
|
31708
31709
|
return {
|
|
31709
31710
|
class: "REMUX_CANDIDATE",
|
|
31710
31711
|
strategy: "remux",
|
|
31711
|
-
reason: `${ctx.container} container with native-supported codecs \u2014 remux to fragmented MP4 for reliable playback
|
|
31712
|
+
reason: `${ctx.container} container with native-supported codecs \u2014 remux to fragmented MP4 for reliable playback`,
|
|
31713
|
+
fallbackChain
|
|
31712
31714
|
};
|
|
31713
31715
|
}
|
|
31714
31716
|
if (webCodecsAvailable()) {
|
|
@@ -34465,6 +34467,29 @@ function registerBuiltins(registry) {
|
|
|
34465
34467
|
init_subtitles2();
|
|
34466
34468
|
init_debug();
|
|
34467
34469
|
init_errors();
|
|
34470
|
+
function readDecodedFrameCount(target) {
|
|
34471
|
+
if (typeof HTMLVideoElement === "undefined" || !(target instanceof HTMLVideoElement)) return 0;
|
|
34472
|
+
const vq = target.getVideoPlaybackQuality;
|
|
34473
|
+
if (typeof vq === "function") {
|
|
34474
|
+
try {
|
|
34475
|
+
return vq.call(target).totalVideoFrames;
|
|
34476
|
+
} catch {
|
|
34477
|
+
}
|
|
34478
|
+
}
|
|
34479
|
+
const legacy = target.webkitDecodedFrameCount;
|
|
34480
|
+
return typeof legacy === "number" ? legacy : 0;
|
|
34481
|
+
}
|
|
34482
|
+
function evaluateDecodeHealth(input) {
|
|
34483
|
+
const timeThreshold = input.timeStallThresholdMs ?? 5e3;
|
|
34484
|
+
const frameThreshold = input.frameStallThresholdMs ?? 3e3;
|
|
34485
|
+
if (!input.timeAdvanced && input.now - input.lastProgressTime > timeThreshold) {
|
|
34486
|
+
return { escalate: true, kind: "time-stall" };
|
|
34487
|
+
}
|
|
34488
|
+
if (input.hasVideoTrack && input.timeAdvanced && !input.framesAdvanced && input.now - input.lastFrameProgressTime > frameThreshold) {
|
|
34489
|
+
return { escalate: true, kind: "silent-video" };
|
|
34490
|
+
}
|
|
34491
|
+
return { escalate: false };
|
|
34492
|
+
}
|
|
34468
34493
|
var UnifiedPlayer = class _UnifiedPlayer {
|
|
34469
34494
|
/**
|
|
34470
34495
|
* @internal Use {@link createPlayer} or {@link UnifiedPlayer.create} instead.
|
|
@@ -34490,6 +34515,13 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
34490
34515
|
stallTimer = null;
|
|
34491
34516
|
lastProgressTime = 0;
|
|
34492
34517
|
lastProgressPosition = -1;
|
|
34518
|
+
/** Last observed `HTMLVideoElement.getVideoPlaybackQuality().totalVideoFrames`
|
|
34519
|
+
* (or `webkitDecodedFrameCount` fallback). Used by the silent-video
|
|
34520
|
+
* watchdog — catches cases where `currentTime` advances (audio plays)
|
|
34521
|
+
* but the decoder produces no frames, e.g. Firefox claiming `hev1.*`
|
|
34522
|
+
* via MSE when the decoder actually can't decode HEVC. */
|
|
34523
|
+
lastVideoFrameCount = 0;
|
|
34524
|
+
lastVideoFrameProgressTime = 0;
|
|
34493
34525
|
errorListener = null;
|
|
34494
34526
|
// Bound so we can removeEventListener in destroy(); without this the
|
|
34495
34527
|
// listener outlives the player and accumulates on elements that swap
|
|
@@ -34734,22 +34766,41 @@ var UnifiedPlayer = class _UnifiedPlayer {
|
|
|
34734
34766
|
if (strategy === "native" || strategy === "remux") {
|
|
34735
34767
|
this.lastProgressPosition = this.options.target.currentTime;
|
|
34736
34768
|
this.lastProgressTime = performance.now();
|
|
34769
|
+
this.lastVideoFrameCount = readDecodedFrameCount(this.options.target);
|
|
34770
|
+
this.lastVideoFrameProgressTime = performance.now();
|
|
34771
|
+
const hasVideoTrack = (this.mediaContext?.videoTracks.length ?? 0) > 0;
|
|
34737
34772
|
this.stallTimer = setInterval(() => {
|
|
34738
34773
|
const t = this.options.target;
|
|
34774
|
+
const now = performance.now();
|
|
34739
34775
|
if (t.paused || t.ended || t.readyState < 2) {
|
|
34740
34776
|
this.lastProgressPosition = t.currentTime;
|
|
34741
|
-
this.lastProgressTime =
|
|
34777
|
+
this.lastProgressTime = now;
|
|
34778
|
+
this.lastVideoFrameCount = readDecodedFrameCount(t);
|
|
34779
|
+
this.lastVideoFrameProgressTime = now;
|
|
34742
34780
|
return;
|
|
34743
34781
|
}
|
|
34744
|
-
|
|
34782
|
+
const timeAdvanced = t.currentTime !== this.lastProgressPosition;
|
|
34783
|
+
const frames = readDecodedFrameCount(t);
|
|
34784
|
+
const framesAdvanced = frames > this.lastVideoFrameCount;
|
|
34785
|
+
const health = evaluateDecodeHealth({
|
|
34786
|
+
hasVideoTrack,
|
|
34787
|
+
timeAdvanced,
|
|
34788
|
+
framesAdvanced,
|
|
34789
|
+
now,
|
|
34790
|
+
lastProgressTime: this.lastProgressTime,
|
|
34791
|
+
lastFrameProgressTime: this.lastVideoFrameProgressTime
|
|
34792
|
+
});
|
|
34793
|
+
if (timeAdvanced) {
|
|
34745
34794
|
this.lastProgressPosition = t.currentTime;
|
|
34746
|
-
this.lastProgressTime =
|
|
34747
|
-
return;
|
|
34795
|
+
this.lastProgressTime = now;
|
|
34748
34796
|
}
|
|
34749
|
-
if (
|
|
34750
|
-
|
|
34751
|
-
|
|
34752
|
-
|
|
34797
|
+
if (framesAdvanced) {
|
|
34798
|
+
this.lastVideoFrameCount = frames;
|
|
34799
|
+
this.lastVideoFrameProgressTime = now;
|
|
34800
|
+
}
|
|
34801
|
+
if (health.escalate) {
|
|
34802
|
+
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`;
|
|
34803
|
+
void this.escalate(reason);
|
|
34753
34804
|
}
|
|
34754
34805
|
}, 1e3);
|
|
34755
34806
|
const onError = () => {
|