/*
lookahead队列中的帧类型确定
过程:
1.计算每一帧的时长duration
2.对lookahead里的每一帧进行帧类型分析
·x264_ratecontrol_slice_type()
·x264_slicetpye_analyse()
3.对一段BBB...BBB(IDR/I/P)
1.检查BREF是否合规
2.检查Keyframe,设置为openGOP?I:IDR
3.检查是否超过了最大关键帧距离,控制GOPsize,将当前帧设置为openGOP?I:IDR
4.检查I帧,openGOP?I:IDR
5.检查IDR帧,将前一帧AUTO/B改为P
6.检查是否超出两参考帧间最大允许的连续B帧数量,或是否lookahead最后一帧,AUTO/B=>P
7.检查剩下的AUTO,AUTO=>B
8.检查是否可以使用BREF,将最中间的帧B=>BREF
9.计算每个B帧的帧开销,计算最后一帧的intra cost,若最后一帧为P,还要计算其inter cost
10.若使用P权重??不知道是啥
11.按照帧类型重新组织这些帧,播放顺序=>编码顺序,即原BBB BREF BBB (IDR/I/P) => (IDR/I/P) BREF BBBBBB
12.进行calculate_duration(),还不知道计算啥
*/
void x264_slicetype_decide( x264_t *h )
{
x264_frame_t *frames[X264_BFRAME_MAX+2];
x264_frame_t *frm;
int bframes;
int brefs;
if( !h->lookahead->next.i_size ) //若不用lookahead,则return
return;
int lookahead_size = h->lookahead->next.i_size;
/****************************
计算每一帧的时长duration
*****************************/
for( int i = 0; i < h->lookahead->next.i_size; i++ ) //遍历next列表
{
if( h->param.b_vfr_input ) //variable framerate
{
if( lookahead_size-- > 1 ) //不是最后一帧,则 当前帧时长 = 2 * (后帧pts - 当前帧pts)
h->lookahead->next.list[i]->i_duration = 2 * (h->lookahead->next.list[i+1]->i_pts - h->lookahead->next.list[i]->i_pts);
else // 最后一帧时长 = 前一帧时长
h->lookahead->next.list[i]->i_duration = h->i_prev_duration;
}
else
/* 根据frame的结构来得到duration,其中i_pic_struct取值:
PIC_STRUCT_AUTO = 0, // automatically decide (default)
PIC_STRUCT_PROGRESSIVE = 1, // progressive frame
// "TOP" and "BOTTOM" are not supported in x264 (PAFF only)
PIC_STRUCT_TOP_BOTTOM = 4, // top field followed by bottom
PIC_STRUCT_BOTTOM_TOP = 5, // bottom field followed by top
PIC_STRUCT_TOP_BOTTOM_TOP = 6, // top field, bottom field, top field repeated
PIC_STRUCT_BOTTOM_TOP_BOTTOM = 7, // bottom field, top field, bottom field repeated
PIC_STRUCT_DOUBLE = 8, // double frame
PIC_STRUCT_TRIPLE = 9, // triple frame */
h->lookahead->next.list[i]->i_duration = delta_tfi_divisor[h->lookahead->next.list[i]->i_pic_struct];
//将上一个duration更新为当前帧的duration
h->i_prev_duration = h->lookahead->next.list[i]->i_duration;
/* 通过i_duration和sps里的i_time_scale计算f_duration,
f_duration是以秒为单位的float,
i_duration是以sps里面time_scale为单位的int */
h->lookahead->next.list[i]->f_duration = (double)h->lookahead->next.list[i]->i_duration
* h->sps->vui.i_num_units_in_tick
/ h->sps->vui.i_time_scale;
//计算Presentation field count
if( h->lookahead->next.list[i]->i_frame > h->i_disp_fields_last_frame && lookahead_size > 0 )
{
h->lookahead->next.list[i]->i_field_cnt = h->i_disp_fields;
h->i_disp_fields += h->lookahead->next.list[i]->i_duration;
h->i_disp_fields_last_frame = h->lookahead->next.list[i]->i_frame;
}
else if( lookahead_size == 0 )
{
h->lookahead->next.list[i]->i_field_cnt = h->i_disp_fields;
h->lookahead->next.list[i]->i_duration = h->i_prev_duration;
}
} //end of for( int i = 0; i < h->lookahead->next.i_size; i++ )
/*****************************************
对lookahead里的每一帧进行帧类型分析
******************************************/
if( h->param.rc.b_stat_read ) //若stat_read读取编码配置文件
{
/* Use the frame types from the first pass */
//将lookahead队列中next的所有帧进行码率控制帧类型选择
for( int i = 0; i < h->lookahead->next.i_size; i++ )
h->lookahead->next.list[i]->i_type =
x264_ratecontrol_slice_type( h, h->lookahead->next.list[i]->i_frame );
}
//允许B帧 && B帧自适应 || 使用场景转换 || 使用mb-tree || 使用vbv&&码率控制
else if( (h->param.i_bframe && h->param.i_bframe_adaptive)
|| h->param.i_scenecut_threshold
|| h->param.rc.b_mb_tree
|| (h->param.rc.i_vbv_buffer_size && h->param.rc.i_lookahead) )
x264_slicetype_analyse( h, 0 ); //进行帧类型分析,得到每个frame的i_type
/******************************************************************
逐个检查各帧的帧类型是否合理
边遍历边统计B帧个数,直到出现非B帧为止,即BBB...BBB(IDR/I/P)
*******************************************************************/
for( bframes = 0, brefs = 0;; bframes++ )
{
frm = h->lookahead->next.list[bframes]; //取lookahead中next中的帧
if( frm->i_forced_type != X264_TYPE_AUTO && frm->i_type != frm->i_forced_type &&
!(frm->i_forced_type == X264_TYPE_KEYFRAME && IS_X264_TYPE_I( frm->i_type )) )
{ //输出log,表明该帧的forced类型被改成某类型
x264_log( h, X264_LOG_WARNING, "forced frame type (%d) at %d was changed to frame type (%d)\n",
frm->i_forced_type, frm->i_frame, frm->i_type );
}
/* 检查BREF类型是否合规
若使用的是X264_B_PYRAMID_NONE(0)或X264_B_PYRAMID_STRICT(1)
且brefs == h->param.i_bframe_pyramid??不明白
则将当前BREF改为B
若使用的是X264_B_PYRAMID_NORMAL(2)
且有BREF帧
且最大参考帧数量<=BREF帧数量+3
则将当前BREF改为B
*/
if( frm->i_type == X264_TYPE_BREF && h->param.i_bframe_pyramid < X264_B_PYRAMID_NORMAL &&
brefs == h->param.i_bframe_pyramid )
{
frm->i_type = X264_TYPE_B;
x264_log( h, X264_LOG_WARNING, "B-ref at frame %d incompatible with B-pyramid %s \n",
frm->i_frame, x264_b_pyramid_names[h->param.i_bframe_pyramid] );
}
/* pyramid with multiple B-refs needs a big enough dpb that the preceding P-frame stays available.
smaller dpb could be supported by smart enough use of mmco, but it's easier just to forbid it. */
else if( frm->i_type == X264_TYPE_BREF && h->param.i_bframe_pyramid == X264_B_PYRAMID_NORMAL &&
brefs && h->param.i_frame_reference <= (brefs+3) )
{
frm->i_type = X264_TYPE_B;
x264_log( h, X264_LOG_WARNING, "B-ref at frame %d incompatible with B-pyramid %s and %d reference frames\n",
frm->i_frame, x264_b_pyramid_names[h->param.i_bframe_pyramid], h->param.i_frame_reference );
}
/*检查关键帧*/
if( frm->i_type == X264_TYPE_KEYFRAME )
frm->i_type = h->param.b_open_gop ? X264_TYPE_I : X264_TYPE_IDR;
/* Limit GOP size 限制GOP大小
检查当前帧距离上一个关键帧的距离>=最大关键帧间距,
则超出GOP的size,将当前帧改为openGOP ? I : IDR */
if( (!h->param.b_intra_refresh || frm->i_frame == 0) && frm->i_frame - h->lookahead->i_last_keyframe >= h->param.i_keyint_max )
{
if( frm->i_type == X264_TYPE_AUTO || frm->i_type == X264_TYPE_I ) //若是auto或I帧
frm->i_type = h->param.b_open_gop && h->lookahead->i_last_keyframe >= 0 ? X264_TYPE_I : X264_TYPE_IDR;
int warn = frm->i_type != X264_TYPE_IDR; //若当前非IDR,则warn = 1
if( warn && h->param.b_open_gop )//若warn 且 openGOP
warn &= frm->i_type != X264_TYPE_I;
if( warn ) //警告,GOPsize超标
{
x264_log( h, X264_LOG_WARNING, "specified frame type (%d) at %d is not compatible with keyframe interval\n", frm->i_type, frm->i_frame );
//将当前帧类型改为I/IDR
frm->i_type = h->param.b_open_gop && h->lookahead->i_last_keyframe >= 0 ? X264_TYPE_I : X264_TYPE_IDR;
}
}
/* 检查当前帧是I帧时,且满足距离上一关键帧距离>=最小关键帧距离 */
if( frm->i_type == X264_TYPE_I && frm->i_frame - h->lookahead->i_last_keyframe >= h->param.i_keyint_min )
{
if( h->param.b_open_gop ) //若是openGOP
{
h->lookahead->i_last_keyframe = frm->i_frame; // Use display order 更新上一个关键帧为当前帧
if( h->param.b_bluray_compat )
h->lookahead->i_last_keyframe -= bframes; // Use bluray order
frm->b_keyframe = 1; //更新当前帧的关键帧标识
}
else //non-openGOP
frm->i_type = X264_TYPE_IDR;
}
/* 检查当前IDR,将其前一帧改为P */
if( frm->i_type == X264_TYPE_IDR ) //若是IDR
{
/* Close GOP */
h->lookahead->i_last_keyframe = frm->i_frame; //更新上一个关键帧为当前帧
frm->b_keyframe = 1; //设置关键帧标识
//将前一帧改为P帧,并将B帧计数器-1
if( bframes > 0 )
{
bframes--;
h->lookahead->next.list[bframes]->i_type = X264_TYPE_P; //IDR前一帧设置为P帧
}
}
/* 若B帧的数量已经达到了两个参考帧之间所允许的B帧数量上限i_bframe
或当前帧已是最后一帧
则当前帧AUTO/B=>P,I/IDR则不变 */
if( bframes == h->param.i_bframe ||
!h->lookahead->next.list[bframes+1] )
{
if( IS_X264_TYPE_B( frm->i_type ) ) //若是B帧则打印log
x264_log( h, X264_LOG_WARNING, "specified frame type is not compatible with max B-frames\n" );
if( frm->i_type == X264_TYPE_AUTO //若是AUTO/B帧,则改为P帧
|| IS_X264_TYPE_B( frm->i_type ) )
frm->i_type = X264_TYPE_P;
}
/*若是可BREF,则bref计数器累加*/
if( frm->i_type == X264_TYPE_BREF )
brefs++;
/*若是AUTO,则改为B*/
if( frm->i_type == X264_TYPE_AUTO )
frm->i_type = X264_TYPE_B;
/*若是非B帧则退出循环*/
else if( !IS_X264_TYPE_B( frm->i_type ) ) break;
} //end of for( bframes = 0, brefs = 0;; bframes++ )
if( bframes ) //如果有B帧
h->lookahead->next.list[bframes-1]->b_last_minigop_bframe = 1; //标记最后一个B帧
h->lookahead->next.list[bframes]->i_bframes = bframes; //存储两个参考帧之间的B帧数量
/* insert a bref into the sequence
如果允许B帧被参考,且B帧数量>1,且目前可参考B帧数量=0
则取最中间的那一个B帧作为BREF */
if( h->param.i_bframe_pyramid && bframes > 1 && !brefs )
{
h->lookahead->next.list[(bframes-1)/2]->i_type = X264_TYPE_BREF; //在B帧最中间插入BREF
brefs++; //brefs累加器累加
}
/*****************************************************************
calculate the frame costs ahead of time for x264_rc_analyse_slice while we still have lowres
若码率控制不是CQP方法,则在进行码率控制分析之前计算帧开销
****************************************************************/
if( h->param.rc.i_rc_method != X264_RC_CQP )
{
x264_mb_analysis_t a;
int p0, p1, b;
p1 = b = bframes + 1; //
lowres_context_init( h, &a ); //低分辨率上下文初始化
frames[0] = h->lookahead->last_nonb; //取上一个非B帧
//将bframes+1个帧拷贝下来,即BBB...BBB(IDR/I/P)
memcpy( &frames[1], h->lookahead->next.list, (bframes+1) * sizeof(x264_frame_t*) );
//若最后一帧是IDR/I帧,则p0 = p1 = b = bframes+1
if( IS_X264_TYPE_I( h->lookahead->next.list[bframes]->i_type ) )
p0 = bframes + 1;
else //最后一帧P帧,p0 = 0,即上一个非B帧,p1 = b = bframes+1
p0 = 0;
//计算最后一帧b的开销
slicetype_frame_cost( h, &a, frames, p0, p1, b );
if( (p0 != p1 || bframes) && h->param.rc.i_vbv_buffer_size )
//非I帧 || 有B帧 , && 使用vbv
{
/* We need the intra costs for row SATDs.
同时我们还要计算帧b作为I帧时候的cost*/
slicetype_frame_cost( h, &a, frames, b, b, b );
/* We need B-frame costs for row SATDs.
计算每一个B帧的帧开销 */
p0 = 0;
for( b = 1; b <= bframes; b++ )
{
if( frames[b]->i_type == X264_TYPE_B )
for( p1 = b; frames[p1]->i_type == X264_TYPE_B; )
p1++;
else
p1 = bframes + 1;
slicetype_frame_cost( h, &a, frames, p0, p1, b );
if( frames[b]->i_type == X264_TYPE_BREF )
p0 = b;
}
}
}
/* Analyse for weighted P frames */
if( !h->param.rc.b_stat_read && h->lookahead->next.list[bframes]->i_type == X264_TYPE_P
&& h->param.analyse.i_weighted_pred >= X264_WEIGHTP_SIMPLE )
//不使用stat_read && 最后一帧是P帧 &&
{
x264_emms();
x264_weights_analyse( h, h->lookahead->next.list[bframes], h->lookahead->last_nonb, 0 );
}
/* shift sequence to coded order.
use a small temporary list to avoid shifting the entire next buffer around
将定义好的帧类型序列调整顺序
即 播放顺序 => 编码顺序 */
int i_coded = h->lookahead->next.list[0]->i_frame;
if( bframes )
{
int idx_list[] = { brefs+1, 1 };
for( int i = 0; i < bframes; i++ )
{
int idx = idx_list[h->lookahead->next.list[i]->i_type == X264_TYPE_BREF]++;
frames[idx] = h->lookahead->next.list[i];
frames[idx]->i_reordered_pts = h->lookahead->next.list[idx]->i_pts;
}
frames[0] = h->lookahead->next.list[bframes];
frames[0]->i_reordered_pts = h->lookahead->next.list[0]->i_pts;
memcpy( h->lookahead->next.list, frames, (bframes+1) * sizeof(x264_frame_t*) );
}
/* 计算时间?好像跟SPS/时间戳有关 */
for( int i = 0; i <= bframes; i++ )
{
h->lookahead->next.list[i]->i_coded = i_coded++;
if( i )
{
calculate_durations( h, h->lookahead->next.list[i], h->lookahead->next.list[i-1], &h->i_cpb_delay, &h->i_coded_fields );
h->lookahead->next.list[0]->f_planned_cpb_duration[i-1] = (double)h->lookahead->next.list[i]->i_cpb_duration *
h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
}
else
calculate_durations( h, h->lookahead->next.list[i], NULL, &h->i_cpb_delay, &h->i_coded_fields );
}
}
帧类型决策-x264_slicetype_decide()
最新推荐文章于 2024-07-28 11:33:23 发布