Segment parser finishes with the example TS segment
The parser can process the example TS but it doesn't appear to be correctly formed. The player does seem to correctly interpret the video duration, but the display area is black and there is no audio.
Showing
11 changed files
with
470 additions
and
160 deletions
example.html
0 → 100644
1 | /<!DOCTYPE html> | ||
2 | <html> | ||
3 | <head> | ||
4 | <meta charset="utf-8"> | ||
5 | <title>video.js HLS Plugin Example</title> | ||
6 | |||
7 | <link href="http://vjs.zencdn.net/4.0/video-js.css" rel="stylesheet"> | ||
8 | |||
9 | <!-- video.js --> | ||
10 | <script src="libs/video-js/src/js/core.js"></script> | ||
11 | <script src="libs/video-js/src/js/core-object.js"></script> | ||
12 | <script src="libs/video-js/src/js/events.js"></script> | ||
13 | <script src="libs/video-js/src/js/lib.js"></script> | ||
14 | <script src="libs/video-js/src/js/component.js"></script> | ||
15 | <script src="libs/video-js/src/js/button.js"></script> | ||
16 | <script src="libs/video-js/src/js/slider.js"></script> | ||
17 | <script src="libs/video-js/src/js/menu.js"></script> | ||
18 | <script src="libs/video-js/src/js/player.js"></script> | ||
19 | <script src="libs/video-js/src/js/control-bar/control-bar.js"></script> | ||
20 | <script src="libs/video-js/src/js/control-bar/play-toggle.js"></script> | ||
21 | <script src="libs/video-js/src/js/control-bar/time-display.js"></script> | ||
22 | <script src="libs/video-js/src/js/control-bar/fullscreen-toggle.js"></script> | ||
23 | <script src="libs/video-js/src/js/control-bar/progress-control.js"></script> | ||
24 | <script src="libs/video-js/src/js/control-bar/volume-control.js"></script> | ||
25 | <script src="libs/video-js/src/js/control-bar/mute-toggle.js"></script> | ||
26 | <script src="libs/video-js/src/js/control-bar/volume-menu-button.js"></script> | ||
27 | <script src="libs/video-js/src/js/poster.js"></script> | ||
28 | <script src="libs/video-js/src/js/loading-spinner.js"></script> | ||
29 | <script src="libs/video-js/src/js/big-play-button.js"></script> | ||
30 | <script src="libs/video-js/src/js/media/media.js"></script> | ||
31 | <script src="libs/video-js/src/js/media/html5.js"></script> | ||
32 | <script src="libs/video-js/src/js/media/flash.js"></script> | ||
33 | <script src="libs/video-js/src/js/media/loader.js"></script> | ||
34 | <script src="libs/video-js/src/js/tracks.js"></script> | ||
35 | <script src="libs/video-js/src/js/json.js"></script> | ||
36 | <script src="libs/video-js/src/js/setup.js"></script> | ||
37 | <script src="libs/video-js/src/js/plugins.js"></script> | ||
38 | |||
39 | <!-- Media Sources plugin --> | ||
40 | <script src="libs/videojs-media-sources.js"></script> | ||
41 | <!-- HLS plugin --> | ||
42 | <script src="src/video-js-hls.js"></script> | ||
43 | <script src="src/flv-tag.js"></script> | ||
44 | <script src="src/exp-golomb.js"></script> | ||
45 | <script src="src/h264-stream.js"></script> | ||
46 | <script src="src/aac-stream.js"></script> | ||
47 | <script src="src/segment-parser.js"></script> | ||
48 | <!-- an example MPEG2-TS segment --> | ||
49 | <script src="test/tsSegment.js"></script> | ||
50 | |||
51 | </head> | ||
52 | <body> | ||
53 | <video id='video' | ||
54 | class="video-js vjs-default-skin" | ||
55 | height="300" | ||
56 | width="600" | ||
57 | controls> | ||
58 | </video> | ||
59 | <script> | ||
60 | var video, mediaSource; | ||
61 | |||
62 | // initialize the player | ||
63 | videojs.options.flash.swf = "libs/video-js.swf"; | ||
64 | video = videojs('video'); | ||
65 | |||
66 | // create a media source | ||
67 | mediaSource = new videojs.MediaSource(); | ||
68 | |||
69 | mediaSource.addEventListener('sourceopen', function(event){ | ||
70 | var tag, bytes, parser; | ||
71 | |||
72 | // feed parsed bytes into the player | ||
73 | var sourceBuffer = mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"'); | ||
74 | parser = new videojs.hls.SegmentParser(); | ||
75 | var header = parser.getFlvHeader(); | ||
76 | sourceBuffer.appendBuffer(header); | ||
77 | |||
78 | parser.parseSegmentBinaryData(window.testSegment); | ||
79 | while (parser.tagsAvailable()) { | ||
80 | tag = parser.getNextTag(); | ||
81 | console.log('sending bytes', tag.length); | ||
82 | sourceBuffer.appendBuffer(tag.bytes.subarray(0, tag.length), video); | ||
83 | } | ||
84 | }, false); | ||
85 | |||
86 | url = videojs.URL.createObjectURL(mediaSource); | ||
87 | |||
88 | video.src({ | ||
89 | src: url, | ||
90 | type: "video/flv" | ||
91 | }); | ||
92 | </script> | ||
93 | </body> | ||
94 | </html> |
... | @@ -7,9 +7,216 @@ | ... | @@ -7,9 +7,216 @@ |
7 | */ | 7 | */ |
8 | 8 | ||
9 | (function(window) { | 9 | (function(window) { |
10 | var | ||
11 | FlvTag = window.videojs.hls.FlvTag, | ||
12 | adtsSampleingRates = [ | ||
13 | 96000, 88200, | ||
14 | 64000, 48000, | ||
15 | 44100, 32000, | ||
16 | 24000, 22050, | ||
17 | 16000, 12000 | ||
18 | ]; | ||
19 | |||
20 | window.videojs.hls.AacStream = function() { | ||
21 | var | ||
22 | next_pts, // :uint | ||
23 | pts_delta = -1, // :int | ||
24 | state, // :uint | ||
25 | pes_length, // :int | ||
26 | |||
27 | adtsProtectionAbsent, // :Boolean | ||
28 | adtsObjectType, // :int | ||
29 | adtsSampleingIndex, // :int | ||
30 | adtsChanelConfig, // :int | ||
31 | adtsFrameSize, // :int | ||
32 | adtsSampleCount, // :int | ||
33 | adtsDuration, // :int | ||
34 | |||
35 | aacFrame, // :FlvTag = null; | ||
36 | extraData; // :uint; | ||
10 | 37 | ||
11 | window.videojs.hls.AacStream = function(){ | ||
12 | this.tags = []; | 38 | this.tags = []; |
39 | |||
40 | // (pts:uint, pes_size:int, dataAligned:Boolean):void | ||
41 | this.setNextTimeStamp = function(pts, pes_size, dataAligned) { | ||
42 | if (0 > pts_delta) { | ||
43 | // We assume the very firts pts is less than 0x80000000 | ||
44 | pts_delta = pts; | ||
45 | } | ||
46 | |||
47 | next_pts = pts - pts_delta; | ||
48 | pes_length = pes_size; | ||
49 | |||
50 | // If data is aligned, flush all internal buffers | ||
51 | if (dataAligned) { | ||
52 | state = 0; | ||
53 | } | ||
54 | }; | ||
55 | |||
56 | // (pData:ByteArray, o:int = 0, l:int = 0):void | ||
57 | this.writeBytes = function(pData, o, l) { | ||
58 | var | ||
59 | e, // :int | ||
60 | newExtraData, // :uint | ||
61 | bytesToCopy; // :int | ||
62 | |||
63 | // default arguments | ||
64 | o = o || 0; | ||
65 | l = l || 0; | ||
66 | |||
67 | // Do not allow more that 'pes_length' bytes to be written | ||
68 | l = (pes_length < l ? pes_length : l); | ||
69 | pes_length -= l; | ||
70 | e = o + l; | ||
71 | while(o < e) { | ||
72 | switch (state) { | ||
73 | default: | ||
74 | state = 0; | ||
75 | break; | ||
76 | case 0: | ||
77 | if (o >= e) { | ||
78 | return; | ||
79 | } | ||
80 | if (0xFF !== pData[o]) { | ||
81 | console.log("Error no ATDS header found"); | ||
82 | o += 1; | ||
83 | state = 0; | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | o += 1; | ||
88 | state = 1; | ||
89 | break; | ||
90 | case 1: | ||
91 | if (o >= e) { | ||
92 | return; | ||
93 | } | ||
94 | if (0xF0 !== (pData[o] & 0xF0)) { | ||
95 | console.log("Error no ATDS header found"); | ||
96 | o +=1; | ||
97 | state = 0; | ||
98 | return; | ||
99 | } | ||
100 | |||
101 | adtsProtectionAbsent = !!(pData[o] & 0x01); | ||
102 | |||
103 | o += 1; | ||
104 | state = 2; | ||
105 | break; | ||
106 | case 2: | ||
107 | if (o >= e) { | ||
108 | return; | ||
109 | } | ||
110 | adtsObjectType = ((pData[o] & 0xC0) >>> 6) + 1; | ||
111 | adtsSampleingIndex = ((pData[o] & 0x3C) >>> 2); | ||
112 | adtsChanelConfig = ((pData[o] & 0x01) << 2); | ||
113 | |||
114 | o += 1; | ||
115 | state = 3; | ||
116 | break; | ||
117 | case 3: | ||
118 | if (o >= e) { | ||
119 | return; | ||
120 | } | ||
121 | adtsChanelConfig |= ((pData[o] & 0xC0) >>> 6); | ||
122 | adtsFrameSize = ((pData[o] & 0x03) << 11); | ||
123 | |||
124 | o += 1; | ||
125 | state = 4; | ||
126 | break; | ||
127 | case 4: | ||
128 | if (o >= e) { | ||
129 | return; | ||
130 | } | ||
131 | adtsFrameSize |= (pData[o] << 3); | ||
132 | |||
133 | o += 1; | ||
134 | state = 5; | ||
135 | break; | ||
136 | case 5: | ||
137 | if(o >= e) { | ||
138 | return; | ||
139 | } | ||
140 | adtsFrameSize |= ((pData[o] & 0xE0) >>> 5); | ||
141 | adtsFrameSize -= (adtsProtectionAbsent ? 7 : 9); | ||
142 | |||
143 | o += 1; | ||
144 | state = 6; | ||
145 | break; | ||
146 | case 6: | ||
147 | if (o >= e) { | ||
148 | return; | ||
149 | } | ||
150 | adtsSampleCount = ((pData[o] & 0x03) + 1) * 1024; | ||
151 | adtsDuration = (adtsSampleCount * 1000) / adtsSampleingRates[adtsSampleingIndex]; | ||
152 | |||
153 | newExtraData = (adtsObjectType << 11) | | ||
154 | (adtsSampleingIndex << 7) | | ||
155 | (adtsChanelConfig << 3); | ||
156 | if (newExtraData !== extraData) { | ||
157 | aacFrame = new FlvTag(FlvTag.METADATA_TAG); | ||
158 | aacFrame.pts = next_pts; | ||
159 | aacFrame.dts = next_pts; | ||
160 | |||
161 | // AAC is always 10 | ||
162 | aacFrame.writeMetaDataDouble("audiocodecid", 10); | ||
163 | aacFrame.writeMetaDataBoolean("stereo", 2 === adtsChanelConfig); | ||
164 | aacFrame.writeMetaDataDouble ("audiosamplerate", adtsSampleingRates[adtsSampleingIndex]); | ||
165 | // Is AAC always 16 bit? | ||
166 | aacFrame.writeMetaDataDouble ("audiosamplesize", 16); | ||
167 | |||
168 | this.tags.push(aacFrame); | ||
169 | |||
170 | extraData = newExtraData; | ||
171 | aacFrame = new FlvTag(FlvTag.AUDIO_TAG, true); | ||
172 | aacFrame.pts = aacFrame.dts; | ||
173 | // For audio, DTS is always the same as PTS. We want to set the DTS | ||
174 | // however so we can compare with video DTS to determine approximate | ||
175 | // packet order | ||
176 | aacFrame.pts = next_pts; | ||
177 | aacFrame.view.setUint16(aacFrame.position, newExtraData); | ||
178 | aacFrame.position += 2; | ||
179 | |||
180 | this.tags.push(aacFrame); | ||
181 | } | ||
182 | |||
183 | // Skip the checksum if there is one | ||
184 | o += 1; | ||
185 | state = 7; | ||
186 | break; | ||
187 | case 7: | ||
188 | if (!adtsProtectionAbsent) { | ||
189 | if (2 > (e - o)) { | ||
190 | return; | ||
191 | } else { | ||
192 | o += 2; | ||
193 | } | ||
194 | } | ||
195 | |||
196 | aacFrame = new FlvTag(FlvTag.AUDIO_TAG); | ||
197 | aacFrame.pts = next_pts; | ||
198 | aacFrame.dts = next_pts; | ||
199 | state = 8; | ||
200 | break; | ||
201 | case 8: | ||
202 | while (adtsFrameSize) { | ||
203 | if (o >= e) { | ||
204 | return; | ||
205 | } | ||
206 | bytesToCopy = (e - o) < adtsFrameSize ? (e - o) : adtsFrameSize; | ||
207 | aacFrame.writeBytes( pData, o, bytesToCopy ); | ||
208 | o += bytesToCopy; | ||
209 | adtsFrameSize -= bytesToCopy; | ||
210 | } | ||
211 | |||
212 | this.tags.push(aacFrame); | ||
213 | |||
214 | // finished with this frame | ||
215 | state = 0; | ||
216 | next_pts += adtsDuration; | ||
217 | } | ||
218 | } | ||
219 | }; | ||
13 | }; | 220 | }; |
14 | 221 | ||
15 | })(this); | 222 | })(this); | ... | ... |
1 | (function() { | 1 | (function(window) { |
2 | 2 | ||
3 | window.videojs.hls.ExpGolomb = function() {}; | 3 | window.videojs.hls.ExpGolomb = function(workingData) { |
4 | 4 | var | |
5 | /* | 5 | // the number of bytes left to examine in workingData |
6 | public class ExpGolomb | 6 | workingBytesAvailable = workingData.byteLength, |
7 | { | 7 | |
8 | private var workingData:ByteArray; | 8 | // the current word being examined |
9 | private var workingWord:uint; | 9 | workingWord, // :uint |
10 | private var workingbBitsAvailable:uint; | 10 | |
11 | 11 | // the number of bits left to examine in the current word | |
12 | public function ExpGolomb(pData:ByteArray) | 12 | workingBitsAvailable; // :uint; |
13 | { | 13 | |
14 | workingData = pData; | 14 | // ():uint |
15 | workingData.position = 0; | 15 | this.length = function() { |
16 | loadWord(); | 16 | return (8 * workingBytesAvailable); |
17 | } | 17 | }; |
18 | 18 | ||
19 | public function length():uint | 19 | // ():uint |
20 | { | 20 | this.bitsAvailable = function() { |
21 | return ( 8 * workingData.length ); | 21 | return (8 * workingBytesAvailable) + workingBitsAvailable; |
22 | } | 22 | }; |
23 | 23 | ||
24 | public function bitsAvailable():uint | 24 | // ():void |
25 | { | 25 | this.loadWord = function() { |
26 | return ( 8 * workingData.bytesAvailable ) + workingbBitsAvailable; | 26 | var |
27 | } | 27 | workingBytes = new Uint8Array(4), |
28 | 28 | availableBytes = Math.min(4, workingBytesAvailable); | |
29 | private function loadWord():void | 29 | |
30 | { | 30 | // console.assert(availableBytes > 0); |
31 | workingWord = 0; workingbBitsAvailable = 0; | 31 | |
32 | switch( workingData.bytesAvailable ) | 32 | workingBytes.set(workingData.subarray(0, availableBytes)); |
33 | { | 33 | workingWord = new DataView(workingBytes.buffer).getUint32(0); |
34 | case 0: workingbBitsAvailable = 0; break; | 34 | |
35 | default: // not 0, but greater than 4 | 35 | // track the amount of workingData that has been processed |
36 | case 4: workingWord = workingData.readUnsignedByte(); workingbBitsAvailable = 8; | 36 | workingBitsAvailable = availableBytes * 8; |
37 | case 3: workingWord = ( workingWord << 8 ) | workingData.readUnsignedByte(); workingbBitsAvailable += 8; | 37 | workingBytesAvailable -= availableBytes; |
38 | case 2: workingWord = ( workingWord << 8 ) | workingData.readUnsignedByte(); workingbBitsAvailable += 8; | 38 | }; |
39 | case 1: workingWord = ( workingWord << 8 ) | workingData.readUnsignedByte(); workingbBitsAvailable += 8; | 39 | |
40 | } | 40 | // (size:int):void |
41 | 41 | this.skipBits = function(size) { | |
42 | workingWord <<= (32 - workingbBitsAvailable); | 42 | var skipBytes; // :int |
43 | } | 43 | if (workingBitsAvailable > size) { |
44 | 44 | workingWord <<= size; | |
45 | public function skipBits(size:int):void | 45 | workingBitsAvailable -= size; |
46 | { | 46 | } else { |
47 | if ( workingbBitsAvailable > size ) | 47 | size -= workingBitsAvailable; |
48 | { | 48 | skipBytes = size / 8; |
49 | workingWord <<= size; | 49 | |
50 | workingbBitsAvailable -= size; | 50 | size -= (skipBytes * 8); |
51 | } | 51 | workingData.position += skipBytes; |
52 | else | 52 | |
53 | { | 53 | this.loadWord(); |
54 | size -= workingbBitsAvailable; | 54 | |
55 | var skipBytes:int = size / 8; | 55 | workingWord <<= size; |
56 | 56 | workingBitsAvailable -= size; | |
57 | size -= ( skipBytes * 8 ); | ||
58 | workingData.position += skipBytes; | ||
59 | |||
60 | loadWord(); | ||
61 | |||
62 | workingWord <<= size; | ||
63 | workingbBitsAvailable -= size; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | public function readBits(size:int):uint | ||
68 | { | ||
69 | // if ( 32 < size ) | ||
70 | // throw new Error("Can not read more than 32 bits at a time"); | ||
71 | |||
72 | var bits:uint = ( workingbBitsAvailable < size ? workingbBitsAvailable : size); | ||
73 | var valu:uint = workingWord >>> (32 - bits); | ||
74 | |||
75 | workingbBitsAvailable -= bits; | ||
76 | if ( 0 < workingbBitsAvailable ) | ||
77 | workingWord <<= bits; | ||
78 | else | ||
79 | loadWord(); | ||
80 | |||
81 | bits = size - bits; | ||
82 | if ( 0 < bits ) | ||
83 | return valu << bits | readBits( bits ); | ||
84 | else | ||
85 | return valu; | ||
86 | } | ||
87 | |||
88 | private function skipLeadingZeros():uint | ||
89 | { | ||
90 | for( var clz:uint = 0 ; clz < workingbBitsAvailable ; ++clz ) | ||
91 | { | ||
92 | if( 0 != ( workingWord & ( 0x80000000 >>> clz ) ) ) | ||
93 | { | ||
94 | workingWord <<= clz; | ||
95 | workingbBitsAvailable -= clz; | ||
96 | return clz; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | loadWord(); // we exhausted workingWord and still have not found a 1 | ||
101 | return clz + skipLeadingZeros(); | ||
102 | } | ||
103 | |||
104 | public function skipUnsignedExpGolomb():void | ||
105 | { | ||
106 | skipBits(1 + skipLeadingZeros() ); | ||
107 | } | ||
108 | |||
109 | public function skipExpGolomb():void | ||
110 | { | ||
111 | skipBits(1 + skipLeadingZeros() ); | ||
112 | } | ||
113 | |||
114 | public function readUnsignedExpGolomb():uint | ||
115 | { | ||
116 | var clz:uint = skipLeadingZeros(); | ||
117 | return readBits(clz+1) - 1; | ||
118 | } | ||
119 | |||
120 | public function readExpGolomb():int | ||
121 | { | ||
122 | var valu:int = readUnsignedExpGolomb(); | ||
123 | if ( 0x01 & valu ) // the number is odd if the low order bit is set | ||
124 | return (1 + valu) >>> 1; // add 1 to make it even, and devide by 2 | ||
125 | else | ||
126 | return -1 * (valu >>> 1); // devide by two then make it negative | ||
127 | } | ||
128 | |||
129 | // Some convenience functions | ||
130 | public function readBoolean():Boolean | ||
131 | { | ||
132 | return 1 == readBits(1); | ||
133 | } | ||
134 | |||
135 | public function readUnsignedByte():int | ||
136 | { | ||
137 | return readBits(8); | ||
138 | } | ||
139 | } | 57 | } |
140 | */ | 58 | }; |
141 | })(); | 59 | |
60 | // (size:int):uint | ||
61 | this.readBits = function(size) { | ||
62 | var | ||
63 | bits = Math.min(workingBitsAvailable, size), // :uint | ||
64 | valu = workingWord >>> (32 - bits); // :uint | ||
65 | |||
66 | console.assert(32 > size, 'Cannot read more than 32 bits at a time'); | ||
67 | |||
68 | workingBitsAvailable -= bits; | ||
69 | if (0 < workingBitsAvailable) { | ||
70 | workingWord <<= bits; | ||
71 | } else { | ||
72 | this.loadWord(); | ||
73 | } | ||
74 | |||
75 | bits = size - bits; | ||
76 | if (0 < bits) { | ||
77 | return valu << bits | this.readBits(bits); | ||
78 | } else { | ||
79 | return valu; | ||
80 | } | ||
81 | }; | ||
82 | |||
83 | // ():uint | ||
84 | this.skipLeadingZeros = function() { | ||
85 | var clz; // :uint | ||
86 | for (clz = 0 ; clz < workingBitsAvailable ; ++clz) { | ||
87 | if (0 !== (workingWord & (0x80000000 >>> clz))) { | ||
88 | workingWord <<= clz; | ||
89 | workingBitsAvailable -= clz; | ||
90 | return clz; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | // we exhausted workingWord and still have not found a 1 | ||
95 | this.loadWord(); | ||
96 | return clz + this.skipLeadingZeros(); | ||
97 | }; | ||
98 | |||
99 | // ():void | ||
100 | this.skipUnsignedExpGolomb = function() { | ||
101 | this.skipBits(1 + this.skipLeadingZeros()); | ||
102 | }; | ||
103 | |||
104 | // ():void | ||
105 | this.skipExpGolomb = function() { | ||
106 | this.skipBits(1 + this.skipLeadingZeros()); | ||
107 | }; | ||
108 | |||
109 | // ():uint | ||
110 | this.readUnsignedExpGolomb = function() { | ||
111 | var clz = this.skipLeadingZeros(); // :uint | ||
112 | return this.readBits(clz + 1) - 1; | ||
113 | }; | ||
114 | |||
115 | // ():int | ||
116 | this.readExpGolomb = function() { | ||
117 | var valu = this.readUnsignedExpGolomb(); // :int | ||
118 | if (0x01 & valu) { | ||
119 | // the number is odd if the low order bit is set | ||
120 | return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2 | ||
121 | } else { | ||
122 | return -1 * (valu >>> 1); // divide by two then make it negative | ||
123 | } | ||
124 | }; | ||
125 | |||
126 | // Some convenience functions | ||
127 | // :Boolean | ||
128 | this.readBoolean = function() { | ||
129 | return 1 === this.readBits(1); | ||
130 | }; | ||
131 | |||
132 | // ():int | ||
133 | this.readUnsignedByte = function() { | ||
134 | return this.readBits(8); | ||
135 | }; | ||
136 | |||
137 | this.loadWord(); | ||
138 | |||
139 | }; | ||
140 | })(this); | ... | ... |
This diff is collapsed.
Click to expand it.
This diff is collapsed.
Click to expand it.
... | @@ -187,7 +187,7 @@ | ... | @@ -187,7 +187,7 @@ |
187 | } | 187 | } |
188 | 188 | ||
189 | // attempt to parse a m2ts packet | 189 | // attempt to parse a m2ts packet |
190 | if (parseTSPacket(data.subarray(dataPosition, m2tsPacketSize))) { | 190 | if (parseTSPacket(data.subarray(dataPosition, dataPosition + m2tsPacketSize))) { |
191 | dataPosition += m2tsPacketSize; | 191 | dataPosition += m2tsPacketSize; |
192 | } else { | 192 | } else { |
193 | // If there was an error parsing a TS packet. it could be | 193 | // If there was an error parsing a TS packet. it could be |
... | @@ -203,7 +203,7 @@ | ... | @@ -203,7 +203,7 @@ |
203 | // packet! | 203 | // packet! |
204 | parseTSPacket = function(data) { // :ByteArray):Boolean { | 204 | parseTSPacket = function(data) { // :ByteArray):Boolean { |
205 | var | 205 | var |
206 | s = data.position, //:uint | 206 | s = 0, //:uint |
207 | o = s, // :uint | 207 | o = s, // :uint |
208 | e = o + m2tsPacketSize, // :uint | 208 | e = o + m2tsPacketSize, // :uint |
209 | 209 | ||
... | @@ -211,10 +211,10 @@ | ... | @@ -211,10 +211,10 @@ |
211 | // parseSegmentBinaryData() | 211 | // parseSegmentBinaryData() |
212 | 212 | ||
213 | // Payload Unit Start Indicator | 213 | // Payload Unit Start Indicator |
214 | pusi = !!(data[o+1] & 0x40), | 214 | pusi = !!(data[o + 1] & 0x40), // mask: 0100 0000 |
215 | 215 | ||
216 | // PacketId | 216 | // PacketId |
217 | pid = (data[o + 1] & 0x1F) << 8 | data[o+2], | 217 | pid = (data[o + 1] & 0x1F) << 8 | data[o + 2], // mask: 0001 1111 |
218 | afflag = (data[o + 3] & 0x30 ) >>> 4, | 218 | afflag = (data[o + 3] & 0x30 ) >>> 4, |
219 | 219 | ||
220 | aflen, // :uint | 220 | aflen, // :uint |
... | @@ -259,7 +259,7 @@ | ... | @@ -259,7 +259,7 @@ |
259 | 259 | ||
260 | console.assert(0x00 === patTableId, 'patTableId should be 0x00'); | 260 | console.assert(0x00 === patTableId, 'patTableId should be 0x00'); |
261 | 261 | ||
262 | patCurrentNextIndicator = !!(data[o+5] & 0x01); | 262 | patCurrentNextIndicator = !!(data[o + 5] & 0x01); |
263 | if (patCurrentNextIndicator) { | 263 | if (patCurrentNextIndicator) { |
264 | patSectionLength = (data[o + 1] & 0x0F) << 8 | data[o + 2]; | 264 | patSectionLength = (data[o + 1] & 0x0F) << 8 | data[o + 2]; |
265 | o += 8; // skip past PSI header | 265 | o += 8; // skip past PSI header |
... | @@ -280,10 +280,10 @@ | ... | @@ -280,10 +280,10 @@ |
280 | } else if (videoPid === pid || audioPid === pid) { | 280 | } else if (videoPid === pid || audioPid === pid) { |
281 | if (pusi) { | 281 | if (pusi) { |
282 | // comment out for speed | 282 | // comment out for speed |
283 | // if( 0x00 != data[o+0] || 0x00 != data[o+1] || 0x01 != data[o+2] ) | 283 | if (0x00 !== data[o + 0] || 0x00 !== data[o + 1] || 0x01 !== data[o + 2]) { |
284 | // {// look for PES start code | 284 | // look for PES start code |
285 | // throw new Error("PES did not begin with start code"); | 285 | throw new Error("PES did not begin with start code"); |
286 | // } | 286 | } |
287 | 287 | ||
288 | // var sid:int = data[o+3]; // StreamID | 288 | // var sid:int = data[o+3]; // StreamID |
289 | pesPacketSize = (data[o + 4] << 8) | data[o + 5]; | 289 | pesPacketSize = (data[o + 4] << 8) | data[o + 5]; |
... | @@ -334,14 +334,14 @@ | ... | @@ -334,14 +334,14 @@ |
334 | } | 334 | } |
335 | 335 | ||
336 | if (audioPid === pid) { | 336 | if (audioPid === pid) { |
337 | aacStream.writeBytes(data,o,e-o); | 337 | aacStream.writeBytes(data, o, e - o); |
338 | } else if (videoPid === pid) { | 338 | } else if (videoPid === pid) { |
339 | h264Stream.writeBytes(data,o,e-o); | 339 | h264Stream.writeBytes(data, o, e - o); |
340 | } | 340 | } |
341 | } else if (pmtPid === pid) { | 341 | } else if (pmtPid === pid) { |
342 | // TODO sanity check data[o] | 342 | // TODO sanity check data[o] |
343 | // if pusi is set we must skip X bytes (PSI pointer field) | 343 | // if pusi is set we must skip X bytes (PSI pointer field) |
344 | o += ( pusi ? 1 + data[o] : 0 ); | 344 | o += (pusi ? 1 + data[o] : 0); |
345 | pmtTableId = data[o]; | 345 | pmtTableId = data[o]; |
346 | 346 | ||
347 | console.assert(0x02 === pmtTableId); | 347 | console.assert(0x02 === pmtTableId); |
... | @@ -349,7 +349,7 @@ | ... | @@ -349,7 +349,7 @@ |
349 | pmtCurrentNextIndicator = !!(data[o + 5] & 0x01); | 349 | pmtCurrentNextIndicator = !!(data[o + 5] & 0x01); |
350 | if (pmtCurrentNextIndicator) { | 350 | if (pmtCurrentNextIndicator) { |
351 | audioPid = videoPid = 0; | 351 | audioPid = videoPid = 0; |
352 | pmtSectionLength = (data[o + 1] & 0x0F ) << 8 | data[o + 2]; | 352 | pmtSectionLength = (data[o + 1] & 0x0F) << 8 | data[o + 2]; |
353 | // skip CRC and PSI data we dont care about | 353 | // skip CRC and PSI data we dont care about |
354 | pmtSectionLength -= 13; | 354 | pmtSectionLength -= 13; |
355 | 355 | ||
... | @@ -357,7 +357,7 @@ | ... | @@ -357,7 +357,7 @@ |
357 | while (0 < pmtSectionLength) { | 357 | while (0 < pmtSectionLength) { |
358 | streamType = data[o + 0]; | 358 | streamType = data[o + 0]; |
359 | elementaryPID = (data[o + 1] & 0x1F) << 8 | data[o + 2]; | 359 | elementaryPID = (data[o + 1] & 0x1F) << 8 | data[o + 2]; |
360 | ESInfolength = (data[o + 3] & 0x0F ) << 8 | data[o + 4]; | 360 | ESInfolength = (data[o + 3] & 0x0F) << 8 | data[o + 4]; |
361 | o += 5 + ESInfolength; | 361 | o += 5 + ESInfolength; |
362 | pmtSectionLength -= 5 + ESInfolength; | 362 | pmtSectionLength -= 5 + ESInfolength; |
363 | 363 | ... | ... |
test/bipbop0.flv
0 → 100644
No preview for this file type
test/bipbop0.ts
0 → 100644
No preview for this file type
test/bipbop0.ts.b64
0 → 100644
This diff could not be displayed because it is too large.
... | @@ -39,9 +39,10 @@ | ... | @@ -39,9 +39,10 @@ |
39 | 39 | ||
40 | <!-- HLS plugin --> | 40 | <!-- HLS plugin --> |
41 | <script src="../src/video-js-hls.js"></script> | 41 | <script src="../src/video-js-hls.js"></script> |
42 | <script src="../src/flv-tag.js"></script> | ||
43 | <script src="../src/exp-golomb.js"></script> | ||
42 | <script src="../src/h264-stream.js"></script> | 44 | <script src="../src/h264-stream.js"></script> |
43 | <script src="../src/aac-stream.js"></script> | 45 | <script src="../src/aac-stream.js"></script> |
44 | <script src="../src/flv-tag.js"></script> | ||
45 | <script src="../src/segment-parser.js"></script> | 46 | <script src="../src/segment-parser.js"></script> |
46 | <!-- an example MPEG2-TS segment --> | 47 | <!-- an example MPEG2-TS segment --> |
47 | <script src="tsSegment.js"></script> | 48 | <script src="tsSegment.js"></script> | ... | ... |
... | @@ -19,7 +19,13 @@ | ... | @@ -19,7 +19,13 @@ |
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 | var |
23 | parser, | ||
24 | |||
25 | expectedHeader = [ | ||
26 | 0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00, | ||
27 | 0x09, 0x00, 0x00, 0x00, 0x00 | ||
28 | ]; | ||
23 | 29 | ||
24 | module('environment'); | 30 | module('environment'); |
25 | 31 | ||
... | @@ -35,17 +41,20 @@ | ... | @@ -35,17 +41,20 @@ |
35 | }); | 41 | }); |
36 | 42 | ||
37 | test('creates an flv header', function() { | 43 | test('creates an flv header', function() { |
38 | var header = parser.getFlvHeader(); | 44 | var header = Array.prototype.slice.call(parser.getFlvHeader()); |
39 | ok(header, 'the header is truthy'); | 45 | ok(header, 'the header is truthy'); |
40 | equal(9 + 4, header.byteLength, 'the header length is correct'); | 46 | equal(9 + 4, header.length, 'the header length is correct'); |
41 | equal(header[0], 'F'.charCodeAt(0), 'the signature is correct'); | 47 | equal(header[0], 'F'.charCodeAt(0), 'the signature is correct'); |
42 | equal(header[1], 'L'.charCodeAt(0), 'the signature is correct'); | 48 | equal(header[1], 'L'.charCodeAt(0), 'the signature is correct'); |
43 | equal(header[2], 'V'.charCodeAt(0), 'the signature is correct'); | 49 | equal(header[2], 'V'.charCodeAt(0), 'the signature is correct'); |
50 | |||
51 | deepEqual(expectedHeader, header, 'the rest of the header is correct'); | ||
44 | }); | 52 | }); |
45 | 53 | ||
46 | test('parses the first bipbop segment', function() { | 54 | test('parses the first bipbop segment', function() { |
55 | var tag, bytes; | ||
47 | parser.parseSegmentBinaryData(window.testSegment); | 56 | parser.parseSegmentBinaryData(window.testSegment); |
48 | 57 | ||
49 | ok(parser.tagsAvailable(), 'tags should be available'); | 58 | ok(parser.tagsAvailable(), 'tags are available'); |
50 | }); | 59 | }); |
51 | })(this); | 60 | })(this); | ... | ... |
-
Please register or sign in to post a comment