H264参考帧列表管理

P/SP/B帧的参考帧列表的初始化

参考H.264标准文档的8.2.4.2章节,暂不研究场编码。在初始化P/SP帧或B帧的参考帧列表过程中,DPB中至少要存在一个有效的、即被标记为"用于短期或长期参考"的参考帧。

P/SP Slice 参考帧列表初始化

P/SP Slice参考帧列表初始化在**[1]的8.2.4.2.1章节定义,**仅有前向参考,只有List0。初始化依据如下原则:

  • 短期参考帧在前,长期参考帧在后
  • 短期参考帧按照PicNum由大到小排序,即短期参考帧按照距离当前帧由近至远的顺序排序。
  • 长期参考帧按照PicNum由小到大顺序,即长期参考帧按照距离按当前帧由远至近的顺序排序。

如短期参考帧的PicNum分别为300,302,303;长期参考帧的PicNum为0和3,在refList0分别为:

RefList0 [0] = 303

RefList0 [1] = 302

RefList0 [2] = 300

RefList0 [3] = 0

RefList0 [4] = 3

B Slice参考帧列表初始化

B Slice存在两个参考帧列表,RefList[0]和RefList[1]。协议中并没有规定List0中存放的一定是前向参考帧,List1中存放的一定是后向参考帧。但是习惯上一般List0存放的是前向参考帧,List1中存放的是后向参考帧。在**[1]8.2.4.2.3**章节有介绍,初始化按照如下原则:

  • RefList[0]和P/SP Slice的RefList[0]完全一样,这里不在赘述、
  • RefList[1]是短期参考帧在前,长期参考帧在后,且短期参考帧按照picNum的升序排列,长期参考帧按照picNum的升序排列。

为何RefList[1]个人猜测是因为RefList[1]一般存放的是后向参考帧,所以对短期参考帧来说,仍然是按照距离当前帧由进到远排序,长期参考帧是按照由近到远排序。这样也是为了便于找到在参考帧列表中的短期参考帧和长期参考帧的界限。

RefList0RefList1队列长度分别为num_ref_idx_l0_active_minus1 + 1和num_ref_idx_l1_active_minus1 + 1。短期参考帧是按降序排列,长期参考帧是按升序排列,且按照先STR后LTR的顺序。

如果参考队列中的元素个数大于参考队列的长度,则删除下标为num_ref_idx_l0_active_minus1后的元素;如果参考队列中的元素个数小于参考队列的长度,多余的元素标记为非参考帧。

如果RefList0和RefList1完全一样,且里面有大于1个元素,则需要把RefList1中的RefList1[0] 和**RefList1[1]**交换位置,这么做的目的是防止RefList0和RefList1完全一致。

Modification process for reference list

在**[1]** 的8.2.4.3 章节中**,**是对参考帧队列的重排序,当ref_pic_list_modification_flag_l0为1时表示需要对RefList0进行重排序;当ref_pic_list_modification_flag_l1为1时表示需要对RefList1进行重排序。

解析码流中的modification_of_pic_nums_idc 字段来执行相应的命令,modification_of_pic_nums_idc所代表的含义如图1所示,参考帧列表重排序分为两种情况,短期参考帧重排序和长期参考帧重排序。

图1

短期参考帧重排序

**短期参考帧重排序见[1]的8.2.4.3.1,7.4.3.1,7.3.3.1章节。**重新排序其实原理就是从RefIdxLX=0开始,每个位置放置期望的参考帧(其实本质是把其它位置的参考帧移到这个地方),所以我们希望知道到底是把参考列表中的哪个参考帧移到当前位置。

图2 ~ 图4说明了如何获取当前需要移动的参考帧的过程,当前需要移动的参考帧即为picNumLX,picNumLXPred 的初始值为currentPicNum。

如果modification_of_pic_num_idc为0,对RefList0进行重排序操作,根据下式计算picNumLXNoWrap

图2

如果modification_of_pic_num_idc为1,对RefList1进行重排序操作,根据下式计算picNumLXNoWrap

图3

图4

每一个command的执行伪代码如图5所示,本质上就是移位和赋值操作。

图5

picNumLX的值将会带入图2的picNumLXPred执行下一个command命令,从图5 可以看出,refIdxLX每执行一次命令就加1,用于下次命令操作。

