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 = (URLProtocol **)opaque;
00089 *p = ffurl_protocol_next(*p);
00090 if (!*p) 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 goto fail;
00160 }
00161 memmove(start, key+1, strlen(key));
00162 }
00163 }
00164 }
00165 if (int_cb)
00166 uc->interrupt_callback = *int_cb;
00167
00168 *puc = uc;
00169 return 0;
00170 fail:
00171 *puc = NULL;
00172 #if CONFIG_NETWORK
00173 if (up->flags & URL_PROTOCOL_FLAG_NETWORK)
00174 ff_network_close();
00175 #endif
00176 return err;
00177 }
00178
00179 int ffurl_connect(URLContext* uc, AVDictionary **options)
00180 {
00181 int err =
00182 uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) :
00183 uc->prot->url_open(uc, uc->filename, uc->flags);
00184 if (err)
00185 return err;
00186 uc->is_connected = 1;
00187
00188 if( (uc->flags & AVIO_FLAG_WRITE)
00189 || !strcmp(uc->prot->name, "file"))
00190 if(!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
00191 uc->is_streamed= 1;
00192 return 0;
00193 }
00194
00195 #define URL_SCHEME_CHARS \
00196 "abcdefghijklmnopqrstuvwxyz" \
00197 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
00198 "0123456789+-."
00199
00200 int ffurl_alloc(URLContext **puc, const char *filename, int flags,
00201 const AVIOInterruptCB *int_cb)
00202 {
00203 URLProtocol *up = NULL;
00204 char proto_str[128], proto_nested[128], *ptr;
00205 size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
00206
00207 if (!first_protocol) {
00208 av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. "
00209 "Missing call to av_register_all()?\n");
00210 }
00211
00212 if (filename[proto_len] != ':' && filename[proto_len] != ',' || is_dos_path(filename))
00213 strcpy(proto_str, "file");
00214 else
00215 av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str)));
00216
00217 if ((ptr = strchr(proto_str, ',')))
00218 *ptr = '\0';
00219 av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
00220 if ((ptr = strchr(proto_nested, '+')))
00221 *ptr = '\0';
00222
00223 while (up = ffurl_protocol_next(up)) {
00224 if (!strcmp(proto_str, up->name))
00225 return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
00226 if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
00227 !strcmp(proto_nested, up->name))
00228 return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
00229 }
00230 *puc = NULL;
00231 return AVERROR_PROTOCOL_NOT_FOUND;
00232 }
00233
00234 int ffurl_open(URLContext **puc, const char *filename, int flags,
00235 const AVIOInterruptCB *int_cb, AVDictionary **options)
00236 {
00237 int ret = ffurl_alloc(puc, filename, flags, int_cb);
00238 if (ret)
00239 return ret;
00240 if (options && (*puc)->prot->priv_data_class &&
00241 (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
00242 goto fail;
00243 ret = ffurl_connect(*puc, options);
00244 if (!ret)
00245 return 0;
00246 fail:
00247 ffurl_close(*puc);
00248 *puc = NULL;
00249 return ret;
00250 }
00251
00252 static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min,
00253 int (*transfer_func)(URLContext *h, unsigned char *buf, int size))
00254 {
00255 int ret, len;
00256 int fast_retries = 5;
00257 int64_t wait_since = 0;
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 if (h->rw_timeout) {
00272 if (!wait_since)
00273 wait_since = av_gettime();
00274 else if (av_gettime() > wait_since + h->rw_timeout)
00275 return AVERROR(EIO);
00276 }
00277 av_usleep(1000);
00278 }
00279 } else if (ret < 1)
00280 return ret < 0 ? ret : len;
00281 if (ret)
00282 fast_retries = FFMAX(fast_retries, 2);
00283 len += ret;
00284 if (len < size && ff_check_interrupt(&h->interrupt_callback))
00285 return AVERROR_EXIT;
00286 }
00287 return len;
00288 }
00289
00290 int ffurl_read(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, 1, h->prot->url_read);
00295 }
00296
00297 int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
00298 {
00299 if (!(h->flags & AVIO_FLAG_READ))
00300 return AVERROR(EIO);
00301 return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read);
00302 }
00303
00304 int ffurl_write(URLContext *h, const unsigned char *buf, int size)
00305 {
00306 if (!(h->flags & AVIO_FLAG_WRITE))
00307 return AVERROR(EIO);
00308
00309 if (h->max_packet_size && size > h->max_packet_size)
00310 return AVERROR(EIO);
00311
00312 return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (void*)h->prot->url_write);
00313 }
00314
00315 int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
00316 {
00317 int64_t ret;
00318
00319 if (!h->prot->url_seek)
00320 return AVERROR(ENOSYS);
00321 ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
00322 return ret;
00323 }
00324
00325 int ffurl_closep(URLContext **hh)
00326 {
00327 URLContext *h= *hh;
00328 int ret = 0;
00329 if (!h) return 0;
00330
00331 if (h->is_connected && h->prot->url_close)
00332 ret = h->prot->url_close(h);
00333 #if CONFIG_NETWORK
00334 if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK)
00335 ff_network_close();
00336 #endif
00337 if (h->prot->priv_data_size) {
00338 if (h->prot->priv_data_class)
00339 av_opt_free(h->priv_data);
00340 av_freep(&h->priv_data);
00341 }
00342 av_freep(hh);
00343 return ret;
00344 }
00345
00346 int ffurl_close(URLContext *h)
00347 {
00348 return ffurl_closep(&h);
00349 }
00350
00351
00352 int avio_check(const char *url, int flags)
00353 {
00354 URLContext *h;
00355 int ret = ffurl_alloc(&h, url, flags, NULL);
00356 if (ret)
00357 return ret;
00358
00359 if (h->prot->url_check) {
00360 ret = h->prot->url_check(h, flags);
00361 } else {
00362 ret = ffurl_connect(h, NULL);
00363 if (ret >= 0)
00364 ret = flags;
00365 }
00366
00367 ffurl_close(h);
00368 return ret;
00369 }
00370
00371 int64_t ffurl_size(URLContext *h)
00372 {
00373 int64_t pos, size;
00374
00375 size= ffurl_seek(h, 0, AVSEEK_SIZE);
00376 if(size<0){
00377 pos = ffurl_seek(h, 0, SEEK_CUR);
00378 if ((size = ffurl_seek(h, -1, SEEK_END)) < 0)
00379 return size;
00380 size++;
00381 ffurl_seek(h, pos, SEEK_SET);
00382 }
00383 return size;
00384 }
00385
00386 int ffurl_get_file_handle(URLContext *h)
00387 {
00388 if (!h->prot->url_get_file_handle)
00389 return -1;
00390 return h->prot->url_get_file_handle(h);
00391 }
00392
00393 int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles)
00394 {
00395 if (!h->prot->url_get_multi_file_handle) {
00396 if (!h->prot->url_get_file_handle)
00397 return AVERROR(ENOSYS);
00398 *handles = av_malloc(sizeof(*handles));
00399 if (!*handles)
00400 return AVERROR(ENOMEM);
00401 *numhandles = 1;
00402 *handles[0] = h->prot->url_get_file_handle(h);
00403 return 0;
00404 }
00405 return h->prot->url_get_multi_file_handle(h, handles, numhandles);
00406 }
00407
00408 int ffurl_shutdown(URLContext *h, int flags)
00409 {
00410 if (!h->prot->url_shutdown)
00411 return AVERROR(EINVAL);
00412 return h->prot->url_shutdown(h, flags);
00413 }
00414
00415 int ff_check_interrupt(AVIOInterruptCB *cb)
00416 {
00417 int ret;
00418 if (cb && cb->callback && (ret = cb->callback(cb->opaque)))
00419 return ret;
00420 return 0;
00421 }