5351582b by David LaPalomento

Fix loadWord in ExpGolomb and code cleanup

loadWord wasn't incrementing the position in the byte stream so fix it so that the accounting is taken care of. Replace a bunch of single-letter variable names with more descriptive terms. Add some comments. Add tests for exponential golomb parsing.
1 parent b2ed4313
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);
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
51 <script src="../src/bin-utils.js"></script> 51 <script src="../src/bin-utils.js"></script>
52 52
53 <script src="video-js-hls_test.js"></script> 53 <script src="video-js-hls_test.js"></script>
54 <script src="exp-golomb_test.js"></script>
54 </head> 55 </head>
55 <body> 56 <body>
56 <div id="qunit"></div> 57 <div id="qunit"></div>
......
...@@ -65,16 +65,6 @@ ...@@ -65,16 +65,6 @@
65 65
66 console.log('h264 tags:', parser.stats.h264Tags(), 66 console.log('h264 tags:', parser.stats.h264Tags(),
67 'aac tags:', parser.stats.aacTags()); 67 'aac tags:', parser.stats.aacTags());
68
69 console.log(videojs.hls.utils.hexDump(parser.getFlvHeader()));
70 for (i = 0; i < 4; ++i) {
71 parser.getNextTag();
72 }
73 console.log(videojs.hls.utils.tagDump(parser.getNextTag()));
74 console.log('bad tag:');
75 for (i = 0; i < 3; ++i) {
76 console.log(videojs.hls.utils.tagDump(parser.getNextTag()));
77 }
78 }); 68 });
79 69
80 testAudioTag = function(tag) { 70 testAudioTag = function(tag) {
...@@ -100,7 +90,10 @@ ...@@ -100,7 +90,10 @@
100 frameType = (byte & 0xF0) >>> 4, 90 frameType = (byte & 0xF0) >>> 4,
101 codecId = byte & 0x0F, 91 codecId = byte & 0x0F,
102 packetType = tag.bytes[12], 92 packetType = tag.bytes[12],
103 compositionTime = (tag.view.getInt32(13) & 0xFFFFFF00) >> 8; 93 compositionTime = (tag.view.getInt32(13) & 0xFFFFFF00) >> 8,
94 nalHeader;
95
96 // payload starts at tag.bytes[16]
104 97
105 console.log(frameType); 98 console.log(frameType);
106 99
...@@ -109,7 +102,7 @@ ...@@ -109,7 +102,7 @@
109 'the frame type should be valid'); 102 'the frame type should be valid');
110 103
111 equal(7, codecId, 'the codec ID is AVC for h264'); 104 equal(7, codecId, 'the codec ID is AVC for h264');
112 ok(packetType <=2 && packetType >= 0, 'the packet type is within [0, 2]'); 105 ok(packetType <= 2 && packetType >= 0, 'the packet type is within [0, 2]');
113 if (packetType !== 1) { 106 if (packetType !== 1) {
114 equal(0, 107 equal(0,
115 compositionTime, 108 compositionTime,
...@@ -117,7 +110,25 @@ ...@@ -117,7 +110,25 @@
117 } 110 }
118 111
119 // TODO: the rest of the bytes are an NLU unit 112 // TODO: the rest of the bytes are an NLU unit
113 if (packetType === 0) {
114 // AVC decoder configuration record
115 } else {
116 // NAL units
117 testNalUnit(tag.bytes.subarray(16));
118 }
119 };
120
121 testNalUnit = function(bytes) {
122 var
123 nalHeader = bytes[0],
124 unitType = nalHeader & 0x1F;
125
126 equal(0, (nalHeader & 0x80) >>> 7, 'the first bit is always 0');
127 // equal(90, (nalHeader & 0x60) >>> 5, 'the NAL reference indicator is something');
128 // ok(unitType > 0, 'NAL unit type ' + unitType + ' is greater than 0');
129 // ok(unitType < 22 , 'NAL unit type ' + unitType + ' is less than 22');
120 }; 130 };
131
121 132
122 asciiFromBytes = function(bytes) { 133 asciiFromBytes = function(bytes) {
123 var 134 var
...@@ -168,6 +179,7 @@ ...@@ -168,6 +179,7 @@
168 offset++; 179 offset++;
169 } else { 180 } else {
170 // number 181 // number
182 ok(!isNaN(tag.view.getFloat64(offset)), 'the value is not NaN');
171 offset += 8; 183 offset += 8;
172 } 184 }
173 } 185 }
......