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