ced9b85e by Tom Johnson
2 parents 0f3916fb 5351582b
1 /<!DOCTYPE html> 1 <!DOCTYPE html>
2 <html> 2 <html>
3 <head> 3 <head>
4 <meta charset="utf-8"> 4 <meta charset="utf-8">
......
1 (function(window) { 1 (function(window) {
2 2
3 /**
4 * Parser for exponential Golomb codes, a variable-bitwidth number encoding
5 * scheme used by h264.
6 */
3 window.videojs.hls.ExpGolomb = function(workingData) { 7 window.videojs.hls.ExpGolomb = function(workingData) {
4 var 8 var
5 // the number of bytes left to examine in workingData 9 // the number of bytes left to examine in workingData
6 workingBytesAvailable = workingData.byteLength, 10 workingBytesAvailable = workingData.byteLength,
7 11
8 // the current word being examined 12 // the current word being examined
9 workingWord, // :uint 13 workingWord = 0, // :uint
10 14
11 // the number of bits left to examine in the current word 15 // the number of bits left to examine in the current word
12 workingBitsAvailable; // :uint; 16 workingBitsAvailable = 0; // :uint;
13 17
14 // ():uint 18 // ():uint
15 this.length = function() { 19 this.length = function() {
...@@ -21,15 +25,24 @@ window.videojs.hls.ExpGolomb = function(workingData) { ...@@ -21,15 +25,24 @@ window.videojs.hls.ExpGolomb = function(workingData) {
21 return (8 * workingBytesAvailable) + workingBitsAvailable; 25 return (8 * workingBytesAvailable) + workingBitsAvailable;
22 }; 26 };
23 27
28 this.logStuff = function() {
29 console.log('bits', workingBitsAvailable, 'word', (workingWord >>> 0));
30 };
31
24 // ():void 32 // ():void
25 this.loadWord = function() { 33 this.loadWord = function() {
26 var 34 var
35 position = workingData.byteLength - workingBytesAvailable,
27 workingBytes = new Uint8Array(4), 36 workingBytes = new Uint8Array(4),
28 availableBytes = Math.min(4, workingBytesAvailable); 37 availableBytes = Math.min(4, workingBytesAvailable);
29 38
30 // console.assert(availableBytes > 0); 39 // console.assert(availableBytes > 0);
40 if (availableBytes === 0) {
41 throw new Error('no bytes available');
42 }
31 43
32 workingBytes.set(workingData.subarray(0, availableBytes)); 44 workingBytes.set(workingData.subarray(position,
45 position + availableBytes));
33 workingWord = new DataView(workingBytes.buffer).getUint32(0); 46 workingWord = new DataView(workingBytes.buffer).getUint32(0);
34 47
35 // track the amount of workingData that has been processed 48 // track the amount of workingData that has been processed
...@@ -37,23 +50,23 @@ window.videojs.hls.ExpGolomb = function(workingData) { ...@@ -37,23 +50,23 @@ window.videojs.hls.ExpGolomb = function(workingData) {
37 workingBytesAvailable -= availableBytes; 50 workingBytesAvailable -= availableBytes;
38 }; 51 };
39 52
40 // (size:int):void 53 // (count:int):void
41 this.skipBits = function(size) { 54 this.skipBits = function(count) {
42 var skipBytes; // :int 55 var skipBytes; // :int
43 if (workingBitsAvailable > size) { 56 if (workingBitsAvailable > count) {
44 workingWord <<= size; 57 workingWord <<= count;
45 workingBitsAvailable -= size; 58 workingBitsAvailable -= count;
46 } else { 59 } else {
47 size -= workingBitsAvailable; 60 count -= workingBitsAvailable;
48 skipBytes = size / 8; 61 skipBytes = count / 8;
49 62
50 size -= (skipBytes * 8); 63 count -= (skipBytes * 8);
51 workingData.position += skipBytes; 64 workingBytesAvailable -= skipBytes;
52 65
53 this.loadWord(); 66 this.loadWord();
54 67
55 workingWord <<= size; 68 workingWord <<= count;
56 workingBitsAvailable -= size; 69 workingBitsAvailable -= count;
57 } 70 }
58 }; 71 };
59 72
...@@ -63,17 +76,17 @@ window.videojs.hls.ExpGolomb = function(workingData) { ...@@ -63,17 +76,17 @@ window.videojs.hls.ExpGolomb = function(workingData) {
63 bits = Math.min(workingBitsAvailable, size), // :uint 76 bits = Math.min(workingBitsAvailable, size), // :uint
64 valu = workingWord >>> (32 - bits); // :uint 77 valu = workingWord >>> (32 - bits); // :uint
65 78
66 console.assert(32 > size, 'Cannot read more than 32 bits at a time'); 79 console.assert(size < 32, 'Cannot read more than 32 bits at a time');
67 80
68 workingBitsAvailable -= bits; 81 workingBitsAvailable -= bits;
69 if (0 < workingBitsAvailable) { 82 if (workingBitsAvailable > 0) {
70 workingWord <<= bits; 83 workingWord <<= bits;
71 } else { 84 } else if (workingBytesAvailable > 0) {
72 this.loadWord(); 85 this.loadWord();
73 } 86 }
74 87
75 bits = size - bits; 88 bits = size - bits;
76 if (0 < bits) { 89 if (bits > 0) {
77 return valu << bits | this.readBits(bits); 90 return valu << bits | this.readBits(bits);
78 } else { 91 } else {
79 return valu; 92 return valu;
...@@ -82,18 +95,19 @@ window.videojs.hls.ExpGolomb = function(workingData) { ...@@ -82,18 +95,19 @@ window.videojs.hls.ExpGolomb = function(workingData) {
82 95
83 // ():uint 96 // ():uint
84 this.skipLeadingZeros = function() { 97 this.skipLeadingZeros = function() {
85 var clz; // :uint 98 var leadingZeroCount; // :uint
86 for (clz = 0 ; clz < workingBitsAvailable ; ++clz) { 99 for (leadingZeroCount = 0 ; leadingZeroCount < workingBitsAvailable ; ++leadingZeroCount) {
87 if (0 !== (workingWord & (0x80000000 >>> clz))) { 100 if (0 !== (workingWord & (0x80000000 >>> leadingZeroCount))) {
88 workingWord <<= clz; 101 // the first bit of working word is 1
89 workingBitsAvailable -= clz; 102 workingWord <<= leadingZeroCount;
90 return clz; 103 workingBitsAvailable -= leadingZeroCount;
104 return leadingZeroCount;
91 } 105 }
92 } 106 }
93 107
94 // we exhausted workingWord and still have not found a 1 108 // we exhausted workingWord and still have not found a 1
95 this.loadWord(); 109 this.loadWord();
96 return clz + this.skipLeadingZeros(); 110 return leadingZeroCount + this.skipLeadingZeros();
97 }; 111 };
98 112
99 // ():void 113 // ():void
......
...@@ -105,7 +105,8 @@ hls.FlvTag = function(type, extraData) { ...@@ -105,7 +105,8 @@ hls.FlvTag = function(type, extraData) {
105 105
106 // Rewind to the marker and write the size 106 // Rewind to the marker and write the size
107 if (this.length === adHoc + 4) { 107 if (this.length === adHoc + 4) {
108 this.length -= 4; // we started a nal unit, but didnt write one, so roll back the 4 byte size value 108 // we started a nal unit, but didnt write one, so roll back the 4 byte size value
109 this.length -= 4;
109 } else if (adHoc > 0) { 110 } else if (adHoc > 0) {
110 nalStart = adHoc + 4; 111 nalStart = adHoc + 4;
111 nalLength = this.length - nalStart; 112 nalLength = this.length - nalStart;
...@@ -207,13 +208,16 @@ hls.FlvTag = function(type, extraData) { ...@@ -207,13 +208,16 @@ hls.FlvTag = function(type, extraData) {
207 208
208 len = this.length - 11; 209 len = this.length - 11;
209 210
211 // write the DataSize field
210 this.bytes[ 1] = (len & 0x00FF0000) >>> 16; 212 this.bytes[ 1] = (len & 0x00FF0000) >>> 16;
211 this.bytes[ 2] = (len & 0x0000FF00) >>> 8; 213 this.bytes[ 2] = (len & 0x0000FF00) >>> 8;
212 this.bytes[ 3] = (len & 0x000000FF) >>> 0; 214 this.bytes[ 3] = (len & 0x000000FF) >>> 0;
215 // write the Timestamp
213 this.bytes[ 4] = (this.pts & 0x00FF0000) >>> 16; 216 this.bytes[ 4] = (this.pts & 0x00FF0000) >>> 16;
214 this.bytes[ 5] = (this.pts & 0x0000FF00) >>> 8; 217 this.bytes[ 5] = (this.pts & 0x0000FF00) >>> 8;
215 this.bytes[ 6] = (this.pts & 0x000000FF) >>> 0; 218 this.bytes[ 6] = (this.pts & 0x000000FF) >>> 0;
216 this.bytes[ 7] = (this.pts & 0xFF000000) >>> 24; 219 this.bytes[ 7] = (this.pts & 0xFF000000) >>> 24;
220 // write the StreamID
217 this.bytes[ 8] = 0; 221 this.bytes[ 8] = 0;
218 this.bytes[ 9] = 0; 222 this.bytes[ 9] = 0;
219 this.bytes[10] = 0; 223 this.bytes[10] = 0;
...@@ -230,9 +234,9 @@ hls.FlvTag = function(type, extraData) { ...@@ -230,9 +234,9 @@ hls.FlvTag = function(type, extraData) {
230 }; 234 };
231 }; 235 };
232 236
233 hls.FlvTag.AUDIO_TAG = 0x08; // :uint 237 hls.FlvTag.AUDIO_TAG = 0x08; // == 8, :uint
234 hls.FlvTag.VIDEO_TAG = 0x09; // :uint 238 hls.FlvTag.VIDEO_TAG = 0x09; // == 9, :uint
235 hls.FlvTag.METADATA_TAG = 0x12; // :uint 239 hls.FlvTag.METADATA_TAG = 0x12; // == 18, :uint
236 240
237 // (tag:ByteArray):Boolean { 241 // (tag:ByteArray):Boolean {
238 hls.FlvTag.isAudioFrame = function(tag) { 242 hls.FlvTag.isAudioFrame = function(tag) {
......
...@@ -55,6 +55,19 @@ ...@@ -55,6 +55,19 @@
55 } 55 }
56 }; 56 };
57 57
58 /**
59 * NAL unit
60 * |- NAL header -|------ RBSP ------|
61 *
62 * NAL unit: Network abstraction layer unit. The combination of a NAL
63 * header and an RBSP.
64 * NAL header: the encapsulation unit for transport-specific metadata in
65 * an h264 stream.
66 * RBSP: raw bit-stream payload. The actual encoded video data.
67 *
68 * SPS: sequence parameter set. Part of the RBSP. Metadata to be applied
69 * to a complete video sequence, like width and height.
70 */
58 this.getSps0Rbsp = function() { // :ByteArray 71 this.getSps0Rbsp = function() { // :ByteArray
59 // remove emulation bytes. Is this nesessary? is there ever emulation 72 // remove emulation bytes. Is this nesessary? is there ever emulation
60 // bytes in the SPS? 73 // bytes in the SPS?
...@@ -62,22 +75,20 @@ ...@@ -62,22 +75,20 @@
62 spsCount = 0, 75 spsCount = 0,
63 sps0 = this.sps[0], // :ByteArray 76 sps0 = this.sps[0], // :ByteArray
64 rbspCount = 0, 77 rbspCount = 0,
65 s, // :uint 78 start = 1, // :uint
66 e, // :uint 79 end = sps0.byteLength - 2, // :uint
67 rbsp, // :ByteArray 80 rbsp = new Uint8Array(sps0.byteLength), // :ByteArray
68 o; // :uint 81 offset = 0; // :uint
69 82
70 s = 1; 83 // H264 requires emulation bytes (0x03) be dropped to interpret NAL
71 e = sps0.byteLength - 2; 84 // units. For instance, 0x8a03b4 should be read as 0x8ab4.
72 rbsp = new Uint8Array(sps0.byteLength); // new ByteArray(); 85 for (offset = start ; offset < end ;) {
73 86 if (3 !== sps0[offset + 2]) {
74 for (o = s ; o < e ;) { 87 offset += 3;
75 if (3 !== sps0[o + 2]) { 88 } else if (0 !== sps0[offset + 1]) {
76 o += 3; 89 offset += 2;
77 } else if (0 !== sps0[o + 1]) { 90 } else if (0 !== sps0[offset + 0]) {
78 o += 2; 91 offset += 1;
79 } else if (0 !== sps0[o + 0]) {
80 o += 1;
81 } else { 92 } else {
82 console.log('found emulation bytes'); 93 console.log('found emulation bytes');
83 94
...@@ -85,21 +96,22 @@ ...@@ -85,21 +96,22 @@
85 spsCount += 2; 96 spsCount += 2;
86 rbspCount += 2; 97 rbspCount += 2;
87 98
88 if (o > s) { 99 if (offset > start) {
89 // If there are bytes to write, write them 100 // If there are bytes to write, write them
90 rbsp.set(sps0.subarray(0, o - s), rbspCount); 101 rbsp.set(sps0.subarray(start, offset - start), rbspCount);
91 spsCount += o - s; 102 spsCount += offset - start;
92 rbspCount += o - s; 103 rbspCount += offset - start;
93 } 104 }
94 105
95 // skip the emulation bytes 106 // skip the emulation bytes
96 o += 3; 107 offset += 3;
97 s = o; 108 start = offset;
98 } 109 }
99 } 110 }
100 111
101 // copy any remaining bytes 112 // copy any remaining bytes
102 rbsp.set(sps0.subarray(spsCount), rbspCount); // sps0.readBytes(rbsp, rbsp.length); 113 rbsp.set(sps0.subarray(spsCount), rbspCount); // sps0.readBytes(rbsp, rbsp.length);
114
103 return rbsp; 115 return rbsp;
104 }; 116 };
105 117
...@@ -122,10 +134,10 @@ ...@@ -122,10 +134,10 @@
122 frame_mbs_only_flag, // :int 134 frame_mbs_only_flag, // :int
123 frame_cropping_flag, // :Boolean 135 frame_cropping_flag, // :Boolean
124 136
125 frame_crop_left_offset, // :int 137 frame_crop_left_offset = 0, // :int
126 frame_crop_right_offset, // :int 138 frame_crop_right_offset = 0, // :int
127 frame_crop_top_offset, // :int 139 frame_crop_top_offset = 0, // :int
128 frame_crop_bottom_offset, // :int 140 frame_crop_bottom_offset = 0, // :int
129 141
130 width, 142 width,
131 height; 143 height;
...@@ -202,8 +214,8 @@ ...@@ -202,8 +214,8 @@
202 frame_crop_bottom_offset = expGolomb.readUnsignedExpGolomb(); 214 frame_crop_bottom_offset = expGolomb.readUnsignedExpGolomb();
203 } 215 }
204 216
205 width = ((pic_width_in_mbs_minus1 +1)*16) - frame_crop_left_offset*2 - frame_crop_right_offset*2; 217 width = ((pic_width_in_mbs_minus1 + 1) * 16) - frame_crop_left_offset * 2 - frame_crop_right_offset * 2;
206 height = ((2 - frame_mbs_only_flag)* (pic_height_in_map_units_minus1 +1) * 16) - (frame_crop_top_offset * 2) - (frame_crop_bottom_offset * 2); 218 height = ((2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16) - (frame_crop_top_offset * 2) - (frame_crop_bottom_offset * 2);
207 219
208 tag.writeMetaDataDouble("videocodecid", 7); 220 tag.writeMetaDataDouble("videocodecid", 7);
209 tag.writeMetaDataDouble("width", width); 221 tag.writeMetaDataDouble("width", width);
...@@ -318,20 +330,25 @@ ...@@ -318,20 +330,25 @@
318 }; 330 };
319 331
320 // (data:ByteArray, o:int, l:int):void 332 // (data:ByteArray, o:int, l:int):void
321 this.writeBytes = function(data, o, l) { 333 this.writeBytes = function(data, offset, length) {
322 var 334 var
323 nalUnitSize, // :uint 335 nalUnitSize, // :uint
324 s, // :uint 336 start, // :uint
325 e, // :uint 337 end, // :uint
326 t; // :int 338 t; // :int
327 339
328 if (l <= 0) { 340 // default argument values
341 offset = offset || 0;
342 length = length || 0;
343
344 if (length <= 0) {
329 // data is empty so there's nothing to write 345 // data is empty so there's nothing to write
330 return; 346 return;
331 } 347 }
332 348
333 // scan through the bytes until we find the start code (0x000001) for a 349 // scan through the bytes until we find the start code (0x000001) for a
334 // NAL unit and then begin writing it out 350 // NAL unit and then begin writing it out
351 // strip NAL start codes as we go
335 switch (state) { 352 switch (state) {
336 default: 353 default:
337 /* falls through */ 354 /* falls through */
...@@ -341,11 +358,11 @@ ...@@ -341,11 +358,11 @@
341 case 1: 358 case 1:
342 // A NAL unit may be split across two TS packets. Look back a bit to 359 // A NAL unit may be split across two TS packets. Look back a bit to
343 // make sure the prefix of the start code wasn't already written out. 360 // make sure the prefix of the start code wasn't already written out.
344 if (data[o] <= 1) { 361 if (data[offset] <= 1) {
345 nalUnitSize = h264Frame ? h264Frame.nalUnitSize() : 0; 362 nalUnitSize = h264Frame ? h264Frame.nalUnitSize() : 0;
346 if (nalUnitSize >= 1 && h264Frame.negIndex(1) === 0) { 363 if (nalUnitSize >= 1 && h264Frame.negIndex(1) === 0) {
347 // ?? ?? 00 | O[01] ?? ?? 364 // ?? ?? 00 | O[01] ?? ??
348 if (1 === data[o] && 2 <= nalUnitSize && 0 === h264Frame.negIndex(2)) { 365 if (1 === data[offset] && 2 <= nalUnitSize && 0 === h264Frame.negIndex(2)) {
349 // ?? 00 00 : 01 366 // ?? 00 00 : 01
350 if (3 <= nalUnitSize && 0 === h264Frame.negIndex(3)) { 367 if (3 <= nalUnitSize && 0 === h264Frame.negIndex(3)) {
351 h264Frame.length -= 3; // 00 00 00 : 01 368 h264Frame.length -= 3; // 00 00 00 : 01
...@@ -354,10 +371,10 @@ ...@@ -354,10 +371,10 @@
354 } 371 }
355 372
356 state = 3; 373 state = 3;
357 return this.writeBytes(data, o + 1, l - 1); 374 return this.writeBytes(data, offset + 1, length - 1);
358 } 375 }
359 376
360 if (1 < l && 0 === data[o] && 1 === data[o + 1]) { 377 if (1 < length && 0 === data[offset] && 1 === data[offset + 1]) {
361 // ?? 00 | 00 01 378 // ?? 00 | 00 01
362 if (2 <= nalUnitSize && 0 === h264Frame.negIndex(2)) { 379 if (2 <= nalUnitSize && 0 === h264Frame.negIndex(2)) {
363 h264Frame.length -= 2; // 00 00 : 00 01 380 h264Frame.length -= 2; // 00 00 : 00 01
...@@ -366,14 +383,17 @@ ...@@ -366,14 +383,17 @@
366 } 383 }
367 384
368 state = 3; 385 state = 3;
369 return this.writeBytes(data, o + 2, l - 2); 386 return this.writeBytes(data, offset + 2, length - 2);
370 } 387 }
371 388
372 if (2 < l && 0 === data[o] && 0 === data[o + 1] && 1 === data[o + 2]) { 389 if (2 < length
390 && 0 === data[offset]
391 && 0 === data[offset + 1]
392 && 1 === data[offset + 2]) {
373 // 00 | 00 00 01 393 // 00 | 00 00 01
374 h264Frame.length -= 1; 394 h264Frame.length -= 1;
375 state = 3; 395 state = 3;
376 return this.writeBytes(data, o + 3, l - 3); 396 return this.writeBytes(data, offset + 3, length - 3);
377 } 397 }
378 } 398 }
379 } 399 }
...@@ -382,45 +402,45 @@ ...@@ -382,45 +402,45 @@
382 state = 2; 402 state = 2;
383 /* falls through */ 403 /* falls through */
384 case 2: // Look for start codes in data 404 case 2: // Look for start codes in data
385 s = o; // s = Start 405 start = offset;
386 e = s + l; // e = End 406 end = start + length;
387 for (t = e - 3 ; o < t ;) { 407 for (t = end - 3 ; offset < t ;) {
388 if (1 < data[o + 2]) { 408 if (1 < data[offset + 2]) {
389 o += 3; // if data[o + 2] is greater than 1, there is no way a start code can begin before o+3 409 offset += 3; // if data[offset + 2] is greater than 1, there is no way a start code can begin before offset+3
390 } else if (0 !== data[o + 1]) { 410 } else if (0 !== data[offset + 1]) {
391 o += 2; 411 offset += 2;
392 } else if (0 !== data[o]) { 412 } else if (0 !== data[offset]) {
393 o += 1; 413 offset += 1;
394 } else { 414 } else {
395 // If we get here we have 00 00 00 or 00 00 01 415 // If we get here we have 00 00 00 or 00 00 01
396 if (1 === data[o + 2]) { 416 if (1 === data[offset + 2]) {
397 if (o > s) { 417 if (offset > start) {
398 h264Frame.writeBytes(data, s, o - s); 418 h264Frame.writeBytes(data, start, offset - start);
399 } 419 }
400 state = 3; 420 state = 3;
401 o += 3; 421 offset += 3;
402 return this.writeBytes(data, o, e - o); 422 return this.writeBytes(data, offset, end - offset);
403 } 423 }
404 424
405 if (e - o >= 4 && 0 === data[o + 2] && 1 === data[o + 3]) { 425 if (end - offset >= 4 && 0 === data[offset + 2] && 1 === data[offset + 3]) {
406 if (o > s) { 426 if (offset > start) {
407 h264Frame.writeBytes(data, s, o - s); 427 h264Frame.writeBytes(data, start, offset - start);
408 } 428 }
409 state = 3; 429 state = 3;
410 o += 4; 430 offset += 4;
411 return this.writeBytes(data, o, e - o); 431 return this.writeBytes(data, offset, end - offset);
412 } 432 }
413 433
414 // We are at the end of the buffer, or we have 3 NULLS followed by 434 // We are at the end of the buffer, or we have 3 NULLS followed by
415 // something that is not a 1, either way we can step forward by at 435 // something that is not a 1, either way we can step forward by at
416 // least 3 436 // least 3
417 o += 3; 437 offset += 3;
418 } 438 }
419 } 439 }
420 440
421 // We did not find any start codes. Try again next packet 441 // We did not find any start codes. Try again next packet
422 state = 1; 442 state = 1;
423 h264Frame.writeBytes(data, s, l); 443 h264Frame.writeBytes(data, start, length);
424 return; 444 return;
425 case 3: 445 case 3:
426 // The next byte is the first byte of a NAL Unit 446 // The next byte is the first byte of a NAL Unit
...@@ -447,7 +467,7 @@ ...@@ -447,7 +467,7 @@
447 } 467 }
448 468
449 // setup to begin processing the new NAL unit 469 // setup to begin processing the new NAL unit
450 nalUnitType = data[o] & 0x1F; 470 nalUnitType = data[offset] & 0x1F;
451 if (h264Frame && 9 === nalUnitType) { 471 if (h264Frame && 9 === nalUnitType) {
452 this.finishFrame(); // We are starting a new access unit. Flush the previous one 472 this.finishFrame(); // We are starting a new access unit. Flush the previous one
453 } 473 }
...@@ -461,7 +481,7 @@ ...@@ -461,7 +481,7 @@
461 481
462 h264Frame.startNalUnit(); 482 h264Frame.startNalUnit();
463 state = 2; // We know there will not be an overlapping start code, so we can skip that test 483 state = 2; // We know there will not be an overlapping start code, so we can skip that test
464 return this.writeBytes(data, o, l); 484 return this.writeBytes(data, offset, length);
465 /*--------------------------------------------------------------------------------------------------------------------*/ 485 /*--------------------------------------------------------------------------------------------------------------------*/
466 } // switch 486 } // switch
467 }; 487 };
......
1 (function(window) {
2 /*
3 ======== A Handy Little QUnit Reference ========
4 http://api.qunitjs.com/
5
6 Test methods:
7 module(name, {[setup][ ,teardown]})
8 test(name, callback)
9 expect(numberOfAssertions)
10 stop(increment)
11 start(decrement)
12 Test assertions:
13 ok(value, [message])
14 equal(actual, expected, [message])
15 notEqual(actual, expected, [message])
16 deepEqual(actual, expected, [message])
17 notDeepEqual(actual, expected, [message])
18 strictEqual(actual, expected, [message])
19 notStrictEqual(actual, expected, [message])
20 throws(block, [expected], [message])
21 */
22 var
23 buffer,
24 expGolomb,
25 view;
26
27 module('Exponential Golomb coding');
28
29 test('small numbers are coded correctly', function() {
30 var
31 expected = [
32 [0xF8, 0],
33 [0x5F, 1],
34 [0x7F, 2],
35 [0x27, 3],
36 [0x2F, 4],
37 [0x37, 5],
38 [0x3F, 6],
39 [0x11, 7],
40 [0x13, 8],
41 [0x15, 9]
42 ],
43 i = expected.length,
44 result;
45
46 while (i--) {
47 buffer = new Uint8Array([expected[i][0]]);
48 expGolomb = new window.videojs.hls.ExpGolomb(buffer);
49 result = expGolomb.readUnsignedExpGolomb();
50 equal(expected[i][1], result, expected[i][0] + ' is decoded to ' + expected[i][1]);
51 }
52 });
53
54 test('drops working data as it is parsed', function() {
55 var expGolomb = new window.videojs.hls.ExpGolomb(new Uint8Array([0x00, 0xFF]));
56 expGolomb.skipBits(8);
57 equal(8, expGolomb.bitsAvailable(), '8 bits remain');
58 equal(0xFF, expGolomb.readBits(8), 'the second byte is read');
59 });
60
61 test('drops working data when skipping leading zeros', function() {
62 var expGolomb = new window.videojs.hls.ExpGolomb(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0xFF]));
63 equal(32, expGolomb.skipLeadingZeros(), '32 leading zeros are dropped');
64 equal(8, expGolomb.bitsAvailable(), '8 bits remain');
65 equal(0xFF, expGolomb.readBits(8), 'the second byte is read');
66 });
67
68 test('drops working data when skipping leading zeros', function() {
69 var expGolomb = new window.videojs.hls.ExpGolomb(new Uint8Array([0x15, 0xab, 0x40, 0xc8, 0xFF]));
70 equal(3, expGolomb.skipLeadingZeros(), '3 leading zeros are dropped');
71 equal((8 * 4) + 5, expGolomb.bitsAvailable(), '37 bits remain');
72 expGolomb.skipBits(1);
73 equal(0x5a, expGolomb.readBits(8), 'the next bits are read');
74 });
75
76 })(this);
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
69 69
70 70
71 <script src="video-js-hls_test.js"></script> 71 <script src="video-js-hls_test.js"></script>
72 <script src="exp-golomb_test.js"></script>
72 </head> 73 </head>
73 <body> 74 <body>
74 <div id="qunit"></div> 75 <div id="qunit"></div>
......
...@@ -67,16 +67,6 @@ ...@@ -67,16 +67,6 @@
67 67
68 console.log('h264 tags:', parser.stats.h264Tags(), 68 console.log('h264 tags:', parser.stats.h264Tags(),
69 'aac tags:', parser.stats.aacTags()); 69 'aac tags:', parser.stats.aacTags());
70
71 console.log(videojs.hls.utils.hexDump(parser.getFlvHeader()));
72 for (i = 0; i < 4; ++i) {
73 parser.getNextTag();
74 }
75 console.log(videojs.hls.utils.tagDump(parser.getNextTag()));
76 console.log('bad tag:');
77 for (i = 0; i < 3; ++i) {
78 console.log(videojs.hls.utils.tagDump(parser.getNextTag()));
79 }
80 }); 70 });
81 71
82 testAudioTag = function(tag) { 72 testAudioTag = function(tag) {
...@@ -102,7 +92,10 @@ ...@@ -102,7 +92,10 @@
102 frameType = (byte & 0xF0) >>> 4, 92 frameType = (byte & 0xF0) >>> 4,
103 codecId = byte & 0x0F, 93 codecId = byte & 0x0F,
104 packetType = tag.bytes[12], 94 packetType = tag.bytes[12],
105 compositionTime = (tag.view.getInt32(13) & 0xFFFFFF00) >> 8; 95 compositionTime = (tag.view.getInt32(13) & 0xFFFFFF00) >> 8,
96 nalHeader;
97
98 // payload starts at tag.bytes[16]
106 99
107 100
108 // XXX: I'm not sure that frame types 3-5 are invalid 101 // XXX: I'm not sure that frame types 3-5 are invalid
...@@ -110,7 +103,7 @@ ...@@ -110,7 +103,7 @@
110 'the frame type should be valid'); 103 'the frame type should be valid');
111 104
112 equal(7, codecId, 'the codec ID is AVC for h264'); 105 equal(7, codecId, 'the codec ID is AVC for h264');
113 ok(packetType <=2 && packetType >= 0, 'the packet type is within [0, 2]'); 106 ok(packetType <= 2 && packetType >= 0, 'the packet type is within [0, 2]');
114 if (packetType !== 1) { 107 if (packetType !== 1) {
115 equal(0, 108 equal(0,
116 compositionTime, 109 compositionTime,
...@@ -118,8 +111,26 @@ ...@@ -118,8 +111,26 @@
118 } 111 }
119 112
120 // TODO: the rest of the bytes are an NLU unit 113 // TODO: the rest of the bytes are an NLU unit
114 if (packetType === 0) {
115 // AVC decoder configuration record
116 } else {
117 // NAL units
118 testNalUnit(tag.bytes.subarray(16));
119 }
121 }; 120 };
122 121
122 testNalUnit = function(bytes) {
123 var
124 nalHeader = bytes[0],
125 unitType = nalHeader & 0x1F;
126
127 equal(0, (nalHeader & 0x80) >>> 7, 'the first bit is always 0');
128 // equal(90, (nalHeader & 0x60) >>> 5, 'the NAL reference indicator is something');
129 // ok(unitType > 0, 'NAL unit type ' + unitType + ' is greater than 0');
130 // ok(unitType < 22 , 'NAL unit type ' + unitType + ' is less than 22');
131 };
132
133
123 asciiFromBytes = function(bytes) { 134 asciiFromBytes = function(bytes) {
124 var 135 var
125 string = [], 136 string = [],
...@@ -169,6 +180,7 @@ ...@@ -169,6 +180,7 @@
169 offset++; 180 offset++;
170 } else { 181 } else {
171 // number 182 // number
183 ok(!isNaN(tag.view.getFloat64(offset)), 'the value is not NaN');
172 offset += 8; 184 offset += 8;
173 } 185 }
174 } 186 }
......