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(experimentalBufferBasedABR): call selectPlaylist and change media on an interval #978

Merged
merged 8 commits into from
Oct 30, 2020

Conversation

gkatsev
Copy link
Member

@gkatsev gkatsev commented Oct 23, 2020

This is a followup to #886. There, I noticed that if we get sustained bad network we will time out because we will an opportunity for earlyabort. At first, I tried working around by delaying earlyabort in that case (#966) but as I was getting that ready I kept finding edge cases that we needed to account for. Specifically, the issue is that we only run our ABR algorithm on progress and bandwidthchange events from the segment loader. This means that if the download stalls, we stop run our algorithm and will eventually stall playback. Instead, we should remove earlyabort altogether (f897dde) and set up an interval (currently 250ms) to re-run our ABR algorithm. This means that if the network becomes bad for a sustained period, we will drop down once the buffer allows us but if the network recovers, we will switch up appropriately as well.

Also, the interval is stopped while we're paused.

Fixes #964.

@gkatsev gkatsev changed the title fix: call selectPlaylist and change media on an interval fix(experimentalBufferBasedABR): call selectPlaylist and change media on an interval Oct 28, 2020
Copy link
Contributor

@brandonocasey brandonocasey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments for now, still thinking about this. I think we might need to start the abr timer in a few other places just to be safe. Going to do some testing.

src/master-playlist-controller.js Show resolved Hide resolved
@@ -258,6 +259,12 @@ export class MasterPlaylistController extends videojs.EventTarget {

this.setupSegmentLoaderListeners_();

if (this.experimentalBufferBasedABR) {
this.startABRTimer_();
this.tech_.on('pause', () => this.stopABRTimer_());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should handle seeks that cause a pause differently here. Should they be ignored so that we can switch playlists during a seek? Maybe that already happens?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was thinking about this a bit more and perhaps we just want to call this.checkABR_ on seeking so that we can change playlist during a seek rather than afterwards?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're just seeking by clicking, the pause is so small as to not matter. If we're scrubbing, we'll get a whole bunch of seeking events, so, we'd want to debounce it or something.
Maybe stopABRTimer_ should check whether scrubbing is set? So, we only stop it if we're paused for not scrubbing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the stop handler to check tech.scrubbing() if it exists, which is being added here videojs/video.js#6920

});

this.mainSegmentLoader_.on('progress', () => {
if (this.experimentalBufferBasedABR) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove this if statement and everything inside of it now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, this is unnecessary now.

@@ -1066,6 +1091,7 @@ export class MasterPlaylistController extends videojs.EventTarget {
*/
pauseLoading() {
this.delegateLoaders_('all', ['abort', 'pause']);
this.stopABRTimer_();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we start the abr timer in load()?

@@ -258,6 +259,12 @@ export class MasterPlaylistController extends videojs.EventTarget {

this.setupSegmentLoaderListeners_();

if (this.experimentalBufferBasedABR) {
this.startABRTimer_();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be starting the timer before the first play?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably need to update this based on the preload setting. If preload is none or metedata (i.e. when we shouldn't have downloaded any segments) there's no point to it but with preload auto, I think we should.

I'll add updating this as part of the story to fix preload=none

@brandonocasey
Copy link
Contributor

After some testing I think that we start/stop the timers in a fine places. Everything seems to work very smoothly now too. This solution seems way better than earlyabort, and it will keep all of the logic in masterPlaylistController where it belongs 👍

@gkatsev gkatsev merged commit 200c87b into main Oct 30, 2020
@gkatsev gkatsev deleted the use-a-timer branch October 30, 2020 22:58
@@ -5534,53 +5534,6 @@ QUnit.module('MasterPlaylistController experimentalBufferBasedABR', {
}
});

QUnit.test('Determines if playlist should be aborted on earlyabort', function(assert) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this test be left in just when experimentalBufferBasedABR is false?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, though, this test was added as part of #886.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, looks like we do have a similar test that already

QUnit.test('blacklists playlist on earlyabort', function(assert) {
this.masterPlaylistController.mediaSource.trigger('sourceopen');
// master
this.standardXHRResponse(this.requests.shift());
// media
this.standardXHRResponse(this.requests.shift());
const mediaChanges = [];
const playlistLoader = this.masterPlaylistController.masterPlaylistLoader_;
const currentMedia = playlistLoader.media();
const origMedia = playlistLoader.media.bind(playlistLoader);
const origWarn = videojs.log.warn;
const warnings = [];
this.masterPlaylistController.masterPlaylistLoader_.media = (media) => {
if (media) {
mediaChanges.push(media);
}
return origMedia(media);
};
videojs.log.warn = (text) => warnings.push(text);
assert.notOk(currentMedia.excludeUntil > 0, 'playlist not blacklisted');
assert.equal(mediaChanges.length, 0, 'no media change');
this.masterPlaylistController.mainSegmentLoader_.trigger('earlyabort');
assert.ok(currentMedia.excludeUntil > 0, 'playlist blacklisted');
assert.equal(mediaChanges.length, 1, 'one media change');
assert.equal(warnings.length, 1, 'one warning logged');
assert.equal(
warnings[0],
`Problem encountered with playlist ${currentMedia.id}. ` +
'Aborted early because there isn\'t enough bandwidth to complete the ' +
`request without rebuffering. Switching to playlist ${mediaChanges[0].id}.`,
'warning message is correct'
);
videojs.log.warn = origWarn;
});
. This test is a variation of that that accounts for shouldSwitchToMedia variations with experimentalBufferBasedABR

gkatsev added a commit that referenced this pull request Nov 2, 2020
Updates based on late comments from #978.
@gkatsev gkatsev added this to the ABR milestone Nov 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

experimentalBufferBasedABR (and maybe regular ABR too): fixup ABR when no progress events from XHR
3 participants