Skip to content

Commit

Permalink
feat: Custom Pixel Ratio (#1497)
Browse files Browse the repository at this point in the history
  • Loading branch information
wseymour15 authored Mar 8, 2024
1 parent c50ba7e commit 0e9d9d8
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 4 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Video.js Compatibility: 7.x, 8.x
- [enableLowInitialPlaylist](#enablelowinitialplaylist)
- [limitRenditionByPlayerDimensions](#limitrenditionbyplayerdimensions)
- [useDevicePixelRatio](#usedevicepixelratio)
- [customPixelRatio](#custompixelratio)
- [allowSeeksWithinUnsafeLiveWindow](#allowseekswithinunsafelivewindow)
- [customTagParsers](#customtagparsers)
- [customTagMappers](#customtagmappers)
Expand Down Expand Up @@ -404,6 +405,18 @@ This setting is `true` by default.
If true, this will take the device pixel ratio into account when doing rendition switching. This means that if you have a player with the width of `540px` in a high density display with a device pixel ratio of 2, a rendition of `1080p` will be allowed.
This setting is `false` by default.

##### customPixelRatio
* Type: `number`
* can be used as an initialization option.

If set, this will take the initial player dimensions and multiply it by a custom ratio when the player automatically selects renditions. This means that if you have a player where the dimension is `540p`, with a custom pixel ratio of `2`, a rendition of `1080p` or a lower rendition closest to this value will be chosen. Additionally, if you have a player where the dimension is `540p`, with a custom pixel ratio of `0.5`, a rendition of `270p` or a lower rendition closest to this value will be chosen. When the custom pixel ratio is 0, the lowest available rendition will be selected.

It is worth noting that if the player dimension multiplied by the custom pixel ratio is greater than any available rendition resolution, a rendition will be selected based on bandwidth, and the player dimension will be disregarded.

`limitRenditionByPlayerDimensions` must be `true` in order for this feature to be enabled. This is the default value.

If `useDevicePixelRatio` is set to `true`, the custom pixel ratio will be prioritized and overwrite any previous pixel ratio.

##### allowSeeksWithinUnsafeLiveWindow
* Type: `boolean`
* can be used as a source option
Expand Down
12 changes: 10 additions & 2 deletions src/playlist-selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,11 @@ export const TEST_ONLY_SIMPLE_SELECTOR = (newSimpleSelector) => {
* bandwidth variance
*/
export const lastBandwidthSelector = function() {
const pixelRatio = this.useDevicePixelRatio ? window.devicePixelRatio || 1 : 1;
let pixelRatio = this.useDevicePixelRatio ? window.devicePixelRatio || 1 : 1;

if (!isNaN(this.customPixelRatio)) {
pixelRatio = this.customPixelRatio;
}

return simpleSelector(
this.playlists.main,
Expand Down Expand Up @@ -399,7 +403,11 @@ export const movingAverageBandwidthSelector = function(decay) {
}

return function() {
const pixelRatio = this.useDevicePixelRatio ? window.devicePixelRatio || 1 : 1;
let pixelRatio = this.useDevicePixelRatio ? window.devicePixelRatio || 1 : 1;

if (!isNaN(this.customPixelRatio)) {
pixelRatio = this.customPixelRatio;
}

if (average < 0) {
average = this.systemBandwidth;
Expand Down
8 changes: 8 additions & 0 deletions src/videojs-http-streaming.js
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,7 @@ class VhsHandler extends Component {
[
'withCredentials',
'useDevicePixelRatio',
'customPixelRatio',
'limitRenditionByPlayerDimensions',
'bandwidth',
'customTagParsers',
Expand All @@ -761,6 +762,13 @@ class VhsHandler extends Component {

this.limitRenditionByPlayerDimensions = this.options_.limitRenditionByPlayerDimensions;
this.useDevicePixelRatio = this.options_.useDevicePixelRatio;

const customPixelRatio = this.options_.customPixelRatio;

// Ensure the custom pixel ratio is a number greater than or equal to 0
if (typeof customPixelRatio === 'number' && customPixelRatio >= 0) {
this.customPixelRatio = customPixelRatio;
}
}
// alias for public method to set options
setOptions(options = {}) {
Expand Down
9 changes: 8 additions & 1 deletion test/configuration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,14 @@ const options = [{
default: false,
test: true,
alt: false
}, {
},
{
name: 'customPixelRatio',
default: undefined,
test: 1,
alt: 0.5
},
{
name: 'bandwidth',
default: 4194304,
test: 5,
Expand Down
115 changes: 114 additions & 1 deletion test/playlist-selectors.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { module, test } from 'qunit';
import document from 'global/document';
import window from 'global/window';
import {
TEST_ONLY_SIMPLE_SELECTOR,
simpleSelector,
movingAverageBandwidthSelector,
minRebufferMaxBandwidthSelector,
lowestBitrateCompatibleVariantSelector
lowestBitrateCompatibleVariantSelector,
lastBandwidthSelector
} from '../src/playlist-selectors';
import Config from '../src/config';

Expand Down Expand Up @@ -325,5 +327,116 @@ test('simpleSelector leastPixelDiffSelector selects least pixel diff resolution.

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

test('lastBandwidthSelector uses customPixelRatio to pick rendition', function(assert) {
let playlist;
const bandwidth = 20;

const oldGetComputedStyle = window.getComputedStyle;

// Mock a 540p player.
window.getComputedStyle = function() {
return {
width: 960,
height: 540
};
};

// Ensure system bandwith is greater than the rendition bandwidths.
this.vhs.systemBandwidth = bandwidth + 10;
// This is true by default.
this.vhs.limitRenditionByPlayerDimensions = true;

this.vhs.playlists.main.playlists = [
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 480, height: 270 } } },
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 960, height: 540 } } },
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 1440, height: 810 } } },
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 1920, height: 1080 } } }
];

// Picks the lowest possible rendition
this.vhs.customPixelRatio = 0;
playlist = lastBandwidthSelector.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 270, 'selected the lowest rendition');

this.vhs.customPixelRatio = 0.5;
playlist = lastBandwidthSelector.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 270, 'selected the rendition with 270p');

this.vhs.customPixelRatio = 1;
playlist = lastBandwidthSelector.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 540, 'selected the rendition with 540p');

this.vhs.customPixelRatio = 1.5;
playlist = lastBandwidthSelector.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 810, 'selected the rendition with 810p');

this.vhs.customPixelRatio = 2;
playlist = lastBandwidthSelector.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 1080, 'selected the rendition with 1080p');

// Since the customPixelRatio sets the player dimension higher than any available rendition,
// This value is entirely based on bandwidth.
this.vhs.customPixelRatio = 4;
playlist = lastBandwidthSelector.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 270, 'selected the rendition based on bandwidth');

window.getComputedStyle = oldGetComputedStyle;
});

test('movingAverageBandwidthSelector uses customPixelRatio to pick rendition', function(assert) {
let playlist;
const bandwidth = 20;
const selectionFunction = movingAverageBandwidthSelector(1);
const oldGetComputedStyle = window.getComputedStyle;

// Mock a 540p player.
window.getComputedStyle = function() {
return {
width: 960,
height: 540
};
};

// Ensure system bandwith is greater than the rendition bandwidths.
this.vhs.systemBandwidth = bandwidth + 10;
// This is true by default.
this.vhs.limitRenditionByPlayerDimensions = true;

this.vhs.playlists.main.playlists = [
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 480, height: 270 } } },
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 960, height: 540 } } },
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 1440, height: 810 } } },
{ attributes: { BANDWIDTH: bandwidth, RESOLUTION: { width: 1920, height: 1080 } } }
];

// Picks the lowest possible rendition
this.vhs.customPixelRatio = 0;
playlist = selectionFunction.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 270, 'selected the lowest rendition');

this.vhs.customPixelRatio = 0.5;
playlist = selectionFunction.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 270, 'selected the rendition with 270p');

this.vhs.customPixelRatio = 1;
playlist = selectionFunction.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 540, 'selected the rendition with 540p');

this.vhs.customPixelRatio = 1.5;
playlist = selectionFunction.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 810, 'selected the rendition with 810p');

this.vhs.customPixelRatio = 2;
playlist = selectionFunction.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 1080, 'selected the rendition with 1080p');

// Since the customPixelRatio sets the player dimension higher than any available rendition,
// This value is entirely based on bandwidth.
this.vhs.customPixelRatio = 4;
playlist = selectionFunction.call(this.vhs);
assert.equal(playlist.attributes.RESOLUTION.height, 270, 'selected the rendition based on bandwidth');

window.getComputedStyle = oldGetComputedStyle;
});

0 comments on commit 0e9d9d8

Please sign in to comment.