Skip to content

Commit

Permalink
feat: add experimental pixel diff selector behind a flag defaulted off (
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonocasey authored Jul 27, 2021
1 parent b3d1ec0 commit a0c0359
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 57 deletions.
4 changes: 4 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ <h3>Options</h3>
<input id=buffer-water type="checkbox">
[EXPERIMENTAL] Use Buffer Level for ABR (reloads player)
</label>
<label>
<input id=pixel-diff-selector type="checkbox">
[EXPERIMENTAL] Use the Pixel difference resolution selector (reloads player)
</label>
<label>
<input id=override-native type="checkbox" checked>
Override Native (reloads player)
Expand Down
65 changes: 19 additions & 46 deletions scripts/index-demo-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@
'type',
'keysystems',
'buffer-water',
'pixel-diff-selector',
'override-native',
'preload',
'mirror-source'
Expand Down Expand Up @@ -275,58 +276,29 @@
window.player.autoplay(event.target.checked);
});

stateEls['mirror-source'].addEventListener('change', function(event) {
saveState();

// reload the player and scripts
stateEls.minified.dispatchEvent(newEvent('change'));
});

stateEls['sync-workers'].addEventListener('change', function(event) {
saveState();

// reload the player and scripts
stateEls.minified.dispatchEvent(newEvent('change'));
});