重排序的句法如下图所示([1] 7.3.3.1

图6

长期参考帧重排序

**长期参考帧的重排序见[1]的8.2.4.3.2,7.4.3.1,7.3.3.1章节,**长期参考帧重排序和短期参考帧的重排序过程极其相似,这是每一条command的执行的操作稍有差别,每条command执行的伪代码如下图所示,这里不再赘述,后面用例子说明参考帧重排序过程。

图7

An example of reference list sort

假设参考帧队列中的元素如下,前三个为短期参考帧,后两个为长期参考帧,当前帧号为158,参考帧队列如下图所示。

图8

假设只对RefList0排序,command命令依次为:

  • modification_of_pic nums_idc = 0, abs_diff_pic num_minus_1 = 4
  • modification_of_pic nums_idc = 1, abs_diff_pic num_minus_1 = 1
  • modification_of_pic nums_idc = 2, long_term_pic_num = 3
  • modification_of_pic nums_idc = 3

下面我们来分析重排序命令的执行过程:

  • modification_of_pic nums_idc = 0, abs_diff_pic num_minus_1 = 4

picNumL0NoWrap = 158 - (4 + 1) = 153

picNumL0 = 153,初始的RefIdxLX = 0。

根据图5,短期参考帧重排序后为153 157 155 1 3 3,因为参考帧个数为5,所有只有一个entry无效。

  • modification_of_pic nums_idc = 1, abs_diff_pic num_minus_1 = 1

RefIdxLX = 1,picNumLXPred = 153

picNumL0NoWrap = 153 + (1 + 1) = 155

根据图5,短期参考帧重排序后为153 155 157 1 3 3

  • modification_of_pic nums_idc = 2, long_term_pic_num = 3

RefIdxLX = 2,参考图7, 长期参考帧重排序后为153 155 3 157 1 3

  • modification_of_pic nums_idc = 3

更新参考帧列表结束,最终的参考帧列表为153 155 3 157 1 3

marking process for reference list

参考帧列表的标记在**[1]7.3.3.3, 8.2.5** 章节中介绍**,** 参考帧列表的标记主要的作用是对参考帧的状态做变化,如当前参考帧列表的某个参考帧是作为短期参考帧使用,我们在下一帧编码时需要把它标记为长期参考帧或者标记位"未作为参考帧"(unused for reference)。

和参考帧重排序操作一样,参考帧标记需要顺序解析码流中的相关mmco(memory management control operatiopn)命令,协议中定义的mmco命令如下:

图9

参考帧标记句法如下:

图10

对图10的句法结构进行分析:

**no_output_of_prior_pics_flag :**仅在当前图像是 IDR 图像时出现这个句法元素,指明是否要将前面已解码的图像全部输出。

long_term_reference_flag: 仅在当前图像是 IDR 图像时出现这一句法元素。这个 句法元素指明是否使用长期参考这个机制。如果取值为 1,表明使用长期参考,并且每个 IDR 图像被解码后自动成为长期参考帧,否则(取值为 0),IDR 图像被解码后自动成为短期参考帧。

**adaptive_ref_pic_marking_mode_flag:**指明标记(marking)操作的模式。当为0时,使用滑窗机制,先进先出,这种模式下无法对长期参考帧操作。当为1时,为自适应模式,句法结构中可以灵活地对短期参考帧和长期参考帧进行操作。

图11

下面解读下mmco的各控制指令:

0: 解析mmco指令结束

1: 把DPB中的一个STR标记为unused reference,此操作带有一个参数difference_of_pic_nums_minus1,由此参数可 以推算出需要设置为unused reference的pic_num.

2: 把DPB中的一个LTR标记为unused reference,带有一个参数long_term_pic_num, 此参数即为DPB中的LTR的索引编 号,long_term_pic_num的范围是0 - reference_num。

3: 把DPB中的一个STR标记为LTR并且给长期参考帧赋long_term_frame_idx ,所以执行此命令需要找到需要标记为 LTR的STR,所以此命令有两个参数difference_of_pic_nums_minus1long_term_frame_idx。 通过第一个参数 找到参考帧列表中对应的STR,然后将其标记为LTR,并设置long_term_frame_idx。long_term_frame_idxlong_term_pic_num不是一回事,两个值可以设置为一样。

4: 标记maximum long-term frame index,并且把大于此值的所有LTR标记为unused reference。其作用就是使当前部 分的长期参考帧置为"unused for reference "。如果max_long_term_frame_idx_plus1 设置为0**,**则所有的长期 参考帧都无效。

5: 把DPB中所有的参考图像都设置为unused reference,并且把max_long_term_frame_idx设置为 "no long-term frame indices"(即相当于把max_long_term_frame_idx_plus1设置为0)。

6: 把当前的picture设置为LTR,并且分配long-term frame index。

Reference

《T-REC-H.264-202108》 [1]

<<THE H.264 ADVANCED VIDEO COMPRESSION STANDARD>> [2]

相关推荐
superconvert3 天前
主流流媒体的综合性能大 PK ( smart_rtmpd, srs, zlm, nginx rtmp )
websocket·ffmpeg·webrtc·hevc·rtmp·h264·hls·dash·rtsp·srt·flv
daqinzl17 天前
利用javacv实现视频转h264
ffmpeg·h264·javacv·视频转换
IT_阿水21 天前
基于FFMPEG读取摄像头图像编码为h264
ffmpeg·h264
Android技术栈1 个月前
鸿蒙(API 12 Beta3版)【获取支持的编解码能力】 音视频编码
音视频·harmonyos·鸿蒙·媒体·openharmony·编解码·鸿蒙开发
小李飞刀李寻欢1 个月前
centos下如何解决av.codec.codec.UnknownCodecError: libx264
linux·运维·centos·视频·video·h264·编解码
NullPointerExpection2 个月前
windows 使用 ffmpeg + cuda 进行视频压缩测试
c++·windows·ffmpeg·nvidia·cuda·h264·英伟达显卡
R-QWERT2 个月前
音视频解封装demo:使用libmp4v2解封装(demux)出mp4文件中的h264视频数据和aac语音数据
aac·mp4·h264·音视频容器·解封装
R-QWERT2 个月前
音视频封装demo:将h264数据和aac数据封装(mux)成FLV文件(纯手工,不依赖第三方开源库)
aac·h264·封装·flv·音视频容器
R-QWERT2 个月前
音视频封装demo:将h264数据和aac数据封装(mux)成TS文件(纯手工,不依赖第三方开源库)
ts·aac·h264·封装·音视频容器