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

Revert 4.12.9 and fix the Html5 tech with sourceHandlers that use MSE #2271

Closed
wants to merge 8 commits into from
51 changes: 46 additions & 5 deletions src/js/media/html5.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,10 +314,27 @@ vjs.Html5.prototype.exitFullScreen = function(){
this.el_.webkitExitFullScreen();
};

// Checks to see if the element's reported URI (either from `el_.src`
// or `el_.currentSrc`) is a blob-uri and, if so, returns the uri that
// was passed into the source-handler when it was first invoked instead
// of the blob-uri
vjs.Html5.prototype.returnOriginalIfBlobURI_ = function (elementURI, originalURI) {
var blobURIRegExp = /^blob\:/i;

// If originalURI is undefined then we are probably in a non-source-handler-enabled
// tech that inherits from the Html5 tech so we should just return the elementURI
// regardless of it's blobby-ness
if (originalURI && elementURI && blobURIRegExp.test(elementURI)) {
return originalURI;
}
return elementURI;
};

vjs.Html5.prototype.src = function(src) {
var elementSrc = this.el_.src;

if (src === undefined) {
return this.el_.src;
return this.returnOriginalIfBlobURI_(elementSrc, this.source_);
} else {
// Setting src through `src` instead of `setSrc` will be deprecated
this.setSrc(src);
Expand All @@ -330,11 +347,13 @@ vjs.Html5.prototype.setSrc = function(src) {

vjs.Html5.prototype.load = function(){ this.el_.load(); };
vjs.Html5.prototype.currentSrc = function(){
if (this.currentSource_) {
return this.currentSource_.src;
} else {
return this.el_.currentSrc;
var elementSrc = this.el_.currentSrc;

if (!this.currentSource_) {
return elementSrc;
}

return this.returnOriginalIfBlobURI_(elementSrc, this.currentSource_.src);
};

vjs.Html5.prototype.poster = function(){ return this.el_.poster; };
Expand Down Expand Up @@ -470,6 +489,28 @@ vjs.Html5.isSupported = function(){
// Add Source Handler pattern functions to this tech
vjs.MediaTechController.withSourceHandlers(vjs.Html5);

/*
* Override the withSourceHandler mixin's methods with our own because
* the HTML5 Media Element returns blob urls when utilizing MSE and we
* want to still return proper source urls even when in that case
*/
(function(){
var
origSetSource = vjs.Html5.prototype.setSource,
origDisposeSourceHandler = vjs.Html5.prototype.disposeSourceHandler;

vjs.Html5.prototype.setSource = function (source) {
var retVal = origSetSource.call(this, source);
this.source_ = source.src;
return retVal;
};

vjs.Html5.prototype.disposeSourceHandler = function () {
this.source_ = undefined;
return origDisposeSourceHandler.call(this);
};
})();

/**
* The default native source handler.
* This simply passes the source to the video element. Nothing fancy.
Expand Down
29 changes: 1 addition & 28 deletions src/js/media/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ vjs.MediaTechController = vjs.Component.extend({
this.emulateTextTracks();
}

this.on('loadstart', this.updateCurrentSource_);

this.initTextTrackListeners();
}
});
Expand Down Expand Up @@ -170,24 +168,6 @@ vjs.MediaTechController.prototype.onTap = function(){
this.player().userActive(!this.player().userActive());
};

/**
* Set currentSource_ asynchronously to simulate the media element's
* asynchronous execution of the `resource selection algorithm`
*
* currentSource_ is set either as the first loadstart event OR
* in a timeout to make sure it is set asynchronously before anything else
* but before other loadstart handlers have had a chance to execute
*/
vjs.MediaTechController.prototype.updateCurrentSource_ = function () {
// We could have been called with a 0-ms setTimeout OR via loadstart (which ever
// happens first) so we should clear the timeout to be a good citizen
this.clearTimeout(this.updateSourceTimer_);

if (this.pendingSource_) {
this.currentSource_ = this.pendingSource_;
}
};

/* Fallbacks for unsupported event types
================================================================================ */
// Manually trigger progress events based on changes to the buffered amount
Expand Down Expand Up @@ -446,8 +426,6 @@ vjs.MediaTechController.prototype['featuresNativeTextTracks'] = false;
*
*/
vjs.MediaTechController.withSourceHandlers = function(Tech){
Tech.prototype.currentSource_ = {src: ''};

/**
* Register a source handler
* Source handlers are scripts for handling specific formats.
Expand Down Expand Up @@ -532,12 +510,7 @@ vjs.MediaTechController.withSourceHandlers = function(Tech){
this.disposeSourceHandler();
this.off('dispose', this.disposeSourceHandler);

// Schedule currentSource_ to be set asynchronously
if (source && source.src !== '') {
this.pendingSource_ = source;
this.updateSourceTimer_ = this.setTimeout(vjs.bind(this, this.updateCurrentSource_), 0);
}

this.currentSource_ = source;
this.sourceHandler_ = sh.handleSource(source, this);
this.on('dispose', this.disposeSourceHandler);

Expand Down
7 changes: 1 addition & 6 deletions src/js/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1228,12 +1228,7 @@ vjs.Player.prototype.load = function(){
* @return {String} The current source
*/
vjs.Player.prototype.currentSrc = function(){
var techSrc = this.techGet('currentSrc');

if (techSrc === undefined) {
return this.cache_.src || '';
}
return techSrc;
return this.techGet('currentSrc') || this.cache_.src || '';
};

/**
Expand Down
18 changes: 18 additions & 0 deletions test/unit/media.html5.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,21 @@ test('native source handler canHandleSource', function(){
// Reset test video canPlayType
vjs.TEST_VID.canPlayType = origCPT;
});

test('handling of blob URIs with a source handler', function(){
var origEl = tech.el_;

// Override element
tech.el_ = {};

tech.setSource({ type: 'video/mp4', src: 'video.flv' });
equal(tech.src(), 'video.flv', 'el_.src is properly set');

tech.el_.src = 'http://url';
equal(tech.src(), 'http://url', 'el_.src is returned if not a blob uri');

tech.el_.src = 'blob:http://blob-url';
equal(tech.src(), 'video.flv', 'original src set via setSource is returned if el_.src is a blob uri');

tech.el_ = origEl;
});
56 changes: 1 addition & 55 deletions test/unit/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,6 @@ test('should add the source hanlder interface to a tech', function(){

// Pass a source through the source handler process of a tech instance
tech.setSource(sourceA);

// Increment clock since currentSource_ is set asynchronously
this.clock.tick(1);

strictEqual(tech.currentSource_, sourceA, 'sourceA was handled and stored');
ok(tech.sourceHandler_.dispose, 'the handlerOne state instance was stored');

Expand Down Expand Up @@ -254,54 +250,4 @@ test('should handle unsupported sources with the source hanlder API', function()

tech.setSource('');
ok(usedNative, 'native source handler was used when an unsupported source was set');
});

test('should emulate the video element\'s behavior for currentSrc when src is set', function(){
var mockPlayer = {
off: this.noop,
trigger: this.noop
};
var sourceA = { src: 'foo.mp4', type: 'video/mp4' };
var sourceB = { src: '', type: 'video/mp4' };

// Define a new tech class
var Tech = videojs.MediaTechController.extend();

// Extend Tech with source handlers
vjs.MediaTechController.withSourceHandlers(Tech);

// Create an instance of Tech
var tech = new Tech(mockPlayer);

// Create source handlers
var handler = {
canHandleSource: function(source){
return 'probably';
},
handleSource: function(s, t){return {};}
};

Tech.registerSourceHandler(handler);

// Pass a source through the source handler process of a tech instance
tech.setSource(sourceA);

// Test that currentSource_ is not immediately specified
deepEqual(tech.currentSource_, {src:''}, 'sourceA was not stored immediately');

this.clock.tick(1);

// Test that currentSource_ is specified after yielding to the event loop
strictEqual(tech.currentSource_, sourceA, 'sourceA was handled and stored');

// Pass a source with an empty src
tech.setSource(sourceB);

// Test that currentSource_ is not immediately changed
strictEqual(tech.currentSource_, sourceA, 'sourceB was not stored immediately');

this.clock.tick(1);

// Test that currentSource_ is still unchanged
strictEqual(tech.currentSource_, sourceA, 'sourceB was not stored if equal to the empty string');
});
});