stateEls.preload.addEventListener('change', function(event) {
saveState();
// reload the player and scripts
stateEls.minified.dispatchEvent(newEvent('change'));
// stateEls that reload the player and scripts
[
'mirror-source',
'sync-workers',
'preload',
'llhls',
'buffer-water',
'override-native',
'liveui',
'pixel-diff-selector'
].forEach(function(name) {
stateEls[name].addEventListener('change', function(event) {
saveState();

stateEls.minified.dispatchEvent(newEvent('change'));
});
});

stateEls.debug.addEventListener('change', function(event) {
saveState();
window.videojs.log.level(event.target.checked ? 'debug' : 'info');
});

stateEls.llhls.addEventListener('change', function(event) {
saveState();

// reload the player and scripts
stateEls.minified.dispatchEvent(newEvent('change'));
});

stateEls['buffer-water'].addEventListener('change', function(event) {
saveState();

// reload the player and scripts
stateEls.minified.dispatchEvent(newEvent('change'));
});

stateEls['override-native'].addEventListener('change', function(event) {
saveState();

// reload the player and scripts
stateEls.minified.dispatchEvent(newEvent('change'));
});

stateEls.liveui.addEventListener('change', function(event) {
saveState();

stateEls.minified.dispatchEvent(newEvent('change'));
});

stateEls.minified.addEventListener('change', function(event) {
var urls = [
'node_modules/video.js/dist/alt/video.core',
Expand Down Expand Up @@ -377,7 +349,8 @@
vhs: {
overrideNative: getInputValue(stateEls['override-native']),
experimentalBufferBasedABR: getInputValue(stateEls['buffer-water']),
experimentalLLHLS: getInputValue(stateEls.llhls)
experimentalLLHLS: getInputValue(stateEls.llhls),
experimentalLeastPixelDiffSelector: getInputValue(stateEls['pixel-diff-selector'])
}
}
});
Expand Down
4 changes: 3 additions & 1 deletion src/master-playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ export class MasterPlaylistController extends videojs.EventTarget {
enableLowInitialPlaylist,
sourceType,
cacheEncryptionKeys,
experimentalBufferBasedABR
experimentalBufferBasedABR,
experimentalLeastPixelDiffSelector
} = options;

if (!src) {
Expand All @@ -160,6 +161,7 @@ export class MasterPlaylistController extends videojs.EventTarget {
Vhs = externVhs;

this.experimentalBufferBasedABR = Boolean(experimentalBufferBasedABR);
this.experimentalLeastPixelDiffSelector = Boolean(experimentalLeastPixelDiffSelector);
this.withCredentials = withCredentials;
this.tech_ = tech;
this.vhs_ = tech.vhs;
Expand Down
30 changes: 29 additions & 1 deletion src/playlist-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,34 @@ export let simpleSelector = function(
resolutionPlusOneRep = resolutionPlusOneSmallest.filter((rep) => rep.bandwidth === highestRemainingBandwidthRep.bandwidth)[0];
}

let leastPixelDiffRep;

// If this selector proves to be better than others,
// resolutionPlusOneRep and resolutionBestRep and all
// the code involving them should be removed.
if (masterPlaylistController.experimentalLeastPixelDiffSelector) {
// find the variant that is closest to the player's pixel size
const leastPixelDiffList = haveResolution.map((rep) => {
rep.pixelDiff = Math.abs(rep.width - playerWidth) + Math.abs(rep.height - playerHeight);
return rep;
});

// get the highest bandwidth, closest resolution playlist
stableSort(leastPixelDiffList, (left, right) => {
// sort by highest bandwidth if pixelDiff is the same
if (left.pixelDiff === right.pixelDiff) {
return right.bandwidth - left.bandwidth;
}

return left.pixelDiff - right.pixelDiff;
});

leastPixelDiffRep = leastPixelDiffList[0];
}

// fallback chain of variants
const chosenRep = (
leastPixelDiffRep ||
resolutionPlusOneRep ||
resolutionBestRep ||
bandwidthBestRep ||
Expand All @@ -296,7 +322,9 @@ export let simpleSelector = function(
if (chosenRep && chosenRep.playlist) {
let type = 'sortedPlaylistReps';

if (resolutionPlusOneRep) {
if (leastPixelDiffRep) {
type = 'leastPixelDiffRep';
} else if (resolutionPlusOneRep) {
type = 'resolutionPlusOneRep';
} else if (resolutionBestRep) {
type = 'resolutionBestRep';
Expand Down
3 changes: 2 additions & 1 deletion src/videojs-http-streaming.js
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,8 @@ class VhsHandler extends Component {
'initialPlaylistSelector',
'experimentalBufferBasedABR',
'liveRangeSafeTimeDelta',
'experimentalLLHLS'
'experimentalLLHLS',
'experimentalLeastPixelDiffSelector'
].forEach((option) => {
if (typeof this.source_[option] !== 'undefined') {
this.options_[option] = this.source_[option];
Expand Down
25 changes: 20 additions & 5 deletions test/master-playlist-controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,10 @@ QUnit.test('smoothQualityChange_ calls fastQualityChange_', function(assert) {
this.standardXHRResponse(this.requests.shift());

this.masterPlaylistController.selectPlaylist = () => {
return this.masterPlaylistController.master().playlists[0];
const playlists = this.masterPlaylistController.master().playlists;
const currentPlaylist = this.masterPlaylistController.media();

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

this.masterPlaylistController.fastQualityChange_ = () => fastQualityChangeCalls++;
Expand Down Expand Up @@ -600,7 +603,10 @@ QUnit.test('resets everything for a fast quality change', function(assert) {

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

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

this.masterPlaylistController.fastQualityChange_();
Expand Down Expand Up @@ -630,7 +636,10 @@ QUnit.test('seeks in place for fast quality switch on non-IE/Edge browsers', fun
}).then(() => {
// media is changed
this.masterPlaylistController.selectPlaylist = () => {
return this.masterPlaylistController.master().playlists[0];
const playlists = this.masterPlaylistController.master().playlists;
const currentPlaylist = this.masterPlaylistController.media();

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

this.player.tech_.on('seeking', function() {
Expand Down Expand Up @@ -855,7 +864,10 @@ QUnit.test('seeks forward 0.04 sec for fast quality switch on Edge', function(as
}).then(() => {
// media is changed
this.masterPlaylistController.selectPlaylist = () => {
return this.masterPlaylistController.master().playlists[0];
const playlists = this.masterPlaylistController.master().playlists;
const currentPlaylist = this.masterPlaylistController.media();

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

this.player.tech_.on('seeking', function() {
Expand Down Expand Up @@ -909,7 +921,10 @@ QUnit.test('seeks forward 0.04 sec for fast quality switch on IE', function(asse
}).then(() => {
// media is changed
this.masterPlaylistController.selectPlaylist = () => {
return this.masterPlaylistController.master().playlists[0];
const playlists = this.masterPlaylistController.master().playlists;
const currentPlaylist = this.masterPlaylistController.media();

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

this.player.tech_.on('seeking', function() {
Expand Down
53 changes: 51 additions & 2 deletions test/playlist-selectors.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ module('Playlist Selectors', {
master: {
playlists: []
}
}
},
masterPlaylistController_: {}
};
},
afterEach() {
Expand Down Expand Up @@ -241,7 +242,7 @@ test('simpleSelector limits using resolution information when it exists', functi

master.playlists = trickyPlaylists;

const selectedPlaylist = simpleSelector(master, Config.INITIAL_BANDWIDTH, 444, 790, true);
const selectedPlaylist = simpleSelector(master, Config.INITIAL_BANDWIDTH, 444, 790, true, {});

assert.equal(selectedPlaylist, master.playlists[3], 'selected the playlist with the lowest bandwidth higher than player resolution');
});
Expand Down Expand Up @@ -278,3 +279,51 @@ test('simpleSelector chooses between current audio playlists for audio only', fu

assert.equal(selectedPlaylist, audioPlaylists[1], 'selected an audio based solely on bandwidth');
});

test('simpleSelector experimentalLeastPixelDiffSelector selects least pixel diff resolution.', function(assert) {
const bandwidth = Config.INITIAL_BANDWIDTH;
const master = this.vhs.playlists.master;
const usePixelDiff = {experimentalLeastPixelDiffSelector: true};

master.playlists = [
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 768, height: 432 } } },
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 1024, height: 576 } } },
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 1280, height: 720 } } },
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 1920, height: 1080 } } }
];

let pixelDiff;
let nonPixelDiff;

// +1 pixel
pixelDiff = simpleSelector(master, Infinity, 1281, 721, true, usePixelDiff);
nonPixelDiff = simpleSelector(master, Infinity, 1281, 721, true, {});

assert.equal(pixelDiff, master.playlists[2], '1281w x 721h pixel diff');
assert.equal(nonPixelDiff, master.playlists[3], '1281w x 721h resolution plus one');

// -1 pixel
pixelDiff = simpleSelector(master, Infinity, 1279, 719, true, usePixelDiff);
nonPixelDiff = simpleSelector(master, Infinity, 1279, 719, true, {});

assert.equal(pixelDiff, master.playlists[2], '1279w x 719h pixel diff');
assert.equal(nonPixelDiff, master.playlists[2], '1279w x 719h resolution plus one');

// equal to player resolution
pixelDiff = simpleSelector(master, Infinity, 1280, 720, true, usePixelDiff);
nonPixelDiff = simpleSelector(master, Infinity, 1280, 720, true, {});

assert.equal(pixelDiff, master.playlists[2], '1280w x 720h pixel diff');
assert.equal(nonPixelDiff, master.playlists[2], '1280w x 720h resolution plus one');

master.playlists.push({ attributes: { BANDWIDTH: bandwidth - 1, RESOLUTION: { width: 1280, height: 720 } } });
master.playlists.push({ attributes: { BANDWIDTH: bandwidth + 1, RESOLUTION: { width: 1280, height: 720 } } });

// equal to player resolution, chooses higher bandwidth
pixelDiff = simpleSelector(master, Infinity, 1280, 720, true, usePixelDiff);
nonPixelDiff = simpleSelector(master, Infinity, 1280, 720, true, {});

assert.equal(pixelDiff, master.playlists[5], '1280w x 720h pixel diff higher bandwidth');
assert.equal(nonPixelDiff, master.playlists[5], '1280w x 720h resolution plus higher bandwidth');

});
2 changes: 1 addition & 1 deletion test/videojs-http-streaming.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1353,7 +1353,7 @@ QUnit.test('selects a playlist below the current bandwidth', function(assert) {
});

QUnit.test(
'selects a primary rendtion when there are multiple rendtions share same attributes',
'selects a primary rendition when there are multiple rendtions share same attributes',
function(assert) {
let playlist;

Expand Down

0 comments on commit a0c0359

Please sign in to comment.