Skip to content

Commit

Permalink
Force auto level on emergency switch down
Browse files Browse the repository at this point in the history
Update estimates on frag load timeout
Do not abort request in _abandonRulesCheck
Remove two segment forward buffer length limit in _abandonRulesCheck
Reset estimate when candidate bitrate is lower than adjusted estimate
Resolves #6079
  • Loading branch information
robwalch committed Jan 8, 2024
1 parent 0b0c98a commit 87330a0
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 23 deletions.
72 changes: 51 additions & 21 deletions src/controller/abr-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ class AbrController implements AbrComponentAPI {
private audioTracksByGroup: AudioTracksByGroup | null = null;
private codecTiers: Record<string, CodecSetTier> | null = null;
private timer: number = -1;
private onCheck: Function = this._abandonRulesCheck.bind(this);
private fragCurrent: Fragment | null = null;
private partCurrent: Part | null = null;
private bitrateTestDelay: number = 0;
Expand Down Expand Up @@ -105,7 +104,7 @@ class AbrController implements AbrComponentAPI {
this.unregisterListeners();
this.clearTimer();
// @ts-ignore
this.hls = this.onCheck = null;
this.hls = this._abandonRulesCheck = null;
this.fragCurrent = this.partCurrent = null;
}

Expand Down Expand Up @@ -146,7 +145,7 @@ class AbrController implements AbrComponentAPI {
this.partCurrent = data.part ?? null;
}
this.clearTimer();
this.timer = self.setInterval(this.onCheck, 100);
this.timer = self.setInterval(this._abandonRulesCheck, 100);
}

protected onLevelSwitching(
Expand All @@ -166,6 +165,35 @@ class AbrController implements AbrComponentAPI {
// Reset last loaded level so that a new selection can be made after calling recoverMediaError
this.lastLoadedFragLevel = -1;
this.firstSelection = -1;
break;
case ErrorDetails.FRAG_LOAD_TIMEOUT: {
const frag = data.frag;
const { fragCurrent, partCurrent: part } = this;
if (
frag &&
fragCurrent &&
frag.sn === fragCurrent.sn &&
frag.level === fragCurrent.level
) {
const now = performance.now();
const stats: LoaderStats = part ? part.stats : frag.stats;
const timeLoading = now - stats.loading.start;
const ttfb = stats.loading.first
? stats.loading.first - stats.loading.start
: -1;
const loadedFirstByte = stats.loaded && ttfb > -1;
if (loadedFirstByte) {
const ttfbEstimate = this.bwEstimator.getEstimateTTFB();
this.bwEstimator.sample(
timeLoading - Math.min(ttfbEstimate, ttfb),
stats.loaded,
);
} else {
this.bwEstimator.sampleTTFB(timeLoading);
}
}
break;
}
}
}

Expand Down Expand Up @@ -198,7 +226,7 @@ class AbrController implements AbrComponentAPI {
This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load
quickly enough to prevent underbuffering
*/
private _abandonRulesCheck() {
private _abandonRulesCheck = () => {
const { fragCurrent: frag, partCurrent: part, hls } = this;
const { autoLevelEnabled, media } = hls;
if (!frag || !media) {
Expand Down Expand Up @@ -249,11 +277,6 @@ class AbrController implements AbrComponentAPI {

// bufferStarvationDelay is an estimate of the amount time (in seconds) it will take to exhaust the buffer
const bufferStarvationDelay = bufferInfo.len / playbackRate;
// Only downswitch if less than 2 fragment lengths are buffered
if (bufferStarvationDelay >= (2 * duration) / playbackRate) {
return;
}

const ttfb = stats.loading.first
? stats.loading.first - stats.loading.start
: -1;
Expand Down Expand Up @@ -312,7 +335,7 @@ class AbrController implements AbrComponentAPI {
if (fragLevelNextLoadedDelay > duration * 10) {
return;
}
hls.nextLoadLevel = nextLoadLevel;
hls.nextLoadLevel = hls.nextAutoLevel = nextLoadLevel;
if (loadedFirstByte) {
// If there has been loading progress, sample bandwidth using loading time offset by minimum TTFB time
this.bwEstimator.sample(
Expand All @@ -323,6 +346,13 @@ class AbrController implements AbrComponentAPI {
// If there has been no loading progress, sample TTFB
this.bwEstimator.sampleTTFB(timeLoading);
}
const nextLoadLevelBitrate = levels[nextLoadLevel].bitrate;
if (
this.getBwEstimate() * this.hls.config.abrBandWidthUpFactor >
nextLoadLevelBitrate
) {
this.resetEstimator(nextLoadLevelBitrate);
}

this.clearTimer();
logger.warn(`[abr] Fragment ${frag.sn}${
Expand All @@ -333,18 +363,14 @@ class AbrController implements AbrComponentAPI {
Estimated load time for down switch fragment: ${fragLevelNextLoadedDelay.toFixed(
3,
)} s
TTFB estimate: ${ttfb}
TTFB estimate: ${ttfb | 0} ms
Current BW estimate: ${
Number.isFinite(bwEstimate) ? (bwEstimate / 1024).toFixed(3) : 'Unknown'
} Kb/s
New BW estimate: ${(this.getBwEstimate() / 1024).toFixed(3)} Kb/s
Aborting and switching to level ${nextLoadLevel}`);
if (frag.loader) {
this.fragCurrent = this.partCurrent = null;
frag.abortRequests();
}
Number.isFinite(bwEstimate) ? bwEstimate | 0 : 'Unknown'
} bps
New BW estimate: ${this.getBwEstimate() | 0} bps
Switching to level ${nextLoadLevel} @ ${nextLoadLevelBitrate | 0} bps`);
hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, { frag, part, stats });
}
};

protected onFragLoaded(
event: Events.FRAG_LOADED,
Expand Down Expand Up @@ -799,7 +825,11 @@ class AbrController implements AbrComponentAPI {
(live && !this.bitrateTestDelay) ||
fetchDuration < maxFetchDuration);
if (canSwitchWithinTolerance) {
if (i !== loadLevel) {
const forcedAutoLevel = this.forcedAutoLevel;
if (
i !== loadLevel &&
(forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)
) {
if (levelsSkipped.length) {
logger.trace(
`[abr] Skipped level(s) ${levelsSkipped.join(
Expand Down
5 changes: 3 additions & 2 deletions src/utils/level-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,9 @@ export function mergeDetails(
: newDetails.fragments;
fragmentsToCheck.forEach((frag) => {
if (
!frag.initSegment ||
frag.initSegment.relurl === currentInitSegment?.relurl
frag &&
(!frag.initSegment ||
frag.initSegment.relurl === currentInitSegment?.relurl)
) {
frag.initSegment = currentInitSegment;
}
Expand Down

0 comments on commit 87330a0

Please sign in to comment.