00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <unistd.h>
00023
00024 #include "libavutil/avstring.h"
00025 #include "libavutil/dict.h"
00026 #include "libavutil/opt.h"
00027 #include "os_support.h"
00028 #include "avformat.h"
00029 #if CONFIG_NETWORK
00030 #include "network.h"
00031 #endif
00032 #include "url.h"
00033
00034 static URLProtocol *first_protocol = NULL;
00035
00036 URLProtocol *ffurl_protocol_next(URLProtocol *prev)
00037 {
00038 return prev ? prev->next : first_protocol;
00039 }
00040
00043 static const char *urlcontext_to_name(void *ptr)
00044 {
00045 URLContext *h = (URLContext *)ptr;
00046 if(h->prot) return h->prot->name;
00047 else return "NULL";
00048 }
00049
00050 static void *urlcontext_child_next(void *obj, void *prev)
00051 {
00052 URLContext *h = obj;
00053 if (!prev && h->priv_data && h->prot->priv_data_class)
00054 return h->priv_data;
00055 return NULL;
00056 }
00057
00058 static const AVClass *urlcontext_child_class_next(const AVClass *prev)
00059 {
00060 URLProtocol *p = NULL;
00061
00062
00063 while (prev && (p = ffurl_protocol_next(p)))
00064 if (p->priv_data_class == prev)
00065 break;
00066
00067
00068 while (p = ffurl_protocol_next(p))
00069 if (p->priv_data_class)
00070 return p->priv_data_class;
00071 return NULL;
00072
00073 }
00074
00075 static const AVOption options[] = {{NULL}};
00076 const AVClass ffurl_context_class = {
00077 .class_name = "URLContext",
00078 .item_name = urlcontext_to_name,
00079 .option = options,
00080 .version = LIBAVUTIL_VERSION_INT,
00081 .child_next = urlcontext_child_next,
00082 .child_class_next = urlcontext_child_class_next,
00083 };
00087 const char *avio_enum_protocols(void **opaque, int output)
00088 {
00089 URLProtocol **p = (URLProtocol **)opaque;
00090 *p = ffurl_protocol_next(*p);
00091 if (!*p) return NULL;
00092 if ((output && (*p)->url_write) || (!output && (*p)->url_read))
00093 return (*p)->name;
00094 return avio_enum_protocols(opaque, output);
00095 }
00096
00097 int ffurl_register_protocol(URLProtocol *protocol, int size)
00098 {
00099 URLProtocol **p;
00100 if (size < sizeof(URLProtocol)) {
00101 URLProtocol* temp = av_mallocz(sizeof(URLProtocol));
00102 memcpy(temp, protocol, size);
00103 protocol = temp;
00104 }
00105 p = &first_protocol;
00106 while (*p != NULL) p = &(*p)->next;
00107 *p = protocol;
00108 protocol->next = NULL;
00109 return 0;
00110 }
00111
00112 static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up,
00113 const char *filename, int flags,
00114 const AVIOInterruptCB *int_cb)
00115 {
00116 URLContext *uc;
00117 int err;
00118
00119 #if CONFIG_NETWORK
00120 if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init())
00121 return AVERROR(EIO);
00122 #endif
00123 uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);
00124 if (!uc) {
00125 err = AVERROR(ENOMEM);
00126 goto fail;
00127 }
00128 uc->av_class = &ffurl_context_class;
00129 uc->filename = (char *) &uc[1];
00130 strcpy(uc->filename, filename);
00131 uc->prot = up;
00132 uc->flags = flags;
00133 uc->is_streamed = 0;
00134 uc->max_packet_size = 0;
00135 if (up->priv_data_size) {
00136 uc->priv_data = av_mallocz(up->priv_data_size);
00137 if (up->priv_data_class) {
00138 int proto_len= strlen(up->name);
00139 char *start = strchr(uc->filename, ',');
00140 *(const AVClass**)uc->priv_data = up->priv_data_class;
00141 av_opt_set_defaults(uc->priv_data);
00142 if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){
00143 int ret= 0;
00144 char *p= start;
00145 char sep= *++p;
00146 char *key, *val;
00147 p++;
00148 while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){
00149 *val= *key= 0;
00150 ret= av_opt_set(uc->priv_data, p, key+1, 0);
00151 if (ret == AVERROR_OPTION_NOT_FOUND)
00152 av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p);
00153 *val= *key= sep;
00154 p= val+1;
00155 }
00156 if(ret<0 || p!=key){
00157 av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start);
00158 av_freep(&uc->priv_data);
00159 av_freep(&uc);
00160 goto fail;
00161 }
00162 memmove(start, key+1, strlen(key));
00163 }
00164 }
00165 }
00166 if (int_cb)
00167 uc->interrupt_callback = *int_cb;
00168
00169 *puc = uc;
00170 return 0;
00171 fail:
00172 *puc = NULL;
00173 #if CONFIG_NETWORK
00174 if (up->flags & URL_PROTOCOL_FLAG_NETWORK)
00175 ff_network_close();
00176 #endif
00177 return err;
00178 }
00179
00180 int ffurl_connect(URLContext* uc, AVDictionary **options)
00181 {
00182 int err =
00183 uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) :
00184 uc->prot->url_open(uc, uc->filename, uc->flags);
00185 if (err)
00186 return err;
00187 uc->is_connected = 1;
00188
00189 if( (uc->flags & AVIO_FLAG_WRITE)
00190 || !strcmp(uc->prot->name, "file"))
00191 if(!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
00192 uc->is_streamed= 1;
00193 return 0;
00194 }
00195
00196 #define URL_SCHEME_CHARS \
00197 "abcdefghijklmnopqrstuvwxyz" \
00198 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
00199 "0123456789+-."
00200
00201 int ffurl_alloc(URLContext **puc, const char *filename, int flags,
00202 const AVIOInterruptCB *int_cb)
00203 {
00204 URLProtocol *up = NULL;
00205 char proto_str[128], proto_nested[128], *ptr;
00206 size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
00207
00208 if (!first_protocol) {
00209 av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. "
00210 "Missing call to av_register_all()?\n");
00211 }
00212
00213 if (filename[proto_len] != ':' && filename[proto_len] != ',' || is_dos_path(filename))
00214 strcpy(proto_str, "file");
00215 else
00216 av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str)));
00217
00218 if ((ptr = strchr(proto_str, ',')))
00219 *ptr = '\0';
00220 av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
00221 if ((ptr = strchr(proto_nested, '+')))
00222 *ptr = '\0';
00223
00224 while (up = ffurl_protocol_next(up)) {
00225 if (!strcmp(proto_str, up->name))
00226 return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
00227 if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
00228 !strcmp(proto_nested, up->name))
00229 return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
00230 }
00231 *puc = NULL;
00232 return AVERROR_PROTOCOL_NOT_FOUND;
00233 }
00234
00235 int ffurl_open(URLContext **puc, const char *filename, int flags,
00236 const AVIOInterruptCB *int_cb, AVDictionary **options)
00237 {
00238 int ret = ffurl_alloc(puc, filename, flags, int_cb);
00239 if (ret)
00240 return ret;
00241 if (options && (*puc)->prot->priv_data_class &&
00242 (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
00243 goto fail;
00244 ret = ffurl_connect(*puc, options);
00245 if (!ret)
00246 return 0;
00247 fail:
00248 ffurl_close(*puc);
00249 *puc = NULL;
00250 return ret;
00251 }
00252
00253 static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min,
00254 int (*transfer_func)(URLContext *h, unsigned char *buf, int size))
00255 {
00256 int ret, len;
00257 int fast_retries = 5;
00258
00259 len = 0;
00260 while (len < size_min) {
00261 ret = transfer_func(h, buf+len, size-len);
00262 if (ret == AVERROR(EINTR))
00263 continue;
00264 if (h->flags & AVIO_FLAG_NONBLOCK)
00265 return ret;
00266 if (ret == AVERROR(EAGAIN)) {
00267 ret = 0;
00268 if (fast_retries)
00269 fast_retries--;
00270 else
00271 usleep(1000);
00272 } else if (ret < 1)
00273 return ret < 0 ? ret : len;
00274 if (ret)
00275 fast_retries = FFMAX(fast_retries, 2);
00276 len += ret;
00277 if (len < size && ff_check_interrupt(&h->interrupt_callback))
00278 return AVERROR_EXIT;
00279 }
00280 return len;
00281 }
00282
00283 int ffurl_read(URLContext *h, unsigned char *buf, int size)
00284 {
00285 if (!(h->flags & AVIO_FLAG_READ))
00286 return AVERROR(EIO);
00287 return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);
00288 }
00289
00290 int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
00291 {
00292 if (!(h->flags & AVIO_FLAG_READ))
00293 return AVERROR(EIO);
00294 return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read);
00295 }
00296
00297 int ffurl_write(URLContext *h, const unsigned char *buf, int size)
00298 {
00299 if (!(h->flags & AVIO_FLAG_WRITE))
00300 return AVERROR(EIO);
00301
00302 if (h->max_packet_size && size > h->max_packet_size)
00303 return AVERROR(EIO);
00304
00305 return retry_transfer_wrapper(h, buf, size, size, (void*)h->prot->url_write);
00306 }
00307
00308 int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
00309 {
00310 int64_t ret;
00311
00312 if (!h->prot->url_seek)
00313 return AVERROR(ENOSYS);
00314 ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
00315 return ret;
00316 }
00317
00318 int ffurl_close(URLContext *h)
00319 {
00320 int ret = 0;
00321 if (!h) return 0;
00322
00323 if (h->is_connected && h->prot->url_close)
00324 ret = h->prot->url_close(h);
00325 #if CONFIG_NETWORK
00326 if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK)
00327 ff_network_close();
00328 #endif
00329 if (h->prot->priv_data_size) {
00330 if (h->prot->priv_data_class)
00331 av_opt_free(h->priv_data);
00332 av_free(h->priv_data);
00333 }
00334 av_free(h);
00335 return ret;
00336 }
00337
00338 int avio_check(const char *url, int flags)
00339 {
00340 URLContext *h;
00341 int ret = ffurl_alloc(&h, url, flags, NULL);
00342 if (ret)
00343 return ret;
00344
00345 if (h->prot->url_check) {
00346 ret = h->prot->url_check(h, flags);
00347 } else {
00348 ret = ffurl_connect(h, NULL);
00349 if (ret >= 0)
00350 ret = flags;
00351 }
00352
00353 ffurl_close(h);
00354 return ret;
00355 }
00356
00357 int64_t ffurl_size(URLContext *h)
00358 {
00359 int64_t pos, size;
00360
00361 size= ffurl_seek(h, 0, AVSEEK_SIZE);
00362 if(size<0){
00363 pos = ffurl_seek(h, 0, SEEK_CUR);
00364 if ((size = ffurl_seek(h, -1, SEEK_END)) < 0)
00365 return size;
00366 size++;
00367 ffurl_seek(h, pos, SEEK_SET);
00368 }
00369 return size;
00370 }
00371
00372 int ffurl_get_file_handle(URLContext *h)
00373 {
00374 if (!h->prot->url_get_file_handle)
00375 return -1;
00376 return h->prot->url_get_file_handle(h);
00377 }
00378
00379 int ffurl_shutdown(URLContext *h, int flags)
00380 {
00381 if (!h->prot->url_shutdown)
00382 return AVERROR(EINVAL);
00383 return h->prot->url_shutdown(h, flags);
00384 }
00385
00386 int ff_check_interrupt(AVIOInterruptCB *cb)
00387 {
00388 int ret;
00389 if (cb && cb->callback && (ret = cb->callback(cb->opaque)))
00390 return ret;
00391 return 0;
00392 }