-
Notifications
You must be signed in to change notification settings - Fork 428
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
Conversation
There was a problem hiding this 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.
@@ -258,6 +259,12 @@ export class MasterPlaylistController extends videojs.EventTarget { | |||
|
|||
this.setupSegmentLoaderListeners_(); | |||
|
|||
if (this.experimentalBufferBasedABR) { | |||
this.startABRTimer_(); | |||
this.tech_.on('pause', () => this.stopABRTimer_()); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
src/master-playlist-controller.js
Outdated
}); | ||
|
||
this.mainSegmentLoader_.on('progress', () => { | ||
if (this.experimentalBufferBasedABR) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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_(); |
There was a problem hiding this comment.
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_(); |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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
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 👍 |
The getter will be added via videojs/video.js#6920
@@ -5534,53 +5534,6 @@ QUnit.module('MasterPlaylistController experimentalBufferBasedABR', { | |||
} | |||
}); | |||
|
|||
QUnit.test('Determines if playlist should be aborted on earlyabort', function(assert) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
http-streaming/test/master-playlist-controller.test.js
Lines 1847 to 1887 in ca7655e
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; | |
}); |
shouldSwitchToMedia
variations with experimentalBufferBasedABR
Updates based on late comments from #978.
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
andbandwidthchange
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.