FFmpeg
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 FFmpeg.
6  *
7  * FFmpeg 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  * FFmpeg 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 FFmpeg; 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 
40 /** @name Logging context. */
41 /*@{*/
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 };
83 /*@}*/
84 
85 
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)) {
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  int proto_len= strlen(up->name);
138  char *start = strchr(uc->filename, ',');
139  *(const AVClass**)uc->priv_data = up->priv_data_class;
141  if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){
142  int ret= 0;
143  char *p= start;
144  char sep= *++p;
145  char *key, *val;
146  p++;
147  while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){
148  *val= *key= 0;
149  ret= av_opt_set(uc->priv_data, p, key+1, 0);
150  if (ret == AVERROR_OPTION_NOT_FOUND)
151  av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p);
152  *val= *key= sep;
153  p= val+1;
154  }
155  if(ret<0 || p!=key){
156  av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start);
157  av_freep(&uc->priv_data);
158  av_freep(&uc);
159  err = AVERROR(EINVAL);
160  goto fail;
161  }
162  memmove(start, key+1, strlen(key));
163  }
164  }
165  }
166  if (int_cb)
167  uc->interrupt_callback = *int_cb;
168 
169  *puc = uc;
170  return 0;
171  fail:
172  *puc = NULL;
173 #if CONFIG_NETWORK
176 #endif
177  return err;
178 }
179 
181 {
182  int err =
183  uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) :
184  uc->prot->url_open(uc, uc->filename, uc->flags);
185  if (err)
186  return err;
187  uc->is_connected = 1;
188  //We must be careful here as ffurl_seek() could be slow, for example for http
189  if( (uc->flags & AVIO_FLAG_WRITE)
190  || !strcmp(uc->prot->name, "file"))
191  if(!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
192  uc->is_streamed= 1;
193  return 0;
194 }
195 
196 #define URL_SCHEME_CHARS \
197  "abcdefghijklmnopqrstuvwxyz" \
198  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
199  "0123456789+-."
200 
201 int ffurl_alloc(URLContext **puc, const char *filename, int flags,
202  const AVIOInterruptCB *int_cb)
203 {
204  URLProtocol *up = NULL;
205  char proto_str[128], proto_nested[128], *ptr;
206  size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
207 
208  if (!first_protocol) {
209  av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. "
210  "Missing call to av_register_all()?\n");
211  }
212 
213  if (filename[proto_len] != ':' &&
214  (filename[proto_len] != ',' || !strchr(filename + proto_len + 1, ':')) ||
215  is_dos_path(filename))
216  strcpy(proto_str, "file");
217  else
218  av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str)));
219 
220  if ((ptr = strchr(proto_str, ',')))
221  *ptr = '\0';
222  av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
223  if ((ptr = strchr(proto_nested, '+')))
224  *ptr = '\0';
225 
226  while (up = ffurl_protocol_next(up)) {
227  if (!strcmp(proto_str, up->name))
228  return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
230  !strcmp(proto_nested, up->name))
231  return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
232  }
233  *puc = NULL;
235 }
236 
237 int ffurl_open(URLContext **puc, const char *filename, int flags,
238  const AVIOInterruptCB *int_cb, AVDictionary **options)
239 {
240  int ret = ffurl_alloc(puc, filename, flags, int_cb);
241  if (ret)
242  return ret;
243  if (options && (*puc)->prot->priv_data_class &&
244  (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
245  goto fail;
246  ret = ffurl_connect(*puc, options);
247  if (!ret)
248  return 0;
249 fail:
250  ffurl_close(*puc);
251  *puc = NULL;
252  return ret;
253 }
254 
255 static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min,
256  int (*transfer_func)(URLContext *h, unsigned char *buf, int size))
257 {
258  int ret, len;
259  int fast_retries = 5;
260  int64_t wait_since = 0;
261 
262  len = 0;
263  while (len < size_min) {
264  ret = transfer_func(h, buf+len, size-len);
265  if (ret == AVERROR(EINTR))
266  continue;
267  if (h->flags & AVIO_FLAG_NONBLOCK)
268  return ret;
269  if (ret == AVERROR(EAGAIN)) {
270  ret = 0;
271  if (fast_retries) {
272  fast_retries--;
273  } else {
274  if (h->rw_timeout) {
275  if (!wait_since)
276  wait_since = av_gettime();
277  else if (av_gettime() > wait_since + h->rw_timeout)
278  return AVERROR(EIO);
279  }
280  av_usleep(1000);
281  }
282  } else if (ret < 1)
283  return ret < 0 ? ret : len;
284  if (ret)
285  fast_retries = FFMAX(fast_retries, 2);
286  len += ret;
287  if (len < size && ff_check_interrupt(&h->interrupt_callback))
288  return AVERROR_EXIT;
289  }
290  return len;
291 }
292 
293 int ffurl_read(URLContext *h, unsigned char *buf, int size)
294 {
295  if (!(h->flags & AVIO_FLAG_READ))
296  return AVERROR(EIO);
297  return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);
298 }
299 
300 int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
301 {
302  if (!(h->flags & AVIO_FLAG_READ))
303  return AVERROR(EIO);
304  return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read);
305 }
306 
307 int ffurl_write(URLContext *h, const unsigned char *buf, int size)
308 {
309  if (!(h->flags & AVIO_FLAG_WRITE))
310  return AVERROR(EIO);
311  /* avoid sending too big packets */
312  if (h->max_packet_size && size > h->max_packet_size)
313  return AVERROR(EIO);
314 
315  return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (void*)h->prot->url_write);
316 }
317 
318 int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
319 {
320  int64_t ret;
321 
322  if (!h->prot->url_seek)
323  return AVERROR(ENOSYS);
324  ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
325  return ret;
326 }
327 
329 {
330  URLContext *h= *hh;
331  int ret = 0;
332  if (!h) return 0; /* can happen when ffurl_open fails */
333 
334  if (h->is_connected && h->prot->url_close)
335  ret = h->prot->url_close(h);
336 #if CONFIG_NETWORK
339 #endif
340  if (h->prot->priv_data_size) {
341  if (h->prot->priv_data_class)
343  av_freep(&h->priv_data);
344  }
345  av_freep(hh);
346  return ret;
347 }
348 
350 {
351  return ffurl_closep(&h);
352 }
353 
354 
355 int avio_check(const char *url, int flags)
356 {
357  URLContext *h;
358  int ret = ffurl_alloc(&h, url, flags, NULL);
359  if (ret)
360  return ret;
361 
362  if (h->prot->url_check) {
363  ret = h->prot->url_check(h, flags);
364  } else {
365  ret = ffurl_connect(h, NULL);
366  if (ret >= 0)
367  ret = flags;
368  }
369 
370  ffurl_close(h);
371  return ret;
372 }
373 
375 {
376  int64_t pos, size;
377 
378  size= ffurl_seek(h, 0, AVSEEK_SIZE);
379  if(size<0){
380  pos = ffurl_seek(h, 0, SEEK_CUR);
381  if ((size = ffurl_seek(h, -1, SEEK_END)) < 0)
382  return size;
383  size++;
384  ffurl_seek(h, pos, SEEK_SET);
385  }
386  return size;
387 }
388 
390 {
391  if (!h->prot->url_get_file_handle)
392  return -1;
393  return h->prot->url_get_file_handle(h);
394 }
395 
396 int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles)
397 {
398  if (!h->prot->url_get_multi_file_handle) {
399  if (!h->prot->url_get_file_handle)
400  return AVERROR(ENOSYS);
401  *handles = av_malloc(sizeof(**handles));
402  if (!*handles)
403  return AVERROR(ENOMEM);
404  *numhandles = 1;
405  *handles[0] = h->prot->url_get_file_handle(h);
406  return 0;
407  }
408  return h->prot->url_get_multi_file_handle(h, handles, numhandles);
409 }
410 
412 {
413  if (!h->prot->url_shutdown)
414  return AVERROR(EINVAL);
415  return h->prot->url_shutdown(h, flags);
416 }
417 
419 {
420  int ret;
421  if (cb && cb->callback && (ret = cb->callback(cb->opaque)))
422  return ret;
423  return 0;
424 }