Skip to content

Commit

Permalink
src() should not return blob URLs with MSE source handlers. closes vi…
Browse files Browse the repository at this point in the history
  • Loading branch information
jrivera authored and dmlap committed Jun 23, 2015
1 parent 1404121 commit 56d4861
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 95 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ CHANGELOG
=========

## HEAD (Unreleased)
_(none)_
* src() should not return blob URLs with MSE source handlers ([view](https://github.com/videojs/video.js/pull/2271))

--------------------

Expand Down
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');
});
});

0 comments on commit 56d4861

Please sign in to comment.