前面一篇博客讲解了,在打开scenecut的情况下,帧类型是由icost和pcost以及距离上一次编码IDR的距离来计算得到的最终结果的。
那么这些cost是如何计算得到的呢?
看下面代码:
static int scenecut_internal( x264_t *h, x264_mb_analysis_t *a, x264_frame_t **frames, int p0, int p1, int real_scenecut )
{
x264_frame_t *frame = frames[p1];
/* Don't do scenecuts on the right view of a frame-packed video. */
if( real_scenecut && h->param.i_frame_packing == 5 && (frame->i_frame&1) ) // frame_packing和3d有关就不管了
return 0;
slicetype_frame_cost( h, a, frames, p0, p1, p1 );
int icost = frame->i_cost_est[0][0]; //icost
int pcost = frame->i_cost_est[p1-p0][0];// pcost
这里主要是看p0, p1,参数,本来还应该有一个b参数
当前帧的cost其实可以表示为int cur_cost = frame->i_cost_est[ b - p0 ][ p1 - b];
再和上面的下标对比,就可以看得出来。b表示当前帧,为什么写成b?
因为我们姑且认为所有的帧都有两个方向上的参考帧,b - p0就是向前参考,p1 - b 就是后向参考,他们的差值就是距离参考帧的距离。
可以简单用表格表示为:
p0 | b | p1 |
当 p0 = b = p1的时候 就是当前帧,意思就是I帧没有参考帧。
接下来进到slicetype_frame_cost函数深入看下cost怎么计算的。(函数比较复杂,还没有完全弄懂,粗略介绍下!)
static int slicetype_frame_cost( x264_t *h, x264_mb_analysis_t *a,
x264_frame_t **frames, int p0, int p1, int b )// 0, 1, 1
{
int i_score = 0;// 临时变量cost值
int do_search[2];
const x264_weight_t *w = x264_weight_none;
x264_frame_t *fenc = frames[b]; //帧顺序 p0, b, p1, b的前后参考帧为p0和p1, 当p1 == b的时候,b这个位置就是p帧,没有后向参考
多线程跑
s[i] = (x264_slicetype_slice_t){ t, a, frames, p0, p1, b, dist_scale_factor, do_search, w, output_inter[i], output_intra[i] };
t->i_threadslice_start = ((h->mb.i_mb_height * i + h->param.i_lookahead_threads/2) / h->param.i_lookahead_threads);
t->i_threadslice_end = ((h->mb.i_mb_height * (i+1) + h->param.i_lookahead_threads/2) / h->param.i_lookahead_threads);
//一个线程计算几行的cost,
int thread_height = t->i_threadslice_end - t->i_threadslice_start;
int thread_output_size = thread_height + NUM_INTS;
memset( output_inter[i], 0, thread_output_size * sizeof(int) );
memset( output_intra[i], 0, thread_output_size * sizeof(int) );
output_inter[i][NUM_ROWS] = output_intra[i][NUM_ROWS] = thread_height;
x264_threadpool_run( h->lookaheadpool, (void*)slicetype_slice_cost, &s[i] );
进入slicetype_slice_cost看看
循环遍历计算每一个宏块的cost
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. */
/* The edge mbs seem to reduce the predictive quality of the
* whole frame's score, but are needed for a spatial distribution. */
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;
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 );
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-- )
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 );
}
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 )
{
x264_frame_t *fref0 = frames[p0];
x264_frame_t *fref1 = frames[p1];
x264_frame_t *fenc = frames[b];
const int b_bidir = (b < p1);// 是B帧
const int i_mb_x = h->mb.i_mb_x;
const int i_mb_y = h->mb.i_mb_y;
const int i_mb_stride = h->mb.i_mb_width;//一行宏块个数
const int i_mb_xy = i_mb_x + i_mb_y * i_mb_stride;//宏块坐标
const int i_stride = fenc->i_stride_lowres;
const int i_pel_offset = 8 * (i_mb_x + i_mb_y * i_stride);
const int i_bipred_weight = h->param.analyse.b_weighted_bipred ? 64 - (dist_scale_factor>>2) : 32;
int16_t (*fenc_mvs[2])[2] = { b != p0 ? &fenc->lowres_mvs[0][b-p0-1][i_mb_xy] : NULL, b != p1 ? &fenc->lowres_mvs[1][p1-b-1][i_mb_xy] : NULL };
//前后两个方向的mv, 这里直接从fenc 中取内存, 自己没有内存
int (*fenc_costs[2]) = { b != p0 ? &fenc->lowres_mv_costs[0][b-p0-1][i_mb_xy] : NULL, b != p1 ? &fenc->lowres_mv_costs[1][p1-b-1][i_mb_xy] : NULL };
// 编码cost也是一样的道理
int b_frame_score_mb = (i_mb_x > 0 && i_mb_x < h->mb.i_mb_width - 1 &&
i_mb_y > 0 && i_mb_y < h->mb.i_mb_height - 1) ||
h->mb.i_mb_width <= 2 || h->mb.i_mb_height <= 2;
/*后续计算在这个参考帧中的icost和pcost*/