avio.c
Go to the documentation of this file.
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 
35 
37 {
38  return prev ? prev->next : first_protocol;
39 }
40 
43 static 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 
50 static 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 
58 static 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 
75 static const AVOption options[] = {{NULL}};
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 };
87 #if FF_API_OLD_INTERRUPT_CB
88 static int default_interrupt_cb(void);
89 int (*url_interrupt_cb)(void) = default_interrupt_cb;
90 #endif
91 
92 #if FF_API_OLD_AVIO
93 URLProtocol *av_protocol_next(URLProtocol *p)
94 {
95  return ffurl_protocol_next(p);
96 }
97 #endif
98 
99 const 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 
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 
124 static 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
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  }
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) {
149  if (up->priv_data_class) {
150  *(const AVClass**)uc->priv_data = up->priv_data_class;
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
164 #endif
165  return err;
166 }
167 
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
187 int 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 }
203 int url_alloc(URLContext **puc, const char *filename, int flags)
204 {
205  return ffurl_alloc(puc, filename, flags, NULL);
206 }
207 int url_connect(URLContext* uc)
208 {
209  return ffurl_connect(uc, NULL);
210 }
211 int url_open(URLContext **puc, const char *filename, int flags)
212 {
213  return ffurl_open(puc, filename, flags, NULL, NULL);
214 }
215 int url_read(URLContext *h, unsigned char *buf, int size)
216 {
217  return ffurl_read(h, buf, size);
218 }
219 int url_read_complete(URLContext *h, unsigned char *buf, int size)
220 {
221  return ffurl_read_complete(h, buf, size);
222 }
223 int url_write(URLContext *h, const unsigned char *buf, int size)
224 {
225  return ffurl_write(h, buf, size);
226 }
227 int64_t url_seek(URLContext *h, int64_t pos, int whence)
228 {
229  return ffurl_seek(h, pos, whence);
230 }
231 int url_close(URLContext *h)
232 {
233  return ffurl_close(h);
234 }
235 int64_t url_filesize(URLContext *h)
236 {
237  return ffurl_size(h);
238 }
239 int url_get_file_handle(URLContext *h)
240 {
241  return ffurl_get_file_handle(h);
242 }
243 int url_get_max_packet_size(URLContext *h)
244 {
245  return h->max_packet_size;
246 }
247 void url_get_filename(URLContext *h, char *buf, int buf_size)
248 {
249  av_strlcpy(buf, h->filename, buf_size);
250 }
251 void url_set_interrupt_cb(URLInterruptCB *interrupt_cb)
252 {
253  avio_set_interrupt_cb(interrupt_cb);
254 }
255 int 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 
266 int 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);
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 
293 int 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;
305 fail:
306  ffurl_close(*puc);
307  *puc = NULL;
308  return ret;
309 }
310 
311 static 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 != AVERROR_EOF) ? ret : len;
332  if (ret)
333  fast_retries = FFMAX(fast_retries, 2);
334  len += ret;
336  return AVERROR_EXIT;
337  }
338  return len;
339 }
340 
341 int 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 
348 int 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 
355 int 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 
366 int64_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 
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
386 #endif
387  if (h->prot->priv_data_size) {
388  if (h->prot->priv_data_class)
390  av_free(h->priv_data);
391  }
392  av_free(h);
393  return ret;
394 }
395 
396 #if FF_API_OLD_AVIO
397 int 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 
407 int 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 
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 
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
449 static int default_interrupt_cb(void)
450 {
451  return 0;
452 }
453 
454 void 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 
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
475 int 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 
482 int64_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