1/* 2 * unbuffered I/O 3 * Copyright (c) 2001 Fabrice Bellard 4 * 5 * This file is part of Libav. 6 * 7 * Libav is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * Libav is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with Libav; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include <unistd.h> 23 24#include "libavutil/avstring.h" 25#include "libavutil/dict.h" 26#include "libavutil/opt.h" 27#include "os_support.h" 28#include "avformat.h" 29#if CONFIG_NETWORK 30#include "network.h" 31#endif 32#include "url.h" 33 34static URLProtocol *first_protocol = NULL; 35 36URLProtocol *ffurl_protocol_next(URLProtocol *prev) 37{ 38 return prev ? prev->next : first_protocol; 39} 40 41/** @name Logging context. */ 42/*@{*/ 43static const char *urlcontext_to_name(void *ptr) 44{ 45 URLContext *h = (URLContext *)ptr; 46 if(h->prot) return h->prot->name; 47 else return "NULL"; 48} 49 50static void *urlcontext_child_next(void *obj, void *prev) 51{ 52 URLContext *h = obj; 53 if (!prev && h->priv_data && h->prot->priv_data_class) 54 return h->priv_data; 55 return NULL; 56} 57 58static const AVClass *urlcontext_child_class_next(const AVClass *prev) 59{ 60 URLProtocol *p = NULL; 61 62 /* find the protocol that corresponds to prev */ 63 while (prev && (p = ffurl_protocol_next(p))) 64 if (p->priv_data_class == prev) 65 break; 66 67 /* find next protocol with priv options */ 68 while (p = ffurl_protocol_next(p)) 69 if (p->priv_data_class) 70 return p->priv_data_class; 71 return NULL; 72 73} 74 75static const AVOption options[] = {{NULL}}; 76const AVClass ffurl_context_class = { 77 .class_name = "URLContext", 78 .item_name = urlcontext_to_name, 79 .option = options, 80 .version = LIBAVUTIL_VERSION_INT, 81 .child_next = urlcontext_child_next, 82 .child_class_next = urlcontext_child_class_next, 83}; 84/*@}*/ 85 86 87#if FF_API_OLD_INTERRUPT_CB 88static int default_interrupt_cb(void); 89int (*url_interrupt_cb)(void) = default_interrupt_cb; 90#endif 91 92#if FF_API_OLD_AVIO 93URLProtocol *av_protocol_next(URLProtocol *p) 94{ 95 return ffurl_protocol_next(p); 96} 97#endif 98 99const char *avio_enum_protocols(void **opaque, int output) 100{ 101 URLProtocol **p = opaque; 102 *p = ffurl_protocol_next(*p); 103 if (!*p) return NULL; 104 if ((output && (*p)->url_write) || (!output && (*p)->url_read)) 105 return (*p)->name; 106 return avio_enum_protocols(opaque, output); 107} 108 109int ffurl_register_protocol(URLProtocol *protocol, int size) 110{ 111 URLProtocol **p; 112 if (size < sizeof(URLProtocol)) { 113 URLProtocol* temp = av_mallocz(sizeof(URLProtocol)); 114 memcpy(temp, protocol, size); 115 protocol = temp; 116 } 117 p = &first_protocol; 118 while (*p != NULL) p = &(*p)->next; 119 *p = protocol; 120 protocol->next = NULL; 121 return 0; 122} 123 124static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up, 125 const char *filename, int flags, 126 const AVIOInterruptCB *int_cb) 127{ 128 URLContext *uc; 129 int err; 130 131#if CONFIG_NETWORK 132 if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init()) 133 return AVERROR(EIO); 134#endif 135 uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1); 136 if (!uc) { 137 err = AVERROR(ENOMEM); 138 goto fail; 139 } 140 uc->av_class = &ffurl_context_class; 141 uc->filename = (char *) &uc[1]; 142 strcpy(uc->filename, filename); 143 uc->prot = up; 144 uc->flags = flags; 145 uc->is_streamed = 0; /* default = not streamed */ 146 uc->max_packet_size = 0; /* default: stream file */ 147 if (up->priv_data_size) { 148 uc->priv_data = av_mallocz(up->priv_data_size); 149 if (up->priv_data_class) { 150 *(const AVClass**)uc->priv_data = up->priv_data_class; 151 av_opt_set_defaults(uc->priv_data); 152 } 153 } 154 if (int_cb) 155 uc->interrupt_callback = *int_cb; 156 157 *puc = uc; 158 return 0; 159 fail: 160 *puc = NULL; 161#if CONFIG_NETWORK 162 if (up->flags & URL_PROTOCOL_FLAG_NETWORK) 163 ff_network_close(); 164#endif 165 return err; 166} 167 168int ffurl_connect(URLContext* uc, AVDictionary **options) 169{ 170 int err = 171#if !FF_API_OLD_AVIO 172 uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) : 173#endif 174 uc->prot->url_open(uc, uc->filename, uc->flags); 175 if (err) 176 return err; 177 uc->is_connected = 1; 178 //We must be careful here as ffurl_seek() could be slow, for example for http 179 if( (uc->flags & AVIO_FLAG_WRITE) 180 || !strcmp(uc->prot->name, "file")) 181 if(!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0) 182 uc->is_streamed= 1; 183 return 0; 184} 185 186#if FF_API_OLD_AVIO 187int url_open_protocol (URLContext **puc, struct URLProtocol *up, 188 const char *filename, int flags) 189{ 190 int ret; 191 192 ret = url_alloc_for_protocol(puc, up, filename, flags, NULL); 193 if (ret) 194 goto fail; 195 ret = ffurl_connect(*puc, NULL); 196 if (!ret) 197 return 0; 198 fail: 199 ffurl_close(*puc); 200 *puc = NULL; 201 return ret; 202} 203int url_alloc(URLContext **puc, const char *filename, int flags) 204{ 205 return ffurl_alloc(puc, filename, flags, NULL); 206} 207int url_connect(URLContext* uc) 208{ 209 return ffurl_connect(uc, NULL); 210} 211int url_open(URLContext **puc, const char *filename, int flags) 212{ 213 return ffurl_open(puc, filename, flags, NULL, NULL); 214} 215int url_read(URLContext *h, unsigned char *buf, int size) 216{ 217 return ffurl_read(h, buf, size); 218} 219int url_read_complete(URLContext *h, unsigned char *buf, int size) 220{ 221 return ffurl_read_complete(h, buf, size); 222} 223int url_write(URLContext *h, const unsigned char *buf, int size) 224{ 225 return ffurl_write(h, buf, size); 226} 227int64_t url_seek(URLContext *h, int64_t pos, int whence) 228{ 229 return ffurl_seek(h, pos, whence); 230} 231int url_close(URLContext *h) 232{ 233 return ffurl_close(h); 234} 235int64_t url_filesize(URLContext *h) 236{ 237 return ffurl_size(h); 238} 239int url_get_file_handle(URLContext *h) 240{ 241 return ffurl_get_file_handle(h); 242} 243int url_get_max_packet_size(URLContext *h) 244{ 245 return h->max_packet_size; 246} 247void url_get_filename(URLContext *h, char *buf, int buf_size) 248{ 249 av_strlcpy(buf, h->filename, buf_size); 250} 251void url_set_interrupt_cb(URLInterruptCB *interrupt_cb) 252{ 253 avio_set_interrupt_cb(interrupt_cb); 254} 255int av_register_protocol2(URLProtocol *protocol, int size) 256{ 257 return ffurl_register_protocol(protocol, size); 258} 259#endif 260 261#define URL_SCHEME_CHARS \ 262 "abcdefghijklmnopqrstuvwxyz" \ 263 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ 264 "0123456789+-." 265 266int ffurl_alloc(URLContext **puc, const char *filename, int flags, 267 const AVIOInterruptCB *int_cb) 268{ 269 URLProtocol *up = NULL; 270 char proto_str[128], proto_nested[128], *ptr; 271 size_t proto_len = strspn(filename, URL_SCHEME_CHARS); 272 273 if (filename[proto_len] != ':' || is_dos_path(filename)) 274 strcpy(proto_str, "file"); 275 else 276 av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str))); 277 278 av_strlcpy(proto_nested, proto_str, sizeof(proto_nested)); 279 if ((ptr = strchr(proto_nested, '+'))) 280 *ptr = '\0'; 281 282 while (up = ffurl_protocol_next(up)) { 283 if (!strcmp(proto_str, up->name)) 284 return url_alloc_for_protocol (puc, up, filename, flags, int_cb); 285 if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME && 286 !strcmp(proto_nested, up->name)) 287 return url_alloc_for_protocol (puc, up, filename, flags, int_cb); 288 } 289 *puc = NULL; 290 return AVERROR(ENOENT); 291} 292 293int ffurl_open(URLContext **puc, const char *filename, int flags, 294 const AVIOInterruptCB *int_cb, AVDictionary **options) 295{ 296 int ret = ffurl_alloc(puc, filename, flags, int_cb); 297 if (ret) 298 return ret; 299 if (options && (*puc)->prot->priv_data_class && 300 (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0) 301 goto fail; 302 ret = ffurl_connect(*puc, options); 303 if (!ret) 304 return 0; 305fail: 306 ffurl_close(*puc); 307 *puc = NULL; 308 return ret; 309} 310 311static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min, 312 int (*transfer_func)(URLContext *h, unsigned char *buf, int size)) 313{ 314 int ret, len; 315 int fast_retries = 5; 316 317 len = 0; 318 while (len < size_min) { 319 ret = transfer_func(h, buf+len, size-len); 320 if (ret == AVERROR(EINTR)) 321 continue; 322 if (h->flags & AVIO_FLAG_NONBLOCK) 323 return ret; 324 if (ret == AVERROR(EAGAIN)) { 325 ret = 0; 326 if (fast_retries) 327 fast_retries--; 328 else 329 usleep(1000); 330 } else if (ret < 1) 331 return ret < 0 ? ret : len; 332 if (ret) 333 fast_retries = FFMAX(fast_retries, 2); 334 len += ret; 335 if (ff_check_interrupt(&h->interrupt_callback)) 336 return AVERROR_EXIT; 337 } 338 return len; 339} 340 341int ffurl_read(URLContext *h, unsigned char *buf, int size) 342{ 343 if (!(h->flags & AVIO_FLAG_READ)) 344 return AVERROR(EIO); 345 return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read); 346} 347 348int ffurl_read_complete(URLContext *h, unsigned char *buf, int size) 349{ 350 if (!(h->flags & AVIO_FLAG_READ)) 351 return AVERROR(EIO); 352 return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read); 353} 354 355int ffurl_write(URLContext *h, const unsigned char *buf, int size) 356{ 357 if (!(h->flags & AVIO_FLAG_WRITE)) 358 return AVERROR(EIO); 359 /* avoid sending too big packets */ 360 if (h->max_packet_size && size > h->max_packet_size) 361 return AVERROR(EIO); 362 363 return retry_transfer_wrapper(h, buf, size, size, h->prot->url_write); 364} 365 366int64_t ffurl_seek(URLContext *h, int64_t pos, int whence) 367{ 368 int64_t ret; 369 370 if (!h->prot->url_seek) 371 return AVERROR(ENOSYS); 372 ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE); 373 return ret; 374} 375 376int ffurl_close(URLContext *h) 377{ 378 int ret = 0; 379 if (!h) return 0; /* can happen when ffurl_open fails */ 380 381 if (h->is_connected && h->prot->url_close) 382 ret = h->prot->url_close(h); 383#if CONFIG_NETWORK 384 if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK) 385 ff_network_close(); 386#endif 387 if (h->prot->priv_data_size) { 388 if (h->prot->priv_data_class) 389 av_opt_free(h->priv_data); 390 av_free(h->priv_data); 391 } 392 av_free(h); 393 return ret; 394} 395 396#if FF_API_OLD_AVIO 397int url_exist(const char *filename) 398{ 399 URLContext *h; 400 if (ffurl_open(&h, filename, AVIO_FLAG_READ, NULL, NULL) < 0) 401 return 0; 402 ffurl_close(h); 403 return 1; 404} 405#endif 406 407int avio_check(const char *url, int flags) 408{ 409 URLContext *h; 410 int ret = ffurl_alloc(&h, url, flags, NULL); 411 if (ret) 412 return ret; 413 414 if (h->prot->url_check) { 415 ret = h->prot->url_check(h, flags); 416 } else { 417 ret = ffurl_connect(h, NULL); 418 if (ret >= 0) 419 ret = flags; 420 } 421 422 ffurl_close(h); 423 return ret; 424} 425 426int64_t ffurl_size(URLContext *h) 427{ 428 int64_t pos, size; 429 430 size= ffurl_seek(h, 0, AVSEEK_SIZE); 431 if(size<0){ 432 pos = ffurl_seek(h, 0, SEEK_CUR); 433 if ((size = ffurl_seek(h, -1, SEEK_END)) < 0) 434 return size; 435 size++; 436 ffurl_seek(h, pos, SEEK_SET); 437 } 438 return size; 439} 440 441int ffurl_get_file_handle(URLContext *h) 442{ 443 if (!h->prot->url_get_file_handle) 444 return -1; 445 return h->prot->url_get_file_handle(h); 446} 447 448#if FF_API_OLD_INTERRUPT_CB 449static int default_interrupt_cb(void) 450{ 451 return 0; 452} 453 454void avio_set_interrupt_cb(int (*interrupt_cb)(void)) 455{ 456 if (!interrupt_cb) 457 interrupt_cb = default_interrupt_cb; 458 url_interrupt_cb = interrupt_cb; 459} 460#endif 461 462int ff_check_interrupt(AVIOInterruptCB *cb) 463{ 464 int ret; 465 if (cb && cb->callback && (ret = cb->callback(cb->opaque))) 466 return ret; 467#if FF_API_OLD_INTERRUPT_CB 468 return url_interrupt_cb(); 469#else 470 return 0; 471#endif 472} 473 474#if FF_API_OLD_AVIO 475int av_url_read_pause(URLContext *h, int pause) 476{ 477 if (!h->prot->url_read_pause) 478 return AVERROR(ENOSYS); 479 return h->prot->url_read_pause(h, pause); 480} 481 482int64_t av_url_read_seek(URLContext *h, 483 int stream_index, int64_t timestamp, int flags) 484{ 485 if (!h->prot->url_read_seek) 486 return AVERROR(ENOSYS); 487 return h->prot->url_read_seek(h, stream_index, timestamp, flags); 488} 489#endif 490