e19a796f by David LaPalomento

Write track fragment metadata

Output real tfhd info. Prepare trun to accept sample information-- this still needs to be generated properly by the transmuxer.
1 parent 85ee7214
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 'use strict'; 2 'use strict';
3 3
4 var box, dinf, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd, trak, 4 var box, dinf, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd, trak,
5 tkhd, mdia, mdhd, hdlr, stbl, stsd, styp, trex, types, 5 tkhd, mdia, mdhd, hdlr, stbl, stsd, styp, traf, trex, trun, types,
6 MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR, AUDIO_HDLR, 6 MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR, AUDIO_HDLR,
7 HDLR_TYPES, VMHD, DREF, STCO, STSC, STSZ, STTS, Uint8Array, 7 HDLR_TYPES, VMHD, DREF, STCO, STSC, STSZ, STTS, Uint8Array,
8 DataView; 8 DataView;
...@@ -40,6 +40,7 @@ DataView = window.DataView; ...@@ -40,6 +40,7 @@ DataView = window.DataView;
40 tfhd: [], 40 tfhd: [],
41 traf: [], 41 traf: [],
42 trak: [], 42 trak: [],
43 trun: [],
43 trex: [], 44 trex: [],
44 tkhd: [], 45 tkhd: [],
45 vmhd: [] 46 vmhd: []
...@@ -202,23 +203,16 @@ minf = function(track) { ...@@ -202,23 +203,16 @@ minf = function(track) {
202 }; 203 };
203 moof = function(sequenceNumber, tracks) { 204 moof = function(sequenceNumber, tracks) {
204 var 205 var
205 trafCall = [], 206 trackFragments = [],
206 i = tracks.length; 207 i = tracks.length;
207 // build tfhd boxes for each track fragment 208 // build traf boxes for each track fragment
208 while (i--) { 209 while (i--) {
209 trafCall[i] = box(types.tfhd, new Uint8Array([ 210 trackFragments[i] = traf(tracks[i]);
210 0x00, // version 0
211 0x00, 0x00, 0x00, // flags
212 (tracks[i].trackId & 0xFF000000) >> 24,
213 (tracks[i].trackId & 0xFF0000) >> 16,
214 (tracks[i].trackId & 0xFF00) >> 8,
215 (tracks[i].trackId & 0xFF),
216 ]));
217 } 211 }
218 trafCall.unshift(types.traf); 212 return box.apply(null, [
219 return box(types.moof, 213 types.moof,
220 mfhd(sequenceNumber), 214 mfhd(sequenceNumber)
221 box.apply(null, trafCall)); 215 ].concat(trackFragments));
222 }; 216 };
223 /** 217 /**
224 * @param tracks... (optional) {array} the tracks associated with this movie 218 * @param tracks... (optional) {array} the tracks associated with this movie
...@@ -404,6 +398,19 @@ tkhd = function(track) { ...@@ -404,6 +398,19 @@ tkhd = function(track) {
404 ])); 398 ]));
405 }; 399 };
406 400
401 traf = function(track) {
402 return box(types.traf,
403 box(types.tfhd, new Uint8Array([
404 0x00, // version 0
405 0x00, 0x00, 0x00, // flags
406 (track.id & 0xFF000000) >> 24,
407 (track.id & 0xFF0000) >> 16,
408 (track.id & 0xFF00) >> 8,
409 (track.id & 0xFF),
410 ])),
411 trun(track));
412 };
413
407 /** 414 /**
408 * Generate a track box. 415 * Generate a track box.
409 * @param track {object} a track definition 416 * @param track {object} a track definition
...@@ -431,6 +438,44 @@ trex = function(track) { ...@@ -431,6 +438,44 @@ trex = function(track) {
431 ])); 438 ]));
432 }; 439 };
433 440
441 trun = function(track) {
442 var bytes, samples, sample, i;
443
444 samples = track.samples || [];
445
446 bytes = [
447 0x00, // version 0
448 0x00, 0x0f, 0x00, // flags
449 (samples.length & 0xFF000000) >>> 24,
450 (samples.length & 0xFF0000) >>> 16,
451 (samples.length & 0xFF00) >>> 8,
452 samples.length & 0xFF // sample_count
453 ];
454
455 for (i = 0; i < samples.length; i++) {
456 sample = samples[i];
457 bytes = bytes.concat([
458 (sample.duration & 0xFF000000) >>> 24,
459 (sample.duration & 0xFF0000) >>> 16,
460 (sample.duration & 0xFF00) >>> 8,
461 sample.duration & 0xFF, // sample_duration
462 (sample.size & 0xFF000000) >>> 24,
463 (sample.size & 0xFF0000) >>> 16,
464 (sample.size & 0xFF00) >>> 8,
465 sample.size & 0xFF, // sample_size
466 (sample.flags & 0xFF000000) >>> 24,
467 (sample.flags & 0xFF0000) >>> 16,
468 (sample.flags & 0xFF00) >>> 8,
469 sample.flags & 0xFF, // sample_flags
470 (sample.compositionTimeOffset & 0xFF000000) >>> 24,
471 (sample.compositionTimeOffset & 0xFF0000) >>> 16,
472 (sample.compositionTimeOffset & 0xFF00) >>> 8,
473 sample.compositionTimeOffset & 0xFF, // sample_composition_time_offset
474 ]);
475 }
476 return box(types.trun, new Uint8Array(bytes));
477 };
478
434 window.videojs.mp4 = { 479 window.videojs.mp4 = {
435 ftyp: ftyp, 480 ftyp: ftyp,
436 mdat: mdat, 481 mdat: mdat,
......
...@@ -737,7 +737,7 @@ Transmuxer = function() { ...@@ -737,7 +737,7 @@ Transmuxer = function() {
737 flushVideo = function() { 737 flushVideo = function() {
738 var moof, mdat, boxes, i, data; 738 var moof, mdat, boxes, i, data;
739 739
740 moof = mp4.moof(sequenceNumber, []); 740 moof = mp4.moof(sequenceNumber, tracks);
741 741
742 // concatenate the video data and construct the mdat 742 // concatenate the video data and construct the mdat
743 data = new Uint8Array(videoSamplesSize); 743 data = new Uint8Array(videoSamplesSize);
......
...@@ -301,11 +301,21 @@ test('generates an initialization segment', function() { ...@@ -301,11 +301,21 @@ test('generates an initialization segment', function() {
301 test('generates a minimal moof', function() { 301 test('generates a minimal moof', function() {
302 var 302 var
303 data = mp4.moof(7, [{ 303 data = mp4.moof(7, [{
304 trackId: 1 304 id: 17,
305 samples: [{
306 duration: 9000,
307 size: 10,
308 flags: 14,
309 compositionTimeOffset: 500
305 }, { 310 }, {
306 trackId: 2 311 duration: 10000,
312 size: 11,
313 flags: 9,
314 compositionTimeOffset: 1000
315 }]
307 }]), 316 }]),
308 moof = videojs.inspectMp4(data); 317 moof = videojs.inspectMp4(data),
318 trun;
309 319
310 equal(moof.length, 1, 'generated one box'); 320 equal(moof.length, 1, 'generated one box');
311 equal(moof[0].type, 'moof', 'generated a moof box'); 321 equal(moof[0].type, 'moof', 'generated a moof box');
...@@ -313,10 +323,33 @@ test('generates a minimal moof', function() { ...@@ -313,10 +323,33 @@ test('generates a minimal moof', function() {
313 equal(moof[0].boxes[0].type, 'mfhd', 'generated an mfhd box'); 323 equal(moof[0].boxes[0].type, 'mfhd', 'generated an mfhd box');
314 equal(moof[0].boxes[0].sequenceNumber, 7, 'included the sequence_number'); 324 equal(moof[0].boxes[0].sequenceNumber, 7, 'included the sequence_number');
315 equal(moof[0].boxes[1].type, 'traf', 'generated a traf box'); 325 equal(moof[0].boxes[1].type, 'traf', 'generated a traf box');
316 equal(moof[0].boxes[1].boxes.length, 2, 'generated two fragment headers'); 326 equal(moof[0].boxes[1].boxes.length, 2, 'generated track fragment info');
327 equal(moof[0].boxes[1].boxes[0].type, 'tfhd', 'generated a tfhd box');
328 equal(moof[0].boxes[1].boxes[0].trackId, 17, 'wrote the first track id');
317 equal(moof[0].boxes[1].boxes[0].type, 'tfhd', 'generated a tfhd box'); 329 equal(moof[0].boxes[1].boxes[0].type, 'tfhd', 'generated a tfhd box');
318 equal(moof[0].boxes[1].boxes[0].trackId, 1, 'wrote the first track id'); 330 trun = moof[0].boxes[1].boxes[1];
319 equal(moof[0].boxes[1].boxes[1].trackId, 2, 'wrote the second track id'); 331 equal(trun.type, 'trun', 'generated a trun box');
332 equal(trun.samples.length, 2, 'wrote two samples');
333
334 equal(trun.samples[0].duration, 9000, 'wrote a sample duration');
335 equal(trun.samples[0].size, 10, 'wrote a sample size');
336 equal(trun.samples[0].flags, 14, 'wrote the sample flags');
337 equal(trun.samples[0].compositionTimeOffset, 500, 'wrote the composition time offset');
338
339 equal(trun.samples[1].duration, 10000, 'wrote a sample duration');
340 equal(trun.samples[1].size, 11, 'wrote a sample size');
341 equal(trun.samples[1].flags, 9, 'wrote the sample flags');
342 equal(trun.samples[1].compositionTimeOffset, 1000, 'wrote the composition time offset');
343 });
344
345 test('can generate a traf without samples', function() {
346 var
347 data = mp4.moof(8, [{
348 trackId: 13
349 }]),
350 moof = videojs.inspectMp4(data);
351
352 equal(moof[0].boxes[1].boxes[1].samples.length, 0, 'generated no samples');
320 }); 353 });
321 354
322 test('generates an mdat', function() { 355 test('generates an mdat', function() {
......
...@@ -718,6 +718,32 @@ test('can parse a trun', function() { ...@@ -718,6 +718,32 @@ test('can parse a trun', function() {
718 }]); 718 }]);
719 }); 719 });
720 720
721 test('can parse a trun with per-sample flags', function() {
722 var data = box('trun',
723 0x00, // version
724 0x00, 0x0f, 0x00, // flags
725 0x00, 0x00, 0x00, 0x01, // sample_count
726
727 0x00, 0x00, 0x00, 0x09, // sample_duration
728 0x00, 0x00, 0x00, 0xff, // sample_size
729 0x01, 0x02, 0x03, 0x04, // sample_flags
730 0x00, 0x00, 0x00, 0x00); // sample_composition_time_offset
731 deepEqual(videojs.inspectMp4(new Uint8Array(data)),
732 [{
733 type: 'trun',
734 version: 0,
735 size: 32,
736 flags: new Uint8Array([0, 0x0f, 0x00]),
737 samples: [{
738 duration: 9,
739 size: 0xff,
740 flags: 0x01020304,
741 compositionTimeOffset: 0
742 }]
743 }]);
744
745 });
746
721 test('can parse a sidx', function(){ 747 test('can parse a sidx', function(){
722 var data = box('sidx', 748 var data = box('sidx',
723 0x00, // version 749 0x00, // version
......
...@@ -537,10 +537,6 @@ var ...@@ -537,10 +537,6 @@ var
537 }; 537 };
538 while (sampleCount--) { 538 while (sampleCount--) {
539 sample = {}; 539 sample = {};
540 if (sampleFlagsPresent) {
541 sample.flags = view.getUint32(offset);
542 offset += 4;
543 }
544 if (sampleDurationPresent) { 540 if (sampleDurationPresent) {
545 sample.duration = view.getUint32(offset); 541 sample.duration = view.getUint32(offset);
546 offset += 4; 542 offset += 4;
...@@ -549,6 +545,10 @@ var ...@@ -549,6 +545,10 @@ var
549 sample.size = view.getUint32(offset); 545 sample.size = view.getUint32(offset);
550 offset += 4; 546 offset += 4;
551 } 547 }
548 if (sampleFlagsPresent) {
549 sample.flags = view.getUint32(offset);
550 offset += 4;
551 }
552 if (sampleCompositionTimeOffsetPresent) { 552 if (sampleCompositionTimeOffsetPresent) {
553 sample.compositionTimeOffset = view.getUint32(offset); 553 sample.compositionTimeOffset = view.getUint32(offset);
554 offset += 4; 554 offset += 4;
......