两个常用的宏定义

时间:2021-07-12 23:19:19

两个经常用到的宏定义

MATCH_PER_STREAM_OPT


#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\
{\
    int i, ret;\
    for (i = 0; i < o->nb_ ## name; i++) {\
        char *spec = o->name[i].specifier;\
        if ((ret = check_stream_specifier(fmtctx, st, spec)) > 0)\/*检查命令行传入的参数格式是否正确*/
            outvar = o->name[i].u.type;\
        else if (ret < 0)\
            exit_program(1);\
    }\
}
//例子
MATCH_PER_STREAM_OPT(ts_scale, dbl, ist->ts_scale, ic, st);
{
    int i, ret;\
    for (i = 0; i < o->nb_ts_scale; i++) {\
        char *spec = o->ts_scale[i].specifier;\
        if ((ret = check_stream_specifier(ic, st, spec)) > 0)\
            ist->ts_scale = o->name[i].u.dbl;\
        else if (ret < 0)\
            exit_program(1);\
    }\
}
typedef struct SpecifierOpt {
    char *specifier;    /**< stream/chapter/program/... specifier */
    union {
        uint8_t *str;
        int        i;
        int64_t  i64;
        float      f;
        double   dbl;
    } u;
} SpecifierOpt;

从上面的例子中可以看出这个宏定义是在取OptionsContext结构体中定义为类型SpecifierOpt的变量所存储的值。显然它是以最后一个参数的值为准的。
参数含义:
name:OptionsContext中的一个成员变量,并用它加前缀nb_,找到这个变量链表的对应个数。
type:表示此变量在SpecifierOpt类型的变量中存放在union u中的那个变量中。
outvar:返回值存放的变量。
fmtctx, st:check_stream_specifier函数的传入参数。此函数用来检查命令行出入的格式是否正确。

MATCH_PER_TYPE_OPT

#define MATCH_PER_TYPE_OPT(name, type, outvar, fmtctx, mediatype)\
{\
    int i;\
    for (i = 0; i < o->nb_ ## name; i++) {\
        char *spec = o->name[i].specifier;\
        if (!strcmp(spec, mediatype))\
            outvar = o->name[i].u.type;\
    }\
}
//例子
MATCH_PER_TYPE_OPT(codec_names, str,    video_codec_name, ic, "v");
{\
    int i;\
    for (i = 0; i < o->nb_codec_names; i++) {\
        char *spec = o->codec_names[i].specifier;\
        if (!strcmp(spec, "v"))\
            video_codec_name = o->codec_names[i].u.str;\
    }\
}

根据上面的例子可以看出,与前一个宏定义不同,这次不是找出最后一个符合格式标准的值。而是从中挑选一个spec和传入参数mediatype相同的项。mediatype一般是”v”(视频流),”a”(音频流),”s”(字幕流),”d”(数据流)。和流类型相关的。

write_option

要理解怎么取值,就要明白当初是怎么放进去的。这就涉及到一个函数write_option
在解析命令行参数的时候有两处调用关系是这样的。具体流程可以参数open_input_file()函数介绍
ffmpeg_parse_options()->parse_optgroup()->write_option():这条路是设置全局参数的
open_files()->parse_optgroup()->write_option():这条路里面分别设置输入和输出文件的参数

static int write_option(void *optctx, const OptionDef *po, const char *opt,
                        const char *arg)
{
    /* new-style options contain an offset into optctx, old-style address of * a global var*/
    void *dst = po->flags & (OPT_OFFSET | OPT_SPEC) ?
                (uint8_t *)optctx + po->u.off : po->u.dst_ptr;//存入数据的地址
    int *dstcount;//如果数据是SpecifierOpt **类型,对应的存入链表成员个数的变量地址,

    if (po->flags & OPT_SPEC) {//在OptionsContext中对应为SpecifierOpt类型的变量
        SpecifierOpt **so = (SpecifierOpt **)dst;
        char *p = (char *)strchr(opt, ':');
        char *str;
        /*将偏移对应的下一个地址给dstcount。记录数量. 例如: SpecifierOpt *codec_names; int nb_codec_names;*/
        dstcount = (int *)(so + 1);//
        *so = (SpecifierOpt *)grow_array(*so, sizeof(**so), dstcount, *dstcount + 1);//dstcount的值就是在这里加1的。
        str = av_strdup(p ? p + 1 : "");
        if (!str)
            return AVERROR(ENOMEM);
        //将冒号后面的存入此,一般为:v :a :s :d
        (*so)[*dstcount - 1].specifier = str;
        dst = &(*so)[*dstcount - 1].u;
    }
    //下面都是根据option[]中预先定义的参数处理方式进行处理,有的是直接赋值,有的是调用预定义的函数。
    if (po->flags & OPT_STRING) {
        char *str;
        str = av_strdup(arg);
        av_freep(dst);
        if (!str)
            return AVERROR(ENOMEM);
        *(char **)dst = str;
    } else if (po->flags & OPT_BOOL || po->flags & OPT_INT) {
        *(int *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT_MIN, INT_MAX);
    } else if (po->flags & OPT_INT64) {
        *(int64_t *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT64_MIN, INT64_MAX);
    } else if (po->flags & OPT_TIME) {
        *(int64_t *)dst = parse_time_or_die(opt, arg, 1);
    } else if (po->flags & OPT_FLOAT) {
        *(float *)dst = parse_number_or_die(opt, arg, OPT_FLOAT, -INFINITY, INFINITY);
    } else if (po->flags & OPT_DOUBLE) {
        *(double *)dst = parse_number_or_die(opt, arg, OPT_DOUBLE, -INFINITY, INFINITY);
    } else if (po->u.func_arg) {
        int ret = po->u.func_arg(optctx, opt, arg);
        if (ret < 0) {
            av_log(NULL, AV_LOG_ERROR,
                   "Failed to set value '%s' for option '%s': %s\n",
                   arg, opt, av_err2str(ret));
            return ret;
        }
    }
#ifdef EXIT_PROGRAM
    if (po->flags & OPT_EXIT)//如果参数标识中定义了OPT_EXIT,退出程序
        exit_program(0);
#endif

    return 0;
}