Skip to content

Commit ca99183

Browse files
authored
Check-in on abandon rules check and switch to lowest when stuck on in-flight frag (video-dev#6763)
Resolves video-dev#6689
1 parent 9faef0b commit ca99183

File tree

2 files changed

+71
-14
lines changed

2 files changed

+71
-14
lines changed

src/controller/abr-controller.ts

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -296,15 +296,18 @@ class AbrController extends Logger implements AbrComponentAPI {
296296
? (stats.loaded * 1000) / timeStreaming
297297
: 0;
298298
// fragLoadDelay is an estimate of the time (in seconds) it will take to buffer the remainder of the fragment
299+
const ttfbSeconds = ttfbEstimate / 1000;
299300
const fragLoadedDelay = loadRate
300301
? (expectedLen - stats.loaded) / loadRate
301-
: (expectedLen * 8) / bwEstimate + ttfbEstimate / 1000;
302+
: (expectedLen * 8) / bwEstimate + ttfbSeconds;
302303
// Only downswitch if the time to finish loading the current fragment is greater than the amount of buffer left
303304
if (fragLoadedDelay <= bufferStarvationDelay) {
304305
return;
305306
}
306307

307308
const bwe = loadRate ? loadRate * 8 : bwEstimate;
309+
const live = this.hls.latestLevelDetails?.live === true;
310+
const abrBandWidthUpFactor = this.hls.config.abrBandWidthUpFactor;
308311
let fragLevelNextLoadedDelay: number = Number.POSITIVE_INFINITY;
309312
let nextLoadLevel: number;
310313
// Iterate through lower level and try to find the largest one that avoids rebuffering
@@ -316,13 +319,17 @@ class AbrController extends Logger implements AbrComponentAPI {
316319
// compute time to load next fragment at lower level
317320
// 8 = bits per byte (bps/Bps)
318321
const levelNextBitrate = levels[nextLoadLevel].maxBitrate;
322+
const requiresLevelLoad = !levels[nextLoadLevel].details || live;
319323
fragLevelNextLoadedDelay = this.getTimeToLoadFrag(
320-
ttfbEstimate / 1000,
324+
ttfbSeconds,
321325
bwe,
322326
duration * levelNextBitrate,
323-
!levels[nextLoadLevel].details,
327+
requiresLevelLoad,
324328
);
325-
if (fragLevelNextLoadedDelay < bufferStarvationDelay) {
329+
if (
330+
fragLevelNextLoadedDelay <
331+
Math.min(bufferStarvationDelay, duration + ttfbSeconds)
332+
) {
326333
break;
327334
}
328335
}
@@ -336,7 +343,6 @@ class AbrController extends Logger implements AbrComponentAPI {
336343
if (fragLevelNextLoadedDelay > duration * 10) {
337344
return;
338345
}
339-
hls.nextLoadLevel = hls.nextAutoLevel = nextLoadLevel;
340346
if (loadedFirstByte) {
341347
// If there has been loading progress, sample bandwidth using loading time offset by minimum TTFB time
342348
this.bwEstimator.sample(
@@ -348,17 +354,26 @@ class AbrController extends Logger implements AbrComponentAPI {
348354
this.bwEstimator.sampleTTFB(timeLoading);
349355
}
350356
const nextLoadLevelBitrate = levels[nextLoadLevel].maxBitrate;
351-
if (
352-
this.getBwEstimate() * this.hls.config.abrBandWidthUpFactor >
353-
nextLoadLevelBitrate
354-
) {
357+
if (this.getBwEstimate() * abrBandWidthUpFactor > nextLoadLevelBitrate) {
355358
this.resetEstimator(nextLoadLevelBitrate);
356359
}
360+
const bestSwitchLevel = this.findBestLevel(
361+
nextLoadLevelBitrate,
362+
minAutoLevel,
363+
nextLoadLevel,
364+
0,
365+
bufferStarvationDelay,
366+
1,
367+
1,
368+
);
369+
if (bestSwitchLevel > -1) {
370+
nextLoadLevel = bestSwitchLevel;
371+
}
357372

358-
this.clearTimer();
359373
this.warn(`Fragment ${frag.sn}${
360374
part ? ' part ' + part.index : ''
361375
} of level ${frag.level} is loading too slowly;
376+
Fragment duration: ${frag.duration.toFixed(3)}
362377
Time to underbuffer: ${bufferStarvationDelay.toFixed(3)} s
363378
Estimated load time for current fragment: ${fragLoadedDelay.toFixed(3)} s
364379
Estimated load time for down switch fragment: ${fragLevelNextLoadedDelay.toFixed(
@@ -370,6 +385,44 @@ class AbrController extends Logger implements AbrComponentAPI {
370385
} bps
371386
New BW estimate: ${this.getBwEstimate() | 0} bps
372387
Switching to level ${nextLoadLevel} @ ${nextLoadLevelBitrate | 0} bps`);
388+
389+
hls.nextLoadLevel = hls.nextAutoLevel = nextLoadLevel;
390+
391+
this.clearTimer();
392+
this.timer = self.setInterval(() => {
393+
// Are nextLoadLevel details available or is stream-controller still in "WAITING_LEVEL" state?
394+
this.clearTimer();
395+
if (
396+
this.fragCurrent === frag &&
397+
this.hls.loadLevel === nextLoadLevel &&
398+
nextLoadLevel > 0
399+
) {
400+
const bufferStarvationDelay = this.getStarvationDelay();
401+
this
402+
.warn(`Aborting inflight request ${nextLoadLevel > 0 ? 'and switching down' : ''}
403+
Fragment duration: ${frag.duration.toFixed(3)} s
404+
Time to underbuffer: ${bufferStarvationDelay.toFixed(3)} s`);
405+
frag.abortRequests();
406+
this.fragCurrent = this.partCurrent = null;
407+
if (nextLoadLevel > minAutoLevel) {
408+
let lowestSwitchLevel = this.findBestLevel(
409+
this.hls.levels[minAutoLevel].bitrate,
410+
minAutoLevel,
411+
nextLoadLevel,
412+
0,
413+
bufferStarvationDelay,
414+
1,
415+
1,
416+
);
417+
if (lowestSwitchLevel === -1) {
418+
lowestSwitchLevel = minAutoLevel;
419+
}
420+
this.hls.nextLoadLevel = this.hls.nextAutoLevel = lowestSwitchLevel;
421+
this.resetEstimator(this.hls.levels[lowestSwitchLevel].bitrate);
422+
}
423+
}
424+
}, fragLevelNextLoadedDelay * 1000);
425+
373426
hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, { frag, part, stats });
374427
};
375428

@@ -665,7 +718,7 @@ class AbrController extends Logger implements AbrComponentAPI {
665718
return 0;
666719
}
667720
const level: Level | undefined = levels[selectionBaseLevel];
668-
const live = !!level?.details?.live;
721+
const live = !!this.hls.latestLevelDetails?.live;
669722
const firstSelection = loadLevel === -1 || lastLoadedFragLevel === -1;
670723
let currentCodecSet: string | undefined;
671724
let currentVideoRange: VideoRange | undefined = 'SDR';

src/controller/level-controller.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -439,11 +439,12 @@ export default class LevelController extends BasePlaylistController {
439439

440440
if (
441441
lastLevelIndex === newLevel &&
442-
level.details &&
443442
lastLevel &&
444443
lastPathwayId === pathwayId
445444
) {
446-
return;
445+
if (level.details || this.requestScheduled !== -1) {
446+
return;
447+
}
447448
}
448449

449450
this.log(
@@ -571,7 +572,10 @@ export default class LevelController extends BasePlaylistController {
571572
data.context.type === PlaylistContextType.LEVEL &&
572573
data.context.level === this.level
573574
) {
574-
this.checkRetry(data);
575+
const retry = this.checkRetry(data);
576+
if (!retry) {
577+
this.requestScheduled = -1;
578+
}
575579
}
576580
}
577581

0 commit comments

Comments
 (0)