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 "libavutil/avstring.h"
23 #include "libavutil/dict.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/time.h"
26 #include "os_support.h"
27 #include "avformat.h"
28 #if CONFIG_NETWORK
29 #include "network.h"
30 #endif
31 #include "url.h"
32 
34 
36 {
37  return prev ? prev->next : first_protocol;
38 }
39 
42 static const char *urlcontext_to_name(void *ptr)
43 {
44  URLContext *h = (URLContext *)ptr;
45  if(h->prot) return h->prot->name;
46  else return "NULL";
47 }
48 
49 static void *urlcontext_child_next(void *obj, void *prev)
50 {
51  URLContext *h = obj;
52  if (!prev && h->priv_data && h->prot->priv_data_class)
53  return h->priv_data;
54  return NULL;
55 }
56 
57 static const AVClass *urlcontext_child_class_next(const AVClass *prev)
58 {
59  URLProtocol *p = NULL;
60 
61  /* find the protocol that corresponds to prev */
62  while (prev && (p = ffurl_protocol_next(p)))
63  if (p->priv_data_class == prev)
64  break;
65 
66  /* find next protocol with priv options */
67  while (p = ffurl_protocol_next(p))
68  if (p->priv_data_class)
69  return p->priv_data_class;
70  return NULL;
71 
72 }
73 
74 static const AVOption options[] = {{NULL}};
76  .class_name = "URLContext",
77  .item_name = urlcontext_to_name,
78  .option = options,
79  .version = LIBAVUTIL_VERSION_INT,
80  .child_next = urlcontext_child_next,
81  .child_class_next = urlcontext_child_class_next,
82 };
86 const char *avio_enum_protocols(void **opaque, int output)
87 {
88  URLProtocol *p;
89  *opaque = ffurl_protocol_next(*opaque);
90  if (!(p = *opaque)) return NULL;
91  if ((output && p->url_write) || (!output && p->url_read))
92  return p->name;
93  return avio_enum_protocols(opaque, output);
94 }
95 
97 {
98  URLProtocol **p;
99  if (size < sizeof(URLProtocol)) {
100  URLProtocol* temp = av_mallocz(sizeof(URLProtocol));
101  memcpy(temp, protocol, size);
102  protocol = temp;
103  }
104  p = &first_protocol;
105  while (*p != NULL) p = &(*p)->next;
106  *p = protocol;
107  protocol->next = NULL;
108  return 0;
109 }
110 
111 static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up,
112  const char *filename, int flags,
113  const AVIOInterruptCB *int_cb)
114 {
115  URLContext *uc;
116  int err;
117 
118 #if CONFIG_NETWORK
120  return AVERROR(EIO);
121 #endif
122  uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);
123  if (!uc) {
124  err = AVERROR(ENOMEM);
125  goto fail;
126  }
128  uc->filename = (char *) &uc[1];
129  strcpy(uc->filename, filename);
130  uc->prot = up;
131  uc->flags = flags;
132  uc->is_streamed = 0; /* default = not streamed */
133  uc->max_packet_size = 0; /* default: stream file */
134  if (up->priv_data_size) {
136  if (up->priv_data_class) {
137  *(const AVClass**)uc->priv_data = up->priv_data_class;
139  }
140  }
141  if (int_cb)
142  uc->interrupt_callback = *int_cb;
143 
144  *puc = uc;
145  return 0;
146  fail:
147  *puc = NULL;
148 #if CONFIG_NETWORK
151 #endif
152  return err;
153 }
154 
156 {
157  int err =
158  uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) :
159  uc->prot->url_open(uc, uc->filename, uc->flags);
160  if (err)
161  return err;
162  uc->is_connected = 1;
163  //We must be careful here as ffurl_seek() could be slow, for example for http
164  if( (uc->flags & AVIO_FLAG_WRITE)
165  || !strcmp(uc->prot->name, "file"))
166  if(!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
167  uc->is_streamed= 1;
168  return 0;
169 }
170 
171 #define URL_SCHEME_CHARS \
172  "abcdefghijklmnopqrstuvwxyz" \
173  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
174  "0123456789+-."
175 
176 int ffurl_alloc(URLContext **puc, const char *filename, int flags,
177  const AVIOInterruptCB *int_cb)
178 {
179  URLProtocol *up = NULL;
180  char proto_str[128], proto_nested[128], *ptr;
181  size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
182 
183  if (filename[proto_len] != ':' || is_dos_path(filename))
184  strcpy(proto_str, "file");
185  else
186  av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str)));
187 
188  av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
189  if ((ptr = strchr(proto_nested, '+')))
190  *ptr = '\0';
191 
192  while (up = ffurl_protocol_next(up)) {
193  if (!strcmp(proto_str, up->name))
194  return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
196  !strcmp(proto_nested, up->name))
197  return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
198  }
199  *puc = NULL;
200  return AVERROR(ENOENT);
201 }
202 
203 int ffurl_open(URLContext **puc, const char *filename, int flags,
204  const AVIOInterruptCB *int_cb, AVDictionary **options)
205 {
206  int ret = ffurl_alloc(puc, filename, flags, int_cb);
207  if (ret)
208  return ret;
209  if (options && (*puc)->prot->priv_data_class &&
210  (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
211  goto fail;
212  ret = ffurl_connect(*puc, options);
213  if (!ret)
214  return 0;
215 fail:
216  ffurl_close(*puc);
217  *puc = NULL;
218  return ret;
219 }
220 
221 static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min,
222  int (*transfer_func)(URLContext *h, unsigned char *buf, int size))
223 {
224  int ret, len;
225  int fast_retries = 5;
226 
227  len = 0;
228  while (len < size_min) {
229  ret = transfer_func(h, buf+len, size-len);
230  if (ret == AVERROR(EINTR))
231  continue;
232  if (h->flags & AVIO_FLAG_NONBLOCK)
233  return ret;
234  if (ret == AVERROR(EAGAIN)) {
235  ret = 0;
236  if (fast_retries)
237  fast_retries--;
238  else
239  av_usleep(1000);
240  } else if (ret < 1)
241  return (ret < 0 && ret != AVERROR_EOF) ? ret : len;
242  if (ret)
243  fast_retries = FFMAX(fast_retries, 2);
244  len += ret;
246  return AVERROR_EXIT;
247  }
248  return len;
249 }
250 
251 int ffurl_read(URLContext *h, unsigned char *buf, int size)
252 {
253  if (!(h->flags & AVIO_FLAG_READ))
254  return AVERROR(EIO);
255  return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);
256 }
257 
258 int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
259 {
260  if (!(h->flags & AVIO_FLAG_READ))
261  return AVERROR(EIO);
262  return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read);
263 }
264 
265 int ffurl_write(URLContext *h, const unsigned char *buf, int size)
266 {
267  if (!(h->flags & AVIO_FLAG_WRITE))
268  return AVERROR(EIO);
269  /* avoid sending too big packets */
270  if (h->max_packet_size && size > h->max_packet_size)
271  return AVERROR(EIO);
272 
273  return retry_transfer_wrapper(h, buf, size, size, h->prot->url_write);
274 }
275 
276 int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
277 {
278  int64_t ret;
279 
280  if (!h->prot->url_seek)
281  return AVERROR(ENOSYS);
282  ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
283  return ret;
284 }
285 
287 {
288  int ret = 0;
289  if (!h) return 0; /* can happen when ffurl_open fails */
290 
291  if (h->is_connected && h->prot->url_close)
292  ret = h->prot->url_close(h);
293 #if CONFIG_NETWORK
296 #endif
297  if (h->prot->priv_data_size) {
298  if (h->prot->priv_data_class)
300  av_free(h->priv_data);
301  }
302  av_free(h);
303  return ret;
304 }
305 
306 int avio_check(const char *url, int flags)
307 {
308  URLContext *h;
309  int ret = ffurl_alloc(&h, url, flags, NULL);
310  if (ret)
311  return ret;
312 
313  if (h->prot->url_check) {
314  ret = h->prot->url_check(h, flags);
315  } else {
316  ret = ffurl_connect(h, NULL);
317  if (ret >= 0)
318  ret = flags;
319  }
320 
321  ffurl_close(h);
322  return ret;
323 }
324 
326 {
327  int64_t pos, size;
328 
329  size= ffurl_seek(h, 0, AVSEEK_SIZE);
330  if(size<0){
331  pos = ffurl_seek(h, 0, SEEK_CUR);
332  if ((size = ffurl_seek(h, -1, SEEK_END)) < 0)
333  return size;
334  size++;
335  ffurl_seek(h, pos, SEEK_SET);
336  }
337  return size;
338 }
339 
341 {
342  if (!h->prot->url_get_file_handle)
343  return -1;
344  return h->prot->url_get_file_handle(h);
345 }
346 
347 int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles)
348 {
349  if (!h->prot->url_get_multi_file_handle) {
350  if (!h->prot->url_get_file_handle)
351  return AVERROR(ENOSYS);
352  *handles = av_malloc(sizeof(*handles));
353  if (!*handles)
354  return AVERROR(ENOMEM);
355  *numhandles = 1;
356  *handles[0] = h->prot->url_get_file_handle(h);
357  return 0;
358  }
359  return h->prot->url_get_multi_file_handle(h, handles, numhandles);
360 }
361 
363 {
364  if (!h->prot->url_shutdown)
365  return AVERROR(EINVAL);
366  return h->prot->url_shutdown(h, flags);
367 }
368 
370 {
371  int ret;
372  if (cb && cb->callback && (ret = cb->callback(cb->opaque)))
373  return ret;
374  return 0;
375 }