Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: reBufferingGoal is not respected #6433

Merged
merged 9 commits into from May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/media/stall_detector.js
Expand Up @@ -180,6 +180,12 @@ shaka.media.StallDetector.MediaElementImplementation = class {
if (this.mediaElement_.buffered.length == 0) {
return false;
}
// the playback rate is at 0, the player is not stalled but simply pausing
// it's buffering due to not enough buffer to respect reBufferingGoal
// or minbufferTime
if (this.mediaElement_.playbackRate == 0) {
return false;
}

return shaka.media.StallDetector.MediaElementImplementation.hasContentFor_(
this.mediaElement_.buffered,
Expand Down
2 changes: 2 additions & 0 deletions lib/player.js
Expand Up @@ -5717,6 +5717,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
const loaded = this.stats_ && this.bufferObserver_ && this.playhead_;

if (loaded) {
const defaultRate = this.playRateController_.getDefaultRate() || 1.0;
this.playRateController_.set(isBuffering? 0: defaultRate);
Iragne marked this conversation as resolved.
Show resolved Hide resolved
if (this.cmcdManager_) {
this.cmcdManager_.setBuffering(isBuffering);
}
Expand Down
89 changes: 81 additions & 8 deletions test/player_integration.js
Expand Up @@ -948,6 +948,79 @@ describe('Player', () => {
});
});

describe('rebufferGoal', () => {
const startBuffering = jasmine.objectContaining({buffering: true});
const endBuffering = jasmine.objectContaining({buffering: false});
/** @type {!jasmine.Spy} */
const onBuffering = jasmine.createSpy('onBuffering');
/** @type {!jasmine.Spy} */
const onSeeking = jasmine.createSpy('onSeeking');
/** @type {!jasmine.Spy} */
const onPlaying = jasmine.createSpy('onPlaying');
/** @type {!jasmine.Spy} */
const onTimeUpdate = jasmine.createSpy('onTimeUpdate');
/** @type {!shaka.test.Waiter} */
let waiter;

beforeEach(() => {
player.addEventListener('buffering', Util.spyFunc(onBuffering));
eventManager.listen(video, 'seeking', Util.spyFunc(onSeeking));
eventManager.listen(video, 'timeupdate', Util.spyFunc(onTimeUpdate));
eventManager.listen(video, 'playing', Util.spyFunc(onPlaying));
video.autoplay = false;
waiter = new shaka.test.Waiter(eventManager)
.setPlayer(player)
.timeoutAfter(10)
.failOnTimeout(true);
});

it('state orchestration and buffer length', async () => {
// the expected player behaviours should be the following one
// buffering event start
// playing event but player is not playing
// because of the rebufferingGoal > to the buffer )
// buffering event stop
// timeupdate

// create a delay in the request to check the event's order
const netEngine = player.getNetworkingEngine();
netEngine.registerResponseFilter(
async (type, response, context) => {
await shaka.test.Util.delay(2);
});

player.configure('streaming.rebufferingGoal', 25);
player.configure('streaming.bufferingGoal', 60);
player.configure('streaming.stallEnabled', true);
player.configure('streaming.stallThreshold', 0.1);
player.configure('manifest.dash.ignoreMinBufferTime', true);
video.autoplay = true;
await player.load('test:sintel_long_compiled');

expect(onBuffering).toHaveBeenCalledTimes(1);
expect(onBuffering).toHaveBeenCalledWith(startBuffering);
expect(onSeeking).not.toHaveBeenCalled();
expect(onTimeUpdate).not.toHaveBeenCalled();
expect(getBufferedAhead()).toBeLessThanOrEqual(25);
onBuffering.calls.reset();

await waiter.waitForEvent(video, 'playing');
expect(getBufferedAhead()).toBeLessThanOrEqual(25);
expect(video.currentTime).toBe(0);

await waiter.waitForEvent(player, 'buffering');
await shaka.test.Util.delay(1);
expect(onSeeking).not.toHaveBeenCalled();
expect(onTimeUpdate).toHaveBeenCalled();
expect(onBuffering).toHaveBeenCalledTimes(1);
expect(onBuffering).toHaveBeenCalledWith(endBuffering);
await waiter.waitForEvent(video, 'timeupdate');
// segment duration are 10s meaning
// that the expected buffer should be 30..
expect(getBufferedAhead()).toBeGreaterThanOrEqual(25);
});
});

describe('buffering', () => {
const startBuffering = jasmine.objectContaining({buffering: true});
const endBuffering = jasmine.objectContaining({buffering: false});
Expand Down Expand Up @@ -1066,14 +1139,6 @@ describe('Player', () => {
expect(getBufferedBehind()).toBeLessThanOrEqual(10);
});

function getBufferedAhead() {
const end = shaka.media.TimeRangesUtils.bufferEnd(video.buffered);
if (end == null) {
return 0;
}
return end - video.currentTime;
}

function getBufferedBehind() {
const start = shaka.media.TimeRangesUtils.bufferStart(video.buffered);
if (start == null) {
Expand All @@ -1083,6 +1148,14 @@ describe('Player', () => {
}
}); // describe('buffering')

function getBufferedAhead() {
const end = shaka.media.TimeRangesUtils.bufferEnd(video.buffered);
if (end == null) {
return 0;
}
return end - video.currentTime;
}

describe('configuration', () => {
it('has the correct number of arguments in compiled callbacks', () => {
// Get the default configuration for both the compiled & uncompiled
Expand Down
1 change: 1 addition & 0 deletions test/player_unit.js
Expand Up @@ -2710,6 +2710,7 @@ describe('Player', () => {
});

it('tracks info about current stream', () => {
forceBufferingTo(false);
let stats = player.getStats();
// Should have chosen the first of each type of stream.
expect(stats.width).toBe(100);
Expand Down