50a78066 by David LaPalomento

Merge pull request #62 from videojs/tech2

HLS Tech
2 parents fafd0563 d30df95f
......@@ -45,9 +45,6 @@ module.exports = function(grunt) {
dest: 'dist/videojs.hls.min.js'
}
},
qunit: {
files: ['test/**/*.html', '!test/perf.html', '!test/muxer/**']
},
jshint: {
gruntfile: {
options: {
......@@ -93,11 +90,11 @@ module.exports = function(grunt) {
},
src: {
files: '<%= jshint.src.src %>',
tasks: ['jshint:src', 'qunit']
tasks: ['jshint:src', 'test']
},
test: {
files: '<%= jshint.test.src %>',
tasks: ['jshint:test', 'qunit']
tasks: ['jshint:test', 'test']
}
},
concurrent: {
......@@ -194,7 +191,6 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-connect');
......@@ -255,7 +251,7 @@ module.exports = function(grunt) {
['clean',
'jshint',
'manifests-to-js',
'qunit',
'test',
'concat',
'uglify']);
......
......@@ -31,7 +31,7 @@
<!-- bipbop -->
<!-- <script src="test/tsSegment.js"></script> -->
<!-- bunnies -->
<script src="test/tsSegment-bc.js"></script>
<!--<script src="test/tsSegment-bc.js"></script>-->
<style>
body {
......@@ -63,10 +63,12 @@
<script>
videojs.options.flash.swf = 'node_modules/video.js/dist/video-js/video-js.swf';
// initialize the player
var player = videojs('video');
var player = videojs('video', {
techOrder: ['hls']
});
// initialize the plugin
player.hls();
//player.hls()
</script>
</body>
</html>
......
......@@ -19,7 +19,6 @@
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-connect": "~0.6.0",
"grunt-contrib-jshint": "~0.6.0",
"grunt-contrib-qunit": "~0.2.0",
"grunt-contrib-uglify": "~0.2.0",
"grunt-contrib-watch": "~0.4.0",
"grunt-karma": "~0.6.2",
......
......@@ -8,7 +8,7 @@
(function(window) {
var
FlvTag = window.videojs.hls.FlvTag,
FlvTag = window.videojs.Hls.FlvTag,
adtsSampleingRates = [
96000, 88200,
64000, 48000,
......@@ -17,7 +17,7 @@ var
16000, 12000
];
window.videojs.hls.AacStream = function() {
window.videojs.Hls.AacStream = function() {
var
next_pts, // :uint
pts_offset, // :int
......
......@@ -29,5 +29,5 @@
}
};
window.videojs.hls.utils = module;
window.videojs.Hls.utils = module;
})(this);
......
......@@ -4,7 +4,7 @@
* Parser for exponential Golomb codes, a variable-bitwidth number encoding
* scheme used by h264.
*/
window.videojs.hls.ExpGolomb = function(workingData) {
window.videojs.Hls.ExpGolomb = function(workingData) {
var
// the number of bytes left to examine in workingData
workingBytesAvailable = workingData.byteLength,
......
(function(window) {
window.videojs = window.videojs || {};
window.videojs.hls = window.videojs.hls || {};
window.videojs.Hls = window.videojs.Hls || {};
var hls = window.videojs.hls;
var hls = window.videojs.Hls;
// (type:uint, extraData:Boolean = false) extends ByteArray
hls.FlvTag = function(type, extraData) {
......
......@@ -8,8 +8,8 @@
(function(window) {
var
ExpGolomb = window.videojs.hls.ExpGolomb,
FlvTag = window.videojs.hls.FlvTag,
ExpGolomb = window.videojs.Hls.ExpGolomb,
FlvTag = window.videojs.Hls.FlvTag,
H264ExtraData = function() {
this.sps = []; // :Array
......@@ -234,7 +234,7 @@
* an h264 stream. Exactly one byte.
*/
// incomplete, see Table 7.1 of ITU-T H.264 for 12-32
window.videojs.hls.NALUnitType = NALUnitType = {
window.videojs.Hls.NALUnitType = NALUnitType = {
unspecified: 0,
slice_layer_without_partitioning_rbsp_non_idr: 1,
slice_data_partition_a_layer_rbsp: 2,
......@@ -249,7 +249,7 @@
end_of_stream_rbsp: 11
};
window.videojs.hls.H264Stream = function() {
window.videojs.Hls.H264Stream = function() {
var
next_pts, // :uint;
next_dts, // :uint;
......
......@@ -32,7 +32,7 @@
}
return result;
},
Stream = videojs.hls.Stream,
Stream = videojs.Hls.Stream,
LineStream,
ParseStream,
Parser;
......
......@@ -5,8 +5,8 @@
(function(window, videojs) {
'use strict';
var
resolveUrl = videojs.hls.resolveUrl,
xhr = videojs.hls.xhr,
resolveUrl = videojs.Hls.resolveUrl,
xhr = videojs.Hls.xhr,
/**
* Returns a new master playlist that is the result of merging an
......@@ -51,6 +51,7 @@
var
loader = this,
media,
mediaUpdateTimeout,
request,
haveMetadata = function(error, xhr, url) {
......@@ -88,7 +89,7 @@
// refresh live playlists after a target duration passes
if (!loader.media().endList) {
window.setTimeout(function() {
mediaUpdateTimeout = window.setTimeout(function() {
loader.trigger('mediaupdatetimeout');
}, refreshDelay);
}
......@@ -104,6 +105,13 @@
loader.state = 'HAVE_NOTHING';
loader.dispose = function() {
if (request) {
request.abort();
}
window.clearTimeout(mediaUpdateTimeout);
};
/**
* When called without any arguments, returns the currently
* active media playlist. When called with a single argument,
......@@ -213,7 +221,9 @@
haveMetadata(error,
this,
parser.manifest.playlists[0].uri);
loader.trigger('loadedmetadata');
if (!error) {
loader.trigger('loadedmetadata');
}
});
return loader.trigger('loadedplaylist');
}
......@@ -231,7 +241,7 @@
return loader.trigger('loadedmetadata');
});
};
PlaylistLoader.prototype = new videojs.hls.Stream();
PlaylistLoader.prototype = new videojs.Hls.Stream();
videojs.hls.PlaylistLoader = PlaylistLoader;
videojs.Hls.PlaylistLoader = PlaylistLoader;
})(window, window.videojs);
......
(function(window) {
var
videojs = window.videojs,
FlvTag = videojs.hls.FlvTag,
H264Stream = videojs.hls.H264Stream,
AacStream = videojs.hls.AacStream,
FlvTag = videojs.Hls.FlvTag,
H264Stream = videojs.Hls.H264Stream,
AacStream = videojs.Hls.AacStream,
MP2T_PACKET_LENGTH,
STREAM_TYPES;
......@@ -11,7 +11,7 @@
* An object that incrementally transmuxes MPEG2 Trasport Stream
* chunks into an FLV.
*/
videojs.hls.SegmentParser = function() {
videojs.Hls.SegmentParser = function() {
var
self = this,
parseTSPacket,
......@@ -432,8 +432,8 @@
};
// MPEG2-TS constants
videojs.hls.SegmentParser.MP2T_PACKET_LENGTH = MP2T_PACKET_LENGTH = 188;
videojs.hls.SegmentParser.STREAM_TYPES = STREAM_TYPES = {
videojs.Hls.SegmentParser.MP2T_PACKET_LENGTH = MP2T_PACKET_LENGTH = 188;
videojs.Hls.SegmentParser.STREAM_TYPES = STREAM_TYPES = {
h264: 0x1b,
adts: 0x0f
};
......
......@@ -65,5 +65,5 @@
});
};
videojs.hls.Stream = Stream;
videojs.Hls.Stream = Stream;
})(window.videojs);
......
......@@ -21,7 +21,7 @@
*/
var
buffer,
ExpGolomb = window.videojs.hls.ExpGolomb,
ExpGolomb = window.videojs.Hls.ExpGolomb,
expGolomb;
module('Exponential Golomb coding');
......
......@@ -19,7 +19,7 @@
notStrictEqual(actual, expected, [message])
throws(block, [expected], [message])
*/
var FlvTag = window.videojs.hls.FlvTag;
var FlvTag = window.videojs.Hls.FlvTag;
module('FLV tag');
......
......@@ -2,12 +2,12 @@
module('H264 Stream');
var
nalUnitTypes = window.videojs.hls.NALUnitType,
FlvTag = window.videojs.hls.FlvTag;
nalUnitTypes = window.videojs.Hls.NALUnitType,
FlvTag = window.videojs.Hls.FlvTag;
test('metadata is generated for IDRs after a full NAL unit is written', function() {
var
h264Stream = new videojs.hls.H264Stream(),
h264Stream = new videojs.Hls.H264Stream(),
accessUnitDelimiter = new Uint8Array([
0x00,
0x00,
......@@ -62,7 +62,7 @@ test('metadata is generated for IDRs after a full NAL unit is written', function
test('starting PTS values can be negative', function() {
var
h264Stream = new videojs.hls.H264Stream(),
h264Stream = new videojs.Hls.H264Stream(),
accessUnitDelimiter = new Uint8Array([
0x00,
0x00,
......
......@@ -127,7 +127,7 @@
original.addEventListener('change', function() {
var reader = new FileReader();
reader.addEventListener('loadend', function() {
var parser = new videojs.hls.SegmentParser(),
var parser = new videojs.Hls.SegmentParser(),
tags = [parser.getFlvHeader()],
tag,
hex,
......@@ -164,7 +164,7 @@
}
hex = '<pre>'
hex += videojs.hls.utils.hexDump(data);
hex += videojs.Hls.utils.hexDump(data);
hex += '</pre>'
vjsOutput.innerHTML = hex;
......@@ -201,7 +201,7 @@
}
// output the hex dump
hex += videojs.hls.utils.hexDump(bytes);
hex += videojs.Hls.utils.hexDump(bytes);
hex += '</pre>';
workingOutput.innerHTML = hex;
});
......
......@@ -36,26 +36,26 @@
test('throws if the playlist url is empty or undefined', function() {
throws(function() {
videojs.hls.PlaylistLoader();
videojs.Hls.PlaylistLoader();
}, 'requires an argument');
throws(function() {
videojs.hls.PlaylistLoader('');
videojs.Hls.PlaylistLoader('');
}, 'does not accept the empty string');
});
test('starts without any metadata', function() {
var loader = new videojs.hls.PlaylistLoader('master.m3u8');
var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
strictEqual(loader.state, 'HAVE_NOTHING', 'no metadata has loaded yet');
});
test('requests the initial playlist immediately', function() {
new videojs.hls.PlaylistLoader('master.m3u8');
new videojs.Hls.PlaylistLoader('master.m3u8');
strictEqual(requests.length, 1, 'made a request');
strictEqual(requests[0].url, 'master.m3u8', 'requested the initial playlist');
});
test('moves to HAVE_MASTER after loading a master playlist', function() {
var loader = new videojs.hls.PlaylistLoader('master.m3u8');
var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXT-X-STREAM-INF:\n' +
......@@ -67,7 +67,7 @@
test('jumps to HAVE_METADATA when initialized with a media playlist', function() {
var
loadedmetadatas = 0,
loader = new videojs.hls.PlaylistLoader('media.m3u8');
loader = new videojs.Hls.PlaylistLoader('media.m3u8');
loader.on('loadedmetadata', function() {
loadedmetadatas++;
});
......@@ -85,7 +85,7 @@
});
test('jumps to HAVE_METADATA when initialized with a live media playlist', function() {
var loader = new videojs.hls.PlaylistLoader('media.m3u8');
var loader = new videojs.Hls.PlaylistLoader('media.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXTINF:10,\n' +
......@@ -99,7 +99,7 @@
var
loadedPlaylist = 0,
loadedMetadata = 0,
loader = new videojs.hls.PlaylistLoader('master.m3u8');
loader = new videojs.Hls.PlaylistLoader('master.m3u8');
loader.on('loadedplaylist', function() {
loadedPlaylist++;
});
......@@ -131,7 +131,7 @@
});
test('moves to HAVE_CURRENT_METADATA when refreshing the playlist', function() {
var loader = new videojs.hls.PlaylistLoader('live.m3u8');
var loader = new videojs.Hls.PlaylistLoader('live.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXTINF:10,\n' +
......@@ -145,7 +145,7 @@
});
test('returns to HAVE_METADATA after refreshing the playlist', function() {
var loader = new videojs.hls.PlaylistLoader('live.m3u8');
var loader = new videojs.Hls.PlaylistLoader('live.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXTINF:10,\n' +
......@@ -161,7 +161,7 @@
test('emits an error when an initial playlist request fails', function() {
var
errors = [],
loader = new videojs.hls.PlaylistLoader('master.m3u8');
loader = new videojs.Hls.PlaylistLoader('master.m3u8');
loader.on('error', function() {
errors.push(loader.error);
......@@ -175,7 +175,7 @@
test('errors when an initial media playlist request fails', function() {
var
errors = [],
loader = new videojs.hls.PlaylistLoader('master.m3u8');
loader = new videojs.Hls.PlaylistLoader('master.m3u8');
loader.on('error', function() {
errors.push(loader.error);
......@@ -197,7 +197,7 @@
// http://tools.ietf.org/html/draft-pantos-http-live-streaming-12#section-6.3.4
test('halves the refresh timeout if a playlist is unchanged' +
'since the last reload', function() {
new videojs.hls.PlaylistLoader('live.m3u8');
new videojs.Hls.PlaylistLoader('live.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXT-X-MEDIA-SEQUENCE:0\n' +
......@@ -218,7 +218,7 @@
});
test('media-sequence updates are considered a playlist change', function() {
new videojs.hls.PlaylistLoader('live.m3u8');
new videojs.Hls.PlaylistLoader('live.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXT-X-MEDIA-SEQUENCE:0\n' +
......@@ -238,7 +238,7 @@
test('emits an error if a media refresh fails', function() {
var
errors = 0,
loader = new videojs.hls.PlaylistLoader('live.m3u8');
loader = new videojs.Hls.PlaylistLoader('live.m3u8');
loader.on('error', function() {
errors++;
......@@ -256,7 +256,7 @@
});
test('switches media playlists when requested', function() {
var loader = new videojs.hls.PlaylistLoader('master.m3u8');
var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXT-X-STREAM-INF:BANDWIDTH=1\n' +
......@@ -284,7 +284,7 @@
});
test('can switch media playlists based on URI', function() {
var loader = new videojs.hls.PlaylistLoader('master.m3u8');
var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXT-X-STREAM-INF:BANDWIDTH=1\n' +
......@@ -312,7 +312,7 @@
});
test('aborts in-flight playlist refreshes when switching', function() {
var loader = new videojs.hls.PlaylistLoader('master.m3u8');
var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXT-X-STREAM-INF:BANDWIDTH=1\n' +
......@@ -331,7 +331,7 @@
});
test('switching to the active playlist is a no-op', function() {
var loader = new videojs.hls.PlaylistLoader('master.m3u8');
var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXT-X-STREAM-INF:BANDWIDTH=1\n' +
......@@ -350,7 +350,7 @@
});
test('throws an error if a media switch is initiated too early', function() {
var loader = new videojs.hls.PlaylistLoader('master.m3u8');
var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
throws(function() {
loader.media('high.m3u8');
......@@ -368,7 +368,7 @@
});
test('throws an error if a switch to an unrecognized playlist is requested', function() {
var loader = new videojs.hls.PlaylistLoader('master.m3u8');
var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXT-X-STREAM-INF:BANDWIDTH=1\n' +
......@@ -378,4 +378,31 @@
loader.media('unrecognized.m3u8');
}, 'throws an error');
});
test('dispose cancels the refresh timeout', function() {
var loader = new videojs.Hls.PlaylistLoader('live.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXT-X-MEDIA-SEQUENCE:0\n' +
'#EXTINF:10,\n' +
'0.ts\n');
loader.dispose();
// a lot of time passes...
clock.tick(15 * 1000);
strictEqual(requests.length, 0, 'no refresh request was made');
});
test('dispose aborts pending refresh requests', function() {
var loader = new videojs.Hls.PlaylistLoader('live.m3u8');
requests.pop().respond(200, null,
'#EXTM3U\n' +
'#EXT-X-MEDIA-SEQUENCE:0\n' +
'#EXTINF:10,\n' +
'0.ts\n');
clock.tick(10 * 1000);
loader.dispose();
ok(requests[0].aborted, 'refresh request aborted');
});
})(window);
......
......@@ -39,7 +39,7 @@
module('segment parser', {
setup: function() {
parser = new window.videojs.hls.SegmentParser();
parser = new window.videojs.Hls.SegmentParser();
}
});
......@@ -168,11 +168,11 @@
result = result.concat(makePsi(settings));
// ensure the resulting packet is the correct size
result.length = window.videojs.hls.SegmentParser.MP2T_PACKET_LENGTH;
result.length = window.videojs.Hls.SegmentParser.MP2T_PACKET_LENGTH;
return result;
},
h264Type = window.videojs.hls.SegmentParser.STREAM_TYPES.h264,
adtsType = window.videojs.hls.SegmentParser.STREAM_TYPES.adts;
h264Type = window.videojs.Hls.SegmentParser.STREAM_TYPES.h264,
adtsType = window.videojs.Hls.SegmentParser.STREAM_TYPES.adts;
parser.parseSegmentBinaryData(new Uint8Array(makePacket({
programs: {
......