123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611 |
- /**
- * 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 box(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 dinf() {
- return box(types.dinf, box(types.dref, DREF));
- };
- esds = function esds(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 ftyp() {
- return box(types.ftyp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND, AVC1_BRAND);
- };
- hdlr = function hdlr(type) {
- return box(types.hdlr, HDLR_TYPES[type]);
- };
- mdat = function mdat(data) {
- return box(types.mdat, data);
- };
- mdhd = function mdhd(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 mdia(track) {
- return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
- };
- mfhd = function mfhd(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 minf(track) {
- return box(types.minf, track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD), dinf(), stbl(track));
- };
- moof = function moof(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 moov(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 mvex(tracks) {
- var i = tracks.length,
- boxes = [];
- while (i--) {
- boxes[i] = trex(tracks[i]);
- }
- return box.apply(null, [types.mvex].concat(boxes));
- };
- mvhd = function mvhd(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 sdtp(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 stbl(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 stsd(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 videoSample(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 audioSample(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 tkhd(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 traf(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 trak(track) {
- track.duration = track.duration || 0xffffffff;
- return box(types.trak, tkhd(track), mdia(track));
- };
- trex = function trex(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 trunHeader(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 videoTrun(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 audioTrun(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 trun(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 initSegment(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;
- }
- };
|