Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add canPlayType method to player #2709

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1639,6 +1639,40 @@ class Player extends Component {
this.trigger('exitFullWindow');
}

/**
* Check whether the player can play a given mimetype
*
* @param {String} type The mimetype to check
* @return {String} 'probably', 'maybe', or '' (empty string)
* @method canPlayType
*/
canPlayType(type) {
let can;

// Loop through each playback technology in the options order
for (let i = 0, j = this.options_.techOrder; i < j.length; i++) {
let techName = toTitleCase(j[i]);
let 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()) {
can = tech.canPlayType(type);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should the tech's canPlayType check whether it is supported itself?
I guess the downside is that you won't know whether it can't because it isn't supported or because it can't play that type if you ask the tech directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also kinda copied this from selectSource, to keep them "in sync".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping them in sync might not be a bad idea. Not certain which I like more.
Do you have a strong preference, @nickygerritsen?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No not really, although maybe when we want to add support for #2705 that it would be better to add the isSupported call inside the tech. Otherwise we need to duplicate that again there?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, thinking about it some more, I think I like keeping the isSupported out of canPlayType. Makes canPlayType super simple and do exactly one thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK then an update is incoming in 3...


if (can) {
return can;
}
}
}

return '';
}

/**
* Select source based on tech order
*
Expand Down
23 changes: 21 additions & 2 deletions src/js/tech/flash-rtmp.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,31 @@ function FlashRtmpDecorator(Flash) {
Flash.rtmpSourceHandler = {};

/**
* Check Flash can handle the source natively
* Check if Flash can play the given videotype
* @param {String} type The mimetype to check
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
Flash.rtmpSourceHandler.canPlayType = function(type){
if (Flash.isStreamingType(type)) {
return 'maybe';
}

return '';
};

/**
* Check if Flash can handle the source natively
* @param {Object} source The source object
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
Flash.rtmpSourceHandler.canHandleSource = function(source){
if (Flash.isStreamingType(source.type) || Flash.isStreamingSrc(source.src)) {
let can = Flash.rtmpSourceHandler.canPlayType(source.type);

if (can) {
return can;
}

if (Flash.isStreamingSrc(source.src)) {
return 'maybe';
}

Expand Down
19 changes: 14 additions & 5 deletions src/js/tech/flash.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,19 @@ Tech.withSourceHandlers(Flash);
*/
Flash.nativeSourceHandler = {};

/**
* Check if Flash can play the given videotype
* @param {String} type The mimetype to check
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
Flash.nativeSourceHandler.canPlayType = function(type){
if (type in Flash.formats) {
return 'maybe';
}

return '';
};

/*
* Check Flash can handle the source natively
*
Expand All @@ -376,11 +389,7 @@ Flash.nativeSourceHandler.canHandleSource = function(source){
type = source.type.replace(/;.*/, '').toLowerCase();
}

if (type in Flash.formats) {
return 'maybe';
}

return '';
return Flash.nativeSourceHandler.canPlayType(type);
};

/*
Expand Down
30 changes: 18 additions & 12 deletions src/js/tech/html5.js
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,22 @@ Tech.withSourceHandlers(Html5);
*/
Html5.nativeSourceHandler = {};

/*
* Check if the video element can play the given videotype
*
* @param {String} type The mimetype to check
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
Html5.nativeSourceHandler.canPlayType = function(type){
// IE9 on Windows 7 without MediaPlayer throws an error here
// https://github.com/videojs/video.js/issues/519
try {
return Html5.TEST_VID.canPlayType(type);
} catch(e) {
return '';
}
};

/*
* Check if the video element can handle the source natively
*
Expand All @@ -837,24 +853,14 @@ Html5.nativeSourceHandler = {};
Html5.nativeSourceHandler.canHandleSource = function(source){
var match, ext;

function canPlayType(type){
// IE9 on Windows 7 without MediaPlayer throws an error here
// https://github.com/videojs/video.js/issues/519
try {
return Html5.TEST_VID.canPlayType(type);
} catch(e) {
return '';
}
}

// If a type was provided we should rely on that
if (source.type) {
return canPlayType(source.type);
return Html5.nativeSourceHandler.canPlayType(source.type);
} else if (source.src) {
// If no type, fall back to checking 'video/[EXTENSION]'
ext = Url.getFileExtension(source.src);

return canPlayType(`video/${ext}`);
return Html5.nativeSourceHandler.canPlayType(`video/${ext}`);
}

return '';
Expand Down
33 changes: 33 additions & 0 deletions src/js/tech/tech.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,19 @@ class Tech extends Component {
*/
setPoster() {}

