diff options
Diffstat (limited to 'libavformat/avio.c')
-rw-r--r-- | libavformat/avio.c | 376 |
1 files changed, 332 insertions, 44 deletions
diff --git a/libavformat/avio.c b/libavformat/avio.c index 1692f1ba47..1e79c9dd5c 100644 --- a/libavformat/avio.c +++ b/libavformat/avio.c @@ -2,20 +2,20 @@ * unbuffered I/O * Copyright (c) 2001 Fabrice Bellard * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -23,6 +23,7 @@ #include "libavutil/dict.h" #include "libavutil/opt.h" #include "libavutil/time.h" +#include "libavutil/avassert.h" #include "os_support.h" #include "avformat.h" #if CONFIG_NETWORK @@ -49,10 +50,16 @@ static void *urlcontext_child_next(void *obj, void *prev) return NULL; } +#define OFFSET(x) offsetof(URLContext,x) +#define E AV_OPT_FLAG_ENCODING_PARAM +#define D AV_OPT_FLAG_DECODING_PARAM static const AVOption options[] = { - { "rw_timeout", "Timeout for IO operations (in microseconds)", offsetof(URLContext, rw_timeout), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM }, + {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, + {"protocol_blacklist", "List of protocols that are not allowed to be used", OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, + {"rw_timeout", "Timeout for IO operations (in microseconds)", offsetof(URLContext, rw_timeout), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_DECODING_PARAM }, { NULL } }; + const AVClass ffurl_context_class = { .class_name = "URLContext", .item_name = urlcontext_to_name, @@ -65,8 +72,7 @@ const AVClass ffurl_context_class = { static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up, const char *filename, int flags, - const AVIOInterruptCB *int_cb, - const URLProtocol **protocols) + const AVIOInterruptCB *int_cb) { URLContext *uc; int err; @@ -75,6 +81,16 @@ static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up, 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); @@ -87,7 +103,6 @@ static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up, uc->flags = flags; uc->is_streamed = 0; /* default = not streamed */ uc->max_packet_size = 0; /* default: stream file */ - uc->protocols = protocols; if (up->priv_data_size) { uc->priv_data = av_mallocz(up->priv_data_size); if (!uc->priv_data) { @@ -95,8 +110,40 @@ static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up, 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); + 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; + } + memmove(start, key+1, strlen(key)); + } } } if (int_cb) @@ -118,12 +165,53 @@ fail: int ffurl_connect(URLContext *uc, AVDictionary **options) { - int err = + int err; + AVDictionary *tmp_opts = NULL; + AVDictionaryEntry *e; + + if (!options) + options = &tmp_opts; + + // Check that URLContext was initialized correctly and lists are matching if set + av_assert0(!(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) || + (uc->protocol_whitelist && !strcmp(uc->protocol_whitelist, e->value))); + av_assert0(!(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) || + (uc->protocol_blacklist && !strcmp(uc->protocol_blacklist, e->value))); + + if (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, ',') <= 0) { + av_log(uc, AV_LOG_ERROR, "Protocol '%s' not on whitelist '%s'!\n", uc->prot->name, uc->protocol_whitelist); + return AVERROR(EINVAL); + } + + if (uc->protocol_blacklist && av_match_list(uc->prot->name, uc->protocol_blacklist, ',') > 0) { + av_log(uc, AV_LOG_ERROR, "Protocol '%s' on blacklist '%s'!\n", uc->prot->name, uc->protocol_blacklist); + return AVERROR(EINVAL); + } + + if (!uc->protocol_whitelist && uc->prot->default_whitelist) { + av_log(uc, AV_LOG_DEBUG, "Setting default whitelist '%s'\n", uc->prot->default_whitelist); + uc->protocol_whitelist = av_strdup(uc->prot->default_whitelist); + if (!uc->protocol_whitelist) { + return AVERROR(ENOMEM); + } + } else if (!uc->protocol_whitelist) + av_log(uc, AV_LOG_DEBUG, "No default whitelist set\n"); // This should be an error once all declare a default whitelist + + if ((err = av_dict_set(options, "protocol_whitelist", uc->protocol_whitelist, 0)) < 0) + return err; + if ((err = av_dict_set(options, "protocol_blacklist", uc->protocol_blacklist, 0)) < 0) + return err; + + err = uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) : uc->prot->url_open(uc, uc->filename, uc->flags); + + av_dict_set(options, "protocol_whitelist", NULL, 0); + av_dict_set(options, "protocol_blacklist", NULL, 0); + if (err) return err; uc->is_connected = 1; @@ -135,50 +223,98 @@ int ffurl_connect(URLContext *uc, AVDictionary **options) return 0; } +int ffurl_accept(URLContext *s, URLContext **c) +{ + av_assert0(!*c); + if (s->prot->url_accept) + return s->prot->url_accept(s, c); + return AVERROR(EBADF); +} + +int ffurl_handshake(URLContext *c) +{ + int ret; + if (c->prot->url_handshake) { + ret = c->prot->url_handshake(c); + if (ret) + return ret; + } + c->is_connected = 1; + return 0; +} + #define URL_SCHEME_CHARS \ "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "0123456789+-." -int ffurl_alloc(URLContext **puc, const char *filename, int flags, - const AVIOInterruptCB *int_cb, - const URLProtocol **protocols) +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] != ':' || is_dos_path(filename)) + 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); + if (!protocols) + return NULL; for (i = 0; protocols[i]; i++) { - const URLProtocol *up = protocols[i]; - if (!strcmp(proto_str, up->name)) - return url_alloc_for_protocol(puc, up, filename, flags, int_cb, - protocols); + 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)) - return url_alloc_for_protocol(puc, up, filename, flags, int_cb, - protocols); + !strcmp(proto_nested, up->name)) { + av_freep(&protocols); + return up; + } } + av_freep(&protocols); + + return NULL; +} + +int ffurl_alloc(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb) +{ + const URLProtocol *p = NULL; + + p = url_find_protocol(filename); + if (p) + return url_alloc_for_protocol(puc, p, filename, flags, int_cb); + *puc = NULL; + if (av_strstart(filename, "https:", NULL)) + av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile FFmpeg with " + "openssl, gnutls " + "or securetransport enabled.\n"); return AVERROR_PROTOCOL_NOT_FOUND; } -int ffurl_open(URLContext **puc, const char *filename, int flags, - const AVIOInterruptCB *int_cb, AVDictionary **options, - const URLProtocol **protocols, - URLContext *parent) +int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options, + const char *whitelist, const char* blacklist, + URLContext *parent) { - int ret = ffurl_alloc(puc, filename, flags, int_cb, protocols); - if (ret) + AVDictionary *tmp_opts = NULL; + AVDictionaryEntry *e; + int ret = ffurl_alloc(puc, filename, flags, int_cb); + if (ret < 0) return ret; if (parent) av_opt_copy(*puc, parent); @@ -188,7 +324,28 @@ int ffurl_open(URLContext **puc, const char *filename, int flags, if (options && (*puc)->prot->priv_data_class && (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0) goto fail; + + if (!options) + options = &tmp_opts; + + av_assert0(!whitelist || + !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) || + !strcmp(whitelist, e->value)); + av_assert0(!blacklist || + !(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) || + !strcmp(blacklist, e->value)); + + if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0) + goto fail; + + if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0) + goto fail; + + if ((ret = av_opt_set_dict(*puc, options)) < 0) + goto fail; + ret = ffurl_connect(*puc, options); + if (!ret) return 0; fail: @@ -197,6 +354,13 @@ fail: return ret; } +int ffurl_open(URLContext **puc, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options) +{ + return ffurl_open_whitelist(puc, filename, flags, + int_cb, options, NULL, NULL, NULL); +} + static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf, int size, int size_min, int (*transfer_func)(URLContext *h, @@ -209,6 +373,8 @@ static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf, len = 0; while (len < size_min) { + if (ff_check_interrupt(&h->interrupt_callback)) + return AVERROR_EXIT; ret = transfer_func(h, buf + len, size - len); if (ret == AVERROR(EINTR)) continue; @@ -234,8 +400,6 @@ static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf, wait_since = 0; } len += ret; - if (ff_check_interrupt(&h->interrupt_callback)) - return AVERROR_EXIT; } return len; } @@ -262,7 +426,7 @@ int ffurl_write(URLContext *h, const unsigned char *buf, int size) if (h->max_packet_size && size > h->max_packet_size) return AVERROR(EIO); - return retry_transfer_wrapper(h, buf, size, size, + return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (int (*)(struct URLContext *, uint8_t *, int)) h->prot->url_write); } @@ -277,8 +441,9 @@ int64_t ffurl_seek(URLContext *h, int64_t pos, int whence) return ret; } -int ffurl_close(URLContext *h) +int ffurl_closep(URLContext **hh) { + URLContext *h= *hh; int ret = 0; if (!h) return 0; /* can happen when ffurl_open fails */ @@ -292,27 +457,32 @@ int ffurl_close(URLContext *h) if (h->prot->priv_data_size) { if (h->prot->priv_data_class) av_opt_free(h->priv_data); - av_free(h->priv_data); + av_freep(&h->priv_data); } - av_free(h); + av_opt_free(h); + av_freep(hh); return ret; } -int avio_check(const char *url, int flags) +int ffurl_close(URLContext *h) { - const URLProtocol **protocols; - URLContext *h; - int ret; + return ffurl_closep(&h); +} - protocols = ffurl_get_protocols(NULL, NULL); - if (!protocols) - return AVERROR(ENOMEM); - ret = ffurl_alloc(&h, url, flags, NULL, protocols); - if (ret) { - av_freep(&protocols); +const char *avio_find_protocol_name(const char *url) +{ + const URLProtocol *p = url_find_protocol(url); + + return p ? p->name : NULL; +} + +int avio_check(const char *url, int flags) +{ + URLContext *h; + int ret = ffurl_alloc(&h, url, flags, NULL); + if (ret < 0) return ret; - } if (h->prot->url_check) { ret = h->prot->url_check(h, flags); @@ -323,10 +493,121 @@ int avio_check(const char *url, int flags) } ffurl_close(h); - av_freep(&protocols); return ret; } +int avpriv_io_move(const char *url_src, const char *url_dst) +{ + URLContext *h_src, *h_dst; + int ret = ffurl_alloc(&h_src, url_src, AVIO_FLAG_READ_WRITE, NULL); + if (ret < 0) + return ret; + ret = ffurl_alloc(&h_dst, url_dst, AVIO_FLAG_WRITE, NULL); + if (ret < 0) { + ffurl_close(h_src); + return ret; + } + + if (h_src->prot == h_dst->prot && h_src->prot->url_move) + ret = h_src->prot->url_move(h_src, h_dst); + else + ret = AVERROR(ENOSYS); + + ffurl_close(h_src); + ffurl_close(h_dst); + return ret; +} + +int avpriv_io_delete(const char *url) +{ + URLContext *h; + int ret = ffurl_alloc(&h, url, AVIO_FLAG_WRITE, NULL); + if (ret < 0) + return ret; + + if (h->prot->url_delete) + ret = h->prot->url_delete(h); + else + ret = AVERROR(ENOSYS); + + ffurl_close(h); + return ret; +} + +int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options) +{ + URLContext *h = NULL; + AVIODirContext *ctx = NULL; + int ret; + av_assert0(s); + + ctx = av_mallocz(sizeof(*ctx)); + if (!ctx) { + ret = AVERROR(ENOMEM); + goto fail; + } + + if ((ret = ffurl_alloc(&h, url, AVIO_FLAG_READ, NULL)) < 0) + goto fail; + + if (h->prot->url_open_dir && h->prot->url_read_dir && h->prot->url_close_dir) { + if (options && h->prot->priv_data_class && + (ret = av_opt_set_dict(h->priv_data, options)) < 0) + goto fail; + ret = h->prot->url_open_dir(h); + } else + ret = AVERROR(ENOSYS); + if (ret < 0) + goto fail; + + h->is_connected = 1; + ctx->url_context = h; + *s = ctx; + return 0; + + fail: + av_free(ctx); + *s = NULL; + ffurl_close(h); + return ret; +} + +int avio_read_dir(AVIODirContext *s, AVIODirEntry **next) +{ + URLContext *h; + int ret; + + if (!s || !s->url_context) + return AVERROR(EINVAL); + h = s->url_context; + if ((ret = h->prot->url_read_dir(h, next)) < 0) + avio_free_directory_entry(next); + return ret; +} + +int avio_close_dir(AVIODirContext **s) +{ + URLContext *h; + + av_assert0(s); + if (!(*s) || !(*s)->url_context) + return AVERROR(EINVAL); + h = (*s)->url_context; + h->prot->url_close_dir(h); + ffurl_close(h); + av_freep(s); + *s = NULL; + return 0; +} + +void avio_free_directory_entry(AVIODirEntry **entry) +{ + if (!entry || !*entry) + return; + av_free((*entry)->name); + av_freep(entry); +} + int64_t ffurl_size(URLContext *h) { int64_t pos, size; @@ -364,6 +645,13 @@ int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles) return h->prot->url_get_multi_file_handle(h, handles, numhandles); } +int ffurl_get_short_seek(URLContext *h) +{ + if (!h->prot->url_get_short_seek) + return AVERROR(ENOSYS); + return h->prot->url_get_short_seek(h); +} + int ffurl_shutdown(URLContext *h, int flags) { if (!h->prot->url_shutdown) |