From 589cab7fa72d4a747b188e2990ee762fefbb9411 Mon Sep 17 00:00:00 2001 From: Nicky Gerritsen Date: Tue, 27 Oct 2015 13:46:05 -0400 Subject: [PATCH] @nickygerritsen added canPlayType method to player. closes #2709 --- CHANGELOG.md | 1 + src/js/player.js | 34 ++++++++++++++++++++++++++++++++++ src/js/tech/flash-rtmp.js | 23 +++++++++++++++++++++-- src/js/tech/flash.js | 19 ++++++++++++++----- src/js/tech/html5.js | 30 ++++++++++++++++++------------ src/js/tech/tech.js | 33 +++++++++++++++++++++++++++++++++ test/unit/player.test.js | 9 +++++++++ test/unit/tech/flash.test.js | 10 ++++++++++ test/unit/tech/html5.test.js | 21 +++++++++++++++++++++ test/unit/tech/tech-faker.js | 1 + test/unit/tech/tech.test.js | 16 ++++++++++++++++ 11 files changed, 178 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04178495c..1d7c6d347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * @gkatsev removed unhelpful isCrossOrigin test ([view](https://github.com/videojs/video.js/pull/2715)) * @forbesjo updated karma to use all installed browsers for unit tests ([view](https://github.com/videojs/video.js/pull/2708)) * @forbesjo removed android/ios tests to increase build stability ([view](https://github.com/videojs/video.js/pull/2739)) +* @nickygerritsen added canPlayType method to player ([view](https://github.com/videojs/video.js/pull/2709)) -------------------- diff --git a/src/js/player.js b/src/js/player.js index 366eb3dcb..4796eb71c 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -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); + + if (can) { + return can; + } + } + } + + return ''; + } + /** * Select source based on tech order * diff --git a/src/js/tech/flash-rtmp.js b/src/js/tech/flash-rtmp.js index 92e540e39..6bff49f48 100644 --- a/src/js/tech/flash-rtmp.js +++ b/src/js/tech/flash-rtmp.js @@ -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'; } diff --git a/src/js/tech/flash.js b/src/js/tech/flash.js index 571756d2d..3f13a41fd 100644 --- a/src/js/tech/flash.js +++ b/src/js/tech/flash.js @@ -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 * @@ -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); }; /* diff --git a/src/js/tech/html5.js b/src/js/tech/html5.js index 005d2a845..645652ad2 100644 --- a/src/js/tech/html5.js +++ b/src/js/tech/html5.js @@ -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 * @@ -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 ''; diff --git a/src/js/tech/tech.js b/src/js/tech/tech.js index fc5c199e9..895ff60fc 100644 --- a/src/js/tech/tech.js +++ b/src/js/tech/tech.js @@ -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 ''; + } + } /* @@ -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){ + 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' diff --git a/test/unit/player.test.js b/test/unit/player.test.js index 6b8632956..2789e2608 100644 --- a/test/unit/player.test.js +++ b/test/unit/player.test.js @@ -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(); +}); diff --git a/test/unit/tech/flash.test.js b/test/unit/tech/flash.test.js index 665281350..09a567d70 100644 --- a/test/unit/tech/flash.test.js +++ b/test/unit/tech/flash.test.js @@ -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; diff --git a/test/unit/tech/html5.test.js b/test/unit/tech/html5.test.js index 20564f94f..e7e861a66 100644 --- a/test/unit/tech/html5.test.js +++ b/test/unit/tech/html5.test.js @@ -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; diff --git a/test/unit/tech/tech-faker.js b/test/unit/tech/tech-faker.js index df1312c19..1e0b0dba4 100644 --- a/test/unit/tech/tech-faker.js +++ b/test/unit/tech/tech-faker.js @@ -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'; } } diff --git a/test/unit/tech/tech.test.js b/test/unit/tech/tech.test.js index 19762377d..5af163f4c 100644 --- a/test/unit/tech/tech.test.js +++ b/test/unit/tech/tech.test.js @@ -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'; @@ -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 }, @@ -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'); @@ -239,6 +252,9 @@ test('delegates seekable to the source handler', function(){ }; MyTech.registerSourceHandler({ + canPlayType: function() { + return true; + }, canHandleSource: function() { return true; },