Skip to content

Commit

Permalink
Merge pull request #4806 from video-dev/bugfix/wait-for-active-fragme…
Browse files Browse the repository at this point in the history
…nt-before-gap-seek

Make gap controller wait for active fragments while seeking
  • Loading branch information
robwalch authored Jul 26, 2022
2 parents 8484d80 + 300cce6 commit 3010022
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 3 deletions.
5 changes: 3 additions & 2 deletions src/controller/gap-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Events } from '../events';
import { logger } from '../utils/logger';
import type Hls from '../hls';
import type { HlsConfig } from '../config';
import type { Fragment } from '../loader/fragment';
import type { FragmentTracker } from './fragment-tracker';
import { Fragment } from '../loader/fragment';

export const STALL_MINIMUM_DURATION_MS = 250;
export const MAX_START_GAP_JUMP = 2.0;
Expand Down Expand Up @@ -43,7 +43,7 @@ export default class GapController {
*
* @param {number} lastCurrentTime Previously read playhead position
*/
public poll(lastCurrentTime: number) {
public poll(lastCurrentTime: number, activeFrag: Fragment | null) {
const { config, media, stalled } = this;
if (media === null) {
return;
Expand Down Expand Up @@ -104,6 +104,7 @@ export default class GapController {
// Next buffered range is too far ahead to jump to while still seeking
const noBufferGap =
!nextStart ||
(activeFrag && activeFrag.start <= currentTime) ||
(nextStart - currentTime > MAX_START_GAP_JUMP &&
!this.fragmentTracker.getPartialFragment(currentTime));
if (hasEnoughBuffer || noBufferGap) {
Expand Down
3 changes: 2 additions & 1 deletion src/controller/stream-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,8 @@ export default class StreamController
this.seekToStartPos();
} else {
// Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
gapController.poll(this.lastCurrentTime);
const activeFrag = this.state !== State.IDLE ? this.fragCurrent : null;
gapController.poll(this.lastCurrentTime, activeFrag);
}

this.lastCurrentTime = media.currentTime;
Expand Down
24 changes: 24 additions & 0 deletions tests/unit/controller/gap-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,30 @@ describe('GapController', function () {
wallClock.tick(2 * STALL_HANDLING_RETRY_PERIOD_MS);
});

it('should not detect stalls when loading an earlier fragment while seeking', function () {
wallClock.tick(2 * STALL_HANDLING_RETRY_PERIOD_MS);
mockMedia.currentTime += 0.1;
gapController.poll(0);
expect(gapController.stalled).to.equal(null, 'buffered start');

wallClock.tick(2 * STALL_HANDLING_RETRY_PERIOD_MS);
mockMedia.currentTime += 5;
mockMedia.seeking = true;
mockTimeRangesData.length = 1;
mockTimeRangesData[0] = [5.5, 10];
gapController.poll(mockMedia.currentTime - 5);
expect(gapController.stalled).to.equal(null, 'new seek position');

wallClock.tick(2 * STALL_HANDLING_RETRY_PERIOD_MS);
gapController.poll(mockMedia.currentTime, {
start: 5,
});
expect(gapController.stalled).to.equal(
null,
'seeking while loading fragment'
);
});

it('should trigger reportStall when stalling for 250ms or longer', function () {
setStalling();
wallClock.tick(250);
Expand Down

0 comments on commit 3010022

Please sign in to comment.