yading@11: /* yading@11: * unbuffered I/O yading@11: * Copyright (c) 2001 Fabrice Bellard yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: #include "libavutil/avstring.h" yading@11: #include "libavutil/dict.h" yading@11: #include "libavutil/opt.h" yading@11: #include "libavutil/time.h" yading@11: #include "os_support.h" yading@11: #include "avformat.h" yading@11: #if CONFIG_NETWORK yading@11: #include "network.h" yading@11: #endif yading@11: #include "url.h" yading@11: yading@11: static URLProtocol *first_protocol = NULL; yading@11: yading@11: URLProtocol *ffurl_protocol_next(URLProtocol *prev) yading@11: { yading@11: return prev ? prev->next : first_protocol; yading@11: } yading@11: yading@11: /** @name Logging context. */ yading@11: /*@{*/ yading@11: static const char *urlcontext_to_name(void *ptr) yading@11: { yading@11: URLContext *h = (URLContext *)ptr; yading@11: if(h->prot) return h->prot->name; yading@11: else return "NULL"; yading@11: } yading@11: yading@11: static void *urlcontext_child_next(void *obj, void *prev) yading@11: { yading@11: URLContext *h = obj; yading@11: if (!prev && h->priv_data && h->prot->priv_data_class) yading@11: return h->priv_data; yading@11: return NULL; yading@11: } yading@11: yading@11: static const AVClass *urlcontext_child_class_next(const AVClass *prev) yading@11: { yading@11: URLProtocol *p = NULL; yading@11: yading@11: /* find the protocol that corresponds to prev */ yading@11: while (prev && (p = ffurl_protocol_next(p))) yading@11: if (p->priv_data_class == prev) yading@11: break; yading@11: yading@11: /* find next protocol with priv options */ yading@11: while (p = ffurl_protocol_next(p)) yading@11: if (p->priv_data_class) yading@11: return p->priv_data_class; yading@11: return NULL; yading@11: yading@11: } yading@11: yading@11: static const AVOption options[] = {{NULL}}; yading@11: const AVClass ffurl_context_class = { yading@11: .class_name = "URLContext", yading@11: .item_name = urlcontext_to_name, yading@11: .option = options, yading@11: .version = LIBAVUTIL_VERSION_INT, yading@11: .child_next = urlcontext_child_next, yading@11: .child_class_next = urlcontext_child_class_next, yading@11: }; yading@11: /*@}*/ yading@11: yading@11: yading@11: const char *avio_enum_protocols(void **opaque, int output) yading@11: { yading@11: URLProtocol *p; yading@11: *opaque = ffurl_protocol_next(*opaque); yading@11: if (!(p = *opaque)) return NULL; yading@11: if ((output && p->url_write) || (!output && p->url_read)) yading@11: return p->name; yading@11: return avio_enum_protocols(opaque, output); yading@11: } yading@11: yading@11: int ffurl_register_protocol(URLProtocol *protocol, int size) yading@11: { yading@11: URLProtocol **p; yading@11: if (size < sizeof(URLProtocol)) { yading@11: URLProtocol* temp = av_mallocz(sizeof(URLProtocol)); yading@11: memcpy(temp, protocol, size); yading@11: protocol = temp; yading@11: } yading@11: p = &first_protocol; yading@11: while (*p != NULL) p = &(*p)->next; yading@11: *p = protocol; yading@11: protocol->next = NULL; yading@11: return 0; yading@11: } yading@11: yading@11: static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up, yading@11: const char *filename, int flags, yading@11: const AVIOInterruptCB *int_cb) yading@11: { yading@11: URLContext *uc; yading@11: int err; yading@11: yading@11: #if CONFIG_NETWORK yading@11: if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init()) yading@11: return AVERROR(EIO); yading@11: #endif yading@11: if ((flags & AVIO_FLAG_READ) && !up->url_read) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Impossible to open the '%s' protocol for reading\n", up->name); yading@11: return AVERROR(EIO); yading@11: } yading@11: if ((flags & AVIO_FLAG_WRITE) && !up->url_write) { yading@11: av_log(NULL, AV_LOG_ERROR, yading@11: "Impossible to open the '%s' protocol for writing\n", up->name); yading@11: return AVERROR(EIO); yading@11: } yading@11: uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1); yading@11: if (!uc) { yading@11: err = AVERROR(ENOMEM); yading@11: goto fail; yading@11: } yading@11: uc->av_class = &ffurl_context_class; yading@11: uc->filename = (char *) &uc[1]; yading@11: strcpy(uc->filename, filename); yading@11: uc->prot = up; yading@11: uc->flags = flags; yading@11: uc->is_streamed = 0; /* default = not streamed */ yading@11: uc->max_packet_size = 0; /* default: stream file */ yading@11: if (up->priv_data_size) { yading@11: uc->priv_data = av_mallocz(up->priv_data_size); yading@11: if (up->priv_data_class) { yading@11: int proto_len= strlen(up->name); yading@11: char *start = strchr(uc->filename, ','); yading@11: *(const AVClass**)uc->priv_data = up->priv_data_class; yading@11: av_opt_set_defaults(uc->priv_data); yading@11: if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){ yading@11: int ret= 0; yading@11: char *p= start; yading@11: char sep= *++p; yading@11: char *key, *val; yading@11: p++; yading@11: while(ret >= 0 && (key= strchr(p, sep)) && ppriv_data, p, key+1, 0); yading@11: if (ret == AVERROR_OPTION_NOT_FOUND) yading@11: av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p); yading@11: *val= *key= sep; yading@11: p= val+1; yading@11: } yading@11: if(ret<0 || p!=key){ yading@11: av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start); yading@11: av_freep(&uc->priv_data); yading@11: av_freep(&uc); yading@11: err = AVERROR(EINVAL); yading@11: goto fail; yading@11: } yading@11: memmove(start, key+1, strlen(key)); yading@11: } yading@11: } yading@11: } yading@11: if (int_cb) yading@11: uc->interrupt_callback = *int_cb; yading@11: yading@11: *puc = uc; yading@11: return 0; yading@11: fail: yading@11: *puc = NULL; yading@11: #if CONFIG_NETWORK yading@11: if (up->flags & URL_PROTOCOL_FLAG_NETWORK) yading@11: ff_network_close(); yading@11: #endif yading@11: return err; yading@11: } yading@11: yading@11: int ffurl_connect(URLContext* uc, AVDictionary **options) yading@11: { yading@11: int err = yading@11: uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) : yading@11: uc->prot->url_open(uc, uc->filename, uc->flags); yading@11: if (err) yading@11: return err; yading@11: uc->is_connected = 1; yading@11: //We must be careful here as ffurl_seek() could be slow, for example for http yading@11: if( (uc->flags & AVIO_FLAG_WRITE) yading@11: || !strcmp(uc->prot->name, "file")) yading@11: if(!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0) yading@11: uc->is_streamed= 1; yading@11: return 0; yading@11: } yading@11: yading@11: #define URL_SCHEME_CHARS \ yading@11: "abcdefghijklmnopqrstuvwxyz" \ yading@11: "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ yading@11: "0123456789+-." yading@11: yading@11: int ffurl_alloc(URLContext **puc, const char *filename, int flags, yading@11: const AVIOInterruptCB *int_cb) yading@11: { yading@11: URLProtocol *up = NULL; yading@11: char proto_str[128], proto_nested[128], *ptr; yading@11: size_t proto_len = strspn(filename, URL_SCHEME_CHARS); yading@11: yading@11: if (!first_protocol) { yading@11: av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. " yading@11: "Missing call to av_register_all()?\n"); yading@11: } yading@11: yading@11: if (filename[proto_len] != ':' && yading@11: (filename[proto_len] != ',' || !strchr(filename + proto_len + 1, ':')) || yading@11: is_dos_path(filename)) yading@11: strcpy(proto_str, "file"); yading@11: else yading@11: av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str))); yading@11: yading@11: if ((ptr = strchr(proto_str, ','))) yading@11: *ptr = '\0'; yading@11: av_strlcpy(proto_nested, proto_str, sizeof(proto_nested)); yading@11: if ((ptr = strchr(proto_nested, '+'))) yading@11: *ptr = '\0'; yading@11: yading@11: while (up = ffurl_protocol_next(up)) { yading@11: if (!strcmp(proto_str, up->name)) yading@11: return url_alloc_for_protocol (puc, up, filename, flags, int_cb); yading@11: if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME && yading@11: !strcmp(proto_nested, up->name)) yading@11: return url_alloc_for_protocol (puc, up, filename, flags, int_cb); yading@11: } yading@11: *puc = NULL; yading@11: return AVERROR_PROTOCOL_NOT_FOUND; yading@11: } yading@11: yading@11: int ffurl_open(URLContext **puc, const char *filename, int flags, yading@11: const AVIOInterruptCB *int_cb, AVDictionary **options) yading@11: { yading@11: int ret = ffurl_alloc(puc, filename, flags, int_cb); yading@11: if (ret) yading@11: return ret; yading@11: if (options && (*puc)->prot->priv_data_class && yading@11: (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0) yading@11: goto fail; yading@11: ret = ffurl_connect(*puc, options); yading@11: if (!ret) yading@11: return 0; yading@11: fail: yading@11: ffurl_close(*puc); yading@11: *puc = NULL; yading@11: return ret; yading@11: } yading@11: yading@11: static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min, yading@11: int (*transfer_func)(URLContext *h, unsigned char *buf, int size)) yading@11: { yading@11: int ret, len; yading@11: int fast_retries = 5; yading@11: int64_t wait_since = 0; yading@11: yading@11: len = 0; yading@11: while (len < size_min) { yading@11: ret = transfer_func(h, buf+len, size-len); yading@11: if (ret == AVERROR(EINTR)) yading@11: continue; yading@11: if (h->flags & AVIO_FLAG_NONBLOCK) yading@11: return ret; yading@11: if (ret == AVERROR(EAGAIN)) { yading@11: ret = 0; yading@11: if (fast_retries) { yading@11: fast_retries--; yading@11: } else { yading@11: if (h->rw_timeout) { yading@11: if (!wait_since) yading@11: wait_since = av_gettime(); yading@11: else if (av_gettime() > wait_since + h->rw_timeout) yading@11: return AVERROR(EIO); yading@11: } yading@11: av_usleep(1000); yading@11: } yading@11: } else if (ret < 1) yading@11: return ret < 0 ? ret : len; yading@11: if (ret) yading@11: fast_retries = FFMAX(fast_retries, 2); yading@11: len += ret; yading@11: if (len < size && ff_check_interrupt(&h->interrupt_callback)) yading@11: return AVERROR_EXIT; yading@11: } yading@11: return len; yading@11: } yading@11: yading@11: int ffurl_read(URLContext *h, unsigned char *buf, int size) yading@11: { yading@11: if (!(h->flags & AVIO_FLAG_READ)) yading@11: return AVERROR(EIO); yading@11: return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read); yading@11: } yading@11: yading@11: int ffurl_read_complete(URLContext *h, unsigned char *buf, int size) yading@11: { yading@11: if (!(h->flags & AVIO_FLAG_READ)) yading@11: return AVERROR(EIO); yading@11: return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read); yading@11: } yading@11: yading@11: int ffurl_write(URLContext *h, const unsigned char *buf, int size) yading@11: { yading@11: if (!(h->flags & AVIO_FLAG_WRITE)) yading@11: return AVERROR(EIO); yading@11: /* avoid sending too big packets */ yading@11: if (h->max_packet_size && size > h->max_packet_size) yading@11: return AVERROR(EIO); yading@11: yading@11: return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (void*)h->prot->url_write); yading@11: } yading@11: yading@11: int64_t ffurl_seek(URLContext *h, int64_t pos, int whence) yading@11: { yading@11: int64_t ret; yading@11: yading@11: if (!h->prot->url_seek) yading@11: return AVERROR(ENOSYS); yading@11: ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE); yading@11: return ret; yading@11: } yading@11: yading@11: int ffurl_closep(URLContext **hh) yading@11: { yading@11: URLContext *h= *hh; yading@11: int ret = 0; yading@11: if (!h) return 0; /* can happen when ffurl_open fails */ yading@11: yading@11: if (h->is_connected && h->prot->url_close) yading@11: ret = h->prot->url_close(h); yading@11: #if CONFIG_NETWORK yading@11: if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK) yading@11: ff_network_close(); yading@11: #endif yading@11: if (h->prot->priv_data_size) { yading@11: if (h->prot->priv_data_class) yading@11: av_opt_free(h->priv_data); yading@11: av_freep(&h->priv_data); yading@11: } yading@11: av_freep(hh); yading@11: return ret; yading@11: } yading@11: yading@11: int ffurl_close(URLContext *h) yading@11: { yading@11: return ffurl_closep(&h); yading@11: } yading@11: yading@11: yading@11: int avio_check(const char *url, int flags) yading@11: { yading@11: URLContext *h; yading@11: int ret = ffurl_alloc(&h, url, flags, NULL); yading@11: if (ret) yading@11: return ret; yading@11: yading@11: if (h->prot->url_check) { yading@11: ret = h->prot->url_check(h, flags); yading@11: } else { yading@11: ret = ffurl_connect(h, NULL); yading@11: if (ret >= 0) yading@11: ret = flags; yading@11: } yading@11: yading@11: ffurl_close(h); yading@11: return ret; yading@11: } yading@11: yading@11: int64_t ffurl_size(URLContext *h) yading@11: { yading@11: int64_t pos, size; yading@11: yading@11: size= ffurl_seek(h, 0, AVSEEK_SIZE); yading@11: if(size<0){ yading@11: pos = ffurl_seek(h, 0, SEEK_CUR); yading@11: if ((size = ffurl_seek(h, -1, SEEK_END)) < 0) yading@11: return size; yading@11: size++; yading@11: ffurl_seek(h, pos, SEEK_SET); yading@11: } yading@11: return size; yading@11: } yading@11: yading@11: int ffurl_get_file_handle(URLContext *h) yading@11: { yading@11: if (!h->prot->url_get_file_handle) yading@11: return -1; yading@11: return h->prot->url_get_file_handle(h); yading@11: } yading@11: yading@11: int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles) yading@11: { yading@11: if (!h->prot->url_get_multi_file_handle) { yading@11: if (!h->prot->url_get_file_handle) yading@11: return AVERROR(ENOSYS); yading@11: *handles = av_malloc(sizeof(**handles)); yading@11: if (!*handles) yading@11: return AVERROR(ENOMEM); yading@11: *numhandles = 1; yading@11: *handles[0] = h->prot->url_get_file_handle(h); yading@11: return 0; yading@11: } yading@11: return h->prot->url_get_multi_file_handle(h, handles, numhandles); yading@11: } yading@11: yading@11: int ffurl_shutdown(URLContext *h, int flags) yading@11: { yading@11: if (!h->prot->url_shutdown) yading@11: return AVERROR(EINVAL); yading@11: return h->prot->url_shutdown(h, flags); yading@11: } yading@11: yading@11: int ff_check_interrupt(AVIOInterruptCB *cb) yading@11: { yading@11: int ret; yading@11: if (cb && cb->callback && (ret = cb->callback(cb->opaque))) yading@11: return ret; yading@11: return 0; yading@11: }