mp4box
Version:
JavaScript version of GPAC's MP4Box tool
488 lines (456 loc) • 14.5 kB
JavaScript
/*
* Copyright (c) Telecom ParisTech/TSI/MM/GPAC Cyril Concolato
* License: BSD-3-Clause (see LICENSE file)
*/
BoxParser.Box.prototype.writeHeader = function(stream, msg) {
this.size += 8;
if (this.size > MAX_SIZE) {
this.size += 8;
}
Log.d("BoxWriter", "Writing box "+this.type+" of size: "+this.size+" at position "+stream.position+(msg || ""));
if (this.size > MAX_SIZE) {
stream.writeUint32(1);
} else {
this.sizePosition = stream.position;
stream.writeUint32(this.size);
}
stream.writeString(this.type, null, 4);
if (this.size > MAX_SIZE) {
stream.writeUint64(this.size);
}
}
BoxParser.FullBox.prototype.writeHeader = function(stream) {
this.size += 4;
BoxParser.Box.prototype.writeHeader.call(this, stream, " v="+this.version+" f="+this.flags);
stream.writeUint8(this.version);
stream.writeUint24(this.flags);
}
BoxParser.Box.prototype.write = function(stream) {
if (this.type === "mdat") {
/* TODO: fix this */
if (this.data) {
this.size = this.data.length;
this.writeHeader(stream);
stream.writeUint8Array(this.data);
}
} else {
this.size = this.data.length;
this.writeHeader(stream);
stream.writeUint8Array(this.data);
}
}
BoxParser.ContainerBox.prototype.write = function(stream) {
this.size = 0;
this.writeHeader(stream);
for (var i=0; i<this.boxes.length; i++) {
if (this.boxes[i]) {
this.boxes[i].write(stream);
this.size += this.boxes[i].size;
}
}
/* adjusting the size, now that all sub-boxes are known */
Log.d("BoxWriter", "Adjusting box "+this.type+" with new size "+this.size);
stream.adjustUint32(this.sizePosition, this.size);
}
BoxParser.TrackReferenceTypeBox.prototype.write = function(stream) {
this.size = this.track_ids.length*4;
this.writeHeader(stream);
stream.writeUint32Array(this.track_ids);
}
BoxParser.ftypBox.prototype.write = function(stream) {
this.size = 8+4*this.compatible_brands.length;
this.writeHeader(stream);
stream.writeString(this.major_brand, null, 4);
stream.writeUint32(this.minor_version);
for (var i = 0; i < this.compatible_brands.length; i++) {
stream.writeString(this.compatible_brands[i], null, 4);
}
}
BoxParser.mvhdBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 23*4+2*2;
this.writeHeader(stream);
stream.writeUint32(this.creation_time);
stream.writeUint32(this.modification_time);
stream.writeUint32(this.timescale);
stream.writeUint32(this.duration);
stream.writeUint32(this.rate);
stream.writeUint16(this.volume<<8);
stream.writeUint16(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32Array(this.matrix);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(this.next_track_id);
}
BoxParser.tkhdBox.prototype.write = function(stream) {
this.version = 0;
//this.flags = 0;
this.size = 4*18+2*4;
this.writeHeader(stream);
stream.writeUint32(this.creation_time);
stream.writeUint32(this.modification_time);
stream.writeUint32(this.track_id);
stream.writeUint32(0);
stream.writeUint32(this.duration);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeInt16(this.layer);
stream.writeInt16(this.alternate_group);
stream.writeInt16(this.volume<<8);
stream.writeUint16(0);
stream.writeInt32Array(this.matrix);
stream.writeUint32(this.width);
stream.writeUint32(this.height);
}
BoxParser.mdhdBox.prototype.write = function(stream) {
this.size = 4*4+2*2;
this.flags = 0;
this.version = 0;
this.writeHeader(stream);
stream.writeUint32(this.creation_time);
stream.writeUint32(this.modification_time);
stream.writeUint32(this.timescale);
stream.writeUint32(this.duration);
stream.writeUint16(this.language);
stream.writeUint16(0);
}
BoxParser.hdlrBox.prototype.write = function(stream) {
this.size = 5*4+this.name.length+1;
this.version = 0;
this.flags = 0;
this.writeHeader(stream);
stream.writeUint32(0);
stream.writeString(this.handler, null, 4);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeCString(this.name);
}
BoxParser.stsdBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 0;
this.writeHeader(stream);
stream.writeUint32(this.entries.length);
this.size += 4;
for (i = 0; i < this.entries.length; i++) {
this.entries[i].write(stream);
this.size += this.entries[i].size;
}
/* adjusting the size, now that all sub-boxes are known */
Log.d("BoxWriter", "Adjusting box "+this.type+" with new size "+this.size);
stream.adjustUint32(this.sizePosition, this.size);
}
BoxParser.SampleEntry.prototype.writeHeader = function(stream) {
this.size = 8;
BoxParser.Box.prototype.writeHeader.call(this, stream);
stream.writeUint8(0);
stream.writeUint8(0);
stream.writeUint8(0);
stream.writeUint8(0);
stream.writeUint8(0);
stream.writeUint8(0);
stream.writeUint16(this.data_reference_index);
}
BoxParser.SampleEntry.prototype.writeFooter = function(stream) {
for (var i=0; i<this.boxes.length; i++) {
this.boxes[i].write(stream);
this.size += this.boxes[i].size;
}
Log.d("BoxWriter", "Adjusting box "+this.type+" with new size "+this.size);
stream.adjustUint32(this.sizePosition, this.size);
}
BoxParser.SampleEntry.prototype.write = function(stream) {
this.writeHeader(stream);
this.writeFooter(stream);
}
BoxParser.VisualSampleEntry.prototype.write = function(stream) {
this.writeHeader(stream);
this.size += 2*7+6*4+32;
stream.writeUint16(0);
stream.writeUint16(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint16(this.width);
stream.writeUint16(this.height);
stream.writeUint32(this.horizresolution);
stream.writeUint32(this.vertresolution);
stream.writeUint32(0);
stream.writeUint16(this.frame_count);
stream.writeString(this.compressorname, null, 32);
stream.writeUint16(this.depth);
stream.writeInt16(-1);
this.writeFooter(stream);
}
BoxParser.AudioSampleEntry.prototype.write = function(stream) {
this.writeHeader(stream);
this.size += 2*4+3*4;
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint16(this.channel_count);
stream.writeUint16(this.samplesize);
stream.writeUint16(0);
stream.writeUint16(0);
stream.writeUint32(this.samplerate<<16);
this.writeFooter(stream);
}
BoxParser.avcCBox.prototype.write = function(stream) {
var i;
this.size = 7;
for (i = 0; i < this.SPS.length; i++) {
this.size += 2+this.SPS[i].length;
}
for (i = 0; i < this.PPS.length; i++) {
this.size += 2+this.PPS[i].length;
}
if (this.ext) {
this.size += this.ext.length;
}
this.writeHeader(stream);
stream.writeUint8(this.configurationVersion);
stream.writeUint8(this.AVCProfileIndication);
stream.writeUint8(this.profile_compatibility);
stream.writeUint8(this.AVCLevelIndication);
stream.writeUint8(this.lengthSizeMinusOne + (63<<2));
stream.writeUint8(this.SPS.length + (7<<5));
for (i = 0; i < this.SPS.length; i++) {
stream.writeUint16(this.SPS[i].length);
stream.writeUint8Array(this.SPS[i]);
}
stream.writeUint8(this.PPS.length);
for (i = 0; i < this.PPS.length; i++) {
stream.writeUint16(this.PPS[i].length);
stream.writeUint8Array(this.PPS[i]);
}
if (this.ext) {
stream.writeUint8Array(this.ext);
}
}
BoxParser.cttsBox.prototype.write = function(stream) {
var i;
this.version = 1;
this.flags = 0;
this.size = 4+8*this.sample_counts.length;
this.writeHeader(stream);
stream.writeUint32(this.sample_counts.length);
for(i=0; i<this.sample_counts.length; i++) {
stream.writeUint32(this.sample_counts[i]);
stream.writeInt32(this.sample_offsets[i]); /* signed */
}
}
BoxParser.cslgBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 4*5;
this.writeHeader(stream);
stream.writeInt32(this.compositionToDTSShift);
stream.writeInt32(this.leastDecodeToDisplayDelta);
stream.writeInt32(this.greatestDecodeToDisplayDelta);
stream.writeInt32(this.compositionStartTime);
stream.writeInt32(this.compositionEndTime);
}
BoxParser.sttsBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 4+8*this.sample_counts.length;
this.writeHeader(stream);
stream.writeUint32(this.sample_counts.length);
for(i=0; i<this.sample_counts.length; i++) {
stream.writeUint32(this.sample_counts[i]);
stream.writeUint32(this.sample_deltas[i]);
}
}
BoxParser.stssBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4+4*this.sample_numbers.length;
this.writeHeader(stream);
stream.writeUint32(this.sample_numbers.length);
stream.writeUint32Array(this.sample_numbers);
}
BoxParser.stshBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 4+8*this.shadowed_sample_numbers.length;
this.writeHeader(stream);
stream.writeUint32(this.shadowed_sample_numbers.length);
for(i=0; i<this.shadowed_sample_numbers.length; i++) {
stream.writeUint32(this.shadowed_sample_numbers[i]);
stream.writeUint32(this.sync_sample_numbers[i]);
}
}
BoxParser.stcoBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4+4*this.chunk_offsets.length;
this.writeHeader(stream);
stream.writeUint32(this.chunk_offsets.length);
stream.writeUint32Array(this.chunk_offsets);
}
BoxParser.co64Box.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 4+8*this.chunk_offsets.length;
this.writeHeader(stream);
stream.writeUint32(this.chunk_offsets.length);
for(i=0; i<this.chunk_offsets.length; i++) {
stream.writeUint64(this.chunk_offsets[i]);
}
}
BoxParser.stscBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 4+12*this.first_chunk.length;
this.writeHeader(stream);
stream.writeUint32(this.first_chunk.length);
for(i=0; i<this.first_chunk.length; i++) {
stream.writeUint32(this.first_chunk[i]);
stream.writeUint32(this.samples_per_chunk[i]);
stream.writeUint32(this.sample_description_index[i]);
}
}
BoxParser.stszBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 8+12*this.sample_sizes.length;
this.writeHeader(stream);
stream.writeUint32(0);
stream.writeUint32(this.sample_sizes.length);
stream.writeUint32Array(this.sample_sizes);
}
BoxParser.mehdBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4;
this.writeHeader(stream);
stream.writeUint32(this.fragment_duration);
}
BoxParser.trexBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4*5;
this.writeHeader(stream);
stream.writeUint32(this.track_id);
stream.writeUint32(this.default_sample_description_index);
stream.writeUint32(this.default_sample_duration);
stream.writeUint32(this.default_sample_size);
stream.writeUint32(this.default_sample_flags);
}
BoxParser.mfhdBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4;
this.writeHeader(stream);
stream.writeUint32(this.sequence_number);
}
BoxParser.tfhdBox.prototype.write = function(stream) {
this.version = 0;
this.size = 4;
if (this.flags & BoxParser.TFHD_FLAG_BASE_OFFSET) {
this.size += 8;
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC) {
this.size += 4;
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR) {
this.size += 4;
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE) {
this.size += 4;
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS) {
this.size += 4;
}
this.writeHeader(stream);
stream.writeUint32(this.track_id);
if (this.flags & BoxParser.TFHD_FLAG_BASE_OFFSET) {
stream.writeUint64(this.base_data_offset);
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC) {
stream.writeUint32(this.default_sample_description_index);
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR) {
stream.writeUint32(this.default_sample_duration);
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE) {
stream.writeUint32(this.default_sample_size);
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS) {
stream.writeUint32(this.default_sample_flags);
}
}
BoxParser.trunBox.prototype.write = function(stream) {
this.version = 0;
this.size = 4;
if (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) {
this.size += 4;
}
if (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) {
this.size += 4;
}
if (this.flags & BoxParser.TRUN_FLAGS_DURATION) {
this.size += 4*this.sample_duration.length;
}
if (this.flags & BoxParser.TRUN_FLAGS_SIZE) {
this.size += 4*this.sample_size.length;
}
if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) {
this.size += 4*this.sample_flags.length;
}
if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) {
this.size += 4*this.sample_composition_time_offset.length;
}
this.writeHeader(stream);
stream.writeUint32(this.sample_count);
if (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) {
this.data_offset_position = stream.position;
stream.writeInt32(this.data_offset); //signed
}
if (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) {
stream.writeUint32(this.first_sample_flags);
}
for (var i = 0; i < this.sample_count; i++) {
if (this.flags & BoxParser.TRUN_FLAGS_DURATION) {
stream.writeUint32(this.sample_duration[i]);
}
if (this.flags & BoxParser.TRUN_FLAGS_SIZE) {
stream.writeUint32(this.sample_size[i]);
}
if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) {
stream.writeUint32(this.sample_flags[i]);
}
if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) {
if (this.version === 0) {
stream.writeUint32(this.sample_composition_time_offset[i]);
} else {
stream.writeInt32(this.sample_composition_time_offset[i]); //signed
}
}
}
}
BoxParser.tfdtBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4;
this.writeHeader(stream);
if (this.version == 1) {
stream.writeUint64(this.baseMediaDecodeTime);
} else {
stream.writeUint32(this.baseMediaDecodeTime);
}
}