Skip to content

Commit

Permalink
fix: fastQualitySwitch stability (#1525)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrums86 authored Jul 17, 2024
1 parent 0b4da7c commit 28cb9fd
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 62 deletions.
9 changes: 2 additions & 7 deletions src/playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -1059,15 +1059,10 @@ export class PlaylistController extends videojs.EventTarget {

runFastQualitySwitch_() {
this.waitingForFastQualityPlaylistReceived_ = false;
// 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 was 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
// Delete all buffered data to allow an immediate quality switch.
this.mainSegmentLoader_.pause();
this.mainSegmentLoader_.resetEverything(() => {
this.tech_.setCurrentTime(this.tech_.currentTime());
this.mainSegmentLoader_.load();
});

// don't need to reset audio as it is reset when media changes
Expand Down
70 changes: 15 additions & 55 deletions test/playlist-controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -688,10 +688,11 @@ QUnit.test(
}
);

QUnit.test('resets everything for a fast quality change', function(assert) {
QUnit.test('resets everything for a fast quality change then calls load', function(assert) {
let resyncs = 0;
let resets = 0;
let removeFuncArgs = {};
const done = assert.async();

this.playlistController.mediaSource.trigger('sourceopen');
// main
Expand All @@ -709,16 +710,24 @@ QUnit.test('resets everything for a fast quality change', function(assert) {

const origResetEverything = segmentLoader.resetEverything;
const origRemove = segmentLoader.remove;
const origLoad = segmentLoader.load;

segmentLoader.resetEverything = () => {
// ensure load is called
segmentLoader.load = () => {
done();
origLoad.call(segmentLoader);
};

segmentLoader.resetEverything = (doneFn) => {
resets++;
origResetEverything.call(segmentLoader);
origResetEverything.call(segmentLoader, doneFn);
};

segmentLoader.remove = (start, end) => {
segmentLoader.remove = (start, end, doneFn) => {
assert.equal(end, Infinity, 'on a remove all, end should be Infinity');

origRemove.call(segmentLoader, start, end);
assert.ok(doneFn);
doneFn();
origRemove.call(segmentLoader, start, end, doneFn);
};

segmentLoader.startingMediaInfo_ = { hasVideo: true };
Expand Down Expand Up @@ -777,55 +786,6 @@ QUnit.test('loadVttJs should be passed to the vttSegmentLoader and rejected on v
});
});

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

this.playlistController.mediaSource.trigger('sourceopen');
// main
this.standardXHRResponse(this.requests.shift());
// media
this.standardXHRResponse(this.requests.shift());

const segmentLoader = this.playlistController.mainSegmentLoader_;

return requestAndAppendSegment({
request: this.requests.shift(),
segmentLoader,
clock: this.clock
}).then(() => {
// media is changed
this.playlistController.selectPlaylist = () => {
const playlists = this.playlistController.main().playlists;
const currentPlaylist = this.playlistController.media();

return playlists.find((playlist) => playlist !== currentPlaylist);
};

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

const timeBeforeSwitch = this.player.currentTime();

// mock buffered values so removes are processed
segmentLoader.sourceUpdater_.audioBuffer.buffered = createTimeRanges([[0, 10]]);
segmentLoader.sourceUpdater_.videoBuffer.buffered = createTimeRanges([[0, 10]]);

this.playlistController.runFastQualitySwitch_();
// trigger updateend to indicate the end of the remove operation
segmentLoader.sourceUpdater_.audioBuffer.trigger('updateend');
segmentLoader.sourceUpdater_.videoBuffer.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('basic timeToLoadedData, mediaAppends, appendsToLoadedData stats', function(assert) {
this.player.tech_.trigger('loadstart');
this.playlistController.mediaSource.trigger('sourceopen');
Expand Down

0 comments on commit 28cb9fd

Please sign in to comment.