Get init segments working with real metadata
Parse and pass along additional track info from the sequence parameter set.
Showing
4 changed files
with
166 additions
and
73 deletions
1 | (function(window, videojs, undefined) { | 1 | (function(window, videojs, undefined) { |
2 | 'use strict'; | 2 | 'use strict'; |
3 | 3 | ||
4 | var box, dinf, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd, trak, tkhd, mdia, mdhd, hdlr, stbl, | 4 | var box, dinf, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd, trak, |
5 | stsd, styp, types, MAJOR_BRAND, MINOR_VERSION, VIDEO_HDLR, AUDIO_HDLR, HDLR_TYPES, VMHD, DREF, STCO, STSC, STSZ, STTS, TREX, | 5 | tkhd, mdia, mdhd, hdlr, stbl, stsd, styp, trex, types, |
6 | Uint8Array, DataView; | 6 | MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR, AUDIO_HDLR, |
7 | HDLR_TYPES, VMHD, DREF, STCO, STSC, STSZ, STTS, Uint8Array, | ||
8 | DataView; | ||
7 | 9 | ||
8 | Uint8Array = window.Uint8Array; | 10 | Uint8Array = window.Uint8Array; |
9 | DataView = window.DataView; | 11 | DataView = window.DataView; |
... | @@ -60,6 +62,12 @@ DataView = window.DataView; | ... | @@ -60,6 +62,12 @@ DataView = window.DataView; |
60 | 'o'.charCodeAt(0), | 62 | 'o'.charCodeAt(0), |
61 | 'm'.charCodeAt(0) | 63 | 'm'.charCodeAt(0) |
62 | ]); | 64 | ]); |
65 | AVC1_BRAND = new Uint8Array([ | ||
66 | 'a'.charCodeAt(0), | ||
67 | 'v'.charCodeAt(0), | ||
68 | 'c'.charCodeAt(0), | ||
69 | '1'.charCodeAt(0) | ||
70 | ]); | ||
63 | MINOR_VERSION = new Uint8Array([0, 0, 0, 1]); | 71 | MINOR_VERSION = new Uint8Array([0, 0, 0, 1]); |
64 | VIDEO_HDLR = new Uint8Array([ | 72 | VIDEO_HDLR = new Uint8Array([ |
65 | 0x00, // version 0 | 73 | 0x00, // version 0 |
... | @@ -98,15 +106,6 @@ DataView = window.DataView; | ... | @@ -98,15 +106,6 @@ DataView = window.DataView; |
98 | 0x00, // version 0 | 106 | 0x00, // version 0 |
99 | 0x00, 0x00, 0x01 // entry_flags | 107 | 0x00, 0x00, 0x01 // entry_flags |
100 | ]); | 108 | ]); |
101 | TREX = new Uint8Array([ | ||
102 | 0x00, // version 0 | ||
103 | 0x00, 0x00, 0x00, // flags | ||
104 | 0x00, 0x00, 0x00, 0x01, // track_ID | ||
105 | 0x00, 0x00, 0x00, 0x01, // default_sample_description_index | ||
106 | 0x00, 0x00, 0x00, 0x00, // default_sample_duration | ||
107 | 0x00, 0x00, 0x00, 0x00, // default_sample_size | ||
108 | 0x00, 0x01, 0x00, 0x01 // default_sample_flags | ||
109 | ]); | ||
110 | STCO = new Uint8Array([ | 109 | STCO = new Uint8Array([ |
111 | 0x00, // version | 110 | 0x00, // version |
112 | 0x00, 0x00, 0x00, // flags | 111 | 0x00, 0x00, 0x00, // flags |
... | @@ -160,7 +159,7 @@ dinf = function() { | ... | @@ -160,7 +159,7 @@ dinf = function() { |
160 | }; | 159 | }; |
161 | 160 | ||
162 | ftyp = function() { | 161 | ftyp = function() { |
163 | return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND); | 162 | return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND); |
164 | }; | 163 | }; |
165 | 164 | ||
166 | hdlr = function(type) { | 165 | hdlr = function(type) { |
... | @@ -185,8 +184,8 @@ mdhd = function(duration) { | ... | @@ -185,8 +184,8 @@ mdhd = function(duration) { |
185 | 0x00, 0x00 | 184 | 0x00, 0x00 |
186 | ])); | 185 | ])); |
187 | }; | 186 | }; |
188 | mdia = function(duration, width, height, type) { | 187 | mdia = function(track) { |
189 | return box(types.mdia, mdhd(duration), hdlr(type), minf(width, height)); | 188 | return box(types.mdia, mdhd(track.duration), hdlr(track.type), minf(track)); |
190 | }; | 189 | }; |
191 | mfhd = function(sequenceNumber) { | 190 | mfhd = function(sequenceNumber) { |
192 | return box(types.mfhd, new Uint8Array([ | 191 | return box(types.mfhd, new Uint8Array([ |
... | @@ -198,8 +197,8 @@ mfhd = function(sequenceNumber) { | ... | @@ -198,8 +197,8 @@ mfhd = function(sequenceNumber) { |
198 | sequenceNumber & 0xFF, // sequence_number | 197 | sequenceNumber & 0xFF, // sequence_number |
199 | ])); | 198 | ])); |
200 | }; | 199 | }; |
201 | minf = function(width, height) { | 200 | minf = function(track) { |
202 | return box(types.minf, box(types.vmhd, VMHD), dinf(), stbl(width, height)); | 201 | return box(types.minf, box(types.vmhd, VMHD), dinf(), stbl(track)); |
203 | }; | 202 | }; |
204 | moof = function(sequenceNumber, tracks) { | 203 | moof = function(sequenceNumber, tracks) { |
205 | var | 204 | var |
... | @@ -233,10 +232,17 @@ moov = function(tracks) { | ... | @@ -233,10 +232,17 @@ moov = function(tracks) { |
233 | boxes[i] = trak(tracks[i]); | 232 | boxes[i] = trak(tracks[i]); |
234 | } | 233 | } |
235 | 234 | ||
236 | return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex())); | 235 | return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks))); |
237 | }; | 236 | }; |
238 | mvex = function() { | 237 | mvex = function(tracks) { |
239 | return box(types.mvex, box(types.trex, TREX)); | 238 | var |
239 | i = tracks.length, | ||
240 | boxes = []; | ||
241 | |||
242 | while (i--) { | ||
243 | boxes[i] = trex(tracks[i]); | ||
244 | } | ||
245 | return box.apply(null, [types.mvex].concat(boxes)); | ||
240 | }; | 246 | }; |
241 | mvhd = function(duration) { | 247 | mvhd = function(duration) { |
242 | var | 248 | var |
... | @@ -270,21 +276,41 @@ mvhd = function(duration) { | ... | @@ -270,21 +276,41 @@ mvhd = function(duration) { |
270 | 0x00, 0x00, 0x00, 0x00, | 276 | 0x00, 0x00, 0x00, 0x00, |
271 | 0x00, 0x00, 0x00, 0x00, | 277 | 0x00, 0x00, 0x00, 0x00, |
272 | 0x00, 0x00, 0x00, 0x00, // pre_defined | 278 | 0x00, 0x00, 0x00, 0x00, // pre_defined |
273 | 0x00, 0x00, 0x00, 0x01 // next_track_ID | 279 | 0xff, 0xff, 0xff, 0xff // next_track_ID |
274 | ]); | 280 | ]); |
275 | return box(types.mvhd, bytes); | 281 | return box(types.mvhd, bytes); |
276 | }; | 282 | }; |
277 | 283 | ||
278 | stbl = function(width, height) { | 284 | stbl = function(track) { |
279 | return box(types.stbl, | 285 | return box(types.stbl, |
280 | stsd(width, height), | 286 | stsd(track), |
281 | box(types.stts, STTS), | 287 | box(types.stts, STTS), |
282 | box(types.stsc, STSC), | 288 | box(types.stsc, STSC), |
283 | box(types.stsz, STSZ), | 289 | box(types.stsz, STSZ), |
284 | box(types.stco, STCO)); | 290 | box(types.stco, STCO)); |
285 | }; | 291 | }; |
286 | 292 | ||
287 | stsd = function(width, height) { | 293 | stsd = function(track) { |
294 | var sequenceParameterSets = [], pictureParameterSets = [], i; | ||
295 | |||
296 | if (track.type === 'audio') { | ||
297 | return box(types.stsd); | ||
298 | } | ||
299 | |||
300 | // assemble the SPSs | ||
301 | for (i = 0; i < track.sps.length; i++) { | ||
302 | sequenceParameterSets.push((track.sps[i].byteLength & 0xFF00) >>> 8); | ||
303 | sequenceParameterSets.push((track.sps[i].byteLength & 0xFF)); // sequenceParameterSetLength | ||
304 | sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(track.sps[i])); // SPS | ||
305 | } | ||
306 | |||
307 | // assemble the PPSs | ||
308 | for (i = 0; i < track.pps.length; i++) { | ||
309 | pictureParameterSets.push((track.pps[i].byteLength & 0xFF00) >>> 8); | ||
310 | pictureParameterSets.push((track.pps[i].byteLength & 0xFF)); | ||
311 | pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(track.pps[i])); | ||
312 | } | ||
313 | |||
288 | return box(types.stsd, new Uint8Array([ | 314 | return box(types.stsd, new Uint8Array([ |
289 | 0x00, // version 0 | 315 | 0x00, // version 0 |
290 | 0x00, 0x00, 0x00, // flags | 316 | 0x00, 0x00, 0x00, // flags |
... | @@ -298,10 +324,10 @@ stsd = function(width, height) { | ... | @@ -298,10 +324,10 @@ stsd = function(width, height) { |
298 | 0x00, 0x00, 0x00, 0x00, | 324 | 0x00, 0x00, 0x00, 0x00, |
299 | 0x00, 0x00, 0x00, 0x00, | 325 | 0x00, 0x00, 0x00, 0x00, |
300 | 0x00, 0x00, 0x00, 0x00, // pre_defined | 326 | 0x00, 0x00, 0x00, 0x00, // pre_defined |
301 | (width & 0xff00) >> 8, | 327 | (track.width & 0xff00) >> 8, |
302 | width & 0xff, // width | 328 | track.width & 0xff, // width |
303 | (height & 0xff00) >> 8, | 329 | (track.height & 0xff00) >> 8, |
304 | height & 0xff, // height | 330 | track.height & 0xff, // height |
305 | 0x00, 0x48, 0x00, 0x00, // horizresolution | 331 | 0x00, 0x48, 0x00, 0x00, // horizresolution |
306 | 0x00, 0x48, 0x00, 0x00, // vertresolution | 332 | 0x00, 0x48, 0x00, 0x00, // vertresolution |
307 | 0x00, 0x00, 0x00, 0x00, // reserved | 333 | 0x00, 0x00, 0x00, 0x00, // reserved |
... | @@ -319,18 +345,15 @@ stsd = function(width, height) { | ... | @@ -319,18 +345,15 @@ stsd = function(width, height) { |
319 | 0x11, 0x11]), // pre_defined = -1 | 345 | 0x11, 0x11]), // pre_defined = -1 |
320 | box(types.avcC, new Uint8Array([ | 346 | box(types.avcC, new Uint8Array([ |
321 | 0x01, // configurationVersion | 347 | 0x01, // configurationVersion |
322 | 0x4d, // AVCProfileIndication?? | 348 | track.profileIdc, // AVCProfileIndication |
323 | 0x40, // profile_compatibility | 349 | track.profileCompatibility, // profile_compatibility |
324 | 0x20, // AVCLevelIndication | 350 | track.levelIdc, // AVCLevelIndication |
325 | 0xff, // lengthSizeMinusOne | 351 | 0xff // lengthSizeMinusOne |
326 | 0xe1, // numOfSequenceParameterSets | 352 | ].concat([ |
327 | 0x00, 0x0c, // sequenceParameterSetLength | 353 | track.sps.length // numOfSequenceParameterSets |
328 | 0x67, 0x4d, 0x40, 0x20, | 354 | ]).concat(sequenceParameterSets).concat([ |
329 | 0x96, 0x52, 0x80, 0xa0, | 355 | track.pps.length // numOfPictureParameterSets |
330 | 0x0b, 0x76, 0x02, 0x05, // SPS | 356 | ]).concat(pictureParameterSets))), // "PPS" |
331 | 0x01, // numOfPictureParameterSets | ||
332 | 0x00, 0x04, // pictureParameterSetLength | ||
333 | 0x68, 0xef, 0x38, 0x80])), // "PPS" | ||
334 | box(types.btrt, new Uint8Array([ | 357 | box(types.btrt, new Uint8Array([ |
335 | 0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB | 358 | 0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB |
336 | 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate | 359 | 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate |
... | @@ -390,7 +413,22 @@ trak = function(track) { | ... | @@ -390,7 +413,22 @@ trak = function(track) { |
390 | track.duration = track.duration || 0xffffffff; | 413 | track.duration = track.duration || 0xffffffff; |
391 | return box(types.trak, | 414 | return box(types.trak, |
392 | tkhd(track), | 415 | tkhd(track), |
393 | mdia(track.duration, track.width, track.height, track.type)); | 416 | mdia(track)); |
417 | }; | ||
418 | |||
419 | trex = function(track) { | ||
420 | return box(types.trex, new Uint8Array([ | ||
421 | 0x00, // version 0 | ||
422 | 0x00, 0x00, 0x00, // flags | ||
423 | (track.id & 0xFF000000) >> 24, | ||
424 | (track.id & 0xFF0000) >> 16, | ||
425 | (track.id & 0xFF00) >> 8, | ||
426 | (track.id & 0xFF), // track_ID | ||
427 | 0x00, 0x00, 0x00, 0x01, // default_sample_description_index | ||
428 | 0x00, 0x00, 0x00, 0x00, // default_sample_duration | ||
429 | 0x00, 0x00, 0x00, 0x00, // default_sample_size | ||
430 | 0x00, 0x01, 0x00, 0x01 // default_sample_flags | ||
431 | ])); | ||
394 | }; | 432 | }; |
395 | 433 | ||
396 | window.videojs.mp4 = { | 434 | window.videojs.mp4 = { | ... | ... |
... | @@ -497,7 +497,10 @@ H264Stream = function() { | ... | @@ -497,7 +497,10 @@ H264Stream = function() { |
497 | 497 | ||
498 | case 0x07: | 498 | case 0x07: |
499 | event.nalUnitType = 'seq_parameter_set_rbsp'; | 499 | event.nalUnitType = 'seq_parameter_set_rbsp'; |
500 | event.dimensions = readSequenceParameterSet(data.subarray(1)); | 500 | event.config = readSequenceParameterSet(data.subarray(1)); |
501 | break; | ||
502 | case 0x08: | ||
503 | event.nalUnitType = 'pic_parameter_set_rbsp'; | ||
501 | break; | 504 | break; |
502 | 505 | ||
503 | default: | 506 | default: |
... | @@ -541,8 +544,9 @@ H264Stream = function() { | ... | @@ -541,8 +544,9 @@ H264Stream = function() { |
541 | * properties. A sequence parameter set is the H264 metadata that | 544 | * properties. A sequence parameter set is the H264 metadata that |
542 | * describes the properties of upcoming video frames. | 545 | * describes the properties of upcoming video frames. |
543 | * @param data {Uint8Array} the bytes of a sequence parameter set | 546 | * @param data {Uint8Array} the bytes of a sequence parameter set |
544 | * @return {object} an object with width and height properties | 547 | * @return {object} an object with configuration parsed from the |
545 | * specifying the dimensions of the associated video frames. | 548 | * sequence parameter set, including the dimensions of the |
549 | * associated video frames. | ||
546 | */ | 550 | */ |
547 | readSequenceParameterSet = function(data) { | 551 | readSequenceParameterSet = function(data) { |
548 | var | 552 | var |
... | @@ -550,16 +554,19 @@ H264Stream = function() { | ... | @@ -550,16 +554,19 @@ H264Stream = function() { |
550 | frameCropRightOffset = 0, | 554 | frameCropRightOffset = 0, |
551 | frameCropTopOffset = 0, | 555 | frameCropTopOffset = 0, |
552 | frameCropBottomOffset = 0, | 556 | frameCropBottomOffset = 0, |
553 | expGolombDecoder, profileIdc, chromaFormatIdc, picOrderCntType, | 557 | expGolombDecoder, profileIdc, levelIdc, profileCompatibility, |
558 | chromaFormatIdc, picOrderCntType, | ||
554 | numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1, | 559 | numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1, |
555 | picHeightInMapUnitsMinus1, frameMbsOnlyFlag, | 560 | picHeightInMapUnitsMinus1, |
561 | frameMbsOnlyFlag, | ||
556 | scalingListCount, | 562 | scalingListCount, |
557 | i; | 563 | i; |
558 | 564 | ||
559 | expGolombDecoder = new videojs.Hls.ExpGolomb(data); | 565 | expGolombDecoder = new videojs.Hls.ExpGolomb(data); |
560 | profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc | 566 | profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc |
561 | // constraint_set[0-5]_flag, u(1), reserved_zero_2bits u(2), level_idc u()8 | 567 | profileCompatibility = expGolombDecoder.readBits(5); // constraint_set[0-5]_flag |
562 | expGolombDecoder.skipBits(16); | 568 | expGolombDecoder.skipBits(3); // u(1), reserved_zero_2bits u(2) |
569 | levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8) | ||
563 | expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id | 570 | expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id |
564 | 571 | ||
565 | // some profiles have more optional data we don't need | 572 | // some profiles have more optional data we don't need |
... | @@ -628,6 +635,9 @@ H264Stream = function() { | ... | @@ -628,6 +635,9 @@ H264Stream = function() { |
628 | } | 635 | } |
629 | 636 | ||
630 | return { | 637 | return { |
638 | profileIdc: profileIdc, | ||
639 | levelIdc: levelIdc, | ||
640 | profileCompatibility: profileCompatibility, | ||
631 | width: ((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2, | 641 | width: ((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2, |
632 | height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2) | 642 | height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2) |
633 | }; | 643 | }; |
... | @@ -644,7 +654,8 @@ Transmuxer = function() { | ... | @@ -644,7 +654,8 @@ Transmuxer = function() { |
644 | videoSamples = [], | 654 | videoSamples = [], |
645 | videoSamplesSize = 0, | 655 | videoSamplesSize = 0, |
646 | tracks, | 656 | tracks, |
647 | dimensions, | 657 | config, |
658 | pps, | ||
648 | 659 | ||
649 | packetStream, parseStream, programStream, aacStream, h264Stream, | 660 | packetStream, parseStream, programStream, aacStream, h264Stream, |
650 | 661 | ||
... | @@ -673,21 +684,43 @@ Transmuxer = function() { | ... | @@ -673,21 +684,43 @@ Transmuxer = function() { |
673 | videoSamples.length) { | 684 | videoSamples.length) { |
674 | //flushVideo(); | 685 | //flushVideo(); |
675 | } | 686 | } |
676 | // generate an init segment once all the metadata is available | 687 | // record the track config |
677 | if (data.nalUnitType === 'seq_parameter_set_rbsp' && | 688 | if (data.nalUnitType === 'seq_parameter_set_rbsp' && |
678 | !dimensions) { | 689 | !config) { |
679 | dimensions = data.dimensions; | 690 | config = data.config; |
680 | 691 | ||
681 | i = tracks.length; | 692 | i = tracks.length; |
682 | while (i--) { | 693 | while (i--) { |
683 | if (tracks[i].type === 'video') { | 694 | if (tracks[i].type === 'video') { |
684 | tracks[i].width = dimensions.width; | 695 | tracks[i].width = config.width; |
685 | tracks[i].height = dimensions.height; | 696 | tracks[i].height = config.height; |
697 | tracks[i].sps = [data.data]; | ||
698 | tracks[i].profileIdc = config.profileIdc; | ||
699 | tracks[i].levelIdc = config.levelIdc; | ||
700 | tracks[i].profileCompatibility = config.profileCompatibility; | ||
686 | } | 701 | } |
687 | } | 702 | } |
688 | self.trigger('data', { | 703 | // generate an init segment once all the metadata is available |
689 | data: videojs.mp4.initSegment(tracks) | 704 | if (pps) { |
690 | }); | 705 | self.trigger('data', { |
706 | data: videojs.mp4.initSegment(tracks) | ||
707 | }); | ||
708 | } | ||
709 | } | ||
710 | if (data.nalUnitType === 'pic_parameter_set_rbsp' && | ||
711 | !pps) { | ||
712 | pps = data.data;i = tracks.length; | ||
713 | |||
714 | while (i--) { | ||
715 | if (tracks[i].type === 'video') { | ||
716 | tracks[i].pps = [data.data]; | ||
717 | } | ||
718 | } | ||
719 | if (config) { | ||
720 | self.trigger('data', { | ||
721 | data: videojs.mp4.initSegment(tracks) | ||
722 | }); | ||
723 | } | ||
691 | } | 724 | } |
692 | 725 | ||
693 | // buffer video until we encounter a new access unit (aka the next frame) | 726 | // buffer video until we encounter a new access unit (aka the next frame) | ... | ... |
... | @@ -46,7 +46,12 @@ test('generates a moov', function() { | ... | @@ -46,7 +46,12 @@ test('generates a moov', function() { |
46 | duration: 100, | 46 | duration: 100, |
47 | width: 600, | 47 | width: 600, |
48 | height: 300, | 48 | height: 300, |
49 | type: 'video' | 49 | type: 'video', |
50 | profileIdc: 3, | ||
51 | levelIdc: 5, | ||
52 | profileCompatibility: 7, | ||
53 | sps: [new Uint8Array([0, 1, 2]), new Uint8Array([3, 4, 5])], | ||
54 | pps: [new Uint8Array([6, 7, 8])] | ||
50 | }]); | 55 | }]); |
51 | 56 | ||
52 | ok(data, 'box is not null'); | 57 | ok(data, 'box is not null'); |
... | @@ -60,6 +65,7 @@ test('generates a moov', function() { | ... | @@ -60,6 +65,7 @@ test('generates a moov', function() { |
60 | mvhd = boxes[0].boxes[0]; | 65 | mvhd = boxes[0].boxes[0]; |
61 | equal(mvhd.type, 'mvhd', 'generated a mvhd'); | 66 | equal(mvhd.type, 'mvhd', 'generated a mvhd'); |
62 | equal(mvhd.duration, 0xffffffff, 'wrote the maximum movie header duration'); | 67 | equal(mvhd.duration, 0xffffffff, 'wrote the maximum movie header duration'); |
68 | equal(mvhd.nextTrackId, 0xffffffff, 'wrote the max next track id'); | ||
63 | 69 | ||
64 | equal(boxes[0].boxes[1].type, 'trak', 'generated a trak'); | 70 | equal(boxes[0].boxes[1].type, 'trak', 'generated a trak'); |
65 | equal(boxes[0].boxes[1].boxes.length, 2, 'generated two track sub boxes'); | 71 | equal(boxes[0].boxes[1].boxes.length, 2, 'generated two track sub boxes'); |
... | @@ -119,15 +125,15 @@ test('generates a moov', function() { | ... | @@ -119,15 +125,15 @@ test('generates a moov', function() { |
119 | equal(minf.boxes[2].type, 'stbl', 'generates an stbl type'); | 125 | equal(minf.boxes[2].type, 'stbl', 'generates an stbl type'); |
120 | deepEqual({ | 126 | deepEqual({ |
121 | type: 'stbl', | 127 | type: 'stbl', |
122 | size: 233, | 128 | size: 228, |
123 | boxes: [{ | 129 | boxes: [{ |
124 | type: 'stsd', | 130 | type: 'stsd', |
125 | size: 157, | 131 | size: 152, |
126 | version: 0, | 132 | version: 0, |
127 | flags: new Uint8Array([0, 0, 0]), | 133 | flags: new Uint8Array([0, 0, 0]), |
128 | sampleDescriptions: [{ | 134 | sampleDescriptions: [{ |
129 | type: 'avc1', | 135 | type: 'avc1', |
130 | size: 141, | 136 | size: 136, |
131 | dataReferenceIndex: 1, | 137 | dataReferenceIndex: 1, |
132 | width: 600, | 138 | width: 600, |
133 | height: 300, | 139 | height: 300, |
... | @@ -137,19 +143,19 @@ test('generates a moov', function() { | ... | @@ -137,19 +143,19 @@ test('generates a moov', function() { |
137 | depth: 24, | 143 | depth: 24, |
138 | config: [{ | 144 | config: [{ |
139 | type: 'avcC', | 145 | type: 'avcC', |
140 | size: 35, | 146 | size: 30, |
141 | configurationVersion: 1, | 147 | configurationVersion: 1, |
142 | avcProfileIndication: 0x4d, | 148 | avcProfileIndication: 3, |
143 | profileCompatibility: 0x40, | 149 | avcLevelIndication: 5, |
144 | avcLevelIndication: 0x20, | 150 | profileCompatibility: 7, |
145 | lengthSizeMinusOne: 3, | 151 | lengthSizeMinusOne: 3, |
146 | sps: [new Uint8Array([ | 152 | sps: [new Uint8Array([ |
147 | 0x67, 0x4d, 0x40, 0x20, | 153 | 0, 1, 2 |
148 | 0x96, 0x52, 0x80, 0xa0, | 154 | ]), new Uint8Array([ |
149 | 0x0b, 0x76, 0x02, 0x05 | 155 | 3, 4, 5 |
150 | ])], | 156 | ])], |
151 | pps: [new Uint8Array([ | 157 | pps: [new Uint8Array([ |
152 | 0x68, 0xef, 0x38, 0x80 | 158 | 6, 7, 8 |
153 | ])] | 159 | ])] |
154 | }, { | 160 | }, { |
155 | type: 'btrt', | 161 | type: 'btrt', |
... | @@ -198,7 +204,7 @@ test('generates a moov', function() { | ... | @@ -198,7 +204,7 @@ test('generates a moov', function() { |
198 | size: 32, | 204 | size: 32, |
199 | version: 0, | 205 | version: 0, |
200 | flags: new Uint8Array([0, 0, 0]), | 206 | flags: new Uint8Array([0, 0, 0]), |
201 | trackId: 1, | 207 | trackId: 7, |
202 | defaultSampleDescriptionIndex: 1, | 208 | defaultSampleDescriptionIndex: 1, |
203 | defaultSampleDuration: 0, | 209 | defaultSampleDuration: 0, |
204 | defaultSampleSize: 0, | 210 | defaultSampleSize: 0, |
... | @@ -237,7 +243,9 @@ test('generates a video hdlr', function() { | ... | @@ -237,7 +243,9 @@ test('generates a video hdlr', function() { |
237 | duration: 100, | 243 | duration: 100, |
238 | width: 600, | 244 | width: 600, |
239 | height: 300, | 245 | height: 300, |
240 | type: 'video' | 246 | type: 'video', |
247 | sps: [], | ||
248 | pps: [] | ||
241 | }]); | 249 | }]); |
242 | 250 | ||
243 | ok(data, 'box is not null'); | 251 | ok(data, 'box is not null'); |
... | @@ -256,7 +264,9 @@ test('generates an initialization segment', function() { | ... | @@ -256,7 +264,9 @@ test('generates an initialization segment', function() { |
256 | id: 1, | 264 | id: 1, |
257 | width: 600, | 265 | width: 600, |
258 | height: 300, | 266 | height: 300, |
259 | type: 'video' | 267 | type: 'video', |
268 | sps: [new Uint8Array([0])], | ||
269 | pps: [new Uint8Array([1])] | ||
260 | }, { | 270 | }, { |
261 | id: 2, | 271 | id: 2, |
262 | type: 'audio' | 272 | type: 'audio' | ... | ... |
... | @@ -750,6 +750,18 @@ test('parses nal unit types', function() { | ... | @@ -750,6 +750,18 @@ test('parses nal unit types', function() { |
750 | h264Stream.end(); | 750 | h264Stream.end(); |
751 | ok(data, 'generated a data event'); | 751 | ok(data, 'generated a data event'); |
752 | equal(data.nalUnitType, 'seq_parameter_set_rbsp', 'identified a sequence parameter set'); | 752 | equal(data.nalUnitType, 'seq_parameter_set_rbsp', 'identified a sequence parameter set'); |
753 | |||
754 | data = null; | ||
755 | h264Stream.push({ | ||
756 | type: 'video', | ||
757 | data: new Uint8Array([ | ||
758 | 0x00, 0x00, 0x00, 0x01, | ||
759 | 0x08, 0x01 | ||
760 | ]) | ||
761 | }); | ||
762 | h264Stream.end(); | ||
763 | ok(data, 'generated a data event'); | ||
764 | equal(data.nalUnitType, 'pic_parameter_set_rbsp', 'identified a picture parameter set'); | ||
753 | }); | 765 | }); |
754 | 766 | ||
755 | module('Transmuxer', { | 767 | module('Transmuxer', { |
... | @@ -775,7 +787,7 @@ test('generates an init segment', function() { | ... | @@ -775,7 +787,7 @@ test('generates an init segment', function() { |
775 | ], true))); | 787 | ], true))); |
776 | transmuxer.end(); | 788 | transmuxer.end(); |
777 | 789 | ||
778 | equal(segments.length, 2, 'has an init segment'); | 790 | equal(segments.length, 1, 'has an init segment'); |
779 | }); | 791 | }); |
780 | 792 | ||
781 | test('buffers video samples until ended', function() { | 793 | test('buffers video samples until ended', function() { | ... | ... |
-
Please register or sign in to post a comment