You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
433 lines
11 KiB
433 lines
11 KiB
import QUnit from 'qunit';
|
|
import { createPlaylistID } from '../src/manifest';
|
|
import RenditionMixin from '../src/rendition-mixin.js';
|
|
import videojs from 'video.js';
|
|
|
|
const makeMockPlaylist = function(options) {
|
|
options = options || {};
|
|
|
|
const playlist = {
|
|
segments: [],
|
|
attributes: {}
|
|
};
|
|
|
|
playlist.attributes.BANDWIDTH = options.bandwidth;
|
|
|
|
if ('codecs' in options) {
|
|
playlist.attributes.CODECS = options.codecs;
|
|
}
|
|
|
|
if ('audio' in options) {
|
|
playlist.attributes.AUDIO = options.audio;
|
|
}
|
|
|
|
if ('width' in options) {
|
|
playlist.attributes.RESOLUTION = playlist.attributes.RESOLUTION || {};
|
|
|
|
playlist.attributes.RESOLUTION.width = options.width;
|
|
}
|
|
|
|
if ('height' in options) {
|
|
playlist.attributes.RESOLUTION = playlist.attributes.RESOLUTION || {};
|
|
|
|
playlist.attributes.RESOLUTION.height = options.height;
|
|
}
|
|
|
|
if ('excludeUntil' in options) {
|
|
playlist.excludeUntil = options.excludeUntil;
|
|
}
|
|
|
|
if ('uri' in options) {
|
|
playlist.uri = options.uri;
|
|
}
|
|
|
|
if ('disabled' in options) {
|
|
playlist.disabled = options.disabled;
|
|
}
|
|
|
|
return playlist;
|
|
};
|
|
|
|
const makeMockVhsHandler = function(playlistOptions, handlerOptions) {
|
|
const mpc = {
|
|
fastQualityChange_: () => {
|
|
mpc.fastQualityChange_.calls++;
|
|
},
|
|
smoothQualityChange_: () => {
|
|
mpc.smoothQualityChange_.calls++;
|
|
},
|
|
master: () => {
|
|
return {};
|
|
}
|
|
};
|
|
|
|
mpc.fastQualityChange_.calls = 0;
|
|
mpc.smoothQualityChange_.calls = 0;
|
|
|
|
const vhsHandler = {
|
|
masterPlaylistController_: mpc,
|
|
options_: handlerOptions || {}
|
|
};
|
|
|
|
vhsHandler.playlists = new videojs.EventTarget();
|
|
vhsHandler.playlists.master = { playlists: [] };
|
|
|
|
playlistOptions.forEach((playlist, i) => {
|
|
vhsHandler.playlists.master.playlists[i] = makeMockPlaylist(playlist);
|
|
|
|
if (playlist.uri) {
|
|
const id = createPlaylistID(i, playlist.uri);
|
|
|
|
vhsHandler.playlists.master.playlists[i].id = id;
|
|
vhsHandler.playlists.master.playlists[id] =
|
|
vhsHandler.playlists.master.playlists[i];
|
|
}
|
|
});
|
|
|
|
return vhsHandler;
|
|
};
|
|
|
|
QUnit.module('Rendition Selector API Mixin');
|
|
|
|
QUnit.test('adds the representations API to VhsHandler', function(assert) {
|
|
const vhsHandler = makeMockVhsHandler([
|
|
{}
|
|
]);
|
|
|
|
RenditionMixin(vhsHandler);
|
|
|
|
assert.equal(
|
|
typeof vhsHandler.representations, 'function',
|
|
'added the representations API'
|
|
);
|
|
});
|
|
|
|
QUnit.test('returns proper number of representations', function(assert) {
|
|
const vhsHandler = makeMockVhsHandler([
|
|
{}, {}, {}
|
|
]);
|
|
|
|
RenditionMixin(vhsHandler);
|
|
|
|
const renditions = vhsHandler.representations();
|
|
|
|
assert.equal(renditions.length, 3, 'number of renditions is 3');
|
|
});
|
|
|
|
QUnit.test('returns representations in playlist order', function(assert) {
|
|
const vhsHandler = makeMockVhsHandler([
|
|
{
|
|
bandwidth: 10
|
|
},
|
|
{
|
|
bandwidth: 20
|
|
},
|
|
{
|
|
bandwidth: 30
|
|
}
|
|
]);
|
|
|
|
RenditionMixin(vhsHandler);
|
|
|
|
const renditions = vhsHandler.representations();
|
|
|
|
assert.equal(renditions[0].bandwidth, 10, 'rendition has bandwidth 10');
|
|
assert.equal(renditions[1].bandwidth, 20, 'rendition has bandwidth 20');
|
|
assert.equal(renditions[2].bandwidth, 30, 'rendition has bandwidth 30');
|
|
});
|
|
|
|
QUnit.test('returns representations with width and height if present', function(assert) {
|
|
const vhsHandler = makeMockVhsHandler([
|
|
{
|
|
bandwidth: 10,
|
|
width: 100,
|
|
height: 200
|
|
},
|
|
{
|
|
bandwidth: 20,
|
|
width: 500,
|
|
height: 600
|
|
},
|
|
{
|
|
bandwidth: 30
|
|
}
|
|
]);
|
|
|
|
RenditionMixin(vhsHandler);
|
|
|
|
const renditions = vhsHandler.representations();
|
|
|
|
assert.equal(renditions[0].width, 100, 'rendition has a width of 100');
|
|
assert.equal(renditions[0].height, 200, 'rendition has a height of 200');
|
|
assert.equal(renditions[1].width, 500, 'rendition has a width of 500');
|
|
assert.equal(renditions[1].height, 600, 'rendition has a height of 600');
|
|
assert.equal(renditions[2].width, undefined, 'rendition has a width of undefined');
|
|
assert.equal(renditions[2].height, undefined, 'rendition has a height of undefined');
|
|
});
|
|
|
|
QUnit.test(
|
|
'incompatible playlists are not included in the representations list',
|
|
function(assert) {
|
|
const vhsHandler = makeMockVhsHandler([
|
|
{
|
|
bandwidth: 0,
|
|
excludeUntil: Infinity,
|
|
uri: 'media0.m3u8'
|
|
},
|
|
{
|
|
bandwidth: 0,
|
|
excludeUntil: 0,
|
|
uri: 'media1.m3u8'
|
|
},
|
|
{
|
|
bandwidth: 0,
|
|
excludeUntil: Date.now() + 999999,
|
|
uri: 'media2.m3u8'
|
|
},
|
|
{
|
|
bandwidth: 0,
|
|
excludeUntil: 1,
|
|
uri: 'media3.m3u8'
|
|
},
|
|
{
|
|
bandwidth: 0,
|
|
uri: 'media4.m3u8'
|
|
}
|
|
]);
|
|
|
|
RenditionMixin(vhsHandler);
|
|
|
|
const renditions = vhsHandler.representations();
|
|
|
|
assert.equal(renditions.length, 4, 'incompatible rendition not added');
|
|
assert.equal(renditions[0].id, '1-media1.m3u8', 'rendition is enabled');
|
|
assert.equal(renditions[1].id, '2-media2.m3u8', 'rendition is enabled');
|
|
assert.equal(renditions[2].id, '3-media3.m3u8', 'rendition is enabled');
|
|
assert.equal(renditions[3].id, '4-media4.m3u8', 'rendition is enabled');
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
'setting a representation to disabled sets disabled to true',
|
|
function(assert) {
|
|
let renditiondisabled = 0;
|
|
const vhsHandler = makeMockVhsHandler([
|
|
{
|
|
bandwidth: 0,
|
|
excludeUntil: 0,
|
|
uri: 'media0.m3u8'
|
|
},
|
|
{
|
|
bandwidth: 0,
|
|
excludeUntil: 0,
|
|
uri: 'media1.m3u8'
|
|
}
|
|
]);
|
|
const playlists = vhsHandler.playlists.master.playlists;
|
|
|
|
vhsHandler.playlists.on('renditiondisabled', function() {
|
|
renditiondisabled++;
|
|
});
|
|
|
|
RenditionMixin(vhsHandler);
|
|
|
|
const renditions = vhsHandler.representations();
|
|
|
|
assert.equal(renditiondisabled, 0, 'renditiondisabled event has not been triggered');
|
|
renditions[0].enabled(false);
|
|
|
|
assert.equal(renditiondisabled, 1, 'renditiondisabled event has been triggered');
|
|
assert.equal(playlists[0].disabled, true, 'rendition has been disabled');
|
|
assert.equal(playlists[1].disabled, undefined, 'rendition has not been disabled');
|
|
assert.equal(
|
|
playlists[0].excludeUntil, 0,
|
|
'excludeUntil not touched when disabling a rendition'
|
|
);
|
|
assert.equal(
|
|
playlists[1].excludeUntil, 0,
|
|
'excludeUntil not touched when disabling a rendition'
|
|
);
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
'changing the enabled state of a representation calls fastQualityChange_ by default',
|
|
function(assert) {
|
|
let renditionEnabledEvents = 0;
|
|
const vhsHandler = makeMockVhsHandler([
|
|
{
|
|
bandwidth: 0,
|
|
disabled: true,
|
|
uri: 'media0.m3u8'
|
|
},
|
|
{
|
|
bandwidth: 0,
|
|
uri: 'media1.m3u8'
|
|
}
|
|
]);
|
|
const mpc = vhsHandler.masterPlaylistController_;
|
|
|
|
vhsHandler.playlists.on('renditionenabled', function() {
|
|
renditionEnabledEvents++;
|
|
});
|
|
|
|
RenditionMixin(vhsHandler);
|
|
|
|
const renditions = vhsHandler.representations();
|
|
|
|
assert.equal(mpc.fastQualityChange_.calls, 0, 'fastQualityChange_ was never called');
|
|
assert.equal(
|
|
renditionEnabledEvents, 0,
|
|
'renditionenabled event has not been triggered'
|
|
);
|
|
|
|
renditions[0].enabled(true);
|
|
|
|
assert.equal(mpc.fastQualityChange_.calls, 1, 'fastQualityChange_ was called once');
|
|
assert.equal(
|
|
renditionEnabledEvents, 1,
|
|
'renditionenabled event has been triggered once'
|
|
);
|
|
|
|
renditions[1].enabled(false);
|
|
|
|
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;
|
|
const vhsHandler = makeMockVhsHandler([
|
|
{
|
|
bandwidth: 0,
|
|
disabled: true,
|
|
uri: 'media0.m3u8'
|
|
},
|
|
{
|
|
bandwidth: 0,
|
|
uri: 'media1.m3u8'
|
|
}
|
|
], {
|
|
smoothQualityChange: true
|
|
});
|
|
const mpc = vhsHandler.masterPlaylistController_;
|
|
|
|
vhsHandler.playlists.on('renditionenabled', function() {
|
|
renditionEnabledEvents++;
|
|
});
|
|
|
|
RenditionMixin(vhsHandler);
|
|
|
|
const renditions = vhsHandler.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');
|
|
}
|
|
);
|
|
|
|
QUnit.test('playlist is exposed on renditions', function(assert) {
|
|
const vhsHandler = makeMockVhsHandler([
|
|
{
|
|
bandwidth: 0,
|
|
uri: 'media0.m3u8',
|
|
codecs: 'mp4a.40.2'
|
|
},
|
|
{
|
|
bandwidth: 0,
|
|
uri: 'media1.m3u8',
|
|
codecs: 'mp4a.40.5'
|
|
},
|
|
{
|
|
bandwidth: 0,
|
|
uri: 'media2.m3u8'
|
|
}
|
|
]);
|
|
|
|
RenditionMixin(vhsHandler);
|
|
|
|
const renditions = vhsHandler.representations();
|
|
|
|
assert.deepEqual(renditions[0].playlist, vhsHandler.playlists.master.playlists[0], 'rendition 1 has correct playlist');
|
|
assert.deepEqual(renditions[1].playlist, vhsHandler.playlists.master.playlists[1], 'rendition 2 has correct playlist');
|
|
assert.deepEqual(renditions[2].playlist, vhsHandler.playlists.master.playlists[2], 'rendition 3 has no playlist');
|
|
});
|
|
|
|
QUnit.test('codecs attribute is exposed on renditions when available', function(assert) {
|
|
const vhsHandler = makeMockVhsHandler([
|
|
{
|
|
bandwidth: 0,
|
|
uri: 'media0.m3u8',
|
|
codecs: 'mp4a.40.2'
|
|
},
|
|
{
|
|
bandwidth: 0,
|
|
uri: 'media1.m3u8',
|
|
codecs: 'mp4a.40.5'
|
|
},
|
|
{
|
|
bandwidth: 0,
|
|
uri: 'media2.m3u8'
|
|
}
|
|
]);
|
|
|
|
RenditionMixin(vhsHandler);
|
|
|
|
const renditions = vhsHandler.representations();
|
|
|
|
assert.deepEqual(renditions[0].codecs, {audio: 'mp4a.40.2'}, 'rendition 1 has correct codec');
|
|
assert.deepEqual(renditions[1].codecs, {audio: 'mp4a.40.5'}, 'rendition 2 has correct codec');
|
|
assert.deepEqual(renditions[2].codecs, {}, 'rendition 3 has no codec');
|
|
});
|
|
|
|
QUnit.test('codecs attribute gets codecs from master', function(assert) {
|
|
const vhsHandler = makeMockVhsHandler([
|
|
{
|
|
bandwidth: 0,
|
|
uri: 'media0.m3u8',
|
|
audio: 'a1'
|
|
}
|
|
]);
|
|
const mpc = vhsHandler.masterPlaylistController_;
|
|
|
|
mpc.master = () => {
|
|
return {
|
|
mediaGroups: {
|
|
AUDIO: {
|
|
a1: {
|
|
eng: {
|
|
default: true,
|
|
uri: 'audio.m3u8',
|
|
playlists: [
|
|
{attributes: {CODECS: 'mp4a.40.2'}}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
RenditionMixin(vhsHandler);
|
|
|
|
const renditions = vhsHandler.representations();
|
|
|
|
assert.deepEqual(renditions[0].codecs, {audio: 'mp4a.40.2'}, 'rendition 1 has correct codec');
|
|
});
|