7094ab96 by David LaPalomento

Upgrade to video.js 5 and convert to a source handler

Bump up to a vjs 5 release candidate. Make the necessary changes to work with the updated APIs. Convert the project from a subclass of the Flash tech to a source handler.
1 parent 67abe293
......@@ -4,10 +4,10 @@
<meta charset="utf-8">
<title>video.js HLS Plugin Example</title>
<link href="node_modules/video.js/dist/video-js/video-js.css" rel="stylesheet">
<link href="node_modules/video.js/dist/video-js.css" rel="stylesheet">
<!-- video.js -->
<script src="node_modules/video.js/dist/video-js/video.dev.js"></script>
<script src="node_modules/video.js/dist/video.js"></script>
<!-- Media Sources plugin -->
<script src="node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script>
......@@ -72,8 +72,7 @@
type="application/x-mpegURL">
</video>
<script>
videojs.options.flash.swf = 'node_modules/videojs-swf/dist/video-js.swf';
videojs.getGlobalOptions().flash.swf = 'node_modules/videojs-swf/dist/video-js.swf';
// initialize the player
var player = videojs('video');
</script>
......
/**
* QUnit v1.11.0 - A JavaScript Unit Testing Framework
*
* http://qunitjs.com
*
* Copyright 2012 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699a4;
background-color: #0d3349;
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
border-radius: 5px 5px 0 0;
-moz-border-radius: 5px 5px 0 0;
-webkit-border-top-right-radius: 5px;
-webkit-border-top-left-radius: 5px;
}
#qunit-header a {
text-decoration: none;
color: #c2ccd1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #fff;
}
#qunit-testrunner-toolbar label {
display: inline-block;
padding: 0 .5em 0 .1em;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
overflow: hidden;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
#qunit-modulefilter-container {
float: right;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li a {
padding: 0.5em;
color: #c2ccd1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests li .runtime {
float: right;
font-size: smaller;
}
.qunit-assert-list {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
.qunit-collapsed {
display: none;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: .2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 .5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #e0f2be;
color: #374e0c;
text-decoration: none;
}
#qunit-tests ins {
background-color: #ffcaca;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: black; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
padding: 5px;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #3c510c;
background-color: #fff;
border-left: 10px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 10px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 5px 5px;
-moz-border-radius: 0 0 5px 5px;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Result */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-bottom: 1px solid white;
}
#qunit-testresult .module-name {
font-weight: bold;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
width: 1000px;
height: 1000px;
}
......@@ -42,13 +42,13 @@
"karma-qunit": "~0.1.1",
"karma-safari-launcher": "~0.1.1",
"karma-sauce-launcher": "~0.1.8",
"qunitjs": "^1.15.0",
"qunitjs": "^1.18.0",
"sinon": "1.10.2",
"video.js": "^4.12.0"
"video.js": "^5.0.0-rc.4"
},
"dependencies": {
"pkcs7": "^0.2.2",
"videojs-contrib-media-sources": "^1.0.0",
"videojs-swf": "^4.7.0"
"videojs-swf": "5.0.0-rc0"
}
}
......
......@@ -299,7 +299,7 @@ AsyncStream.prototype = new videojs.Hls.Stream();
AsyncStream.prototype.processJob_ = function() {
this.jobs.shift()();
if (this.jobs.length) {
this.timeout_ = setTimeout(videojs.bind(this, this.processJob_),
this.timeout_ = setTimeout(this.processJob_.bind(this),
this.delay);
} else {
this.timeout_ = null;
......@@ -308,7 +308,7 @@ AsyncStream.prototype.processJob_ = function() {
AsyncStream.prototype.push = function(job) {
this.jobs.push(job);
if (!this.timeout_) {
this.timeout_ = setTimeout(videojs.bind(this, this.processJob_),
this.timeout_ = setTimeout(this.processJob_.bind(this),
this.delay);
}
};
......
......@@ -585,4 +585,4 @@
ParseStream: ParseStream,
Parser: Parser
};
})(window.videojs, window.parseInt, window.isFinite, window.videojs.util.mergeOptions);
})(window.videojs, window.parseInt, window.isFinite, window.videojs.mergeOptions);
......
......@@ -18,7 +18,7 @@
resolveUrl = videojs.Hls.resolveUrl,
xhr = videojs.Hls.xhr,
Playlist = videojs.Hls.Playlist,
mergeOptions = videojs.util.mergeOptions,
mergeOptions = videojs.mergeOptions,
/**
* Returns a new master playlist that is the result of merging an
......@@ -262,10 +262,10 @@
// request the new playlist
request = xhr({
url: resolveUrl(loader.master.uri, playlist.uri),
uri: resolveUrl(loader.master.uri, playlist.uri),
withCredentials: withCredentials
}, function(error) {
haveMetadata(error, this, playlist.uri);
}, function(error, request) {
haveMetadata(error, request, playlist.uri);
loader.trigger('mediachange');
});
};
......@@ -283,32 +283,35 @@
loader.state = 'HAVE_CURRENT_METADATA';
request = xhr({
url: resolveUrl(loader.master.uri, loader.media().uri),
uri: resolveUrl(loader.master.uri, loader.media().uri),
withCredentials: withCredentials
}, function(error) {
haveMetadata(error, this, loader.media().uri);
}, function(error, request) {
haveMetadata(error, request, loader.media().uri);
});
});
// request the specified URL
xhr({
url: srcUrl,
request = xhr({
uri: srcUrl,
withCredentials: withCredentials
}, function(error) {
}, function(error, req) {
var parser, i;
// clear the loader's request reference
request = null;
if (error) {
loader.error = {
status: this.status,
status: req.status,
message: 'HLS playlist request error at URL: ' + srcUrl,
responseText: this.responseText,
responseText: req.responseText,
code: 2 // MEDIA_ERR_NETWORK
};
return loader.trigger('error');
}
parser = new videojs.m3u8.Parser();
parser.push(this.responseText);
parser.push(req.responseText);
parser.end();
loader.state = 'HAVE_MASTER';
......@@ -326,12 +329,12 @@
}
request = xhr({
url: resolveUrl(srcUrl, parser.manifest.playlists[0].uri),
uri: resolveUrl(srcUrl, parser.manifest.playlists[0].uri),
withCredentials: withCredentials
}, function(error) {
}, function(error, request) {
// pass along the URL specified in the master playlist
haveMetadata(error,
this,
request,
parser.manifest.playlists[0].uri);
if (!error) {
loader.trigger('loadedmetadata');
......@@ -349,7 +352,7 @@
}]
};
loader.master.playlists[srcUrl] = loader.master.playlists[0];
haveMetadata(null, this, srcUrl);
haveMetadata(null, req, srcUrl);
return loader.trigger('loadedmetadata');
});
};
......
(function(videojs){
(function(videojs) {
'use strict';
/**
* Creates and sends an XMLHttpRequest.
* TODO - expose video.js core's XHR and use that instead
*
* @param options {string | object} if this argument is a string, it
* is intrepreted as a URL and a simple GET request is
* inititated. If it is an object, it should contain a `url`
* property that indicates the URL to request and optionally a
* `method` which is the type of HTTP request to send.
* @param callback (optional) {function} a function to call when the
* request completes. If the request was not successful, the first
* argument will be falsey.
* @return {object} the XMLHttpRequest that was initiated.
* A wrapper for videojs.xhr that tracks bandwidth.
*/
videojs.Hls.xhr = function(url, callback) {
var
options = {
method: 'GET',
timeout: 45 * 1000
},
request,
abortTimeout;
if (typeof callback !== 'function') {
callback = function() {};
}
if (typeof url === 'object') {
options = videojs.util.mergeOptions(options, url);
url = options.url;
}
request = new window.XMLHttpRequest();
request.open(options.method, url);
request.url = url;
request.requestTime = new Date().getTime();
if (options.responseType) {
request.responseType = options.responseType;
}
if (options.withCredentials) {
request.withCredentials = true;
}
if (options.timeout) {
abortTimeout = window.setTimeout(function() {
if (request.readyState !== 4) {
request.timedout = true;
request.abort();
}
}, options.timeout);
}
request.onreadystatechange = function() {
// wait until the request completes
if (this.readyState !== 4) {
return;
videojs.Hls.xhr = function(options, callback) {
var request = videojs.xhr(options, function(error, request) {
if (request.response) {
request.responseTime = (new Date()).getTime();
request.roundTripTime = request.responseTime - request.requestTime;
request.bytesReceived = request.response.byteLength || request.response.length;
request.bandwidth = Math.floor((request.bytesReceived / request.roundTripTime) * 8 * 1000);
}
// clear outstanding timeouts
window.clearTimeout(abortTimeout);
callback(error, request);
});
// request timeout
if (request.timedout) {
return callback.call(this, 'timeout', url);
}
// request aborted or errored
if (this.status >= 400 || this.status === 0) {
return callback.call(this, true, url);
}
if (this.response) {
this.responseTime = new Date().getTime();
this.roundTripTime = this.responseTime - this.requestTime;
this.bytesReceived = this.response.byteLength || this.response.length;
this.bandwidth = Math.floor((this.bytesReceived / this.roundTripTime) * 8 * 1000);
}
return callback.call(this, false, url);
};
request.send(null);
request.requestTime = (new Date()).getTime();
return request;
};
})(window.videojs);
......
......@@ -69,12 +69,9 @@ module.exports = function(config) {
// add their paths to this list.
files: [
'../node_modules/sinon/lib/sinon.js',
'../node_modules/sinon/lib/sinon/util/event.js',
'../node_modules/sinon/lib/sinon/util/fake_xml_http_request.js',
'../node_modules/sinon/lib/sinon/util/xhr_ie.js',
'../node_modules/sinon/lib/sinon/util/fake_timers.js',
'../node_modules/video.js/dist/video-js/video.dev.js',
'../node_modules/sinon/pkg/sinon.js',
'../node_modules/video.js/dist/video-js.css',
'../node_modules/video.js/dist/video.js',
'../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js',
'../node_modules/pkcs7/dist/pkcs7.unpad.js',
'../test/karma-qunit-shim.js',
......
......@@ -34,12 +34,9 @@ module.exports = function(config) {
// add their paths to this list.
files: [
'../node_modules/sinon/lib/sinon.js',
'../node_modules/sinon/lib/sinon/util/event.js',
'../node_modules/sinon/lib/sinon/util/fake_xml_http_request.js',
'../node_modules/sinon/lib/sinon/util/xhr_ie.js',
'../node_modules/sinon/lib/sinon/util/fake_timers.js',
'../node_modules/video.js/dist/video-js/video.dev.js',
'../node_modules/sinon/pkg/sinon.js',
'../node_modules/video.js/dist/video-js.css',
'../node_modules/video.js/dist/video.js',
'../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js',
'../node_modules/pkcs7/dist/pkcs7.unpad.js',
'../test/karma-qunit-shim.js',
......
......@@ -27,7 +27,7 @@
0x09, 0x00, 0x00, 0x00, 0x00
],
extend = window.videojs.util.mergeOptions,
mergeOptions = window.videojs.mergeOptions,
makePat,
makePsi,
......@@ -178,7 +178,7 @@
makePacket = function(options) {
var
result = [],
settings = extend({
settings = mergeOptions({
payloadUnitStartIndicator: true,
pid: 0x00
}, options);
......
......@@ -4,18 +4,15 @@
<meta charset="utf-8">
<title>video.js HLS Plugin Test Suite</title>
<!-- Load sinon server for fakeXHR -->
<script src="../node_modules/sinon/lib/sinon.js"></script>
<script src="../node_modules/sinon/lib/sinon/util/event.js"></script>
<script src="../node_modules/sinon/lib/sinon/util/fake_xml_http_request.js"></script>
<script src="../node_modules/sinon/lib/sinon/util/xhr_ie.js"></script>
<script src="../node_modules/sinon/lib/sinon/util/fake_timers.js"></script>
<script src="../node_modules/sinon/pkg/sinon.js"></script>
<!-- Load local QUnit. -->
<link rel="stylesheet" href="../libs/qunit/qunit.css" media="screen">
<script src="../libs/qunit/qunit.js"></script>
<link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css" media="screen">
<script src="../node_modules/qunitjs/qunit/qunit.js"></script>
<!-- video.js -->
<script src="../node_modules/video.js/dist/video-js/video.dev.js"></script>
<script src="../node_modules/video.js/dist/video.js"></script>
<link rel="stylesheet" href="../node_modules/video.js/dist/video-js.css" media="screen">
<script src="../node_modules/videojs-contrib-media-sources/src/videojs-media-sources.js"></script>
<!-- HLS plugin -->
......@@ -63,7 +60,6 @@
<script src="playlist_test.js"></script>
<script src="playlist-loader_test.js"></script>
<script src="decrypter_test.js"></script>
<script src="xhr_test.js"></script>
</head>
<body>
<div id="qunit"></div>
......
(function(window, videojs, undefined) {
'use strict';
/*
XHR test suite
*/
var xhr;
module('XHR', {
setup: function() {
xhr = sinon.useFakeXMLHttpRequest();
},
teardown: function() {
xhr.restore();
}
});
test('handles xhr timeouts correctly', function () {
var error;
var clock = sinon.useFakeTimers();
videojs.Hls.xhr({
url: 'http://example.com',
timeout: 1
}, function(innerError) {
error = innerError;
});
clock.tick(1);
strictEqual(error, 'timeout', 'called with timeout error');
clock.restore();
});
})(window, window.videojs);