Skip to content

Commit

Permalink
@heff added the Source Handler interface for handling advanced format…
Browse files Browse the repository at this point in the history
…s including adaptive streaming. closes #1560
  • Loading branch information
heff committed Dec 2, 2014
1 parent 8a019ee commit 76e662a
Show file tree
Hide file tree
Showing 14 changed files with 624 additions and 198 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ CHANGELOG
* @toloudis fixed an issue with checking for an existing source on the video element ([view](https://github.com/videojs/video.js/pull/1651))
* @rafalwrzeszcz fixed the Flash object tag markup for strict XML ([view](https://github.com/videojs/video.js/pull/1702))
* @thijstriemstra fixed a number of typos in the docs ([view](https://github.com/videojs/video.js/pull/1704))
* @heff added the Source Handler interface for handling advanced formats including adaptive streaming ([view](https://github.com/videojs/video.js/pull/1560))

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

Expand Down
2 changes: 2 additions & 0 deletions build/source-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var sourceFiles = [
"src/js/core-object.js",
"src/js/events.js",
"src/js/lib.js",
"src/js/xhr.js",
"src/js/util.js",
"src/js/component.js",
"src/js/button.js",
Expand All @@ -44,6 +45,7 @@ var sourceFiles = [
"src/js/media/media.js",
"src/js/media/html5.js",
"src/js/media/flash.js",
"src/js/media/flash.rtmp.js",
"src/js/media/loader.js",
"src/js/tracks.js",
"src/js/json.js",
Expand Down
80 changes: 0 additions & 80 deletions src/js/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -614,86 +614,6 @@ vjs.createTimeRange = function(start, end){
};
};

/**
* Simple http request for retrieving external files (e.g. text tracks)
* @param {String} url URL of resource
* @param {Function} onSuccess Success callback
* @param {Function=} onError Error callback
* @param {Boolean=} withCredentials Flag which allow credentials
* @private
*/
vjs.get = function(url, onSuccess, onError, withCredentials){
var fileUrl, request, urlInfo, winLoc, crossOrigin;

onError = onError || function(){};

if (typeof XMLHttpRequest === 'undefined') {
// Shim XMLHttpRequest for older IEs
window.XMLHttpRequest = function () {
try { return new window.ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch (e) {}
try { return new window.ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch (f) {}
try { return new window.ActiveXObject('Msxml2.XMLHTTP'); } catch (g) {}
throw new Error('This browser does not support XMLHttpRequest.');
};
}

request = new XMLHttpRequest();

urlInfo = vjs.parseUrl(url);
winLoc = window.location;
// check if url is for another domain/origin
// ie8 doesn't know location.origin, so we won't rely on it here
crossOrigin = (urlInfo.protocol + urlInfo.host) !== (winLoc.protocol + winLoc.host);

// Use XDomainRequest for IE if XMLHTTPRequest2 isn't available
// 'withCredentials' is only available in XMLHTTPRequest2
// Also XDomainRequest has a lot of gotchas, so only use if cross domain
if(crossOrigin && window.XDomainRequest && !('withCredentials' in request)) {
request = new window.XDomainRequest();
request.onload = function() {
onSuccess(request.responseText);
};
request.onerror = onError;
// these blank handlers need to be set to fix ie9 http://cypressnorth.com/programming/internet-explorer-aborting-ajax-requests-fixed/
request.onprogress = function() {};
request.ontimeout = onError;

// XMLHTTPRequest
} else {
fileUrl = (urlInfo.protocol == 'file:' || winLoc.protocol == 'file:');

request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200 || fileUrl && request.status === 0) {
onSuccess(request.responseText);
} else {
onError(request.responseText);
}
}
};
}

// open the connection
try {
// Third arg is async, or ignored by XDomainRequest
request.open('GET', url, true);
// withCredentials only supported by XMLHttpRequest2
if(withCredentials) {
request.withCredentials = true;
}
} catch(e) {
onError(e);
return;
}

// send the request
try {
request.send();
} catch(e) {
onError(e);
}
};

/**
* Add to local storage (may removable)
* @private
Expand Down
143 changes: 59 additions & 84 deletions src/js/media/flash.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,9 @@ vjs.Flash = vjs.MediaTechController.extend({

// If source was supplied pass as a flash var.
if (source) {
if (source.type && vjs.Flash.isStreamingType(source.type)) {
var parts = vjs.Flash.streamToParts(source.src);
flashVars['rtmpConnection'] = encodeURIComponent(parts.connection);
flashVars['rtmpStream'] = encodeURIComponent(parts.stream);
}
else {
flashVars['src'] = encodeURIComponent(vjs.getAbsoluteURL(source.src));
}
this.ready(function(){
this.setSource(source);
});
}

// Add placeholder to player div
Expand Down Expand Up @@ -124,15 +119,14 @@ vjs.Flash.prototype.src = function(src){
return this['currentSrc']();
}

if (vjs.Flash.isStreamingSrc(src)) {
src = vjs.Flash.streamToParts(src);
this.setRtmpConnection(src.connection);
this.setRtmpStream(src.stream);
} else {
// Make sure source URL is absolute.
src = vjs.getAbsoluteURL(src);
this.el_.vjs_src(src);
}
// Setting src through `src` not `setSrc` will be deprecated
return this.setSrc(src);
};

vjs.Flash.prototype.setSrc = function(src){
// Make sure source URL is absolute.
src = vjs.getAbsoluteURL(src);
this.el_.vjs_src(src);

// Currently the SWF doesn't autoplay if you load a source later.
// e.g. Load player w/ no source, wait 2s, set src.
Expand All @@ -158,17 +152,11 @@ vjs.Flash.prototype['currentTime'] = function(time){
};

vjs.Flash.prototype['currentSrc'] = function(){
var src = this.el_.vjs_getProperty('currentSrc');
// no src, check and see if RTMP
if (src == null) {
var connection = this['rtmpConnection'](),
stream = this['rtmpStream']();

if (connection && stream) {
src = vjs.Flash.streamFromParts(connection, stream);
}
if (this.currentSource_) {
return this.currentSource_.src;
} else {
return this.el_.vjs_getProperty('currentSrc');
}
return src;
};

vjs.Flash.prototype.load = function(){
Expand Down Expand Up @@ -229,31 +217,66 @@ vjs.Flash.isSupported = function(){
// return swfobject.hasFlashPlayerVersion('10');
};

vjs.Flash.canPlaySource = function(srcObj){
// Add Source Handler pattern functions to this tech
vjs.MediaTechController.withSourceHandlers(vjs.Flash);

/**
* The default native source handler.
* This simply passes the source to the video element. Nothing fancy.
* @param {Object} source The source object
* @param {vjs.Flash} tech The instance of the Flash tech
*/
vjs.Flash.nativeSourceHandler = {};

/**
* Check Flash can handle the source natively
* @param {Object} source The source object
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
vjs.Flash.nativeSourceHandler.canHandleSource = function(source){
var type;

if (!srcObj.type) {
if (!source.type) {
return '';
}

type = srcObj.type.replace(/;.*/,'').toLowerCase();
if (type in vjs.Flash.formats || type in vjs.Flash.streamingFormats) {
// Strip code information from the type because we don't get that specific
type = source.type.replace(/;.*/,'').toLowerCase();

if (type in vjs.Flash.formats) {
return 'maybe';
}

return '';
};

/**
* Pass the source to the flash object
* Adaptive source handlers will have more complicated workflows before passing
* video data to the video element
* @param {Object} source The source object
* @param {vjs.Flash} tech The instance of the Flash tech
*/
vjs.Flash.nativeSourceHandler.handleSource = function(source, tech){
tech.setSrc(source.src);
};

/**
* Clean up the source handler when disposing the player or switching sources..
* (no cleanup is needed when supporting the format natively)
*/
vjs.Flash.nativeSourceHandler.dispose = function(){};

// Register the native source handler
vjs.Flash.registerSourceHandler(vjs.Flash.nativeSourceHandler);

vjs.Flash.formats = {
'video/flv': 'FLV',
'video/x-flv': 'FLV',
'video/mp4': 'MP4',
'video/m4v': 'MP4'
};

vjs.Flash.streamingFormats = {
'rtmp/mp4': 'MP4',
'rtmp/flv': 'FLV'
};

vjs.Flash['onReady'] = function(currSwf){
var el, player;

Expand Down Expand Up @@ -398,51 +421,3 @@ vjs.Flash.getEmbedCode = function(swf, flashVars, params, attributes){

return objTag + attrsString + '>' + paramsString + '</object>';
};

vjs.Flash.streamFromParts = function(connection, stream) {
return connection + '&' + stream;
};

vjs.Flash.streamToParts = function(src) {
var parts = {
connection: '',
stream: ''
};

if (! src) {
return parts;
}

// Look for the normal URL separator we expect, '&'.
// If found, we split the URL into two pieces around the
// first '&'.
var connEnd = src.indexOf('&');
var streamBegin;
if (connEnd !== -1) {
streamBegin = connEnd + 1;
}
else {
// If there's not a '&', we use the last '/' as the delimiter.
connEnd = streamBegin = src.lastIndexOf('/') + 1;
if (connEnd === 0) {
// really, there's not a '/'?
connEnd = streamBegin = src.length;
}
}
parts.connection = src.substring(0, connEnd);
parts.stream = src.substring(streamBegin, src.length);

return parts;
};

vjs.Flash.isStreamingType = function(srcType) {
return srcType in vjs.Flash.streamingFormats;
};

// RTMP has four variations, any string starting
// with one of these protocols should be valid
vjs.Flash.RTMP_RE = /^rtmp[set]?:\/\//i;

vjs.Flash.isStreamingSrc = function(src) {
return vjs.Flash.RTMP_RE.test(src);
};
88 changes: 88 additions & 0 deletions src/js/media/flash.rtmp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
vjs.Flash.streamingFormats = {
'rtmp/mp4': 'MP4',
'rtmp/flv': 'FLV'
};

vjs.Flash.streamFromParts = function(connection, stream) {
return connection + '&' + stream;
};

vjs.Flash.streamToParts = function(src) {
var parts = {
connection: '',
stream: ''
};

if (! src) {
return parts;
}

// Look for the normal URL separator we expect, '&'.
// If found, we split the URL into two pieces around the
// first '&'.
var connEnd = src.indexOf('&');
var streamBegin;
if (connEnd !== -1) {
streamBegin = connEnd + 1;
}
else {
// If there's not a '&', we use the last '/' as the delimiter.
connEnd = streamBegin = src.lastIndexOf('/') + 1;
if (connEnd === 0) {
// really, there's not a '/'?
connEnd = streamBegin = src.length;
}
}
parts.connection = src.substring(0, connEnd);
parts.stream = src.substring(streamBegin, src.length);

return parts;
};

vjs.Flash.isStreamingType = function(srcType) {
return srcType in vjs.Flash.streamingFormats;
};

// RTMP has four variations, any string starting
// with one of these protocols should be valid
vjs.Flash.RTMP_RE = /^rtmp[set]?:\/\//i;

vjs.Flash.isStreamingSrc = function(src) {
return vjs.Flash.RTMP_RE.test(src);
};

/**
* A source handler for RTMP urls
* @type {Object}
*/
vjs.Flash.rtmpSourceHandler = {};

/**
* Check Flash can handle the source natively
* @param {Object} source The source object
* @return {String} 'probably', 'maybe', or '' (empty string)
*/
vjs.Flash.rtmpSourceHandler.canHandleSource = function(source){
if (vjs.Flash.isStreamingType(source.type) || vjs.Flash.isStreamingSrc(source.src)) {
return 'maybe';
}

return '';
};

/**
* Pass the source to the flash object
* Adaptive source handlers will have more complicated workflows before passing
* video data to the video element
* @param {Object} source The source object
* @param {vjs.Flash} tech The instance of the Flash tech
*/
vjs.Flash.rtmpSourceHandler.handleSource = function(source, tech){
var srcParts = vjs.Flash.streamToParts(source.src);

tech.setRtmpConnection(srcParts.connection);
tech.setRtmpStream(srcParts.stream);
};

// Register the native source handler
vjs.Flash.registerSourceHandler(vjs.Flash.rtmpSourceHandler);
Loading

0 comments on commit 76e662a

Please sign in to comment.