ffmpeg源代码解读:ffurl_alloc

来源:互联网 发布:外网访问centos 编辑:程序博客网 时间:2024/05/18 04:37

ffurl_alloc主要包括url_find_protocol和url_alloc_for_protocol两个函数,前者根据filename查找匹配的url协议类型,后者根据找到的URLProtocol创建对应的URLContext。

url_find_protocol 函数根据文件名filename (准确来说可以叫做url),函数如下

static const struct URLProtocol *url_find_protocol(const char *filename){    const URLProtocol **protocols;    char proto_str[128], proto_nested[128], *ptr;    size_t proto_len = strspn(filename, URL_SCHEME_CHARS);      int i;    if (filename[proto_len] != ':' &&        (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) ||        is_dos_path(filename))        strcpy(proto_str, "file");    else        av_strlcpy(proto_str, filename,                   FFMIN(proto_len + 1, sizeof(proto_str)));    if ((ptr = strchr(proto_str, ',')))        *ptr = '\0';    av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));    if ((ptr = strchr(proto_nested, '+')))        *ptr = '\0';    protocols = ffurl_get_protocols(NULL, NULL);   //获得已经注册的所有url protocols    if (!protocols)        return NULL;    for (i = 0; protocols[i]; i++) {            const URLProtocol *up = protocols[i];        if (!strcmp(proto_str, up->name)) {            av_freep(&protocols);            return up;        }        if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&            !strcmp(proto_nested, up->name)) {            av_freep(&protocols);            return up;        }    }    av_freep(&protocols);    return NULL;}

这个函数比较简单,首先是截取到url开头的字符串获得url类型。比如 rtp://xxxxx/yyyyy就会截取到”rtp”,于是可以获得协议的类型,将描述协议类型的字符串如“rtp”,“http”等拷贝到proto_str中,接下来进行比对。需要注意的是,如果输入的是普通的磁盘上的文件,这里的filename就是文件的路径和文件名了,此时提取不到url协议的类型,ffmpeg将文件也当做一种特殊的url协议,即“file”协议。如果是普通的磁盘上的文件输入,则把proto_str赋值为”file”。 接下来就是遍历所有的ffmpeg支持的url协议类型,将和我们的proto_str匹配的协议类型的URLProtocol指针返回。 找到对应的输入url的协议类型后,调用url_alloc_for_protocol函数创建对应的URLContex,并进行初始化,如下

static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up,                                  const char *filename, int flags,                                  const AVIOInterruptCB *int_cb){    URLContext *uc;    int err;#if CONFIG_NETWORK    if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init())        return AVERROR(EIO);#endif    if ((flags & AVIO_FLAG_READ) && !up->url_read) {        av_log(NULL, AV_LOG_ERROR,               "Impossible to open the '%s' protocol for reading\n", up->name);        return AVERROR(EIO);    }    if ((flags & AVIO_FLAG_WRITE) && !up->url_write) {        av_log(NULL, AV_LOG_ERROR,               "Impossible to open the '%s' protocol for writing\n", up->name);        return AVERROR(EIO);    }    uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);    if (!uc) {        err = AVERROR(ENOMEM);        goto fail;    }    uc->av_class = &ffurl_context_class;    uc->filename = (char *)&uc[1];    strcpy(uc->filename, filename);    uc->prot            = up;    uc->flags           = flags;    uc->is_streamed     = 0; /* default = not streamed */    uc->max_packet_size = 0; /* default: stream file */    if (up->priv_data_size) {        uc->priv_data = av_mallocz(up->priv_data_size);        if (!uc->priv_data) {            err = AVERROR(ENOMEM);            goto fail;        }        if (up->priv_data_class) {            int proto_len= strlen(up->name);            char *start = strchr(uc->filename, ',');            *(const AVClass **)uc->priv_data = up->priv_data_class;            av_opt_set_defaults(uc->priv_data);          // subfile协议,比如下面一行所描述的filename           /*           subfile,,start,153391104,end,268142592,,:/media/dvd/VIDEO_TS/VTS_08_1.VOB           */            if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){                int ret= 0;                char *p= start;                char sep= *++p;                char *key, *val;                p++;                if (strcmp(up->name, "subfile"))                    ret = AVERROR(EINVAL);                while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){                    *val= *key= 0;                    if (strcmp(p, "start") && strcmp(p, "end")) {                        ret = AVERROR_OPTION_NOT_FOUND;                    } else                        ret= av_opt_set(uc->priv_data, p, key+1, 0);                    if (ret == AVERROR_OPTION_NOT_FOUND)                        av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p);                    *val= *key= sep;                    p= val+1;                }                if(ret<0 || p!=key){                    av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start);                    av_freep(&uc->priv_data);                    av_freep(&uc);                    err = AVERROR(EINVAL);                    goto fail;                }               // subfile:/media/dvd/VIDEO_TS/VTS_08_1.VOB                memmove(start, key+1, strlen(key));            }        }    }    if (int_cb)        uc->interrupt_callback = *int_cb;    *puc = uc;    return 0;fail:    *puc = NULL;    if (uc)        av_freep(&uc->priv_data);    av_freep(&uc);#if CONFIG_NETWORK    if (up->flags & URL_PROTOCOL_FLAG_NETWORK)        ff_network_close();#endif    return err;}

函数首先判断相关的flag所描述的需求是否满足,比如flag有AVIO_FLAG_READ,那么对应找到的URLProtocol必须要有url_read函数。后面是对创建的URLContext进行初始化,如av_class 、filename、 prot等等,其中prot就是之前我们找到的匹配的URLProtocol。接下来的if (up->priv_data_size){xx}是对priv_data的初始化,设定默认的参数值。后面if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start) 这个if语句块是对于“subfile”协议进行start和end值得设置,并且将正确的文件名前移,形成类似“subfile:abc.vob” 这样格式的filename。
subfile协议url的一个例子我上面注释中给了一个,具体的subfile协议可以参考subfile协议

0 0
原创粉丝点击