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/opt.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 #if FF_API_URL_CLASS
00034
00036 static const char *urlcontext_to_name(void *ptr)
00037 {
00038 URLContext *h = (URLContext *)ptr;
00039 if(h->prot) return h->prot->name;
00040 else return "NULL";
00041 }
00042 static const AVOption options[] = {{NULL}};
00043 static const AVClass urlcontext_class = {
00044 .class_name = "URLContext",
00045 .item_name = urlcontext_to_name,
00046 .option = options,
00047 .version = LIBAVUTIL_VERSION_INT,
00048 };
00050 #endif
00051
00052 static int default_interrupt_cb(void);
00053
00054 URLProtocol *first_protocol = NULL;
00055 int (*url_interrupt_cb)(void) = default_interrupt_cb;
00056
00057 URLProtocol *av_protocol_next(URLProtocol *p)
00058 {
00059 if(p) return p->next;
00060 else return first_protocol;
00061 }
00062
00063 const char *avio_enum_protocols(void **opaque, int output)
00064 {
00065 URLProtocol *p = *opaque;
00066 p = p ? p->next : first_protocol;
00067 if (!p) return NULL;
00068 if ((output && p->url_write) || (!output && p->url_read))
00069 return p->name;
00070 return avio_enum_protocols(opaque, output);
00071 }
00072
00073 int ffurl_register_protocol(URLProtocol *protocol, int size)
00074 {
00075 URLProtocol **p;
00076 if (size < sizeof(URLProtocol)) {
00077 URLProtocol* temp = av_mallocz(sizeof(URLProtocol));
00078 memcpy(temp, protocol, size);
00079 protocol = temp;
00080 }
00081 p = &first_protocol;
00082 while (*p != NULL) p = &(*p)->next;
00083 *p = protocol;
00084 protocol->next = NULL;
00085 return 0;
00086 }
00087
00088 #if FF_API_REGISTER_PROTOCOL
00089
00090 struct URLProtocol_compat {
00091 const char *name;
00092 int (*url_open)(URLContext *h, const char *filename, int flags);
00093 int (*url_read)(URLContext *h, unsigned char *buf, int size);
00094 int (*url_write)(URLContext *h, unsigned char *buf, int size);
00095 int64_t (*url_seek)(URLContext *h, int64_t pos, int whence);
00096 int (*url_close)(URLContext *h);
00097 struct URLProtocol *next;
00098 };
00099
00100 int av_register_protocol(URLProtocol *protocol)
00101 {
00102 return ffurl_register_protocol(protocol, sizeof(struct URLProtocol_compat));
00103 }
00104
00105 int register_protocol(URLProtocol *protocol)
00106 {
00107 return ffurl_register_protocol(protocol, sizeof(struct URLProtocol_compat));
00108 }
00109 #endif
00110
00111 static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up,
00112 const char *filename, int flags)
00113 {
00114 URLContext *uc;
00115 int err;
00116
00117 #if CONFIG_NETWORK
00118 if (!ff_network_init())
00119 return AVERROR(EIO);
00120 #endif
00121 uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);
00122 if (!uc) {
00123 err = AVERROR(ENOMEM);
00124 goto fail;
00125 }
00126 #if FF_API_URL_CLASS
00127 uc->av_class = &urlcontext_class;
00128 #endif
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 *(const AVClass**)uc->priv_data = up->priv_data_class;
00139 av_opt_set_defaults(uc->priv_data);
00140 }
00141 }
00142
00143 *puc = uc;
00144 return 0;
00145 fail:
00146 *puc = NULL;
00147 #if CONFIG_NETWORK
00148 ff_network_close();
00149 #endif
00150 return err;
00151 }
00152
00153 int ffurl_connect(URLContext* uc)
00154 {
00155 int err = uc->prot->url_open(uc, uc->filename, uc->flags);
00156 if (err)
00157 return err;
00158 uc->is_connected = 1;
00159
00160 if( (uc->flags & (AVIO_WRONLY | AVIO_RDWR))
00161 || !strcmp(uc->prot->name, "file"))
00162 if(!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
00163 uc->is_streamed= 1;
00164 return 0;
00165 }
00166
00167 #if FF_API_OLD_AVIO
00168 int url_open_protocol (URLContext **puc, struct URLProtocol *up,
00169 const char *filename, int flags)
00170 {
00171 int ret;
00172
00173 ret = url_alloc_for_protocol(puc, up, filename, flags);
00174 if (ret)
00175 goto fail;
00176 ret = ffurl_connect(*puc);
00177 if (!ret)
00178 return 0;
00179 fail:
00180 ffurl_close(*puc);
00181 *puc = NULL;
00182 return ret;
00183 }
00184 int url_alloc(URLContext **puc, const char *filename, int flags)
00185 {
00186 return ffurl_alloc(puc, filename, flags);
00187 }
00188 int url_connect(URLContext* uc)
00189 {
00190 return ffurl_connect(uc);
00191 }
00192 int url_open(URLContext **puc, const char *filename, int flags)
00193 {
00194 return ffurl_open(puc, filename, flags);
00195 }
00196 int url_read(URLContext *h, unsigned char *buf, int size)
00197 {
00198 return ffurl_read(h, buf, size);
00199 }
00200 int url_read_complete(URLContext *h, unsigned char *buf, int size)
00201 {
00202 return ffurl_read_complete(h, buf, size);
00203 }
00204 int url_write(URLContext *h, const unsigned char *buf, int size)
00205 {
00206 return ffurl_write(h, buf, size);
00207 }
00208 int64_t url_seek(URLContext *h, int64_t pos, int whence)
00209 {
00210 return ffurl_seek(h, pos, whence);
00211 }
00212 int url_close(URLContext *h)
00213 {
00214 return ffurl_close(h);
00215 }
00216 int64_t url_filesize(URLContext *h)
00217 {
00218 return ffurl_size(h);
00219 }
00220 int url_get_file_handle(URLContext *h)
00221 {
00222 return ffurl_get_file_handle(h);
00223 }
00224 int url_get_max_packet_size(URLContext *h)
00225 {
00226 return h->max_packet_size;
00227 }
00228 void url_get_filename(URLContext *h, char *buf, int buf_size)
00229 {
00230 av_strlcpy(buf, h->filename, buf_size);
00231 }
00232 void url_set_interrupt_cb(URLInterruptCB *interrupt_cb)
00233 {
00234 avio_set_interrupt_cb(interrupt_cb);
00235 }
00236 int av_register_protocol2(URLProtocol *protocol, int size)
00237 {
00238 return ffurl_register_protocol(protocol, size);
00239 }
00240 #endif
00241
00242 #define URL_SCHEME_CHARS \
00243 "abcdefghijklmnopqrstuvwxyz" \
00244 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
00245 "0123456789+-."
00246
00247 int ffurl_alloc(URLContext **puc, const char *filename, int flags)
00248 {
00249 URLProtocol *up;
00250 char proto_str[128], proto_nested[128], *ptr;
00251 size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
00252
00253 if (filename[proto_len] != ':' || is_dos_path(filename))
00254 strcpy(proto_str, "file");
00255 else
00256 av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str)));
00257
00258 av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
00259 if ((ptr = strchr(proto_nested, '+')))
00260 *ptr = '\0';
00261
00262 up = first_protocol;
00263 while (up != NULL) {
00264 if (!strcmp(proto_str, up->name))
00265 return url_alloc_for_protocol (puc, up, filename, flags);
00266 if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
00267 !strcmp(proto_nested, up->name))
00268 return url_alloc_for_protocol (puc, up, filename, flags);
00269 up = up->next;
00270 }
00271 *puc = NULL;
00272 return AVERROR(ENOENT);
00273 }
00274
00275 int ffurl_open(URLContext **puc, const char *filename, int flags)
00276 {
00277 int ret = ffurl_alloc(puc, filename, flags);
00278 if (ret)
00279 return ret;
00280 ret = ffurl_connect(*puc);
00281 if (!ret)
00282 return 0;
00283 ffurl_close(*puc);
00284 *puc = NULL;
00285 return ret;
00286 }
00287
00288 static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min,
00289 int (*transfer_func)(URLContext *h, unsigned char *buf, int size))
00290 {
00291 int ret, len;
00292 int fast_retries = 5;
00293
00294 len = 0;
00295 while (len < size_min) {
00296 ret = transfer_func(h, buf+len, size-len);
00297 if (ret == AVERROR(EINTR))
00298 continue;
00299 if (h->flags & AVIO_FLAG_NONBLOCK)
00300 return ret;
00301 if (ret == AVERROR(EAGAIN)) {
00302 ret = 0;
00303 if (fast_retries)
00304 fast_retries--;
00305 else
00306 usleep(1000);
00307 } else if (ret < 1)
00308 return ret < 0 ? ret : len;
00309 if (ret)
00310 fast_retries = FFMAX(fast_retries, 2);
00311 len += ret;
00312 if (len < size && url_interrupt_cb())
00313 return AVERROR_EXIT;
00314 }
00315 return len;
00316 }
00317
00318 int ffurl_read(URLContext *h, unsigned char *buf, int size)
00319 {
00320 if (h->flags & AVIO_WRONLY)
00321 return AVERROR(EIO);
00322 return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);
00323 }
00324
00325 int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
00326 {
00327 if (h->flags & AVIO_WRONLY)
00328 return AVERROR(EIO);
00329 return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read);
00330 }
00331
00332 int ffurl_write(URLContext *h, const unsigned char *buf, int size)
00333 {
00334 if (!(h->flags & (AVIO_WRONLY | AVIO_RDWR)))
00335 return AVERROR(EIO);
00336
00337 if (h->max_packet_size && size > h->max_packet_size)
00338 return AVERROR(EIO);
00339
00340 return retry_transfer_wrapper(h, buf, size, size, (void*)h->prot->url_write);
00341 }
00342
00343 int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
00344 {
00345 int64_t ret;
00346
00347 if (!h->prot->url_seek)
00348 return AVERROR(ENOSYS);
00349 ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
00350 return ret;
00351 }
00352
00353 int ffurl_close(URLContext *h)
00354 {
00355 int ret = 0;
00356 if (!h) return 0;
00357
00358 if (h->is_connected && h->prot->url_close)
00359 ret = h->prot->url_close(h);
00360 #if CONFIG_NETWORK
00361 ff_network_close();
00362 #endif
00363 if (h->prot->priv_data_size)
00364 av_free(h->priv_data);
00365 av_free(h);
00366 return ret;
00367 }
00368
00369 #if FF_API_OLD_AVIO
00370 int url_exist(const char *filename)
00371 {
00372 URLContext *h;
00373 if (ffurl_open(&h, filename, AVIO_RDONLY) < 0)
00374 return 0;
00375 ffurl_close(h);
00376 return 1;
00377 }
00378 #endif
00379
00380 int avio_check(const char *url, int flags)
00381 {
00382 URLContext *h;
00383 int ret = ffurl_alloc(&h, url, flags);
00384 if (ret)
00385 return ret;
00386
00387 if (h->prot->url_check) {
00388 ret = h->prot->url_check(h, flags);
00389 } else {
00390 ret = ffurl_connect(h);
00391 if (ret >= 0)
00392 ret = flags;
00393 }
00394
00395 ffurl_close(h);
00396 return ret;
00397 }
00398
00399 int64_t ffurl_size(URLContext *h)
00400 {
00401 int64_t pos, size;
00402
00403 size= ffurl_seek(h, 0, AVSEEK_SIZE);
00404 if(size<0){
00405 pos = ffurl_seek(h, 0, SEEK_CUR);
00406 if ((size = ffurl_seek(h, -1, SEEK_END)) < 0)
00407 return size;
00408 size++;
00409 ffurl_seek(h, pos, SEEK_SET);
00410 }
00411 return size;
00412 }
00413
00414 int ffurl_get_file_handle(URLContext *h)
00415 {
00416 if (!h->prot->url_get_file_handle)
00417 return -1;
00418 return h->prot->url_get_file_handle(h);
00419 }
00420
00421 static int default_interrupt_cb(void)
00422 {
00423 return 0;
00424 }
00425
00426 void avio_set_interrupt_cb(int (*interrupt_cb)(void))
00427 {
00428 if (!interrupt_cb)
00429 interrupt_cb = default_interrupt_cb;
00430 url_interrupt_cb = interrupt_cb;
00431 }
00432
00433 #if FF_API_OLD_AVIO
00434 int av_url_read_pause(URLContext *h, int pause)
00435 {
00436 if (!h->prot->url_read_pause)
00437 return AVERROR(ENOSYS);
00438 return h->prot->url_read_pause(h, pause);
00439 }
00440
00441 int64_t av_url_read_seek(URLContext *h,
00442 int stream_index, int64_t timestamp, int flags)
00443 {
00444 if (!h->prot->url_read_seek)
00445 return AVERROR(ENOSYS);
00446 return h->prot->url_read_seek(h, stream_index, timestamp, flags);
00447 }
00448 #endif