eda08662 by Tom Johnson Committed by David LaPalomento

toms working example base

1 parent 3b271282
1 <!DOCTYPE html> 1 <!DOCTYPE html>
2 <html> 2 <html>
3 <head> 3 <head>
4 <meta charset="utf-8"> 4 <meta charset="utf-8">
5 <title>video.js HLS Plugin Example</title> 5 <title>video.js HLS Plugin Example</title>
6 6
7 <link href="node_modules/video.js/video-js.css" rel="stylesheet"> 7 <link href="node_modules/video.js/video-js.css" rel="stylesheet">
8 8
...@@ -25,9 +25,11 @@ ...@@ -25,9 +25,11 @@
25 25
26 <!-- m3u8 handling --> 26 <!-- m3u8 handling -->
27 <script src="src/m3u8/m3u8.js"></script> 27 <script src="src/m3u8/m3u8.js"></script>
28 <script src="src/m3u8/m3u8-parser.js"></script>
29 <script src="src/m3u8/m3u8-tag-types.js"></script> 28 <script src="src/m3u8/m3u8-tag-types.js"></script>
29 <script src="src/m3u8/m3u8-parser.js"></script>
30 <script src="src/manifest-controller.js"></script> 30 <script src="src/manifest-controller.js"></script>
31 <script src="src/segment-controller.js"></script>
32 <script src="src/hls-playback-controller.js"></script>
31 33
32 <!-- example MPEG2-TS segments --> 34 <!-- example MPEG2-TS segments -->
33 <!-- bipbop --> 35 <!-- bipbop -->
...@@ -48,51 +50,12 @@ ...@@ -48,51 +50,12 @@
48 50
49 // initialize the player 51 // initialize the player
50 videojs.options.flash.swf = 'node_modules/videojs-media-sources/video-js-with-mse.swf'; 52 videojs.options.flash.swf = 'node_modules/videojs-media-sources/video-js-with-mse.swf';
51 video = videojs('video'); 53 video = videojs('video',{},function(){
52 54 this.playbackController = new window.videojs.hls.HLSPlaybackController(this);
53 // create a media source 55 this.playbackController.loadManifest("http://localhost:7070/test/basic-playback/zencoder/gogo/manifest.m3u8",function(data){console.log(data)});
54 mediaSource = new videojs.MediaSource();
55 mediaSource.addEventListener('sourceopen', function(event){
56 var
57 parser = new videojs.hls.SegmentParser(),
58 sourceBuffer = mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"');
59 everything = [];
60
61 // feed parsed bytes into the player
62 everything.push(parser.getFlvHeader());
63 sourceBuffer.appendBuffer(everything[everything.length - 1], video);
64
65 parser.parseSegmentBinaryData(window.bcSegment);
66
67 while (parser.tagsAvailable()) {
68 everything.push(parser.getNextTag().bytes);
69 sourceBuffer.appendBuffer(everything[everything.length - 1], video);
70 }
71 parser.flushTags();
72 while (parser.tagsAvailable()) {
73 everything.push(parser.getNextTag().bytes);
74 sourceBuffer.appendBuffer(everything[everything.length - 1], video);
75 }
76
77 var iframe = document.createElement('iframe');
78 iframe.src = 'data:video/x-flv;base64,' + window.btoa((Array.prototype.map.call(everything.reduce(function(result, next) {
79 var array = new Uint8Array(result.byteLength + next.byteLength);
80 array.set(result);
81 array.set(next, result.length);
82 return array;
83 }), function(byte) {
84 return String.fromCharCode(byte);
85 })).join(''));
86 //console.log(iframe);
87 // document.body.appendChild(iframe);
88 }, false);
89
90 url = videojs.URL.createObjectURL(mediaSource);
91
92 video.src({
93 src: url,
94 type: "video/flv"
95 }); 56 });
96 </script> 57 </script>
58
59
97 </body> 60 </body>
98 </html> 61 </html>
......
1 (function (window) {
2 window.videojs.hls.HLSPlaybackController = function (vjsPlayerReference) {
3 var ManifestController = window.videojs.hls.ManifestController;
4 var SegmentController = window.videojs.hls.SegmentController;
5 var MediaSource = window.videojs.MediaSource;
6 var SegmentParser = window.videojs.hls.SegmentParser;
7 var M3U8 = window.videojs.hls.M3U8;
8
9 var self = this;
10
11 self.player = vjsPlayerReference;
12 self.mediaSource = new MediaSource();
13 self.parser = new SegmentParser();
14
15 self.manifestController = null;
16 self.segmentController = null;
17 self.manifestLoaded = false;
18 self.currentSegment = 0;
19 self.currentManifest = null;
20 self.currentPlaylist = null;
21 self.currentRendition = null;
22
23 // Register Externall Callbacks
24 self.manifestLoadCompleteCallback;
25
26 self.player.on('timeupdate', function () {
27 console.log(self.player.currentTime());
28 });
29
30 self.player.on('onsrcchange', function () {
31 console.log('src change', self.player.currentSrc());
32 //if src.url.m3u8 -- loadManifest.url
33 });
34
35 self.rendition = function (rendition) {
36 self.currentRendition = rendition;
37 self.loadManifest(self.currentRendition.url, self.onM3U8LoadComplete, self.onM3U8LoadError, self.onM3U8Update);
38 };
39
40 self.loadManifest = function (manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback) {
41 self.mediaSource.addEventListener('sourceopen', function (event) {
42 console.log('source open here');
43 // feed parsed bytes into the player
44 self.sourceBuffer = self.mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"');
45
46 self.parser = new SegmentParser();
47
48 self.sourceBuffer.appendBuffer(self.parser.getFlvHeader(), video);
49
50 if( onDataCallback )
51 {
52 self.manifestLoadCompleteCallback = onDataCallback;
53 }
54
55 self.manifestController = new ManifestController();
56 self.manifestController.loadManifest(manifestUrl, self.onM3U8LoadComplete, self.onM3U8LoadError, self.onM3U8Update);
57
58 }, false);
59
60 self.player.src({
61 src: videojs.URL.createObjectURL(self.mediaSource),
62 type: "video/flv"
63 });
64 };
65
66 self.onM3U8LoadComplete = function (m3u8) {
67 if (m3u8.invalidReasons.length == 0) {
68 if(m3u8.isPlaylist)
69 {
70 self.currentPlaylist = m3u8;
71 self.rendition(self.currentPlaylist.playlistItems[0]);
72 } else {
73 self.currentManifest = m3u8;
74 self.manifestLoaded = true;
75
76 self.loadSegment(self.currentManifest.mediaItems[0]);
77
78 if(self.manifestLoadCompleteCallback)
79 {
80 self.manifestLoadCompleteCallback(m3u8);
81 }
82 }
83 }
84 };
85
86 self.onM3U8LoadError = function (error) {
87
88 };
89
90 self.onM3U8Update = function (m3u8) {
91
92 };
93
94 self.loadSegment = function(segment) {
95 self.segmentController = new SegmentController();
96 self.segmentController.loadSegment(segment.url, self.onSegmentLoadComplete, self.onSegmentLoadError);
97
98 };
99
100 self.onSegmentLoadComplete = function (segment) {
101 self.parser.parseSegmentBinaryData(segment.binaryData);
102
103 while (self.parser.tagsAvailable()) {
104 self.sourceBuffer.appendBuffer(self.parser.getNextTag().bytes, self.player);
105 };
106
107 console.log('load another',self.currentSegment,self.currentManifest.mediaItems.length);
108
109 if(self.currentSegment < self.currentManifest.mediaItems.length-1)
110 {
111 console.log('load another');
112 self.loadNextSegment();
113 }
114 };
115
116 self.loadNextSegment = function () {
117 self.currentSegment++;
118 self.loadSegment(self.currentManifest.mediaItems[self.currentSegment]);
119 }
120
121 self.onSegmentLoadError = function (error) {
122
123 };
124
125 };
126 })(this);
...\ No newline at end of file ...\ No newline at end of file
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
4 window.videojs.hls.M3U8Parser = function() { 4 window.videojs.hls.M3U8Parser = function() {
5 5
6 var self = this; 6 var self = this;
7 self.directory;
8
7 var tagTypes = window.videojs.hls.m3u8TagType; 9 var tagTypes = window.videojs.hls.m3u8TagType;
8 var lines = []; 10 var lines = [];
9 var data; 11 var data;
...@@ -31,6 +33,11 @@ ...@@ -31,6 +33,11 @@
31 self.parse = function( rawDataString ) { 33 self.parse = function( rawDataString ) {
32 data = new M3U8(); 34 data = new M3U8();
33 35
36 if(self.directory)
37 {
38 data.directory = self.directory;
39 }
40
34 if( rawDataString != undefined && rawDataString.toString().length > 0 ) 41 if( rawDataString != undefined && rawDataString.toString().length > 0 )
35 { 42 {
36 lines = rawDataString.split('\n'); 43 lines = rawDataString.split('\n');
...@@ -72,6 +79,15 @@ ...@@ -72,6 +79,15 @@
72 segment.url = lines[index+1]; 79 segment.url = lines[index+1];
73 } 80 }
74 81
82 if(segment.url.indexOf("http")===-1 && self.directory)
83 {
84 if(data.directory[data.directory.length-1] === segment.url[0] && segment.url[0] === "/")
85 {
86 segment.url = segment.url.substr(1);
87 }
88 segment.url = self.directory + segment.url;
89 }
90
75 data.mediaItems.push(segment); 91 data.mediaItems.push(segment);
76 92
77 break; 93 break;
...@@ -116,7 +132,7 @@ ...@@ -116,7 +132,7 @@
116 break; 132 break;
117 133
118 case tagTypes.ZEN_TOTAL_DURATION: 134 case tagTypes.ZEN_TOTAL_DURATION:
119 data.totalDuration = self.getTagValue(value); 135 data.totalDuration = Number(self.getTagValue(value));
120 break; 136 break;
121 137
122 case tagTypes.VERSION: 138 case tagTypes.VERSION:
......
1 (function(window) { 1 (function(window) {
2 window.videojs.hls.M3U8 = function() { 2 window.videojs.hls.M3U8 = function() {
3 this.directory = "";
3 this.allowCache = "NO"; 4 this.allowCache = "NO";
4 this.playlistItems = []; 5 this.playlistItems = [];
5 this.mediaItems = []; 6 this.mediaItems = [];
......
1 (function(window) { 1 (function (window) {
2 var M3U8 = window.videojs.hls.M3U8; 2 var M3U8 = window.videojs.hls.M3U8;
3 var M3U8Parser = window.videojs.hls.M3U8Parser; 3 var M3U8Parser = window.videojs.hls.M3U8Parser;
4 4
5 window.videojs.hls.ManifestController = function(){ 5 window.videojs.hls.ManifestController = function () {
6
7 var self = this; 6 var self = this;
8 var parser;
9 var data;
10 7
11 var onDataCallback; 8 self.parser;
12 var onErrorCallback; 9 self.data;
13 var onUpdateCallback; 10 self.url;
11
12 self.onDataCallback;
13 self.onErrorCallback;
14 self.onUpdateCallback;
15
16 self.loadManifest = function (manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback) {
17 self.url = manifestUrl;
14 18
15 self.loadManifest = function ( manifestUrl, onDataCallback, onErrorCallback, onUpdateCallback ) { 19 if (onDataCallback) {
16 self.onDataCallback = onDataCallback; 20 self.onDataCallback = onDataCallback;
17 self.onErrorCallback = onErrorCallback; 21 }
18 self.onUpdateCallback = onUpdateCallback; 22 if (onErrorCallback) {
23 self.onErrorCallback = onErrorCallback;
24 }
25
26 if (onUpdateCallback) {
27 self.onUpdateCallback = onUpdateCallback;
28 }
19 29
20 vjs.get(manifestUrl, self.onManifestLoadComplete, self.onManifestLoadError); 30 vjs.get(manifestUrl, self.onManifestLoadComplete, self.onManifestLoadError);
21 }; 31 };
22 32
23 self.parseManifest = function ( dataAsString ) { 33 self.parseManifest = function (dataAsString) {
24 self.parser = new M3U8Parser(); 34 self.parser = new M3U8Parser();
25 self.data = self.parser.parse( dataAsString ); 35 self.parser.directory = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(self.url).slice(1)[1];
36 self.data = self.parser.parse(dataAsString);
26 37
27 return self.data; 38 return self.data;
28 }; 39 };
29 40
30 self.onManifestLoadComplete = function(response) { 41 self.onManifestLoadComplete = function (response) {
31 var output = self.parseManifest(response); 42 var output = self.parseManifest(response);
32 43
33 if(self.onDataCallback != undefined) 44 if (self.onDataCallback != undefined) {
34 {
35 self.onDataCallback(output); 45 self.onDataCallback(output);
36 } 46 }
37 }; 47 };
38 48
39 self.onManifestLoadError = function(err) { 49 self.onManifestLoadError = function (err) {
40 if(err) 50 if (self.onErrorCallback != undefined) {
41 { 51 self.onErrorCallback((err != undefined) ? err : null);
42 console.log(err.message);
43 }
44
45 if(self.onErrorCallback != undefined)
46 {
47 onErrorCallback((err != undefined) ? err : null);
48 } 52 }
49 }; 53 };
50 } 54 }
......
1 (function(window) { 1 (function(window) {
2 2
3 var SegmentParser = window.videojs.hls.SegmentParser;
4
5 window.videojs.hls.SegmentController = function(){ 3 window.videojs.hls.SegmentController = function(){
6 4
7 var self = this; 5 var self = this;
8 var url;
9 var parser;
10 var requestTimestamp;
11 var responseTimestamp;
12 var data;
13 6
14 var onDataCallback; 7 self.url;
15 var onErrorCallback; 8
16 var onUpdateCallback; 9 self.requestTimestamp;
10 self.responseTimestamp;
11 self.data;
12
13 self.onDataCallback;
14 self.onErrorCallback;
15 self.onUpdateCallback;
17 16
18 self.loadSegment = function ( segmentUrl, onDataCallback, onErrorCallback, onUpdateCallback ) { 17 self.loadSegment = function ( segmentUrl, onDataCallback, onErrorCallback, onUpdateCallback ) {
19 self.url = segmentUrl; 18 self.url = segmentUrl;
...@@ -22,29 +21,31 @@ ...@@ -22,29 +21,31 @@
22 self.onUpdateCallback = onUpdateCallback; 21 self.onUpdateCallback = onUpdateCallback;
23 self.requestTimestamp = new Date().getTime(); 22 self.requestTimestamp = new Date().getTime();
24 23
25 var req = new XMLHttpRequest(); 24 var req = new XMLHttpRequest();
26 req.open('GET', segmentUrl, true); 25 req.open('GET', segmentUrl, true);
27 req.responseType = 'arraybuffer'; 26 req.responseType = 'arraybuffer';
28 req.onload = function(response) { 27 req.onload = function(response) {
29 self.onSegmentLoadComplete(new Uint8Array(req.response)); 28 self.onSegmentLoadComplete(new Uint8Array(req.response));
30 }; 29 };
31 30
32 req.send(null); 31 req.send(null);
32
33 //vjs.get(segmentUrl, self.onSegmentLoadComplete, self.onSegmentLoadError);
33 }; 34 };
34 35
35 self.parseSegment = function ( incomingData ) { 36 self.parseSegment = function ( incomingData ) {
36 // Add David's code later // 37 // Add David's code later //
38 console.log('got segment data', incomingData.byteLength);
37 39
38 self.data = { 40 self.data = {};
39 whatever: incomingData 41 self.data.binaryData = incomingData;
40 };
41 self.data.url = self.url; 42 self.data.url = self.url;
42 self.data.isCached = false; 43 self.data.isCached = false;
43 self.data.requestTimestamp = self.requestTimestamp; 44 self.data.requestTimestamp = self.requestTimestamp;
44 self.data.responseTimestamp = self.responseTimestamp; 45 self.data.responseTimestamp = self.responseTimestamp;
45 self.data.byteLength = incomingData.byteLength; 46 self.data.byteLength = incomingData.byteLength;
46 self.data.isCached = ( parseInt(self.responseTimestamp - self.requestTimestamp) < 75 ); 47 self.data.isCached = ( parseInt(self.responseTimestamp - self.requestTimestamp) < 75 );
47 self.data.throughput = self.calculateThroughput(self.data.byteLength, self.requestTimestamp ,self.responseTimestamp) 48 self.data.throughput = self.calculateThroughput(self.data.byteLength, self.requestTimestamp ,self.responseTimestamp);
48 49
49 return self.data; 50 return self.data;
50 }; 51 };
......
1 (function(window) { 1 (function (window) {
2 /* 2 /*
3 ======== A Handy Little QUnit Reference ======== 3 ======== A Handy Little QUnit Reference ========
4 http://api.qunitjs.com/ 4 http://api.qunitjs.com/
5 5
6 Test methods: 6 Test methods:
7 module(name, {[setup][ ,teardown]}) 7 module(name, {[setup][ ,teardown]})
8 test(name, callback) 8 test(name, callback)
9 expect(numberOfAssertions) 9 expect(numberOfAssertions)
10 stop(increment) 10 stop(increment)
11 start(decrement) 11 start(decrement)
12 Test assertions: 12 Test assertions:
13 ok(value, [message]) 13 ok(value, [message])
14 equal(actual, expected, [message]) 14 equal(actual, expected, [message])
15 notEqual(actual, expected, [message]) 15 notEqual(actual, expected, [message])
16 deepEqual(actual, expected, [message]) 16 deepEqual(actual, expected, [message])
17 notDeepEqual(actual, expected, [message]) 17 notDeepEqual(actual, expected, [message])
18 strictEqual(actual, expected, [message]) 18 strictEqual(actual, expected, [message])
19 notStrictEqual(actual, expected, [message]) 19 notStrictEqual(actual, expected, [message])
20 throws(block, [expected], [message]) 20 throws(block, [expected], [message])
21 */ 21 */
22 var 22 var
23 manifestController, 23 manifestController,
24 segmentController, 24 segmentController,
25 m3u8parser, 25 m3u8parser,
26 parser, 26 parser,
27 27
28 expectedHeader = [ 28 expectedHeader = [
29 0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00, 29 0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00,
30 0x09, 0x00, 0x00, 0x00, 0x00 30 0x09, 0x00, 0x00, 0x00, 0x00
31 ], 31 ],
32 testAudioTag, 32 testAudioTag,
33 testVideoTag, 33 testVideoTag,
34 testScriptTag, 34 testScriptTag,
35 asciiFromBytes, 35 asciiFromBytes,
36 testScriptString, 36 testScriptString,
37 testScriptEcmaArray; 37 testScriptEcmaArray;
38 38
39 module('environment'); 39 module('environment');
40 40
41 test('is sane', function() { 41 test('is sane', function () {
42 expect(1); 42 expect(1);
43 ok(true); 43 ok(true);
44 }); 44 });
45 45
46 module('segment parser', { 46 module('segment parser', {
47 setup: function() { 47 setup: function () {
48 parser = new window.videojs.hls.SegmentParser(); 48 parser = new window.videojs.hls.SegmentParser();
49 } 49 }
50 }); 50 });
51 51
52 test('creates an flv header', function() { 52 test('creates an flv header', function () {
53 var header = Array.prototype.slice.call(parser.getFlvHeader()); 53 var header = Array.prototype.slice.call(parser.getFlvHeader());
54 ok(header, 'the header is truthy'); 54 ok(header, 'the header is truthy');
55 equal(9 + 4, header.length, 'the header length is correct'); 55 equal(9 + 4, header.length, 'the header length is correct');
56 equal(header[0], 'F'.charCodeAt(0), 'the first character is "F"'); 56 equal(header[0], 'F'.charCodeAt(0), 'the first character is "F"');
57 equal(header[1], 'L'.charCodeAt(0), 'the second character is "L"'); 57 equal(header[1], 'L'.charCodeAt(0), 'the second character is "L"');
58 equal(header[2], 'V'.charCodeAt(0), 'the third character is "V"'); 58 equal(header[2], 'V'.charCodeAt(0), 'the third character is "V"');
59 59
60 deepEqual(expectedHeader, header, 'the rest of the header is correct'); 60 deepEqual(expectedHeader, header, 'the rest of the header is correct');
61 }); 61 });
62 62
63 test('parses the first bipbop segment', function() { 63 test('parses the first bipbop segment', function () {
64 var tag, bytes, i; 64 var tag, bytes, i;
65 parser.parseSegmentBinaryData(window.bcSegment); 65 parser.parseSegmentBinaryData(window.bcSegment);
66 66
67 ok(parser.tagsAvailable(), 'tags are available'); 67 ok(parser.tagsAvailable(), 'tags are available');
68 68
69 console.log('h264 tags:', parser.stats.h264Tags(), 69 console.log('h264 tags:', parser.stats.h264Tags(),
70 'aac tags:', parser.stats.aacTags()); 70 'aac tags:', parser.stats.aacTags());
71 }); 71 });
72 72
73 testAudioTag = function(tag) { 73 testAudioTag = function (tag) {
74 var 74 var
75 byte = tag.bytes[11], 75 byte = tag.bytes[11],
76 format = (byte & 0xF0) >>> 4, 76 format = (byte & 0xF0) >>> 4,
77 soundRate = byte & 0x03, 77 soundRate = byte & 0x03,
78 soundSize = (byte & 0x2) >>> 1, 78 soundSize = (byte & 0x2) >>> 1,
79 soundType = byte & 0x1, 79 soundType = byte & 0x1,
80 aacPacketType = tag.bytes[12]; 80 aacPacketType = tag.bytes[12];
81 81
82 equal(10, format, 'the audio format is aac'); 82 equal(10, format, 'the audio format is aac');
83 equal(3, soundRate, 'the sound rate is 44kHhz'); 83 equal(3, soundRate, 'the sound rate is 44kHhz');
84 equal(1, soundSize, 'the sound size is 16-bit samples'); 84 equal(1, soundSize, 'the sound size is 16-bit samples');
85 equal(1, soundType, 'the sound type is stereo'); 85 equal(1, soundType, 'the sound type is stereo');
86 86
87 ok(aacPacketType === 0 || aacPacketType === 1, 'aac packets should have a valid type'); 87 ok(aacPacketType === 0 || aacPacketType === 1, 'aac packets should have a valid type');
88 }; 88 };
89 89
90 testVideoTag = function(tag) { 90 testVideoTag = function (tag) {
91 var 91 var
92 byte = tag.bytes[11], 92 byte = tag.bytes[11],
93 frameType = (byte & 0xF0) >>> 4, 93 frameType = (byte & 0xF0) >>> 4,
94 codecId = byte & 0x0F, 94 codecId = byte & 0x0F,
95 packetType = tag.bytes[12], 95 packetType = tag.bytes[12],
96 compositionTime = (tag.view.getInt32(13) & 0xFFFFFF00) >> 8, 96 compositionTime = (tag.view.getInt32(13) & 0xFFFFFF00) >> 8,
97 nalHeader; 97 nalHeader;
98 98
99 // payload starts at tag.bytes[16] 99 // payload starts at tag.bytes[16]
100 100
101 101
102 // XXX: I'm not sure that frame types 3-5 are invalid 102 // XXX: I'm not sure that frame types 3-5 are invalid
103 ok(frameType === 1 || frameType === 2, 103 ok(frameType === 1 || frameType === 2,
104 'the frame type should be valid'); 104 'the frame type should be valid');
105 105
106 equal(7, codecId, 'the codec ID is AVC for h264'); 106 equal(7, codecId, 'the codec ID is AVC for h264');
107 ok(packetType <= 2 && packetType >= 0, 'the packet type is within [0, 2]'); 107 ok(packetType <= 2 && packetType >= 0, 'the packet type is within [0, 2]');
108 if (packetType !== 1) { 108 if (packetType !== 1) {
109 equal(0, 109 equal(0,
110 compositionTime, 110 compositionTime,
111 'the composition time is zero for non-NALU packets'); 111 'the composition time is zero for non-NALU packets');
112 } 112 }
113 113
114 // TODO: the rest of the bytes are an NLU unit 114 // TODO: the rest of the bytes are an NLU unit
115 if (packetType === 0) { 115 if (packetType === 0) {
116 // AVC decoder configuration record 116 // AVC decoder configuration record
117 } else { 117 } else {
118 // NAL units 118 // NAL units
119 testNalUnit(tag.bytes.subarray(16)); 119 testNalUnit(tag.bytes.subarray(16));
120 } 120 }
121 }; 121 };
122 122
123 testNalUnit = function(bytes) { 123 testNalUnit = function (bytes) {
124 var 124 var
125 nalHeader = bytes[0], 125 nalHeader = bytes[0],
126 unitType = nalHeader & 0x1F; 126 unitType = nalHeader & 0x1F;
127 127
128 equal(0, (nalHeader & 0x80) >>> 7, 'the first bit is always 0'); 128 equal(0, (nalHeader & 0x80) >>> 7, 'the first bit is always 0');
129 // equal(90, (nalHeader & 0x60) >>> 5, 'the NAL reference indicator is something'); 129 // equal(90, (nalHeader & 0x60) >>> 5, 'the NAL reference indicator is something');
130 // ok(unitType > 0, 'NAL unit type ' + unitType + ' is greater than 0'); 130 // ok(unitType > 0, 'NAL unit type ' + unitType + ' is greater than 0');
131 // ok(unitType < 22 , 'NAL unit type ' + unitType + ' is less than 22'); 131 // ok(unitType < 22 , 'NAL unit type ' + unitType + ' is less than 22');
132 }; 132 };
133 133
134 134
135 asciiFromBytes = function(bytes) { 135 asciiFromBytes = function (bytes) {
136 var 136 var
137 string = [], 137 string = [],
138 i = bytes.byteLength; 138 i = bytes.byteLength;
139 139
140 while (i--) { 140 while (i--) {
141 string[i] = String.fromCharCode(bytes[i]); 141 string[i] = String.fromCharCode(bytes[i]);
142 } 142 }
143 return string.join(''); 143 return string.join('');
144 }; 144 };
145 145
146 testScriptString = function(tag, offset, expected) { 146 testScriptString = function (tag, offset, expected) {
147 var type = tag.bytes[offset], 147 var type = tag.bytes[offset],
148 stringLength = tag.view.getUint16(offset + 1), 148 stringLength = tag.view.getUint16(offset + 1),
149 string, 149 string,
150 i = expected.length; 150 i = expected.length;
151 151
152 equal(2, type, 'the script element is of string type'); 152 equal(2, type, 'the script element is of string type');
153 equal(stringLength, expected.length, 'the script string length is correct'); 153 equal(stringLength, expected.length, 'the script string length is correct');
154 string = asciiFromBytes(tag.bytes.subarray(offset + 3, 154 string = asciiFromBytes(tag.bytes.subarray(offset + 3,
155 offset + 3 + stringLength)); 155 offset + 3 + stringLength));
156 equal(expected, string, 'the string value is "' + expected + '"'); 156 equal(expected, string, 'the string value is "' + expected + '"');
157 }; 157 };
158 158
159 testScriptEcmaArray = function(tag, start) { 159 testScriptEcmaArray = function (tag, start) {
160 var 160 var
161 numItems = tag.view.getUint32(start), 161 numItems = tag.view.getUint32(start),
162 i = numItems, 162 i = numItems,
163 offset = start + 4, 163 offset = start + 4,
164 length, 164 length,
165 type; 165 type;
166 166
167 while (i--) { 167 while (i--) {
168 length = tag.view.getUint16(offset); 168 length = tag.view.getUint16(offset);
169 169
170 // advance offset to the property value 170 // advance offset to the property value
171 offset += 2 + length; 171 offset += 2 + length;
172 172
173 type = tag.bytes[offset]; 173 type = tag.bytes[offset];
174 ok(type === 1 || type === 0, 174 ok(type === 1 || type === 0,
175 'the ecma array property value type is number or boolean'); 175 'the ecma array property value type is number or boolean');
176 offset++; 176 offset++;
177 if (type) { 177 if (type) {
178 // boolean 178 // boolean
179 ok(tag.bytes[offset] === 0 || tag.bytes[offset] === 1, 179 ok(tag.bytes[offset] === 0 || tag.bytes[offset] === 1,
180 'the script boolean value is 0 or 1'); 180 'the script boolean value is 0 or 1');
181 offset++; 181 offset++;
182 } else { 182 } else {
183 // number 183 // number
184 ok(!isNaN(tag.view.getFloat64(offset)), 'the value is not NaN'); 184 ok(!isNaN(tag.view.getFloat64(offset)), 'the value is not NaN');
185 offset += 8; 185 offset += 8;
186 } 186 }
187 } 187 }
188 equal(tag.bytes[offset], 0, 'the property array terminator is valid'); 188 equal(tag.bytes[offset], 0, 'the property array terminator is valid');
189 equal(tag.bytes[offset + 1], 0, 'the property array terminator is valid'); 189 equal(tag.bytes[offset + 1], 0, 'the property array terminator is valid');
190 equal(tag.bytes[offset + 2], 9, 'the property array terminator is valid'); 190 equal(tag.bytes[offset + 2], 9, 'the property array terminator is valid');
191 }; 191 };
192 192
193 testScriptTag = function(tag) { 193 testScriptTag = function (tag) {
194 testScriptString(tag, 11, 'onMetaData'); 194 testScriptString(tag, 11, 'onMetaData');
195 195
196 // the onMetaData object is stored as an 'ecma array', an array with non- 196 // the onMetaData object is stored as an 'ecma array', an array with non-
197 // integer indices (i.e. a dictionary or hash-map). 197 // integer indices (i.e. a dictionary or hash-map).
198 equal(8, tag.bytes[24], 'onMetaData is of ecma array type'); 198 equal(8, tag.bytes[24], 'onMetaData is of ecma array type');
199 testScriptEcmaArray(tag, 25); 199 testScriptEcmaArray(tag, 25);
200 }; 200 };
201 201
202 test('the flv tags are well-formed', function() { 202 test('the flv tags are well-formed', function () {
203 var 203 var
204 tag, 204 tag,
205 byte, 205 byte,
206 type, 206 type,
207 lastTime = 0; 207 lastTime = 0;
208 parser.parseSegmentBinaryData(window.bcSegment); 208 parser.parseSegmentBinaryData(window.bcSegment);
209 209
210 while (parser.tagsAvailable()) { 210 while (parser.tagsAvailable()) {
211 tag = parser.getNextTag(); 211 tag = parser.getNextTag();
212 type = tag.bytes[0]; 212 type = tag.bytes[0];
213 213
214 // generic flv headers 214 // generic flv headers
215 ok(type === 8 || type === 9 || type === 18, 215 ok(type === 8 || type === 9 || type === 18,
216 'the type field specifies audio, video or script'); 216 'the type field specifies audio, video or script');
217 217
218 byte = (tag.view.getUint32(1) & 0xFFFFFF00) >>> 8; 218 byte = (tag.view.getUint32(1) & 0xFFFFFF00) >>> 8;
219 equal(tag.bytes.byteLength - 11 - 4, byte, 'the size field is correct'); 219 equal(tag.bytes.byteLength - 11 - 4, byte, 'the size field is correct');
220 220
221 byte = tag.view.getUint32(5) & 0xFFFFFF00; 221 byte = tag.view.getUint32(5) & 0xFFFFFF00;
222 ok(byte >= lastTime, 'the timestamp for the tag is greater than zero'); 222 ok(byte >= lastTime, 'the timestamp for the tag is greater than zero');
223 lastTime = byte; 223 lastTime = byte;
224 224
225 // tag type-specific headers 225 // tag type-specific headers
226 ({ 226 ({
227 8: testAudioTag, 227 8: testAudioTag,
228 9: testVideoTag, 228 9: testVideoTag,
229 18: testScriptTag 229 18: testScriptTag
230 })[type](tag); 230 })[type](tag);
231 231
232 // previous tag size 232 // previous tag size
233 equal(tag.bytes.byteLength - 4, 233 equal(tag.bytes.byteLength - 4,
234 tag.view.getUint32(tag.bytes.byteLength - 4), 234 tag.view.getUint32(tag.bytes.byteLength - 4),
235 'the size of the previous tag is correct'); 235 'the size of the previous tag is correct');
236 } 236 }
237 }); 237 });
238 238
239 /* 239 /*
240 M3U8 Test Suite 240 M3U8 Test Suite
241 */ 241 */
242 242
243 module('m3u8 parser', { 243 module('m3u8 parser', {
244 setup: function() { 244 setup: function () {
245 m3u8parser = new window.videojs.hls.M3U8Parser(); 245 m3u8parser = new window.videojs.hls.M3U8Parser();
246 } 246 }
247 }); 247 });
248 248
249 test('should create my parser', function() { 249 test('should create my parser', function () {
250 ok(m3u8parser != undefined); 250 ok(m3u8parser != undefined);
251 } 251 }
252 ); 252 );
253 253
254 test('should successfully parse manifest data', function() { 254 test('should successfully parse manifest data', function () {
255 var parsedData = m3u8parser.parse(window.playlistData); 255 var parsedData = m3u8parser.parse(window.playlistData);
256 ok(parsedData); 256 ok(parsedData);
257 } 257 }
258 ); 258 );
259 259
260 test('test for expected results', function() { 260 test('test for expected results', function () {
261 var data = m3u8parser.parse(window.playlistData); 261 var data = m3u8parser.parse(window.playlistData);
262 262
263 notEqual(data, null, 'data is not NULL'); 263 notEqual(data, null, 'data is not NULL');
264 equal(data.invalidReasons.length, 0,'data has 0 invalid reasons'); 264 equal(data.invalidReasons.length, 0, 'data has 0 invalid reasons');
265 equal(data.hasValidM3UTag, true, 'data has valid EXTM3U'); 265 equal(data.hasValidM3UTag, true, 'data has valid EXTM3U');
266 equal(data.targetDuration, 10, 'data has correct TARGET DURATION'); 266 equal(data.targetDuration, 10, 'data has correct TARGET DURATION');
267 equal(data.allowCache, "NO", 'acceptable ALLOW CACHE'); 267 equal(data.allowCache, "NO", 'acceptable ALLOW CACHE');
...@@ -275,109 +275,109 @@ ...@@ -275,109 +275,109 @@
275 ); 275 );
276 276
277 module('brightcove playlist', { 277 module('brightcove playlist', {
278 setup: function() { 278 setup: function () {
279 m3u8parser = new window.videojs.hls.M3U8Parser(); 279 m3u8parser = new window.videojs.hls.M3U8Parser();
280 } 280 }
281 }); 281 });
282 282
283 test('should parse a brightcove manifest data', function() { 283 test('should parse a brightcove manifest data', function () {
284 var data = m3u8parser.parse(window.brightcove_playlist_data); 284 var data = m3u8parser.parse(window.brightcove_playlist_data);
285 285
286 ok(data); 286 ok(data);
287 equal(data.playlistItems.length, 4, 'Has correct rendition count'); 287 equal(data.playlistItems.length, 4, 'Has correct rendition count');
288 equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct' ); 288 equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct');
289 equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct' ); 289 equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct');
290 equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct' ); 290 equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct');
291 equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct' ); 291 equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct');
292 292
293 } 293 }
294 ); 294 );
295 295
296 module('manifest controller', { 296 module('manifest controller', {
297 setup: function() { 297 setup: function () {
298 manifestController = new window.videojs.hls.ManifestController(); 298 manifestController = new window.videojs.hls.ManifestController();
299 this.vjsget = vjs.get; 299 this.vjsget = vjs.get;
300 vjs.get = function(url, success, error){ 300 vjs.get = function (url, success, error) {
301 console.log(url); 301 console.log(url);
302 success(window.brightcove_playlist_data); 302 success(window.brightcove_playlist_data);
303 }; 303 };
304 }, 304 },
305 teardown: function() { 305 teardown: function () {
306 vjs.get = this.vjsget; 306 vjs.get = this.vjsget;
307 } 307 }
308 }); 308 });
309 309
310 test('should create', function() { 310 test('should create', function () {
311 ok(manifestController); 311 ok(manifestController);
312 }); 312 });
313 313
314 test('should return a parsed object', function() { 314 test('should return a parsed object', function () {
315 var data = manifestController.parseManifest(window.brightcove_playlist_data); 315 var data = manifestController.parseManifest(window.brightcove_playlist_data);
316 316
317 ok(data); 317 ok(data);
318 318
319 equal(data.playlistItems.length, 4, 'Has correct rendition count'); 319 equal(data.playlistItems.length, 4, 'Has correct rendition count');
320 equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct' ); 320 equal(data.playlistItems[0].bandwidth, 240000, 'First rendition index bandwidth is correct');
321 equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct' ); 321 equal(data.playlistItems[0]["program-id"], 1, 'First rendition index program-id is correct');
322 equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct' ); 322 equal(data.playlistItems[0].resolution.width, 396, 'First rendition index resolution width is correct');
323 equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct' ); 323 equal(data.playlistItems[0].resolution.height, 224, 'First rendition index resolution height is correct');
324 }) 324 })
325 325
326 test('should get a manifest from hermes', function() { 326 test('should get a manifest from hermes', function () {
327 var hermesUrl = "http://localhost:7070/test/basic-playback/brightcove/16x9-master.m3u8"; 327 var hermesUrl = "http://localhost:7070/test/basic-playback/brightcove/16x9-master.m3u8";
328 328
329 manifestController.loadManifest( 329 manifestController.loadManifest(
330 hermesUrl, 330 hermesUrl,
331 function(responseData){ 331 function (responseData) {
332 ok(true); 332 ok(true);
333 }, 333 },
334 function(errorData){ 334 function (errorData) {
335 console.log('got error data'); 335 console.log('got error data');
336 }, 336 },
337 function(updateData){ 337 function (updateData) {
338 console.log('got update data'); 338 console.log('got update data');
339 } 339 }
340 ) 340 )
341 }); 341 });
342 342
343 module('segment controller', { 343 module('segment controller', {
344 setup: function() { 344 setup: function () {
345 segmentController = new window.videojs.hls.SegmentController(); 345 segmentController = new window.videojs.hls.SegmentController();
346 this.vjsget = vjs.get; 346 this.vjsget = vjs.get;
347 vjs.get = function(url, success, error){ 347 vjs.get = function (url, success, error) {
348 console.log('load segment url', url); 348 console.log('load segment url', url);
349 success(window.bcSegment); 349 success(window.bcSegment);
350 }; 350 };
351 }, 351 },
352 teardown: function() { 352 teardown: function () {
353 vjs.get = this.vjsget; 353 vjs.get = this.vjsget;
354 } 354 }
355 }); 355 });
356 356
357 test('should get a segment data', function() { 357 test('should get a segment data', function () {
358 ok(true); 358 ok(true);
359 var hermesUrl = "http://localhost:7070/test/ts-files/brightcove/s-1.ts"; 359 var hermesUrl = "http://localhost:7070/test/ts-files/brightcove/s-1.ts";
360 360
361 segmentController.loadSegment( 361 segmentController.loadSegment(
362 hermesUrl, 362 hermesUrl,
363 function(responseData){ 363 function (responseData) {
364 console.log('got response from segment controller'); 364 console.log('got response from segment controller');
365 ok(true); 365 ok(true);
366 366
367 }, 367 },
368 function(errorData){ 368 function (errorData) {
369 console.log('got error data'); 369 console.log('got error data');
370 }, 370 },
371 function(updateData){ 371 function (updateData) {
372 console.log('got update data'); 372 console.log('got update data');
373 } 373 }
374 ) 374 )
375 } 375 }
376 ) 376 )
377 377
378 test('bandwidth calulation test', function() { 378 test('bandwidth calulation test', function () {
379 var multiSecondData = segmentController.calculateThroughput(10000,1000,2000); 379 var multiSecondData = segmentController.calculateThroughput(10000, 1000, 2000);
380 var subSecondData = segmentController.calculateThroughput(10000,1000,1500); 380 var subSecondData = segmentController.calculateThroughput(10000, 1000, 1500);
381 equal(multiSecondData, 80000, 'MULTI-Second bits per second calculation'); 381 equal(multiSecondData, 80000, 'MULTI-Second bits per second calculation');
382 equal(subSecondData, 160000, 'SUB-Second bits per second calculation'); 382 equal(subSecondData, 160000, 'SUB-Second bits per second calculation');
383 383
......