Administrator
Administrator
Published on 2024-11-30 / 16 Visits
0
0

golang取视频文件宽高和时长

通过搜索关键字找到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
}


Comment