2。3 解码器(decoder)
H。264 解码器按功能大致可以分为两部分:解析器和解码器。解码器中负责解码 的功能块包括熵解码部分,宏块解码部分-帧内宏块,宏块解码部分-帧间宏块和环路 滤波部分[16]。
2。3。1 解析器
这部分的代码用于分割 H。264 的 NALU,并且解析 SPS、PPS、SEI 等信息。主要包 括的重要解析函数:
ff_h264_decode_nal():解析 NALU。 ff_h264_decode_slice_header():解析 Slice Header。 ff_h264_decode_sei():解析 SEI。 ff_h264_decode_seq_parameter_set():解析 SPS。 ff_h264_decode_picture_parameter_set():解析 PPS。
图 2。1 H。264 原始码流
如上图 2。1 所示,H。264 标准中的比特流都是以 NAL(每个包含一个 RBSP)为单 位的,每个 NALU 的头信息定义了所有的类型信息。RBSP 所属类型的信息一般可以分 为序列参数集(SPS)、图像参数集(PPS)、增强信息(SEI)、条带(Slice)等等, 所以解析 NALU 的函数是后几个解析函数的前提。H。264 标准采用参数集机制是为了能 够识别一些主要的序列、图像参数,并将其先解码出来,上文中提到的的 SPS 和 PPS
就属于参数集。
图 2。2 解析器(Parser)部分的源代码的调用关系
2。3。2 解码器主体部分
除了初始化工作和关闭工作,H。264 解码器在解码图像帧的时候调用了一系列函 数,实现主要功能的部分主要调用了两类函数——解析函数和解码函数:
(1)解析函数(获取信息): ff_h264_decode_nal():解析 NALU Header。 ff_h264_decode_seq_parameter_set():解析 SPS。 ff_h264_decode_picture_parameter_set():解析 PPS。 ff_h264_decode_sei():解析 SEI。 ff_h264_decode_slice_header():解析 Slice Header。
(2)解码函数(解码获得图像): ff_h264_execute_decode_slices():解码 Slice。
ff_h264_decode_mb_cabac():CABAC 熵解码函数。 ff_h264_decode_mb_cavlc():CAVLC 熵解码函数。 ff_h264_hl_decode_mb():宏块解码函数。 loop_filter():环路滤波函数。
主体部分是指解码器中熵解码,宏块解码和环路滤波以外的部分,主要讨论的 是解码器的架构,熵解码,宏块解码和环路滤波这几部分我们会在后面讨论。
在这部分中,所有的的初始化函数都有一个共同的特点:为了方便为不同的颜 色位深的码流初始化不同的功能函数,宏定义都很长。
解码器中最重要的函数是 h264_decode_frame(),该函数根据输入的包的数据 data 部分是否为空做出不同的应对流程:
(1) 输入的包的 data 为空的时候,输出解码器中缓存的帧(通常是称为“Flush Decoder”的功能)。
(2)输入的包的 data 不为空的时候,首先解码包中的 data,然后再输出解码后 的视频帧(需要注意的一点是:由于帧重排等因素,输出的 YUV 帧顺序上并不一定对 应于输入的包)。输入的包的 data 不为空时,其核心在于解码压缩编码数据时候用 到的函数。该函数做的第一件事就是判断 NALU 的类型,然后根据 NALU 类型的不同调 用不同的处理函数。
解析函数已经在上文(2。4。1)分析过了,主要用来获取一些比特流的信息,就 不再重复叙述了,解码函数完成了解码 Slice 的工作,其中包括熵解码,宏块解码, 环路滤波,错误隐藏等解码的细节任务。
2。3。3 熵解码
对解码函数进行仔细观察后,不难明白一个明显的流程,程序需要首先判断该 H。264 码流是 CABAC 编码还是 CAVLC 编码,然后才能根据判断的结果,调用不同的熵 解码函数。