/*
* Check if the tech can support the given type
*
* The base tech does not support any type, but source handlers might
* overwrite this.
*
* @param {String} type The mimetype to check
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
canPlayType() {
return '';
}

}

/*
Expand Down Expand Up @@ -498,6 +511,26 @@ Tech.withSourceHandlers = function(_Tech){
handlers.splice(index, 0, handler);
};

/*
* Check if the tech can support the given type
* @param {String} type The mimetype to check
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
_Tech.canPlayType = function(type){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that only sourcehandlers will get the canPlayType method, I believe. We probably want all techs to have a canPlayType. Probably need to add a canPlayType = () => '' to the prototype of Tech.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah totally overlooked that; will add an empty function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should return an empty string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I mean

let handlers = _Tech.sourceHandlers || [];
let can;

for (let i = 0; i < handlers.length; i++) {
can = handlers[i].canPlayType(type);

if (can) {
return can;
}
}

return '';
};

/*
* Return the first source handler that supports the source
* TODO: Answer question: should 'probably' be prioritized over 'maybe'
Expand Down
9 changes: 9 additions & 0 deletions test/unit/player.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -824,3 +824,12 @@ expect(3);
player.language('en-GB');
strictEqual(player.localize('Good'), 'Brilliant', 'Ignored case');
});

test('should return correct values for canPlayType', function(){
var player = TestHelpers.makePlayer();

equal(player.canPlayType('video/mp4'), 'maybe', 'player can play mp4 files');
equal(player.canPlayType('video/unsupported-format'), '', 'player can not play unsupported files');

player.dispose();
});
10 changes: 10 additions & 0 deletions test/unit/tech/flash.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,16 @@ test('should have the source handler interface', function() {
ok(Flash.registerSourceHandler, 'has the registerSourceHandler function');
});

test('canPlayType should select the correct types to play', function () {
let canPlayType = Flash.nativeSourceHandler.canPlayType;

equal(canPlayType('video/flv'), 'maybe', 'should be able to play FLV files');
equal(canPlayType('video/x-flv'), 'maybe', 'should be able to play x-FLV files');
equal(canPlayType('video/mp4'), 'maybe', 'should be able to play MP4 files');
equal(canPlayType('video/m4v'), 'maybe', 'should be able to play M4V files');
equal(canPlayType('video/ogg'), '', 'should return empty string if it can not play the video');
});

test('canHandleSource should be able to work with src objects without a type', function () {
let canHandleSource = Flash.nativeSourceHandler.canHandleSource;

Expand Down
21 changes: 21 additions & 0 deletions test/unit/tech/html5.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,27 @@ test('should have the source handler interface', function() {
ok(Html5.registerSourceHandler, 'has the registerSourceHandler function');
});

test('native source handler canPlayType', function(){
var result;

// Stub the test video canPlayType (used in canPlayType) to control results
var origCPT = Html5.TEST_VID.canPlayType;
Html5.TEST_VID.canPlayType = function(type){
if (type === 'video/mp4') {
return 'maybe';
}
return '';
};

var canPlayType = Html5.nativeSourceHandler.canPlayType;

equal(canPlayType('video/mp4'), 'maybe', 'Native source handler reported type support');
equal(canPlayType('foo'), '', 'Native source handler handled bad type');

// Reset test video canPlayType
Html5.TEST_VID.canPlayType = origCPT;
});

test('native source handler canHandleSource', function(){
var result;

Expand Down
1 change: 1 addition & 0 deletions test/unit/tech/tech-faker.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class TechFaker extends Tech {

// Support everything except for "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'; }
}

Expand Down
16 changes: 16 additions & 0 deletions test/unit/tech/tech.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ test('should add the source handler interface to a tech', function(){

// Create source handlers
var handlerOne = {
canPlayType: function(type){
if (type !=='no-support') {
return 'probably';
}
return '';
},
canHandleSource: function(source){
if (source.type !=='no-support') {
return 'probably';
Expand All @@ -146,6 +152,9 @@ test('should add the source handler interface to a tech', function(){
};

var handlerTwo = {
canPlayType: function(type){
return ''; // no support
},
canHandleSource: function(source){
return ''; // no support
},
Expand All @@ -164,6 +173,10 @@ test('should add the source handler interface to a tech', function(){
strictEqual(MyTech.selectSourceHandler(sourceA), handlerOne, 'handlerOne was selected to handle the valid source');
strictEqual(MyTech.selectSourceHandler(sourceB), null, 'no handler was selected to handle the invalid source');

// Test canPlayType return values
strictEqual(MyTech.canPlayType(sourceA.type), 'probably', 'the Tech returned probably for the valid source');
strictEqual(MyTech.canPlayType(sourceB.type), '', 'the Tech returned an empty string for the invalid source');

// Test canPlaySource return values
strictEqual(MyTech.canPlaySource(sourceA), 'probably', 'the Tech returned probably for the valid source');
strictEqual(MyTech.canPlaySource(sourceB), '', 'the Tech returned an empty string for the invalid source');
Expand Down Expand Up @@ -239,6 +252,9 @@ test('delegates seekable to the source handler', function(){
};

MyTech.registerSourceHandler({
canPlayType: function() {
return true;
},
canHandleSource: function() {
return true;
},
Expand Down