H264的主要目标是实现高的视频压缩比和提供良好的网络亲和性(可适用于各种网络传输),因此在功能层面上划分为视频编码层VCL和网络提取层NAL两层其中预测(帧内预测和帧间预测)、DCT、量化、编码和切片等步骤都属于VCL,VCL最终会被包装为NALNAL由一系列NAL Unit组成
不过接下来并不对NALU进行展开,而是关注另一个问题,即:在H264码流中是如何将NALU进行分割的要搞清楚这点,就需要了解目前H264主流的码流组织方式AnnexB和AVCC两种格式,其中Android的硬解码MediaCodec只支持AnnexB格式的数据,而Apple的VideoToolBox只支持AVCCAnnexB
AnnexB格式也叫做MPEG-2 transport stream format格式(ts格式),用于TS流中,以及使用TS作为切片的HLS格式中
它的原理是通过在NALU前面添加一个叫Start Code(起始码)的东西,起始码的内容为三字节的0 0 1或者4字节的0 0 0 1 (其中起始码在NALU为SPS、PPS或NALU为AU的第一个NALU时使用4字节,其他情况使用3字节)
当我们读取一个 H264码流的时候,一旦遇到起始码,就认为一个新的 NALU 开始了
不过在NALU前加入起始码会引入一个新的问题,因为原始码流中是可能出现和起始码一样的数据,这样就会导致错误的NALU分割。为了防止这种情况发生,AnnexB 引入了防竞争字节(Emulation Prevention Bytes)的概念
具体操作为:编码器编完一个NALU后,检查内部是否出现如下左侧的字节序列,如果存在,则在最后一个字节前插入一个新的字节0x03
0x00 00 00 => 0x00 00 03 00
0x00 00 01 => 0x00 00 03 01
0x00 00 02 => 0x00 00 03 02
0x00 00 03 => 0x00 00 03 03
解码器在NALU内部检测到防竞争字节后将0x03丢弃来恢复原始数据
AVCC
AVCC格式也叫AVC1格式,MPEG-4格式,常用于mp4/flv等封装中它的原理是在NALU 前面添加固定字节(可能是1字节、2字节或4字节,其中4字节较常见),这几个字节组成一个整数(大端字节序)表示整个 NALU 的长度,在读取的时候,先把这个整数读出来(例如ffmpeg从extradata获取),拿到这个 NALU 的长度,再按照长度读取整个 NALU
AVCC header sequence or extradata
按位读取
8 version ( always 0x01 )
8 avc profile ( sps[0][1] )
8 avc compatibility ( sps[0][2] )
8 avc level ( sps[0][3] )
6 reserved ( all bits on )
2 NALULengthSizeMinusOne // 每个NALU数据长度所占byte-1
3 reserved ( all bits on )
5 number of SPS NALUs (usually 1)
repeated once per SPS:
16 SPS size
variable SPS NALU data
8 number of PPS NALUs (usually 1)
repeated once per PPS
16 PPS size
variable PPS NALU data
读取 NALULengthSizeMinusOne的值然后加 1 ,我们就得出了后续每个 NALU 前面表示NAL size(也就是上图紫色块length)的字节数,一般NALULengthSizeMinusOne 是 3,那么每个 NALU length就是 4 个字节,然后把这四个字节转成整数,就是这个 NALU 的长度
参考
1.【ISO-14496-10】
http://www.staroceans.org/e-book/ISO-14496-10.pdf
2.【H264编码格式整理】
https://zhuanlan.zhihu.com/p/71928833
3.【逐字节详解H.264 AVCC header】
https://www.jianshu.com/p/4f95617f30d0
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。