Skip to content

Commit

Permalink
feat: smoothQualityChange flag (#235)
Browse files Browse the repository at this point in the history
  • Loading branch information
squarebracket authored and gesinger committed Oct 22, 2018
1 parent fbdae9b commit 0e4fdf9
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 10 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ Video.js Compatibility: 6.0, 7.0
- [blacklistDuration](#blacklistduration)
- [bandwidth](#bandwidth)
- [enableLowInitialPlaylist](#enablelowinitialplaylist)
- [limitRenditionByPlayerDimensions](#limitrenditionbyplayerdimensions)
- [smoothQualityChange](#smoothqualitychange)
- [Runtime Properties](#runtime-properties)
- [hls.playlists.master](#hlsplaylistsmaster)
- [hls.playlists.media](#hlsplaylistsmedia)
Expand Down Expand Up @@ -342,6 +344,22 @@ selection logic will take into account the player size and rendition
resolutions when making a decision.
This setting is `true` by default.

##### smoothQualityChange
* Type: `boolean`
* can be used as a source option
* can be used as an initialization option

When the `smoothQualityChange` property is set to `true`, a manual quality
change triggered via the [representations API](#hlsrepresentations) will use
smooth quality switching rather than the default fast (buffer-ejecting)
quality switching. Using smooth quality switching will mean no loading spinner
will appear during quality switches, but will cause quality switches to only
be visible after a few seconds.

Note that this _only_ affects quality changes triggered via the representations
API; automatic quality switches based on available bandwidth will always be
smooth switches.

### Runtime Properties
Runtime properties are attached to the tech object when HLS is in
use. You can get a reference to the HLS source handler like this:
Expand Down
14 changes: 8 additions & 6 deletions src/rendition-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ const enableFunction = (loader, playlistUri, changePlaylistFn) => (enable) => {
*/
class Representation {
constructor(hlsHandler, playlist, id) {
// Get a reference to a bound version of fastQualityChange_
let fastChangeFunction = hlsHandler
.masterPlaylistController_
.fastQualityChange_
.bind(hlsHandler.masterPlaylistController_);
const {
masterPlaylistController_: mpc,
options_: { smoothQualityChange }
} = hlsHandler;
// Get a reference to a bound version of the quality change function
const changeType = smoothQualityChange ? 'smooth' : 'fast';
const qualityChangeFunction = mpc[`${changeType}QualityChange_`].bind(mpc);

// some playlist attributes are optional
if (playlist.attributes.RESOLUTION) {
Expand All @@ -72,7 +74,7 @@ class Representation {
// specific variant
this.enabled = enableFunction(hlsHandler.playlists,
playlist.uri,
fastChangeFunction);
qualityChangeFunction);
}
}

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 @@ -341,6 +341,7 @@ class HlsHandler extends Component {
// defaults
this.options_.withCredentials = this.options_.withCredentials || false;
this.options_.limitRenditionByPlayerDimensions = this.options_.limitRenditionByPlayerDimensions === false ? false : true;
this.options_.smoothQualityChange = this.options_.smoothQualityChange || false;

if (typeof this.options_.blacklistDuration !== 'number') {
this.options_.blacklistDuration = 5 * 60;
Expand All @@ -359,7 +360,7 @@ class HlsHandler extends Component {
this.options_.bandwidth === Config.INITIAL_BANDWIDTH;

// grab options passed to player.src
['withCredentials', 'limitRenditionByPlayerDimensions', 'bandwidth'].forEach((option) => {
['withCredentials', 'limitRenditionByPlayerDimensions', 'bandwidth', 'smoothQualityChange'].forEach((option) => {
if (typeof this.source_[option] !== 'undefined') {
this.options_[option] = this.source_[option];
}
Expand Down
5 changes: 5 additions & 0 deletions test/configuration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ const options = [{
default: 4194304,
test: 5,
alt: 555
}, {
name: 'smoothQualityChange',
default: false,
test: true,
alt: false
}];

const CONFIG_KEYS = Object.keys(Config);
Expand Down
53 changes: 50 additions & 3 deletions test/rendition-mixin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,22 @@ const makeMockPlaylist = function(options) {
return playlist;
};

const makeMockHlsHandler = function(playlistOptions) {
const makeMockHlsHandler = function(playlistOptions, handlerOptions) {
let mcp = {
fastQualityChange_: () => {
mcp.fastQualityChange_.calls++;
},
smoothQualityChange_: () => {
mcp.smoothQualityChange_.calls++;
}
};

mcp.fastQualityChange_.calls = 0;
mcp.smoothQualityChange_.calls = 0;

let hlsHandler = {
masterPlaylistController_: mcp
masterPlaylistController_: mcp,
options_: handlerOptions || {}
};

hlsHandler.playlists = new videojs.EventTarget();
Expand Down Expand Up @@ -222,7 +227,7 @@ function(assert) {
'excludeUntil not touched when disabling a rendition');
});

QUnit.test('changing the enabled state of a representation calls fastQualityChange_',
QUnit.test('changing the enabled state of a representation calls fastQualityChange_ by default',
function(assert) {
let renditionEnabledEvents = 0;
let hlsHandler = makeMockHlsHandler([
Expand Down Expand Up @@ -260,3 +265,45 @@ function(assert) {

assert.equal(mpc.fastQualityChange_.calls, 2, 'fastQualityChange_ was called twice');
});

QUnit.test('changing the enabled state of a representation calls smoothQualityChange_ ' +
'when the flag is set',
function(assert) {
let renditionEnabledEvents = 0;
let hlsHandler = makeMockHlsHandler([
{
bandwidth: 0,
disabled: true,
uri: 'media0.m3u8'
},
{
bandwidth: 0,
uri: 'media1.m3u8'
}
], {
smoothQualityChange: true
});
let mpc = hlsHandler.masterPlaylistController_;

hlsHandler.playlists.on('renditionenabled', function() {
renditionEnabledEvents++;
});

RenditionMixin(hlsHandler);

let renditions = hlsHandler.representations();

assert.equal(mpc.smoothQualityChange_.calls, 0, 'smoothQualityChange_ was never called');
assert.equal(renditionEnabledEvents, 0,
'renditionenabled event has not been triggered');

renditions[0].enabled(true);

assert.equal(mpc.smoothQualityChange_.calls, 1, 'smoothQualityChange_ was called once');
assert.equal(renditionEnabledEvents, 1,
'renditionenabled event has been triggered once');

renditions[1].enabled(false);

assert.equal(mpc.smoothQualityChange_.calls, 2, 'smoothQualityChange_ was called twice');
});

0 comments on commit 0e4fdf9

Please sign in to comment.