如何读H.264的标准和代码

首先,还是要弄清楚编解码的流程和 H.264 的关键技术,看白皮书就知道了,另外 H.264 综述类的文章和别人的学位论文一般也会讲到; 其次,弄清楚代码的各个函数实现的功能,这个可以看看 JM 代码里各个函数前面的函数说明; 最后,弄清楚标准各个章节讲的什么内容:这

首先,还是要弄清楚编解码的流程和 H.264 的关键技术,看白皮书就知道了,另外 H.264 综述类的文章和别人的学位论文一般也会讲到;

其次,弄清楚代码的各个函数实现的功能,这个可以看看 JM 代码里各个函数前面的函数说明;

最后,弄清楚标准各个章节讲的什么内容:这里只说重要的。第三章是名词解释,第四章是缩略语,第五章是一些计算方式和运算符号的说明,第六章是与 H.264 相关的一些视频基础知识和 H.264 中用到的一些过程推导,第七章是 NALU 及其以下语法结构的语法和语义(如果要知道码流结构就要看这一章了),第八章是详细说明解码过程中某一个模块的功能怎么完成,第九章是熵编码,附录 A 是关于 profile 和 level 的具体规定,附录 B 是关于如何从字节流中解析 NALU(标准没有说明如何在 RTP 流中解析 NALU)。

有了上面的基本知识,下面我们结合对码流的解析过程来讲讲怎么读标准:
1、如果是字节流的码流当然就首先要对字节流进行解析,这就要看附录 B 了;如果是 RTP 格式的码流,那首先就要按 RFC3984 来解析了(标准没有规定 RTP 格式码流的解析过程);

2、字节流解析完后提取出来的就是 NALU 了,对 NALU 的解析就要看 7.3.1 小节了。第七章中黑色的粗体字都是在码流中可能出现的语法元素,解码器的首要任务就是要对这些语法元素进行解析。对于这些码流中的语法元素我们要进行解析必须知道三个问题:
(1)、什么时候存在于码流中?这样我们才能知道当前解析的是哪个语法元素;
(2)、采用什么样的熵编码方式?这样我们才能知道如何解析;
(3)、含义是什么?这样我们才知道解析出来之后用来干什么。
三个问题的答案分别是:
(1)、有 if 条件关联的就是可能出现的,没有 if 条件关联的就是必然出现的。例如,7.3.1 小节表中的 forbidden_zero_bit 就没有 if 条件关联,所以它必然出现在码流中;
(2)、每个语法表最后一列都对所在行语法元素的熵编码方式做了规定,而最后一列各个符号具体是代表什么编码方式那就去看 7.2 小节最后的部分;
(3)、看 7.4 小节与语法表对应的语义部分,例如你查的语法表是 7.3.1,那么该语法表里出现的语法元素的解释就在 7.4.1 小节中。

3、NALU 的前面三个语法元素所组成的一个字节我们称为 NALU 头,其余部分(也就是语法表 7.3.1 中的其余部分)我们称为 NALU 体。对 NALU 体的解析要看 7.3.2 小节。因为 NALU 有很多种类型,所以要针对 NALU 的不同类型去解析 NALU 体(表 7-1 说明了不同 NALU 对应的语法表)。例如,如果当前的 NALU 是 SPS,那么当然就要看 7.3.1 小节;如果当前的 NALU 是 DPA,那么当然就要看 7.3.2.9.1 小节了;

4、对于属于 VCL 的 NALU(哪些 NALU 是 VCL NALU 呢?如果你看了 nal_unit_type 的语义,你就应该知道),例如表 7-1 中类型为 5 的 NALU,根据表 7-1 我们知道 NALU 体的语法表是 7.3.2.8。而从 7.3.2.8 我们可以看到,对这种 NALU 的 NALU 体解析实际就是对片级语法进行解析。语法表 7.3.2.8 显示片级语法解析首先要解析 slice_header()(这种带括号的表示是另一个语法结构),那么 slice_header() 怎么解析呢?往下看,7.3.3 的所有内容都被第一行的 slice_header() 包括在内,所以 7.3.3 就是对 slice_header() 这个语法层的码流规定;

5、按照语法表 7.3.2.8 解析完了 slice_header() 就该解析 slice_data() 了。下面以最常见的 I 帧(CAVLC 熵编码、非 MBAFF)的解析过程为例简单描述怎么继续读标准。这时在码流中出现的第一个 slice_data() 层的语法元素是语法表 7.3.4 中的 macroblock_layer(),也就是说直接到了宏块层的语法解析,那就要又要看 7.3.5 小节了;

6、基于我们对编解码流程的了解,我们知道解码是一个预测值加残差得到重建图像的过程,那么我们下面的解码过程就要分成两步走了:首先,得到预测值;其次,得到残差。基于我们对 H.264 关键技术的了解,我们知道 intra 宏块(提醒:我们举的例子是 I 帧,因此解析的是 intra 宏块)的预测值是需要使用到预测模式的,所以我们需要解析语法表 7.3.5 中的 mb_pred(mb_type) 语法层,那么又去看 7.3.5.1 小节。按照 7.3.5.1 小节解析出宏块或块的预测方式后我们怎么计算预测值呢?去看标准 8.3 小节;得到预测值后我们继续按照语法表 7.3.5 解析语法元素直到 residual() 语法层,这就又要去看 7.3.5.3 小节;按照 7.3.5.3 小节解析出残差系数后我们如何把它还原成真实的残差呢?去看标准 8.5 小节;

7、预测值和残差都有了,加起来就是解码图像了。解码的主要工作到此也算基本完成了。当然,上面的过程中还会用到标准其他章节的相关内容(例如,8.5 小节会用到 5.7 小节中定义的 InverseRasterScan),这就留给大家自己去学习了。


上面讲了如何读标准,那么如何读代码呢?非常简单,因为你现在已经知道了代码中各个函数所实现的功能以及标准各个章节所涉及的内容,那么你就该知道标准某个部分的内容与代码中的哪个函数对应,因此对于你想详细了解实现过程的模块,对照标准去仔细啃那个函数吧。对于代码中不明白的变量或者参数,把程序跑起来,看第 1 个 MB 解码时候该变量的值是多少,第 23 个 MB 解码时候该变量的值是多少……多做几个观察值,注意不要选择特殊位置,然后总结一下规律,这样你就自然能分析出该变量的作用和含义了。

以上讲的是解码过程,编码过程就是解码的反过程,因此同理。

——天之骄子(firstime)——
2008年8月5日

原文:bbs.chinavideo.org/viewthread.php?tid=4164

你可能感兴趣的:(h264)