入口函数main
int main(int argc, char **argv)
{
int i, ret;
int64_t ti;
init_dynload();
register_exit(ffmpeg_cleanup);
setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */
av_log_set_flags(AV_LOG_SKIP_REPEATED);
parse_loglevel(argc, argv, options);
if(argc>1 && !strcmp(argv[1], "-d")){
run_as_daemon=1;
av_log_set_callback(log_callback_null);
argc--;
argv++;
}
//注册
avcodec_register_all();
#if CONFIG_AVDEVICE
avdevice_register_all();
#endif
avfilter_register_all();
av_register_all();
avformat_network_init();
//显示版本信息
show_banner(argc, argv, options);
//解析输入参数,并打开输入输出
/* parse options and open all input/output files */
ret = ffmpeg_parse_options(argc, argv);
if (ret < 0)
exit_program(1);
if (nb_output_files <= 0 && nb_input_files == 0) {
show_usage();
av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name);
exit_program(1);
}
/* file converter / grab */
if (nb_output_files <= 0) {
av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n");
exit_program(1);
}
// if (nb_input_files == 0) {
// av_log(NULL, AV_LOG_FATAL, "At least one input file must be specified\n");
// exit_program(1);
// }
for (i = 0; i < nb_output_files; i++) {
if (strcmp(output_files[i]->ctx->oformat->name, "rtp"))
want_sdp = 0;
}
//开始转码
current_time = ti = getutime();
if (transcode() < 0)
exit_program(1);
ti = getutime() - ti;
if (do_benchmark) {
av_log(NULL, AV_LOG_INFO, "bench: utime=%0.3fs\n", ti / 1000000.0);
}
av_log(NULL, AV_LOG_DEBUG, "%"PRIu64" frames successfully decoded, %"PRIu64" decoding errors\n",
decode_error_stat[0], decode_error_stat[1]);
if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1])
exit_program(69);
exit_program(received_nb_signals ? 255 : main_return_code);
return main_return_code;
}
main 函数主要做了如下工作:
- 注册各种信息
- 解析参数并打开输入输出
- 持续转码
- 退出,清理
main - avcodec_register_all
以avcodec_register_all为例主要学习
void avcodec_register_all(void)
{
static AVOnce control = AV_ONCE_INIT;
ff_thread_once(&control, register_all);
}
//只初始化一次
#define ff_thread_once(control, routine) pthread_once(control, routine)
//传入的静态函数
static void register_all(void)
{
/* hardware accelerators */
REGISTER_HWACCEL(H263_VAAPI, h263_vaapi);
...
REGISTER_PARSER(H264, h264);
REGISTER_PARSER(HEVC, hevc);
...
}
//宏定义
//REGISTER_PARSER(H264, h264);
#define REGISTER_PARSER(X, x) \
{ \
//extern AVCodecParser ff_h264_parser;
extern AVCodecParser ff_##x##_parser; \
//if (CONFIG_H264_PARSER)
if (CONFIG_##X##_PARSER) \
//av_register_codec_parser(&ff_h264_parser); \
av_register_codec_parser(&ff_##x##_parser); \
}
//注册
void av_register_codec_parser(AVCodecParser *parser)
{
do {
parser->next = av_first_parser;
} while (parser->next != avpriv_atomic_ptr_cas((void * volatile *)&av_first_parser, parser->next, parser));
}
一句话概括就是:遍历链表并把当前的AVCodec加到链表的尾部。
关于注册可以参考雷神的blog
main-show_banner
void show_banner(int argc, char **argv, const OptionDef *options)
{
int idx = locate_option(argc, argv, options, "version");
if (hide_banner || idx)
return;
print_program_info (INDENT|SHOW_COPYRIGHT, AV_LOG_INFO);
print_all_libs_info(INDENT|SHOW_CONFIG, AV_LOG_INFO);
print_all_libs_info(INDENT|SHOW_VERSION, AV_LOG_INFO);
}
打印一些信息,如下图