FFMPEG源码之ffmpeg.c解析

重要函数解析

main()

cpp 复制代码
int main(int argc, char **argv)
{
    int ret;
    BenchmarkTimeStamps ti;

    /* 初始化动态加载 */
    init_dynload();

    /* 注册退出回调函数 */
    register_exit(ffmpeg_cleanup);

    /* 设置stderr的缓冲模式(win32运行时需要) */
    setvbuf(stderr, NULL, _IONBF, 0);

    /* 设置日志打印选项 */
    av_log_set_flags(AV_LOG_SKIP_REPEATED);
    parse_loglevel(argc, argv, options);

#if CONFIG_AVDEVICE
    /* 注册音视频设备 */
    avdevice_register_all();
#endif

    /* 初始化网络模块 */
    avformat_network_init();

    /* 显示ffmpeg的banner信息 */
    show_banner(argc, argv, options);

    /* 解析命令行选项并打开所有的输入/输出文件 */
    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);
    }

    /* 检查是否至少指定一个输出文件 */
    if (nb_output_files <= 0) {
        av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n");
        exit_program(1);
    }

    current_time = ti = get_benchmark_time_stamps();

    /* 文件转码或抓取 */
    if (transcode() < 0)
        exit_program(1);

    if (do_benchmark) {
        int64_t utime, stime, rtime;
        current_time = get_benchmark_time_stamps();
        utime = current_time.user_usec - ti.user_usec;
        stime = current_time.sys_usec  - ti.sys_usec;
        rtime = current_time.real_usec - ti.real_usec;
        av_log(NULL, AV_LOG_INFO,
               "bench: utime=%0.3fs stime=%0.3fs rtime=%0.3fs\n",
               utime / 1000000.0, stime / 1000000.0, rtime / 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;
}

下面是对每个步骤的功能的详细解释:

  1. 初始化动态加载。
    • 调用init_dynload函数,用于初始化动态加载库的相关资源,以便在需要时加载需要的库。
  2. 注册退出回调函数。
    • 调用register_exit函数,将ffmpeg_cleanup函数注册为在程序退出时被调用的回调函数。
  3. 设置stderr的缓冲模式。
    • 调用setvbuf函数,将stderr的缓冲模式设置为无缓冲模式,以确保错误信息可以立即显示在终端上。
  4. 设置日志打印选项。
    • 调用av_log_set_flags函数,设置日志打印选项。在这里设置
      AV_LOG_SKIP_REPEATED选项,表示日志会跳过重复的消息。
  5. 解析命令行参数中的日志级别选项。
    • 调用parse_loglevel函数,解析命令行参数中的日志级别选项,并将其应用到日志系统中。
  6. 注册音视频设备。
    • 调用avdevice_register_all函数,用于注册所有的音视频设备。
  7. 初始化网络模块。
    • 调用avformat_network_init函数,初始化网络模块,以便进行网络相关的操作,如打开网络流。
  8. 显示ffmpeg的banner信息。
    • 调用show_banner函数,根据命令行参数、选项和程序信息,打印ffmpeg的banner信息。
  9. 解析命令行选项并打开所有的输入/输出文件。
    • 调用ffmpeg_parse_options函数,解析命令行选项,并根据选项打开所有的输入/输出文件。
  10. 检查是否没有指定输出文件并且没有输入文件。
    • 检查nb_output_files和nb_input_files的值。
    • 若满足条件,则打印用法信息和警告,并终止程序。
  11. 检查是否至少指定一个输出文件。
    • 检查nb_output_files的值。
    • 若不满足条件,则打印致命错误信息,并终止程序。
  12. 进行文件转码或抓取。
    • 调用transcode函数,进行文件转码或抓取。
    • transcode函数返回值小于0表示出错,并通过调用exit_program函数终止程序。
  13. 如果启用了性能评测,输出性能数据。
    • 如果do_benchmark为真,计算从开始到结束的用户时间、系统时间和真实时间,并打印出来。
  14. 打印解码帧数和解码错误数。
    • 调用av_log函数,打印成功解码的帧数和解码错误数。
  15. 检查解码错误是否超过了指定的错误率。
    • 检查解码错误数是否超过了最大错误率。
    • 若满足条件,则通过调用exit_program终止程序。
  16. 根据是否接收到信号确定程序的返回码,并终止程序。
  17. 返回main_return_code作为main函数的返回值。

ffmpeg_parse_options()

cpp 复制代码
// 解析命令行参数并设置选项
int ffmpeg_parse_options(int argc, char **argv)
{
    // 定义选项解析上下文和错误信息
    OptionParseContext octx;
    uint8_t error[128];
    int ret;

    memset(&octx, 0, sizeof(octx));

    /* split the commandline into an internal representation */
    // 将命令行参数拆分为内部表示形式
    ret = split_commandline(&octx, argc, argv, options, groups,
                            FF_ARRAY_ELEMS(groups));
    if (ret < 0) {
        av_log(NULL, AV_LOG_FATAL, "Error splitting the argument list: ");
        goto fail;
    }

    /* apply global options */
    // 应用全局选项
    ret = parse_optgroup(NULL, &octx.global_opts);
    if (ret < 0) {
        av_log(NULL, AV_LOG_FATAL, "Error parsing global options: ");
        goto fail;
    }

    /* configure terminal and setup signal handlers */
    // 配置终端并设置信号处理程序
    term_init();

    /* open input files */
    // 打开输入文件
    ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);
    if (ret < 0) {
        av_log(NULL, AV_LOG_FATAL, "Error opening input files: ");
        goto fail;
    }

    // 应用同步偏移量
    apply_sync_offsets();

    /* create the complex filtergraphs */
    // 创建复杂的滤波器图
    ret = init_complex_filters();
    if (ret < 0) {
        av_log(NULL, AV_LOG_FATAL, "Error initializing complex filters.\n");
        goto fail;
    }

    /* open output files */
    // 打开输出文件
    ret = open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);
    if (ret < 0) {
        av_log(NULL, AV_LOG_FATAL, "Error opening output files: ");
        goto fail;
    }

    // 检查滤波器的输出
    check_filter_outputs();

