简介
H.264从1999年开始,到2003年形成草案,最后在2007年定稿有待核实。在ITU的标准⾥称为H.264,在MPEG的标准⾥是MPEG-4的⼀个组成部分MPEG-4 Part 10,⼜叫Advanced Video Codec,因此常常称为MPEG-4 AVC或直接AVC。
其实在这里有个地方值得了解,为什么同一个视频编码标准会有两个名字?
- 联合开发的背景
H.264/MPEG-4 AVC是由ITU-T(国际电信联盟电信标准化部门)的VCEG(视频编码专家组)和ISO(国际标准化组织)/IEC(国际电工委员会)的MPEG(运动图像专家组)联合开发的。为了提高效率并避免重复工作,这两个组织在2001年成立了联合视频小组(JVT,Joint Vdieo Team),共同制定这一标准。
联合开发的结果是一个同一的技术规范,但两个组织保留了各自的命名方式,以适应各自的标准体系和用户群体。
H.264的编码原理
在音视频的传输过程中,视频文件的传输是一个大问题;例如一段分辨率为 19201080,每个像素点为RGB占3字节,帧率是25的视频,对于传输带宽的要求是 19201080325/1024/1024=148.3154296875MB/s,换成bps则意味视频每秒带宽为1,186.5234375Mbps,这样的速率对于网络存储是不可接受的,因此视频压缩和编码技术应运而生。
对于视频文件来说,视频由单张图片帧所组成,比如每秒25帧,但是图片帧的像素块之间存在相似性,因此视频帧图像可以进行图像压缩;H264采用了16*16的分块大小对视频图像进行相似比较和压缩编码。如图所示:
H264的I帧、P帧和B帧
H264采用帧内压缩和帧间压缩的方式提高编码压缩率;H264采用了独特的I帧、P帧和B帧策略来实现,实现帧之间的压缩。
详情看:视频I P B帧理解
从上面可以得知 压缩率:B>P>I
H264编码结构解析
H264除了实现对视频的压缩处理外,为了方便网络传输,提供了对应的视频编码和分片策略;类似于网络数据封装成IPB帧,在H264中将其称为组(GOP, group of pictures)、片(slice)、宏块(Macroblock)这些组成了H264的码流分层结构,H264将其组织成为 序列(GOP),图片(picture)、片(slice)、宏块(Macroblock)、子块(subblock)五个层次。
GOP(图像组)注意用来形容一个IDR帧(Instantaneous Decoding Refresh Frame,即瞬时解码刷新帧)到下一个IDR帧中间隔了多少帧。
H264将视频分为连续的帧进⾏传输,在连续的帧之间使⽤I帧、P帧和B帧。同时对于帧内⽽⾔,将图像分块为⽚、宏块和字块进⾏分⽚传输;通过这个过程实现对视频⽂件的压缩包装。
这里值得注意的是:IDR帧一定是I帧,但I帧不一定是IDR帧
IDR帧和I帧的区别
- 共同点
1、I帧和IDR帧都是帧内编码帧(Intra-coded frame),只用当前帧的信息编码,不依赖其他帧。
2、它们都由宏块组成,使用帧内预测(如Intra 16x16或Intra 4x4) - 区别
I帧:
只是一个独立编码的帧,后续的P帧或B帧可能仍然参考I帧之前的帧。
不会重置解码器的状态,错误可能从之前的帧传播到I帧之后的帧。
IDR帧:
不仅独立编码,还会重置解码器,清空参考帧缓冲区。
后续的P帧或B帧只能参考IDR帧及其之后的帧,禁止参考IDR帧之前的帧。
作用:提供“干净”的起点,适合随机访问(如视频跳转)和错误恢复。 - 实际场景
视频播放:
你拖动视频进度条到10分钟处,播放器会找到最近的IDR帧开始解码,因为IDR帧保证不需要之前的帧数据。
如果是普通I帧,可能无法正确解码,因为后续帧可能依赖I帧之前的帧。
网络传输:
如果网络丢包导致某帧丢失,IDR帧可以让解码器“从头开始”,避免错误影响后续帧。而I帧可能让错误继续传播。
什么是NALU?
NALU(Network Abstraction Layer Unit,网络抽象层单元)是H.264视频编码标准中用于数据封装和传输的基本单元。它是H.264码流的最小组织单元,包含了视频编码数据和相关信息的"包",便于网络传输和解码。
-
定义
NALU是H.264码流中的一个数据块,每个NALU包含一个头部(描述数据类型)和有效载荷(实际数据,如视频帧、参数集等)。 -
作用:
将H.264编码的视频数据(或元数据)封装成适合网络传输的格式。
提供灵活性和鲁棒性,方便在不同网络环境下(如实时流媒体)传输视频。
支持解码器识别和处理不同类型的数据(如视频帧、序列参数等)。
发I帧之前,⾄少要发⼀次SPS和PPS
NALU结构
⼀个NALU = ⼀组对应于视频编码的NALU头部信息+ ⼀个原始字节序列负荷(RBSP,Raw Byte Sequence Payload)
NALU结构单元的主体结构如下所示;⼀个原始的H.264 NALU单元通常由[StartCode] [NALU Header] [NALU Payload]三部分组成,其中Start Code ⽤于标示这是⼀个NALU 单元的开始,必须是"00 00 00 01" 或"00 00 01",除此之外基本相当于⼀个NAL header + RBSP
(FFmpeg解复用后,MP4文件读取处理的packet是不带startcode的,TS文件读取出来的packet带了startcode)
解析NALU
每个NAL单元是⼀个⼀定语法元素的可变⻓字节字符串,包括包含⼀个字节的头信息(⽤来表示数据类型),以及若⼲整数字节的负荷数据。
其中:
- T为负荷数据类型,占5bit
nal_unit_type:这个NALU单元的类型,1~12由H.264使用,24-31由H.264外的应用使用,13-23是H.264预留 - R为重要性指标,占2bit
nal_ref_idc:范围0-3,值越大,表示此单元越重要,如00的NALU解码器可以丢弃它⽽不影响图像的回放,如果当前NAL是属于参考帧的⽚,或是序列参数集,或是图像参数集这些重要的单位时,本句法元素必需⼤于0 - F为禁止位,占1bit
forbidden_zero_bit:在H.264 规范中规定了这⼀位必须为0
H.264标准指出,当数据流是储存在介质上时,在每个NALU 前添加起始码:0x000001 或0x00000001,⽤来指示⼀个NALU 的起始和终⽌位置:
在这样的机制下,在码流中检测起始码,作为⼀个NALU得起始标识,当检测到下⼀个起始码时,当前NALU结束。3字节的0x000001只有⼀种场合下使⽤,就是⼀个完整的帧被编为多个slice(⽚)的时候,包含这些slice的NALU 使⽤3字节起始码。其余场合都是4字节0x00000001的。
H.264的两种封装格式
- Annex B字节流格式(Byte Stream Format)
- AVCC格式(AVC Configuration Format)