static inline void x264_reference_build_list( x264_t *h, int i_poc )
{
int b_ok;
/* build ref list 0/1 */
// 初始化 h->i_ref[0], h->i_ref[1]为0
// h->i_ref[0]为参考帧队列ref[0]的索引
// h->i_ref[1]为参考帧队列ref[1]的索引
h->mb.pic.i_fref[0] = h->i_ref[0] = 0;
h->mb.pic.i_fref[1] = h->i_ref[1] = 0;
// 如果当前的slice类型为SLICE_TYPE_I, 则返回
if( h->sh.i_type == SLICE_TYPE_I )
return;
for( int i = 0; h->frames.reference[i]; i++ )
{
// 如果帧被破坏, 则跳过继续
if( h->frames.reference[i]->b_corrupt )
continue;
// 将参考帧队列中i_poc小于当前i_poc的帧
// 放入参考帧队列ref[0];
// 否则放入ref[1]中
if( h->frames.reference[i]->i_poc < i_poc )
h->fref[0][h->i_ref[0]++] = h->frames.reference[i];
else if( h->frames.reference[i]->i_poc > i_poc )
h->fref[1][h->i_ref[1]++] = h->frames.reference[i];
}
// 依据参考帧与当前帧的poc距离来排序参考帧
// 排序参考帧, 并且获取各自参考帧列表的最近的参考帧
// 这里先依据POC距离来排序参考帧列表
// 然后在x264_reference_check_reorder中
// list0 依据 i_frame_num 来确定是否重序,
// list1 依然依据 poc 来确定是否重序
// 在函数x264_reference_check_reorder中,
// 设置重序标志,在x264_slice_header_init
// 利用这两个标志来确定将是否需要重序的标志
// 写进slice_header里,并通过x264_slice_write
// 写进码流
/* Order reference lists by distance from the current frame. */
for( int list = 0; list < 2; list++ )
{
h->fref_nearest[list] = h->fref[list][0];
do
{
b_ok = 1;
for( int i = 0; i < h->i_ref[list] - 1; i++ )
{
// 参考帧队列0放的是前向参考帧,而参考帧队列1放的是后向参考帧
// 前向参考帧列表按降序排列,而后向参考帧列表按升序排列
if( list ? h->fref[list][i+1]->i_poc < h->fref_nearest[list]->i_poc
: h->fref[list][i+1]->i_poc > h->fref_nearest[list]->i_poc )
h->fref_nearest[list] = h->fref[list][i+1];
if( x264_reference_distance( h, h->fref[list][i] ) > x264_reference_distance( h, h->fref[list][i+1] ) )
{
// 如果参考帧i到待编码帧的距离 大于 参考帧i+1到待编码帧的距离
// 交换在参考帧列表里的两参考帧
XCHG( x264_frame_t*, h->fref[list][i], h->fref[list][i+1] );
b_ok = 0;
break;
}
}
} while( !b_ok );
}
// h->sh.i_mmco_remove_from_end 在 x264_reference_hierarchy_reset
// 函数中被设置 (h->sh.i_mmco_remove_from_end = X264_MAX( ref + 2 - h->frames.i_max_dpb, 0 );)
// 标记清除参考帧列表0中的帧,从距离远的帧开始清除
// 清除h->sh.i_mmco_remove_from_end个参考帧
if( h->sh.i_mmco_remove_from_end )
for( int i = h->i_ref[0]-1; i >= h->i_ref[0] - h->sh.i_mmco_remove_from_end; i-- )
{
int diff = h->i_frame_num - h->fref[0][i]->i_frame_num;
h->sh.mmco[h->sh.i_mmco_command_count].i_poc = h->fref[0][i]->i_poc;
h->sh.mmco[h->sh.i_mmco_command_count++].i_difference_of_pic_nums = diff;
}
// 检查参考帧列表是否需要重序
x264_reference_check_reorder( h );
// 设定list0, list1参考帧数目
h->i_ref[1] = X264_MIN( h->i_ref[1], h->frames.i_max_ref1 );
h->i_ref[0] = X264_MIN( h->i_ref[0], h->frames.i_max_ref0 );
h->i_ref[0] = X264_MIN( h->i_ref[0], h->param.i_frame_reference ); // if reconfig() has lowered the limit
/* For Blu-ray compliance, don't reference frames outside of the minigop. */
if( IS_X264_TYPE_B( h->fenc->i_type ) && h->param.b_bluray_compat )
h->i_ref[0] = X264_MIN( h->i_ref[0], IS_X264_TYPE_B( h->fref[0][0]->i_type ) + 1 );
/* add duplicates */
if( h->fenc->i_type == X264_TYPE_P )
{
int idx = -1;
if( h->param.analyse.i_weighted_pred >= X264_WEIGHTP_SIMPLE )
{
x264_weight_t w[3];
w[1].weightfn = w[2].weightfn = NULL;
if( h->param.rc.b_stat_read )
x264_ratecontrol_set_weights( h, h->fenc );
if( !h->fenc->weight[0][0].weightfn )
{
h->fenc->weight[0][0].i_denom = 0;
SET_WEIGHT( w[0], 1, 1, 0, -1 );
idx = x264_weighted_reference_duplicate( h, 0, w );
}
else
{
if( h->fenc->weight[0][0].i_scale == 1<<h->fenc->weight[0][0].i_denom )
{
SET_WEIGHT( h->fenc->weight[0][0], 1, 1, 0, h->fenc->weight[0][0].i_offset );
}
x264_weighted_reference_duplicate( h, 0, x264_weight_none );
if( h->fenc->weight[0][0].i_offset > -128 )
{
w[0] = h->fenc->weight[0][0];
w[0].i_offset--;
h->mc.weight_cache( h, &w[0] );
idx = x264_weighted_reference_duplicate( h, 0, w );
}
}
}
h->mb.ref_blind_dupe = idx;
}
assert( h->i_ref[0] + h->i_ref[1] <= X264_REF_MAX );
// 将list0, list1参考帧数目保存在h->mb.pic.i_fref[0], [1]
h->mb.pic.i_fref[0] = h->i_ref[0];
h->mb.pic.i_fref[1] = h->i_ref[1];
}
static inline int x264_reference_distance( x264_t *h, x264_frame_t *frame )
{
// 返回待编码帧和参考帧列表里帧的距离
// 计算方法:待编码帧编码序号 与 参考帧编码序号 之差的绝对值
if( h->param.i_frame_packing == 5 )
return abs((h->fenc->i_frame&~1) - (frame->i_frame&~1)) +
((h->fenc->i_frame&1) != (frame->i_frame&1));
else
return abs(h->fenc->i_frame - frame->i_frame);
}