]> git.sesse.net Git - ffmpeg/blob - libavformat/avio.c
Merge remote-tracking branch 'qatar/master'
[ffmpeg] / libavformat / avio.c
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
33 static URLProtocol *first_protocol = NULL;
34
35 URLProtocol *ffurl_protocol_next(URLProtocol *prev)
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}};
75 const AVClass ffurl_context_class = {
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
96 int ffurl_register_protocol(URLProtocol *protocol, int size)
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
119     if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init())
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     }
127     uc->av_class = &ffurl_context_class;
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) {
135         uc->priv_data = av_mallocz(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;
140             av_opt_set_defaults(uc->priv_data);
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
174     if (up->flags & URL_PROTOCOL_FLAG_NETWORK)
175         ff_network_close();
176 #endif
177     return err;
178 }
179
180 int ffurl_connect(URLContext* uc, AVDictionary **options)
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] != ':' &&  filename[proto_len] != ',' || is_dos_path(filename))
214         strcpy(proto_str, "file");
215     else
216         av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str)));
217
218     if ((ptr = strchr(proto_str, ',')))
219         *ptr = '\0';
220     av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
221     if ((ptr = strchr(proto_nested, '+')))
222         *ptr = '\0';
223
224     while (up = ffurl_protocol_next(up)) {
225         if (!strcmp(proto_str, up->name))
226             return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
227         if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
228             !strcmp(proto_nested, up->name))
229             return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
230     }
231     *puc = NULL;
232     return AVERROR_PROTOCOL_NOT_FOUND;
233 }
234
235 int ffurl_open(URLContext **puc, const char *filename, int flags,
236                const AVIOInterruptCB *int_cb, AVDictionary **options)
237 {
238     int ret = ffurl_alloc(puc, filename, flags, int_cb);
239     if (ret)
240         return ret;
241     if (options && (*puc)->prot->priv_data_class &&
242         (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
243         goto fail;
244     ret = ffurl_connect(*puc, options);
245     if (!ret)
246         return 0;
247 fail:
248     ffurl_close(*puc);
249     *puc = NULL;
250     return ret;
251 }
252
253 static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min,
254                                          int (*transfer_func)(URLContext *h, unsigned char *buf, int size))
255 {
256     int ret, len;
257     int fast_retries = 5;
258     int64_t wait_since = 0;
259
260     len = 0;
261     while (len < size_min) {
262         ret = transfer_func(h, buf+len, size-len);
263         if (ret == AVERROR(EINTR))
264             continue;
265         if (h->flags & AVIO_FLAG_NONBLOCK)
266             return ret;
267         if (ret == AVERROR(EAGAIN)) {
268             ret = 0;
269             if (fast_retries) {
270                 fast_retries--;
271             } else {
272                 if (h->rw_timeout) {
273                     if (!wait_since)
274                         wait_since = av_gettime();
275                     else if (av_gettime() > wait_since + h->rw_timeout)
276                         return AVERROR(EIO);
277                 }
278                 av_usleep(1000);
279             }
280         } else if (ret < 1)
281             return ret < 0 ? ret : len;
282         if (ret)
283            fast_retries = FFMAX(fast_retries, 2);
284         len += ret;
285         if (len < size && ff_check_interrupt(&h->interrupt_callback))
286             return AVERROR_EXIT;
287     }
288     return len;
289 }
290
291 int ffurl_read(URLContext *h, unsigned char *buf, int size)
292 {
293     if (!(h->flags & AVIO_FLAG_READ))
294         return AVERROR(EIO);
295     return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);
296 }
297
298 int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
299 {
300     if (!(h->flags & AVIO_FLAG_READ))
301         return AVERROR(EIO);
302     return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read);
303 }
304
305 int ffurl_write(URLContext *h, const unsigned char *buf, int size)
306 {
307     if (!(h->flags & AVIO_FLAG_WRITE))
308         return AVERROR(EIO);
309     /* avoid sending too big packets */
310     if (h->max_packet_size && size > h->max_packet_size)
311         return AVERROR(EIO);
312
313     return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (void*)h->prot->url_write);
314 }
315
316 int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
317 {
318     int64_t ret;
319
320     if (!h->prot->url_seek)
321         return AVERROR(ENOSYS);
322     ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
323     return ret;
324 }
325
326 int ffurl_closep(URLContext **hh)
327 {
328     URLContext *h= *hh;
329     int ret = 0;
330     if (!h) return 0; /* can happen when ffurl_open fails */
331
332     if (h->is_connected && h->prot->url_close)
333         ret = h->prot->url_close(h);
334 #if CONFIG_NETWORK
335     if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK)
336         ff_network_close();
337 #endif
338     if (h->prot->priv_data_size) {
339         if (h->prot->priv_data_class)
340             av_opt_free(h->priv_data);
341         av_freep(&h->priv_data);
342     }
343     av_freep(hh);
344     return ret;
345 }
346
347 int ffurl_close(URLContext *h)
348 {
349     return ffurl_closep(&h);
350 }
351
352
353 int avio_check(const char *url, int flags)
354 {
355     URLContext *h;
356     int ret = ffurl_alloc(&h, url, flags, NULL);
357     if (ret)
358         return ret;
359
360     if (h->prot->url_check) {
361         ret = h->prot->url_check(h, flags);
362     } else {
363         ret = ffurl_connect(h, NULL);
364         if (ret >= 0)
365             ret = flags;
366     }
367
368     ffurl_close(h);
369     return ret;
370 }
371
372 int64_t ffurl_size(URLContext *h)
373 {
374     int64_t pos, size;
375
376     size= ffurl_seek(h, 0, AVSEEK_SIZE);
377     if(size<0){
378         pos = ffurl_seek(h, 0, SEEK_CUR);
379         if ((size = ffurl_seek(h, -1, SEEK_END)) < 0)
380             return size;
381         size++;
382         ffurl_seek(h, pos, SEEK_SET);
383     }
384     return size;
385 }
386
387 int ffurl_get_file_handle(URLContext *h)
388 {
389     if (!h->prot->url_get_file_handle)
390         return -1;
391     return h->prot->url_get_file_handle(h);
392 }
393
394 int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles)
395 {
396     if (!h->prot->url_get_multi_file_handle) {
397         if (!h->prot->url_get_file_handle)
398             return AVERROR(ENOSYS);
399         *handles = av_malloc(sizeof(**handles));
400         if (!*handles)
401             return AVERROR(ENOMEM);
402         *numhandles = 1;
403         *handles[0] = h->prot->url_get_file_handle(h);
404         return 0;
405     }
406     return h->prot->url_get_multi_file_handle(h, handles, numhandles);
407 }
408
409 int ffurl_shutdown(URLContext *h, int flags)
410 {
411     if (!h->prot->url_shutdown)
412         return AVERROR(EINVAL);
413     return h->prot->url_shutdown(h, flags);
414 }
415
416 int ff_check_interrupt(AVIOInterruptCB *cb)
417 {
418     int ret;
419     if (cb && cb->callback && (ret = cb->callback(cb->opaque)))
420         return ret;
421     return 0;
422 }