ffmpeg源码分析

1 简述

ffmpeg.exe 是一个非常快速的音频视频转换工具, 我们可以通过它进行格式,编码转换,还可以加滤镜。它可以读取普通文件,网络文件,甚至抓取设备数据。ffmpeg 转码的过程如下图所示:

ffmpeg 涉及的c 文件有三个:ffmpeg.c , ffmpeg_opt.c,cmdutils.c ,有关ffmpeg.exe 框架,引用雷神画的流程图:

从上图我们可以看到,main() 函数中调用的函数有:

1)av_register_all() 

2)show_banner() :打印ffmpeg 版本,编译器版本,已配置的编解码器,以及动态库的版本信息。

3)parse_options():解析命令行传入的参数,并根据传入的参数,去打开相应的stream,创建该输出的流。

4)transcode() : 转码,还支持滤镜的添加

5)exit_program()

下面我们围绕show_banner(), parse_option(), transcode() 三个函数来介绍ffmpeg.

2 show_banner()

 这函数在cmdutils.c 文件中实现的,我们每次使用FFMPEG 进行转码时,都会有下面的输出, 这些都是在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);
}

 

3 ffmpeg_parse_options()

这函数的实现是在ffmpeg_opt.c 中, 解析拆解命令行传下来的参数,并打开输入的stream, 创建要输出的stream,  并创建每一个视频,音频的编解码器资源。

int ffmpeg_parse_options(int argc, char **argv)
{
    ......
    /* split the commandline into an internal representation */
    ret = split_commandline(&octx, argc, argv, options, groups,
                            FF_ARRAY_ELEMS(groups));

    .......
    /* apply global options */
    ret = parse_optgroup(NULL, &octx.global_opts);
    ......
    /* configure terminal and setup signal handlers */
    term_init();
    ........
    /* open input files */
    ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);
    ........
    /* create the complex filtergraphs */
    ret = init_complex_filters();
    ..........
    /* open output files */
    ret = open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);
    ........
    check_filter_outputs();
    ........
}

 当我们在终端输入 ffmpeg.exe  -help, 就会显示很多关于ffmpeg 使用的帮助信息出来。这又是怎么实现的呢?

在ffmpeg_opt.c 中有定义一个options 数组, 它存放所有 ffmpeg 命令的相关参数。

#define OFFSET(x) offsetof(OptionsContext, x)
const OptionDef options[] = {
    /* main options */
    CMDUTILS_COMMON_OPTIONS    //它实际是一个宏,记录common option, 如help option
    { "f",//命令参数              HAS_ARG | OPT_STRING | OPT_OFFSET |
                        OPT_INPUT | OPT_OUTPUT,//命令参数类型                      { .off       = OFFSET(format) },//一个union, 成员有void *, 函数指针,size_t off 
        "force format",  // 帮助信息  "fmt" //参数名字 },
    { "y",              OPT_BOOL,                                    {              &file_overwrite },
        "overwrite output files" },
    { "n",              OPT_BOOL,                                    {              &no_file_overwrite },
        "never overwrite output files" },
...........

在上面关于options 定义中,有个CMDUTILS_COMMON_OPTIONS 的宏, 它是common option, 宏定义如下:

#define CMDUTILS_COMMON_OPTIONS                                                                                         \
    { "L",           OPT_EXIT,             { .func_arg = show_license },     "show license" },                          \
    { "h",           OPT_EXIT,             { .func_arg = show_help },        "show help", "topic" },                    \
    { "?",           OPT_EXIT,             { .func_arg = show_help },        "show help", "topic" },                    \
    { "help",        OPT_EXIT,             { .func_arg = show_help },        "show help", "topic" },                    \
    { "-help",       OPT_EXIT,             { .func_arg = show_help },        "show help", "topic" },                    \
    { "version",     OPT_EXIT,             { .func_arg = show_version },     "show version" },                          \
    { "buildconf",   OPT_EXIT,             { .func_arg = show_buildconf },   "show build configuration" },              \
    { "formats",     OPT_EXIT,             { .func_arg = show_formats },     "show available formats" },                \
    { "muxers",      OPT_EXIT,             { .func_arg = show_muxers },      "show available muxers" },

当我们输入ffmpeg.exe  -help 时, 它实际会跑到show_help() 这个函数中, 那么它又是怎么跑到的呢?

main()  ---> ffmpeg_parse_options()  --->  parse_optgroup()  --> write_option() -->  po->u.func_arg()    ( show_help() )

它 的大体流程就是这样的,func_arg 实际是个指针函数, 指向的函数在定义options 就已经定好了, 另外关于ffmpeg.exe -formats ; ffmpeg -codecs 等等流程都是一样的。

4 ffmpeg转码实现

假设我们在consle 输入 "ffmpeg.exe -i  test.mp4 -vcodec libx264   out.flv" , 那么会发生什么呢? 

step 1: 打开输入的stream 

step 2: 创建输出stream

step 3: 打开解码器

step 4: 打开编码器

 

step 5: 解码

 step 6:编码

 

在transcode() 函数中,调用transcode_init() 初始化编解码器,然后循环调用transcode_step() 进行一帧一帧解码


/*
 * The following code is the main loop of the file converter
 */
static int transcode(void)
{
    ........
    ret = transcode_init();//初始化编解码器
      /* 循环调用transcode_step() 解码,编码。并解码编码完一帧后,会调用print_report() 打印相关信息 */
    while (!received_sigterm) {
        int64_t cur_time= av_gettime_relative();

        /* if 'q' pressed, exits */
        if (stdin_interaction)
            if (check_keyboard_interaction(cur_time) < 0)
                break;

        ret = transcode_step();
        if (ret < 0 && ret != AVERROR_EOF) {
            av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret));
            break;
        }

        print_report(0, timer_start, cur_time);
    }
    ................
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值