最近看x264源代码,帧级编码函数static int encode( x264_param_t *param, cli_opt_t *opt )中有两个循环
for( ; !b_ctrl_c && (i_frame < param->i_frame_total || !param->i_frame_total); i_frame++ )
{
if( filter.get_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) )
break;
x264_picture_init( &pic );
convert_cli_to_lib_pic( &pic, &cli_pic );
if( !param->b_vfr_input )
pic.i_pts = i_frame;
if( opt->i_pulldown && !param->b_vfr_input )
{
pic.i_pic_struct = pulldown->pattern[ i_frame % pulldown->mod ];
pic.i_pts = (int64_t)( pulldown_pts + 0.5 );
pulldown_pts += pulldown_frame_duration[pic.i_pic_struct];
}
else if( opt->timebase_convert_multiplier )
pic.i_pts = (int64_t)( pic.i_pts * opt->timebase_convert_multiplier + 0.5 );
if( pic.i_pts <= largest_pts )
{
if( cli_log_level >= X264_LOG_DEBUG || pts_warning_cnt < MAX_PTS_WARNING )
x264_cli_log( "x264", X264_LOG_WARNING, "non-strictly-monotonic pts at frame %d (%"PRId64" <= %"PRId64")\n",
i_frame, pic.i_pts, largest_pts );
else if( pts_warning_cnt == MAX_PTS_WARNING )
x264_cli_log( "x264", X264_LOG_WARNING, "too many nonmonotonic pts warnings, suppressing further ones\n" );
pts_warning_cnt++;
pic.i_pts = largest_pts + ticks_per_frame;
}
second_largest_pts = largest_pts;
largest_pts = pic.i_pts;
if( opt->tcfile_out )
fprintf( opt->tcfile_out, "%.6f\n", pic.i_pts * ((double)param->i_timebase_num / param->i_timebase_den) * 1e3 );
if( opt->qpfile )
parse_qpfile( opt, &pic, i_frame + opt->i_seek );
prev_dts = last_dts;
i_frame_size = encode_frame( h, opt->hout, &pic, &last_dts ); //!!!!!!
if( i_frame_size < 0 )
{
b_ctrl_c = 1; /* lie to exit the loop */
retval = -1;
}
else if( i_frame_size )
{
i_file += i_frame_size;
i_frame_output++;
if( i_frame_output == 1 )
first_dts = prev_dts = last_dts;
}
if( filter.release_frame( opt->hin, &cli_pic, i_frame + opt->i_seek ) )
break;
/* update status line (up to 1000 times per input file) */
if( opt->b_progress && i_frame_output )
i_previous = print_status( i_start, i_previous, i_frame_output, param->i_frame_total, i_file, param, 2 * last_dts - prev_dts - first_dts );
}
/* Flush delayed frames */
while( !b_ctrl_c && x264_encoder_delayed_frames( h ) )
{
prev_dts = last_dts;
i_frame_size = encode_frame( h, opt->hout, NULL, &last_dts );//进入MBtree模块
if( i_frame_size < 0 )
{
b_ctrl_c = 1; /* lie to exit the loop */
retval = -1;
}
else if( i_frame_size )
{
i_file += i_frame_size;
i_frame_output++;
if( i_frame_output == 1 )
first_dts = prev_dts = last_dts;
}
if( opt->b_progress && i_frame_output )
i_previous = print_status( i_start, i_previous, i_frame_output, param->i_frame_total, i_file, param, 2 * last_dts - prev_dts - first_dts );
}
其中encode_frame出现了两次,不知道各自有什么作用。
假设视频100帧,
当开启mbtree时,第一个encode_frame函数执行65次后,开始在此函数编码第1帧,在此循环内共编100-65=35帧,其余的65帧在第二个循环内编码。就相当于,前65帧为预处理,在预处理时,把读入的帧放入了缓存内,总共放入65帧缓存。
当关闭mbtree时,第一个encode_frame函数执行17次后,开始在此函数编码第1帧,在此循环内共编100-17=83帧,其余的17帧在第二个循环内编码。就相当于,前17帧为预处理,在预处理时,把读入的帧放入了缓存内,总共放入17帧缓存。
当视频输入帧数较少时,比如少于10帧,则在第一个循环内进行10帧的预处理,在第二个循环内进行这10帧的编码。
具体分析x264为什么这么做呢?
可以这么理解,第一个循环主要是为了读入全部的帧,并进行预处理;预处理窗口满了之后进行编码。这个编码帧应该比读入的帧延迟n帧,n为预处理窗口的大小。
第二个循环主要是用来对剩余在缓存器里的,也就是预处理窗口剩下的帧进行编码;因为已经输入了,所以不用再输入图像了,所以第二个encode_frame输入的参数有一个为NUL。
缓存器的大小为z,那么 z = max(rc-lookahed+6,18),自己实验测得的。