UNPKG

mp4box

Version:

JavaScript version of GPAC's MP4Box tool

228 lines (211 loc) 8.26 kB
ISOFile.prototype.add = BoxParser.Box.prototype.add; ISOFile.prototype.addBox = BoxParser.Box.prototype.addBox; ISOFile.prototype.init = function (_options) { var options = _options || {}; var ftyp = this.add("ftyp").set("major_brand", (options.brands && options.brands[0]) || "iso4") .set("minor_version", 0) .set("compatible_brands", options.brands || ["iso4"]); var moov = this.add("moov"); moov.add("mvhd").set("timescale", options.timescale || 600) .set("rate", options.rate || 1<<16) .set("creation_time", 0) .set("modification_time", 0) .set("duration", options.duration || 0) .set("volume", (options.width) ? 0 : 0x0100) .set("matrix", [ 1<<16, 0, 0, 0, 1<<16, 0, 0, 0, 0x40000000]) .set("next_track_id", 1); moov.add("mvex"); return this; } ISOFile.prototype.addTrack = function (_options) { if (!this.moov) { this.init(_options); } var options = _options || {}; options.width = options.width || 320; options.height = options.height || 320; options.id = options.id || this.moov.mvhd.next_track_id; options.type = options.type || "avc1"; var trak = this.moov.add("trak"); this.moov.mvhd.next_track_id = options.id+1; trak.add("tkhd").set("flags",BoxParser.TKHD_FLAG_ENABLED | BoxParser.TKHD_FLAG_IN_MOVIE | BoxParser.TKHD_FLAG_IN_PREVIEW) .set("creation_time",0) .set("modification_time", 0) .set("track_id", options.id) .set("duration", options.duration || 0) .set("layer", options.layer || 0) .set("alternate_group", 0) .set("volume", 1) .set("matrix", [ 1<<16, 0, 0, 0, 1<<16, 0, 0, 0, 0x40000000 ]) .set("width", options.width << 16) .set("height", options.height << 16); var mdia = trak.add("mdia"); mdia.add("mdhd").set("creation_time", 0) .set("modification_time", 0) .set("timescale", options.timescale || 1) .set("duration", options.media_duration || 0) .set("language", options.language || "und"); mdia.add("hdlr").set("handler", options.hdlr || "vide") .set("name", options.name || "Track created with MP4Box.js"); mdia.add("elng").set("extended_language", options.language || "fr-FR"); var minf = mdia.add("minf"); if (BoxParser[options.type+"SampleEntry"] === undefined) return; var sample_description_entry = new BoxParser[options.type+"SampleEntry"](); sample_description_entry.data_reference_index = 1; var media_type = ""; for (var mediaType in BoxParser.sampleEntryCodes) { var codes = BoxParser.sampleEntryCodes[mediaType]; for (var i = 0; i < codes.length; i++) { if (codes.indexOf(options.type) > -1) { media_type = mediaType; break; } } } switch(media_type) { case "Visual": minf.add("vmhd").set("graphicsmode",0).set("opcolor", [ 0, 0, 0 ]); sample_description_entry.set("width", options.width) .set("height", options.height) .set("horizresolution", 0x48<<16) .set("vertresolution", 0x48<<16) .set("frame_count", 1) .set("compressorname", options.type+" Compressor") .set("depth", 0x18); if (options.avcDecoderConfigRecord) { var avcC = new BoxParser.avcCBox(); avcC.parse(new MP4BoxStream(options.avcDecoderConfigRecord)); sample_description_entry.addBox(avcC); } else if (options.hevcDecoderConfigRecord) { var hvcC = new BoxParser.hvcCBox(); hvcC.parse(new MP4BoxStream(options.hevcDecoderConfigRecord)); sample_description_entry.addBox(hvcC); } break; case "Audio": minf.add("smhd").set("balance", options.balance || 0); sample_description_entry.set("channel_count", options.channel_count || 2) .set("samplesize", options.samplesize || 16) .set("samplerate", options.samplerate || 1<<16); break; case "Hint": minf.add("hmhd"); // TODO: add properties break; case "Subtitle": minf.add("sthd"); switch (options.type) { case "stpp": sample_description_entry.set("namespace", options.namespace || "nonamespace") .set("schema_location", options.schema_location || "") .set("auxiliary_mime_types", options.auxiliary_mime_types || ""); break; } break; case "Metadata": minf.add("nmhd"); break; case "System": minf.add("nmhd"); break; default: minf.add("nmhd"); break; } if (options.description) { sample_description_entry.addBox(options.description); } if (options.description_boxes) { options.description_boxes.forEach(function (b) { sample_description_entry.addBox(b); }); } minf.add("dinf").add("dref").addEntry((new BoxParser["url Box"]()).set("flags", 0x1)); var stbl = minf.add("stbl"); stbl.add("stsd").addEntry(sample_description_entry); stbl.add("stts").set("sample_counts", []) .set("sample_deltas", []); stbl.add("stsc").set("first_chunk", []) .set("samples_per_chunk", []) .set("sample_description_index", []); stbl.add("stco").set("chunk_offsets", []); stbl.add("stsz").set("sample_sizes", []); this.moov.mvex.add("trex").set("track_id", options.id) .set("default_sample_description_index", options.default_sample_description_index || 1) .set("default_sample_duration", options.default_sample_duration || 0) .set("default_sample_size", options.default_sample_size || 0) .set("default_sample_flags", options.default_sample_flags || 0); this.buildTrakSampleLists(trak); return options.id; } BoxParser.Box.prototype.computeSize = function(stream_) { var stream = stream_ || new DataStream(); stream.endianness = DataStream.BIG_ENDIAN; this.write(stream); } ISOFile.prototype.addSample = function (track_id, data, _options) { var options = _options || {}; var sample = {}; var trak = this.getTrackById(track_id); if (trak === null) return; sample.number = trak.samples.length; sample.track_id = trak.tkhd.track_id; sample.timescale = trak.mdia.mdhd.timescale; sample.description_index = (options.sample_description_index ? options.sample_description_index - 1: 0); sample.description = trak.mdia.minf.stbl.stsd.entries[sample.description_index]; sample.data = data; sample.size = data.byteLength; sample.alreadyRead = sample.size; sample.duration = options.duration || 1; sample.cts = options.cts || 0; sample.dts = options.dts || 0; sample.is_sync = options.is_sync || false; sample.is_leading = options.is_leading || 0; sample.depends_on = options.depends_on || 0; sample.is_depended_on = options.is_depended_on || 0; sample.has_redundancy = options.has_redundancy || 0; sample.degradation_priority = options.degradation_priority || 0; sample.offset = 0; sample.subsamples = options.subsamples; trak.samples.push(sample); trak.samples_size += sample.size; trak.samples_duration += sample.duration; if (trak.first_dts === undefined) { trak.first_dts = options.dts; } this.processSamples(); var moof = this.createSingleSampleMoof(sample); this.addBox(moof); moof.computeSize(); /* adjusting the data_offset now that the moof size is known*/ moof.trafs[0].truns[0].data_offset = moof.size+8; //8 is mdat header this.add("mdat").data = new Uint8Array(data); return sample; } ISOFile.prototype.createSingleSampleMoof = function(sample) { var sample_flags = 0; if (sample.is_sync) sample_flags = (1 << 25); // sample_depends_on_none (I picture) else sample_flags = (1 << 16); // non-sync var moof = new BoxParser.moofBox(); moof.add("mfhd").set("sequence_number", this.nextMoofNumber); this.nextMoofNumber++; var traf = moof.add("traf"); var trak = this.getTrackById(sample.track_id); traf.add("tfhd").set("track_id", sample.track_id) .set("flags", BoxParser.TFHD_FLAG_DEFAULT_BASE_IS_MOOF); traf.add("tfdt").set("baseMediaDecodeTime", (sample.dts - (trak.first_dts || 0))); traf.add("trun").set("flags", BoxParser.TRUN_FLAGS_DATA_OFFSET | BoxParser.TRUN_FLAGS_DURATION | BoxParser.TRUN_FLAGS_SIZE | BoxParser.TRUN_FLAGS_FLAGS | BoxParser.TRUN_FLAGS_CTS_OFFSET) .set("data_offset",0) .set("first_sample_flags",0) .set("sample_count",1) .set("sample_duration",[sample.duration]) .set("sample_size",[sample.size]) .set("sample_flags",[sample_flags]) .set("sample_composition_time_offset", [sample.cts - sample.dts]); return moof; }