记ffmpeg调用av_dict_set设置含有"+"号的选项

av_dict_set(&options, "rtsp_transport", "+udp+tcp", 0);

av_dict_set(&options, "rtsp_flags", "+prefer_tcp", 0);

av_dict_set(&options, "stimeout", "60000000", 0);

在ffmpeg中,获取rtsp流时,设置av_dict_set底层rtsp_demuxer_class结构体时,按照上面来设置,其中,里面的"+udp+tcp",在一开始我以为写错了,后来查了一下ffmpeg源代码,发现的确可以用"+"加号连接两个不同的选项,ffmpeg真是tmd的坑爹。特此记录一下:

在 ffmpeg\libavformat\rtspdec.c 中,有定义rtsp解复用器"RTSP demuxer":


static const AVClass rtsp_demuxer_class = {
    .class_name     = "RTSP demuxer",
    .item_name      = av_default_item_name,
    .option         = ff_rtsp_options,
    .version        = LIBAVUTIL_VERSION_INT,
};

AVInputFormat ff_rtsp_demuxer = {
    .name           = "rtsp",
    .long_name      = NULL_IF_CONFIG_SMALL("RTSP input"),
    .priv_data_size = sizeof(RTSPState),
    .read_probe     = rtsp_probe,
    .read_header    = rtsp_read_header,
    .read_packet    = rtsp_read_packet,
    .read_close     = rtsp_read_close,
    .read_seek      = rtsp_read_seek,
    .flags          = AVFMT_NOFILE,
    .read_play      = rtsp_read_play,
    .read_pause     = rtsp_read_pause,
    .priv_class     = &rtsp_demuxer_class,
};

其中, rtsp_demuxer_class ->ff_rtsp_options 就是 av_dict_set(&options, "rtsp_transport", "+udp+tcp", 0); 这个函数将会赋值的结构体数组,在调用 avformat_open_input(&m_format_ctx, url_temp, NULL, &options); 后,就会填充这个字段,调用过程如下:

avformat_open_input -> av_opt_set_dict -> av_opt_set_dict2 -> av_opt_set -> set_string_number

就是在 static int set_string_number(void *obj, void *target_obj, const AVOption *o, const char *val, void *dst) 这个函数里面解析"+udp+tcp" 中的"+"的,

static int set_string_number(void *obj, void *target_obj, const AVOption *o, const char *val, void *dst)
{
    int ret = 0;
    int num, den;
    char c;

    if (sscanf(val, "%d%*1[:/]%d%c", &num, &den, &c) == 2) {
        if ((ret = write_number(obj, o, dst, 1, den, num)) >= 0)
            return ret;
        ret = 0;
    }

    for (;;) {
        int i = 0;
        char buf[256];
        int cmd = 0;
        double d;
        int64_t intnum = 1;

        if (o->type == AV_OPT_TYPE_FLAGS) {
            if (*val == '+' || *val == '-') //就是在这里解析的"+"加号,NND
                cmd = *(val++);
            for (; i < sizeof(buf) - 1 && val[i] && val[i] != '+' && val[i] != '-'; i++)
                buf[i] = val[i];
            buf[i] = 0;
        }

        {
            const AVOption *o_named = av_opt_find(target_obj, i ? buf : val, o->unit, 0, 0);
            int res;
            int ci = 0;
            double const_values[64];
            const char * const_names[64];
            if (o_named && o_named->type == AV_OPT_TYPE_CONST)
                d = DEFAULT_NUMVAL(o_named);
            else {
                if (o->unit) {
                    for (o_named = NULL; o_named = av_opt_next(target_obj, o_named); ) {
                        if (o_named->type == AV_OPT_TYPE_CONST &&
                            o_named->unit &&
                            !strcmp(o_named->unit, o->unit)) {
                            if (ci + 6 >= FF_ARRAY_ELEMS(const_values)) {
                                av_log(obj, AV_LOG_ERROR, "const_values array too small for %s\n", o->unit);
                                return AVERROR_PATCHWELCOME;
                            }
                            const_names [ci  ] = o_named->name;
                            const_values[ci++] = DEFAULT_NUMVAL(o_named);
                        }
                    }
                }
                const_names [ci  ] = "default";
                const_values[ci++] = DEFAULT_NUMVAL(o);
                const_names [ci  ] = "max";
                const_values[ci++] = o->max;
                const_names [ci  ] = "min";
                const_values[ci++] = o->min;
                const_names [ci  ] = "none";
                const_values[ci++] = 0;
                const_names [ci  ] = "all";
                const_values[ci++] = ~0;
                const_names [ci] = NULL;
                const_values[ci] = 0;

                res = av_expr_parse_and_eval(&d, i ? buf : val, const_names,
                                            const_values, NULL, NULL, NULL, NULL, NULL, 0, obj);
                if (res < 0) {
                    av_log(obj, AV_LOG_ERROR, "Unable to parse option value \"%s\"\n", val);
                    return res;
                }
            }
        }
        if (o->type == AV_OPT_TYPE_FLAGS) {
            read_number(o, dst, NULL, NULL, &intnum);
            if (cmd == '+')
                d = intnum | (int64_t)d;
            else if (cmd == '-')
                d = intnum &~(int64_t)d;
        }

        if ((ret = write_number(obj, o, dst, d, 1, 1)) < 0)
            return ret;
        val += i;
        if (!i || !*val)
            return 0;
    }

    return 0;
}

 

你可能感兴趣的:(分享)