通过搜索关键字找到byte索引,加上固定偏移量找到数据,写了三种常用的视频文件,MP4、AVI、FLV
涉及一个知识点:大端序和小端序
常用是大端序,这也符合人类的书写习惯,比如数字一千,大端序写作:1000。而小端序写作:0001低字节数据存放在低地址处,高字节数据存放在高地址处,(左为低,右为高),个位在前面,千位在最后面
func getMp4Duration(filename string) (width uint32, height uint32, duration float64, err error) {
var b200 = make([]byte, 300)
var file, _ = os.Open(filename)
defer file.Close()
_, err = file.ReadAt(b200, 0)
if err != nil {
return
}
var index = bytes.Index(b200, []byte{109, 118, 104, 100}) //mvhd
var b = make([]byte, 4)
_, err = file.ReadAt(b, int64(index+16))
if err != nil {
return
}
timeScale := binary.BigEndian.Uint32(b) //时间刻度
//总帧数
_, err = file.ReadAt(b, int64(index+20))
if err != nil {
return
}
duration_ := binary.BigEndian.Uint32(b) //时长,单位为timeScale
index = bytes.Index(b200, []byte("tkhd"))
_, err = file.ReadAt(b, int64(index+80))
if err != nil {
return
}
width = binary.BigEndian.Uint32(b) / 65536
_, err = file.ReadAt(b, int64(index+84))
if err != nil {
return
}
height = binary.BigEndian.Uint32(b) / 65536
duration = float64(duration_) / float64(timeScale)
return
}
func getAviDuration(filename string) (width uint32, height uint32, duration float64, err error) {
//AVI通常使用RIFF文件格式,RIFF文件格式是一种通用的文件格式,它由一系列的块组成,每个块由一个四字节的标签和一个四字节的长度字段组成。
// avi文件固定为小端序(little-endian),低位字节在前,高位字节在后。如abcd在内存中存储为{64,63,62,61},十进制56存储为{56,0,0,0}
// 文档:https://blog.csdn.net/qq446252221/article/details/12773259/
var b200 = make([]byte, 200)
var file, _ = os.Open(filename)
_, err = file.ReadAt(b200, 0)
if err != nil {
return
}
defer file.Close()
var index = bytes.Index(b200, []byte{97, 118, 105, 104}) //avih
if index == -1 {
return
}
var b = make([]byte, 4)
//每帧的时间间隔,单位微秒
_, err = file.ReadAt(b, int64(index+8))
if err != nil {
return
}
dwMicroSecPerFrame := binary.LittleEndian.Uint32(b)
//总帧数
_, err = file.ReadAt(b, int64(index+24))
if err != nil {
return
}
frames := binary.LittleEndian.Uint32(b)
_, err = file.ReadAt(b, int64(index+40))
if err != nil {
return
}
width = binary.LittleEndian.Uint32(b)
_, err = file.ReadAt(b, int64(index+44))
if err != nil {
return
}
height = binary.LittleEndian.Uint32(b)
duration = (float64(dwMicroSecPerFrame) * float64(frames)) / 1_000_000.0
return
}
func getFlvDuration(filename string) (width uint32, height uint32, duration float64, err error) {
var file, _ = os.Open(filename)
defer file.Close()
var b200 = make([]byte, 200)
_, err = file.ReadAt(b200, 0)
if err != nil {
return
}
var flag = []byte{100, 117, 114, 97, 116, 105, 111, 110} //duration
var pos = bytes.Index(b200, flag)
var b = make([]byte, 8)
_, err = file.ReadAt(b, int64(pos+1+len(flag)))
if err != nil {
return
}
err = binary.Read(bytes.NewReader(b), binary.BigEndian, &duration) //IEEE 754
if err != nil {
return
}
pos = bytes.Index(b200, []byte("width"))
_, err = file.ReadAt(b, int64(pos+1+len("width")))
if err != nil {
return
}
var width_ float64
err = binary.Read(bytes.NewReader(b), binary.BigEndian, &width_) //IEEE 754
if err != nil {
return
}
width = uint32(width_)
pos = bytes.Index(b200, []byte("height"))
_, err = file.ReadAt(b, int64(pos+1+len("height")))
if err != nil {
return
}
var height_ float64
err = binary.Read(bytes.NewReader(b), binary.BigEndian, &height_) //IEEE 754
if err != nil {
return
}
height = uint32(height_)
return
}