diff --git a/src/js/player.js b/src/js/player.js index 4ba5961a35..4997ebac76 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -31,6 +31,7 @@ import Tech from './tech/tech.js'; import * as middleware from './tech/middleware.js'; import {ALL as TRACK_TYPES} from './tracks/track-types'; import filterSource from './utils/filter-source'; +import {findMimetype} from './utils/mimetypes'; // The following imports are used only to ensure that the corresponding modules // are always included in the video.js package. Importing the modules will @@ -1198,6 +1199,68 @@ class Player extends Component { } } + /** + * Update the internal source caches so that we return the correct source from + * `src()`, `currentSource()`, and `currentSources()`. + * + * > Note: `currentSources` will not be updated if the source that is passed in exists + * in the current `currentSources` cache. + * + * + * @param {Tech~SourceObject} srcObj + * A string or object source to update our caches to. + */ + updateSourceCaches_(srcObj = '') { + + let src = srcObj; + let type = ''; + + if (typeof src !== 'string') { + src = srcObj.src; + type = srcObj.type; + } + // make sure all the caches are set to default values + // to prevent null checking + this.cache_.source = this.cache_.source || {}; + this.cache_.sources = this.cache_.sources || []; + + // try to get the type of the src that was passed in + if (src && !type) { + type = findMimetype(this, src); + } + + // update `currentSource` cache always + this.cache_.source = {src, type}; + + const matchingSources = this.cache_.sources.filter((s) => s.src && s.src === src); + const sourceElSources = []; + const sourceEls = this.$$('source'); + const matchingSourceEls = []; + + for (let i = 0; i < sourceEls.length; i++) { + const sourceObj = Dom.getAttributes(sourceEls[i]); + + sourceElSources.push(sourceObj); + + if (sourceObj.src && sourceObj.src === src) { + matchingSourceEls.push(sourceObj.src); + } + } + + // if we have matching source els but not matching sources + // the current source cache is not up to date + if (matchingSourceEls.length && !matchingSources.length) { + this.cache_.sources = sourceElSources; + // if we don't have matching source or source els set the + // sources cache to the `currentSource` cache + } else if (!matchingSources.length) { + this.cache_.sources = [this.cache_.source]; + } + + // update the tech `src` cache + this.cache_.src = src; + } + /** * *EXPERIMENTAL* Fired when the source is set or changed on the {@link Tech} * causing the media element to reload. @@ -1234,6 +1297,30 @@ class Player extends Component { * @private */ handleTechSourceset_(event) { + // only update the source cache when the source + // was not updated using the player api + if (!this.changingSrc_) { + // update the source to the intial source right away + // in some cases this will be empty string + this.updateSourceCaches_(event.src); + + // if the `sourceset` `src` was an empty string + // wait for a `loadstart` to update the cache to `currentSrc`. + // If a sourceset happens before a `loadstart`, we reset the state + // as this function will be called again. + if (!event.src) { + const updateCache = (e) => { + if (e.type !== 'sourceset') { + this.updateSourceCaches_(this.techGet_('currentSrc')); + } + + this.tech_.off(['sourceset', 'loadstart'], updateCache); + }; + + this.tech_.one(['sourceset', 'loadstart'], updateCache); + } + } + this.trigger({ src: event.src, type: 'sourceset' @@ -2473,16 +2560,20 @@ class Player extends Component { } // intial sources - this.cache_.sources = sources; this.changingSrc_ = true; - // intial source - this.cache_.source = sources[0]; + this.cache_.sources = sources; + this.updateSourceCaches_(sources[0]); // middlewareSource is the source after it has been changed by middleware middleware.setSource(this, sources[0], (middlewareSource, mws) => { this.middleware_ = mws; + // since sourceSet is async we have to update the cache again after we select a source since + // the source that is selected could be out of order from the cache update above this callback. + this.cache_.sources = sources; + this.updateSourceCaches_(middlewareSource); + const err = this.src_(middlewareSource); if (err) { @@ -2490,6 +2581,8 @@ class Player extends Component { return this.src(sources.slice(1)); } + this.changingSrc_ = false; + // We need to wrap this in a timeout to give folks a chance to add error event handlers this.setTimeout(function() { this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) }); @@ -2502,10 +2595,6 @@ class Player extends Component { return; } - this.changingSrc_ = false; - // video element listed source - this.cache_.src = middlewareSource.src; - middleware.setTech(mws, this.tech_); }); } @@ -2552,6 +2641,7 @@ class Player extends Component { this.techCall_('src', source.src); } + this.changingSrc_ = false; }, true); return false; diff --git a/src/js/utils/filter-source.js b/src/js/utils/filter-source.js index b5ede4ab3c..3b5c665485 100644 --- a/src/js/utils/filter-source.js +++ b/src/js/utils/filter-source.js @@ -2,8 +2,7 @@ * @module filter-source */ import {isObject} from './obj'; -import {MimetypesKind} from './mimetypes'; -import * as Url from '../utils/url.js'; +import {getMimetype} from './mimetypes'; /** * Filter out single bad source objects or multiple source objects in an @@ -57,8 +56,7 @@ const filterSource = function(src) { * src Object with known type */ function checkMimetype(src) { - const ext = Url.getFileExtension(src.src); - const mimetype = MimetypesKind[ext.toLowerCase()]; + const mimetype = getMimetype(src.src); if (!src.type && mimetype) { src.type = mimetype; diff --git a/src/js/utils/mimetypes.js b/src/js/utils/mimetypes.js index 9a44c2d267..cb4f88c75f 100644 --- a/src/js/utils/mimetypes.js +++ b/src/js/utils/mimetypes.js @@ -1,3 +1,5 @@ +import * as Url from '../utils/url.js'; + /** * Mimetypes * @@ -17,3 +19,64 @@ export const MimetypesKind = { oga: 'audio/ogg', m3u8: 'application/x-mpegURL' }; + +/** + * Get the mimetype of a given src url if possible + * + * @param {string} src + * The url to the src + * + * @return {string} + * return the mimetype if it was known or empty string otherwise + */ +export const getMimetype = function(src = '') { + const ext = Url.getFileExtension(src); + const mimetype = MimetypesKind[ext.toLowerCase()]; + + return mimetype || ''; +}; + +/** + * Find the mime type of a given source string if possible. Uses the player + * source cache. + * + * @param {Player} player + * The player object + * + * @param {string} src + * The source string + * + * @return {string} + * The type that was found + */ +export const findMimetype = (player, src) => { + if (!src) { + return ''; + } + + // 1. check for the type in the `source` cache + if (player.cache_.source.src === src && player.cache_.source.type) { + return player.cache_.source.type; + } + + // 2. see if we have this source in our `currentSources` cache + const matchingSources = player.cache_.sources.filter((s) => s.src === src); + + if (matchingSources.length) { + return matchingSources[0].type; + } + + // 3. look for the src url in source elements and use the type there + const sources = player.$$('source'); + + for (let i = 0; i < sources.length; i++) { + const s = sources[i]; + + if (s.type && s.src && s.src === src) { + return s.type; + } + } + + // 4. finally fallback to our list of mime types based on src url extension + return getMimetype(src); +}; diff --git a/test/unit/sourceset.test.js b/test/unit/sourceset.test.js index 5096edec91..cc56ed3d19 100644 --- a/test/unit/sourceset.test.js +++ b/test/unit/sourceset.test.js @@ -5,6 +5,7 @@ import window from 'global/window'; import log from '../../src/js/utils/log.js'; import sinon from 'sinon'; import * as browser from '../../src/js/utils/browser.js'; +import {getAbsoluteURL} from '../../src/js/utils/url.js'; const Html5 = videojs.getTech('Html5'); const wait = 1; @@ -15,24 +16,60 @@ const testSrc = { }; const sourceOne = {src: 'http://example.com/one.mp4', type: 'video/mp4'}; const sourceTwo = {src: 'http://example.com/two.mp4', type: 'video/mp4'}; +const sourceThree = {src: 'http://example.com/three.mp4', type: 'video/mp4'}; if (!Html5.canOverrideAttributes()) { qunitFn = 'skip'; } const oldMovingMedia = Html5.prototype.movingMediaElementInDOM; -const validateSource = function(assert, player, sources, checkMediaElSource = true) { - const tech = player.tech_; - const mediaEl = tech.el(); - - if (checkMediaElSource) { - assert.equal(mediaEl.src, sources[0].src, 'mediaEl.src is correct'); - assert.equal(mediaEl.getAttribute('src'), sources[0].src, 'mediaEl attribute is correct'); - assert.equal(tech.src(), sources[0].src, 'tech is correct'); +const validateSource = function(player, expectedSources, event, srcOverrides = {}) { + expectedSources = Array.isArray(expectedSources) ? expectedSources : [expectedSources]; + const mediaEl = player.tech_.el(); + const assert = QUnit.assert; + const expected = { + // player cache checks + currentSources: expectedSources, currentSource: expectedSources[0], src: expectedSources[0].src, + // tech checks + event: expectedSources[0].src, attr: expectedSources[0].src, prop: expectedSources[0].src + }; + + Object.keys(srcOverrides).forEach((k) => { + // only override known properties + if (!expected.hasOwnProperty(k)) { + return; + } + + expected[k] = srcOverrides[k]; + }); + + assert.deepEqual(player.currentSource(), expected.currentSource, 'player.currentSource() is correct'); + assert.deepEqual(player.currentSources(), expected.currentSources, 'player.currentSources() is correct'); + assert.equal(player.src(), expected.src, 'player.src() is correct'); + + assert.equal(event.src, expected.event, 'event src is correct'); + + // if we expect a blank attr it will be null instead + assert.equal(mediaEl.getAttribute('src'), expected.attr || null, 'mediaEl attribute is correct'); + + // mediaEl.src source is always absolute, but can be empty string + // getAbsoluteURL would return the current url of the page for empty string + // so we have to check + expected.prop = expected.prop ? getAbsoluteURL(expected.prop) : expected.prop; + + // on older browsers mediaEl.src will be null instead of empty string + // so if they are both falsey just set them to the same value so the + // check below passes + if (!mediaEl.src && !expected.prop) { + expected.prop = mediaEl.src; } + + assert.equal(mediaEl.src, expected.prop, 'mediaEl src property is correct'); + }; const setupEnv = function(env, testName) { + sinon.stub(log, 'error'); env.fixture = document.getElementById('qunit-fixture'); if (testName === 'change video el' || testName === 'change audio el') { @@ -48,10 +85,6 @@ const setupEnv = function(env, testName) { } else { env.mediaEl = document.createElement('video'); } - env.testSrc = testSrc; - env.sourceOne = sourceOne; - env.sourceTwo = sourceTwo; - env.mediaEl.className = 'video-js'; env.fixture.appendChild(env.mediaEl); }; @@ -84,7 +117,6 @@ QUnit[qunitFn]('sourceset', function(hooks) { QUnit.module('source before player', (subhooks) => testTypes.forEach((testName) => { QUnit.module(testName, { beforeEach() { - sinon.stub(log, 'error'); setupEnv(this, testName); }, @@ -94,13 +126,13 @@ QUnit[qunitFn]('sourceset', function(hooks) { QUnit.test('data-setup one source', function(assert) { const done = assert.async(); - this.mediaEl.setAttribute('data-setup', JSON.stringify({sources: [this.testSrc]})); + this.mediaEl.setAttribute('data-setup', JSON.stringify({sources: [testSrc]})); this.player = videojs(this.mediaEl, { enableSourceset: true }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [testSrc], e); done(); }); }); @@ -108,14 +140,14 @@ QUnit[qunitFn]('sourceset', function(hooks) { QUnit.test('data-setup preload auto', function(assert) { const done = assert.async(); - this.mediaEl.setAttribute('data-setup', JSON.stringify({sources: [this.testSrc]})); + this.mediaEl.setAttribute('data-setup', JSON.stringify({sources: [testSrc]})); this.mediaEl.setAttribute('preload', 'auto'); this.player = videojs(this.mediaEl, { enableSourceset: true }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [testSrc], e); done(); }); }); @@ -123,13 +155,13 @@ QUnit[qunitFn]('sourceset', function(hooks) { QUnit.test('data-setup two sources', function(assert) { const done = assert.async(); - this.mediaEl.setAttribute('data-setup', JSON.stringify({sources: [this.sourceOne, this.sourceTwo]})); + this.mediaEl.setAttribute('data-setup', JSON.stringify({sources: [sourceOne, sourceTwo]})); this.player = videojs(this.mediaEl, { enableSourceset: true }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceOne, this.sourceTwo]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [sourceOne, sourceTwo], e); done(); }); }); @@ -139,11 +171,11 @@ QUnit[qunitFn]('sourceset', function(hooks) { this.player = videojs(this.mediaEl, { enableSourceset: true, - sources: [this.testSrc] + sources: [testSrc] }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [testSrc], e); done(); }); }); @@ -153,11 +185,11 @@ QUnit[qunitFn]('sourceset', function(hooks) { this.player = videojs(this.mediaEl, { enableSourceset: true, - sources: [this.sourceOne, this.sourceTwo] + sources: [sourceOne, sourceTwo] }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceOne, this.sourceTwo]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [sourceOne, sourceTwo], e); done(); }); }); @@ -165,13 +197,13 @@ QUnit[qunitFn]('sourceset', function(hooks) { QUnit.test('mediaEl.src = ...;', function(assert) { const done = assert.async(); - this.mediaEl.src = this.testSrc.src; + this.mediaEl.src = testSrc.src; this.player = videojs(this.mediaEl, { enableSourceset: true }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [testSrc], e); done(); }); }); @@ -179,13 +211,13 @@ QUnit[qunitFn]('sourceset', function(hooks) { QUnit.test('mediaEl.setAttribute("src", ...)"', function(assert) { const done = assert.async(); - this.mediaEl.setAttribute('src', this.testSrc.src); + this.mediaEl.setAttribute('src', testSrc.src); this.player = videojs(this.mediaEl, { enableSourceset: true }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [testSrc], e); done(); }); }); @@ -194,16 +226,16 @@ QUnit[qunitFn]('sourceset', function(hooks) { const done = assert.async(); this.source = document.createElement('source'); - this.source.src = this.testSrc.src; - this.source.type = this.testSrc.type; + this.source.src = testSrc.src; + this.source.type = testSrc.type; this.mediaEl.appendChild(this.source); this.player = videojs(this.mediaEl, { enableSourceset: true }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [testSrc], e); done(); }); }); @@ -212,12 +244,12 @@ QUnit[qunitFn]('sourceset', function(hooks) { const done = assert.async(); this.source = document.createElement('source'); - this.source.src = this.sourceOne.src; - this.source.type = this.sourceOne.type; + this.source.src = sourceOne.src; + this.source.type = sourceOne.type; this.source2 = document.createElement('source'); - this.source2.src = this.sourceTwo.src; - this.source2.type = this.sourceTwo.type; + this.source2.src = sourceTwo.src; + this.source2.type = sourceTwo.type; this.mediaEl.appendChild(this.source); this.mediaEl.appendChild(this.source2); @@ -226,8 +258,8 @@ QUnit[qunitFn]('sourceset', function(hooks) { enableSourceset: true }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceOne, this.sourceTwo]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [sourceOne, sourceTwo], e); done(); }); }); @@ -251,7 +283,6 @@ QUnit[qunitFn]('sourceset', function(hooks) { QUnit.module('source after player', (subhooks) => testTypes.forEach((testName) => { QUnit.module(testName, { beforeEach() { - sinon.stub(log, 'error'); setupEnv(this, testName); }, @@ -264,12 +295,12 @@ QUnit[qunitFn]('sourceset', function(hooks) { this.player = videojs(this.mediaEl, { enableSourceset: true }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [testSrc], e); done(); }); - this.player.src(this.testSrc); + this.player.src(testSrc); }); QUnit.test('player.src({...}) preload auto', function(assert) { @@ -280,12 +311,12 @@ QUnit[qunitFn]('sourceset', function(hooks) { enableSourceset: true }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [testSrc], e); done(); }); - this.player.src(this.testSrc); + this.player.src(testSrc); }); QUnit.test('player.src({...}) two sources', function(assert) { @@ -295,12 +326,12 @@ QUnit[qunitFn]('sourceset', function(hooks) { enableSourceset: true }); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceOne, this.sourceTwo]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [sourceOne, sourceTwo], e); done(); }); - this.player.src([this.sourceOne, this.sourceTwo]); + this.player.src([sourceOne, sourceTwo]); }); QUnit.test('mediaEl.src = ...;', function(assert) { @@ -309,11 +340,11 @@ QUnit[qunitFn]('sourceset', function(hooks) { this.player = videojs(this.mediaEl, {enableSourceset: true}); this.player.one('sourceset', (e) => { - validateSource(assert, this.player, [this.testSrc]); + validateSource(this.player, [testSrc], e); done(); }); - this.player.tech_.el_.src = this.testSrc.src; + this.player.tech_.el_.src = testSrc.src; }); QUnit.test('mediaEl.setAttribute("src", ...)"', function(assert) { @@ -322,11 +353,11 @@ QUnit[qunitFn]('sourceset', function(hooks) { this.player = videojs(this.mediaEl, {enableSourceset: true}); this.player.one('sourceset', (e) => { - validateSource(assert, this.player, [this.testSrc]); + validateSource(this.player, [testSrc], e); done(); }); - this.player.tech_.el_.setAttribute('src', this.testSrc.src); + this.player.tech_.el_.setAttribute('src', testSrc.src); }); const appendTypes = [ @@ -353,13 +384,13 @@ QUnit[qunitFn]('sourceset', function(hooks) { const done = assert.async(); this.source = document.createElement('source'); - this.source.src = this.testSrc.src; - this.source.type = this.testSrc.type; + this.source.src = testSrc.src; + this.source.type = testSrc.type; this.player = videojs(this.mediaEl, {enableSourceset: true}); this.player.one('sourceset', (e) => { - assert.equal(e.src, this.testSrc.src, 'source is as expected'); + validateSource(this.player, testSrc, e, {prop: '', attr: ''}); done(); }); @@ -373,16 +404,16 @@ QUnit[qunitFn]('sourceset', function(hooks) { this.totalSourcesets = 2; this.source = document.createElement('source'); - this.source.src = this.testSrc.src; - this.source.type = this.testSrc.type; + this.source.src = testSrc.src; + this.source.type = testSrc.type; this.player = videojs(this.mediaEl, {enableSourceset: true}); this.player.one('sourceset', (e1) => { - assert.equal(e1.src, this.testSrc.src, 'event has expected source'); + validateSource(this.player, testSrc, e1, {prop: '', attr: ''}); this.player.one('sourceset', (e2) => { - assert.equal(e2.src, this.testSrc.src, 'second event has expected source'); + validateSource(this.player, testSrc, e2, {prop: '', attr: ''}); done(); }); }); @@ -400,16 +431,16 @@ QUnit[qunitFn]('sourceset', function(hooks) { this.totalSourcesets = 2; this.source = document.createElement('source'); - this.source.src = this.testSrc.src; - this.source.type = this.testSrc.type; + this.source.src = testSrc.src; + this.source.type = testSrc.type; this.player = videojs(this.mediaEl, {enableSourceset: true}); - this.player.one('sourceset', (e) => { - assert.equal(e.src, this.testSrc.src, 'source is as expected'); + this.player.one('sourceset', (e1) => { + validateSource(this.player, testSrc, e1, {prop: '', attr: ''}); this.player.one('sourceset', (e2) => { - validateSource(assert, this.player, [this.sourceOne]); + validateSource(this.player, [sourceOne], e2); done(); }); @@ -420,7 +451,7 @@ QUnit[qunitFn]('sourceset', function(hooks) { appendObj.fn(this.player.tech_.el_, this.source); // should fire an additional sourceset - this.player.tech_.el_.src = this.sourceOne.src; + this.player.tech_.el_.src = sourceOne.src; }); QUnit.test(`one through ${appendObj.name} and then mediaEl.setAttribute`, function(assert) { @@ -428,16 +459,16 @@ QUnit[qunitFn]('sourceset', function(hooks) { this.totalSourcesets = 2; this.source = document.createElement('source'); - this.source.src = this.testSrc.src; - this.source.type = this.testSrc.type; + this.source.src = testSrc.src; + this.source.type = testSrc.type; this.player = videojs(this.mediaEl, {enableSourceset: true}); - this.player.one('sourceset', (e) => { - assert.equal(e.src, this.testSrc.src, 'source is as expected'); + this.player.one('sourceset', (e1) => { + validateSource(this.player, testSrc, e1, {prop: '', attr: ''}); this.player.one('sourceset', (e2) => { - validateSource(assert, this.player, [this.sourceOne]); + validateSource(this.player, [sourceOne], e2); done(); }); @@ -448,25 +479,25 @@ QUnit[qunitFn]('sourceset', function(hooks) { appendObj.fn(this.player.tech_.el_, this.source); // should fire an additional sourceset - this.player.tech_.el_.setAttribute('src', this.sourceOne.src); + this.player.tech_.el_.setAttribute('src', sourceOne.src); }); QUnit.test(`mediaEl.src and then through ${appendObj.name}`, function(assert) { const done = assert.async(); this.source = document.createElement('source'); - this.source.src = this.testSrc.src; - this.source.type = this.testSrc.type; + this.source.src = testSrc.src; + this.source.type = testSrc.type; this.player = videojs(this.mediaEl, {enableSourceset: true}); this.player.one('sourceset', (e) => { - validateSource(assert, this.player, [this.sourceOne]); + validateSource(this.player, [sourceOne], e); done(); }); - this.player.tech_.el_.src = this.sourceOne.src; + this.player.tech_.el_.src = sourceOne.src; // should not fire sourceset appendObj.fn(this.player.tech_.el_, this.source); @@ -476,18 +507,18 @@ QUnit[qunitFn]('sourceset', function(hooks) { const done = assert.async(); this.source = document.createElement('source'); - this.source.src = this.testSrc.src; - this.source.type = this.testSrc.type; + this.source.src = testSrc.src; + this.source.type = testSrc.type; this.player = videojs(this.mediaEl, {enableSourceset: true}); this.player.one('sourceset', (e) => { - validateSource(assert, this.player, [this.sourceOne]); + validateSource(this.player, [sourceOne], e); done(); }); - this.player.tech_.el_.setAttribute('src', this.sourceOne.src); + this.player.tech_.el_.setAttribute('src', sourceOne.src); // should not fire sourceset appendObj.fn(this.player.tech_.el_, this.source); @@ -497,17 +528,17 @@ QUnit[qunitFn]('sourceset', function(hooks) { const done = assert.async(); this.source = document.createElement('source'); - this.source.src = this.sourceOne.src; - this.source.type = this.sourceOne.type; + this.source.src = sourceOne.src; + this.source.type = sourceOne.type; this.source2 = document.createElement('source'); - this.source2.src = this.sourceTwo.src; - this.source2.type = this.sourceTwo.type; + this.source2.src = sourceTwo.src; + this.source2.type = sourceTwo.type; this.player = videojs(this.mediaEl, {enableSourceset: true}); this.player.one('sourceset', (e) => { - assert.equal(e.src, this.sourceOne.src, 'source is as expected'); + validateSource(this.player, sourceOne, e, {prop: '', attr: ''}); done(); }); @@ -524,16 +555,16 @@ QUnit[qunitFn]('sourceset', function(hooks) { this.totalSourcesets = 2; this.source = document.createElement('source'); - this.source.src = this.sourceTwo.src; - this.source.type = this.sourceTwo.type; + this.source.src = sourceTwo.src; + this.source.type = sourceTwo.type; this.player = videojs(this.mediaEl, {enableSourceset: true}); this.player.one('sourceset', (e1) => { - validateSource(assert, this.player, [this.sourceOne]); + validateSource(this.player, [sourceOne], e1); this.player.one('sourceset', (e2) => { - validateSource(assert, this.player, [this.sourceTwo], false); + validateSource(this.player, sourceTwo, e2, {prop: '', attr: ''}); done(); }); @@ -547,7 +578,7 @@ QUnit[qunitFn]('sourceset', function(hooks) { }); - this.player.tech_.el_.setAttribute('src', this.sourceOne.src); + this.player.tech_.el_.setAttribute('src', sourceOne.src); }); }); @@ -569,12 +600,11 @@ QUnit[qunitFn]('sourceset', function(hooks) { QUnit.module('source change', (subhooks) => testTypes.forEach((testName) => { QUnit.module(testName, { beforeEach(assert) { - sinon.stub(log, 'error'); const done = assert.async(); setupEnv(this, testName); - this.mediaEl.src = this.testSrc.src; + this.mediaEl.src = testSrc.src; this.player = videojs(this.mediaEl, { enableSourceset: true }); @@ -584,8 +614,8 @@ QUnit[qunitFn]('sourceset', function(hooks) { }); // intial sourceset should happen on player.ready - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e) => { + validateSource(this.player, [testSrc], e); done(); }); }, @@ -595,109 +625,129 @@ QUnit[qunitFn]('sourceset', function(hooks) { QUnit.test('player.src({...})', function(assert) { const done = assert.async(); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e1) => { + validateSource(this.player, [testSrc], e1); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceOne]); + this.player.one('sourceset', (e2) => { + validateSource(this.player, [sourceOne], e2); done(); }); - this.player.src(this.sourceOne); + this.player.src(sourceOne); }); - this.player.src(this.testSrc); + this.player.src(testSrc); }); QUnit.test('player.src({...}) x2 at the same time', function(assert) { const done = assert.async(); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceOne]); + this.player.one('sourceset', (e1) => { + validateSource(this.player, [sourceOne], e1); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceTwo]); + this.player.one('sourceset', (e2) => { + validateSource(this.player, [sourceTwo], e2); done(); }); }); - this.player.src(this.sourceOne); - this.player.src(this.sourceTwo); + this.player.src(sourceOne); + this.player.src(sourceTwo); + }); + + QUnit.test('player.src({...}) x3 at the same time', function(assert) { + const done = assert.async(); + + // we have one more sourceset then other tests + this.totalSourcesets = 4; + + this.player.one('sourceset', (e1) => { + validateSource(this.player, sourceOne, e1); + + this.player.one('sourceset', (e2) => { + validateSource(this.player, sourceTwo, e2); + + this.player.one('sourceset', (e3) => { + validateSource(this.player, sourceThree, e3); + done(); + }); + }); + }); + + this.player.src(sourceOne); + this.player.src(sourceTwo); + this.player.src(sourceThree); }); QUnit.test('mediaEl.src = ...', function(assert) { const done = assert.async(); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e1) => { + validateSource(this.player, [testSrc], e1); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceOne]); + this.player.one('sourceset', (e2) => { + validateSource(this.player, [sourceOne], e2); done(); }); - this.mediaEl.src = this.sourceOne.src; + this.mediaEl.src = sourceOne.src; }); - this.mediaEl.src = this.testSrc.src; + this.mediaEl.src = testSrc.src; }); QUnit.test('mediaEl.src = ... x2 at the same time', function(assert) { const done = assert.async(); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceOne]); + this.player.one('sourceset', (e1) => { + validateSource(this.player, [sourceOne], e1); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceTwo]); + this.player.one('sourceset', (e2) => { + validateSource(this.player, [sourceTwo], e2); done(); }); }); - this.mediaEl.src = this.sourceOne.src; - this.mediaEl.src = this.sourceTwo.src; + this.mediaEl.src = sourceOne.src; + this.mediaEl.src = sourceTwo.src; }); QUnit.test('mediaEl.setAttribute("src", ...)', function(assert) { const done = assert.async(); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.testSrc]); + this.player.one('sourceset', (e1) => { + validateSource(this.player, [testSrc], e1); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceOne]); + this.player.one('sourceset', (e2) => { + validateSource(this.player, [sourceOne], e2); done(); }); - this.mediaEl.setAttribute('src', this.sourceOne.src); + this.mediaEl.setAttribute('src', sourceOne.src); }); - this.mediaEl.setAttribute('src', this.testSrc.src); + this.mediaEl.setAttribute('src', testSrc.src); }); QUnit.test('mediaEl.setAttribute("src", ...) x2 at the same time', function(assert) { const done = assert.async(); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceOne]); + this.player.one('sourceset', (e1) => { + validateSource(this.player, [sourceOne], e1); - this.player.one('sourceset', () => { - validateSource(assert, this.player, [this.sourceTwo]); + this.player.one('sourceset', (e2) => { + validateSource(this.player, [sourceTwo], e2); done(); }); }); - this.mediaEl.setAttribute('src', this.sourceOne.src); - this.mediaEl.setAttribute('src', this.sourceTwo.src); + this.mediaEl.setAttribute('src', sourceOne.src); + this.mediaEl.setAttribute('src', sourceTwo.src); }); - QUnit.test('load() with a src attribute', function(assert) { + QUnit.test('mediaEl.load() with a src attribute', function(assert) { const done = assert.async(); - this.player = videojs(this.mediaEl, { - enableSourceset: true - }); - this.totalSourcesets = 1; window.setTimeout(() => { @@ -705,8 +755,7 @@ QUnit[qunitFn]('sourceset', function(hooks) { this.totalSourcesets = 1; this.player.one('sourceset', (e) => { - assert.equal(e.src, this.mediaEl.src, "the sourceset event's src matches the src attribute"); - + validateSource(this.player, [testSrc], e); done(); }); @@ -717,22 +766,22 @@ QUnit[qunitFn]('sourceset', function(hooks) { QUnit.test('mediaEl.load()', function(assert) { const source = document.createElement('source'); - source.src = this.testSrc.src; - source.type = this.testSrc.type; + source.src = testSrc.src; + source.type = testSrc.type; // the only way to unset a source, so that we use the source // elements instead this.mediaEl.removeAttribute('src'); this.player.one('sourceset', (e1) => { - assert.equal(e1.src, this.testSrc.src, 'we got a sourceset with the expected src'); + validateSource(this.player, [testSrc], e1, {attr: '', prop: ''}); this.player.one('sourceset', (e2) => { - assert.equal(e2.src, this.sourceOne.src, 'we got a sourceset with the expected src'); + validateSource(this.player, [sourceOne], e2, {attr: '', prop: ''}); }); - source.src = this.sourceOne.src; - source.type = this.sourceOne.type; + source.src = sourceOne.src; + source.type = sourceOne.type; this.mediaEl.load(); }); @@ -744,14 +793,14 @@ QUnit[qunitFn]('sourceset', function(hooks) { QUnit.test('mediaEl.load() x2 at the same time', function(assert) { const source = document.createElement('source'); - source.src = this.sourceOne.src; - source.type = this.sourceOne.type; + source.src = sourceOne.src; + source.type = sourceOne.type; this.player.one('sourceset', (e1) => { - assert.equal(e1.src, this.sourceOne.src, 'we got a sourceset with the expected src'); + validateSource(this.player, [sourceOne], e1, {attr: '', prop: ''}); this.player.one('sourceset', (e2) => { - assert.equal(e2.src, this.sourceTwo.src, 'we got a sourceset with the expected src'); + validateSource(this.player, [sourceTwo], e2, {attr: '', prop: ''}); }); }); @@ -761,8 +810,8 @@ QUnit[qunitFn]('sourceset', function(hooks) { this.mediaEl.appendChild(source); this.mediaEl.load(); - source.src = this.sourceTwo.src; - source.type = this.sourceTwo.type; + source.src = sourceTwo.src; + source.type = sourceTwo.type; this.mediaEl.load(); }); @@ -770,8 +819,8 @@ QUnit[qunitFn]('sourceset', function(hooks) { const done = assert.async(); const source = document.createElement('source'); - source.src = this.testSrc.src; - source.type = this.testSrc.type; + source.src = testSrc.src; + source.type = testSrc.type; this.mediaEl.appendChild(source); @@ -787,12 +836,12 @@ QUnit[qunitFn]('sourceset', function(hooks) { const done = assert.async(); const source = document.createElement('source'); - source.src = this.testSrc.src; - source.type = this.testSrc.type; + source.src = testSrc.src; + source.type = testSrc.type; this.mediaEl.appendChild(source); - source.src = this.testSrc.src; + source.src = testSrc.src; this.totalSourcesets = 1;