00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "libavutil/avstring.h"
00023 #include "libavutil/dict.h"
00024 #include "libavutil/opt.h"
00025 #include "libavutil/time.h"
00026 #include "os_support.h"
00027 #include "avformat.h"
00028 #if CONFIG_NETWORK
00029 #include "network.h"
00030 #endif
00031 #include "url.h"
00032
00033 static URLProtocol *first_protocol = NULL;
00034
00035 URLProtocol *ffurl_protocol_next(URLProtocol *prev)
00036 {
00037 return prev ? prev->next : first_protocol;
00038 }
00039
00042 static const char *urlcontext_to_name(void *ptr)
00043 {
00044 URLContext *h = (URLContext *)ptr;
00045 if(h->prot) return h->prot->name;
00046 else return "NULL";
00047 }
00048
00049 static void *urlcontext_child_next(void *obj, void *prev)
00050 {
00051 URLContext *h = obj;
00052 if (!prev && h->priv_data && h->prot->priv_data_class)
00053 return h->priv_data;
00054 return NULL;
00055 }
00056
00057 static const AVClass *urlcontext_child_class_next(const AVClass *prev)
00058 {
00059 URLProtocol *p = NULL;
00060
00061
00062 while (prev && (p = ffurl_protocol_next(p)))
00063 if (p->priv_data_class == prev)
00064 break;
00065
00066
00067 while (p = ffurl_protocol_next(p))
00068 if (p->priv_data_class)
00069 return p->priv_data_class;
00070 return NULL;
00071
00072 }
00073
00074 static const AVOption options[] = {{NULL}};
00075 const AVClass ffurl_context_class = {
00076 .class_name = "URLContext",
00077 .item_name = urlcontext_to_name,
00078 .option = options,
00079 .version = LIBAVUTIL_VERSION_INT,
00080 .child_next = urlcontext_child_next,
00081 .child_class_next = urlcontext_child_class_next,
00082 };
00086 const char *avio_enum_protocols(void **opaque, int output)
00087 {
00088 URLProtocol *p;
00089 *opaque = ffurl_protocol_next(*opaque);
00090 if (!(p = *opaque)) return NULL;
00091 if ((output && p->url_write) || (!output && p->url_read))
00092 return p->name;
00093 return avio_enum_protocols(opaque, output);
00094 }
00095
00096 int ffurl_register_protocol(URLProtocol *protocol, int size)
00097 {
00098 URLProtocol **p;
00099 if (size < sizeof(URLProtocol)) {
00100 URLProtocol* temp = av_mallocz(sizeof(URLProtocol));
00101 memcpy(temp, protocol, size);
00102 protocol = temp;
00103 }
00104 p = &first_protocol;
00105 while (*p != NULL) p = &(*p)->next;
00106 *p = protocol;
00107 protocol->next = NULL;
00108 return 0;
00109 }
00110
00111 static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up,
00112 const char *filename, int flags,
00113 const AVIOInterruptCB *int_cb)
00114 {
00115 URLContext *uc;
00116 int err;
00117
00118 #if CONFIG_NETWORK
00119 if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init())
00120 return AVERROR(EIO);
00121 #endif
00122 uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);
00123 if (!uc) {
00124 err = AVERROR(ENOMEM);
00125 goto fail;
00126 }
00127 uc->av_class = &ffurl_context_class;
00128 uc->filename = (char *) &uc[1];
00129 strcpy(uc->filename, filename);
00130 uc->prot = up;
00131 uc->flags = flags;
00132 uc->is_streamed = 0;
00133 uc->max_packet_size = 0;
00134 if (up->priv_data_size) {
00135 uc->priv_data = av_mallocz(up->priv_data_size);
00136 if (up->priv_data_class) {
00137 int proto_len= strlen(up->name);
00138 char *start = strchr(uc->filename, ',');
00139 *(const AVClass**)uc->priv_data = up->priv_data_class;
00140 av_opt_set_defaults(uc->priv_data);
00141 if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){
00142 int ret= 0;
00143 char *p= start;
00144 char sep= *++p;
00145 char *key, *val;
00146 p++;
00147 while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){
00148 *val= *key= 0;
00149 ret= av_opt_set(uc->priv_data, p, key+1, 0);
00150 if (ret == AVERROR_OPTION_NOT_FOUND)
00151 av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p);
00152 *val= *key= sep;
00153 p= val+1;
00154 }
00155 if(ret<0 || p!=key){
00156 av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start);
00157 av_freep(&uc->priv_data);
00158 av_freep(&uc);
00159 err = AVERROR(EINVAL);
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 int64_t wait_since = 0;
00259
00260 len = 0;
00261 while (len < size_min) {
00262 ret = transfer_func(h, buf+len, size-len);
00263 if (ret == AVERROR(EINTR))
00264 continue;
00265 if (h->flags & AVIO_FLAG_NONBLOCK)
00266 return ret;
00267 if (ret == AVERROR(EAGAIN)) {
00268 ret = 0;
00269 if (fast_retries) {
00270 fast_retries--;
00271 } else {
00272 if (h->rw_timeout) {
00273 if (!wait_since)
00274 wait_since = av_gettime();
00275 else if (av_gettime() > wait_since + h->rw_timeout)
00276 return AVERROR(EIO);
00277 }
00278 av_usleep(1000);
00279 }
00280 } else if (ret < 1)
00281 return ret < 0 ? ret : len;
00282 if (ret)
00283 fast_retries = FFMAX(fast_retries, 2);
00284 len += ret;
00285 if (len < size && ff_check_interrupt(&h->interrupt_callback))
00286 return AVERROR_EXIT;
00287 }
00288 return len;
00289 }
00290
00291 int ffurl_read(URLContext *h, unsigned char *buf, int size)
00292 {
00293 if (!(h->flags & AVIO_FLAG_READ))
00294 return AVERROR(EIO);
00295 return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);
00296 }
00297
00298 int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
00299 {
00300 if (!(h->flags & AVIO_FLAG_READ))
00301 return AVERROR(EIO);
00302 return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read);
00303 }
00304
00305 int ffurl_write(URLContext *h, const unsigned char *buf, int size)
00306 {
00307 if (!(h->flags & AVIO_FLAG_WRITE))
00308 return AVERROR(EIO);
00309
00310 if (h->max_packet_size && size > h->max_packet_size)
00311 return AVERROR(EIO);
00312
00313 return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (void*)h->prot->url_write);
00314 }
00315
00316 int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
00317 {
00318 int64_t ret;
00319
00320 if (!h->prot->url_seek)
00321 return AVERROR(ENOSYS);
00322 ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
00323 return ret;
00324 }
00325
00326 int ffurl_closep(URLContext **hh)
00327 {
00328 URLContext *h= *hh;
00329 int ret = 0;
00330 if (!h) return 0;
00331
00332 if (h->is_connected && h->prot->url_close)
00333 ret = h->prot->url_close(h);
00334 #if CONFIG_NETWORK
00335 if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK)
00336 ff_network_close();
00337 #endif
00338 if (h->prot->priv_data_size) {
00339 if (h->prot->priv_data_class)
00340 av_opt_free(h->priv_data);
00341 av_freep(&h->priv_data);
00342 }
00343 av_freep(hh);
00344 return ret;
00345 }
00346
00347 int ffurl_close(URLContext *h)
00348 {
00349 return ffurl_closep(&h);
00350 }
00351
00352
00353 int avio_check(const char *url, int flags)
00354 {
00355 URLContext *h;
00356 int ret = ffurl_alloc(&h, url, flags, NULL);
00357 if (ret)
00358 return ret;
00359
00360 if (h->prot->url_check) {
00361 ret = h->prot->url_check(h, flags);
00362 } else {
00363 ret = ffurl_connect(h, NULL);
00364 if (ret >= 0)
00365 ret = flags;
00366 }
00367
00368 ffurl_close(h);
00369 return ret;
00370 }
00371
00372 int64_t ffurl_size(URLContext *h)
00373 {
00374 int64_t pos, size;
00375
00376 size= ffurl_seek(h, 0, AVSEEK_SIZE);
00377 if(size<0){
00378 pos = ffurl_seek(h, 0, SEEK_CUR);
00379 if ((size = ffurl_seek(h, -1, SEEK_END)) < 0)
00380 return size;
00381 size++;
00382 ffurl_seek(h, pos, SEEK_SET);
00383 }
00384 return size;
00385 }
00386
00387 int ffurl_get_file_handle(URLContext *h)
00388 {
00389 if (!h->prot->url_get_file_handle)
00390 return -1;
00391 return h->prot->url_get_file_handle(h);
00392 }
00393
00394 int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles)
00395 {
00396 if (!h->prot->url_get_multi_file_handle) {
00397 if (!h->prot->url_get_file_handle)
00398 return AVERROR(ENOSYS);
00399 *handles = av_malloc(sizeof(**handles));
00400 if (!*handles)
00401 return AVERROR(ENOMEM);
00402 *numhandles = 1;
00403 *handles[0] = h->prot->url_get_file_handle(h);
00404 return 0;
00405 }
00406 return h->prot->url_get_multi_file_handle(h, handles, numhandles);
00407 }
00408
00409 int ffurl_shutdown(URLContext *h, int flags)
00410 {
00411 if (!h->prot->url_shutdown)
00412 return AVERROR(EINVAL);
00413 return h->prot->url_shutdown(h, flags);
00414 }
00415
00416 int ff_check_interrupt(AVIOInterruptCB *cb)
00417 {
00418 int ret;
00419 if (cb && cb->callback && (ret = cb->callback(cb->opaque)))
00420 return ret;
00421 return 0;
00422 }