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.
170 lines
5.5 KiB
170 lines
5.5 KiB
4 years ago
|
import { module, test } from 'qunit';
|
||
|
import {
|
||
|
simpleSelector,
|
||
|
movingAverageBandwidthSelector,
|
||
|
minRebufferMaxBandwidthSelector,
|
||
|
lowestBitrateCompatibleVariantSelector
|
||
|
} from '../src/playlist-selectors';
|
||
|
import Config from '../src/config';
|
||
|
|
||
|
module('Playlist Selectors', {
|
||
|
beforeEach(assert) {
|
||
|
const video = document.createElement('video');
|
||
|
|
||
|
this.hls = {
|
||
|
tech_: {
|
||
|
el() {
|
||
|
return video;
|
||
|
}
|
||
|
},
|
||
|
playlists: {
|
||
|
master: {
|
||
|
playlists: []
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
},
|
||
|
afterEach() {
|
||
|
|
||
|
}
|
||
|
});
|
||
|
|
||
|
test('Exponential moving average has a configurable decay parameter', function(assert) {
|
||
|
let playlist;
|
||
|
const instantAverage = movingAverageBandwidthSelector(1.0);
|
||
|
|
||
|
this.hls.playlists.master.playlists = [
|
||
|
{ attributes: { BANDWIDTH: 1 } },
|
||
|
{ attributes: { BANDWIDTH: 50 } },
|
||
|
{ attributes: { BANDWIDTH: 100 } }
|
||
|
];
|
||
|
this.hls.systemBandwidth = 50 * Config.BANDWIDTH_VARIANCE + 1;
|
||
|
playlist = instantAverage.call(this.hls);
|
||
|
assert.equal(playlist.attributes.BANDWIDTH, 50, 'selected the middle playlist');
|
||
|
|
||
|
this.hls.systemBandwidth = 100 * Config.BANDWIDTH_VARIANCE + 1;
|
||
|
playlist = instantAverage.call(this.hls);
|
||
|
assert.equal(playlist.attributes.BANDWIDTH, 100, 'selected the top playlist');
|
||
|
|
||
|
const fiftyPercentDecay = movingAverageBandwidthSelector(0.5);
|
||
|
|
||
|
this.hls.systemBandwidth = 100 * Config.BANDWIDTH_VARIANCE + 1;
|
||
|
playlist = fiftyPercentDecay.call(this.hls);
|
||
|
assert.equal(playlist.attributes.BANDWIDTH, 100, 'selected the top playlist');
|
||
|
|
||
|
// average = decay * systemBandwidth + (1 - decay) * average
|
||
|
// bandwidth = 0.5 * systemBandwidth + 0.5 * (100 * variance + 1)
|
||
|
// 50 * variance + 1 = 0.5 * (systemBandwidth + (100 * variance + 1))
|
||
|
// 2 * 50 * variance + 2 = systemBandwidth + (100 * variance + 1)
|
||
|
// 100 * variance + 2 - (100 * variance + 1) = systemBandwidth
|
||
|
// 1 = systemBandwidth
|
||
|
this.hls.systemBandwidth = 1;
|
||
|
playlist = fiftyPercentDecay.call(this.hls);
|
||
|
assert.equal(playlist.attributes.BANDWIDTH, 50, 'selected the middle playlist');
|
||
|
});
|
||
|
|
||
|
test('minRebufferMaxBandwidthSelector picks highest rendition without rebuffering',
|
||
|
function(assert) {
|
||
|
let master = this.hls.playlists.master;
|
||
|
let currentTime = 0;
|
||
|
let bandwidth = 2000;
|
||
|
let duration = 100;
|
||
|
let segmentDuration = 10;
|
||
|
let timeUntilRebuffer = 5;
|
||
|
let currentTimeline = 0;
|
||
|
let syncController = {
|
||
|
getSyncPoint: (playlist) => playlist.syncPoint
|
||
|
};
|
||
|
|
||
|
const settings = () => {
|
||
|
return {
|
||
|
master,
|
||
|
currentTime,
|
||
|
bandwidth,
|
||
|
duration,
|
||
|
segmentDuration,
|
||
|
timeUntilRebuffer,
|
||
|
currentTimeline,
|
||
|
syncController
|
||
|
};
|
||
|
};
|
||
|
|
||
|
master.playlists = [
|
||
|
{ attributes: { BANDWIDTH: 100 }, syncPoint: false },
|
||
|
{ attributes: { BANDWIDTH: 500 }, syncPoint: false },
|
||
|
{ attributes: { BANDWIDTH: 1000 }, syncPoint: false },
|
||
|
{ attributes: { BANDWIDTH: 2000 }, syncPoint: true },
|
||
|
{ attributes: { BANDWIDTH: 5000 }, syncPoint: false }
|
||
|
];
|
||
|
|
||
|
let result = minRebufferMaxBandwidthSelector(settings());
|
||
|
|
||
|
assert.equal(result.playlist, master.playlists[1], 'selected the correct playlist');
|
||
|
assert.equal(result.rebufferingImpact, 0, 'impact on rebuffering is 0');
|
||
|
|
||
|
master.playlists = [
|
||
|
{ attributes: { BANDWIDTH: 100 }, syncPoint: false },
|
||
|
{ attributes: { BANDWIDTH: 500 }, syncPoint: false },
|
||
|
{ attributes: { BANDWIDTH: 1000 }, syncPoint: true },
|
||
|
{ attributes: { BANDWIDTH: 2000 }, syncPoint: true },
|
||
|
{ attributes: { BANDWIDTH: 5000 }, syncPoint: false }
|
||
|
];
|
||
|
|
||
|
result = minRebufferMaxBandwidthSelector(settings());
|
||
|
|
||
|
assert.equal(result.playlist, master.playlists[2], 'selected the corerct playlist');
|
||
|
assert.equal(result.rebufferingImpact, 0, 'impact on rebuffering is 0');
|
||
|
|
||
|
bandwidth = 500;
|
||
|
timeUntilRebuffer = 3;
|
||
|
|
||
|
result = minRebufferMaxBandwidthSelector(settings());
|
||
|
|
||
|
assert.equal(result.playlist, master.playlists[0], 'selected the correct playlist');
|
||
|
assert.equal(result.rebufferingImpact, 1, 'impact on rebuffering is 1 second');
|
||
|
});
|
||
|
|
||
|
test('lowestBitrateCompatibleVariantSelector picks lowest non-audio playlist',
|
||
|
function(assert) {
|
||
|
// Set this up out of order to make sure that the function sorts all
|
||
|
// playlists by bandwidth
|
||
|
this.hls.playlists.master.playlists = [
|
||
|
{ attributes: { BANDWIDTH: 10, CODECS: 'mp4a.40.2' } },
|
||
|
{ attributes: { BANDWIDTH: 100, CODECS: 'mp4a.40.2, avc1.4d400d' } },
|
||
|
{ attributes: { BANDWIDTH: 50, CODECS: 'mp4a.40.2, avc1.4d400d' } }
|
||
|
];
|
||
|
|
||
|
const expectedPlaylist = this.hls.playlists.master.playlists[2];
|
||
|
const testPlaylist = lowestBitrateCompatibleVariantSelector.call(this.hls);
|
||
|
|
||
|
assert.equal(testPlaylist, expectedPlaylist,
|
||
|
'Selected lowest compatible playlist with video assets');
|
||
|
});
|
||
|
|
||
|
test('lowestBitrateCompatibleVariantSelector return null if no video exists',
|
||
|
function(assert) {
|
||
|
this.hls.playlists.master.playlists = [
|
||
|
{ attributes: { BANDWIDTH: 50, CODECS: 'mp4a.40.2' } },
|
||
|
{ attributes: { BANDWIDTH: 10, CODECS: 'mp4a.40.2' } },
|
||
|
{ attributes: { BANDWIDTH: 100, CODECS: 'mp4a.40.2' } }
|
||
|
];
|
||
|
|
||
|
const testPlaylist = lowestBitrateCompatibleVariantSelector.call(this.hls);
|
||
|
|
||
|
assert.equal(testPlaylist, null,
|
||
|
'Returned null playlist since no video assets exist');
|
||
|
});
|
||
|
|
||
|
test('simpleSelector switches up even without resolution information', function(assert) {
|
||
|
let master = this.hls.playlists.master;
|
||
|
|
||
|
master.playlists = [
|
||
|
{ attributes: { BANDWIDTH: 100 } },
|
||
|
{ attributes: { BANDWIDTH: 1000 } }
|
||
|
];
|
||
|
|
||
|
const selectedPlaylist = simpleSelector(master, 2000, 1, 1);
|
||
|
|
||
|
assert.equal(selectedPlaylist, master.playlists[1], 'selected the correct playlist');
|
||
|
});
|