Skip to content

Commit

Permalink
feat: update the players source cache on sourceset from (#5040) as (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonocasey authored May 9, 2018
1 parent 0f1268e commit 72f84d5
Show file tree
Hide file tree
Showing 4 changed files with 370 additions and 170 deletions.
104 changes: 97 additions & 7 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -2473,23 +2560,29 @@ 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) {
if (sources.length > 1) {
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) });
Expand All @@ -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_);
});
}
Expand Down Expand Up @@ -2552,6 +2641,7 @@ class Player extends Component {
this.techCall_('src', source.src);
}

this.changingSrc_ = false;
}, true);

return false;
Expand Down
6 changes: 2 additions & 4 deletions src/js/utils/filter-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
63 changes: 63 additions & 0 deletions src/js/utils/mimetypes.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as Url from '../utils/url.js';

/**
* Mimetypes
*
Expand All @@ -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);
};
Loading

0 comments on commit 72f84d5

Please sign in to comment.