DataView.prototype.getString = function(offset, length) { text = ''; let end = offset + length; for (let i = offset; i < end; i++) { text += String.fromCharCode(this.getUint8(i)); } return text; } class MP4Tree { constructor(data) { this.data = new DataView(data); this.codecs = []; this.idx = 0; this.parse_codecs(); } parse_codecs() { this.parse_into('moov', 8); while (this.idx < this.data.byteLength) { if (!this.parse_until('trak')) { break; } const curr_track_end = this.idx + this.read_next_head().len; this.idx += 8 this.parse_into('mdia', 8); this.parse_into('minf', 8); const track_type_head = this.read_next_head().name; if (track_type_head == 'smhd' || track_type_head == 'vmhd') { this.parse_into('stbl', 8); this.parse_into('stsd', 16); const media_sample = this.read_next_head().name; if (media_sample == 'avc1') { this.parse_avc1(); } else if (media_sample == 'mp4a') { this.parse_mp4a(); } } this.idx = curr_track_end; } } parse_avc1() { this.parse_into('avc1', 86); console.log(this.read_next_head()); } parse_mp4a() { this.parse_into('mp4a', 36); console.log(this.read_next_head()); } get_mime() { return 'video/mp4; codecs="' + this.get_codec_string() + '"'; } get_codec_string() { return this.codecs.join(', '); } parse_into(name, header_offset) { this.parse_until(name); this.idx += header_offset; } parse_until(name) { let curr_head = null; while (this.idx + 8 < this.data.byteLength) { curr_head = this.read_next_head(); if (curr_head.name == name) { return true; } else { this.idx += curr_head.len; } } return false; } read_next_head() { return {"len": this.data.getUint32(this.idx), "name": this.data.getString(this.idx + 4, 4)}; } }