X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Favio.c;h=9a4d7355c58b55bfefb61b610bac739bfc68e307;hb=3e8c4f96890294e1b7de2d22ab3cfec7e1d7c48f;hp=3447889988374c58b499608dbba92c119e68da1e;hpb=84be6e723930e540ee105949af1a7a498164560c;p=ffmpeg diff --git a/libavformat/avio.c b/libavformat/avio.c index 34478899883..9a4d7355c58 100644 --- a/libavformat/avio.c +++ b/libavformat/avio.c @@ -18,8 +18,32 @@ * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* needed for usleep() */ +#define _XOPEN_SOURCE 600 +#include +#include "libavutil/avstring.h" +#include "libavutil/opt.h" +#include "os_support.h" #include "avformat.h" -#include "avstring.h" +#if CONFIG_NETWORK +#include "network.h" +#endif + +#if FF_API_URL_CLASS +/** @name Logging context. */ +/*@{*/ +static const char *urlcontext_to_name(void *ptr) +{ + URLContext *h = (URLContext *)ptr; + if(h->prot) return h->prot->name; + else return "NULL"; +} +static const AVOption options[] = {{NULL}}; +static const AVClass urlcontext_class = + { "URLContext", urlcontext_to_name, options, LIBAVUTIL_VERSION_INT }; +/*@}*/ +#endif static int default_interrupt_cb(void); @@ -32,9 +56,14 @@ URLProtocol *av_protocol_next(URLProtocol *p) else return first_protocol; } -int register_protocol(URLProtocol *protocol) +int av_register_protocol2(URLProtocol *protocol, int size) { URLProtocol **p; + if (size < sizeof(URLProtocol)) { + URLProtocol* temp = av_mallocz(sizeof(URLProtocol)); + memcpy(temp, protocol, size); + protocol = temp; + } p = &first_protocol; while (*p != NULL) p = &(*p)->next; *p = protocol; @@ -42,67 +71,141 @@ int register_protocol(URLProtocol *protocol) return 0; } -int url_open(URLContext **puc, const char *filename, int flags) +#if FF_API_REGISTER_PROTOCOL +/* The layout of URLProtocol as of when major was bumped to 52 */ +struct URLProtocol_compat { + const char *name; + int (*url_open)(URLContext *h, const char *filename, int flags); + int (*url_read)(URLContext *h, unsigned char *buf, int size); + int (*url_write)(URLContext *h, unsigned char *buf, int size); + int64_t (*url_seek)(URLContext *h, int64_t pos, int whence); + int (*url_close)(URLContext *h); + struct URLProtocol *next; +}; + +int av_register_protocol(URLProtocol *protocol) +{ + return av_register_protocol2(protocol, sizeof(struct URLProtocol_compat)); +} + +int register_protocol(URLProtocol *protocol) +{ + return av_register_protocol2(protocol, sizeof(struct URLProtocol_compat)); +} +#endif + +static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up, + const char *filename, int flags) { URLContext *uc; - URLProtocol *up; - const char *p; - char proto_str[128], *q; int err; - p = filename; - q = proto_str; - while (*p != '\0' && *p != ':') { - /* protocols can only contain alphabetic chars */ - if (!isalpha(*p)) - goto file_proto; - if ((q - proto_str) < sizeof(proto_str) - 1) - *q++ = *p; - p++; - } - /* if the protocol has length 1, we consider it is a dos drive */ - if (*p == '\0' || (q - proto_str) <= 1) { - file_proto: - strcpy(proto_str, "file"); - } else { - *q = '\0'; - } - - up = first_protocol; - while (up != NULL) { - if (!strcmp(proto_str, up->name)) - goto found; - up = up->next; - } - err = AVERROR(ENOENT); - goto fail; - found: - uc = av_malloc(sizeof(URLContext) + strlen(filename) + 1); +#if CONFIG_NETWORK + if (!ff_network_init()) + return AVERROR(EIO); +#endif + uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1); if (!uc) { err = AVERROR(ENOMEM); goto fail; } -#if LIBAVFORMAT_VERSION_INT >= (52<<16) - uc->filename = (char *) &uc[1]; +#if FF_API_URL_CLASS + uc->av_class = &urlcontext_class; #endif + 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 */ - err = up->url_open(uc, filename, flags); - if (err < 0) { - av_free(uc); - *puc = NULL; - return err; + if (up->priv_data_size) { + uc->priv_data = av_mallocz(up->priv_data_size); + if (up->priv_data_class) { + *(const AVClass**)uc->priv_data = up->priv_data_class; + av_opt_set_defaults(uc->priv_data); + } } + *puc = uc; return 0; fail: *puc = NULL; +#if CONFIG_NETWORK + ff_network_close(); +#endif return err; } +int url_connect(URLContext* uc) +{ + int err = uc->prot->url_open(uc, uc->filename, uc->flags); + if (err) + return err; + uc->is_connected = 1; + //We must be careful here as url_seek() could be slow, for example for http + if( (uc->flags & (URL_WRONLY | URL_RDWR)) + || !strcmp(uc->prot->name, "file")) + if(!uc->is_streamed && url_seek(uc, 0, SEEK_SET) < 0) + uc->is_streamed= 1; + return 0; +} + +int url_open_protocol (URLContext **puc, struct URLProtocol *up, + const char *filename, int flags) +{ + int ret; + + ret = url_alloc_for_protocol(puc, up, filename, flags); + if (ret) + goto fail; + ret = url_connect(*puc); + if (!ret) + return 0; + fail: + url_close(*puc); + *puc = NULL; + return ret; +} + +#define URL_SCHEME_CHARS \ + "abcdefghijklmnopqrstuvwxyz" \ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + "0123456789+-." + +int url_alloc(URLContext **puc, const char *filename, int flags) +{ + URLProtocol *up; + char proto_str[128]; + size_t proto_len = strspn(filename, URL_SCHEME_CHARS); + + if (filename[proto_len] != ':' || is_dos_path(filename)) + strcpy(proto_str, "file"); + else + av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str))); + + up = first_protocol; + while (up != NULL) { + if (!strcmp(proto_str, up->name)) + return url_alloc_for_protocol (puc, up, filename, flags); + up = up->next; + } + *puc = NULL; + return AVERROR(ENOENT); +} + +int url_open(URLContext **puc, const char *filename, int flags) +{ + int ret = url_alloc(puc, filename, flags); + if (ret) + return ret; + ret = url_connect(*puc); + if (!ret) + return 0; + url_close(*puc); + *puc = NULL; + return ret; +} + int url_read(URLContext *h, unsigned char *buf, int size) { int ret; @@ -112,27 +215,53 @@ int url_read(URLContext *h, unsigned char *buf, int size) return ret; } -#if defined(CONFIG_MUXERS) || defined(CONFIG_PROTOCOLS) -int url_write(URLContext *h, unsigned char *buf, int size) +static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, + int (*transfer_func)(URLContext *h, unsigned char *buf, int size)) +{ + int ret, len; + int fast_retries = 5; + + len = 0; + while (len < size) { + ret = transfer_func(h, buf+len, size-len); + if (ret == AVERROR(EAGAIN)) { + ret = 0; + if (fast_retries) + fast_retries--; + else + usleep(1000); + } else if (ret < 1) + return ret < 0 ? ret : len; + if (ret) + fast_retries = FFMAX(fast_retries, 2); + len += ret; + } + return len; +} + +int url_read_complete(URLContext *h, unsigned char *buf, int size) +{ + return retry_transfer_wrapper(h, buf, size, url_read); +} + +int url_write(URLContext *h, const unsigned char *buf, int size) { - int ret; if (!(h->flags & (URL_WRONLY | URL_RDWR))) return AVERROR(EIO); /* avoid sending too big packets */ if (h->max_packet_size && size > h->max_packet_size) return AVERROR(EIO); - ret = h->prot->url_write(h, buf, size); - return ret; + + return retry_transfer_wrapper(h, buf, size, h->prot->url_write); } -#endif //CONFIG_MUXERS || CONFIG_PROTOCOLS -offset_t url_seek(URLContext *h, offset_t pos, int whence) +int64_t url_seek(URLContext *h, int64_t pos, int whence) { - offset_t ret; + int64_t ret; if (!h->prot->url_seek) - return AVERROR(EPIPE); - ret = h->prot->url_seek(h, pos, whence); + return AVERROR(ENOSYS); + ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE); return ret; } @@ -141,8 +270,13 @@ int url_close(URLContext *h) int ret = 0; if (!h) return 0; /* can happen when url_open fails */ - if (h->prot->url_close) + if (h->is_connected && h->prot->url_close) ret = h->prot->url_close(h); +#if CONFIG_NETWORK + ff_network_close(); +#endif + if (h->prot->priv_data_size) + av_free(h->priv_data); av_free(h); return ret; } @@ -156,9 +290,9 @@ int url_exist(const char *filename) return 1; } -offset_t url_filesize(URLContext *h) +int64_t url_filesize(URLContext *h) { - offset_t pos, size; + int64_t pos, size; size= url_seek(h, 0, AVSEEK_SIZE); if(size<0){ @@ -171,6 +305,13 @@ offset_t url_filesize(URLContext *h) return size; } +int url_get_file_handle(URLContext *h) +{ + if (!h->prot->url_get_file_handle) + return -1; + return h->prot->url_get_file_handle(h); +} + int url_get_max_packet_size(URLContext *h) { return h->max_packet_size; @@ -194,21 +335,14 @@ void url_set_interrupt_cb(URLInterruptCB *interrupt_cb) url_interrupt_cb = interrupt_cb; } -int av_url_read_play(URLContext *h) -{ - if (!h->prot->url_read_play) - return AVERROR(ENOSYS); - return h->prot->url_read_play(h); -} - -int av_url_read_pause(URLContext *h) +int av_url_read_pause(URLContext *h, int pause) { if (!h->prot->url_read_pause) return AVERROR(ENOSYS); - return h->prot->url_read_pause(h); + return h->prot->url_read_pause(h, pause); } -int av_url_read_seek(URLContext *h, +int64_t av_url_read_seek(URLContext *h, int stream_index, int64_t timestamp, int flags) { if (!h->prot->url_read_seek)