【FFMPEG】学习笔记(3)源码分析之参数结构体

有了开发工具的帮助,就可以开始学习ffmpeg命令行工具的源代码了。大致分析ffmpeg命令行工具的源代码,可以发现整个过程可以分成如下几个大的步骤:

开始
日志级别处理
参数解析
输入处理
输出处理
数据包解析
结束

一、日志级别处理

ffmpeg定义了几个日志级别,这里可以对参数中关于设置日志级别的参数进行解析,并设置。具体的日志级别定义可以查询官方文档。解析后在程序中可以使用av_log方法打印程序信息。

二、参数保存结构

参数解析的功能是把从命令行传入的参数解析成程序定义的结构体,方便后续的操作。结合之前配置的开发环境,用下面这个命令还学习代码:

gdbserver :1234 ./test -re -i /home/pi/Birds.mp4 -c copy -f flv rtmp://192.168.1.202:1985/myapp/demo

这个命令是将树莓派中的一个mp4文件推流至RTMP服务器,可以通过ffplay或者vlc播放。

  1. 命令行传入的参数
    main函数接收两个参数:
int main(int argc, char **argv)
//argc是参数的总个数,这里是9
//argv是个指针,指向第一个参数
//argv[0] = '.test'
//argv[1] = '-re'
//argv[2] = '-i'
//argv[3] = '/home/pi/Birds.mp4'
//......
  1. 参数的结构体
    这里整理了一下程序中定义的结构体及关系
«struct» OptionParseContext OptionGroup global_opts OptionGroupList *groups int nb_groups OptionGroup cur_group «struct» OptionGroupList OptionGroupDef * group_def OptionGroup *groups int nb_groups «struct» OptionGroup OptionGroupDef* group_def char* arg Option* opts int nb_opts AVDictionary* codec_opts AVDictionary* format_opts AVDictionary* resample_opts AVDictionary* sws_dict AVDictionary* swr_opts «struct» AVDictionary int count AVDictionaryEntry *elems «struct» AVDictionaryEntry char *key; char *value; «struct» OptionGroupDef char* name char* sep int flags «struct» Option OptionDef* opt char* key char* val «struct» OptionDef char* name int flags union u char* help char* argname

从基础的结构体开始理解:

Option

通过debug来看下一下解析后的option参数结构,如下图
在这里插入图片描述
在ffmpeg_opt.c中定义了所有OptionDef的常量数组,通过输入的参数在数组中匹配相应的OptionDef值。key是Option的名称,val则是参数中指定的值,如果没有默认设置为“1”。以下是这两个结构的源代码:

typedef struct OptionDef {
    const char *name; 
    int flags;
     union {
        void *dst_ptr;
        int (*func_arg)(void *, const char *, const char *);
        size_t off;
    } u;
    const char *help;
    const char *argname;
} OptionDef;
typedef struct Option {
    const OptionDef  *opt;
    const char       *key;
    const char       *val;
} Option;
OptionGroup

OptionGroup共定义了三组,从上一篇的命令参数解析可以看出是:global(全局参数)、input(输入参数)、output(输出参数)
input解析后的OptionGroup如下图:
在这里插入图片描述
从group_def可以看出,这组参数是input,参数的分割符为“i”,arg是“i”指定的参数值:一个mp4文件的地址,opts是针对input的一些参数,从命令行中可以看出是“-re”。nb_opts的是值为1,代表opts中共有一个参数。opts指向的是第一个参数的指针。组中还有其他的一些参数如:sws_dict等,这些通过修改代码config.h中的值设置进来,准确的方法是在编译的时候指定这些变量。
output的Option参数结构与input相似:
在这里插入图片描述

这部分的源代码如下:

typedef struct OptionGroupDef {
    /**< group name */
    const char *name;
    /**
     * Option to be used as group separator. Can be NULL for groups which
     * are terminated by a non-option argument (e.g. ffmpeg output files)
     */
    const char *sep;
    /**
     * Option flags that must be set on each option that is
     * applied to this group
     */
    int flags;
} OptionGroupDef;
typedef struct OptionGroup {
    const OptionGroupDef *group_def;
    const char *arg;

    Option *opts;
    int  nb_opts;

    AVDictionary *codec_opts;
    AVDictionary *format_opts;
    AVDictionary *resample_opts;
    AVDictionary *sws_dict;
    AVDictionary *swr_opts;
} OptionGroup;
AVDictionary

这个是ffmpeg定一个的一个key-value格式的结构体,并提供三个api来使用这个结构体:
av_dict_set() // 添加一个键值对
v_dict_get() //通过key获取value
av_dict_free() //释放内存空间
一个key可以有多个value,可以在set的时候指定是替换还是添加,count代表value的个数。

