Skip to content

Commit 3010022

Browse files
authored
Merge pull request #4806 from video-dev/bugfix/wait-for-active-fragment-before-gap-seek
Make gap controller wait for active fragments while seeking
2 parents 8484d80 + 300cce6 commit 3010022

File tree

3 files changed

+29
-3
lines changed

3 files changed

+29
-3
lines changed

src/controller/gap-controller.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { Events } from '../events';
55
import { logger } from '../utils/logger';
66
import type Hls from '../hls';
77
import type { HlsConfig } from '../config';
8+
import type { Fragment } from '../loader/fragment';
89
import type { FragmentTracker } from './fragment-tracker';
9-
import { Fragment } from '../loader/fragment';
1010

1111
export const STALL_MINIMUM_DURATION_MS = 250;
1212
export const MAX_START_GAP_JUMP = 2.0;
@@ -43,7 +43,7 @@ export default class GapController {
4343
*
4444
* @param {number} lastCurrentTime Previously read playhead position
4545
*/
46-
public poll(lastCurrentTime: number) {
46+
public poll(lastCurrentTime: number, activeFrag: Fragment | null) {
4747
const { config, media, stalled } = this;
4848
if (media === null) {
4949
return;
@@ -104,6 +104,7 @@ export default class GapController {
104104
// Next buffered range is too far ahead to jump to while still seeking
105105
const noBufferGap =
106106
!nextStart ||
107+
(activeFrag && activeFrag.start <= currentTime) ||
107108
(nextStart - currentTime > MAX_START_GAP_JUMP &&
108109
!this.fragmentTracker.getPartialFragment(currentTime));
109110
if (hasEnoughBuffer || noBufferGap) {

src/controller/stream-controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,8 @@ export default class StreamController
925925
this.seekToStartPos();
926926
} else {
927927
// Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers
928-
gapController.poll(this.lastCurrentTime);
928+
const activeFrag = this.state !== State.IDLE ? this.fragCurrent : null;
929+
gapController.poll(this.lastCurrentTime, activeFrag);
929930
}
930931

931932
this.lastCurrentTime = media.currentTime;

tests/unit/controller/gap-controller.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,30 @@ describe('GapController', function () {
269269
wallClock.tick(2 * STALL_HANDLING_RETRY_PERIOD_MS);
270270
});
271271

272+
it('should not detect stalls when loading an earlier fragment while seeking', function () {
273+
wallClock.tick(2 * STALL_HANDLING_RETRY_PERIOD_MS);
274+
mockMedia.currentTime += 0.1;
275+
gapController.poll(0);
276+
expect(gapController.stalled).to.equal(null, 'buffered start');
277+
278+
wallClock.tick(2 * STALL_HANDLING_RETRY_PERIOD_MS);
279+
mockMedia.currentTime += 5;
280+
mockMedia.seeking = true;
281+
mockTimeRangesData.length = 1;
282+
mockTimeRangesData[0] = [5.5, 10];
283+
gapController.poll(mockMedia.currentTime - 5);
284+
expect(gapController.stalled).to.equal(null, 'new seek position');
285+
286+
wallClock.tick(2 * STALL_HANDLING_RETRY_PERIOD_MS);
287+
gapController.poll(mockMedia.currentTime, {
288+
start: 5,
289+
});
290+
expect(gapController.stalled).to.equal(
291+
null,
292+
'seeking while loading fragment'
293+
);
294+
});
295+
272296
it('should trigger reportStall when stalling for 250ms or longer', function () {
273297
setStalling();
274298
wallClock.tick(250);

0 commit comments

Comments
 (0)