diff --git a/src/js/media/html5.js b/src/js/media/html5.js index 3f36e2e186..af20e7f8b5 100644 --- a/src/js/media/html5.js +++ b/src/js/media/html5.js @@ -266,6 +266,53 @@ vjs.Html5.canControlVolume = function(){ return volume !== vjs.TEST_VID.volume; }; +// HTML5 Feature detection and Device Fixes --------------------------------- // +(function() { + var canPlayType, + mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i, + mp4RE = /^video\/mp4/i; + + vjs.Html5.patchCanPlayType = function() { + // Android 4.0 and above can play HLS to some extent but it reports being unable to do so + if (vjs.ANDROID_VERSION >= 4.0) { + if (!canPlayType) { + canPlayType = vjs.TEST_VID.constructor.prototype.canPlayType; + } + + vjs.TEST_VID.constructor.prototype.canPlayType = function(type) { + if (type && mpegurlRE.test(type)) { + return "maybe"; + } + return canPlayType.call(this, type); + }; + } + + // Override Android 2.2 and less canPlayType method which is broken + if (vjs.IS_OLD_ANDROID) { + if (!canPlayType) { + canPlayType = vjs.TEST_VID.constructor.prototype.canPlayType; + } + + vjs.TEST_VID.constructor.prototype.canPlayType = function(type){ + if (type && mp4RE.test(type)) { + return "maybe"; + } + return canPlayType.call(this, type); + }; + } + }; + + vjs.Html5.unpatchCanPlayType = function() { + var r = vjs.TEST_VID.constructor.prototype.canPlayType; + vjs.TEST_VID.constructor.prototype.canPlayType = canPlayType; + canPlayType = null; + return r; + }; + + // by default, patch the video element + vjs.Html5.patchCanPlayType(); +})(); + // List of all HTML5 events (various uses). vjs.Html5.Events = 'loadstart,suspend,abort,error,emptied,stalled,loadedmetadata,loadeddata,canplay,canplaythrough,playing,waiting,seeking,seeked,ended,durationchange,timeupdate,progress,play,pause,ratechange,volumechange'.split(','); @@ -300,12 +347,3 @@ vjs.Html5.disposeMediaElement = function(el){ })(); } }; - -// HTML5 Feature detection and Device Fixes --------------------------------- // - - // Override Android 2.2 and less canPlayType method which is broken -if (vjs.IS_OLD_ANDROID) { - document.createElement('video').constructor.prototype.canPlayType = function(type){ - return (type && type.toLowerCase().indexOf('video/mp4') != -1) ? 'maybe' : ''; - }; -} diff --git a/test/unit/media.html5.js b/test/unit/media.html5.js index e38777ea0c..082f3718df 100644 --- a/test/unit/media.html5.js +++ b/test/unit/media.html5.js @@ -38,3 +38,59 @@ test('should re-link the player if the tech is moved', function(){ strictEqual(player, tech.el()['player']); }); + +test('patchCanPlayType and unpatchCanPlayType are available on Html5 object', function() { + ok(vjs.Html5.patchCanPlayType, 'patchCanPlayType is available'); + ok(vjs.Html5.unpatchCanPlayType, 'unpatchCanPlayType is available'); +}); + +test('patchCanPlayType patches canplaytype with our function, conditionally', function() { + var oldAV = vjs.ANDROID_VERSION, + video = document.createElement('video'), + canPlayType = vjs.TEST_VID.constructor.prototype.canPlayType, + patchedCanPlayType, + unpatchedCanPlayType; + + vjs.ANDROID_VERSION = 4.0; + vjs.Html5.patchCanPlayType(); + + notStrictEqual(video.canPlayType, canPlayType, 'original canPlayType and patched canPlayType should not be equal'); + + patchedCanPlayType = video.canPlayType; + unpatchedCanPlayType = vjs.Html5.unpatchCanPlayType(); + + strictEqual(canPlayType, vjs.TEST_VID.constructor.prototype.canPlayType, 'original canPlayType and unpatched canPlayType should be equal'); + strictEqual(patchedCanPlayType, unpatchedCanPlayType, 'patched canPlayType and function returned from unpatch are equal'); + + vjs.ANDROID_VERSION = oldAV; + vjs.Html5.unpatchCanPlayType(); +}); + +test('should return maybe for HLS urls on Android 4.0 or above', function() { + var oldAV = vjs.ANDROID_VERSION, + video = document.createElement('video'); + + vjs.ANDROID_VERSION = 4.0; + vjs.Html5.patchCanPlayType(); + + strictEqual(video.canPlayType('application/x-mpegurl'), 'maybe', 'android version 4.0 or above should be a maybe for x-mpegurl'); + strictEqual(video.canPlayType('application/x-mpegURL'), 'maybe', 'android version 4.0 or above should be a maybe for x-mpegURL'); + strictEqual(video.canPlayType('application/vnd.apple.mpegurl'), 'maybe', 'android version 4.0 or above should be a maybe for vnd.apple.mpegurl'); + strictEqual(video.canPlayType('application/vnd.apple.mpegURL'), 'maybe', 'android version 4.0 or above should be a maybe for vnd.apple.mpegurl'); + + vjs.ANDROID_VERSION = oldAV; + vjs.Html5.unpatchCanPlayType(); +}); + +test('should return a maybe for mp4 on OLD ANDROID', function() { + var isOldAndroid = vjs.IS_OLD_ANDROID, + video = document.createElement('video'); + + vjs.IS_OLD_ANDROID = true; + vjs.Html5.patchCanPlayType(); + + strictEqual(video.canPlayType('video/mp4'), 'maybe', 'old android should return a maybe for video/mp4'); + + vjs.IS_OLD_ANDROID = isOldAndroid; + vjs.Html5.unpatchCanPlayType(); +});