最近在学习h264 解码流程,结合h264spec文档和JM代码,总结下参考帧队列重排这部分内容。对应spec 中的8.2.4节
参考帧重排目的
参考帧重排的意义:由于在解码每个(P/B)MB时,都要用到参考帧的索引ref_idx_l0或ref_idx_l1。假如有个参考帧(短期参考帧或者长期参考帧)对于解码一个图像特别有用,但是这个参考帧在缺省的队列中并不位于索引值为0的位置,所以编码大的索引值需要花费多的比特。参考帧的重排序可以使这个参考帧位于索引值比较小的位置,以节省编码比特数。
参考帧队列
H264采用多参考帧提高编码性能。对于每一个P帧和B帧的解码都需要从解码图像缓存DPB中选择一个或多个参考帧。DPB中的参考帧可分为短期参考帧和长期参考帧两种,分别按照PicNum和LongTermFrameIdx索引,通过这两个索引值可以在参考帧列表中获取对应的参考帧图像。
问题:为什么要分成短期参考和长期参考?
从网上找到的答案,不知道是否正确:因为short term参考帧以frame_num做为
索引,而frame_num是有最大值的,达到最大值后会进行取模,所以短期参考帧
不能长期存在于参考列表中,因为一旦frame_num达到最大值后取模为0,该索
引就失去意义了,而长期参考帧则不同
当解码一个P slice时,使用一个参考帧队列RefPicList0;解码B slice是,使用两个参考帧队列RefPicList0和RefPicList1;
参考帧队列解码过程
参考帧队列解析包括两个部分:初始化和重排
1.初始化
初始化过程依赖于解码帧的参考标记(对应spec 8.2.5章)。参考队列初始化的前提是至少有一个参考slice被标记为‘短期参考’或‘长期参考’。 初始化实现过程对应8.2.4.2.1-8.2.4.2.5节,JM代码对应init_lists函数。 主要原则是:1)短期参考帧比长期参考帧索引值小; 2)短期参考帧以递减顺序排列 3)长期参考帧以递增顺序排列
2.重排序
slice header中的语法ref_pic_list_modification 指明了该slice参考帧队列是否需要修改,如果ref_pic_list_modification为0,则说明该slice不需要重排参考帧队列,按照初始化顺序即可。 其中,ref_pic_list_modification_flag_l0为1时,对参考帧列表RefPicList0进行修改;ref_pic_list_modification_flag_l1为1时,对参考帧列表RefPicList1进行修改。本节中以P帧解码时修改参考帧列表RefPicList0为例讨论其执行过程。 1.设定一个值refIdxL0表示参考帧列表中参考帧的索引值,并初始化为0; 2.读取码流中的modification_of_pic_nums_idc值,并根据其取值进行计算:
- 如果modification_of_pic_nums_idc为0或1,执行短期参考帧的修改过程;
- 若modification_of_pic_nums_idc为2,执行长期参考帧的修改过程;
- 若modification_of_pic_nums_idc为3,参考帧列表的修改过程完成; 重排序过程可以这么理解:与该解码slice关联较大的几个参考帧不在短期、长期参考帧队列序号靠前位置,或者新增参考帧时,根据ref_pic_list_modification得到想要放在队列前的参考帧frameNum,然后把该参考帧放在队列头,同时队列中的后续参考帧逐一后移。 代码实现可参考JM 中的reorder_lists 函数。