Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add back manual smooth quality changes via smoothQualityChange flag #235

Merged
merged 1 commit into from
Oct 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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');
});