scenecut_internal
/*
只判断p1相对于p0是否进是两幅不同的场景
若是若是不同的场景就返回1,否则0
分别计算将p1作为I帧的开销intra_cost 和 p1作为P帧以p0为参考帧时的开销inter_cost
若inter_cost >= (1.0 - f_bias) * intra_cost,则认定p1相对p0是不同的场景
*/
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]; //取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 ); //计算当前帧作为P帧的开销
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;//得到GOP的大小,即当前帧-上一个关键帧
float f_thresh_max = h->param.i_scenecut_threshold / 100.0; //得到场景切换的阈值,即max
/* magic numbers pulled out of thin air */
float f_thresh_min = f_thresh_max * 0.25; //得到场景切换阈值的最小值,即min=max*0.25
int res;
if( h->param.i_keyint_min == h->param.i_keyint_max ) //若关键帧之间的最大最小距离相等,即为一个定值
f_thresh_min = f_thresh_max; //则最大最小场景切换阈值为定值=max
//根据当前帧距离上一个关键帧的距离来计算f_bias
if( i_gop_size <= h->param.i_keyint_min / 4 || h->param.b_intra_refresh )
//当前帧距离上一个关键帧的距离 <= 关键帧之间最小距离的1/4 ,或允许使用帧内预测进行刷新
f_bias = f_thresh_min / 4;
else if( i_gop_size <= h->param.i_keyint_min )
//关键帧之间最小距离的1/4 < 当前帧距离上一个关键帧的距离 <= 关键帧之间最小距离
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 );
}
//P帧的开销 >= (1.0-f_bias) * I帧的开销?
res = pcost >= (1.0 - f_bias) * icost;
if( res && real_scenecut ) //若res且real_scenenut,则判定为不同的场景,输出log
{
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 );
}
/* 若P帧的开销 >= (1.0-f_bias) * I帧的开销,则判定为p1相对p0是不同的场景,返回1
* 否则认为是相同的场景,返回0. */
return res;
}
scenecut
static int scenecut( x264_t *h, x264_mb_analysis_t *a, x264_frame_t **frames, int p0, int p1, int real_scenecut, int num_frames, int i_max_search )
{
/* Only do analysis during a normal scenecut check. */
if( real_scenecut && h->param.i_bframe ) //允许B帧且real_scenecut?
{
int origmaxp1 = p0 + 1;
/* Look ahead to avoid coding short flashes as scenecuts. */
if( h->param.i_bframe_adaptive == X264_B_ADAPT_TRELLIS ) //使用viterbi最优路径进行B帧自适应选择
/* Don't analyse any more frames than the trellis would have covered. */
origmaxp1 += h->param.i_bframe; //
else
origmaxp1++;
int maxp1 = X264_MIN( origmaxp1, num_frames ); //选择min作为maxpl
/* Where A and B are scenes: AAAAAABBBAAAAAA
* If BBB is shorter than (maxp1-p0), it is detected as a flash
* and not considered a scenecut.
*
* 当B场景的长度小于maxp1-p0,那么将被视为一个场景闪现,不被考虑成场景切换
* 提出问题:若AAAAAAAAAAAABBBAAAAAABBBAAAAAABBBAAAAAAAAA呢?这样我觉得第
* 一个B定为场景切换帧应该是一个不错的选择,可以考虑优化. */
for( int curp1 = p1; curp1 <= maxp1; curp1++ ) //遍历p1到maxp1
if( !scenecut_internal( h, a, frames, p0, curp1, 0 ) ) //若curp1相对于p0没有场景切换
/* Any frame in between p0 and cur_p1 cannot be a real scenecut. */
//从p0到当前curp1内的所有帧都不可作为一个场景切换帧
for( int i = curp1; i > p0; i-- )
frames[i]->b_scenecut = 0;
/* Where A-F are scenes: AAAAABBCCDDEEFFFFFF
* If each of BB ... EE are shorter than (maxp1-p0), they are
* detected as flashes and not considered scenecuts.
* Instead, the first F frame becomes a scenecut.
* If the video ends before F, no frame becomes a scenecut.
*
* 若每一个场景B/C/.../E都短于maxp1-p0,那么他们都被视为一个场景闪现
* 不被考虑成场景切换,第一帧F被视为场景切换. */
for( int curp0 = p0; curp0 <= maxp1; curp0++ ) //从p0一直遍历到maxp1
if( origmaxp1 > i_max_search || (curp0 < maxp1 && scenecut_internal( h, a, frames, curp0, maxp1, 0 )) )
/* If cur_p0 is the p0 of a scenecut, it cannot be the p1 of a scenecut. */
frames[curp0]->b_scenecut = 0;
}
/* Ignore frames that are part of a flash, i.e. cannot be real scenecuts. */
if( !frames[p1]->b_scenecut ) //若p1不被允许作为场景切换
return 0;
//返回p1相对p0是否进行了场景切换
return scenecut_internal( h, a, frames, p0, p1, real_scenecut );
}