17dd1e68 by David LaPalomento

Create test harness and start porting segment parser

Create a qunit test to verify the flv header. Start work on parsing the m2ts packets. The test harness is using a hard-coded Uint8Array which is the first segment of the "bipbop" video. Currently the segment parser is consuming bytes and passing them off to the internal packet parsing function but that isn't yet implemented.
1 parent 7f0a59b2
...@@ -53,7 +53,7 @@ module.exports = function(grunt) { ...@@ -53,7 +53,7 @@ module.exports = function(grunt) {
53 options: { 53 options: {
54 jshintrc: 'test/.jshintrc' 54 jshintrc: 'test/.jshintrc'
55 }, 55 },
56 src: ['test/**/*.js'] 56 src: ['test/**/*.js', '!test/tsSegment.js']
57 }, 57 },
58 }, 58 },
59 watch: { 59 watch: {
......
1 { 1 {
2 "curly": true, 2 "curly": true,
3 "eqeqeq": true, 3 "eqeqeq": true,
4 "globals": {
5 "console": true
6 },
4 "immed": true, 7 "immed": true,
5 "latedef": true, 8 "latedef": true,
6 "newcap": true, 9 "newcap": true,
......
1 /*
2 * aac-stream
3 *
4 *
5 * Copyright (c) 2013 Brightcove
6 * All rights reserved.
7 */
8
9 (function(window) {
10
11 window.videojs.hls.AacStream = function(){
12 this.tags = [];
13 };
14
15 })(this);
1 (function(window) {
2
3 window.videojs.hls.FlvTag = function() {};
4
5 /*
6 package com.videojs.providers.hls.utils{
7
8 import flash.utils.ByteArray;
9 import flash.utils.Endian;
10
11 public class FlvTag extends ByteArray
12 {
13 public static const AUDIO_TAG:uint = 0x08;
14 public static const VIDEO_TAG:uint = 0x09;
15 public static const METADATA_TAG:uint = 0x12;
16
17 public var keyFrame:Boolean = false;
18 private var extraData:Boolean = false;
19 private var adHoc:uint = 0; // Counter if this is a metadata tag, nal start marker if this is a video tag. unused if this is an audio tag
20
21 public var pts:uint;
22 public var dts:uint;
23
24 public static function isAudioFrame(tag:ByteArray):Boolean
25 {
26 return AUDIO_TAG == tag[0];
27 }
28
29 public static function isVideoFrame(tag:ByteArray):Boolean
30 {
31 return VIDEO_TAG == tag[0];
32 }
33
34 public static function isMetaData(tag:ByteArray):Boolean
35 {
36 return METADATA_TAG == tag[0];
37 }
38
39 public static function isKeyFrame(tag:ByteArray):Boolean
40 {
41 if ( isVideoFrame(tag) )
42 return tag[11] == 0x17;
43
44 if( isAudioFrame(tag) )
45 return true;
46
47 if( isMetaData(tag) )
48 return true;
49
50 return false;
51 }
52
53 public static function frameTime(tag:ByteArray):uint
54 {
55 var pts:uint = tag[ 4] << 16;
56 pts |= tag[ 5] << 8;
57 pts |= tag[ 6] << 0;
58 pts |= tag[ 7] << 24;
59 return pts;
60 }
61
62
63 public function FlvTag(type:uint, ed:Boolean = false)
64 {
65 super();
66 extraData = ed;
67 this.endian = Endian.BIG_ENDIAN;
68 switch(type)
69 {
70 case VIDEO_TAG: this.length = 16; break;
71 case AUDIO_TAG: this.length = 13; keyFrame = true; break;
72 case METADATA_TAG: this.length = 29; keyFrame = true; break;
73 default: throw("Error Unknown TagType");
74 }
75
76 this[0] = type
77 this.position = this.length;
78 keyFrame = extraData; // Defaults to false
79 pts = dts = 0;
80 }
81
82 // Negative index into array
83 public function negIndex(pos:uint):int
84 {
85 return this[this.length - pos];
86 }
87
88 // The functions below ONLY work when this[0] == VIDEO_TAG.
89 // We are not going to check for that because we dont want the overhead
90 public function nalUnitSize(nal:ByteArray = null):int
91 {
92 if( 0 == adHoc )
93 return 0;
94
95 return this.length - ( adHoc + 4 );
96 }
97
98
99 public function startNalUnit():void
100 { // remember position and add 4 bytes
101 if ( 0 < adHoc )
102 {
103 throw new Error("Attempted to create new NAL wihout closing the old one");
104 }
105
106 // reserve 4 bytes for nal unit size
107 adHoc = this.length;
108 this.length += 4;
109 this.position = this.length;
110 }
111
112 public function endNalUnit(nal:ByteArray = null):void
113 { // Rewind to the marker and write the size
114 if ( this.length == adHoc + 4 )
115 {
116 this.length -= 4; // we started a nal unit, but didnt write one, so roll back the 4 byte size value
117 }
118 else
119 if ( 0 < adHoc )
120 {
121 var nalStart:uint = adHoc + 4;
122 var nalLength:uint = this.length - nalStart;
123
124 this.position = adHoc;
125 this.writeUnsignedInt( nalLength );
126 this.position = this.length;
127
128 if ( null != nal ) // If the user pass in a ByteArray, copy the NAL to it.
129 nal.writeBytes( this, nalStart, nalLength );
130 }
131
132 adHoc = 0;
133 }
134
135 public function writeMetaDataDouble(key:String, val:Number):void
136 {
137 writeShort ( key.length );
138 writeUTFBytes ( key );
139 writeByte ( 0x00 );
140 writeDouble ( val );
141 ++adHoc;
142 }
143
144 public function writeMetaDataBoolean(key:String, val:Boolean):void
145 {
146 writeShort ( key.length );
147 writeUTFBytes ( key );
148 writeByte ( 0x01 );
149 writeByte ( true == val ? 0x01 : 0x00 );
150 ++adHoc;
151 }
152
153 public function finalize():ByteArray
154 {
155 switch(this[0])
156 { // Video Data
157 case VIDEO_TAG:
158 this[11] = ( ( keyFrame || extraData ) ? 0x10 : 0x20 ) | 0x07; // We only support AVC, 1 = key frame (for AVC, a seekable frame), 2 = inter frame (for AVC, a non-seekable frame)
159 this[12] = extraData ? 0x00 : 0x01;
160
161 var dtsDelta:int = pts - dts;
162 this[13] = ( dtsDelta & 0x00FF0000 ) >>> 16;
163 this[14] = ( dtsDelta & 0x0000FF00 ) >>> 8;
164 this[15] = ( dtsDelta & 0x000000FF ) >>> 0;
165 break;
166
167 case AUDIO_TAG:
168 this[11] = 0xAF;
169 this[12] = extraData ? 0x00 : 0x01;
170 break;
171
172 case METADATA_TAG:
173 this.position = 11;
174 writeByte(0x02); // String type
175 writeShort(0x0A); // 10 Bytes
176 writeUTFBytes("onMetaData");
177 writeByte(0x08); // Array type
178 writeUnsignedInt( adHoc );
179 this.position = this.length;
180 writeUnsignedInt( 0x09 ); // End Data Tag
181 break;
182 }
183
184 var len:int = this.length - 11;
185
186 this[ 1] = ( len & 0x00FF0000 ) >>> 16;
187 this[ 2] = ( len & 0x0000FF00 ) >>> 8;
188 this[ 3] = ( len & 0x000000FF ) >>> 0;
189 this[ 4] = ( pts & 0x00FF0000 ) >>> 16;
190 this[ 5] = ( pts & 0x0000FF00 ) >>> 8;
191 this[ 6] = ( pts & 0x000000FF ) >>> 0;
192 this[ 7] = ( pts & 0xFF000000 ) >>> 24;
193 this[ 8] = 0;
194 this[ 9] = 0;
195 this[10] = 0;
196
197 this.writeUnsignedInt( this.length );
198 return this;
199 }
200 }
201 }
202 */
203 })(this);
1 /*
2 * h264-stream
3 *
4 *
5 * Copyright (c) 2013 Brightcove
6 * All rights reserved.
7 */
8
9 (function(window) {
10
11 window.videojs.hls.H264Stream = function(){
12 this.tags = [];
13 };
14
15 })(this);
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
6 * All rights reserved. 6 * All rights reserved.
7 */ 7 */
8 8
9 (function() { 9 (function(window) {
10 10
11 })(); 11 window.videojs.hls = {};
12
13 })(this);
......
This diff could not be displayed because it is too large.
...@@ -6,8 +6,46 @@ ...@@ -6,8 +6,46 @@
6 <!-- Load local QUnit. --> 6 <!-- Load local QUnit. -->
7 <link rel="stylesheet" href="../libs/qunit/qunit.css" media="screen"> 7 <link rel="stylesheet" href="../libs/qunit/qunit.css" media="screen">
8 <script src="../libs/qunit/qunit.js"></script> 8 <script src="../libs/qunit/qunit.js"></script>
9 <!-- Load local lib and tests. --> 9
10 <!-- video.js -->
11 <script src="../libs/video-js/src/js/core.js"></script>
12 <script src="../libs/video-js/src/js/core-object.js"></script>
13 <script src="../libs/video-js/src/js/events.js"></script>
14 <script src="../libs/video-js/src/js/lib.js"></script>
15 <script src="../libs/video-js/src/js/component.js"></script>
16 <script src="../libs/video-js/src/js/button.js"></script>
17 <script src="../libs/video-js/src/js/slider.js"></script>
18 <script src="../libs/video-js/src/js/menu.js"></script>
19 <script src="../libs/video-js/src/js/player.js"></script>
20 <script src="../libs/video-js/src/js/control-bar/control-bar.js"></script>
21 <script src="../libs/video-js/src/js/control-bar/play-toggle.js"></script>
22 <script src="../libs/video-js/src/js/control-bar/time-display.js"></script>
23 <script src="../libs/video-js/src/js/control-bar/fullscreen-toggle.js"></script>
24 <script src="../libs/video-js/src/js/control-bar/progress-control.js"></script>
25 <script src="../libs/video-js/src/js/control-bar/volume-control.js"></script>
26 <script src="../libs/video-js/src/js/control-bar/mute-toggle.js"></script>
27 <script src="../libs/video-js/src/js/control-bar/volume-menu-button.js"></script>
28 <script src="../libs/video-js/src/js/poster.js"></script>
29 <script src="../libs/video-js/src/js/loading-spinner.js"></script>
30 <script src="../libs/video-js/src/js/big-play-button.js"></script>
31 <script src="../libs/video-js/src/js/media/media.js"></script>
32 <script src="../libs/video-js/src/js/media/html5.js"></script>
33 <script src="../libs/video-js/src/js/media/flash.js"></script>
34 <script src="../libs/video-js/src/js/media/loader.js"></script>
35 <script src="../libs/video-js/src/js/tracks.js"></script>
36 <script src="../libs/video-js/src/js/json.js"></script>
37 <script src="../libs/video-js/src/js/setup.js"></script>
38 <script src="../libs/video-js/src/js/plugins.js"></script>
39
40 <!-- HLS plugin -->
10 <script src="../src/video-js-hls.js"></script> 41 <script src="../src/video-js-hls.js"></script>
42 <script src="../src/h264-stream.js"></script>
43 <script src="../src/aac-stream.js"></script>
44 <script src="../src/flv-tag.js"></script>
45 <script src="../src/segment-parser.js"></script>
46 <!-- an example MPEG2-TS segment -->
47 <script src="tsSegment.js"></script>
48
11 <script src="video-js-hls_test.js"></script> 49 <script src="video-js-hls_test.js"></script>
12 </head> 50 </head>
13 <body> 51 <body>
......
1 (function() { 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/
...@@ -19,15 +19,33 @@ ...@@ -19,15 +19,33 @@
19 notStrictEqual(actual, expected, [message]) 19 notStrictEqual(actual, expected, [message])
20 throws(block, [expected], [message]) 20 throws(block, [expected], [message])
21 */ 21 */
22 var parser;
22 23
23 module('environment', { 24 module('environment');
24 // This will run before each test in this module.
25 setup: function() {
26 }
27 });
28 25
29 test('is sane', function() { 26 test('is sane', function() {
30 expect(1); 27 expect(1);
31 ok(true); 28 ok(true);
32 }); 29 });
33 }()); 30
31 module('segment parser', {
32 setup: function() {
33 parser = new window.videojs.hls.SegmentParser();
34 }
35 });
36
37 test('creates an flv header', function() {
38 var header = parser.getFlvHeader();
39 ok(header, 'the header is truthy');
40 equal(9 + 4, header.byteLength, 'the header length is correct');
41 equal(header[0], 'F'.charCodeAt(0), 'the signature is correct');
42 equal(header[1], 'L'.charCodeAt(0), 'the signature is correct');
43 equal(header[2], 'V'.charCodeAt(0), 'the signature is correct');
44 });
45
46 test('parses the first bipbop segment', function() {
47 parser.parseSegmentBinaryData(window.testSegment);
48
49 ok(parser.tagsAvailable(), 'tags should be available');
50 });
51 })(this);
......