x264 如何判断当前帧要不要编码出IDR
x264_slicetype_analyse()->scenecut()
判断场景是否切换了,这种是opengop的方式,
还有一种是固定gop的方式,gop之间的帧数是固定的,不是由scenecut决定的。
看下scenecut函数实现
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) )
return 0;
slicetype_frame_cost( h, a, frames, p0, p1, p1 );
int icost = frame->i_cost_est[0][0];// 向前的参考
int pcost = frame->i_cost_est[p1-p0][0];// 计算两个前后参考帧之间的残差
float f_bias;
int i_gop_size = frame->i_frame - h->lookahead->i_last_keyframe;
float f_thresh_max = h->param.i_scenecut_threshold / 100.0;// scenecut_hold 默认 40
/* magic numbers pulled out of thin air */
float f_thresh_min = f_thresh_max * 0.25;
int res;
if( h->param.i_keyint_min == h->param.i_keyint_max )
f_thresh_min = f_thresh_max;
if( i_gop_size <= h->param.i_keyint_min / 4 || h->param.b_intra_refresh )
f_bias = f_thresh_min / 4;
else if( i_gop_size <= h->param.i_keyint_min )
f_bias = f_thresh_min * i_gop_size / h->param.i_keyint_min;
else
{
f_bias = f_thresh_min
+ ( f_thresh_max - f_thresh_min )
* ( i_gop_size - h->param.i_keyint_min )
/ ( h->param.i_keyint_max - h->param.i_keyint_min );
}
// 上面根据不同的情况计算 f_bias ,如果f_bias 为1,则一定是I帧
//比如当
res = pcost >= (1.0 - f_bias) * icost;// 帧间cost 大于带权的帧内cost ,则为场景切换了
if( res && real_scenecut )
{
int imb = frame->i_intra_mbs[p1-p0];
int pmb = NUM_MBS - imb;
x264_log( h, X264_LOG_DEBUG, "scene cut at %d Icost:%d Pcost:%d ratio:%.4f bias:%.4f gop:%d (imb:%d pmb:%d)\n",
frame->i_frame,
icost, pcost, 1. - (double)pcost / icost,
f_bias, i_gop_size, imb, pmb );
}
return res;
}
看这个函数调用的地方
if( IS_X264_TYPE_AUTO_OR_I( frames[1]->i_type ) &&
h->param.i_scenecut_threshold && scenecut( h, &a, frames, 0, 1, 1, orig_num_frames, i_max_search ) )
{//
if( frames[1]->i_type == X264_TYPE_AUTO )
frames[1]->i_type = X264_TYPE_I;
return;
}
/* Check scenecut on the first minigop. */
for( int j = 1; j < num_bframes+1; j++ )
{
if( frames[j]->i_forced_type == X264_TYPE_AUTO && IS_X264_TYPE_AUTO_OR_I( frames[j+1]->i_forced_type ) &&
h->param.i_scenecut_threshold && scenecut( h, &a, frames, j, j+1, 0, orig_num_frames, i_max_search ) )
{
frames[j]->i_type = X264_TYPE_P;
num_analysed_frames = j;
break;
}
}
为什么上面两个都是scenecut条件成立,但是设置的slice 类型不同呢?
注意看上面一个调用的地方,设置的是p1 的slice 类型,也就是后面一帧,下面这个是判断j + 1帧是场景切换后,设置j帧为P帧。
因为一个miniGOp必须以P帧结尾,B帧会前后参考,下一个gop的I帧不能被前面的B帧参考。