Browse Source

support multiple stream-inf with same URI (#670)

* support multiple stream-inf with same URI

* fix existing tests

* add unit test
pull/673/head
Matthew Neil 6 years ago
committed by GitHub
parent
commit
eecdaef623
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 43
      src/dash-playlist-loader.js
  2. 51
      src/playlist-loader.js
  3. 10
      src/rendition-mixin.js
  4. 2
      src/segment-loader.js
  5. 2
      src/videojs-http-streaming.js
  6. 97
      test/dash-playlist-loader.test.js
  7. 1
      test/playback.test.js
  8. 107
      test/playlist-loader.test.js
  9. 14
      test/rendition-mixin.test.js
  10. 3
      test/test-helpers.js
  11. 38
      test/videojs-http-streaming.test.js

43
src/dash-playlist-loader.js

@ -8,7 +8,8 @@ import {
setupMediaPlaylists,
resolveMediaGroupUris,
updateMaster as updatePlaylist,
forEachMediaGroup
forEachMediaGroup,
createPlaylistID
} from './playlist-loader';
import { resolveUrl, resolveManifestRedirect } from './resolve-url';
import mp4Inspector from 'mux.js/lib/tools/mp4-inspector';
@ -51,13 +52,13 @@ export const updateMaster = (oldMaster, newMaster) => {
// Then update media group playlists
forEachMediaGroup(newMaster, (properties, type, group, label) => {
if (properties.playlists && properties.playlists.length) {
const uri = properties.playlists[0].uri;
const id = properties.playlists[0].id;
const playlistUpdate = updatePlaylist(update, properties.playlists[0]);
if (playlistUpdate) {
update = playlistUpdate;
// update the playlist reference within media groups
update.mediaGroups[type][group][label].playlists[0] = update.playlists[uri];
update.mediaGroups[type][group][label].playlists[0] = update.playlists[id];
noChanges = false;
}
}
@ -103,8 +104,8 @@ const equivalentSidx = (a, b) => {
export const compareSidxEntry = (playlists, oldSidxMapping) => {
const newSidxMapping = {};
for (const uri in playlists) {
const playlist = playlists[uri];
for (const id in playlists) {
const playlist = playlists[id];
const currentSidxInfo = playlist.sidx;
if (currentSidxInfo) {
@ -202,7 +203,7 @@ export default class DashPlaylistLoader extends EventTarget {
// live playlist staleness timeout
this.on('mediaupdatetimeout', () => {
this.refreshMedia_(this.media().uri);
this.refreshMedia_(this.media().id);
});
this.state = 'HAVE_NOTHING';
@ -302,12 +303,12 @@ export default class DashPlaylistLoader extends EventTarget {
playlist = this.master.playlists[playlist];
}
const mediaChange = !this.media_ || playlist.uri !== this.media_.uri;
const mediaChange = !this.media_ || playlist.id !== this.media_.id;
// switch to previously loaded playlists immediately
if (mediaChange &&
this.loadedPlaylists_[playlist.uri] &&
this.loadedPlaylists_[playlist.uri].endList) {
this.loadedPlaylists_[playlist.id] &&
this.loadedPlaylists_[playlist.id].endList) {
this.state = 'HAVE_METADATA';
this.media_ = playlist;
@ -377,7 +378,7 @@ export default class DashPlaylistLoader extends EventTarget {
// everything is ready just continue to haveMetadata
this.haveMetadata({
startingState,
playlist: newMaster.playlists[playlist.uri]
playlist: newMaster.playlists[playlist.id]
});
})
);
@ -385,11 +386,11 @@ export default class DashPlaylistLoader extends EventTarget {
haveMetadata({startingState, playlist}) {
this.state = 'HAVE_METADATA';
this.loadedPlaylists_[playlist.uri] = playlist;
this.loadedPlaylists_[playlist.id] = playlist;
this.mediaRequest_ = null;
// This will trigger loadedplaylist
this.refreshMedia_(playlist.uri);
this.refreshMedia_(playlist.id);
// fire loadedmetadata the first time a media playlist is loaded
// to resolve setup of media groups
@ -457,8 +458,6 @@ export default class DashPlaylistLoader extends EventTarget {
const phonyUri = `placeholder-uri-${i}`;
master.playlists[i].uri = phonyUri;
// set up by URI references
master.playlists[phonyUri] = master.playlists[i];
}
// set up phony URIs for the media group playlists since we won't have external
@ -466,10 +465,12 @@ export default class DashPlaylistLoader extends EventTarget {
forEachMediaGroup(master, (properties, mediaType, groupKey, labelKey) => {
if (properties.playlists && properties.playlists.length) {
const phonyUri = `placeholder-uri-${mediaType}-${groupKey}-${labelKey}`;
const id = createPlaylistID(0, phonyUri);
properties.playlists[0].uri = phonyUri;
properties.playlists[0].id = id;
// setup URI references
master.playlists[phonyUri] = properties.playlists[0];
master.playlists[id] = properties.playlists[0];
}
});
@ -707,7 +708,7 @@ export default class DashPlaylistLoader extends EventTarget {
}, this.master.minimumUpdatePeriod);
// TODO: do we need to reload the current playlist?
this.refreshMedia_(this.media().uri);
this.refreshMedia_(this.media().id);
return;
})
@ -730,9 +731,9 @@ export default class DashPlaylistLoader extends EventTarget {
* references. If this is an alternate loader, the updated parsed manifest is retrieved
* from the master loader.
*/
refreshMedia_(mediaUri) {
if (!mediaUri) {
throw new Error('refreshMedia_ must take a media uri');
refreshMedia_(mediaID) {
if (!mediaID) {
throw new Error('refreshMedia_ must take a media id');
}
let oldMaster;
@ -754,9 +755,9 @@ export default class DashPlaylistLoader extends EventTarget {
} else {
this.master = updatedMaster;
}
this.media_ = updatedMaster.playlists[mediaUri];
this.media_ = updatedMaster.playlists[mediaID];
} else {
this.media_ = newMaster.playlists[mediaUri];
this.media_ = newMaster.playlists[mediaID];
this.trigger('playlistunchanged');
}

51
src/playlist-loader.js

@ -86,7 +86,7 @@ export const resolveSegmentUris = (segment, baseUri) => {
*/
export const updateMaster = (master, media) => {
const result = mergeOptions(master, {});
const playlist = result.playlists[media.uri];
const playlist = result.playlists[media.id];
if (!playlist) {
return null;
@ -122,15 +122,19 @@ export const updateMaster = (master, media) => {
// that is referenced by index, and one by URI. The index reference may no longer be
// necessary.
for (let i = 0; i < result.playlists.length; i++) {
if (result.playlists[i].uri === media.uri) {
if (result.playlists[i].id === media.id) {
result.playlists[i] = mergedPlaylist;
}
}
result.playlists[media.uri] = mergedPlaylist;
result.playlists[media.id] = mergedPlaylist;
return result;
};
export const createPlaylistID = (index, uri) => {
return `${index}-${uri}`;
};
export const setupMediaPlaylists = (master) => {
// setup by-URI lookups and resolve media playlist URIs
let i = master.playlists.length;
@ -138,9 +142,10 @@ export const setupMediaPlaylists = (master) => {
while (i--) {
const playlist = master.playlists[i];
master.playlists[playlist.uri] = playlist;
playlist.resolvedUri = resolveUrl(master.uri, playlist.uri);
playlist.id = i;
playlist.id = createPlaylistID(i, playlist.uri);
master.playlists[playlist.id] = playlist;
if (!playlist.attributes) {
// Although the spec states an #EXT-X-STREAM-INF tag MUST have a
@ -238,15 +243,20 @@ export default class PlaylistLoader extends EventTarget {
}
if (error) {
return this.playlistRequestError(this.request, this.media().uri, 'HAVE_METADATA');
return this.playlistRequestError(this.request, this.media(), 'HAVE_METADATA');
}
this.haveMetadata(this.request, this.media().uri);
this.haveMetadata(this.request, this.media().uri, this.media().id);
});
});
}
playlistRequestError(xhr, url, startingState) {
playlistRequestError(xhr, playlist, startingState) {
const {
uri,
id
} = playlist;
// any in-flight request is now finished
this.request = null;
@ -255,9 +265,9 @@ export default class PlaylistLoader extends EventTarget {
}
this.error = {
playlist: this.master.playlists[url],
playlist: this.master.playlists[id],
status: xhr.status,
message: `HLS playlist request error at URL: ${url}.`,
message: `HLS playlist request error at URL: ${uri}.`,
responseText: xhr.responseText,
code: (xhr.status >= 500) ? 4 : 2
};
@ -267,7 +277,7 @@ export default class PlaylistLoader extends EventTarget {
// update the playlist loader's state in response to a new or
// updated playlist.
haveMetadata(xhr, url) {
haveMetadata(xhr, url, id) {
// any in-flight request is now finished
this.request = null;
this.state = 'HAVE_METADATA';
@ -283,6 +293,7 @@ export default class PlaylistLoader extends EventTarget {
parser.push(xhr.responseText);
parser.end();
parser.manifest.uri = url;
parser.manifest.id = id;
// m3u8-parser does not attach an attributes property to media playlists so make
// sure that the property is attached to avoid undefined reference errors
parser.manifest.attributes = parser.manifest.attributes || {};
@ -294,7 +305,7 @@ export default class PlaylistLoader extends EventTarget {
if (update) {
this.master = update;
this.media_ = this.master.playlists[parser.manifest.uri];
this.media_ = this.master.playlists[id];
} else {
this.trigger('playlistunchanged');
}
@ -374,10 +385,10 @@ export default class PlaylistLoader extends EventTarget {
}
const startingState = this.state;
const mediaChange = !this.media_ || playlist.uri !== this.media_.uri;
const mediaChange = !this.media_ || playlist.id !== this.media_.id;
// switch to fully loaded playlists immediately
if (this.master.playlists[playlist.uri].endList) {
if (this.master.playlists[playlist.id].endList) {
// abort outstanding playlist requests
if (this.request) {
this.request.onreadystatechange = null;
@ -431,10 +442,10 @@ export default class PlaylistLoader extends EventTarget {
playlist.resolvedUri = resolveManifestRedirect(this.handleManifestRedirects, playlist.resolvedUri, req);
if (error) {
return this.playlistRequestError(this.request, playlist.uri, startingState);
return this.playlistRequestError(this.request, playlist, startingState);
}
this.haveMetadata(req, playlist.uri);
this.haveMetadata(req, playlist.uri, playlist.id);
// fire loadedmetadata the first time a media playlist is loaded
if (startingState === 'HAVE_MASTER') {
@ -564,6 +575,8 @@ export default class PlaylistLoader extends EventTarget {
return;
}
const id = createPlaylistID(0, this.srcUrl);
// loaded a media playlist
// infer a master playlist if none was previously requested
this.master = {
@ -576,15 +589,15 @@ export default class PlaylistLoader extends EventTarget {
uri: window.location.href,
playlists: [{
uri: this.srcUrl,
id: 0,
id,
resolvedUri: this.srcUrl,
// m3u8-parser does not attach an attributes property to media playlists so make
// sure that the property is attached to avoid undefined reference errors
attributes: {}
}]
};
this.master.playlists[this.srcUrl] = this.master.playlists[0];
this.haveMetadata(req, this.srcUrl);
this.master.playlists[id] = this.master.playlists[0];
this.haveMetadata(req, this.srcUrl, id);
return this.trigger('loadedmetadata');
});
}

10
src/rendition-mixin.js

@ -4,7 +4,7 @@ import { isIncompatible, isEnabled } from './playlist.js';
* Returns a function that acts as the Enable/disable playlist function.
*
* @param {PlaylistLoader} loader - The master playlist loader
* @param {string} playlistUri - uri of the playlist
* @param {string} playlistID - id of the playlist
* @param {Function} changePlaylistFn - A function to be called after a
* playlist's enabled-state has been changed. Will NOT be called if a
* playlist's enabled-state is unchanged
@ -12,8 +12,8 @@ import { isIncompatible, isEnabled } from './playlist.js';
* or if undefined returns the current enabled-state for the playlist
* @return {Function} Function for setting/getting enabled
*/
const enableFunction = (loader, playlistUri, changePlaylistFn) => (enable) => {
const playlist = loader.master.playlists[playlistUri];
const enableFunction = (loader, playlistID, changePlaylistFn) => (enable) => {
const playlist = loader.master.playlists[playlistID];
const incompatible = isIncompatible(playlist);
const currentlyEnabled = isEnabled(playlist);
@ -74,7 +74,7 @@ class Representation {
// specific variant
this.enabled = enableFunction(
hlsHandler.playlists,
playlist.uri,
playlist.id,
qualityChangeFunction
);
}
@ -96,7 +96,7 @@ const renditionSelectionMixin = function(hlsHandler) {
.master
.playlists
.filter((media) => !isIncompatible(media))
.map((e, i) => new Representation(hlsHandler, e, e.uri));
.map((e, i) => new Representation(hlsHandler, e, e.id));
};
};

2
src/segment-loader.js

@ -2196,7 +2196,7 @@ export default class SegmentLoader extends videojs.EventTarget {
byteLength: segmentInfo.byteLength,
uri: segmentInfo.uri,
timeline: segmentInfo.timeline,
playlist: segmentInfo.playlist.uri,
playlist: segmentInfo.playlist.id,
start,
end
};

2
src/videojs-http-streaming.js

@ -110,7 +110,7 @@ const handleHlsMediaChange = function(qualityLevels, playlistLoader) {
let selectedIndex = -1;
for (let i = 0; i < qualityLevels.length; i++) {
if (qualityLevels[i].id === newPlaylist.uri) {
if (qualityLevels[i].id === newPlaylist.id) {
selectedIndex = i;
break;
}

97
test/dash-playlist-loader.test.js

@ -47,6 +47,7 @@ QUnit.test('updateMaster: returns falsy when there are no changes', function(ass
length: 1,
0: {
uri: '0',
id: '0',
segments: []
}
},
@ -79,7 +80,7 @@ QUnit.test('updateMaster: updates playlists', function(assert) {
const master = {
playlists: {
length: 1,
0: { uri: '0' }
0: { uri: '0', id: '0' }
},
mediaGroups: {
AUDIO: {},
@ -93,6 +94,7 @@ QUnit.test('updateMaster: updates playlists', function(assert) {
playlists: {
length: 1,
0: {
id: '0',
uri: '0',
segments: []
}
@ -111,6 +113,7 @@ QUnit.test('updateMaster: updates playlists', function(assert) {
playlists: {
length: 1,
0: {
id: '0',
uri: '0',
segments: []
}
@ -130,6 +133,7 @@ QUnit.test('updateMaster: updates mediaGroups', function(assert) {
playlists: {
length: 1,
0: {
id: '0',
uri: '0',
segments: []
}
@ -139,6 +143,7 @@ QUnit.test('updateMaster: updates mediaGroups', function(assert) {
audio: {
'audio-main': {
playlists: [{
id: '0',
uri: '0',
test: 'old text',
segments: []
@ -165,6 +170,7 @@ QUnit.test('updateMaster: updates mediaGroups', function(assert) {
audio: {
'audio-main': {
playlists: [{
id: '0',
uri: '0',
resolvedUri: '0',
test: 'new text',
@ -198,6 +204,7 @@ QUnit.test('updateMaster: updates playlists and mediaGroups', function(assert) {
playlists: [{
mediaSequence: 0,
attributes: {},
id: 'audio-0-uri',
uri: 'audio-0-uri',
resolvedUri: urlTo('audio-0-uri'),
segments: [{
@ -215,6 +222,7 @@ QUnit.test('updateMaster: updates playlists and mediaGroups', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -234,6 +242,7 @@ QUnit.test('updateMaster: updates playlists and mediaGroups', function(assert) {
playlists: [{
mediaSequence: 1,
attributes: {},
id: 'audio-0-uri',
uri: 'audio-0-uri',
resolvedUri: urlTo('audio-0-uri'),
segments: [{
@ -251,6 +260,7 @@ QUnit.test('updateMaster: updates playlists and mediaGroups', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -276,6 +286,7 @@ QUnit.test('updateMaster: updates playlists and mediaGroups', function(assert) {
playlists: [{
mediaSequence: 1,
attributes: {},
id: 'audio-0-uri',
uri: 'audio-0-uri',
resolvedUri: urlTo('audio-0-uri'),
segments: [{
@ -293,6 +304,7 @@ QUnit.test('updateMaster: updates playlists and mediaGroups', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -360,6 +372,7 @@ QUnit.test('compareSidxEntry: will remove non-matching sidxes from a mapping', f
const playlists = [
{
uri: '0',
id: '0',
sidx: {
byterange: {
offset: 0,
@ -413,8 +426,8 @@ QUnit.test('filterChangedSidxMappings: removes change sidx info from mapping', f
'if no sidx info changed, return the same object'
);
const playlists = loader.master.playlists;
const oldVideoKey = generateSidxKey(playlists['placeholder-uri-0'].sidx);
const oldAudioEnKey = generateSidxKey(playlists['placeholder-uri-AUDIO-audio-en'].sidx);
const oldVideoKey = generateSidxKey(playlists['0-placeholder-uri-0'].sidx);
const oldAudioEnKey = generateSidxKey(playlists['0-placeholder-uri-AUDIO-audio-en'].sidx);
// should change the video playlist
masterXml = loader.masterXml_.replace(/(indexRange)=\"\d+-\d+\"/, '$1="201-400"');
@ -424,7 +437,7 @@ QUnit.test('filterChangedSidxMappings: removes change sidx info from mapping', f
loader.clientOffset_,
loader.sidxMapping_
);
const newVideoKey = `${playlists['placeholder-uri-0'].sidx.uri}-201-400`;
const newVideoKey = `${playlists['0-placeholder-uri-0'].sidx.uri}-201-400`;
assert.notOk(
newSidxMapping[oldVideoKey],
@ -463,6 +476,7 @@ QUnit.test('requestSidx_: creates an XHR request for a sidx range', function(ass
};
const playlist = {
uri: 'fakeplaylist',
id: 'fakeplaylist',
segments: [sidxInfo],
sidx: sidxInfo
};
@ -791,7 +805,7 @@ QUnit.test('media: sets initial media playlist on master loader', function(asser
);
assert.deepEqual(
Object.keys(loader.loadedPlaylists_),
[loader.master.playlists[0].uri],
[loader.master.playlists[0].id],
'updated the loadedPlaylists_'
);
assert.strictEqual(loader.state, 'HAVE_METADATA', 'state should be HAVE_METADATA');
@ -832,7 +846,7 @@ QUnit.test('media: sets a playlist from a string reference', function(assert) {
);
assert.deepEqual(
Object.keys(loader.loadedPlaylists_),
[loader.master.playlists[0].uri],
[loader.master.playlists[0].id],
'updated the loadedPlaylists_'
);
assert.strictEqual(loader.state, 'HAVE_METADATA', 'state should be HAVE_METADATA');
@ -895,8 +909,8 @@ QUnit.test('media: switches to a new playlist from a loaded one', function(asser
assert.deepEqual(
Object.keys(loader.loadedPlaylists_),
[
loader.master.playlists[0].uri,
loader.master.playlists[1].uri
loader.master.playlists[0].id,
loader.master.playlists[1].id
],
'updated loadedPlaylists_'
);
@ -971,8 +985,8 @@ QUnit.test('media: switches to a previously loaded playlist immediately', functi
assert.deepEqual(
Object.keys(loader.loadedPlaylists_),
[
loader.master.playlists[0].uri,
loader.master.playlists[1].uri
loader.master.playlists[0].id,
loader.master.playlists[1].id
],
'loadedPlaylists_ only updated for new playlists'
);
@ -1081,7 +1095,7 @@ QUnit.test('haveMetadata: triggers loadedplaylist if initial selection', functio
);
assert.deepEqual(
Object.keys(loader.loadedPlaylists_),
[loader.master.playlists[0].uri],
[loader.master.playlists[0].id],
'updated loadedPlaylists_'
);
assert.strictEqual(loadedPlaylists, 1, 'one loadedplaylists');
@ -1139,8 +1153,8 @@ QUnit.test('haveMetadata: triggers mediachange if new selection', function(asser
assert.deepEqual(
Object.keys(loader.loadedPlaylists_),
[
loader.master.playlists[1].uri,
loader.master.playlists[0].uri
loader.master.playlists[1].id,
loader.master.playlists[0].id
],
'updated loadedPlaylists_'
);
@ -1172,7 +1186,7 @@ QUnit.test('haveMaster: sets media on child loader', function(assert) {
loader.load();
this.standardXHRResponse(this.requests.shift());
const childPlaylist = loader.master.playlists['placeholder-uri-AUDIO-audio-main'];
const childPlaylist = loader.master.playlists['0-placeholder-uri-AUDIO-audio-main'];
const childLoader = new DashPlaylistLoader(childPlaylist, this.fakeHls, false, loader);
const mediaStub = sinon.stub(childLoader, 'media');
@ -1196,10 +1210,11 @@ QUnit.test('parseMasterXml: setup phony playlists and resolves uris', function(a
assert.strictEqual(masterPlaylist.uri, loader.srcUrl, 'master playlist uri set correctly');
assert.strictEqual(masterPlaylist.playlists[0].uri, 'placeholder-uri-0');
assert.strictEqual(masterPlaylist.playlists[0].id, '0-placeholder-uri-0');
assert.deepEqual(
masterPlaylist.playlists['placeholder-uri-0'],
masterPlaylist.playlists['0-placeholder-uri-0'],
masterPlaylist.playlists[0],
'phony uri setup correctly for playlist'
'phony id setup correctly for playlist'
);
assert.ok(
Object.keys(masterPlaylist.mediaGroups.AUDIO).length,
@ -1286,7 +1301,7 @@ QUnit.test('refreshMedia: updates master and media playlists for master loader',
const newMasterXml = testDataManifests['dash-live'];
loader.masterXml_ = newMasterXml;
loader.refreshMedia_(loader.media().uri);
loader.refreshMedia_(loader.media().id);
assert.notEqual(loader.master, oldMaster, 'new master set');
assert.strictEqual(loadedPlaylists, 1, 'one loadedplaylist');
@ -1318,7 +1333,7 @@ QUnit.test('refreshMedia: triggers playlistunchanged for master loader' +
playlistUnchanged++;
});
loader.refreshMedia_(loader.media().uri);
loader.refreshMedia_(loader.media().id);
assert.strictEqual(loadedPlaylists, 1, 'one loadedplaylists');
assert.strictEqual(playlistUnchanged, 1, 'one playlistunchanged');
});
@ -1353,7 +1368,7 @@ QUnit.test('refreshMedia: updates master and media playlists for child loader',
const newMasterXml = testDataManifests['dash-live'];
loader.masterXml_ = newMasterXml;
childLoader.refreshMedia_(loader.media().uri);
childLoader.refreshMedia_(loader.media().id);
assert.notEqual(loader.master, oldMaster, 'new master set on master loader');
assert.strictEqual(loadedPlaylists, 1, 'one loadedplaylist');
@ -1387,7 +1402,7 @@ QUnit.test('refreshMedia: triggers playlistunchanged for child loader' +
playlistUnchanged++;
});
childLoader.refreshMedia_(loader.media().uri);
childLoader.refreshMedia_(loader.media().id);
assert.strictEqual(loadedPlaylists, 1, 'one loadedplaylist');
assert.strictEqual(playlistUnchanged, 1, 'one playlistunchanged');
@ -1448,6 +1463,7 @@ QUnit.test('sidxRequestFinished_: updates master with sidx information', functio
const loader = new DashPlaylistLoader('dash.mpd', this.fakeHls);
const fakePlaylist = {
segments: [],
id: 'fakeplaylist',
uri: 'fakeplaylist',
sidx: {
byterange: {
@ -1495,6 +1511,7 @@ QUnit.test('sidxRequestFinished_: errors if request for sidx fails', function(as
uri: 'fake-segment',
duration: 15360
}],
id: 'fakeplaylist',
uri: 'fakeplaylist',
sidx: {
byterange: {
@ -1541,8 +1558,8 @@ QUnit.test('sidxRequestFinished_: errors if request for sidx fails', function(as
QUnit.test('setupChildLoader: sets masterPlaylistLoader and ' +
'playlist on child loader', function(assert) {
const fakePlaylist = { uri: 'fakeplaylist1' };
const newPlaylist = { uri: 'fakeplaylist2' };
const fakePlaylist = { uri: 'fakeplaylist1', id: 'fakeplaylist1' };
const newPlaylist = { uri: 'fakeplaylist2', id: 'fakeplaylist2' };
const loader = new DashPlaylistLoader('dash.mpd', this.fakeHls);
const newLoader = new DashPlaylistLoader('dash-sidx.mpd', this.fakeHls);
const childLoader = new DashPlaylistLoader(fakePlaylist, this.fakeHls, false, loader);
@ -1598,7 +1615,7 @@ QUnit.test('hasPendingRequest: returns true if async code is running in child lo
loader.load();
this.standardXHRResponse(this.requests.shift());
const childPlaylist = loader.master.playlists['placeholder-uri-AUDIO-audio-main'];
const childPlaylist = loader.master.playlists['0-placeholder-uri-AUDIO-audio-main'];
const childLoader = new DashPlaylistLoader(childPlaylist, this.fakeHls, false, loader);
assert.notOk(childLoader.hasPendingRequest(), 'no pending requests on construction');
@ -1676,7 +1693,7 @@ QUnit.test('redirect src request when handleManifestRedirects is true', function
modifiedRequest.responseURL = 'http://differenturi.com/test.mpd';
this.standardXHRResponse(modifiedRequest);
const childLoader = new DashPlaylistLoader(loader.master.playlists['placeholder-uri-0'], this.fakeHls, false, loader);
const childLoader = new DashPlaylistLoader(loader.master.playlists['0-placeholder-uri-0'], this.fakeHls, false, loader);
childLoader.load();
this.clock.tick(1);
@ -1773,7 +1790,7 @@ QUnit.test('child loader moves to HAVE_METADATA when initialized with a master p
loader.load();
this.standardXHRResponse(this.requests.shift());
const playlist = loader.master.playlists['placeholder-uri-AUDIO-audio-main'];
const playlist = loader.master.playlists['0-placeholder-uri-AUDIO-audio-main'];
const childLoader = new DashPlaylistLoader(playlist, this.fakeHls, false, loader);
childLoader.on('loadedplaylist', function() {
@ -1806,7 +1823,7 @@ QUnit.test('child playlist moves to HAVE_METADATA when initialized with a live m
loader.load();
this.standardXHRResponse(this.requests.shift());
const playlist = loader.master.playlists['placeholder-uri-AUDIO-audio-main'];
const playlist = loader.master.playlists['0-placeholder-uri-AUDIO-audio-main'];
const childLoader = new DashPlaylistLoader(playlist, this.fakeHls, false, loader);
childLoader.on('loadedplaylist', function() {
@ -1963,11 +1980,11 @@ QUnit.test('can switch playlists after the master is downloaded', function(asser
loader.load();
this.standardXHRResponse(this.requests.shift());
loader.media('placeholder-uri-0');
loader.media('0-placeholder-uri-0');
clock.tick(1);
assert.equal(loader.media().uri, 'placeholder-uri-0', 'changed to new playlist');
loader.media('placeholder-uri-1');
loader.media('1-placeholder-uri-1');
clock.tick(1);
assert.equal(loader.media().uri, 'placeholder-uri-1', 'changed to new playlist');
});
@ -1978,11 +1995,11 @@ QUnit.test('can switch playlists based on object or URI', function(assert) {
loader.load();
this.standardXHRResponse(this.requests.shift());
loader.media('placeholder-uri-0');
loader.media('0-placeholder-uri-0');
this.clock.tick(1);
assert.equal(loader.media().uri, 'placeholder-uri-0', 'changed to playlist by uri');
loader.media('placeholder-uri-1');
loader.media('1-placeholder-uri-1');
this.clock.tick(1);
assert.equal(loader.media().uri, 'placeholder-uri-1', 'changed to playlist by uri');
@ -2020,24 +2037,36 @@ QUnit.test(
loader.master.playlists[0].uri, 'placeholder-uri-0',
'setup phony uri for media playlist'
);
assert.equal(
loader.master.playlists[0].id, '0-placeholder-uri-0',
'setup phony id for media playlist'
);
assert.strictEqual(
loader.master.playlists['placeholder-uri-0'],
loader.master.playlists['0-placeholder-uri-0'],
loader.master.playlists[0], 'set reference by uri for easy access'
);
assert.equal(
loader.master.playlists[1].uri, 'placeholder-uri-1',
'setup phony uri for media playlist'
);
assert.equal(
loader.master.playlists[1].id, '1-placeholder-uri-1',
'setup phony id for media playlist'
);
assert.strictEqual(
loader.master.playlists['placeholder-uri-1'],
loader.master.playlists['1-placeholder-uri-1'],
loader.master.playlists[1], 'set reference by uri for easy access'
);
assert.equal(
loader.master.mediaGroups.AUDIO.audio.main.playlists[0].uri,
'placeholder-uri-AUDIO-audio-main', 'setup phony uri for media groups'
);
assert.equal(
loader.master.mediaGroups.AUDIO.audio.main.playlists[0].id,
'0-placeholder-uri-AUDIO-audio-main', 'setup phony id for media groups'
);
assert.strictEqual(
loader.master.playlists['placeholder-uri-AUDIO-audio-main'],
loader.master.playlists['0-placeholder-uri-AUDIO-audio-main'],
loader.master.mediaGroups.AUDIO.audio.main.playlists[0],
'set reference by uri for easy access'
);
@ -2160,7 +2189,7 @@ QUnit.test('child loaders wait for async action before moving to HAVE_MASTER', f
loader.load();
this.standardXHRResponse(this.requests.shift());
const childPlaylist = loader.master.playlists['placeholder-uri-AUDIO-audio-main'];
const childPlaylist = loader.master.playlists['0-placeholder-uri-AUDIO-audio-main'];
const childLoader = new DashPlaylistLoader(childPlaylist, this.fakeHls, false, loader);
childLoader.load();

1
test/playback.test.js

@ -260,7 +260,6 @@ QUnit[testFn]('DASH sidx', function(assert) {
});
QUnit[testFn]('DASH sidx with alt audio should end', function(assert) {
const done = assert.async();
const player = this.player;

107
test/playlist-loader.test.js

@ -127,6 +127,7 @@ QUnit.test('updateMaster returns null when no change', function(assert) {
BANDWIDTH: 9
},
uri: 'playlist-0-uri',
id: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
duration: 10,
@ -141,6 +142,7 @@ QUnit.test('updateMaster returns null when no change', function(assert) {
BANDWIDTH: 9
},
uri: 'playlist-0-uri',
id: 'playlist-0-uri',
segments: [{
duration: 10,
uri: 'segment-0-uri'
@ -158,6 +160,7 @@ QUnit.test('updateMaster updates master when new media sequence', function(asser
BANDWIDTH: 9
},
uri: 'playlist-0-uri',
id: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
duration: 10,
@ -172,13 +175,14 @@ QUnit.test('updateMaster updates master when new media sequence', function(asser
BANDWIDTH: 9
},
uri: 'playlist-0-uri',
id: 'playlist-0-uri',
segments: [{
duration: 10,
uri: 'segment-0-uri'
}]
};
master.playlists[media.uri] = master.playlists[0];
master.playlists[media.id] = master.playlists[0];
assert.deepEqual(
updateMaster(master, media),
@ -189,6 +193,7 @@ QUnit.test('updateMaster updates master when new media sequence', function(asser
BANDWIDTH: 9
},
uri: 'playlist-0-uri',
id: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
duration: 10,
@ -209,6 +214,7 @@ QUnit.test('updateMaster updates master when endList changes', function(assert)
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -224,6 +230,7 @@ QUnit.test('updateMaster updates master when endList changes', function(assert)
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
segments: [{
duration: 10,
@ -231,7 +238,7 @@ QUnit.test('updateMaster updates master when endList changes', function(assert)
}]
};
master.playlists[media.uri] = master.playlists[0];
master.playlists[media.id] = master.playlists[0];
assert.deepEqual(
updateMaster(master, media),
@ -242,6 +249,7 @@ QUnit.test('updateMaster updates master when endList changes', function(assert)
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -270,6 +278,7 @@ QUnit.test('updateMaster retains top level values in master', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -284,6 +293,7 @@ QUnit.test('updateMaster retains top level values in master', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
segments: [{
duration: 10,
@ -291,7 +301,7 @@ QUnit.test('updateMaster retains top level values in master', function(assert) {
}]
};
master.playlists[media.uri] = master.playlists[0];
master.playlists[media.id] = master.playlists[0];
assert.deepEqual(
updateMaster(master, media),
@ -309,6 +319,7 @@ QUnit.test('updateMaster retains top level values in master', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -337,6 +348,7 @@ QUnit.test('updateMaster adds new segments to master', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -351,6 +363,7 @@ QUnit.test('updateMaster adds new segments to master', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
segments: [{
duration: 10,
@ -361,7 +374,7 @@ QUnit.test('updateMaster adds new segments to master', function(assert) {
}]
};
master.playlists[media.uri] = master.playlists[0];
master.playlists[media.id] = master.playlists[0];
assert.deepEqual(
updateMaster(master, media),
@ -379,6 +392,7 @@ QUnit.test('updateMaster adds new segments to master', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -411,6 +425,7 @@ QUnit.test('updateMaster changes old values', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -426,6 +441,7 @@ QUnit.test('updateMaster changes old values', function(assert) {
BANDWIDTH: 8,
newField: 1
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
segments: [{
duration: 8,
@ -436,7 +452,7 @@ QUnit.test('updateMaster changes old values', function(assert) {
}]
};
master.playlists[media.uri] = master.playlists[0];
master.playlists[media.id] = master.playlists[0];
assert.deepEqual(
updateMaster(master, media),
@ -455,6 +471,7 @@ QUnit.test('updateMaster changes old values', function(assert) {
BANDWIDTH: 8,
newField: 1
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -476,6 +493,7 @@ QUnit.test('updateMaster retains saved segment values', function(assert) {
const master = {
playlists: [{
mediaSequence: 0,
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -489,6 +507,7 @@ QUnit.test('updateMaster retains saved segment values', function(assert) {
};
const media = {
mediaSequence: 0,
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
segments: [{
duration: 8,
@ -499,13 +518,14 @@ QUnit.test('updateMaster retains saved segment values', function(assert) {
}]
};
master.playlists[media.uri] = master.playlists[0];
master.playlists[media.id] = master.playlists[0];
assert.deepEqual(
updateMaster(master, media),
{
playlists: [{
mediaSequence: 0,
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -532,6 +552,7 @@ QUnit.test('updateMaster resolves key and map URIs', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
@ -550,6 +571,7 @@ QUnit.test('updateMaster resolves key and map URIs', function(assert) {
attributes: {
BANDWIDTH: 9
},
id: 'playlist-0-uri',
uri: 'playlist-0-uri',
segments: [{
duration: 9,
@ -572,7 +594,7 @@ QUnit.test('updateMaster resolves key and map URIs', function(assert) {
}]
};
master.playlists[media.uri] = master.playlists[0];
master.playlists[media.id] = master.playlists[0];
assert.deepEqual(
updateMaster(master, media),
@ -583,6 +605,7 @@ QUnit.test('updateMaster resolves key and map URIs', function(assert) {
BANDWIDTH: 9
},
uri: 'playlist-0-uri',
id: 'playlist-0-uri',
resolvedUri: urlTo('playlist-0-uri'),
segments: [{
duration: 9,
@ -640,21 +663,21 @@ QUnit.test('setupMediaPlaylists adds URI keys for each playlist', function(asser
attributes: {},
resolvedUri: urlTo('uri-0'),
uri: 'uri-0',
id: 0
id: '0-uri-0'
};
const expectedPlaylist1 = {
attributes: {},
resolvedUri: urlTo('uri-1'),
uri: 'uri-1',
id: 1
id: '1-uri-1'
};
setupMediaPlaylists(master);
assert.deepEqual(master.playlists[0], expectedPlaylist0, 'retained playlist indices');
assert.deepEqual(master.playlists[1], expectedPlaylist1, 'retained playlist indices');
assert.deepEqual(master.playlists['uri-0'], expectedPlaylist0, 'added playlist key');
assert.deepEqual(master.playlists['uri-1'], expectedPlaylist1, 'added playlist key');
assert.deepEqual(master.playlists['0-uri-0'], expectedPlaylist0, 'added playlist key');
assert.deepEqual(master.playlists['1-uri-1'], expectedPlaylist1, 'added playlist key');
assert.equal(this.env.log.warn.calls, 2, 'logged two warnings');
assert.equal(
@ -735,6 +758,7 @@ QUnit.test('resolveMediaGroupUris resolves media group URIs', function(assert) {
uri: 'master-uri',
playlists: [{
attributes: { BANDWIDTH: 10 },
id: 'playlist-0',
uri: 'playlist-0'
}],
mediaGroups: {
@ -794,6 +818,7 @@ QUnit.test('resolveMediaGroupUris resolves media group URIs', function(assert) {
uri: 'master-uri',
playlists: [{
attributes: { BANDWIDTH: 10 },
id: 'playlist-0',
uri: 'playlist-0'
}],
mediaGroups: {
@ -1558,7 +1583,7 @@ QUnit.test('clears the update timeout when switching quality', function(assert)
'low-0.ts\n'
);
// change to a higher quality playlist
loader.media('live-high.m3u8');
loader.media('1-live-high.m3u8');
this.requests.pop().respond(
200, null,
'#EXTM3U\n' +
@ -1676,7 +1701,7 @@ QUnit.test(
loader.load();
loader.on('loadedplaylist', function() {
loader.media('high.m3u8');
loader.media('1-high.m3u8');
});
this.requests.pop().respond(
200, null,
@ -1690,7 +1715,7 @@ QUnit.test(
}
);
QUnit.test('can switch media playlists based on URI', function(assert) {
QUnit.test('can switch media playlists based on ID', function(assert) {
const loader = new PlaylistLoader('master.m3u8', this.fakeHls);
loader.load();
@ -1711,7 +1736,7 @@ QUnit.test('can switch media playlists based on URI', function(assert) {
'low-0.ts\n'
);
loader.media('high.m3u8');
loader.media('1-high.m3u8');
assert.strictEqual(loader.state, 'SWITCHING_MEDIA', 'updated the state');
this.requests.pop().respond(
@ -1750,7 +1775,7 @@ QUnit.test('aborts in-flight playlist refreshes when switching', function(assert
'low-0.ts\n'
);
this.clock.tick(10 * 1000);
loader.media('high.m3u8');
loader.media('1-high.m3u8');
assert.strictEqual(this.requests[0].aborted, true, 'aborted refresh request');
assert.ok(
!this.requests[0].onreadystatechange,
@ -1784,7 +1809,7 @@ QUnit.test('switching to the active playlist is a no-op', function(assert) {
'low-0.ts\n' +
'#EXT-X-ENDLIST\n'
);
loader.media('low.m3u8');
loader.media('0-low.m3u8');
assert.strictEqual(this.requests.length, 0, 'no requests are sent');
});
@ -1809,7 +1834,7 @@ QUnit.test('switching to the active live playlist is a no-op', function(assert)
'#EXTINF:10,\n' +
'low-0.ts\n'
);
loader.media('low.m3u8');
loader.media('0-low.m3u8');
assert.strictEqual(this.requests.length, 0, 'no requests are sent');
});
@ -1837,7 +1862,7 @@ QUnit.test(
'low-0.ts\n' +
'#EXT-X-ENDLIST\n'
);
loader.media('high.m3u8');
loader.media('1-high.m3u8');
this.requests.pop().respond(
200, null,
'#EXTM3U\n' +
@ -1846,7 +1871,7 @@ QUnit.test(
'high-0.ts\n' +
'#EXT-X-ENDLIST\n'
);
loader.media('low.m3u8');
loader.media('0-low.m3u8');
assert.strictEqual(this.requests.length, 0, 'no outstanding requests');
assert.strictEqual(loader.state, 'HAVE_METADATA', 'returned to loaded playlist');
@ -1876,8 +1901,8 @@ QUnit.test(
'low-0.ts\n' +
'#EXT-X-ENDLIST\n'
);
loader.media('high.m3u8');
loader.media('low.m3u8');
loader.media('1-high.m3u8');
loader.media('0-low.m3u8');
assert.strictEqual(
this.requests.length,
@ -1928,8 +1953,8 @@ QUnit.test(
'low-0.ts\n' +
'#EXT-X-ENDLIST\n'
);
loader.media('high.m3u8');
loader.media('high.m3u8');
loader.media('1-high.m3u8');
loader.media('1-high.m3u8');
assert.strictEqual(this.requests.length, 1, 'made only one request');
assert.ok(!this.requests[0].aborted, 'request not aborted');
@ -1942,7 +1967,7 @@ QUnit.test('throws an error if a media switch is initiated too early', function(
loader.load();
assert.throws(function() {
loader.media('high.m3u8');
loader.media('1-high.m3u8');
}, 'threw an error from HAVE_NOTHING');
this.requests.pop().respond(
@ -2076,7 +2101,7 @@ QUnit.test('triggers an event when the active media changes', function(assert) {
assert.strictEqual(loadedPlaylists, 2, 'two loadedplaylists');
assert.strictEqual(loadedMetadata, 1, 'fired loadedMetadata');
loader.media('high.m3u8');
loader.media('1-high.m3u8');
assert.strictEqual(mediaChangings, 1, 'mediachanging fires immediately');
assert.strictEqual(mediaChanges, 0, 'mediachange does not fire immediately');
assert.strictEqual(loadedPlaylists, 2, 'still two loadedplaylists');
@ -2096,7 +2121,7 @@ QUnit.test('triggers an event when the active media changes', function(assert) {
assert.strictEqual(loadedMetadata, 1, 'still one loadedmetadata');
// switch back to an already loaded playlist
loader.media('low.m3u8');
loader.media('0-low.m3u8');
assert.strictEqual(this.requests.length, 0, 'no requests made');
assert.strictEqual(mediaChangings, 2, 'mediachanging fires');
assert.strictEqual(mediaChanges, 2, 'fired a mediachange');
@ -2104,7 +2129,7 @@ QUnit.test('triggers an event when the active media changes', function(assert) {
assert.strictEqual(loadedMetadata, 1, 'still one loadedmetadata');
// trigger a no-op switch
loader.media('low.m3u8');
loader.media('0-low.m3u8');
assert.strictEqual(this.requests.length, 0, 'no requests made');
assert.strictEqual(mediaChangings, 2, 'mediachanging ignored the no-op');
assert.strictEqual(mediaChanges, 2, 'ignored a no-op media change');
@ -2131,3 +2156,29 @@ QUnit.test(
assert.ok(loader.media().endList, 'flushed the final line of input');
}
);
QUnit.test('Supports multiple STREAM-INF with the same URI', function(assert) {
const loader = new PlaylistLoader('master.m3u8', this.fakeHls);
loader.load();
this.requests.shift().respond(
200, null,
'#EXTM3U\n' +
'#EXT-X-STREAM-INF:BANDWIDTH=1,AUDIO="aud0"\n' +
'video/media.m3u8\n' +
'#EXT-X-STREAM-INF:BANDWIDTH=2,AUDIO="aud1"\n' +
'video/media.m3u8\n'
);
assert.equal(
loader.master.playlists['0-video/media.m3u8'].id,
loader.master.playlists[0].id,
'created key based on playlist id'
);
assert.equal(
loader.master.playlists['1-video/media.m3u8'].id,
loader.master.playlists[1].id,
'created key based on playlist id'
);
});

14
test/rendition-mixin.test.js

@ -1,4 +1,5 @@
import QUnit from 'qunit';
import { createPlaylistID } from '../src/playlist-loader.js';
import RenditionMixin from '../src/rendition-mixin.js';
import videojs from 'video.js';
@ -64,7 +65,10 @@ const makeMockHlsHandler = function(playlistOptions, handlerOptions) {
hlsHandler.playlists.master.playlists[i] = makeMockPlaylist(playlist);
if (playlist.uri) {
hlsHandler.playlists.master.playlists[playlist.uri] =
const id = createPlaylistID(i, playlist.uri);
hlsHandler.playlists.master.playlists[i].id = id;
hlsHandler.playlists.master.playlists[id] =
hlsHandler.playlists.master.playlists[i];
}
});
@ -185,10 +189,10 @@ QUnit.test(
const renditions = hlsHandler.representations();
assert.equal(renditions.length, 4, 'incompatible rendition not added');
assert.equal(renditions[0].id, 'media1.m3u8', 'rendition is enabled');
assert.equal(renditions[1].id, 'media2.m3u8', 'rendition is enabled');
assert.equal(renditions[2].id, 'media3.m3u8', 'rendition is enabled');
assert.equal(renditions[3].id, 'media4.m3u8', 'rendition is enabled');
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');
}
);

3
test/test-helpers.js

@ -427,6 +427,9 @@ export const playlistWithDuration = function(time, conf) {
conf && conf.discontinuitySequence ? conf.discontinuitySequence : 0,
attributes: conf && typeof conf.attributes !== 'undefined' ? conf.attributes : {}
};
result.id = result.uri;
const count = Math.floor(time / 10);
const remainder = time % 10;
let i;

38
test/videojs-http-streaming.test.js

@ -18,6 +18,7 @@ import {
requestAndAppendSegment,
disposePlaybackWatcher
} from './test-helpers.js';
import { createPlaylistID } from '../src/playlist-loader.js';
/* eslint-disable no-unused-vars */
// we need this so that it can register hls with videojs
import {
@ -1557,6 +1558,7 @@ QUnit.test('segment 404 should trigger blacklisting of media', function(assert)
QUnit.test('playlist 404 should blacklist media', function(assert) {
let media;
let url;
let index;
let blacklistplaylist = 0;
let retryplaylist = 0;
let hlsRenditionBlacklistedEvents = 0;
@ -1602,7 +1604,13 @@ QUnit.test('playlist 404 should blacklist media', function(assert) {
// media
this.requests[1].respond(404);
url = this.requests[1].url.slice(this.requests[1].url.lastIndexOf('/') + 1);
media = this.player.tech_.hls.playlists.master.playlists[url];
if (url === 'media.m3u8') {
index = 0;
} else {
index = 1;
}
media = this.player.tech_.hls.playlists.master.playlists[createPlaylistID(index, url)];
assert.ok(media.excludeUntil > 0, 'original media blacklisted for some time');
assert.equal(this.env.log.warn.calls, 1, 'warning logged for blacklist');
@ -1622,7 +1630,13 @@ QUnit.test('playlist 404 should blacklist media', function(assert) {
// request for the final available media
this.requests[2].respond(404);
url = this.requests[2].url.slice(this.requests[2].url.lastIndexOf('/') + 1);
media = this.player.tech_.hls.playlists.master.playlists[url];
if (url === 'media.m3u8') {
index = 0;
} else {
index = 1;
}
media = this.player.tech_.hls.playlists.master.playlists[createPlaylistID(index, url)];
assert.ok(media.excludeUntil > 0, 'second media was blacklisted after playlist 404');
assert.equal(this.env.log.warn.calls, 2, 'warning logged for blacklist');
@ -1650,7 +1664,12 @@ QUnit.test('playlist 404 should blacklist media', function(assert) {
assert.strictEqual(4, this.requests.length, 'one more request was made');
url = this.requests[3].url.slice(this.requests[3].url.lastIndexOf('/') + 1);
media = this.player.tech_.hls.playlists.master.playlists[url];
if (url === 'media.m3u8') {
index = 0;
} else {
index = 1;
}
media = this.player.tech_.hls.playlists.master.playlists[createPlaylistID(index, url)];
// the first media was unblacklisted after a refresh delay
assert.ok(!media.excludeUntil, 'removed first media from blacklist');
@ -1781,7 +1800,7 @@ QUnit.test(
this.requests[1].respond(404);
const url = this.requests[1].url.slice(this.requests[1].url.lastIndexOf('/') + 1);
const media = this.player.tech_.hls.playlists.master.playlists[url];
const media = this.player.tech_.hls.playlists.master.playlists[createPlaylistID(0, url)];
// media wasn't blacklisted because it's the only rendition
assert.ok(!media.excludeUntil, 'media was not blacklisted after playlist 404');
@ -1832,7 +1851,7 @@ QUnit.test('fire loadedmetadata once we successfully load a playlist', function(
hls.masterPlaylistController_.masterPlaylistLoader_.on('loadedmetadata', function() {
count += 1;
});
// master
// masters
this.standardXHRResponse(this.requests.shift());
assert.equal(
count, 0,
@ -2290,7 +2309,14 @@ QUnit.test('playlist blacklisting duration is set through options', function(ass
this.requests[1].respond(404);
// media
const url = this.requests[1].url.slice(this.requests[1].url.lastIndexOf('/') + 1);
const media = this.player.tech_.hls.playlists.master.playlists[url];
let index;
if (url === 'media.m3u8') {
index = 0;
} else {
index = 1;
}
const media = this.player.tech_.hls.playlists.master.playlists[createPlaylistID(index, url)];
assert.ok(media.excludeUntil > 0, 'original media blacklisted for some time');
assert.equal(this.env.log.warn.calls, 1, 'warning logged for blacklist');

Loading…
Cancel
Save