UNPKG

mp4reader

Version:

MP4 parser, parsed according to the standard, borrows the implementation of MP4 package

1,075 lines (995 loc) 30.9 kB
// 引入fs的promise形式,避免使用回调 let fsPromise = require( 'fs/promises'); // let path = require('path') let mp4Info = { ftyp: {}, moov:{} } //判断是否是0x00 00 00 01 function FindStartCode2(buffer = Buffer.from([])){ if(!buffer.length){ return false } if(buffer[0] !=0 || buffer[1] != 0 || buffer[2] != 1){ return false }else{ return true } } //判断是否是0x00 00 00 01 function FindStartCode3(buffer = Buffer.from([])){ if(!buffer.length){ return false } if(buffer[0] !=0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 1){ return false }else{ return true } } // 获取文件基本信息,使用同步的方法,成功时将会打印文件信息并返回,错误时返回空对象 //目的是为了获取文件大小,便于下一步管理buffer async function getFileInfoAsync(filename = ''){ try{ let stat = await fsPromise.stat(filename) // console.log('fileInfo:',stat) return stat } catch(err){ return {} } } // 找到moov盒子 async function Mp4FindMoov(filename = ''){ if(filename == ''){ return -1 } // filename = path.resolve(filename); let offset = 0 const MAX_BUFFER_LEN = 1024 * 1024 //1MB 空间 let buff = Buffer.alloc(MAX_BUFFER_LEN) //共享内存 for(let k =0 ;k < MAX_BUFFER_LEN * 1000; k++){ let filehandle = await fsPromise.open(filename) let { buffer , bytesRead} =await filehandle.read(buff, 0, MAX_BUFFER_LEN, offset ) filehandle.close() if(bytesRead == 0){ return -1 //'没找到' break }else{ let result = buffer.indexOf('moov') if(result > 0){ return { offset:offset + result - 4, size:buff.readUInt32BE(result-4), } }else{ // 继续下一个循环 } } offset += MAX_BUFFER_LEN } } //解析moov盒子 async function Mp4DecodeMoov(filename = ''){ let moovInfo = await Mp4FindMoov(filename) if(moovInfo == -1){ // console.error('cannot find moov box') return -1 } // Object.assign(mp4Info.moov , moovInfo) let {size = 0, offset = 0} = moovInfo if(filename == '' || size === 0){ // console.error('mp4 moov box info error') return -1 } let filehandle = await fsPromise.open(filename,'r') let buff = Buffer.alloc(size) let { buffer:moov_buffer , bytesRead} =await filehandle.read(buff, 0, size, offset ) filehandle.close() if(bytesRead == 0){ return -1 //'没找到' } let mvhd = parse_mvhd(moov_buffer, offset) let udta = parse_udta(moov_buffer, offset) let trak = parse_trak(moov_buffer, offset) return { ...moovInfo, mvhd: mvhd, udta:udta, trak:trak } } //解析mvhd盒子 var parse_mvhd = function( moov_buffer = Buffer.from([]),base_offset = 0) { // console.log("MVHD"); let offset = moov_buffer.indexOf('mvhd') if(offset < 0){ return -1 //没有mvhd盒子 } offset -= 4 // console.log(moov_buffer.slice(offset,offset+ 50)) let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes let ctime = 0; let mtime = 0; let scale = 0; let duration = 0; if (version==0) { ctime = time1904To1970(moov_buffer.readUInt32BE(offset + 12)); // read 4 bytes unpacked N mtime = time1904To1970(moov_buffer.readUInt32BE(offset + 16)); // read 4 bytes unpacked N scale = moov_buffer.readUInt32BE(offset + 20); // read 4 bytes unpacked N duration = moov_buffer.readUInt32BE(offset + 24); // read 4 bytes unpacked N } else if (version==1) { ctime = time1904To1970(moov_buffer.readDoubleBE(offset + 12)); // read 8 bytes unpacked Q mtime = time1904To1970(moov_buffer.readDoubleBE(offset + 20)); // read 8 bytes unpacked Q scale = moov_buffer.readUInt32BE(offset + 28); // read 4 bytes unpacked N duration = moov_buffer.readDoubleBE(offset + 32); // read 8 bytes unpacked Q } return { "ctime": ctime, "mtime": mtime, "scale": scale, "duration": duration, "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "version": version, "flags": flags, } } var parse_trak = function(moov_buffer = Buffer.from([]),base_offset = 0) { let trak = [] // console.log("TRAK"); // 这里比较特殊,可能有多个trak,必须找出所有trak let trak_offset_list = [] for(let i = 0;i< moov_buffer.length;){ let offset = moov_buffer.indexOf('trak',i) if(offset < 0){ break; } let size = moov_buffer.readUInt32BE(offset -4 ) trak_offset_list.push(offset) i = offset + size - 4 } // console.log('trak_offset_list',trak_offset_list) trak_offset_list.forEach(ele=> { let offset = ele offset -= 4 let size = moov_buffer.readUInt32BE(offset ) //read 4 bytes unpacked N let box_type = moov_buffer.slice(offset+4, offset + 8).toString() //read 4 bytes // 8 Bytes reserved; let tkhd = parse_tkhd(moov_buffer.slice(offset) ,offset + base_offset) let mdia = parse_mdia(moov_buffer.slice(offset) ,offset + base_offset) trak.push({ "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, tkhd:tkhd, mdia:mdia }) }); return trak } async function Mp4DecodeFtyp(filename = ''){ if(filename == ''){ return -1 } let offset = 0 // filename = path.resolve(filename); let buff = Buffer.alloc(32) let filehandle = await fsPromise.open(filename) let { buffer , bytesRead} =await filehandle.read(buff, 0, 32, offset ) filehandle.close() if(bytesRead == 0){ return -2 //'没找到' }else{ //按照mp4格式解析即可 // 4-7为ftyp 8-11为Major_brand 12-15为Minor_version 16-31为Compatible_brands let box_type = buffer.slice(4,8).toString() let Major_brand = buffer.slice(8,12).toString() let Minor_version = buffer.slice(12,16).readUInt32BE() let Compatible_brands = buffer.slice(16,31).toString() // console.log(box_type) if(box_type !== 'ftyp'){ return -3 }else{ return { Start_offset: 0, Box_type: box_type, Major_brand: Major_brand, Minor_version: Minor_version, Compatible_brands: Compatible_brands } } } } async function Mp4DecodeAll (filename = ''){ if(filename == ''){ return -1 } // step1 decode box ftyp let ftyp = await Mp4DecodeFtyp(filename) if(ftyp < 0){ console.error('mp4 wrong format') return -1 } Object.assign(mp4Info.ftyp , ftyp) // 合并ftyp // step2 decode box moov let moov=await Mp4DecodeMoov(filename) Object.assign(mp4Info.moov , moov) return mp4Info } function parse_tkhd(moov_buffer = Buffer.from([]),base_offset = 0){ // console.log("TKHD"); let offset = moov_buffer.indexOf('tkhd') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes let ctime = 0; let mtime = 0; let track_id = 0; let reserved = 0; let duration = 0; let layer = 0 //2 Byte let alternate_group = 0 //2 Byte let volume = 0 //2 Byte 整数.小数 let matrix = 0 //36 Byte let width = 0 //4 Byte let height = 0 // 4Byte if (version==0) { ctime = time1904To1970(moov_buffer.readUInt32BE(offset + 12)); // read 4 bytes unpacked N mtime = time1904To1970(moov_buffer.readUInt32BE(offset + 16)); // read 4 bytes unpacked N track_id = moov_buffer.readUInt32BE(offset + 20); // read 4 bytes unpacked N reserved = moov_buffer.readUInt32BE(offset + 24); // read 4 bytes unpacked N duration = moov_buffer.readUInt32BE(offset + 28); // read 4 bytes unpacked N //8 bytes reserved layer = moov_buffer.readUInt16BE(offset + 40) //2 alternate_group = moov_buffer.readUInt16BE(offset + 42) //2 volume = moov_buffer.readUInt16BE(offset + 44) //2 matrix = moov_buffer.slice(offset+46,offset + 82) //36 width = moov_buffer.readUInt32BE(offset + 82); // read 4 bytes height = moov_buffer.readUInt32BE(offset + 86); // read 4 bytes } else if (version==1) { ctime = time1904To1970(moov_buffer.readDoubleBE(offset + 12)); // read 8 bytes unpacked Q mtime = time1904To1970(moov_buffer.readDoubleBE(offset + 20)); // read 8 bytes unpacked Q track_id = moov_buffer.readUInt32BE(offset + 28); // read 4 bytes unpacked N reserved = moov_buffer.readUInt32BE(offset + 32); // read 4 bytes unpacked N duration = moov_buffer.readUInt32BE(offset + 36); // read 4 bytes unpacked Q //8 bytes reserved layer = moov_buffer.readUInt16BE(offset + 48) //2 alternate_group = moov_buffer.readUInt16BE(offset + 50) //2 volume = moov_buffer.readUInt16BE(offset + 52) //2 matrix = moov_buffer.slice(offset+54,offset + 90) //36 width = moov_buffer.readUInt32BE(offset + 90); // read 4 bytes height = moov_buffer.readUInt32BE(offset + 94); // read 4 bytes } return { "ctime": ctime, "mtime": mtime, "track_id": track_id, "duration": duration, "layer":layer, "alternate_group":alternate_group, "volume":volume, "matrix":matrix, "width":width, "height":height, "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "version": version, "flags": flags, } } function parse_mdia(moov_buffer = Buffer.from([]),base_offset = 0){ // console.log("MDIA"); let offset = moov_buffer.indexOf('mdia') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let mdhd = parse_mdhd(moov_buffer, base_offset ) let hdlr = parse_hdlr(moov_buffer, base_offset ) let minf = parse_minf(moov_buffer, base_offset ) return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, mdhd: mdhd, hdlr: hdlr, minf: minf } } function parse_mdhd(moov_buffer = Buffer.from([]),base_offset = 0){ // console.log("MDHD"); let offset = moov_buffer.indexOf('mdhd') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, } } //可以判断轨道类型,Handler type vide / soun function parse_hdlr(moov_buffer = Buffer.from([]),base_offset = 0){ // console.log("HDLR"); let offset = moov_buffer.indexOf('hdlr') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes let Handler_type = moov_buffer.slice(offset+16, offset + 20).toString() let stringLength = size for(let i = 32; i <= size; i++){ if(moov_buffer[offset + i] == 0x00){ stringLength = i break; } } let Name = moov_buffer.slice(offset+32 , offset+stringLength).toString() return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "version": version, "flags": flags, "Handler_type":Handler_type, "Name":Name } } function parse_minf(moov_buffer = Buffer.from([]),base_offset = 0){ // console.log("MINF"); let offset = moov_buffer.indexOf('minf') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let mhd = parse_mhd(moov_buffer,base_offset) // video: vmhd ; sound: smhd let box_name = 'vmhd' let dinf = parse_dinf(moov_buffer,base_offset) let stbl = parse_stbl(moov_buffer,base_offset) let Obj = { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, dinf: dinf, stbl: stbl } if(mhd !== -1 && mhd.Box_type == 'vmhd'){ Object.assign(Obj,{ vmhd: mhd }) }else if(mhd !== -1 && mhd.Box_type == 'smhd'){ Object.assign(Obj,{ smhd: mhd }) } return Obj } function parse_mhd(moov_buffer = Buffer.from([]),base_offset = 0){ // console.log("VMHD"); let offset = moov_buffer.indexOf('vmhd') if(offset == -1){ offset = moov_buffer.indexOf('smhd') } if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, } } function parse_dinf(moov_buffer = Buffer.from([]),base_offset = 0){ // console.log("DINF"); let offset = moov_buffer.indexOf('dinf') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "dref":-1 } } function parse_stbl(moov_buffer = Buffer.from([]),base_offset = 0){ // console.log("STBL"); let offset = moov_buffer.indexOf('stbl') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let stsd = parse_stsd(moov_buffer,base_offset) let stts = parse_stts(moov_buffer,base_offset) let stss = parse_stss(moov_buffer,base_offset) let ctts = parse_ctts(moov_buffer,base_offset) let stsc = parse_stsc(moov_buffer,base_offset) let stsz = parse_stsz(moov_buffer,base_offset) let stco = parse_stco(moov_buffer,base_offset) return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, stsd:stsd, stts:stts, stss: stss, ctts: ctts, stsc: stsc, stsz: stsz, stco: stco } } function parse_stsd(moov_buffer = Buffer.from([]),base_offset = 0){ let offset = moov_buffer.indexOf('stsd') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes let entry_count = moov_buffer.readUInt32BE(offset +12 ) let SampleEntry = moov_buffer.slice(offset+20, offset + 24).toString() switch (SampleEntry) { case 'avc1': let avc1 = parse_avc1(moov_buffer, base_offset) return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, entry_count:entry_count, SampleEntry:SampleEntry, avc1:avc1 } break; case 'mp4a': let mp4a = parse_mp4a(moov_buffer, base_offset) return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, SampleEntry:SampleEntry, mp4a:mp4a } break; case 'mp4v': let mp4v = parse_mp4v(moov_buffer, base_offset) return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, SampleEntry:SampleEntry, mp4v:mp4v } break; case 'hev1': // h265编码 let hev1 = parse_hev1(moov_buffer, base_offset) return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, SampleEntry:SampleEntry, hev1:hev1 } break; default: break; } return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, SampleEntry:SampleEntry } } function parse_hev1(moov_buffer = Buffer.from([]),base_offset = 0){ let offset = moov_buffer.indexOf('hev1') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, } } function parse_mp4v(){ return -1 } function parse_mp4a(moov_buffer = Buffer.from([]),base_offset = 0){ let offset = moov_buffer.indexOf('mp4a') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes // 8 bytes reserved 这里与一些文档的16字节有些不符 let Channel_count = moov_buffer.readUInt16BE(offset + 24) let Sample_size = moov_buffer.readUInt16BE(offset + 26) //4 Bytes reserved let Sample_rate = moov_buffer.readUInt32BE(offset + 30) return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "version": version, "flags": flags, "Channel_count":Channel_count, "Sample_size":Sample_size, "Sample_rate":Sample_rate, "esds": -1 } } function parse_avc1(moov_buffer = Buffer.from([]),base_offset = 0){ let offset = moov_buffer.indexOf('avc1') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes let width = moov_buffer.readUInt16BE(offset + 32) let height = moov_buffer.readUInt16BE(offset + 34) let Horiz_resolution = moov_buffer.readUInt32BE(offset + 36) let Ver_resolution = moov_buffer.readUInt32BE(offset + 36) // 4 Bytes reserved let Frame_count = moov_buffer.readUInt16BE(offset + 44) //每个采样中的帧数 let avcC = parse_avcC(moov_buffer, base_offset ) return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "version": version, "flags": flags, width:width, height: height, Horiz_resolution:Horiz_resolution, Ver_resolution: Ver_resolution, Frame_count: Frame_count, avcC: avcC } } function parse_avcC(moov_buffer = Buffer.from([]),base_offset = 0){ let offset = moov_buffer.indexOf('avcC') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let Configuration_version = moov_buffer[offset + 8]; // read 1 bytes 8bit let AVC_profile_indication = moov_buffer[offset + 9] // read 1 bytes 8bit let AVC_profile_compatibility = moov_buffer[offset + 10] // read 1 bytes 8bit let AVC_level_indication = moov_buffer[offset + 11] // read 1 bytes 8bit // 5 Bit reserved let NALU_length_size = (moov_buffer[offset + 12] & 0x03) + 1 //3 Bit reserved // let Num_sequence_parameter_sets = moov_buffer[offset + 13] let Num_sequence_parameter_sets = (moov_buffer[offset + 13] & 0x1F) //sometime maybe not only one SPS / PPS, decided by Num_sequence_parameter_sets let SPS = []//Sequence parameter set let point = offset + 14 for(let i = 0 ;i< Num_sequence_parameter_sets; i++){ let len = moov_buffer.readUint16BE(point) let sps_array =moov_buffer.slice(point + 2, point + 2 + len ) SPS.push(...sps_array) //Buffer to array point = point + len +2 } let Num_picture_parameter_sets = moov_buffer[point] let PPS = [] for(let i = 0 ;i< Num_picture_parameter_sets; i++){ let len = moov_buffer.readUint16BE(point + 1) let pps_array = Uint8Array.from(moov_buffer.slice(point + 3, point + 3 + len )) PPS.push(...pps_array) //Buffer to array point = point + len +2 } return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "Configuration_version": Configuration_version, "AVC_profile_indication": AVC_profile_indication, "AVC_profile_compatibility": AVC_profile_compatibility, "AVC_level_indication": AVC_level_indication, NALU_length_size: NALU_length_size, SPS: SPS, PPS: PPS, Num_sequence_parameter_sets:Num_sequence_parameter_sets, Num_picture_parameter_sets: Num_picture_parameter_sets } } function parse_stts(moov_buffer = Buffer.from([]),base_offset = 0){ let offset = moov_buffer.indexOf('stts') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes let count = moov_buffer.readUInt32BE(offset + 12) //time-to-sample count let Time_to_sample = [] for(let i = 0;i < count;i++){ Time_to_sample.push({ No: i+1, Sample_count: moov_buffer.readUInt32BE(offset + 16 + i * 8), Sample_duration: moov_buffer.readUInt32BE(offset + 16 + 4+ i * 8), }) } return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "version": version, "flags": flags, count: count, Time_to_sample: Time_to_sample } } //stss确定 media 中的关键帧 function parse_stss(moov_buffer = Buffer.from([]),base_offset = 0){ let offset = moov_buffer.indexOf('stss') if(offset < 0){ return -1 //音频是没有stss的 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes let count = moov_buffer.readUInt32BE(offset + 12) let Sample_list = [] for(let i = 0;i < count;i++){ No: i+1, Sample_list.push(moov_buffer.readUInt32BE(offset + 16 + i * 4)) } return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "version": version, "flags": flags, count: count, Sample_list: Sample_list } } function parse_ctts(moov_buffer = Buffer.from([]),base_offset = 0){ let offset = moov_buffer.indexOf('ctts') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes let count = moov_buffer.readUInt32BE(offset + 12) let Sample_list = [] for(let i = 0;i < count;i++){ Sample_list.push({ No: i + 1, Sample_count: moov_buffer.readUInt32BE(offset + 16 + i * 8), Sample_offset: moov_buffer.readUInt32BE(offset + 16 + 4 + i * 8), }) } return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "version": version, "flags": flags, count: count, Sample_list: Sample_list } } //sample-to-chunk function parse_stsc(moov_buffer = Buffer.from([]),base_offset = 0){ let offset = moov_buffer.indexOf('stsc') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes let count = moov_buffer.readUInt32BE(offset + 12) let Sample_to_chunk = [] for(let i = 0;i < count;i++){ Sample_to_chunk.push({ No: i+1, First_chunk: moov_buffer.readUInt32BE(offset + 16 + i * 8), Sample_perchunk: moov_buffer.readUInt32BE(offset + 16 + 4+ i * 8), Sample_description_index: moov_buffer.readUInt32BE(offset + 16 + 8+ i * 8), }) } return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "version": version, "flags": flags, count: count, Sample_to_chunk: Sample_to_chunk } } //Sample Size Boxes function parse_stsz(moov_buffer = Buffer.from([]),base_offset = 0){ let offset = moov_buffer.indexOf('stsz') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes let Sample_size = moov_buffer.readUInt32BE(offset + 12) let Sample_count = moov_buffer.readUInt32BE(offset + 16) let Sample_size_list = [] if(Sample_size == 0){ for(let i = 0;i < Sample_count;i++){ Sample_size_list.push({ No: i+1, Sample_size: moov_buffer.readUInt32BE(offset + 20 + i * 4), }) } } return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "version": version, "flags": flags, Sample_size: Sample_size, Sample_count: Sample_count, Sample_size_list: Sample_size_list } } //Chunk Offset Box function parse_stco(moov_buffer = Buffer.from([]),base_offset = 0){ let offset = moov_buffer.indexOf('stco') if(offset < 0){ return -1 } offset -= 4 let size = moov_buffer.readUInt32BE(offset ) let box_type = moov_buffer.slice(offset+4, offset + 8).toString() let version = moov_buffer[offset + 8]; // read 1 bytes 8bit unpacked C let flags = (moov_buffer.readUInt32BE(offset + 8) << 8 )>>8; // read 3 bytes let count = moov_buffer.readUInt32BE(offset + 12) //time-to-sample count let Chunk_offset_list = [] for(let i = 0;i < count;i++){ Chunk_offset_list.push({ No: i+1, Chunk_offset: moov_buffer.readUInt32BE(offset + 16 + i * 4), }) } return { "Start_offset": offset + base_offset, "Box_size": size, "Box_type": box_type, "version": version, "flags": flags, count: count, Chunk_offset_list: Chunk_offset_list } } function parse_udta(moov_buffer = Buffer.from([]),base_offset = 0){ // console.log('UDTA') return -1 } function time1904To1970 (UTC = 0){ return new Date(UTC - 2082844800000).toLocaleString() } //传入配置参数 async function Mp4DecodeByModule(filename = '' ,configOption = ['ftyp']){ if(typeof configOption != 'object' || filename == ''){ return -1 } let option = { ftyp : false, moov : false, mvhd : false, trak : false, tkhd : false, mdia : false, hdlr : false, minf : false, stbl : false, stsd : false, stts : false, stss : false, ctts : false, stsc : false, stsz : false, stco : false, } configOption.forEach(el => { if(option.hasOwnProperty(el)){ option[el] = true } }) let resdata = {} let moovInfo = await Mp4FindMoov(filename) // Object.assign(resdata , {...moovInfo}) let {size = 0, offset = 0} = moovInfo if(size === 0 || moovInfo == -1){ return -1 } let filehandle = await fsPromise.open(filename,'r') let buff = Buffer.alloc(size) let { buffer:moov_buffer , bytesRead} =await filehandle.read(buff, 0, size, offset ) filehandle.close() if(bytesRead == 0){ return -1 } for(let key in option){ switch (key){ case 'ftyp': if(option[key]){ let ftyp = await Mp4DecodeFtyp(filename) Object.assign(resdata , {ftyp : ftyp}) } break case 'moov': if(option[key]){ let mvhd = parse_mvhd(moov_buffer, offset) let udta = parse_udta(moov_buffer, offset) let trak = parse_trak(moov_buffer, offset) Object.assign(resdata , { moov: { ...moovInfo, mvhd: mvhd, udta:udta, trak:trak } }) } break case 'mvhd': if(option[key]){ let mvhd = parse_mvhd(moov_buffer, offset) Object.assign(resdata , {mvhd:mvhd}) } break case 'trak': if(option[key]){ let trak = parse_trak(moov_buffer, offset) Object.assign(resdata , {trak: trak}) } break case 'tkhd': case 'mdia': case 'hdlr': case 'minf': case 'stbl': case 'stsd': case 'stts': case 'stss': case 'ctts': case 'stsc': case 'stsz': case 'stco': if(option[key]){ let trak_offset_list = getTrackList(moov_buffer, offset) let base_offset = offset let box_data = [] trak_offset_list.forEach(ele=> { let offsetB = ele offsetB -= 4 let size = moov_buffer.readUInt32BE(offsetB ) //read 4 bytes unpacked N let box_type = moov_buffer.slice(offsetB+4, offsetB + 8).toString() //read 4 bytes // 8 Bytes reserved; let res = parse_exact_box(moov_buffer.slice(offsetB) ,offsetB + base_offset, key) box_data.push(res) }); let obj = {} obj[key] = box_data Object.assign(resdata , obj) } break; default: break; } } return resdata } function parse_exact_box(buffer = [] ,offset = 0, key = null){ switch(key){ case 'tkhd': return parse_tkhd(buffer , offset ) case 'mdia': return parse_mdia(buffer , offset ) case 'hdlr': return parse_hdlr(buffer , offset ) case 'minf': return parse_minf(buffer , offset ) case 'stbl': return parse_stbl(buffer , offset ) case 'stsd': return parse_stsd(buffer , offset ) case 'stts': return parse_stts(buffer , offset ) case 'stss': return parse_stss(buffer , offset ) case 'ctts': return parse_ctts(buffer , offset ) case 'stsc': return parse_stsc(buffer , offset ) case 'stsz': return parse_stsz(buffer , offset ) case 'stco': return parse_stco(buffer , offset ) } return } //返回轨道的偏移量数组,可以明确知道有几条轨道 function getTrackList (moov_buffer = Buffer.from([]),base_offset = 0){ let trak = [] // console.log("TRAK"); // 这里比较特殊,可能有多个trak,必须找出所有trak let trak_offset_list = [] for(let i = 0;i< moov_buffer.length;){ let offset = moov_buffer.indexOf('trak',i) if(offset < 0){ break; } let size = moov_buffer.readUInt32BE(offset -4 ) trak_offset_list.push(offset) i = offset + size - 4 } return trak_offset_list // console.log('trak_offset_list',trak_offset_list) } module.exports = { FindStartCode2:FindStartCode2, FindStartCode3:FindStartCode3, getFileInfoAsync:getFileInfoAsync, Mp4FindMoov: Mp4FindMoov, Mp4DecodeFtyp: Mp4DecodeFtyp, Mp4DecodeAll: Mp4DecodeAll, Mp4DecodeByModule:Mp4DecodeByModule }