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

Monkeypatch canPlayType on Android 4.0+ for HLS #1084

Merged
merged 14 commits into from
Mar 26, 2014
Merged
56 changes: 47 additions & 9 deletions src/js/media/html5.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(',');

Expand Down Expand Up @@ -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' : '';
};
}
56 changes: 56 additions & 0 deletions test/unit/media.html5.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Copy link
Member

Choose a reason for hiding this comment

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

@gkatsev are these meant to check that the functions exist after minifying?

Copy link
Member Author

Choose a reason for hiding this comment

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

um... yes. Though, I guess would we want to check that it as a string so it doesn't get minified since we want those functions exported?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I typically do that in the api.js file, though I guess a string key would do the same thing

Copy link
Member Author

Choose a reason for hiding this comment

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

We could move this test there if you think it would be a better place for them.

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();
Copy link
Member

Choose a reason for hiding this comment

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

unpatchedCanPlayType isn't currently used


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();
});