fail:
    // 反初始化选项解析上下文
    uninit_parse_context(&octx);
    if (ret < 0) {
        // 如果有错误,将错误信息输出到日志
        av_strerror(ret, error, sizeof(error));
        av_log(NULL, AV_LOG_FATAL, "%s\n", error);
    }
    return ret;
}

函数功能:该函数用于解析命令行参数并进行相应处理,包括拆分命令行参数为内部表示形式、应用全局选项、配置终端和设置信号处理程序、打开输入文件、应用同步偏移量、创建复杂的滤波器图、打开输出文件、检查滤波器的输出。如果出现错误,会将错误信息输出到日志。


transcode()

相关推荐
头快撑不住了12 小时前
ffmpeg源码分析(七)结构体之AVStream
ffmpeg
yunhuibin19 小时前
ffmpeg面向对象——拉流协议匹配机制探索
学习·ffmpeg
cuijiecheng20181 天前
音视频入门基础:FLV专题(13)——FFmpeg源码中,解析任意Type值的SCRIPTDATAVALUE类型的实现
ffmpeg·音视频
小神.Chen2 天前
YouTube音视频合并批处理基于 FFmpeg的
ffmpeg·音视频
昱禹3 天前
记一次因视频编码无法在浏览器播放、编码视频报错问题
linux·python·opencv·ffmpeg·音视频
寻找09之夏4 天前
【FFmpeg 深度解析】:全方位视频合成
ffmpeg·音视频
zanglengyu4 天前
ffmpeg取rtsp流音频数据保存声音为wav文件
ffmpeg·音视频
cuijiecheng20184 天前
音视频入门基础:FLV专题(11)——FFmpeg源码中,解析SCRIPTDATASTRING类型的ScriptDataValue的实现
ffmpeg·音视频
汪子熙4 天前
什么是 LDAC、SBC 和 AAC 音频编码技术
ffmpeg·音视频·aac
cpp_learners4 天前
Windows环境 源码编译 FFmpeg
windows·ffmpeg·源码编译·ffmpeg源码编译