AVC的参考帧管理有两部分构成。第一部分为DPB buffer的管理,它决定了每一帧编码之后,下一帧编码之前DPB buffer中的帧的变化情况,也就是哪些帧要留在buffer中,哪些帧要从buffer中移除(或者说标记为可以被移除)。而第二部分为参考帧队列构建,即每一帧编码前,需要从buffer中依次拿取哪一帧,放入当前的参考帧队列中。
1. DPB buffer的管理。
步骤1:解码完一帧,随后判断当前帧的帧类型。如果当前帧为一个IDR帧,则进行以下操作:
(1)将所有的参考帧标记为“不作为参考”;
(2)对于当前帧,如果long_term_reference_flag为0,则该IDR帧被标记为“作为短期参考”,且MaxLongTermFrameIdx设为“无长期参考帧索引”;如果long_term_reference_flag为1,则该IDR帧被标记为“作为长期参考”,其LongTermFrameIdx设为0,并且MaxLongTermFrameIdx设为0;
如果当前帧为非IDR帧,则执行以下操作:
(1)如果adaptive_ref_pic_marking_mode_flag为1,则进行自适应内存控制法标记参考帧;如果adaptive_ref_pic_marking_mode_flag为0,则进行滑动窗口法标记参考帧;
(2)如果当前帧没有因为memory_management_control_operation的值等于6而被标记为“用于长期参考”,则该帧被标记为“用于短期参考”。
步骤2:对于当前帧非IDR帧,开始执行滑动窗口法或者自适应内存控制法标记DPB中的参考帧。
滑动窗口法:
如果当前图像是一个 complementary reference field pair 中按照解码顺序的第二个场,且第一场被标记为“用作短期参考”,那么当前图像和该complementary reference field pair都被标记为“作为短期参考”。
否则的话,根据如下步骤执行:设numShortTerm为作为短期参考的编码帧、编码场或互补场对的总数,numLongTerm为长期参考帧/场/场对的总数;当numShortTerm与numLongTerm之和达到Max(max_num_ref_frames, 1)时,在满足numShortTerm大于0的前提下,FrameNumWrap值最小的那个作为短期参考的编码帧、编码场或互补场对将被标记为“不作为参考”。
自适应内存控制法:
执行自适应内存控制法标记参考帧的条件是adaptive_ref_pic_marking_mode_flag为1,此时slice_header中会包含一些附加的语法元素信息:
- memory_management_control_operation;
- difference_of_pic_nums_minus1;
- long_term_pic_num;
- long_term_frame_idx;
- max_long_term_frame_idx_plus1
- long_term_frame_idx;
- long_term_pic_num;
- difference_of_pic_nums_minus1;
其中,memory_management_control_operation可以取的范围为1~6,分别代表了不同的操作。如表1所示
图表 1 memory_management_control_operation的含义
(1)将短期参考帧标记为“不作为参考”
当memory_management_control_operation为1时,自适应内存控制过程会将某一个短期参考帧标记为“不作为参考”。具体的执行过程为:首先计算picNumX。
计算方法为:picNumX = CurrPicNum − ( difference_of_pic_nums_minus1 + 1 )
其中,CurrPicNum为当前帧的frame_number,difference_of_pic_nums_minus1从dec_ref_pic_marking中解析得到。
对于帧编码的图像,PicNum等于picNumX的短期参考帧会被标记为“不作为参考”;
(2)将长期参考帧标记为“不作为参考”
当memory_management_control_operation为2时,自适应内存控制过程会将某一个长期参考帧标记为“不作为参考”。具体的执行过程很简单,索引为LongTermPicNum等同于long_term_pic_num的长期参考帧将被标记为不作为参考。
(3)将短期参考帧标记为“长期参考帧”
当memory_management_control_operation为3时,自适应内存控制过程会将某一个短期参考帧标记为“作为长期参考帧”。在这种情况下,码流中会同时包含difference_of_pic_nums_minus1以及long_term_frame_idx这两个值。执行过程如下:
按照(1)中的方法计算picNumX;
如果long_term_frame_idx对应的长期参考帧存在,则该长期参考帧标记为“不作为参考”;
对于帧编码图像,由picNumX所代表的短期参考帧,将被标记为长期参考帧,并将对应的LongTermFrameIdx设为long_term_frame_idx。
(4)计算MaxLongTermFrameIdx
当memory_management_control_operation为4时,执行计算MaxLongTermFrameIdx的操作。计算过程如下:
如果码流中解析出的max_long_term_frame_idx_plus1的值为0,则MaxLongTermFrameIdx被设置为“无长期参考帧索引”;
否则,MaxLongTermFrameIdx的值设置为max_long_term_frame_idx_plus1-1。所有被标记为“用作长期参考”且LongTermFrameIdx大于了MaxLongTermFrameIdx的图像都会被标记为“不作为参考”。
(5)清空参考帧列表
当memory_management_control_operation为5时,执行清空参考帧列表操作。该过程会将所有参考帧标记为“不作为参考”并将MaxLongTermFrameIdx设置为“无长期参考帧索引”。
(6)将当前帧标记为长期参考帧
当memory_management_control_operation为6时,将当前帧标记为长期参考帧。在这种情况下,需从码流中解析出long_term_frame_idx。执行过程如下:
如果long_term_frame_idx对应的长期参考帧存在,则该长期参考帧标记为“不作为参考”;将当前帧标记为“作为长期参考”,并将其LongTermFrameIdx设置为long_term_frame_idx;在当前帧标记完成后,所有被标记为“作为参考帧”的帧、场和互补场对的数量综合不能超过Max( max_num_ref_frames, 1 )规定的值。
2.参考帧列表的构建
分两步进行:第一步参考帧队列初始化;第二步为参考帧队列调整。
第一步:参考帧队列初始化以B帧为例进行描述
在两个参考帧列表RefPicList0和RefPicList1中,短期参考帧的顺序按照显示顺序,即POC进行排列。在排列短期参考帧时,会将当前帧的POC与DPB中参考帧的POC进行比较,然后根据结果进行以下操作:
对参考帧列表refPicList0:
如果DPB中短期参考帧的POC小于当前帧的DPB,则短期参考帧按照POC的降序排列在参考帧列表refPicList0的前部,其余短期参考帧按照POC的升序紧随其后排列;DPB中的长期参考帧按照LongTermPicNum递增的顺序在短期参考帧之后排列;
对参考帧列表refPicList1:
如果DPB中短期参考帧的POC大于当前帧的DPB,则短期参考帧按照POC的升序排列在参考帧列表refPicList1的前部,其余短期参考帧按照POC的降序紧随其后排列;DPB中的长期参考帧按照LongTermPicNum递增的顺序在短期参考帧之后排列;
若refPicList1包含多于1个参考帧,且refPicList1与refPicList0等同时,refPicList1中前两个参考帧refPicList1[0]和refPicList1[1]将进行交换。
在参考帧列表初始化后,还需要一项附加操作,即根据图像参数集PPS的参数来计算参考帧列表中图像数目的上限,该参数即为:
num_ref_idx_l0_active_minus1;
num_ref_idx_l1_active_minus1;
第二步:参考帧队列调整
若ref_pic_list_modification_flag_l0或者ref_pic_list_modification_flag_l1(对B帧)的值为1,参考帧列表RefPicList0和RefPicList1(对B帧)会进行修改操作;
两个标识位ref_pic_list_modification_flag_l0和ref_pic_list_modification_flag_l1保存在slice_header中的ref_pic_list_modification结构中,该结构的定义如下:
以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,参考帧列表的修改过程完成;
参考帧列表修改过程以refIdxL0作为输入参数,执行完成后的结果也返回给refIdxL0。
(3)短期参考帧的修改
修改短期参考帧主要步骤如下:
(3.1)计算picNumLXPred
picNumLXPred可以认为是下一步骤中要计算的变量picNumLXNoWrap的预测值。当slice_header中出现第一个modification_of_pic_nums_idc值时,picNumLXPred设置为CurrPicNum,即当前帧的frame_num;随后,每当计算得到一个picNumLXNoWrap后,这一个picNumLXNoWrap值都会赋值给picNumLXPred。
(3.2)计算picNumLXNoWrap
计算picNumLXNoWrap的方法根据modification_of_pic_nums_idc的取值不同而不同。
当modification_of_pic_nums_idc取值为0时,其含义为码流中读出的abs_diff_pic_num_minus1为picNumLXNoWrap为相对于picNumLXPred的负增量,即需要从picNumLXPred中减去该值。计算方法如下:
if( picNumLXPred − ( abs_diff_pic_num_minus1 + 1 ) < 0 )
picNumLXNoWrap = picNumLXPred − ( abs_diff_pic_num_minus1 + 1 ) + MaxPicNum
else
picNumLXNoWrap = picNumLXPred − ( abs_diff_pic_num_minus1 + 1 )
当modification_of_pic_nums_idc取值为0时,其含义为码流中读出的abs_diff_pic_num_minus1为picNumLXNoWrap为相对于picNumLXPred的正增量,即需要从picNumLXPred中加上该值。计算方法如下:
if( picNumLXPred + ( abs_diff_pic_num_minus1 + 1 ) >= MaxPicNum )
picNumLXNoWrap = picNumLXPred + ( abs_diff_pic_num_minus1 + 1 ) − MaxPicNum
else
picNumLXNoWrap = picNumLXPred + ( abs_diff_pic_num_minus1 + 1 )
(3.3)计算picNumLX
picNumLX的值通过picNumLXNoWrap与当前frame_num的值比较后计算得到,具体计算方式如下:
if( picNumLXNoWrap > CurrPicNum )
picNumLX = picNumLXNoWrap − MaxPicNum
else
picNumLX = picNumLXNoWrap
该步骤中得到的picNumLX应等于参考帧列表中的某一个短期参考帧的PicNum值。
(3.4)修改参考帧列表
在计算得到picNumLX后,配合传入的的索引值refIdxLX,接着进行参考帧列表的修改。其方法为将picNumLX对应的短期参考帧置于refIdxLX位置,并且清除掉列表中PicNum等于picNumLX的参考帧。具体计算方法如下:
for( cIdx = num_ref_idx_lX_active_minus1 + 1; cIdx > refIdxLX; cIdx− − )
RefPicListX[ cIdx ] = RefPicListX[ cIdx − 1]
RefPicListX[ refIdxLX++ ] = short-term reference picture with PicNum equal to picNumLX
nIdx = refIdxLX
for( cIdx = refIdxLX; cIdx <= num_ref_idx_lX_active_minus1 + 1; cIdx++ )
if( PicNumF( RefPicListX[ cIdx ] ) != picNumLX )
RefPicListX[ nIdx++ ] = RefPicListX[ cIdx ]
(4)长期参考帧的修改
修改长期参考帧的方法相对简单。在ref_pic_list_modification结构中的long_term_pic_num即表示待操作的长期参考帧索引。修改的方式类似短期参考帧的修改。具体计算方法如下:
for( cIdx = num_ref_idx_lX_active_minus1 + 1; cIdx > refIdxLX; cIdx− − )
RefPicListX[ cIdx ] = RefPicListX[ cIdx − 1]
RefPicListX[ refIdxLX++ ] = long-term reference picture with LongTermPicNum equal to long_term_pic_num
nIdx = refIdxLX
for( cIdx = refIdxLX; cIdx <= num_ref_idx_lX_active_minus1 + 1; cIdx++ )
if( LongTermPicNumF( RefPicListX[ cIdx ] ) != long_term_pic_num )
RefPicListX[ nIdx++ ] = RefPicListX[ cIdx ]