H264文件分析
要想深入学习视频,就必须对h264文件有所了解,我就以前面海思摄像头生成的h264文件为例说明一下h264的基本知识,以方便后期rtsp传输视频时对h264打包。
附件:Hi3518编码H264
请先下载附件里的文件,里面有源代码、生成的h264文件、调试输出的打印信息。我们首先打开“h264数据分析”文件夹里的“log20171229103150_printf.txt”文件,这个文件是调试输出的打印信息。从打印信息可以看出摄像头是AR0130,720P,30fps,这种图片格式需要的内存大小为u32BlkSize=1491840。下面的一系列数据就是hi3518生成h264数据时的打印信息。在结合打印信息说明之前先说明一些h264的基本知识,可能不全,但是以易理解,平时用得最多的概念为主。h264编码过程中的三种不同的数据形式,一般以原始字节序列载荷RBSP为主;h264的功能分为两层,视频编码层(VCL)和网络提取层(NAL),我们常关注网络提取层(NAL),H264在网络传输的也是NALU;一个原始的H.264 NALU 单元常由 [StartCode] [NALU Header] [NALU Payload] 三部分组成
StartCode:00 00 00 01
NAL Header:占一个字节,由三部分组成forbidden_bit(1bit),nal_reference_bit(2bits)(优先级),nal_unit_type(5bits)(类型)
最高bit为forbidden_bit,低5bit为nal类型,剩下两bit为优先级。
如:00 00 00 01 67 42 00 1F 9D A8 14 01 6E 9B 80 80 80 81
起始码就是前面4个字节00 00 00 01,第5个字节就是0x67=(01101111),这里具体的可以百度,我们先暂时记住以下结论:
0x67 A&0x1f==7:sps
0x68 A&0x1f==8:pps
0x65 A&0x1f==5:关键帧
0xZ1 A&0x1f==1:非关键帧
完成视频的解码,不仅需要传输VCL层编码出来的视频帧数据,还需要传输序列参数集SPS、图像参数集PPS等数据。即如果想根据h264数据解码出图片则h264数据必须包含含有0x67,0x68,0x65这三个NALU,否则解析不出图片。至于其它的概念如片、宏块、扩展字节序列载荷EBSP,其它的nal类型我们先暂时不管。
附件里的“mpp”文件夹里是源代码
附件里的“h264.si4project”是source insight4工程文件,用source insight便于阅读代码。如果你用source insight3阅读代码,因为编码问题,里面的中文注释可能会是乱码,推荐用source insight4。
附件里的“h264数据分析”文件夹里的log20171229103150.h264是原始的h264数据文件,是二进制文件,必须用二进制工具才能打开;log20171229103150_data.txt是由log20171229103150.h264文件转码为txt,以便用记事本、notepad这样的文本工具打开,以便于观看数据;log20171229103150_printf.txt是hi3518运行过程中输出的打印信息。可以用eseye_u.exe解析h264数据log20171229103150.h264,显示图片。用ultraedit打来log20171229103150.h264文件查看里面的原始数据。
我们用notepad打开log20171229103150_printf.txt文件,看到h264调试信息如下:
stStream.u32PackCount=4,stStream.pstPack[0].pu8Addr=0xb68c4040,stStream.pstPack[0].u32Offset=0,stStream.pstPack[0].u32Len=18
stStream.u32PackCount=4,stStream.pstPack[1].pu8Addr=0xb68c40c0,stStream.pstPack[1].u32Offset=0,stStream.pstPack[1].u32Len=8
stStream.u32PackCount=4,stStream.pstPack[2].pu8Addr=0xb68c4140,stStream.pstPack[2].u32Offset=0,stStream.pstPack[2].u32Len=9
stStream.u32PackCount=4,stStream.pstPack[3].pu8Addr=0xb68c41c0,stStream.pstPack[3].u32Offset=0,stStream.pstPack[3].u32Len=3620
stStream.u32PackCount=1,stStream.pstPack[0].pu8Addr=0xb68c5040,stStream.pstPack[0].u32Offset=0,stStream.pstPack[0].u32Len=12
第一行stStream.u32PackCount=4,stStream.pstPack[0].pu8Addr=0xb68c4040,stStream.pstPack[0].u32Offset=0,stStream.pstPack[0].u32Len=18
表示这一帧视频有4个h264 nalu,第一个包的虚拟首地址为stStream.pstPack[0].pu8Addr=0xb68c4040,有效数据紧跟在首地址后面,所以偏移stStream.pstPack[0].u32Offset=0,第一个大小为stStream.pstPack[0].u32Len=18,所以在log20171229103150.h264数据文件里看,第一个包的起始码00 00 00 01在0x00000000处,那么第二个包的起始码应该在0x00000000+18=0x00000012处,看数据文件,果然第二个起始码00 00 00 01在0x00000012处。后面的三行都是第一帧的数据,分析方法同上。第一帧的第4个包大小为stStream.pstPack[3].u32Len=3620,其开始位置为0x00000023+3620=0x00000e47,看数据文件,果然下一个00 00 00 01在0x00000e47处。当然在0x00000e47处的包是属于第二个帧了。前面说过要想解析一个h264图片必须需要0x67sps,0x68pps,0x65关键帧。看第一帧数据果然少不了0x67sps,0x68pps,0x65关键帧这三个nalu。至于中间夹杂了一个0x06nalu是补充增加信息nalu。现在我们可以用eseye_u.exe解析出图片,如果我们删除0x67,0x68,0x65中的随便一个nalu就会发现eseye_u.exe解析不了图片了,也验证了我们前面说的h264必须包含0x67,0x68,0x65这3个nalu。
第5行stStream.u32PackCount=1,stStream.pstPack[0].pu8Addr=0xb68c5040,stStream.pstPack[0].u32Offset=0,stStream.pstPack[0].u32Len=12则是另一帧的数据,这一帧的数据依赖于前面的一帧的内容,只是记录了相对前一帧的差异,以便减小文件大小。
用eseye_u.exe解析整个log20171229103150.h264发现总共有13个红块,这13个红块就是关键帧,里面包含0x67sps,0x68pps,0x65关键片,其余的是蓝块,非关键帧,依赖于前面的关键帧。