mirror of
https://github.com/videojs/video.js.git
synced 2024-11-28 08:58:46 +02:00
@imbcmdth added sourceOrder option for source-first ordering in selectSource. closes #2847
This commit is contained in:
parent
8acd28c15a
commit
69b89e51f4
@ -3,6 +3,7 @@ CHANGELOG
|
||||
|
||||
## HEAD (Unreleased)
|
||||
* @forbesjo updated formatTime to not go negative ([view](https://github.com/videojs/video.js/pull/2821))
|
||||
* @imbcmdth added sourceOrder option for source-first ordering in selectSource ([view](https://github.com/videojs/video.js/pull/2847))
|
||||
|
||||
--------------------
|
||||
|
||||
|
@ -49,6 +49,40 @@ When adding additional Tech to a video player, make sure to add the supported te
|
||||
techOrder: ["html5", "flash", "other supported tech"]
|
||||
});
|
||||
|
||||
Technology Ordering
|
||||
==================
|
||||
By default Video.js performs "Tech-first" ordering when it searches for a source/tech combination to play videos. This means that if you have two sources and two techs, video.js will try to play each video with the first tech in the `techOrder` option property before moving on to try the next playback technology.
|
||||
|
||||
Tech-first ordering can present a problem if you have a `sourceHandler` that supports both `Html5` and `Flash` techs such as videojs-contrib-hls.
|
||||
|
||||
For example, given the following video element:
|
||||
|
||||
<video data-setup='{"techOrder": ["html5", "flash"]}'>
|
||||
<source src="http://your.static.provider.net/path/to/video.m3u8" type="application/x-mpegURL">
|
||||
<source src="http://your.static.provider.net/path/to/video.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
There is a good chance that the mp4 source will be selected on platforms that do not have media source extensions. Video.js will try all sources against the first playback technology, in this case `Html5`, and select the first source that can play - in this case MP4.
|
||||
|
||||
In "Tech-first" mode, the tests run something like this:
|
||||
Can video.m3u8 play with Html5? No...
|
||||
Can video.mp4 play with Html5? Yes! Use the second source.
|
||||
|
||||
Video.js now provides another method of selecting the source - "Source-first" ordering. In this mode, Video.js tries the first source against every tech in `techOrder` before moving onto the next source.
|
||||
|
||||
With a player setup as follows:
|
||||
|
||||
<video data-setup='{"techOrder": ["html5", "flash"], "sourceOrder": true}'>
|
||||
<source src="http://your.static.provider.net/path/to/video.m3u8" type="application/x-mpegURL">
|
||||
<source src="http://your.static.provider.net/path/to/video.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
The Flash-based HLS support will be tried before falling back to the MP4 source.
|
||||
|
||||
In "Source-first" mode, the tests run something like this:
|
||||
Can video.m3u8 play with Html5? No...
|
||||
Can video.m3u8 play with Flash? Yes! Use the first source.
|
||||
|
||||
Flash Technology
|
||||
==================
|
||||
The Flash playback tech is a part of the default `techOrder`. You may notice undesirable playback behavior in browsers that are subject to using this playback tech, in particular when scrubbing and seeking within a video. This behavior is a result of Flash's progressive video playback.
|
||||
|
@ -1693,43 +1693,75 @@ class Player extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* Select source based on tech order
|
||||
* Select source based on tech-order or source-order
|
||||
* Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
|
||||
* defaults to tech-order selection
|
||||
*
|
||||
* @param {Array} sources The sources for a media asset
|
||||
* @return {Object|Boolean} Object of source and tech order, otherwise false
|
||||
* @method selectSource
|
||||
*/
|
||||
selectSource(sources) {
|
||||
// Loop through each playback technology in the options order
|
||||
for (var i=0,j=this.options_.techOrder;i<j.length;i++) {
|
||||
let techName = toTitleCase(j[i]);
|
||||
let tech = Tech.getTech(techName);
|
||||
// Support old behavior of techs being registered as components.
|
||||
// Remove once that deprecated behavior is removed.
|
||||
if (!tech) {
|
||||
tech = Component.getComponent(techName);
|
||||
}
|
||||
// Check if the current tech is defined before continuing
|
||||
if (!tech) {
|
||||
log.error(`The "${techName}" tech is undefined. Skipped browser support check for that tech.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the browser supports this technology
|
||||
if (tech.isSupported()) {
|
||||
// Loop through each source object
|
||||
for (var a=0,b=sources;a<b.length;a++) {
|
||||
var source = b[a];
|
||||
|
||||
// Check if source can be played with this technology
|
||||
if (tech.canPlaySource(source)) {
|
||||
return { source: source, tech: techName };
|
||||
// Get only the techs specified in `techOrder` that exist and are supported by the
|
||||
// current platform
|
||||
let techs =
|
||||
this.options_.techOrder
|
||||
.map(toTitleCase)
|
||||
.map((techName) => {
|
||||
// `Component.getComponent(...)` is for support of old behavior of techs
|
||||
// being registered as components.
|
||||
// Remove once that deprecated behavior is removed.
|
||||
return [techName, Tech.getTech(techName) || Component.getComponent(techName)];
|
||||
})
|
||||
.filter(([techName, tech]) => {
|
||||
// Check if the current tech is defined before continuing
|
||||
if (tech) {
|
||||
// Check if the browser supports this technology
|
||||
return tech.isSupported();
|
||||
}
|
||||
}
|
||||
|
||||
log.error(`The "${techName}" tech is undefined. Skipped browser support check for that tech.`);
|
||||
return false;
|
||||
});
|
||||
|
||||
// Iterate over each `innerArray` element once per `outerArray` element and execute
|
||||
// `tester` with both. If `tester` returns a non-falsy value, exit early and return
|
||||
// that value.
|
||||
let findFirstPassingTechSourcePair = function (outerArray, innerArray, tester) {
|
||||
let found;
|
||||
|
||||
outerArray.some((outerChoice) => {
|
||||
return innerArray.some((innerChoice) => {
|
||||
found = tester(outerChoice, innerChoice);
|
||||
|
||||
if (found) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return found;
|
||||
};
|
||||
|
||||
let foundSourceAndTech;
|
||||
let flip = (fn) => (a, b) => fn(b, a);
|
||||
let finder = ([techName, tech], source) => {
|
||||
if (tech.canPlaySource(source)) {
|
||||
return {source: source, tech: techName};
|
||||
}
|
||||
};
|
||||
|
||||
// Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
|
||||
// to select from them based on their priority.
|
||||
if (this.options_.sourceOrder) {
|
||||
// Source-first ordering
|
||||
foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
|
||||
} else {
|
||||
// Tech-first ordering
|
||||
foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
|
||||
}
|
||||
|
||||
return false;
|
||||
return foundSourceAndTech || false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,6 +7,8 @@ import MediaError from '../../src/js/media-error.js';
|
||||
import Html5 from '../../src/js/tech/html5.js';
|
||||
import TestHelpers from './test-helpers.js';
|
||||
import document from 'global/document';
|
||||
import Tech from '../../src/js/tech/tech.js';
|
||||
import TechFaker from './tech/tech-faker.js';
|
||||
|
||||
q.module('Player', {
|
||||
'setup': function() {
|
||||
@ -408,6 +410,43 @@ test('make sure that controls listeners do not get added too many times', functi
|
||||
player.dispose();
|
||||
});
|
||||
|
||||
test('should select the proper tech based on the the sourceOrder option',
|
||||
function() {
|
||||
let fixture = document.getElementById('qunit-fixture');
|
||||
let html =
|
||||
'<video id="example_1">' +
|
||||
'<source src="fake.foo1" type="video/unsupported-format">' +
|
||||
'<source src="fake.foo2" type="video/foo-format">' +
|
||||
'</video>';
|
||||
|
||||
// Extend TechFaker to create a tech that plays the only mime-type that TechFaker
|
||||
// will not play
|
||||
class PlaysUnsupported extends TechFaker {
|
||||
constructor(options, handleReady){
|
||||
super(options, handleReady);
|
||||
}
|
||||
// Support ONLY "video/unsupported-format"
|
||||
static isSupported() { return true; }
|
||||
static canPlayType(type) { return (type === 'video/unsupported-format' ? 'maybe' : ''); }
|
||||
static canPlaySource(srcObj) { return srcObj.type === 'video/unsupported-format'; }
|
||||
}
|
||||
Tech.registerTech('PlaysUnsupported', PlaysUnsupported);
|
||||
|
||||
fixture.innerHTML += html;
|
||||
let tag = document.getElementById('example_1');
|
||||
|
||||
let player = new Player(tag, { techOrder: ['techFaker', 'playsUnsupported'], sourceOrder: true });
|
||||
equal(player.techName_, 'PlaysUnsupported', 'selected the PlaysUnsupported tech when sourceOrder is truthy');
|
||||
player.dispose();
|
||||
|
||||
fixture.innerHTML += html;
|
||||
tag = document.getElementById('example_1');
|
||||
|
||||
player = new Player(tag, { techOrder: ['techFaker', 'playsUnsupported']});
|
||||
equal(player.techName_, 'TechFaker', 'selected the TechFaker tech when sourceOrder is falsey');
|
||||
player.dispose();
|
||||
});
|
||||
|
||||
test('should register players with generated ids', function(){
|
||||
var fixture, video, player, id;
|
||||
fixture = document.getElementById('qunit-fixture');
|
||||
|
Loading…
Reference in New Issue
Block a user