H264笔记

H264

H264是视频编码规范,在这个规范下H264码流有两种方式:AnnexB格式 && AVCC格式

AnnexB

AnnexB 的原理是在每个 NALU 前面写上一个特殊的起始码,通过这个起始码来当做 NALU 的分隔符,从而分割每个 NALU

[start code] NALU [start code] NALU [start code] NALU

AVCC

avcC 则采用了另外一种方式。那就是在 NALU 前面写上几个字节,这几个字节组成一个整数(大端字节序)这个整数表示了整个 NALU 的长度

[extra data] [length] NALU [length] NALU [length] NALU

NALU

NALU (Network Abstraction Layer Unit) 翻译过来就是网络抽象层单元。在 H.264/AVC 视频编码标准中,最外面一层叫做 NAL(Network Abstract Layer) 网络抽象层。所有的码流数据,最终都被封装成了一个一个的 NALU(Network Abstract Layer Unit)就是网络抽象层单元。

nalu

nalu

对应的NAL 还有一个视频编码层VCL(video code layer),视频编码后的原始数据 SODB (String Of Data Bits)

  • 视频编码层(VCL),是对视频编码核心算法过程、子宏块、宏块、片等概念的定义。这层主要是为了尽可能的独立于网络来高效的对视频内容进行编码。编码完成后,输出的数据是 SODB(String Of Data Bits)。
  • 网络适配层(NAL),是对图像序列、图像等片级别以上的概念的定义。这层负责将 VCL 产生的比特字符串适配到各种各样的网络和多元环境中。该层将 VCL 层输出的 SODB 数据打包成 RBSP(Raw Byte Sequence Payload)。SODB 是编码后的原始数据,RBSP 是在原始编码数据后面添加了结尾比特,一个比特 1 和若干个比特 0,用于字节对齐。然后再在 RBSP 头部加上 NAL Header 来组成一个一个的 NAL 单元。

sps & pps

SPS(Sequence Paramater Set) 序列参数集

PPS(Picture Paramater Set) 图像参数集

SPS 和 PPS 存放的的信息是码流中的参数信息,后续的解码流程是要依赖着里面的参数的,所以解码器在解码一路码流的时候,总是要首先读入 SPS 和 PPS

AnnexB 格式的码流,sps 跟 pps 是作为nalu存在的,一般都最前面
AVCC格式的码流,sps 跟 pps 是放在extra data中。

NALU header

nalu 的第一个字节,其实就是nalu header

forbidden_zero_bit nal_ref_idc nal_unit_type

forbidden_zero_bit 占用 1 位,nal_ref_idc 占用 2 位,nal_unit_type 占用 5 位。三个元素一共占用 8 位,也就是一个字节

forbidden_zero_bit

禁止位,正常情况下为 0。在某些情况下,如果 NALU 发生丢失数据的情况,可以将这一位置为 1,以便接收方纠错或丢掉该单元

nal_ref_idc

该元素表示这个 NALU 的重要性。可能的值有 4 个,越重要的 NALU 越不能丢弃

nal_ref_idc 重要性
3 HIGHEST
2 HIGH
1 LOW
0 DISPOSABLE

nal_unit_type

nal_unit_type NALU 类型
0 未定义
1 非 IDR SLICE slice_layer_without_partitioning_rbsp( )
2 非 IDR SLICE,采用 A 类数据划分片段 slice_data_partition_a_layer_rbsp( )
3 非 IDR SLICE,采用 B 类数据划分片段 slice_data_partition_b_layer_rbsp( )
4 非 IDR SLICE,采用 C 类数据划分片段 slice_data_partition_c_layer_rbsp( )
5 IDR SLICE slice_layer_without_partitioning_rbsp( )
6 补充增强信息 SEI sei_rbsp( )
7 序列参数集 SPS seq_parameter_set_rbsp( )
8 图像参数集 PPS pic_parameter_set_rbsp( )
9 分隔符 access_unit_delimiter_rbsp( )
10 序列结束符 end_of_seq_rbsp( )
11 码流结束符 end_of_stream_rbsp( )
12 填充数据 filler_data_rbsp( )
13 序列参数扩展集 seq_parameter_set_extension_rbsp( )
14~18 保留
19 未分割的辅助编码图像的编码条带 slice_layer_without_partitioning_rbsp( )
20~23 保留
24~31 未指定

slice

我们之前介绍过 SPS 和 PPS,SPS 和 PPS 中储存的信息是一些参数项,例如,图像的长宽,图像的 profile 信息等。那么在 Slice 中,存放的信息就是编码后的图像信息了,也就是说,解码 Slice,我们就能还原出来图像了。

一个 Slice 通常被分为两个部分,Slice Header 和 Slice Body

Slice Header Slice Body

slice_type

含义
0 P(P Slice)
1 B(B Slice)
2 I(I Slice)
3 SP(SP Slice)
4 SI(SI Slice)
5 P(P Slice)
6 B(B Slice)
7 I(I Slice)
8 SP(SP Slice)
9 SI(SI Slice)

可以看到 slice_type 规定了 slice 的类型,这也解释了之前的一些误区。有人说根据 NALU 的类型就能判读是 I Slice 还是 P Slice 还是 B Slice。其实是错误的,要判断这些必须要读到 slice_type 才行

pic_parameter_set_id

slice_type 之后,是 pic_parameter_set_id,这个属性表明该 Slice 要依赖的 PPS 的 id。我们在解析 PPS 的时候,PPS 里面有一个 pic_parameter_set_id 的属性。当你解析出 Slice 中的 pic_parameter_set_id 之后,拿着这个 id 找到与之对应的 PPS 就可以了。然后 PPS 里还有个 seq_parameter_set_id,用这个 id 就可以找到依赖的 SPS 的 id。这样,你就可以为这个 Slice 查找到合适的 SPS 和 PPS 了。

MP4

h264编码之后的码流,最终还是会封装到各种媒体文件中,这里就只了解下最常用的mp4.

Mdat Box这个Box是存储音视频数据的Box,要从这个Box解封装出真实的媒体数据。当然这个Box一般都会存在,但是不是必须的。
但是在MP4格式文件中,H264 slice并不是以00 00 00 01 Start Code来进行分割,而是存储在Mdat Box的Data中。

Mdat Box的格式:

Box header + Box Data

Box length + Box Type + NALU length + NALU header + Nalu Data……. NALU length + NALU header + Nalu Data

Mdat里的NALU一般不再包含SPS PPS等数据,这些数据已经放到Moov Box里面了

mp4里的NALU其实就是AVCC格式的

references

深入浅出理解视频编码 H264 结构
最详尽的 H.264 编码相关概念介绍
自己动手写 H.264 解码器
H.264 媒体流 AnnexB 和 AVCC 格式分析 及 FFmpeg 解析mp4的H.264码流方法