Skip to content

Commit

Permalink
Update gap controller to observe buffer gaps regardless of max buffer…
Browse files Browse the repository at this point in the history
… hole
  • Loading branch information
Rob Walch committed Dec 3, 2019
1 parent aea0d99 commit a4732c3
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 17 deletions.
25 changes: 14 additions & 11 deletions src/controller/gap-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default class GapController {
* @param {number} lastCurrentTime Previously read playhead position
*/
public poll (lastCurrentTime: number) {
const { media, stalled } = this;
const { config, media, stalled } = this;
const tnow = self.performance.now();
const { currentTime, seeking } = media;
const seeked = this.seeking && !seeking;
Expand Down Expand Up @@ -111,7 +111,8 @@ export default class GapController {
this._reportStall(bufferInfo.len);
}

this._tryFixBufferStall(bufferInfo, stalledDuration);
const bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);
this._tryFixBufferStall(bufferInfo, bufferedWithHoles, stalledDuration);
}

/**
Expand All @@ -120,7 +121,7 @@ export default class GapController {
* @param stalledDurationMs - The amount of time Hls.js has been stalling for.
* @private
*/
private _tryFixBufferStall (bufferInfo: BufferInfo, stalledDurationMs: number) {
private _tryFixBufferStall (bufferInfo: BufferInfo, bufferedWithHoles: BufferInfo, stalledDurationMs: number) {
const { config, fragmentTracker, media } = this;
const currentTime = media.currentTime;

Expand All @@ -140,14 +141,16 @@ export default class GapController {
// we may just have to "nudge" the playlist as the browser decoding/rendering engine
// needs to cross some sort of threshold covering all source-buffers content
// to start playing properly.
if (bufferInfo.len <= config.maxBufferHole &&
stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
logger.warn('Trying to nudge playhead over buffer-hole');
// Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
// We only try to jump the hole if it's under the configured size
// Reset stalled so to rearm watchdog timer
this.stalled = null;
this._tryNudgeBuffer();
if (stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
if (bufferedWithHoles.len > config.maxBufferHole ||
bufferInfo.len === 0 && bufferInfo.nextStart && bufferInfo.nextStart < config.maxBufferHole) {
logger.warn('Trying to nudge playhead over buffer-hole');
// Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
// We only try to jump the hole if it's under the configured size
// Reset stalled so to rearm watchdog timer
this.stalled = null;
this._tryNudgeBuffer();
}
}
}

Expand Down
25 changes: 19 additions & 6 deletions tests/unit/controller/gap-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ describe('checkBuffer', function () {

beforeEach(function () {
const hls = new Hls({});
media = document.createElement('video');
media = {
currentTime: 0
};
config = hls.config;
gapController = new GapController(config, media, new FragmentTracker(hls), hls);
triggerSpy = sinon.spy(hls, 'trigger');
Expand Down Expand Up @@ -70,26 +72,37 @@ describe('checkBuffer', function () {

describe('_tryFixBufferStall', function () {
it('should nudge when stalling close to the buffer end', function () {
const mockBufferInfo = { len: 1 };
const mockBufferInfo = { len: 0.5, nextStart: 1 };
const mockBufferWithHoles = { len: 1 };
const mockStallDuration = (config.highBufferWatchdogPeriod + 1) * 1000;
const nudgeStub = sandbox.stub(gapController, '_tryNudgeBuffer');
gapController._tryFixBufferStall(mockBufferInfo, mockBufferWithHoles, mockStallDuration);
expect(nudgeStub).to.have.been.calledOnce;
});

it('should nudge when in between buffered ranges', function () {
media.currentTime = 4;
const mockBufferInfo = { len: 0, nextStart: 4.08 };
const mockBufferWithHoles = { len: 5 };
const mockStallDuration = (config.highBufferWatchdogPeriod + 1) * 1000;
const nudgeStub = sandbox.stub(gapController, '_tryNudgeBuffer');
gapController._tryFixBufferStall(mockBufferInfo, mockStallDuration);
gapController._tryFixBufferStall(mockBufferInfo, mockBufferWithHoles, mockStallDuration);
expect(nudgeStub).to.have.been.calledOnce;
});

it('should not nudge when briefly stalling close to the buffer end', function () {
const mockBufferInfo = { len: 1 };
const mockStallDuration = (config.highBufferWatchdogPeriod / 2) * 1000;
const nudgeStub = sandbox.stub(gapController, '_tryNudgeBuffer');
gapController._tryFixBufferStall(mockBufferInfo, mockStallDuration);
gapController._tryFixBufferStall(mockBufferInfo, mockBufferInfo, mockStallDuration);
expect(nudgeStub).to.have.not.been.called;
});

it('should not nudge when too far from the buffer end', function () {
it('should not nudge when too close to the buffer end', function () {
const mockBufferInfo = { len: 0.25 };
const mockStallDuration = (config.highBufferWatchdogPeriod + 1) * 1000;
const nudgeStub = sandbox.stub(gapController, '_tryNudgeBuffer');
gapController._tryFixBufferStall(mockBufferInfo, mockStallDuration);
gapController._tryFixBufferStall(mockBufferInfo, mockBufferInfo, mockStallDuration);
expect(nudgeStub).to.have.not.been.called;
});

Expand Down

0 comments on commit a4732c3

Please sign in to comment.