mp4box
Version:
JavaScript version of GPAC's MP4Box tool
675 lines (627 loc) • 20.6 kB
JavaScript
/*
* Copyright (c) Telecom ParisTech/TSI/MM/GPAC Cyril Concolato
* License: BSD-3-Clause (see LICENSE file)
*/
BoxParser.Box.prototype.parse = function(stream) {
if (this.type != "mdat") {
this.data = stream.readUint8Array(this.size);
} else {
stream.seek(this.start+this.size+this.hdr_size);
}
}
BoxParser.FullBox.prototype.parseFullHeader = function (stream) {
this.version = stream.readUint8();
this.flags = stream.readUint24();
this.size -= 4;
}
BoxParser.ContainerBox.prototype.parse = function(stream) {
var ret;
var box;
var start;
start = stream.position;
while (stream.position < start+this.size) {
ret = BoxParser.parseOneBox(stream);
box = ret.box;
/* store the box in the 'boxes' array to preserve box order (for offset) but also store box in a property for more direct access */
this.boxes.push(box);
if (this.subBoxNames && this.subBoxNames.indexOf(box.type) != -1) {
this[this.subBoxNames+"s"].push(box);
} else {
this[box.type] = box;
}
}
}
BoxParser.SampleEntry.prototype.parseHeader = function(stream) {
this.start = stream.position;
stream.readUint8Array(6);
this.data_reference_index = stream.readUint16();
}
BoxParser.SampleEntry.prototype.parse = function(stream) {
this.parseHeader(stream);
stream.seek(this.start+this.size);
}
BoxParser.SampleEntry.prototype.parseFooter = function(stream) {
var ret;
var box;
while (stream.position < this.start+this.size) {
ret = BoxParser.parseOneBox(stream, false);
box = ret.box;
this.boxes.push(box);
this[box.type] = box;
}
}
BoxParser.VisualSampleEntry.prototype.parse = function(stream) {
this.parseHeader(stream);
stream.readUint16();
stream.readUint16();
stream.readUint32Array(3);
this.width = stream.readUint16();
this.height = stream.readUint16();
this.horizresolution = stream.readUint32();
this.vertresolution = stream.readUint32();
stream.readUint32();
this.frame_count = stream.readUint16();
this.compressorname = stream.readString(32);
this.depth = stream.readUint16();
stream.readUint16();
this.parseFooter(stream);
}
BoxParser.AudioSampleEntry.prototype.parse = function(stream) {
this.parseHeader(stream);
stream.readUint32Array(2);
this.channel_count = stream.readUint16();
this.samplesize = stream.readUint16();
stream.readUint16();
stream.readUint16();
this.samplerate = (stream.readUint32()/(1<<16));
this.parseFooter(stream);
}
BoxParser.SubtitleSampleEntry.prototype.parse = function(stream) {
this.parseHeader(stream);
this.parseFooter(stream);
}
BoxParser.MetadataSampleEntry.prototype.parse = function(stream) {
this.parseHeader(stream);
this.parseFooter(stream);
}
BoxParser.TrackReferenceTypeBox.prototype.parse = function(stream) {
this.track_ids = stream.readUint8Array(this.size);
}
BoxParser.metxBox.prototype.parse = function(stream) {
this.parseHeader(stream);
this.content_encoding = stream.readCString();
this.namespace = stream.readCString();
this.schema_location = stream.readCString();
this.parseFooter(stream);
}
BoxParser.mettBox.prototype.parse = function(stream) {
this.parseHeader(stream);
this.content_encoding = stream.readCString();
this.mime_format = stream.readCString();
this.parseFooter(stream);
}
BoxParser.sbttBox.prototype.parse = function(stream) {
this.parseHeader(stream);
this.content_encoding = stream.readCString();
this.mime_format = stream.readCString();
this.parseFooter(stream);
}
BoxParser.stxtBox.prototype.parse = function(stream) {
this.parseHeader(stream);
this.content_encoding = stream.readCString();
this.mime_format = stream.readCString();
this.parseFooter(stream);
}
BoxParser.stppBox.prototype.parse = function(stream) {
this.parseHeader(stream);
this.namespace = stream.readCString();
this.schema_location = stream.readCString();
this.auxiliary_mime_types = stream.readCString();
this.parseFooter(stream);
}
BoxParser.tx3gBox.prototype.parse = function(stream) {
this.parseHeader(stream);
this.displayFlags = stream.readUint32();
this.horizontal_justification = stream.readInt8();
this.vertical_justification = stream.readInt8();
this.bg_color_rgba = stream.readUint8Array(4);
this.box_record = stream.readInt16Array(4);
this.style_record = stream.readUint8Array(12);
this.parseFooter(stream);
}
BoxParser.ftypBox.prototype.parse = function(stream) {
this.major_brand = stream.readString(4);
this.minor_version = stream.readUint32();
this.size -= 8;
this.compatible_brands = [];
var i = 0;
while (this.size>=4) {
this.compatible_brands[i] = stream.readString(4);
this.size -= 4;
i++;
}
}
BoxParser.stypBox.prototype.parse = BoxParser.ftypBox.prototype.parse;
BoxParser.mvhdBox.prototype.parse = function(stream) {
this.flags = 0;
this.parseFullHeader(stream);
if (this.version == 1) {
this.creation_time = stream.readUint64();
this.modification_time = stream.readUint64();
this.timescale = stream.readUint32();
this.duration = stream.readUint64();
} else {
this.creation_time = stream.readUint32();
this.modification_time = stream.readUint32();
this.timescale = stream.readUint32();
this.duration = stream.readUint32();
}
this.rate = stream.readUint32();
this.volume = stream.readUint16()>>8;
stream.readUint16();
stream.readUint32Array(2);
this.matrix = stream.readUint32Array(9);
stream.readUint32Array(6);
this.next_track_id = stream.readUint32();
}
BoxParser.tkhdBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
if (this.version == 1) {
this.creation_time = stream.readUint64();
this.modification_time = stream.readUint64();
this.track_id = stream.readUint32();
stream.readUint32();
this.duration = stream.readUint64();
} else {
this.creation_time = stream.readUint32();
this.modification_time = stream.readUint32();
this.track_id = stream.readUint32();
stream.readUint32();
this.duration = stream.readUint32();
}
stream.readUint32Array(2);
this.layer = stream.readInt16();
this.alternate_group = stream.readInt16();
this.volume = stream.readInt16()>>8;
stream.readUint16();
this.matrix = stream.readInt32Array(9);
this.width = stream.readUint32();
this.height = stream.readUint32();
}
BoxParser.mdhdBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
if (this.version == 1) {
this.creation_time = stream.readUint64();
this.modification_time = stream.readUint64();
this.timescale = stream.readUint32();
this.duration = stream.readUint64();
} else {
this.creation_time = stream.readUint32();
this.modification_time = stream.readUint32();
this.timescale = stream.readUint32();
this.duration = stream.readUint32();
}
this.language = stream.readUint16();
var chars = [];
chars[0] = (this.language>>10)&0x1F;
chars[1] = (this.language>>5)&0x1F;
chars[2] = (this.language)&0x1F;
this.languageString = String.fromCharCode(chars[0]+0x60, chars[1]+0x60, chars[2]+0x60);
stream.readUint16();
}
BoxParser.hdlrBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
if (this.version === 0) {
stream.readUint32();
this.handler = stream.readString(4);
stream.readUint32Array(3);
this.name = stream.readCString();
} else {
this.data = stream.readUint8Array(size);
}
}
BoxParser.stsdBox.prototype.parse = function(stream) {
var ret;
var entryCount;
this.parseFullHeader(stream);
entryCount = stream.readUint32();
for (i = 1; i <= entryCount; i++) {
ret = BoxParser.parseOneBox(stream, true);
this.entries.push(ret.box);
}
}
BoxParser.avcCBox.prototype.parse = function(stream) {
var i;
var nb_nalus;
var length;
this.configurationVersion = stream.readUint8();
this.AVCProfileIndication = stream.readUint8();
this.profile_compatibility = stream.readUint8();
this.AVCLevelIndication = stream.readUint8();
this.lengthSizeMinusOne = (stream.readUint8() & 0x3);
nb_nalus = (stream.readUint8() & 0x1F);
this.size -= 6;
this.SPS = new Array(nb_nalus);
for (i = 0; i < nb_nalus; i++) {
length = stream.readUint16();
this.SPS[i] = stream.readUint8Array(length);
this.size -= 2+length;
}
nb_nalus = stream.readUint8();
this.size--;
this.PPS = new Array(nb_nalus);
for (i = 0; i < nb_nalus; i++) {
length = stream.readUint16();
this.PPS[i] = stream.readUint8Array(length);
this.size -= 2+length;
}
if (this.size>0) {
this.ext = stream.readUint8Array(this.size);
}
}
BoxParser.hvcCBox.prototype.parse = function(stream) {
var i;
var nb_nalus;
var length;
var tmp_byte;
this.configurationVersion = stream.readUint8();
tmp_byte = stream.readUint8();
this.general_profile_space = tmp_byte >> 6;
this.general_tier_flag = (tmp_byte & 0x20) >> 5;
this.general_profile_idc = (tmp_byte & 0x1F);
this.general_profile_compatibility = stream.readUint32();
this.general_constraint_indicator = stream.readUint8Array(6);
this.general_level_idc = stream.readUint8();
this.min_spatial_segmentation_idc = stream.readUint16() & 0xFFF;
this.parallelismType = (stream.readUint8() & 0x3);
this.chromaFormat = (stream.readUint8() & 0x3);
this.bitDepthLumaMinus8 = (stream.readUint8() & 0x7);
this.bitDepthChromaMinus8 = (stream.readUint8() & 0x7);
this.avgFrameRate = stream.readUint16();
tmp_byte = stream.readUint8();
this.constantFrameRate = (tmp_byte >> 6);
this.numTemporalLayers = (tmp_byte & 0XD) >> 3;
this.temporalIdNested = (tmp_byte & 0X4) >> 2;
this.lengthSizeMinusOne = (tmp_byte & 0X3);
this.nalu_arrays = [];
numOfArrays = stream.readUint8();
for (i = 0; i < numOfArrays; i++) {
var nalu_array = [];
this.nalu_arrays.push(nalu_array);
tmp_byte = stream.readUint8()
nalu_array.completeness = (tmp_byte & 0x80) >> 7;
nalu_array.nalu_type = tmp_byte & 0x3F;
numNalus = stream.readUint16();
for (j = 0; j < numNalus; j++) {
var nalu = {}
nalu_array.push(nalu);
length = stream.readUint16();
nalu.data = stream.readUint8Array(length);
}
}
}
BoxParser.esdsBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
this.data = stream.readUint8Array(this.size);
this.size = 0;
if (typeof MPEG4DescriptorParser !== "undefined") {
var esd_parser = new MPEG4DescriptorParser();
this.esd = esd_parser.parseOneDescriptor(new DataStream(this.data.buffer, 0, DataStream.BIG_ENDIAN));
}
}
BoxParser.txtCBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
this.config = stream.readCString();
}
BoxParser.cttsBox.prototype.parse = function(stream) {
var entry_count;
var i;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.sample_counts = [];
this.sample_offsets = [];
if (this.version === 0) {
for(i=0; i<entry_count; i++) {
this.sample_counts.push(stream.readUint32());
/* some files are buggy and declare version=0 while using signed offsets.
The likelyhood of using the most significant bit in a 32-bits time offset is very low,
so using signed value here as well */
this.sample_offsets.push(stream.readInt32());
}
} else if (this.version == 1) {
for(i=0; i<entry_count; i++) {
this.sample_counts.push(stream.readUint32());
this.sample_offsets.push(stream.readInt32()); /* signed */
}
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.cslgBox.prototype.parse = function(stream) {
var entry_count;
this.parseFullHeader(stream);
if (this.version === 0) {
this.compositionToDTSShift = stream.readInt32(); /* signed */
this.leastDecodeToDisplayDelta = stream.readInt32(); /* signed */
this.greatestDecodeToDisplayDelta = stream.readInt32(); /* signed */
this.compositionStartTime = stream.readInt32(); /* signed */
this.compositionEndTime = stream.readInt32(); /* signed */
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.sttsBox.prototype.parse = function(stream) {
var entry_count;
var i;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.sample_counts = [];
this.sample_deltas = [];
if (this.version === 0) {
for(i=0; i<entry_count; i++) {
this.sample_counts.push(stream.readUint32());
this.sample_deltas.push(stream.readUint32());
}
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.stssBox.prototype.parse = function(stream) {
var entry_count;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
if (this.version === 0) {
this.sample_numbers = [];
for(i=0; i<entry_count; i++) {
this.sample_numbers.push(stream.readUint32());
}
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.stshBox.prototype.parse = function(stream) {
var entry_count;
var i;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.shadowed_sample_numbers = [];
this.sync_sample_numbers = [];
if (this.version === 0) {
for(i=0; i<entry_count; i++) {
this.shadowed_sample_numbers.push(stream.readUint32());
this.sync_sample_numbers.push(stream.readUint32());
}
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.stcoBox.prototype.parse = function(stream) {
var entry_count;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
if (this.version === 0) {
this.chunk_offsets = stream.readUint32Array(entry_count);
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.co64Box.prototype.parse = function(stream) {
var entry_count;
var i;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.chunk_offsets = [];
if (this.version === 0) {
for(i=0; i<entry_count; i++) {
this.chunk_offsets.push(stream.readUint64());
}
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.stscBox.prototype.parse = function(stream) {
var entry_count;
var i;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.first_chunk = [];
this.samples_per_chunk = [];
this.sample_description_index = [];
if (this.version === 0) {
for(i=0; i<entry_count; i++) {
this.first_chunk.push(stream.readUint32());
this.samples_per_chunk.push(stream.readUint32());
this.sample_description_index.push(stream.readUint32());
}
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.stszBox.prototype.parse = function(stream) {
var i;
var sample_size;
var sample_count;
this.parseFullHeader(stream);
this.sample_sizes = [];
if (this.version === 0) {
sample_size = stream.readUint32();
sample_count = stream.readUint32();
if (sample_size === 0) {
this.sample_sizes = stream.readUint32Array(sample_count);
} else {
this.sample_sizes = [];
for (i = 0; i < sample_count; i++) {
this.sample_sizes[i] = sample_size;
}
}
} else {
this.data = stream.readUint8Array(this.size);
}
}
BoxParser.mehdBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
if (this.version == 1) {
this.fragment_duration = stream.readUint64();
} else {
this.fragment_duration = stream.readUint32();
}
}
BoxParser.trexBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
this.track_id = stream.readUint32();
this.default_sample_description_index = stream.readUint32();
this.default_sample_duration = stream.readUint32();
this.default_sample_size = stream.readUint32();
this.default_sample_flags = stream.readUint32();
}
BoxParser.mfhdBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
this.sequence_number = stream.readUint32();
}
BoxParser.tfhdBox.prototype.parse = function(stream) {
var readBytes = 0;
this.parseFullHeader(stream);
this.track_id = stream.readUint32();
if (this.size > readBytes && (this.flags & BoxParser.TFHD_FLAG_BASE_DATA_OFFSET)) {
this.base_data_offset = stream.readUint64();
readBytes += 8;
} else {
this.base_data_offset = 0;
}
if (this.size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC)) {
this.default_sample_description_index = stream.readUint32();
readBytes += 4;
} else {
this.default_sample_description_index = 0;
}
if (this.size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR)) {
this.default_sample_duration = stream.readUint32();
readBytes += 4;
} else {
this.default_sample_duration = 0;
}
if (this.size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE)) {
this.default_sample_size = stream.readUint32();
readBytes += 4;
} else {
this.default_sample_size = 0;
}
if (this.size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS)) {
this.default_sample_flags = stream.readUint32();
readBytes += 4;
} else {
this.default_sample_flags = 0;
}
}
BoxParser.trunBox.prototype.parse = function(stream) {
var readBytes = 0;
this.parseFullHeader(stream);
this.sample_count = stream.readUint32();
readBytes+= 4;
if (this.size > readBytes && (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) ) {
this.data_offset = stream.readInt32(); //signed
readBytes += 4;
} else {
this.data_offset = 0;
}
if (this.size > readBytes && (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) ) {
this.first_sample_flags = stream.readUint32();
readBytes += 4;
} else {
this.first_sample_flags = 0;
}
this.sample_duration = [];
this.sample_size = [];
this.sample_flags = [];
this.sample_composition_time_offset = [];
if (this.size > readBytes) {
for (var i = 0; i < this.sample_count; i++) {
if (this.flags & BoxParser.TRUN_FLAGS_DURATION) {
this.sample_duration[i] = stream.readUint32();
}
if (this.flags & BoxParser.TRUN_FLAGS_SIZE) {
this.sample_size[i] = stream.readUint32();
}
if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) {
this.sample_flags[i] = stream.readUint32();
}
if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) {
if (this.version === 0) {
this.sample_composition_time_offset[i] = stream.readUint32();
} else {
this.sample_composition_time_offset[i] = stream.readInt32(); //signed
}
}
}
}
}
BoxParser.tfdtBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
if (this.version == 1) {
this.baseMediaDecodeTime = stream.readUint64();
} else {
this.baseMediaDecodeTime = stream.readUint32();
}
}
BoxParser.vttCBox.prototype.parse = function(stream) {
this.text = stream.readString(this.size);
}
BoxParser.paylBox.prototype.parse = function(stream) {
this.text = stream.readString(this.size);
}
BoxParser.subsBox.prototype.parse = function(stream) {
var i,j;
var entry_count;
var subsample_count;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.samples = [];
for (i = 0; i < entry_count; i++) {
var sampleInfo = {};
this.samples[i] = sampleInfo;
sampleInfo.sample_delta = stream.readUint32();
sampleInfo.subsamples = [];
subsample_count = stream.readUint16();
if (subsample_count>0) {
for (j = 0; j < subsample_count; j++) {
var subsample = {};
sampleInfo.subsamples.push(subsample);
if (this.version == 1) {
subsample.size = stream.readUint32();
} else {
subsample.size = stream.readUint16();
}
subsample.priority = stream.readUint8();
subsample.discardable = stream.readUint8();
subsample.reserved = stream.readUint32();
}
}
}
}
BoxParser.sidxBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
this.reference_ID = stream.readUint32();
this.timescale = stream.readUint32();
if (this.version === 0) {
this.earliest_presentation_time = stream.readUint32();
this.first_offset = stream.readUint32();
} else {
this.earliest_presentation_time = stream.readUint64();
this.first_offset = stream.readUint64();
}
stream.readUint16();
this.references = [];
var count = stream.readUint16();
for (var i = 0; i < count; i++) {
var ref = {};
this.references.push(ref);
var tmp_32 = stream.readUint32();
ref.type = (tmp_32 >> 31) & 0x1;
ref.size = tmp_32 & 0x8FFFFFFF;
ref.duration = stream.readUint32();
tmp_32 = stream.readUint32();
ref.starts_with_SAP = (tmp_32 >> 31) & 0x1;
ref.SAP_type = (tmp_32 >> 28) & 0x7;
ref.SAP_delta_time = tmp_32 & 0xFFFFFFF;
}
}