7a41ceb5 by David LaPalomento

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.
1 parent c09dfad4
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
3 window.videojs.hls.ExpGolomb = function() {};
4
5 /*
6 public class ExpGolomb
7 {
8 private var workingData:ByteArray;
9 private var workingWord:uint;
10 private var workingbBitsAvailable:uint;
11
12 public function ExpGolomb(pData:ByteArray)
13 {
14 workingData = pData;
15 workingData.position = 0;
16 loadWord();
17 }
18 2
19 public function length():uint 3 window.videojs.hls.ExpGolomb = function(workingData) {
20 { 4 var
21 return ( 8 * workingData.length ); 5 // the number of bytes left to examine in workingData
22 } 6 workingBytesAvailable = workingData.byteLength,
23 7
24 public function bitsAvailable():uint 8 // the current word being examined
25 { 9 workingWord, // :uint
26 return ( 8 * workingData.bytesAvailable ) + workingbBitsAvailable;
27 }
28 10
29 private function loadWord():void 11 // the number of bits left to examine in the current word
30 { 12 workingBitsAvailable; // :uint;
31 workingWord = 0; workingbBitsAvailable = 0;
32 switch( workingData.bytesAvailable )
33 {
34 case 0: workingbBitsAvailable = 0; break;
35 default: // not 0, but greater than 4
36 case 4: workingWord = workingData.readUnsignedByte(); workingbBitsAvailable = 8;
37 case 3: workingWord = ( workingWord << 8 ) | workingData.readUnsignedByte(); workingbBitsAvailable += 8;
38 case 2: workingWord = ( workingWord << 8 ) | workingData.readUnsignedByte(); workingbBitsAvailable += 8;
39 case 1: workingWord = ( workingWord << 8 ) | workingData.readUnsignedByte(); workingbBitsAvailable += 8;
40 }
41 13
42 workingWord <<= (32 - workingbBitsAvailable); 14 // ():uint
43 } 15 this.length = function() {
16 return (8 * workingBytesAvailable);
17 };
18
19 // ():uint
20 this.bitsAvailable = function() {
21 return (8 * workingBytesAvailable) + workingBitsAvailable;
22 };
44 23
45 public function skipBits(size:int):void 24 // ():void
46 { 25 this.loadWord = function() {
47 if ( workingbBitsAvailable > size ) 26 var
48 { 27 workingBytes = new Uint8Array(4),
28 availableBytes = Math.min(4, workingBytesAvailable);
29
30 // console.assert(availableBytes > 0);
31
32 workingBytes.set(workingData.subarray(0, availableBytes));
33 workingWord = new DataView(workingBytes.buffer).getUint32(0);
34
35 // track the amount of workingData that has been processed
36 workingBitsAvailable = availableBytes * 8;
37 workingBytesAvailable -= availableBytes;
38 };
39
40 // (size:int):void
41 this.skipBits = function(size) {
42 var skipBytes; // :int
43 if (workingBitsAvailable > size) {
49 workingWord <<= size; 44 workingWord <<= size;
50 workingbBitsAvailable -= size; 45 workingBitsAvailable -= size;
51 } 46 } else {
52 else 47 size -= workingBitsAvailable;
53 { 48 skipBytes = size / 8;
54 size -= workingbBitsAvailable;
55 var skipBytes:int = size / 8;
56 49
57 size -= ( skipBytes * 8 ); 50 size -= (skipBytes * 8);
58 workingData.position += skipBytes; 51 workingData.position += skipBytes;
59 52
60 loadWord(); 53 this.loadWord();
61 54
62 workingWord <<= size; 55 workingWord <<= size;
63 workingbBitsAvailable -= size; 56 workingBitsAvailable -= size;
64 }
65 } 57 }
58 };
66 59
67 public function readBits(size:int):uint 60 // (size:int):uint
68 { 61 this.readBits = function(size) {
69 // if ( 32 < size ) 62 var
70 // throw new Error("Can not read more than 32 bits at a time"); 63 bits = Math.min(workingBitsAvailable, size), // :uint
64 valu = workingWord >>> (32 - bits); // :uint
71 65
72 var bits:uint = ( workingbBitsAvailable < size ? workingbBitsAvailable : size); 66 console.assert(32 > size, 'Cannot read more than 32 bits at a time');
73 var valu:uint = workingWord >>> (32 - bits);
74 67
75 workingbBitsAvailable -= bits; 68 workingBitsAvailable -= bits;
76 if ( 0 < workingbBitsAvailable ) 69 if (0 < workingBitsAvailable) {
77 workingWord <<= bits; 70 workingWord <<= bits;
78 else 71 } else {
79 loadWord(); 72 this.loadWord();
73 }
80 74
81 bits = size - bits; 75 bits = size - bits;
82 if ( 0 < bits ) 76 if (0 < bits) {
83 return valu << bits | readBits( bits ); 77 return valu << bits | this.readBits(bits);
84 else 78 } else {
85 return valu; 79 return valu;
86 } 80 }
81 };
87 82
88 private function skipLeadingZeros():uint 83 // ():uint
89 { 84 this.skipLeadingZeros = function() {
90 for( var clz:uint = 0 ; clz < workingbBitsAvailable ; ++clz ) 85 var clz; // :uint
91 { 86 for (clz = 0 ; clz < workingBitsAvailable ; ++clz) {
92 if( 0 != ( workingWord & ( 0x80000000 >>> clz ) ) ) 87 if (0 !== (workingWord & (0x80000000 >>> clz))) {
93 {
94 workingWord <<= clz; 88 workingWord <<= clz;
95 workingbBitsAvailable -= clz; 89 workingBitsAvailable -= clz;
96 return clz; 90 return clz;
97 } 91 }
98 } 92 }
99 93
100 loadWord(); // we exhausted workingWord and still have not found a 1 94 // we exhausted workingWord and still have not found a 1
101 return clz + skipLeadingZeros(); 95 this.loadWord();
102 } 96 return clz + this.skipLeadingZeros();
97 };
103 98
104 public function skipUnsignedExpGolomb():void 99 // ():void
105 { 100 this.skipUnsignedExpGolomb = function() {
106 skipBits(1 + skipLeadingZeros() ); 101 this.skipBits(1 + this.skipLeadingZeros());
107 } 102 };
108 103
109 public function skipExpGolomb():void 104 // ():void
110 { 105 this.skipExpGolomb = function() {
111 skipBits(1 + skipLeadingZeros() ); 106 this.skipBits(1 + this.skipLeadingZeros());
112 } 107 };
113 108
114 public function readUnsignedExpGolomb():uint 109 // ():uint
115 { 110 this.readUnsignedExpGolomb = function() {
116 var clz:uint = skipLeadingZeros(); 111 var clz = this.skipLeadingZeros(); // :uint
117 return readBits(clz+1) - 1; 112 return this.readBits(clz + 1) - 1;
118 } 113 };
119 114
120 public function readExpGolomb():int 115 // ():int
121 { 116 this.readExpGolomb = function() {
122 var valu:int = readUnsignedExpGolomb(); 117 var valu = this.readUnsignedExpGolomb(); // :int
123 if ( 0x01 & valu ) // the number is odd if the low order bit is set 118 if (0x01 & valu) {
124 return (1 + valu) >>> 1; // add 1 to make it even, and devide by 2 119 // the number is odd if the low order bit is set
125 else 120 return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
126 return -1 * (valu >>> 1); // devide by two then make it negative 121 } else {
122 return -1 * (valu >>> 1); // divide by two then make it negative
127 } 123 }
124 };
128 125
129 // Some convenience functions 126 // Some convenience functions
130 public function readBoolean():Boolean 127 // :Boolean
131 { 128 this.readBoolean = function() {
132 return 1 == readBits(1); 129 return 1 === this.readBits(1);
133 } 130 };
134 131
135 public function readUnsignedByte():int 132 // ():int
136 { 133 this.readUnsignedByte = function() {
137 return readBits(8); 134 return this.readBits(8);
138 } 135 };
139 } 136
140 */ 137 this.loadWord();
141 })(); 138
139 };
140 })(this);
......
1 (function(window) { 1 (function(window) {
2 2
3 window.videojs.hls.FlvTag = function() {}; 3 var hls = window.videojs.hls;
4 4
5 /* 5 // (type:uint, extraData:Boolean = false) extends ByteArray
6 package com.videojs.providers.hls.utils{ 6 hls.FlvTag = function(type, extraData) {
7 var
8 // Counter if this is a metadata tag, nal start marker if this is a video
9 // tag. unused if this is an audio tag
10 adHoc = 0; // :uint
7 11
8 import flash.utils.ByteArray; 12 this.keyFrame = false; // :Boolean
9 import flash.utils.Endian;
10 13
11 public class FlvTag extends ByteArray 14 switch(type) {
12 { 15 case hls.FlvTag.VIDEO_TAG:
13 public static const AUDIO_TAG:uint = 0x08; 16 this.length = 16;
14 public static const VIDEO_TAG:uint = 0x09; 17 break;
15 public static const METADATA_TAG:uint = 0x12; 18 case hls.FlvTag.AUDIO_TAG:
16 19 this.length = 13;
17 public var keyFrame:Boolean = false; 20 this.keyFrame = true;
18 private var extraData:Boolean = false; 21 break;
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 22 case hls.FlvTag.METADATA_TAG:
20 23 this.length = 29;
21 public var pts:uint; 24 this.keyFrame = true;
22 public var dts:uint; 25 break;
23 26 default:
24 public static function isAudioFrame(tag:ByteArray):Boolean 27 throw("Error Unknown TagType");
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 } 28 }
75 29
76 this[0] = type 30 // XXX: I have no idea if 16k is enough to buffer arbitrary FLV tags
31 this.bytes = new Uint8Array(16384);
32 this.view = new DataView(this.bytes.buffer);
33 this.bytes[0] = type;
77 this.position = this.length; 34 this.position = this.length;
78 keyFrame = extraData; // Defaults to false 35 this.keyFrame = extraData; // Defaults to false
79 pts = dts = 0; 36
37 // presentation timestamp
38 this.pts = 0;
39 // decoder timestamp
40 this.dts = 0;
41
42 // ByteArray#writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0)
43 this.writeBytes = function(bytes, offset, length) {
44 offset = offset || 0;
45 length = length || 0;
46
47 try {
48 this.bytes.set(bytes.subarray(offset, offset + length), this.position);
49 } catch(e) {
50 console.log(e);
51 throw e;
80 } 52 }
53 this.position += length;
54 this.length = Math.max(this.length, this.position);
55 };
56
57 // ByteArray#writeByte(value:int):void
58 this.writeByte = function(byte) {
59 this.bytes[this.position] = byte;
60 this.position++;
61 this.length = Math.max(this.length, this.position);
62 };
63
64 // ByteArray#writeShort(value:int):void
65 this.writeShort = function(short) {
66 this.view.setUint16(short, this.position);
67 this.position += 2;
68 this.length = Math.max(this.length, this.position);
69 };
81 70
82 // Negative index into array 71 // Negative index into array
83 public function negIndex(pos:uint):int 72 // (pos:uint):int
84 { 73 this.negIndex = function(pos) {
85 return this[this.length - pos]; 74 return this.bytes[this.length - pos];
86 } 75 };
87 76
88 // The functions below ONLY work when this[0] == VIDEO_TAG. 77 // 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 78 // We are not going to check for that because we dont want the overhead
90 public function nalUnitSize(nal:ByteArray = null):int 79 // (nal:ByteArray = null):int
91 { 80 this.nalUnitSize = function() {
92 if( 0 == adHoc ) 81 if (adHoc === 0) {
93 return 0; 82 return 0;
94
95 return this.length - ( adHoc + 4 );
96 } 83 }
97 84
85 return this.length - (adHoc + 4);
86 };
98 87
99 public function startNalUnit():void 88 this.startNalUnit = function() {
100 { // remember position and add 4 bytes 89 // remember position and add 4 bytes
101 if ( 0 < adHoc ) 90 if (adHoc > 0) {
102 {
103 throw new Error("Attempted to create new NAL wihout closing the old one"); 91 throw new Error("Attempted to create new NAL wihout closing the old one");
104 } 92 }
105 93
...@@ -107,97 +95,176 @@ package com.videojs.providers.hls.utils{ ...@@ -107,97 +95,176 @@ package com.videojs.providers.hls.utils{
107 adHoc = this.length; 95 adHoc = this.length;
108 this.length += 4; 96 this.length += 4;
109 this.position = this.length; 97 this.position = this.length;
110 } 98 };
111 99
112 public function endNalUnit(nal:ByteArray = null):void 100 // (nal:ByteArray = null):void
113 { // Rewind to the marker and write the size 101 this.endNalUnit = function(nalContainer) {
114 if ( this.length == adHoc + 4 ) 102 var
115 { 103 nalStart, // :uint
104 nalLength; // :uint
105
106 // Rewind to the marker and write the size
107 if (this.length === adHoc + 4) {
116 this.length -= 4; // we started a nal unit, but didnt write one, so roll back the 4 byte size value 108 this.length -= 4; // we started a nal unit, but didnt write one, so roll back the 4 byte size value
117 } 109 } else if (adHoc > 0) {
118 else 110 nalStart = adHoc + 4;
119 if ( 0 < adHoc ) 111 nalLength = this.length - nalStart;
120 {
121 var nalStart:uint = adHoc + 4;
122 var nalLength:uint = this.length - nalStart;
123 112
124 this.position = adHoc; 113 this.position = adHoc;
125 this.writeUnsignedInt( nalLength ); 114 this.view.setUint32(this.position, nalLength);
126 this.position = this.length; 115 this.position = this.length;
127 116
128 if ( null != nal ) // If the user pass in a ByteArray, copy the NAL to it. 117 if (nalContainer) {
129 nal.writeBytes( this, nalStart, nalLength ); 118 // Add the tag to the NAL unit
119 nalContainer.push(this.bytes.subarray(nalStart, nalStart + nalLength));
120 }
130 } 121 }
131 122
132 adHoc = 0; 123 adHoc = 0;
124 };
125
126 // (key:String, val:Number):void
127 this.writeMetaDataDouble = function(key, val) {
128 var i;
129 this.view.setUint16(this.position, key.length);
130 this.position += 2;
131 for (i in key) {
132 console.assert(key.charCodeAt(i) < 255);
133 this.bytes[this.position] = key.charCodeAt(i);
134 this.position++;
133 } 135 }
134 136 this.view.setUint8(this.position, 0x00);
135 public function writeMetaDataDouble(key:String, val:Number):void 137 this.position++;
136 { 138 this.view.setFloat64(this.position, val);
137 writeShort ( key.length ); 139 this.position += 2;
138 writeUTFBytes ( key );
139 writeByte ( 0x00 );
140 writeDouble ( val );
141 ++adHoc; 140 ++adHoc;
141 };
142
143 // (key:String, val:Boolean):void
144 this.writeMetaDataBoolean = function(key, val) {
145 var i;
146 this.view.setUint16(this.position, key.length);
147 this.position += 2;
148 for (i in key) {
149 console.assert(key.charCodeAt(i) < 255);
150 this.bytes[this.position] = key.charCodeAt(i);
151 this.position++;
142 } 152 }
143 153 this.view.setUint8(this.position, 0x01);
144 public function writeMetaDataBoolean(key:String, val:Boolean):void 154 this.position++;
145 { 155 this.view.setUint8(this.position, val ? 0x01 : 0x00);
146 writeShort ( key.length ); 156 this.position++;
147 writeUTFBytes ( key );
148 writeByte ( 0x01 );
149 writeByte ( true == val ? 0x01 : 0x00 );
150 ++adHoc; 157 ++adHoc;
151 } 158 };
152 159
153 public function finalize():ByteArray 160 // ():ByteArray
154 { 161 this.finalize = function() {
155 switch(this[0]) 162 var
156 { // Video Data 163 dtsDelta, // :int
157 case VIDEO_TAG: 164 len; // :int
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) 165
159 this[12] = extraData ? 0x00 : 0x01; 166 switch(this.bytes[0]) {
160 167 // Video Data
161 var dtsDelta:int = pts - dts; 168 case hls.FlvTag.VIDEO_TAG:
162 this[13] = ( dtsDelta & 0x00FF0000 ) >>> 16; 169 this.bytes[11] = ((this.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)
163 this[14] = ( dtsDelta & 0x0000FF00 ) >>> 8; 170 this.bytes[12] = extraData ? 0x00 : 0x01;
164 this[15] = ( dtsDelta & 0x000000FF ) >>> 0; 171
172 dtsDelta = this.pts - this.dts;
173 this.bytes[13] = (dtsDelta & 0x00FF0000) >>> 16;
174 this.bytes[14] = (dtsDelta & 0x0000FF00) >>> 8;
175 this.bytes[15] = (dtsDelta & 0x000000FF) >>> 0;
165 break; 176 break;
166 177
167 case AUDIO_TAG: 178 case hls.FlvTag.AUDIO_TAG:
168 this[11] = 0xAF; 179 this.bytes[11] = 0xAF;
169 this[12] = extraData ? 0x00 : 0x01; 180 this.bytes[12] = extraData ? 0x00 : 0x01;
170 break; 181 break;
171 182
172 case METADATA_TAG: 183 case hls.FlvTag.METADATA_TAG:
173 this.position = 11; 184 this.position = 11;
174 writeByte(0x02); // String type 185 this.view.setUint8(this.position, 0x02); // String type
175 writeShort(0x0A); // 10 Bytes 186 this.position++;
176 writeUTFBytes("onMetaData"); 187 this.view.setUint16(this.position, 0x0A); // 10 Bytes
177 writeByte(0x08); // Array type 188 this.position += 2;
178 writeUnsignedInt( adHoc ); 189 // set "onMetaData"
190 this.bytes.set([0x6f, 0x6e, 0x4d, 0x65,
191 0x74, 0x61, 0x44, 0x61,
192 0x74, 0x61], this.position);
193 this.position += 10;
194 this.view.setUint8(this.position,
195 this.bytes[this.position] | 0x08); // Array type
196 this.position++;
197 this.view.setUint32(this.position, adHoc);
179 this.position = this.length; 198 this.position = this.length;
180 writeUnsignedInt( 0x09 ); // End Data Tag 199 this.view.setUint32(this.position, 0x09); // End Data Tag
200 this.position += 4;
201 this.length = this.position;
181 break; 202 break;
182 } 203 }
183 204
184 var len:int = this.length - 11; 205 len = this.length - 11;
185 206
186 this[ 1] = ( len & 0x00FF0000 ) >>> 16; 207 this.bytes[ 1] = ( len & 0x00FF0000 ) >>> 16;
187 this[ 2] = ( len & 0x0000FF00 ) >>> 8; 208 this.bytes[ 2] = ( len & 0x0000FF00 ) >>> 8;
188 this[ 3] = ( len & 0x000000FF ) >>> 0; 209 this.bytes[ 3] = ( len & 0x000000FF ) >>> 0;
189 this[ 4] = ( pts & 0x00FF0000 ) >>> 16; 210 this.bytes[ 4] = ( this.pts & 0x00FF0000 ) >>> 16;
190 this[ 5] = ( pts & 0x0000FF00 ) >>> 8; 211 this.bytes[ 5] = ( this.pts & 0x0000FF00 ) >>> 8;
191 this[ 6] = ( pts & 0x000000FF ) >>> 0; 212 this.bytes[ 6] = ( this.pts & 0x000000FF ) >>> 0;
192 this[ 7] = ( pts & 0xFF000000 ) >>> 24; 213 this.bytes[ 7] = ( this.pts & 0xFF000000 ) >>> 24;
193 this[ 8] = 0; 214 this.bytes[ 8] = 0;
194 this[ 9] = 0; 215 this.bytes[ 9] = 0;
195 this[10] = 0; 216 this.bytes[10] = 0;
196 217
197 this.writeUnsignedInt( this.length ); 218 this.view.setUint32(this.length, this.length);
219 this.length += 4;
220 this.position += 4;
198 return this; 221 return this;
222 };
223 };
224
225 hls.FlvTag.AUDIO_TAG = 0x08; // :uint
226 hls.FlvTag.VIDEO_TAG = 0x09; // :uint
227 hls.FlvTag.METADATA_TAG = 0x12; // :uint
228
229 // (tag:ByteArray):Boolean {
230 hls.FlvTag.isAudioFrame = function(tag) {
231 return hls.FlvTag.AUDIO_TAG === tag[0];
232 };
233
234 // (tag:ByteArray):Boolean {
235 hls.FlvTag.isVideoFrame = function(tag) {
236 return hls.FlvTag.VIDEO_TAG === tag[0];
237 };
238
239 // (tag:ByteArray):Boolean {
240 hls.FlvTag.isMetaData = function(tag) {
241 return hls.FlvTag.METADATA_TAG === tag[0];
242 };
243
244 // (tag:ByteArray):Boolean {
245 hls.FlvTag.isKeyFrame = function(tag) {
246 if (hls.FlvTag.isVideoFrame(tag)) {
247 return tag[11] === 0x17;
199 } 248 }
249
250 if (hls.FlvTag.isAudioFrame(tag)) {
251 return true;
252 }
253
254 if (hls.FlvTag.isMetaData(tag)) {
255 return true;
200 } 256 }
201 } 257
202 */ 258 return false;
259 };
260
261 // (tag:ByteArray):uint {
262 hls.FlvTag.frameTime = function(tag) {
263 var pts = tag[ 4] << 16; // :uint
264 pts |= tag[ 5] << 8;
265 pts |= tag[ 6] << 0;
266 pts |= tag[ 7] << 24;
267 return pts;
268 };
269
203 })(this); 270 })(this);
......
...@@ -8,27 +8,31 @@ ...@@ -8,27 +8,31 @@
8 8
9 (function(window) { 9 (function(window) {
10 var 10 var
11
11 ExpGolomb = window.videojs.hls.ExpGolomb, 12 ExpGolomb = window.videojs.hls.ExpGolomb,
12 FlvTag = window.videojs.hls.FlvTag, 13 FlvTag = window.videojs.hls.FlvTag,
13 H264ExtraData = function() {
14 var
15 sps = [], // :Array
16 pps = []; // :Array
17 14
18 this.addSPS = function() { // :ByteArray 15 H264ExtraData = function() {
19 var tmp = new Uint8Array(); // :ByteArray 16 this.sps = []; // :Array
20 sps.push(tmp); 17 this.pps = []; // :Array
18
19 this.addSPS = function(size) { // :ByteArray
20 console.log('come on, you fucker');
21 console.assert(size > 0);
22 var tmp = new Uint8Array(size); // :ByteArray
23 this.sps.push(tmp);
21 return tmp; 24 return tmp;
22 }; 25 };
23 26
24 this.addPPS = function() { // :ByteArray 27 this.addPPS = function(size) { // :ByteArray
25 var tmp = new Uint8Array(); // :ByteArray 28 console.assert(size);
26 pps.push(tmp); 29 var tmp = new Uint8Array(size); // :ByteArray
30 this.pps.push(tmp);
27 return tmp; 31 return tmp;
28 }; 32 };
29 33
30 this.extraDataExists = function() { // :Boolean 34 this.extraDataExists = function() { // :Boolean
31 return 0 < sps.length; 35 return 0 < this.sps.length;
32 }; 36 };
33 37
34 // (sizeOfScalingList:int, expGolomb:ExpGolomb):void 38 // (sizeOfScalingList:int, expGolomb:ExpGolomb):void
...@@ -53,18 +57,20 @@ ...@@ -53,18 +57,20 @@
53 }; 57 };
54 58
55 this.getSps0Rbsp = function() { // :ByteArray 59 this.getSps0Rbsp = function() { // :ByteArray
56 // remove emulation bytes. Is this nesessary? is there ever emulation bytes in the SPS? 60 // remove emulation bytes. Is this nesessary? is there ever emulation
61 // bytes in the SPS?
57 var 62 var
58 sps0 = sps[0],// :ByteArray = sps[0]; 63 spsCount = 0,
64 sps0 = this.sps[0], // :ByteArray
65 rbspCount = 0,
59 s, // :uint 66 s, // :uint
60 e, // :uint 67 e, // :uint
61 rbsp, // :ByteArray 68 rbsp, // :ByteArray
62 o; // :uint 69 o; // :uint
63 70
64 sps0.position = 1; 71 s = 1;
65 s = sps0.position; 72 e = sps0.byteLength - 2;
66 e = sps0.bytesAvailable - 2; 73 rbsp = new Uint8Array(sps0.byteLength); // new ByteArray();
67 rbsp = new Uint8Array(); // new ByteArray();
68 74
69 for (o = s ; o < e ;) { 75 for (o = s ; o < e ;) {
70 if (3 !== sps0[o + 2]) { 76 if (3 !== sps0[o + 2]) {
...@@ -73,27 +79,32 @@ ...@@ -73,27 +79,32 @@
73 o += 2; 79 o += 2;
74 } else if (0 !== sps0[o + 0]) { 80 } else if (0 !== sps0[o + 0]) {
75 o += 1; 81 o += 1;
76 } else { // found emulation bytess 82 } else {
77 rbsp.writeShort(0x0000); 83 console.log('found emulation bytes');
78 84
79 if ( o > s ) { 85 rbsp.set([0x00, 0x00], rbspCount);
86 spsCount += 2;
87 rbspCount += 2;
88
89 if (o > s) {
80 // If there are bytes to write, write them 90 // If there are bytes to write, write them
81 sps0.readBytes( rbsp, rbsp.length, o-s ); 91 rbsp.set(sps0.subarray(0, o - s), rbspCount);
92 spsCount += o - s;
93 rbspCount += o - s;
82 } 94 }
83 95
84 // skip the emulation bytes 96 // skip the emulation bytes
85 sps0.position += 3; 97 o += 3;
86 o = s = sps0.position; 98 s = o;
87 } 99 }
88 } 100 }
89 101
90 // copy any remaining bytes 102 // copy any remaining bytes
91 sps0.readBytes(rbsp, rbsp.length); 103 rbsp.set(sps0.subarray(spsCount), rbspCount); // sps0.readBytes(rbsp, rbsp.length);
92 sps0.position = 0;
93 return rbsp; 104 return rbsp;
94 }; 105 };
95 106
96 //(pts:uint):FlvTag 107 // (pts:uint):FlvTag
97 this.metaDataTag = function(pts) { 108 this.metaDataTag = function(pts) {
98 var 109 var
99 tag = new FlvTag(FlvTag.METADATA_TAG), // :FlvTag 110 tag = new FlvTag(FlvTag.METADATA_TAG), // :FlvTag
...@@ -144,7 +155,7 @@ ...@@ -144,7 +155,7 @@
144 expGolomb.skipUnsignedExpGolomb(); // bit_depth_luma_minus8 155 expGolomb.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
145 expGolomb.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8 156 expGolomb.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
146 expGolomb.skipBits(1); // qpprime_y_zero_transform_bypass_flag 157 expGolomb.skipBits(1); // qpprime_y_zero_transform_bypass_flag
147 if ( expGolomb.readBoolean() ) { // seq_scaling_matrix_present_flag 158 if (expGolomb.readBoolean()) { // seq_scaling_matrix_present_flag
148 imax = (chroma_format_idc !== 3) ? 8 : 12; 159 imax = (chroma_format_idc !== 3) ? 8 : 12;
149 for (i = 0 ; i < imax ; ++i) { 160 for (i = 0 ; i < imax ; ++i) {
150 if (expGolomb.readBoolean()) { // seq_scaling_list_present_flag[ i ] 161 if (expGolomb.readBoolean()) { // seq_scaling_list_present_flag[ i ]
...@@ -214,22 +225,38 @@ ...@@ -214,22 +225,38 @@
214 tag.pts = pts; 225 tag.pts = pts;
215 226
216 tag.writeByte(0x01);// version 227 tag.writeByte(0x01);// version
217 tag.writeByte(sps[0][1]);// profile 228 tag.writeByte(this.sps[0][1]);// profile
218 tag.writeByte(sps[0][2]);// compatibility 229 tag.writeByte(this.sps[0][2]);// compatibility
219 tag.writeByte(sps[0][3]);// level 230 tag.writeByte(this.sps[0][3]);// level
220 tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits) 231 tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits)
221 tag.writeByte(0xE0 | 0x01 ); // reserved (3 bits), num of SPS (5 bits) 232 tag.writeByte(0xE0 | 0x01 ); // reserved (3 bits), num of SPS (5 bits)
222 tag.writeShort( sps[0].length ); // data of SPS 233 tag.writeShort( this.sps[0].length ); // data of SPS
223 tag.writeBytes( sps[0] ); // SPS 234 tag.writeBytes( this.sps[0] ); // SPS
224 235
225 tag.writeByte( pps.length ); // num of PPS (will there ever be more that 1 PPS?) 236 tag.writeByte( this.pps.length ); // num of PPS (will there ever be more that 1 PPS?)
226 for (i = 0 ; i < pps.length ; ++i) { 237 for (i = 0 ; i < this.pps.length ; ++i) {
227 tag.writeShort(pps[i].length); // 2 bytes for length of PPS 238 tag.writeShort(this.pps[i].length); // 2 bytes for length of PPS
228 tag.writeBytes(pps[i]); // data of PPS 239 tag.writeBytes(this.pps[i]); // data of PPS
229 } 240 }
230 241
231 return tag; 242 return tag;
232 }; 243 };
244 },
245
246 // incomplete, see Table 7.1 of ITU-T H.264 for 12-32
247 NALUnitType = {
248 unspecified: 0,
249 slice_layer_without_partitioning_rbsp_non_idr: 1,
250 slice_data_partition_a_layer_rbsp: 2,
251 slice_data_partition_b_layer_rbsp: 3,
252 slice_data_partition_c_layer_rbsp: 4,
253 slice_layer_without_partitioning_rbsp_idr: 5,
254 sei_rbsp: 6,
255 seq_parameter_set_rbsp: 7,
256 pic_parameter_set_rbsp: 8,
257 access_unit_delimiter_rbsp: 9,
258 end_of_seq_rbsp: 10,
259 end_of_stream_rbsp: 11
233 }; 260 };
234 261
235 window.videojs.hls.H264Stream = function() { 262 window.videojs.hls.H264Stream = function() {
...@@ -270,17 +297,18 @@ ...@@ -270,17 +297,18 @@
270 }; 297 };
271 298
272 this.finishFrame = function() { 299 this.finishFrame = function() {
273 if (null !== h264Frame) { 300 console.log('finish frame');
301 if (h264Frame) {
274 // Push SPS before EVERY IDR frame fo seeking 302 // Push SPS before EVERY IDR frame fo seeking
275 if (newExtraData.extraDataExists()) { 303 if (newExtraData.extraDataExists()) {
276 oldExtraData = newExtraData; 304 oldExtraData = newExtraData;
277 newExtraData = new H264ExtraData(); 305 newExtraData = new H264ExtraData();
278 } 306 }
279 307
280 if(true === h264Frame.keyFrame) { 308 if (h264Frame.keyFrame) {
281 // Push extra data on every IDR frame in case we did a stream change + seek 309 // Push extra data on every IDR frame in case we did a stream change + seek
282 tags.push( oldExtraData.metaDataTag (h264Frame.pts) ); 310 tags.push(oldExtraData.metaDataTag(h264Frame.pts));
283 tags.push( oldExtraData.extraDataTag(h264Frame.pts) ); 311 tags.push(oldExtraData.extraDataTag(h264Frame.pts));
284 } 312 }
285 313
286 h264Frame.endNalUnit(); 314 h264Frame.endNalUnit();
...@@ -292,31 +320,35 @@ ...@@ -292,31 +320,35 @@
292 state = 0; 320 state = 0;
293 }; 321 };
294 322
295 // (pData:ByteArray, o:int, l:int):void 323 // (data:ByteArray, o:int, l:int):void
296 this.writeBytes = function(pData, o, l) { 324 this.writeBytes = function(data, o, l) {
297 var 325 var
298 nalUnitSize, // :uint 326 nalUnitSize, // :uint
299 s, // :uint 327 s, // :uint
300 e, // :uint 328 e, // :uint
301 t; // :int 329 t; // :int
302 330
303 if (0 >= l) { 331 if (l <= 0) {
332 // data is empty so there's nothing to write
304 return; 333 return;
305 } 334 }
335
336 // scan through the bytes until we find the start code (0x000001) for a
337 // NAL unit and then begin writing it out
306 switch (state) { 338 switch (state) {
307 default: 339 default:
308 state = 1; 340 /* falls through */
309 break;
310 case 0: 341 case 0:
311 state = 1; 342 state = 1;
312 break; 343 /* falls through */
313 /*--------------------------------------------------------------------------------------------------------------------*/ 344 case 1:
314 case 1: // We are looking for overlaping start codes 345 // A NAL unit may be split across two TS packets. Look back a bit to
315 if (1 >= pData[o]) { 346 // make sure the prefix of the start code wasn't already written out.
316 nalUnitSize = (null === h264Frame) ? 0 : h264Frame.nalUnitSize(); 347 if (data[o] <= 1) {
317 if (1 <= nalUnitSize && 0 === h264Frame.negIndex(1)) { 348 nalUnitSize = h264Frame ? h264Frame.nalUnitSize() : 0;
349 if (nalUnitSize >= 1 && h264Frame.negIndex(1) === 0) {
318 // ?? ?? 00 | O[01] ?? ?? 350 // ?? ?? 00 | O[01] ?? ??
319 if (1 === pData[o] && 2 <= nalUnitSize && 0 === h264Frame.negIndex(2) ) { 351 if (1 === data[o] && 2 <= nalUnitSize && 0 === h264Frame.negIndex(2)) {
320 // ?? 00 00 : 01 352 // ?? 00 00 : 01
321 if (3 <= nalUnitSize && 0 === h264Frame.negIndex(3)) { 353 if (3 <= nalUnitSize && 0 === h264Frame.negIndex(3)) {
322 h264Frame.length -= 3; // 00 00 00 : 01 354 h264Frame.length -= 3; // 00 00 00 : 01
...@@ -325,10 +357,10 @@ ...@@ -325,10 +357,10 @@
325 } 357 }
326 358
327 state = 3; 359 state = 3;
328 return this.writeBytes(pData, o + 1, l - 1); 360 return this.writeBytes(data, o + 1, l - 1);
329 } 361 }
330 362
331 if (1 < l && 0 === pData[o] && 1 === pData[o + 1]) { 363 if (1 < l && 0 === data[o] && 1 === data[o + 1]) {
332 // ?? 00 | 00 01 364 // ?? 00 | 00 01
333 if (2 <= nalUnitSize && 0 === h264Frame.negIndex(2)) { 365 if (2 <= nalUnitSize && 0 === h264Frame.negIndex(2)) {
334 h264Frame.length -= 2; // 00 00 : 00 01 366 h264Frame.length -= 2; // 00 00 : 00 01
...@@ -337,71 +369,77 @@ ...@@ -337,71 +369,77 @@
337 } 369 }
338 370
339 state = 3; 371 state = 3;
340 return this.writeBytes(pData, o + 2, l - 2); 372 return this.writeBytes(data, o + 2, l - 2);
341 } 373 }
342 374
343 if (2 < l && 0 === pData[o] && 0 === pData[o + 1] && 1 === pData[o + 2]) { 375 if (2 < l && 0 === data[o] && 0 === data[o + 1] && 1 === data[o + 2]) {
344 // 00 | 00 00 01 376 // 00 | 00 00 01
345 h264Frame.length -= 1; 377 h264Frame.length -= 1;
346 state = 3; 378 state = 3;
347 return this.writeBytes(pData, o + 3, l - 3); 379 return this.writeBytes(data, o + 3, l - 3);
348 } 380 }
349 } 381 }
350 } 382 }
351 // allow fall through if the above fails, we may end up checking a few 383 // allow fall through if the above fails, we may end up checking a few
352 // bytes a second time. But that case will be VERY rare 384 // bytes a second time. But that case will be VERY rare
353 state = 2; 385 state = 2;
354 break; 386 /* falls through */
355 case 2: // Look for start codes in pData 387 case 2: // Look for start codes in data
356 s = o; // s = Start 388 s = o; // s = Start
357 e = s + l; // e = End 389 e = s + l; // e = End
358 for (t = e - 3 ; o < t ;) { 390 for (t = e - 3 ; o < t ;) {
359 if (1 < pData[o + 2]) { 391 if (1 < data[o + 2]) {
360 o += 3; // if pData[o+2] is greater than 1, there is no way a start code can begin before o+3 392 o += 3; // if data[o + 2] is greater than 1, there is no way a start code can begin before o+3
361 } else if (0 !== pData[o + 1]) { 393 } else if (0 !== data[o + 1]) {
362 o += 2; 394 o += 2;
363 } else if (0 !== pData[o]) { 395 } else if (0 !== data[o]) {
364 o += 1; 396 o += 1;
365 } else { 397 } else {
366 // If we get here we have 00 00 00 or 00 00 01 398 // If we get here we have 00 00 00 or 00 00 01
367 if (1 === pData[o + 2]) { 399 if (1 === data[o + 2]) {
368 if (o > s) { 400 if (o > s) {
369 h264Frame.writeBytes(pData, s, o - s); 401 h264Frame.writeBytes(data, s, o - s);
370 } 402 }
371 state = 3; o += 3; 403 state = 3;
372 return this.writeBytes(pData, o, e - o); 404 o += 3;
405 return this.writeBytes(data, o, e - o);
373 } 406 }
374 407
375 if (4 <= e-o && 0 === pData[o + 2] && 1 === pData[o + 3]) { 408 if (e - o >= 4 && 0 === data[o + 2] && 1 === data[o + 3]) {
376 if (o > s) { 409 if (o > s) {
377 h264Frame.writeBytes(pData, s, o - s); 410 h264Frame.writeBytes(data, s, o - s);
378 } 411 }
379 state = 3; 412 state = 3;
380 o += 4; 413 o += 4;
381 return this.writeBytes(pData, o, e - o); 414 return this.writeBytes(data, o, e - o);
382 } 415 }
383 416
384 // We are at the end of the buffer, or we have 3 NULLS followed by something that is not a 1, eaither way we can step forward by at least 3 417 // We are at the end of the buffer, or we have 3 NULLS followed by
418 // something that is not a 1, either way we can step forward by at
419 // least 3
385 o += 3; 420 o += 3;
386 } 421 }
387 } 422 }
388 423
389 // We did not find any start codes. Try again next packet 424 // We did not find any start codes. Try again next packet
390 state = 1; 425 state = 1;
391 h264Frame.writeBytes( pData, s, l ); 426 h264Frame.writeBytes(data, s, l);
392 return; 427 return;
393 /*--------------------------------------------------------------------------------------------------------------------*/ 428 case 3:
394 case 3: // The next byte is the first byte of a NAL Unit 429 // The next byte is the first byte of a NAL Unit
395 if (null !== h264Frame) { 430
431 if (h264Frame) {
432 // we've come to a new NAL unit so finish up the one we've been
433 // working on
434
396 switch (nalUnitType) { 435 switch (nalUnitType) {
397 // We are still operating on the previous NAL Unit 436 case NALUnitType.seq_parameter_set_rbsp:
398 case 7: 437 h264Frame.endNalUnit(newExtraData.sps);
399 h264Frame.endNalUnit(newExtraData.addSPS());
400 break; 438 break;
401 case 8: 439 case NALUnitType.pic_parameter_set_rbsp:
402 h264Frame.endNalUnit(newExtraData.addPPS()); 440 h264Frame.endNalUnit(newExtraData.pps);
403 break; 441 break;
404 case 5: 442 case NALUnitType.slice_layer_without_partitioning_rbsp_idr:
405 h264Frame.keyFrame = true; 443 h264Frame.keyFrame = true;
406 h264Frame.endNalUnit(); 444 h264Frame.endNalUnit();
407 break; 445 break;
...@@ -411,14 +449,14 @@ ...@@ -411,14 +449,14 @@
411 } 449 }
412 } 450 }
413 451
414 nalUnitType = pData[o] & 0x1F; 452 // setup to begin processing the new NAL unit
415 if ( null != h264Frame && 9 === nalUnitType ) { 453 nalUnitType = data[o] & 0x1F;
454 if (h264Frame && 9 === nalUnitType) {
416 this.finishFrame(); // We are starting a new access unit. Flush the previous one 455 this.finishFrame(); // We are starting a new access unit. Flush the previous one
417 } 456 }
418 457
419 // finishFrame may render h264Frame null, so we must test again 458 // finishFrame may render h264Frame null, so we must test again
420 if ( null === h264Frame ) 459 if (!h264Frame) {
421 {
422 h264Frame = new FlvTag(FlvTag.VIDEO_TAG); 460 h264Frame = new FlvTag(FlvTag.VIDEO_TAG);
423 h264Frame.pts = next_pts; 461 h264Frame.pts = next_pts;
424 h264Frame.dts = next_dts; 462 h264Frame.dts = next_dts;
...@@ -426,7 +464,7 @@ ...@@ -426,7 +464,7 @@
426 464
427 h264Frame.startNalUnit(); 465 h264Frame.startNalUnit();
428 state = 2; // We know there will not be an overlapping start code, so we can skip that test 466 state = 2; // We know there will not be an overlapping start code, so we can skip that test
429 return this.writeBytes(pData, o, l); 467 return this.writeBytes(data, o, l);
430 /*--------------------------------------------------------------------------------------------------------------------*/ 468 /*--------------------------------------------------------------------------------------------------------------------*/
431 } // switch 469 } // switch
432 }; 470 };
......
...@@ -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
......
No preview for this file type
No preview for this file type
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);
......