【Go】FLV文件解析(四)

终于到了完结的时刻,这期我们来解析FLV中的视频Tag。

Video Tag Data

视频Tag的总体结构和音频Tag相似,视频的Tag Data结构如下。

FLV Video Tag Data

第一个字节的前4比特表示帧类型,共5种。

类型说明
1key frame关键帧
2inter frame非关键帧
3disposable inter frame仅H.263
4generated keyframe保留
5video info/command frame命令帧

如果帧类型为5,消息流包含一个8位无符号整数,含义如下:

  • 0:Start of client-side seeking video frame sequence

  • 1:End of client-side seeking video frame sequence

后4比特表示视频编码格式,共7种。

类型说明
1JPEG
2Sorenson H.263
3Screen video
4On2 VP6
5On2 VP6 with alpha channel
6Screen video version 2
7AVC

视频数据根据编码格式而不同,以AVC为例,结构如下。

AVC包类型有3种。

类型说明
0AVC sequence headersps,pps
1AVC NALU
2AVC end of sequence视频数据部分为空

当AVC包类型等于0或2时,CompositionTime为0。AVC包类型为0时,负载部分是一个叫AVCDecoderConfigurationRecord的结构,它在ISO 14496-15, 5.2.4.1中定义。其基本结构如下。

关于SPS和PPS的具体结构属于H264编码的内容,涉及到指数哥伦布编码,这里就不再展开了,有机会在讲H264编码时在详细介绍。

AVC包类型为1时,负载部分是NALUs,其基本结构是长度+NALU[+长度+NALU]。NALU里面就是经过编码的视频数据。

实战

第一步还是先定义出需要的数据结构。

type FlvVideoTag struct {
	Header    FlvTagHeader
	FrameType uint8
	CodecID   uint8
	Data      stream.Stream
}

type AVCPacket struct {
	AVCPacketType         uint8
	CompositionTimeOffset uint32
	Data                  stream.Stream
}

type AVCDecoderConfigurationRecord struct {
	ConfigurationVersion uint8
	AVCProfileIndication uint8
	ProfileCompatibility uint8
	AVCLevelIndication   uint8
	LengthSizeMinusOne   uint8
	NumOfSPS             uint8
	SPSLength            uint16
	SPS                  []byte
	NumOfPPS             uint8
	PPSLength            uint16
	PPS                  []byte
}

接下来是解码代码。

// 解码FLV视频Tag
func DecodeFlvVideoTag(t FlvTag) (v FlvVideoTag, err error) {
	v.Header = t.Header
	var b byte
	if err = t.Data.Byte(&b).Error(); err != nil {
		return
	}
	v.FrameType = b >> 4 & 0x0F
	v.CodecID = b & 0x0F
	v.Data, err = t.Data.Produce(t.Data.Remain())
	return
}

// 解码AVC包
func DecodeAVCPacket(t FlvVideoTag) (p AVCPacket, err error) {
	if t.CodecID != CODEC_AVC {
		err = errors.New("not avc format")
	}
	err = t.Data.U8(&p.AVCPacketType).
		U24(&p.CompositionTimeOffset).
		Error()
	if err != nil {
		return
	}
	p.Data, err = t.Data.Produce(t.Data.Remain())
	return
}

// 解码AVCDecoderConfigurationRecord
func DecodeAVCDecoderConfigurationRecord(p AVCPacket) (a AVCDecoderConfigurationRecord, err error) {
	err = p.Data.U8(&a.ConfigurationVersion).
		U8(&a.AVCProfileIndication).
		U8(&a.ProfileCompatibility).
		U8(&a.AVCLevelIndication).
		U8(&a.LengthSizeMinusOne).
		U8(&a.NumOfSPS).
		U16(&a.SPSLength).
		Error()
	a.LengthSizeMinusOne &= 0x03
	a.NumOfSPS &= 0x1F
	if err != nil {
		return
	}
	if a.SPS, err = p.Data.Slice(int(a.SPSLength)); err != nil {
		return
	}
	if err = p.Data.U8(&a.NumOfPPS).U16(&a.PPSLength).Error(); err != nil {
		return
	}
	a.PPS, err = p.Data.Slice(int(a.PPSLength))
	return
}

关于FLV文件的格式和解析就介绍到这里,完结撒花。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值