| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800 | /** * mux.js * * Copyright (c) Brightcove * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE * * Functions that generate fragmented MP4s suitable for use with Media * Source Extensions. */'use strict';var MAX_UINT32 = require('../utils/numbers.js').MAX_UINT32;var box, dinf, esds, ftyp, mdat, mfhd, minf, moof, moov, mvex, mvhd,    trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, traf, trex,    trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND, VIDEO_HDLR,    AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ, STTS;// pre-calculate constants(function() {  var i;  types = {    avc1: [], // codingname    avcC: [],    btrt: [],    dinf: [],    dref: [],    esds: [],    ftyp: [],    hdlr: [],    mdat: [],    mdhd: [],    mdia: [],    mfhd: [],    minf: [],    moof: [],    moov: [],    mp4a: [], // codingname    mvex: [],    mvhd: [],    pasp: [],    sdtp: [],    smhd: [],    stbl: [],    stco: [],    stsc: [],    stsd: [],    stsz: [],    stts: [],    styp: [],    tfdt: [],    tfhd: [],    traf: [],    trak: [],    trun: [],    trex: [],    tkhd: [],    vmhd: []  };  // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we  // don't throw an error  if (typeof Uint8Array === 'undefined') {    return;  }  for (i in types) {    if (types.hasOwnProperty(i)) {      types[i] = [        i.charCodeAt(0),        i.charCodeAt(1),        i.charCodeAt(2),        i.charCodeAt(3)      ];    }  }  MAJOR_BRAND = new Uint8Array([    'i'.charCodeAt(0),    's'.charCodeAt(0),    'o'.charCodeAt(0),    'm'.charCodeAt(0)  ]);  AVC1_BRAND = new Uint8Array([    'a'.charCodeAt(0),    'v'.charCodeAt(0),    'c'.charCodeAt(0),    '1'.charCodeAt(0)  ]);  MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);  VIDEO_HDLR = new Uint8Array([    0x00, // version 0    0x00, 0x00, 0x00, // flags    0x00, 0x00, 0x00, 0x00, // pre_defined    0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'    0x00, 0x00, 0x00, 0x00, // reserved    0x00, 0x00, 0x00, 0x00, // reserved    0x00, 0x00, 0x00, 0x00, // reserved    0x56, 0x69, 0x64, 0x65,    0x6f, 0x48, 0x61, 0x6e,    0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'  ]);  AUDIO_HDLR = new Uint8Array([    0x00, // version 0    0x00, 0x00, 0x00, // flags    0x00, 0x00, 0x00, 0x00, // pre_defined    0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'    0x00, 0x00, 0x00, 0x00, // reserved    0x00, 0x00, 0x00, 0x00, // reserved    0x00, 0x00, 0x00, 0x00, // reserved    0x53, 0x6f, 0x75, 0x6e,    0x64, 0x48, 0x61, 0x6e,    0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'  ]);  HDLR_TYPES = {    video: VIDEO_HDLR,    audio: AUDIO_HDLR  };  DREF = new Uint8Array([    0x00, // version 0    0x00, 0x00, 0x00, // flags    0x00, 0x00, 0x00, 0x01, // entry_count    0x00, 0x00, 0x00, 0x0c, // entry_size    0x75, 0x72, 0x6c, 0x20, // 'url' type    0x00, // version 0    0x00, 0x00, 0x01 // entry_flags  ]);  SMHD = new Uint8Array([    0x00,             // version    0x00, 0x00, 0x00, // flags    0x00, 0x00,       // balance, 0 means centered    0x00, 0x00        // reserved  ]);  STCO = new Uint8Array([    0x00, // version    0x00, 0x00, 0x00, // flags    0x00, 0x00, 0x00, 0x00 // entry_count  ]);  STSC = STCO;  STSZ = new Uint8Array([    0x00, // version    0x00, 0x00, 0x00, // flags    0x00, 0x00, 0x00, 0x00, // sample_size    0x00, 0x00, 0x00, 0x00 // sample_count  ]);  STTS = STCO;  VMHD = new Uint8Array([    0x00, // version    0x00, 0x00, 0x01, // flags    0x00, 0x00, // graphicsmode    0x00, 0x00,    0x00, 0x00,    0x00, 0x00 // opcolor  ]);}());box = function(type) {  var    payload = [],    size = 0,    i,    result,    view;  for (i = 1; i < arguments.length; i++) {    payload.push(arguments[i]);  }  i = payload.length;  // calculate the total size we need to allocate  while (i--) {    size += payload[i].byteLength;  }  result = new Uint8Array(size + 8);  view = new DataView(result.buffer, result.byteOffset, result.byteLength);  view.setUint32(0, result.byteLength);  result.set(type, 4);  // copy the payload into the result  for (i = 0, size = 8; i < payload.length; i++) {    result.set(payload[i], size);    size += payload[i].byteLength;  }  return result;};dinf = function() {  return box(types.dinf, box(types.dref, DREF));};esds = function(track) {  return box(types.esds, new Uint8Array([    0x00, // version    0x00, 0x00, 0x00, // flags    // ES_Descriptor    0x03, // tag, ES_DescrTag    0x19, // length    0x00, 0x00, // ES_ID    0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority    // DecoderConfigDescriptor    0x04, // tag, DecoderConfigDescrTag    0x11, // length    0x40, // object type    0x15,  // streamType    0x00, 0x06, 0x00, // bufferSizeDB    0x00, 0x00, 0xda, 0xc0, // maxBitrate    0x00, 0x00, 0xda, 0xc0, // avgBitrate    // DecoderSpecificInfo    0x05, // tag, DecoderSpecificInfoTag    0x02, // length    // ISO/IEC 14496-3, AudioSpecificConfig    // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35    (track.audioobjecttype << 3) | (track.samplingfrequencyindex >>> 1),    (track.samplingfrequencyindex << 7) | (track.channelcount << 3),    0x06, 0x01, 0x02 // GASpecificConfig  ]));};ftyp = function() {  return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);};hdlr = function(type) {  return box(types.hdlr, HDLR_TYPES[type]);};mdat = function(data) {  return box(types.mdat, data);};mdhd = function(track) {  var result = new Uint8Array([    0x00,                   // version 0    0x00, 0x00, 0x00,       // flags    0x00, 0x00, 0x00, 0x02, // creation_time    0x00, 0x00, 0x00, 0x03, // modification_time    0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second    (track.duration >>> 24) & 0xFF,    (track.duration >>> 16) & 0xFF,    (track.duration >>>  8) & 0xFF,    track.duration & 0xFF,  // duration    0x55, 0xc4,             // 'und' language (undetermined)    0x00, 0x00  ]);  // Use the sample rate from the track metadata, when it is  // defined. The sample rate can be parsed out of an ADTS header, for  // instance.  if (track.samplerate) {    result[12] = (track.samplerate >>> 24) & 0xFF;    result[13] = (track.samplerate >>> 16) & 0xFF;    result[14] = (track.samplerate >>>  8) & 0xFF;    result[15] = (track.samplerate)        & 0xFF;  }  return box(types.mdhd, result);};mdia = function(track) {  return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));};mfhd = function(sequenceNumber) {  return box(types.mfhd, new Uint8Array([    0x00,    0x00, 0x00, 0x00, // flags    (sequenceNumber & 0xFF000000) >> 24,    (sequenceNumber & 0xFF0000) >> 16,    (sequenceNumber & 0xFF00) >> 8,    sequenceNumber & 0xFF // sequence_number  ]));};minf = function(track) {  return box(types.minf,             track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD),             dinf(),             stbl(track));};moof = function(sequenceNumber, tracks) {  var    trackFragments = [],    i = tracks.length;  // build traf boxes for each track fragment  while (i--) {    trackFragments[i] = traf(tracks[i]);  }  return box.apply(null, [    types.moof,    mfhd(sequenceNumber)  ].concat(trackFragments));};/** * Returns a movie box. * @param tracks {array} the tracks associated with this movie * @see ISO/IEC 14496-12:2012(E), section 8.2.1 */moov = function(tracks) {  var    i = tracks.length,    boxes = [];  while (i--) {    boxes[i] = trak(tracks[i]);  }  return box.apply(null, [types.moov, mvhd(0xffffffff)].concat(boxes).concat(mvex(tracks)));};mvex = function(tracks) {  var    i = tracks.length,    boxes = [];  while (i--) {    boxes[i] = trex(tracks[i]);  }  return box.apply(null, [types.mvex].concat(boxes));};mvhd = function(duration) {  var    bytes = new Uint8Array([      0x00, // version 0      0x00, 0x00, 0x00, // flags      0x00, 0x00, 0x00, 0x01, // creation_time      0x00, 0x00, 0x00, 0x02, // modification_time      0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second      (duration & 0xFF000000) >> 24,      (duration & 0xFF0000) >> 16,      (duration & 0xFF00) >> 8,      duration & 0xFF, // duration      0x00, 0x01, 0x00, 0x00, // 1.0 rate      0x01, 0x00, // 1.0 volume      0x00, 0x00, // reserved      0x00, 0x00, 0x00, 0x00, // reserved      0x00, 0x00, 0x00, 0x00, // reserved      0x00, 0x01, 0x00, 0x00,      0x00, 0x00, 0x00, 0x00,      0x00, 0x00, 0x00, 0x00,      0x00, 0x00, 0x00, 0x00,      0x00, 0x01, 0x00, 0x00,      0x00, 0x00, 0x00, 0x00,      0x00, 0x00, 0x00, 0x00,      0x00, 0x00, 0x00, 0x00,      0x40, 0x00, 0x00, 0x00, // transformation: unity matrix      0x00, 0x00, 0x00, 0x00,      0x00, 0x00, 0x00, 0x00,      0x00, 0x00, 0x00, 0x00,      0x00, 0x00, 0x00, 0x00,      0x00, 0x00, 0x00, 0x00,      0x00, 0x00, 0x00, 0x00, // pre_defined      0xff, 0xff, 0xff, 0xff // next_track_ID    ]);  return box(types.mvhd, bytes);};sdtp = function(track) {  var    samples = track.samples || [],    bytes = new Uint8Array(4 + samples.length),    flags,    i;  // leave the full box header (4 bytes) all zero  // write the sample table  for (i = 0; i < samples.length; i++) {    flags = samples[i].flags;    bytes[i + 4] = (flags.dependsOn << 4) |      (flags.isDependedOn << 2) |      (flags.hasRedundancy);  }  return box(types.sdtp,             bytes);};stbl = function(track) {  return box(types.stbl,             stsd(track),             box(types.stts, STTS),             box(types.stsc, STSC),             box(types.stsz, STSZ),             box(types.stco, STCO));};(function() {  var videoSample, audioSample;  stsd = function(track) {    return box(types.stsd, new Uint8Array([      0x00, // version 0      0x00, 0x00, 0x00, // flags      0x00, 0x00, 0x00, 0x01    ]), track.type === 'video' ? videoSample(track) : audioSample(track));  };  videoSample = function(track) {    var      sps = track.sps || [],      pps = track.pps || [],      sequenceParameterSets = [],      pictureParameterSets = [],      i,      avc1Box;    // assemble the SPSs    for (i = 0; i < sps.length; i++) {      sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);      sequenceParameterSets.push((sps[i].byteLength & 0xFF)); // sequenceParameterSetLength      sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS    }    // assemble the PPSs    for (i = 0; i < pps.length; i++) {      pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);      pictureParameterSets.push((pps[i].byteLength & 0xFF));      pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));    }    avc1Box = [      types.avc1, new Uint8Array([        0x00, 0x00, 0x00,        0x00, 0x00, 0x00, // reserved        0x00, 0x01, // data_reference_index        0x00, 0x00, // pre_defined        0x00, 0x00, // reserved        0x00, 0x00, 0x00, 0x00,        0x00, 0x00, 0x00, 0x00,        0x00, 0x00, 0x00, 0x00, // pre_defined        (track.width & 0xff00) >> 8,        track.width & 0xff, // width        (track.height & 0xff00) >> 8,        track.height & 0xff, // height        0x00, 0x48, 0x00, 0x00, // horizresolution        0x00, 0x48, 0x00, 0x00, // vertresolution        0x00, 0x00, 0x00, 0x00, // reserved        0x00, 0x01, // frame_count        0x13,        0x76, 0x69, 0x64, 0x65,        0x6f, 0x6a, 0x73, 0x2d,        0x63, 0x6f, 0x6e, 0x74,        0x72, 0x69, 0x62, 0x2d,        0x68, 0x6c, 0x73, 0x00,        0x00, 0x00, 0x00, 0x00,        0x00, 0x00, 0x00, 0x00,        0x00, 0x00, 0x00, // compressorname        0x00, 0x18, // depth = 24        0x11, 0x11 // pre_defined = -1      ]),      box(types.avcC, new Uint8Array([        0x01, // configurationVersion        track.profileIdc, // AVCProfileIndication        track.profileCompatibility, // profile_compatibility        track.levelIdc, // AVCLevelIndication        0xff // lengthSizeMinusOne, hard-coded to 4 bytes      ].concat(        [sps.length], // numOfSequenceParameterSets        sequenceParameterSets, // "SPS"        [pps.length], // numOfPictureParameterSets        pictureParameterSets // "PPS"      ))),      box(types.btrt, new Uint8Array([        0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB        0x00, 0x2d, 0xc6, 0xc0, // maxBitrate        0x00, 0x2d, 0xc6, 0xc0 // avgBitrate      ]))    ];    if (track.sarRatio) {      var        hSpacing = track.sarRatio[0],        vSpacing = track.sarRatio[1];        avc1Box.push(          box(types.pasp, new Uint8Array([            (hSpacing & 0xFF000000) >> 24,            (hSpacing & 0xFF0000) >> 16,            (hSpacing & 0xFF00) >> 8,            hSpacing & 0xFF,            (vSpacing & 0xFF000000) >> 24,            (vSpacing & 0xFF0000) >> 16,            (vSpacing & 0xFF00) >> 8,            vSpacing & 0xFF          ]))        );    }    return box.apply(null, avc1Box);  };  audioSample = function(track) {    return box(types.mp4a, new Uint8Array([      // SampleEntry, ISO/IEC 14496-12      0x00, 0x00, 0x00,      0x00, 0x00, 0x00, // reserved      0x00, 0x01, // data_reference_index      // AudioSampleEntry, ISO/IEC 14496-12      0x00, 0x00, 0x00, 0x00, // reserved      0x00, 0x00, 0x00, 0x00, // reserved      (track.channelcount & 0xff00) >> 8,      (track.channelcount & 0xff), // channelcount      (track.samplesize & 0xff00) >> 8,      (track.samplesize & 0xff), // samplesize      0x00, 0x00, // pre_defined      0x00, 0x00, // reserved      (track.samplerate & 0xff00) >> 8,      (track.samplerate & 0xff),      0x00, 0x00 // samplerate, 16.16      // MP4AudioSampleEntry, ISO/IEC 14496-14    ]), esds(track));  };}());tkhd = function(track) {  var result = new Uint8Array([    0x00, // version 0    0x00, 0x00, 0x07, // flags    0x00, 0x00, 0x00, 0x00, // creation_time    0x00, 0x00, 0x00, 0x00, // modification_time    (track.id & 0xFF000000) >> 24,    (track.id & 0xFF0000) >> 16,    (track.id & 0xFF00) >> 8,    track.id & 0xFF, // track_ID    0x00, 0x00, 0x00, 0x00, // reserved    (track.duration & 0xFF000000) >> 24,    (track.duration & 0xFF0000) >> 16,    (track.duration & 0xFF00) >> 8,    track.duration & 0xFF, // duration    0x00, 0x00, 0x00, 0x00,    0x00, 0x00, 0x00, 0x00, // reserved    0x00, 0x00, // layer    0x00, 0x00, // alternate_group    0x01, 0x00, // non-audio track volume    0x00, 0x00, // reserved    0x00, 0x01, 0x00, 0x00,    0x00, 0x00, 0x00, 0x00,    0x00, 0x00, 0x00, 0x00,    0x00, 0x00, 0x00, 0x00,    0x00, 0x01, 0x00, 0x00,    0x00, 0x00, 0x00, 0x00,    0x00, 0x00, 0x00, 0x00,    0x00, 0x00, 0x00, 0x00,    0x40, 0x00, 0x00, 0x00, // transformation: unity matrix    (track.width & 0xFF00) >> 8,    track.width & 0xFF,    0x00, 0x00, // width    (track.height & 0xFF00) >> 8,    track.height & 0xFF,    0x00, 0x00 // height  ]);  return box(types.tkhd, result);};/** * Generate a track fragment (traf) box. A traf box collects metadata * about tracks in a movie fragment (moof) box. */traf = function(track) {  var trackFragmentHeader, trackFragmentDecodeTime, trackFragmentRun,      sampleDependencyTable, dataOffset,      upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;  trackFragmentHeader = box(types.tfhd, new Uint8Array([    0x00, // version 0    0x00, 0x00, 0x3a, // flags    (track.id & 0xFF000000) >> 24,    (track.id & 0xFF0000) >> 16,    (track.id & 0xFF00) >> 8,    (track.id & 0xFF), // track_ID    0x00, 0x00, 0x00, 0x01, // sample_description_index    0x00, 0x00, 0x00, 0x00, // default_sample_duration    0x00, 0x00, 0x00, 0x00, // default_sample_size    0x00, 0x00, 0x00, 0x00  // default_sample_flags  ]));  upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (MAX_UINT32));  lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (MAX_UINT32));  trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([    0x01, // version 1    0x00, 0x00, 0x00, // flags    // baseMediaDecodeTime    (upperWordBaseMediaDecodeTime >>> 24) & 0xFF,    (upperWordBaseMediaDecodeTime >>> 16) & 0xFF,    (upperWordBaseMediaDecodeTime >>>  8) & 0xFF,    upperWordBaseMediaDecodeTime & 0xFF,    (lowerWordBaseMediaDecodeTime >>> 24) & 0xFF,    (lowerWordBaseMediaDecodeTime >>> 16) & 0xFF,    (lowerWordBaseMediaDecodeTime >>>  8) & 0xFF,    lowerWordBaseMediaDecodeTime & 0xFF  ]));  // the data offset specifies the number of bytes from the start of  // the containing moof to the first payload byte of the associated  // mdat  dataOffset = (32 + // tfhd                20 + // tfdt                8 +  // traf header                16 + // mfhd                8 +  // moof header                8);  // mdat header  // audio tracks require less metadata  if (track.type === 'audio') {    trackFragmentRun = trun(track, dataOffset);    return box(types.traf,               trackFragmentHeader,               trackFragmentDecodeTime,               trackFragmentRun);  }  // video tracks should contain an independent and disposable samples  // box (sdtp)  // generate one and adjust offsets to match  sampleDependencyTable = sdtp(track);  trackFragmentRun = trun(track,                          sampleDependencyTable.length + dataOffset);  return box(types.traf,             trackFragmentHeader,             trackFragmentDecodeTime,             trackFragmentRun,             sampleDependencyTable);};/** * Generate a track box. * @param track {object} a track definition * @return {Uint8Array} the track box */trak = function(track) {  track.duration = track.duration || 0xffffffff;  return box(types.trak,             tkhd(track),             mdia(track));};trex = function(track) {  var result = new Uint8Array([    0x00, // version 0    0x00, 0x00, 0x00, // flags    (track.id & 0xFF000000) >> 24,    (track.id & 0xFF0000) >> 16,    (track.id & 0xFF00) >> 8,    (track.id & 0xFF), // track_ID    0x00, 0x00, 0x00, 0x01, // default_sample_description_index    0x00, 0x00, 0x00, 0x00, // default_sample_duration    0x00, 0x00, 0x00, 0x00, // default_sample_size    0x00, 0x01, 0x00, 0x01 // default_sample_flags  ]);  // the last two bytes of default_sample_flags is the sample  // degradation priority, a hint about the importance of this sample  // relative to others. Lower the degradation priority for all sample  // types other than video.  if (track.type !== 'video') {    result[result.length - 1] = 0x00;  }  return box(types.trex, result);};(function() {  var audioTrun, videoTrun, trunHeader;  // This method assumes all samples are uniform. That is, if a  // duration is present for the first sample, it will be present for  // all subsequent samples.  // see ISO/IEC 14496-12:2012, Section 8.8.8.1  trunHeader = function(samples, offset) {    var durationPresent = 0, sizePresent = 0,        flagsPresent = 0, compositionTimeOffset = 0;    // trun flag constants    if (samples.length) {      if (samples[0].duration !== undefined) {        durationPresent = 0x1;      }      if (samples[0].size !== undefined) {        sizePresent = 0x2;      }      if (samples[0].flags !== undefined) {        flagsPresent = 0x4;      }      if (samples[0].compositionTimeOffset !== undefined) {        compositionTimeOffset = 0x8;      }    }    return [      0x00, // version 0      0x00,      durationPresent | sizePresent | flagsPresent | compositionTimeOffset,      0x01, // flags      (samples.length & 0xFF000000) >>> 24,      (samples.length & 0xFF0000) >>> 16,      (samples.length & 0xFF00) >>> 8,      samples.length & 0xFF, // sample_count      (offset & 0xFF000000) >>> 24,      (offset & 0xFF0000) >>> 16,      (offset & 0xFF00) >>> 8,      offset & 0xFF // data_offset    ];  };  videoTrun = function(track, offset) {    var bytesOffest, bytes, header, samples, sample, i;    samples = track.samples || [];    offset += 8 + 12 + (16 * samples.length);    header = trunHeader(samples, offset);    bytes = new Uint8Array(header.length + samples.length * 16);    bytes.set(header);    bytesOffest = header.length;    for (i = 0; i < samples.length; i++) {      sample = samples[i];      bytes[bytesOffest++] = (sample.duration & 0xFF000000) >>> 24;      bytes[bytesOffest++] = (sample.duration & 0xFF0000) >>> 16;      bytes[bytesOffest++] = (sample.duration & 0xFF00) >>> 8;      bytes[bytesOffest++] = sample.duration & 0xFF; // sample_duration      bytes[bytesOffest++] = (sample.size & 0xFF000000) >>> 24;      bytes[bytesOffest++] = (sample.size & 0xFF0000) >>> 16;      bytes[bytesOffest++] = (sample.size & 0xFF00) >>> 8;      bytes[bytesOffest++] = sample.size & 0xFF; // sample_size      bytes[bytesOffest++] = (sample.flags.isLeading << 2) | sample.flags.dependsOn;      bytes[bytesOffest++] = (sample.flags.isDependedOn << 6) |          (sample.flags.hasRedundancy << 4) |          (sample.flags.paddingValue << 1) |          sample.flags.isNonSyncSample;      bytes[bytesOffest++] = sample.flags.degradationPriority & 0xF0 << 8;      bytes[bytesOffest++] = sample.flags.degradationPriority & 0x0F; // sample_flags      bytes[bytesOffest++] = (sample.compositionTimeOffset & 0xFF000000) >>> 24;      bytes[bytesOffest++] = (sample.compositionTimeOffset & 0xFF0000) >>> 16;      bytes[bytesOffest++] = (sample.compositionTimeOffset & 0xFF00) >>> 8;      bytes[bytesOffest++] = sample.compositionTimeOffset & 0xFF; // sample_composition_time_offset    }    return box(types.trun, bytes);  };  audioTrun = function(track, offset) {    var bytes, bytesOffest, header, samples, sample, i;    samples = track.samples || [];    offset += 8 + 12 + (8 * samples.length);    header = trunHeader(samples, offset);    bytes = new Uint8Array(header.length + samples.length * 8);    bytes.set(header);    bytesOffest = header.length;    for (i = 0; i < samples.length; i++) {      sample = samples[i];      bytes[bytesOffest++] = (sample.duration & 0xFF000000) >>> 24;      bytes[bytesOffest++] = (sample.duration & 0xFF0000) >>> 16;      bytes[bytesOffest++] = (sample.duration & 0xFF00) >>> 8;      bytes[bytesOffest++] = sample.duration & 0xFF; // sample_duration      bytes[bytesOffest++] = (sample.size & 0xFF000000) >>> 24;      bytes[bytesOffest++] = (sample.size & 0xFF0000) >>> 16;      bytes[bytesOffest++] = (sample.size & 0xFF00) >>> 8;      bytes[bytesOffest++] = sample.size & 0xFF; // sample_size    }    return box(types.trun, bytes);  };  trun = function(track, offset) {    if (track.type === 'audio') {      return audioTrun(track, offset);    }    return videoTrun(track, offset);  };}());module.exports = {  ftyp: ftyp,  mdat: mdat,  moof: moof,  moov: moov,  initSegment: function(tracks) {    var      fileType = ftyp(),      movie = moov(tracks),      result;    result = new Uint8Array(fileType.byteLength + movie.byteLength);    result.set(fileType);    result.set(movie, fileType.byteLength);    return result;  }};
 |