]> git.sesse.net Git - ffmpeg/blob - libavformat/avio.c
Merge remote branch 'qatar/master'
[ffmpeg] / libavformat / avio.c
1 /*
2  * Unbuffered io for ffmpeg system
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 /* needed for usleep() */
23 #define _XOPEN_SOURCE 600
24 #include <unistd.h>
25 #include "libavutil/avstring.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
34 /** @name Logging context. */
35 /*@{*/
36 static const char *urlcontext_to_name(void *ptr)
37 {
38     URLContext *h = (URLContext *)ptr;
39     if(h->prot) return h->prot->name;
40     else        return "NULL";
41 }
42 static const AVOption options[] = {{NULL}};
43 static const AVClass urlcontext_class =
44         { "URLContext", urlcontext_to_name, options, LIBAVUTIL_VERSION_INT };
45 /*@}*/
46
47 static int default_interrupt_cb(void);
48
49 URLProtocol *first_protocol = NULL;
50 int (*url_interrupt_cb)(void) = default_interrupt_cb;
51
52 URLProtocol *av_protocol_next(URLProtocol *p)
53 {
54     if(p) return p->next;
55     else  return first_protocol;
56 }
57
58 const char *avio_enum_protocols(void **opaque, int output)
59 {
60     URLProtocol **p = opaque;
61     *p = *p ? (*p)->next : first_protocol;
62     if (!*p) return NULL;
63     if ((output && (*p)->url_write) || (!output && (*p)->url_read))
64         return (*p)->name;
65     return avio_enum_protocols(opaque, output);
66 }
67
68 int ffurl_register_protocol(URLProtocol *protocol, int size)
69 {
70     URLProtocol **p;
71     if (size < sizeof(URLProtocol)) {
72         URLProtocol* temp = av_mallocz(sizeof(URLProtocol));
73         memcpy(temp, protocol, size);
74         protocol = temp;
75     }
76     p = &first_protocol;
77     while (*p != NULL) p = &(*p)->next;
78     *p = protocol;
79     protocol->next = NULL;
80     return 0;
81 }
82
83 static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up,
84                                    const char *filename, int flags)
85 {
86     URLContext *uc;
87     int err;
88
89 #if CONFIG_NETWORK
90     if (!ff_network_init())
91         return AVERROR(EIO);
92 #endif
93     uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);
94     if (!uc) {
95         err = AVERROR(ENOMEM);
96         goto fail;
97     }
98     uc->av_class = &urlcontext_class;
99     uc->filename = (char *) &uc[1];
100     strcpy(uc->filename, filename);
101     uc->prot = up;
102     uc->flags = flags;
103     uc->is_streamed = 0; /* default = not streamed */
104     uc->max_packet_size = 0; /* default: stream file */
105     if (up->priv_data_size) {
106         uc->priv_data = av_mallocz(up->priv_data_size);
107         if (up->priv_data_class) {
108             *(const AVClass**)uc->priv_data = up->priv_data_class;
109             av_opt_set_defaults(uc->priv_data);
110         }
111     }
112
113     *puc = uc;
114     return 0;
115  fail:
116     *puc = NULL;
117 #if CONFIG_NETWORK
118     ff_network_close();
119 #endif
120     return err;
121 }
122
123 int ffurl_connect(URLContext* uc)
124 {
125     int err = uc->prot->url_open(uc, uc->filename, uc->flags);
126     if (err)
127         return err;
128     uc->is_connected = 1;
129     //We must be careful here as ffurl_seek() could be slow, for example for http
130     if(   (uc->flags & AVIO_FLAG_WRITE)
131        || !strcmp(uc->prot->name, "file"))
132         if(!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
133             uc->is_streamed= 1;
134     return 0;
135 }
136
137 #if FF_API_OLD_AVIO
138 int url_open_protocol (URLContext **puc, struct URLProtocol *up,
139                        const char *filename, int flags)
140 {
141     int ret;
142
143     ret = url_alloc_for_protocol(puc, up, filename, flags);
144     if (ret)
145         goto fail;
146     ret = ffurl_connect(*puc);
147     if (!ret)
148         return 0;
149  fail:
150     ffurl_close(*puc);
151     *puc = NULL;
152     return ret;
153 }
154 int url_alloc(URLContext **puc, const char *filename, int flags)
155 {
156     return ffurl_alloc(puc, filename, flags);
157 }
158 int url_connect(URLContext* uc)
159 {
160     return ffurl_connect(uc);
161 }
162 int url_open(URLContext **puc, const char *filename, int flags)
163 {
164     return ffurl_open(puc, filename, flags);
165 }
166 int url_read(URLContext *h, unsigned char *buf, int size)
167 {
168     return ffurl_read(h, buf, size);
169 }
170 int url_read_complete(URLContext *h, unsigned char *buf, int size)
171 {
172     return ffurl_read_complete(h, buf, size);
173 }
174 int url_write(URLContext *h, const unsigned char *buf, int size)
175 {
176     return ffurl_write(h, buf, size);
177 }
178 int64_t url_seek(URLContext *h, int64_t pos, int whence)
179 {
180     return ffurl_seek(h, pos, whence);
181 }
182 int url_close(URLContext *h)
183 {
184     return ffurl_close(h);
185 }
186 int64_t url_filesize(URLContext *h)
187 {
188     return ffurl_size(h);
189 }
190 int url_get_file_handle(URLContext *h)
191 {
192     return ffurl_get_file_handle(h);
193 }
194 int url_get_max_packet_size(URLContext *h)
195 {
196     return h->max_packet_size;
197 }
198 void url_get_filename(URLContext *h, char *buf, int buf_size)
199 {
200     av_strlcpy(buf, h->filename, buf_size);
201 }
202 void url_set_interrupt_cb(URLInterruptCB *interrupt_cb)
203 {
204     avio_set_interrupt_cb(interrupt_cb);
205 }
206 int av_register_protocol2(URLProtocol *protocol, int size)
207 {
208     return ffurl_register_protocol(protocol, size);
209 }
210 #endif
211
212 #define URL_SCHEME_CHARS                        \
213     "abcdefghijklmnopqrstuvwxyz"                \
214     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"                \
215     "0123456789+-."
216
217 int ffurl_alloc(URLContext **puc, const char *filename, int flags)
218 {
219     URLProtocol *up;
220     char proto_str[128], proto_nested[128], *ptr;
221     size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
222
223     if (filename[proto_len] != ':' || is_dos_path(filename))
224         strcpy(proto_str, "file");
225     else
226         av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str)));
227
228     av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
229     if ((ptr = strchr(proto_nested, '+')))
230         *ptr = '\0';
231
232     up = first_protocol;
233     while (up != NULL) {
234         if (!strcmp(proto_str, up->name))
235             return url_alloc_for_protocol (puc, up, filename, flags);
236         if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
237             !strcmp(proto_nested, up->name))
238             return url_alloc_for_protocol (puc, up, filename, flags);
239         up = up->next;
240     }
241     *puc = NULL;
242     return AVERROR(ENOENT);
243 }
244
245 int ffurl_open(URLContext **puc, const char *filename, int flags)
246 {
247     int ret = ffurl_alloc(puc, filename, flags);
248     if (ret)
249         return ret;
250     ret = ffurl_connect(*puc);
251     if (!ret)
252         return 0;
253     ffurl_close(*puc);
254     *puc = NULL;
255     return ret;
256 }
257
258 static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min,
259                                          int (*transfer_func)(URLContext *h, unsigned char *buf, int size))
260 {
261     int ret, len;
262     int fast_retries = 5;
263
264     len = 0;
265     while (len < size_min) {
266         ret = transfer_func(h, buf+len, size-len);
267         if (ret == AVERROR(EINTR))
268             continue;
269         if (h->flags & AVIO_FLAG_NONBLOCK)
270             return ret;
271         if (ret == AVERROR(EAGAIN)) {
272             ret = 0;
273             if (fast_retries)
274                 fast_retries--;
275             else
276                 usleep(1000);
277         } else if (ret < 1)
278             return ret < 0 ? ret : len;
279         if (ret)
280            fast_retries = FFMAX(fast_retries, 2);
281         len += ret;
282         if (len < size && url_interrupt_cb())
283             return AVERROR_EXIT;
284     }
285     return len;
286 }
287
288 int ffurl_read(URLContext *h, unsigned char *buf, int size)
289 {
290     if (!(h->flags & AVIO_FLAG_READ))
291         return AVERROR(EIO);
292     return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);
293 }
294
295 int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
296 {
297     if (!(h->flags & AVIO_FLAG_READ))
298         return AVERROR(EIO);
299     return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read);
300 }
301
302 int ffurl_write(URLContext *h, const unsigned char *buf, int size)
303 {
304     if (!(h->flags & AVIO_FLAG_WRITE))
305         return AVERROR(EIO);
306     /* avoid sending too big packets */
307     if (h->max_packet_size && size > h->max_packet_size)
308         return AVERROR(EIO);
309
310     return retry_transfer_wrapper(h, buf, size, size, (void*)h->prot->url_write);
311 }
312
313 int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
314 {
315     int64_t ret;
316
317     if (!h->prot->url_seek)
318         return AVERROR(ENOSYS);
319     ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
320     return ret;
321 }
322
323 int ffurl_close(URLContext *h)
324 {
325     int ret = 0;
326     if (!h) return 0; /* can happen when ffurl_open fails */
327
328     if (h->is_connected && h->prot->url_close)
329         ret = h->prot->url_close(h);
330 #if CONFIG_NETWORK
331     ff_network_close();
332 #endif
333     if (h->prot->priv_data_size)
334         av_free(h->priv_data);
335     av_free(h);
336     return ret;
337 }
338
339 #if FF_API_OLD_AVIO
340 int url_exist(const char *filename)
341 {
342     URLContext *h;
343     if (ffurl_open(&h, filename, AVIO_FLAG_READ) < 0)
344         return 0;
345     ffurl_close(h);
346     return 1;
347 }
348 #endif
349
350 int avio_check(const char *url, int flags)
351 {
352     URLContext *h;
353     int ret = ffurl_alloc(&h, url, flags);
354     if (ret)
355         return ret;
356
357     if (h->prot->url_check) {
358         ret = h->prot->url_check(h, flags);
359     } else {
360         ret = ffurl_connect(h);
361         if (ret >= 0)
362             ret = flags;
363     }
364
365     ffurl_close(h);
366     return ret;
367 }
368
369 int64_t ffurl_size(URLContext *h)
370 {
371     int64_t pos, size;
372
373     size= ffurl_seek(h, 0, AVSEEK_SIZE);
374     if(size<0){
375         pos = ffurl_seek(h, 0, SEEK_CUR);
376         if ((size = ffurl_seek(h, -1, SEEK_END)) < 0)
377             return size;
378         size++;
379         ffurl_seek(h, pos, SEEK_SET);
380     }
381     return size;
382 }
383
384 int ffurl_get_file_handle(URLContext *h)
385 {
386     if (!h->prot->url_get_file_handle)
387         return -1;
388     return h->prot->url_get_file_handle(h);
389 }
390
391 static int default_interrupt_cb(void)
392 {
393     return 0;
394 }
395
396 void avio_set_interrupt_cb(int (*interrupt_cb)(void))
397 {
398     if (!interrupt_cb)
399         interrupt_cb = default_interrupt_cb;
400     url_interrupt_cb = interrupt_cb;
401 }
402
403 #if FF_API_OLD_AVIO
404 int av_url_read_pause(URLContext *h, int pause)
405 {
406     if (!h->prot->url_read_pause)
407         return AVERROR(ENOSYS);
408     return h->prot->url_read_pause(h, pause);
409 }
410
411 int64_t av_url_read_seek(URLContext *h,
412         int stream_index, int64_t timestamp, int flags)
413 {
414     if (!h->prot->url_read_seek)
415         return AVERROR(ENOSYS);
416     return h->prot->url_read_seek(h, stream_index, timestamp, flags);
417 }
418 #endif