slicetype_frame_cost
/*
将帧b以slice为单位计算其开销,统计其总开销
其中p0表示b的前向参考帧,p1表示b的后向参考帧
若p0 = p1 = b,则表示没有参考帧,即I帧
若p1 = b,则表示只有前向参考帧,即P帧
作为I帧,所有宏块的cost = intra cost
作为P帧,所有宏块的cost = min( intra cost, inter cost)
作为B帧,所有宏块的cost = inter cost
其中每一个帧都带有开销矩阵i_cost_est[b-p0][p1-b]
表示帧b以p0为前向参考,p1为后向参考时的帧cost
*/
static int slicetype_frame_cost( x264_t *h, x264_mb_analysis_t *a,
x264_frame_t **frames, int p0, int p1, int b )
{
int i_score = 0; //作为帧cost进行返回
int do_search[2]; //用来标记帧b是否已经在 前向参考帧/后向参考帧 上进行了运动搜索
const x264_weight_t *w = x264_weight_none;
x264_frame_t *fenc = frames[b]; //得到帧b
/* Check whether we already evaluated this frame
* If we have tried this frame as P, then we have also tried
* the preceding frames as B. (is this still true?)
* 检查是否已经评估了该帧,如果我们已经把该帧当作P帧计算,
* 那我们仍然需要将前面的帧当作B帧来计算
* Also check that we already calculated the row SATDs for the current frame.
* 同样检查我们是否对该帧进行了行SATD计算. */
if( fenc->i_cost_est[b-p0][p1-b] >= 0 && (!h->param.rc.i_vbv_buffer_size || fenc->i_row_satds[b-p0][p1-b][0] != -1) )
//如果帧b已经计算了前向参考p0后向参考p1的cost,则直接得到开销
i_score = fenc->i_cost_est[b-p0][p1-b];
else //没有计算,则进行计算
{
int dist_scale_factor = 128;
/* For each list, check to see whether we have lowres motion-searched this reference frame before.
* 如果可以前向参考,且mv = 0x7fff,则将他们置0
* 如果可以后向参考,且mv = 0x7fff,同样将他们置0. */
do_search[0] = b != p0 && fenc->lowres_mvs[0][b-p0-1][0][0] == 0x7FFF;
do_search[1] = b != p1 && fenc->lowres_mvs[1][p1-b-1][0][0] == 0x7FFF;
if( do_search[0] )
{
if( h->param.analyse.i_weighted_pred && b == p1 )
{
x264_emms();
x264_weights_analyse( h, fenc, frames[p0], 1 );
w = fenc->weight[0];
}
fenc->lowres_mvs[0][b-p0-1][0][0] = 0;
}
if( do_search[1] ) fenc->lowres_mvs[1][p1-b-1][0][0] = 0;
if( p1 != p0 ) //非I帧,则计算距离因子dist_scale_factor
dist_scale_factor = ( ((b-p0) << 8) + ((p1-p0) >> 1) ) / (p1-p0);
int output_buf_size = h->mb.i_mb_height + (NUM_INTS + PAD_SIZE) * h->param.i_lookahead_threads;
int *output_inter[X264_LOOKAHEAD_THREAD_MAX+1];
int *output_intra[X264_LOOKAHEAD_THREAD_MAX+1];
output_inter[0] = h->scratch_buffer2;
output_intra[0] = output_inter[0] + output_buf_size;
{
if( h->param.i_lookahead_threads > 1 ) //多个lookahead线程
{
/* 申请X264_LOOKAHEAD_THREAD_MAX个threadslice线程所需要的数据结构体
* 其实可以优化成动态内存申请i_lookahead_threads个.
* 这个s[]里面存储着每个threadslice计算所需的数据及其输出 */
x264_slicetype_slice_t s[X264_LOOKAHEAD_THREAD_MAX];
for( int i = 0; i < h->param.i_lookahead_threads; i++ ) //针对各个lookahead线程
{
x264_t *t = h->lookahead_thread[i]; //得到lookahead第i个线程的句柄
/* FIXME move this somewhere else */
//将句柄h中的 运动估计方法/下采样运动估计质量/是否进行色度运动估计 的参数给第i个线程的句柄t
t->mb.i_me_method = h->mb.i_me_method;
t->mb.i_subpel_refine = h->mb.i_subpel_refine;
t->mb.b_chroma_me = h->mb.b_chroma_me;
//初始化threadslice计算所需数据及输出 s[i]
s[i] = (x264_slicetype_slice_t){ t, a, frames, p0, p1, b, dist_scale_factor, do_search, w,
output_inter[i], output_intra[i] };
//得到该threadslice计算的的起始行
t->i_threadslice_start = ((h->mb.i_mb_height * i + h->param.i_lookahead_threads/2) / h->param.i_lookahead_threads);
//得到该threadslice计算的结束行的下一行
t->i_threadslice_end = ((h->mb.i_mb_height * (i+1) + h->param.i_lookahead_threads/2) / h->param.i_lookahead_threads);
//计算该threadslice所需计算的行高
int thread_height = t->i_threadslice_end - t->i_threadslice_start;
/* 计算threadslice[i]计算的输出大小,即output_inter[i]和output_inter[i]的大小
* 每一行一个ROW_SATD输出,另外多申请4个空间用于存储额外数据:
* COST_EST COST_EST_AQ INTRA_MBS NUM_ROWS [ROW_SATD1 ... ROW_SATDn]*/
int thread_output_size = thread_height + NUM_INTS;
//初始化两个输出缓冲,大小为threadslice行高+NUM_INTS
memset( output_inter[i], 0, thread_output_size * sizeof(int) );
memset( output_intra[i], 0, thread_output_size * sizeof(int) );
//将行高赋值给两个输出缓冲的第4个位置
output_inter[i][NUM_ROWS] = output_intra[i][NUM_ROWS] = thread_height;
//PADDING?
output_inter[i+1] = output_inter[i] + thread_output_size + PAD_SIZE;
output_intra[i+1] = output_intra[i] + thread_output_size + PAD_SIZE;
//开启threadslice[i]进行计算,并将结果输出到output_inter[i]/output_intra[i]中
x264_threadpool_run( h->lookaheadpool, (void*)slicetype_slice_cost, &s[i] );
}
//等待各threadslice线程计算结束并退出
for( int i = 0; i < h->param.i_lookahead_threads; i++ )
x264_threadpool_wait( h->lookaheadpool, &s[i] );
}
else //单lookahead线程
{
//设置threadslice的起始行和结束行
h->i_threadslice_start = 0;
h->i_threadslice_end = h->mb.i_mb_height;
//初始化两个输出缓冲,大小为threadslice行高+NUM_INTS
memset( output_inter[0], 0, (output_buf_size - PAD_SIZE) * sizeof(int) );
memset( output_intra[0], 0, (output_buf_size - PAD_SIZE) * sizeof(int) );
//将行高赋值给两个输出缓冲的第4个位置
output_inter[0][NUM_ROWS] = output_intra[0][NUM_ROWS] = h->mb.i_mb_height;
//初始化threadslice计算所需数据及输出 s[i]
x264_slicetype_slice_t s = (x264_slicetype_slice_t){ h, a, frames, p0, p1, b, dist_scale_factor, do_search, w,
output_inter[0], output_intra[0] };
//计算slice的cost,将结果输出到output_inter[0]/output_intra[0]中
slicetype_slice_cost( &s );
}
/* Sum up accumulators */
if( b == p1 ) //若是I/P帧
fenc->i_intra_mbs[b-p0] = 0; //初始化IMB的个数为0
if( !fenc->b_intra_calculated ) //如果还没进行帧内计算
{
//初始化帧b的开销矩阵中[0][0]的cost为0
fenc->i_cost_est[0][0] = 0;
fenc->i_cost_est_aq[0][0] = 0;
}
//将帧b的cost矩阵对应置0
fenc->i_cost_est[b-p0][p1-b] = 0;
fenc->i_cost_est_aq[b-p0][p1-b] = 0;
//取出inter和intra的row_satd向量
int *row_satd_inter = fenc->i_row_satds[b-p0][p1-b];
int *row_satd_intra = fenc->i_row_satds[0][0];
for( int i = 0; i < h->param.i_lookahead_threads; i++ )
{
if( b == p1 ) //若是非B帧
fenc->i_intra_mbs[b-p0] += output_inter[i][INTRA_MBS]; //累计IMB的个数
if( !fenc->b_intra_calculated ) //若还没有进行帧内预测计算
{
//累计intra的cost和aq_cost到开销矩阵i_cost_est[0][0]中
fenc->i_cost_est[0][0] += output_intra[i][COST_EST];
fenc->i_cost_est_aq[0][0] += output_intra[i][COST_EST_AQ];
}
//累计inter的cost和aq_cost到开销矩阵i_cost_est[b-p0][p1-b]中
fenc->i_cost_est[b-p0][p1-b] += output_inter[i][COST_EST];
fenc->i_cost_est_aq[b-p0][p1-b] += output_inter[i][COST_EST_AQ];
if( h->param.rc.i_vbv_buffer_size ) //若使用了vbv
{
int row_count = output_inter[i][NUM_ROWS];
/* 将output_inter中计算得到的row_satd赋值给row_satd_inter
* 若还没进行帧内计算,则将output_intra中计算得到的row_satd赋值给row_satd_intra */
memcpy( row_satd_inter, output_inter[i] + NUM_INTS, row_count * sizeof(int) );
if( !fenc->b_intra_calculated )
memcpy( row_satd_intra, output_intra[i] + NUM_INTS, row_count * sizeof(int) );
//更新row_satd_inter/row_satd_intra指针
row_satd_inter += row_count;
row_satd_intra += row_count;
}
}
//取出帧cost
i_score = fenc->i_cost_est[b-p0][p1-b];
if( b != p1 ) //若是B帧,则根据i_bframe_bias对帧cost进行调整
i_score = (uint64_t)i_score * 100 / (120 + h->param.i_bframe_bias);
else //若I/P,则设置帧内预测计算标致
fenc->b_intra_calculated = 1; //标记已经进行了intra计算
//将帧开销写回帧b的开销矩阵中对应的位置
fenc->i_cost_est[b-p0][p1-b] = i_score;
x264_emms();
}
}
return i_score; //返回帧cost
}
slicetype_slice_cost
/*
将slice以mb为单位计算cost
将结果输出到s->output_inter/s->output_intra中
*/
static void slicetype_slice_cost( x264_slicetype_slice_t *s )
{
//取出传入的句柄
x264_t *h = s->h;
/* Lowres lookahead goes backwards because the MVs are used as predictors in the main encode.
* This considerably improves MV prediction overall.
* 进行低分辨率lookahead回溯,由于MV将会被用在主编码预测中,所以回溯可以明显的提示mv的预测精度 */
/* The edge mbs seem to reduce the predictive quality of the
* whole frame's score, but are needed for a spatial distribution.
* 边缘宏块会降低整帧score(即cost)的预测质量,但是他们需要用于空间分布? */
/* do_edges表示是否计算边界
* 若 使用宏块树 || 使用vbv || 行<=2 || 列<=2 ,则使用计算边界宏块
* 否则不计算边界宏块 */
int do_edges = h->param.rc.b_mb_tree || h->param.rc.i_vbv_buffer_size || h->mb.i_mb_width <= 2 || h->mb.i_mb_height <= 2;
//计算slice的起始行和结束行
int start_y = X264_MIN( h->i_threadslice_end - 1, h->mb.i_mb_height - 2 + do_edges );
int end_y = X264_MAX( h->i_threadslice_start, 1 - do_edges );
//计算slice的起始列和结束列
int start_x = h->mb.i_mb_width - 2 + do_edges;
int end_x = 1 - do_edges;
for( h->mb.i_mb_y = start_y; h->mb.i_mb_y >= end_y; h->mb.i_mb_y-- ) //遍历每一行
for( h->mb.i_mb_x = start_x; h->mb.i_mb_x >= end_x; h->mb.i_mb_x-- )//遍历每一列
//对每一个宏块进行cost计算
slicetype_mb_cost( h, s->a, s->frames, s->p0, s->p1, s->b, s->dist_scale_factor,
s->do_search, s->w, s->output_inter, s->output_intra );
}
slicetype_mb_cost
/*
对帧b的当前宏块计算其intra/inter_cost,intra/inter_aq_cost根据intra/inter_cost进行修正
若intra_cost<inter_cost,则判定为IMB,累计IMB个数到output_inter[INTRA_MBS]中,且令inter_cost=intra_cost
若当前宏块计入全帧的score中,则
将intra/inter_cost累加进各自的output_intra/inter[COST_EST]中
将intra/inter_aq_cost累加进各自的output_intra/inter[COST_AQ_EST]中
将intra/inter_aq_cost累加进各自所在行的output_intra/inter[ROW_SATD]中
*/
static void slicetype_mb_cost( x264_t *h, x264_mb_analysis_t *a,
x264_frame_t **frames, int p0, int p1, int b,
int dist_scale_factor, int do_search[2], const x264_weight_t *w,
int *output_inter, int *output_intra )