Skip to content

Commit

Permalink
fix: stutter after fast quality change in IE/Edge (#213)
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-barstow authored and forbesjo committed Sep 21, 2018
1 parent 28dead6 commit 2c0d9b2
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 7 deletions.
20 changes: 13 additions & 7 deletions src/master-playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -530,14 +530,20 @@ export class MasterPlaylistController extends videojs.EventTarget {

this.masterPlaylistLoader_.media(media);

// delete all buffered data to allow an immediate quality switch, then seek
// in place to give the browser a kick to remove any cached frames from the
// previous rendition
// Delete all buffered data to allow an immediate quality switch, then seek to give
// the browser a kick to remove any cached frames from the previous rendtion (.04 seconds
// ahead is roughly the minimum that will accomplish this across a variety of content
// in IE and Edge, but seeking in place is sufficient on all other browsers)
// Edge/IE bug: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14600375/
// Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=651904
this.mainSegmentLoader_.resetEverything(() => {
// Since this is not a typical seek, we avoid the seekTo method which can cause
// segments from the previously enabled rendition to load before the new playlist
// has finished loading
this.tech_.setCurrentTime(this.tech_.currentTime());
// Since this is not a typical seek, we avoid the seekTo method which can cause segments
// from the previously enabled rendition to load before the new playlist has finished loading
if (videojs.browser.IE_VERSION || videojs.browser.IS_EDGE) {
this.tech_.setCurrentTime(this.tech_.currentTime() + 0.04);
} else {
this.tech_.setCurrentTime(this.tech_.currentTime());
}
});

// don't need to reset audio as it is reset when media changes
Expand Down
115 changes: 115 additions & 0 deletions test/master-playlist-controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,121 @@ QUnit.test('resets everything for a fast quality change', function(assert) {
assert.deepEqual(removeFuncArgs, {start: 0, end: 60}, 'remove() called with correct arguments if media is changed');
});

QUnit.test('seeks in place for fast quality switch on non-IE/Edge browsers', function(assert) {
let seeks = 0;

this.masterPlaylistController.mediaSource.trigger('sourceopen');
// master
this.standardXHRResponse(this.requests.shift());
// media
this.standardXHRResponse(this.requests.shift());
// segment
this.standardXHRResponse(this.requests.shift());
// trigger updateend to indicate the end of the append operation
this.masterPlaylistController.mediaSource.sourceBuffers[0].trigger('updateend');

// media is changed
this.masterPlaylistController.selectPlaylist = () => {
return this.masterPlaylistController.master().playlists[0];
};

this.player.tech_.on('seeking', function() {
seeks++;
});

const timeBeforeSwitch = this.player.currentTime();

this.masterPlaylistController.fastQualityChange_();
// trigger updateend to indicate the end of the remove operation
this.masterPlaylistController.mediaSource.sourceBuffers[0].trigger('updateend');
this.clock.tick(1);

assert.equal(this.player.currentTime(), timeBeforeSwitch, 'current time remains the same on fast quality switch');
assert.equal(seeks, 1, 'seek event occurs on fast quality switch');
});

QUnit.test('seeks forward 0.04 sec for fast quality switch on Edge', function(assert) {
let oldIEVersion = videojs.browser.IE_VERSION;
let oldIsEdge = videojs.browser.IS_EDGE;
let seeks = 0;

this.masterPlaylistController.mediaSource.trigger('sourceopen');
// master
this.standardXHRResponse(this.requests.shift());
// media
this.standardXHRResponse(this.requests.shift());
// segment
this.standardXHRResponse(this.requests.shift());
// trigger updateend to indicate the end of the append operation
this.masterPlaylistController.mediaSource.sourceBuffers[0].trigger('updateend');

// media is changed
this.masterPlaylistController.selectPlaylist = () => {
return this.masterPlaylistController.master().playlists[0];
};

this.player.tech_.on('seeking', function() {
seeks++;
});

const timeBeforeSwitch = this.player.currentTime();

videojs.browser.IE_VERSION = null;
videojs.browser.IS_EDGE = true;

this.masterPlaylistController.fastQualityChange_();
// trigger updateend to indicate the end of the remove operation
this.masterPlaylistController.mediaSource.sourceBuffers[0].trigger('updateend');
this.clock.tick(1);

assert.equal(this.player.currentTime(), timeBeforeSwitch + 0.04, 'seeks forward on fast quality switch');
assert.equal(seeks, 1, 'seek event occurs on fast quality switch');

videojs.browser.IE_VERSION = oldIEVersion;
videojs.browser.IS_EDGE = oldIsEdge;
});

QUnit.test('seeks forward 0.04 sec for fast quality switch on IE', function(assert) {
let oldIEVersion = videojs.browser.IE_VERSION;
let oldIsEdge = videojs.browser.IS_EDGE;
let seeks = 0;

this.masterPlaylistController.mediaSource.trigger('sourceopen');
// master
this.standardXHRResponse(this.requests.shift());
// media
this.standardXHRResponse(this.requests.shift());
// segment
this.standardXHRResponse(this.requests.shift());
// trigger updateend to indicate the end of the append operation
this.masterPlaylistController.mediaSource.sourceBuffers[0].trigger('updateend');

// media is changed
this.masterPlaylistController.selectPlaylist = () => {
return this.masterPlaylistController.master().playlists[0];
};

this.player.tech_.on('seeking', function() {
seeks++;
});

const timeBeforeSwitch = this.player.currentTime();

videojs.browser.IE_VERSION = 11;
videojs.browser.IS_EDGE = false;

this.masterPlaylistController.fastQualityChange_();
// trigger updateend to indicate the end of the remove operation
this.masterPlaylistController.mediaSource.sourceBuffers[0].trigger('updateend');
this.clock.tick(1);

assert.equal(this.player.currentTime(), timeBeforeSwitch + 0.04, 'seeks forward on fast quality switch');
assert.equal(seeks, 1, 'seek event occurs on fast quality switch');

videojs.browser.IE_VERSION = oldIEVersion;
videojs.browser.IS_EDGE = oldIsEdge;
});

QUnit.test('audio segment loader is reset on audio track change', function(assert) {
this.requests.length = 0;
this.player = createPlayer();
Expand Down

0 comments on commit 2c0d9b2

Please sign in to comment.