x264代码的主输入口:main()函数
- 对软件进行多线程初始化
- 对命令行字符串进行编码格式检测,若不为UTF-8编码格式,则转换
- 解析命令行参数,并赋值给 x264_param_t 结构体和 cli_opt_t 结构体,以备后续使用
- 使用encode() 函数对视频数据进行编码压缩
- 关闭文件等内存清理操作
int main( int argc, char **argv )
{
x264_param_t param; // 定义编码器参数结构体
cli_opt_t opt = {0}; // 此结构体是记录一些与编码关系较小的设置信息
int ret = 0;
FAIL_IF_ERROR( x264_threading_init(), "unable to initialize threading\n" );
#ifdef _WIN32
FAIL_IF_ERROR( !get_argv_utf8( &argc, &argv ), "unable to convert command line to UTF-8\n" );
GetConsoleTitleW( org_console_title, CONSOLE_TITLE_SIZE );
_setmode( _fileno( stdin ), _O_BINARY );
_setmode( _fileno( stdout ), _O_BINARY );
_setmode( _fileno( stderr ), _O_BINARY );
#endif
/* Parse command line 解析命令行*/
if( parse( argc, argv, ¶m, &opt ) < 0 ) // 参数赋值以及初始化参数赋值
ret = -1;
#ifdef _WIN32
/* Restore title; it can be changed by input modules */
SetConsoleTitleW( org_console_title );
#endif
/* Control-C handler */
signal( SIGINT, sigint_handler ); // 捕获Control-C 按键信号
if( !ret )
ret = encode( ¶m, &opt ); // 依据所传输的参数,开始编码
/* clean up handles */
if( filter.free )
filter.free( opt.hin ); // 释放输入yuv文件
else if( opt.hin )
cli_input.close_file( opt.hin ); // 关闭输入yuv文件句柄
if( opt.hout )
cli_output.close_file( opt.hout, 0, 0 ); // 关闭编码后输出文件句柄
if( opt.tcfile_out )
fclose( opt.tcfile_out );
if( opt.qpfile )
fclose( opt.qpfile ); // 关闭qp文件句柄
#ifdef _WIN32
SetConsoleTitleW( org_console_title );
free( argv );
#endif
return ret;
}
encode()函数:
- 打开编码器,即初始化编码一帧所需的参数
- NAL包头信息初始化
- 逐帧编码视频帧
- 清空帧缓存并编码
- 关闭编码器,即清理释放内存
static int encode( x264_param_t *param, cli_opt_t *opt )
{
x264_t *h = NULL; // 编码所包含的所有参数结构体信息
x264_picture_t pic;
cli_pic_t cli_pic;
const cli_pulldown_t *pulldown = NULL; // shut up gcc
int i_frame = 0; // 帧索引
int i_frame_output = 0; // 输出编码帧统计
int64_t i_end, i_previous = 0, i_start = 0; // 结束时间、状态统计后时间、开始时间
int64_t i_file = 0; // 记录文件的尺寸大小
int i_frame_size; // 帧数据尺寸
int64_t last_dts = 0; // 最新编码帧的dts
int64_t prev_dts = 0; // 最新编码帧前一帧的dts
int64_t first_dts = 0; // 第一帧编码帧的dts
# define MAX_PTS_WARNING 3 /* arbitrary */
int pts_warning_cnt = 0;
int64_t largest_pts = -1;
int64_t second_largest_pts = -1;
int64_t ticks_per_frame;
double duration;
double pulldown_pts = 0;
int retval = 0; // 函数返回值
opt->b_progress &= param->i_log_level < X264_LOG_DEBUG;
/* set up pulldown ,一般电影为24fps,而电视为50fps或者60fps,这就需要将24fps转换到50或60fps,这个过程就叫做pulldown 模式 */
if( opt->i_pulldown && !param->b_vfr_input ) // 由上面的解释可以得出,帧率必须恒定才可使用pulldown模式,试想变帧率如何进行pulldown模式呢
{
param->b_pulldown = 1; // 1表示使用pulldown模式,而0表示不使用pulldown模式
param->b_pic_struct = 1;
pulldown = &pulldown_values[opt->i_pulldown];
param->i_timebase_num = param->i_fps_den; //帧率分母为时间基分子
FAIL_IF_ERROR2( fmod( param->i_fps_num * pulldown->fps_factor, 1 ),
"unsupported framerate for chosen pulldown\n" );
param->i_timebase_den = param->i_fps_num * pulldown->fps_factor;
}
h = x264_encoder_open( param ); // 打开X264编码器
FAIL_IF_ERROR2( !h, "x264_encoder_open failed\n" );
x264_encoder_parameters( h, param ); // 编码器参数设置
FAIL_IF_ERROR2( cli_output.set_param( opt->hout, param ), "can't set outfile param\n" );
i_start = x264_mdate(); // 记录开始时间
/* ticks/frame = ticks/second / frames/second */
ticks_per_frame = (int64_t)param->i_timebase_den * param->i_fps_den / param->i_timebase_num / param->i_fps_num;
FAIL_IF_ERROR2( ticks_per_frame < 1 && !param->b_vfr_input, "ticks_per_frame invalid: %"PRId64"\n", ticks_per_frame );
ticks_per_frame = X264_MAX( ticks_per_frame, 1 );
if( !param->b_repeat_headers )
{
// Write SPS/PPS/SEI
x264_nal_t *headers;
int i_nal;
FAIL_IF_ERROR2( x264_encoder_headers( h, &headers, &i_nal ) < 0, "x264_encoder_headers failed\n" ); // 写入NAL头信息
FAIL_IF_ERROR2( (i_file = cli_output.write_headers( opt->hout, headers )) < 0, "error writing headers to output file\n" );
}
if( opt->tcfile_out )
fprintf( opt->tcfile_out, "# timecode format v2\n" );
/* Encode frames 对每帧图像进行编码 */
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 ); // 初始化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 ) // 时刻检验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; // 更新第二大pts数值
largest_pts = pic.i_pts; // 更新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 ); // 解析qp参数文件
prev_dts = last_dts; // dts值赋值给prev_dts保存
i_frame_size = encode_frame( h, opt->hout, &pic, &last_dts ); // 编码单帧,得到最新的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 );
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 );
}
fail:
if( pts_warning_cnt >= MAX_PTS_WARNING && cli_log_level < X264_LOG_DEBUG )
x264_cli_log( "x264", X264_LOG_WARNING, "%d suppressed nonmonotonic pts warnings\n", pts_warning_cnt-MAX_PTS_WARNING );
/* duration algorithm fails when only 1 frame is output */
if( i_frame_output == 1 )
duration = (double)param->i_fps_den / param->i_fps_num;
else if( b_ctrl_c )
duration = (double)(2 * last_dts - prev_dts - first_dts) * param->i_timebase_num / param->i_timebase_den;
else
duration = (double)(2 * largest_pts - second_largest_pts) * param->i_timebase_num / param->i_timebase_den;
i_end &#