struct AVDictionary {
    int count;
    AVDictionaryEntry *elems;
};
struct AVDictionaryEntry {
    char *key;
    char *value;
} AVDictionaryEntry;
GroupOptionList

这个结构体中存放就是global、input和output三个OptionGroup中的一个或者多个。

typedef struct OptionGroupList {
    const OptionGroupDef *group_def;

    OptionGroup *groups;
    int       nb_groups;
} OptionGroupList;
OptionParseContext

命令行参数解析的上下文结构体,解析后参数内容被解析到这个结构体,并传递给后面的处理程序
从变量命名上可以看出含义,cur_group是在程序解析时指定当前正在解析的参数组

typedef struct OptionParseContext {
    OptionGroup global_opts;

    OptionGroupList *groups;
    int           nb_groups;

    /* parsing state */
    OptionGroup cur_group;
} OptionParseContext;

理解主要的结构体可以帮助我们后续更好的理解解析的过程

三、参数解析过程

示例中的参数被split_commandline方法循环处理

数组下标012345678
参数值ffmpeg-re-iBird.mp4-ccopy-fflvrtmp://192.168.1.202:1935

主要的处理流程总结:

Created with Raphaël 2.2.0 开始 optindex < argc '--'起始的参数 '-'起始的参数 组分隔符 普通参数 AV相关参数 no yes no
int split_commandline(OptionParseContext *octx, int argc, char *argv[], const OptionDef *options,
                      const OptionGroupDef *groups, int nb_groups) {
    int optindex = 1;
    int dashdash = -2;

    init_parse_context(octx, groups, nb_groups);
    while (optindex < argc) {
        //opt[0]='-' opt[1]='r'
        const char *opt = argv[optindex++], *arg;
        const OptionDef *po;
        int ret;

        av_log(NULL, AV_LOG_DEBUG, "Reading option '%s' ...", opt);

        if (opt[0] == '-' && opt[1] == '-' && !opt[2]) {
            dashdash = optindex;
            continue;
        }
        
        /* unnamed group separators, e.g. output filename */
        if (opt[0] != '-' || !opt[1] || dashdash + 1 == optindex) {
            finish_group(octx, 0, opt);
            av_log(NULL, AV_LOG_DEBUG, " matched as %s.\n", groups[0].name);
            continue;
        }
        opt++;

        if ((ret = match_group_separator(groups, nb_groups, opt)) >= 0) {
            do {
                arg = argv[optindex++];
                if (!arg) {
                    av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'.\n", opt);
                    return AVERROR(EINVAL);
                }
            } while (0);
            finish_group(octx, ret, arg);
            av_log(NULL, AV_LOG_DEBUG, " matched as %s with argument '%s'.\n", groups[ret].name, arg);
            continue;
        }

        /* normal options */
        po = find_option(options, opt);
        if (po->name) {
            if (po->flags & OPT_EXIT) {
                /* optional argument, e.g. -h */
                arg = argv[optindex++];
            } else if (po->flags & HAS_ARG) {
                do {
                    arg = argv[optindex++];
                    if (!arg) {
                        av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'.\n", opt);
                        return AVERROR(EINVAL);
                    }
                } while (0);
            } else {
                arg = "1";
            }
            add_opt(octx, po, opt, arg);
            av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with "
                                       "argument '%s'.\n", po->name, po->help, arg);
            continue;
        }

        /* AVOptions */
        if (argv[optindex]) {
            ret = opt_default(NULL, opt, argv[optindex]);
            if (ret >= 0) {
                av_log(NULL, AV_LOG_DEBUG, " matched as AVOption '%s' with argument '%s'.\n", opt, argv[optindex]);
                optindex++;
                continue;
            } else if (ret != AVERROR_OPTION_NOT_FOUND) {
                av_log(NULL, AV_LOG_ERROR, "Error parsing option '%s' with argument '%s'.\n", opt, argv[optindex]);
                return ret;
            }
        }

        /* boolean -nofoo options */
        if (opt[0] == 'n' && opt[1] == 'o' &&
            (po = find_option(options, opt + 2)) && po->name && po->flags & OPT_BOOL) {
            add_opt(octx, po, opt, "0");
            av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with argument 0.\n",
                   po->name, po->help);
            continue;
        }

        av_log(NULL, AV_LOG_ERROR, "Unrecognized option '%s'.\n", opt);
        return AVERROR_OPTION_NOT_FOUND;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值