/src/ffmpeg/libavformat/avio.c
Line | Count | Source |
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 "config_components.h" |
23 | | |
24 | | #include <stdlib.h> |
25 | | |
26 | | #include "libavutil/avstring.h" |
27 | | #include "libavutil/dict.h" |
28 | | #include "libavutil/mem.h" |
29 | | #include "libavutil/opt.h" |
30 | | #include "libavutil/time.h" |
31 | | #include "libavutil/avassert.h" |
32 | | #include "avio_internal.h" |
33 | | #include "os_support.h" |
34 | | #include "internal.h" |
35 | | #if CONFIG_NETWORK |
36 | | #include "network.h" |
37 | | #endif |
38 | | #include "url.h" |
39 | | |
40 | 0 | #define IO_BUFFER_SIZE 32768 |
41 | 0 | #define NETWORK_IO_BUFFER_SIZE 262144 |
42 | | |
43 | | /** @name Logging context. */ |
44 | | /*@{*/ |
45 | | static const char *urlcontext_to_name(void *ptr) |
46 | 0 | { |
47 | 0 | URLContext *h = (URLContext *)ptr; |
48 | 0 | if (h->prot) |
49 | 0 | return h->prot->name; |
50 | 0 | else |
51 | 0 | return "NULL"; |
52 | 0 | } |
53 | | |
54 | | static void *urlcontext_child_next(void *obj, void *prev) |
55 | 0 | { |
56 | 0 | URLContext *h = obj; |
57 | 0 | if (!prev && h->priv_data && h->prot->priv_data_class) |
58 | 0 | return h->priv_data; |
59 | 0 | return NULL; |
60 | 0 | } |
61 | | |
62 | | #define OFFSET(x) offsetof(URLContext,x) |
63 | | #define E AV_OPT_FLAG_ENCODING_PARAM |
64 | | #define D AV_OPT_FLAG_DECODING_PARAM |
65 | | static const AVOption urlcontext_options[] = { |
66 | | {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, |
67 | | {"protocol_blacklist", "List of protocols that are not allowed to be used", OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, |
68 | | {"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 }, |
69 | | {"prefer_libcurl", "use the libcurl protocol for http(s) URLs when available", OFFSET(prefer_libcurl), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D }, |
70 | | { NULL } |
71 | | }; |
72 | | |
73 | | static const AVClass url_context_class = { |
74 | | .class_name = "URLContext", |
75 | | .item_name = urlcontext_to_name, |
76 | | .option = urlcontext_options, |
77 | | .version = LIBAVUTIL_VERSION_INT, |
78 | | .child_next = urlcontext_child_next, |
79 | | .child_class_iterate = ff_urlcontext_child_class_iterate, |
80 | | }; |
81 | | /*@}*/ |
82 | | |
83 | | static void *avio_child_next(void *obj, void *prev) |
84 | 0 | { |
85 | 0 | AVIOContext *s = obj; |
86 | 0 | return prev ? NULL : s->opaque; |
87 | 0 | } |
88 | | |
89 | | static const AVClass *child_class_iterate(void **iter) |
90 | 0 | { |
91 | 0 | const AVClass *c = *iter ? NULL : &url_context_class; |
92 | 0 | *iter = (void*)(uintptr_t)c; |
93 | 0 | return c; |
94 | 0 | } |
95 | | |
96 | | #define AVIOOFFSET(x) offsetof(AVIOContext,x) |
97 | | #define E AV_OPT_FLAG_ENCODING_PARAM |
98 | | #define D AV_OPT_FLAG_DECODING_PARAM |
99 | | static const AVOption avio_options[] = { |
100 | | {"protocol_whitelist", "List of protocols that are allowed to be used", AVIOOFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, |
101 | | { NULL }, |
102 | | }; |
103 | | |
104 | | const AVClass ff_avio_class = { |
105 | | .class_name = "AVIOContext", |
106 | | .item_name = av_default_item_name, |
107 | | .version = LIBAVUTIL_VERSION_INT, |
108 | | .option = avio_options, |
109 | | .child_next = avio_child_next, |
110 | | .child_class_iterate = child_class_iterate, |
111 | | }; |
112 | | |
113 | | URLContext *ffio_geturlcontext(AVIOContext *s) |
114 | 0 | { |
115 | 0 | if (!s) |
116 | 0 | return NULL; |
117 | | |
118 | 0 | if (s->opaque && s->read_packet == ffurl_read2) |
119 | 0 | return s->opaque; |
120 | 0 | else |
121 | 0 | return NULL; |
122 | 0 | } |
123 | | |
124 | | static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up, |
125 | | const char *filename, int flags, |
126 | | const AVIOInterruptCB *int_cb) |
127 | 0 | { |
128 | 0 | URLContext *uc; |
129 | 0 | int err; |
130 | |
|
131 | 0 | #if CONFIG_NETWORK |
132 | 0 | if (up->flags & URL_PROTOCOL_FLAG_NETWORK && (err = ff_network_init()) < 0) |
133 | 0 | return err; |
134 | 0 | #endif |
135 | 0 | if ((flags & AVIO_FLAG_READ) && !up->url_read) { |
136 | 0 | av_log(NULL, AV_LOG_ERROR, |
137 | 0 | "Impossible to open the '%s' protocol for reading\n", up->name); |
138 | 0 | return AVERROR(EIO); |
139 | 0 | } |
140 | 0 | if ((flags & AVIO_FLAG_WRITE) && !up->url_write) { |
141 | 0 | av_log(NULL, AV_LOG_ERROR, |
142 | 0 | "Impossible to open the '%s' protocol for writing\n", up->name); |
143 | 0 | return AVERROR(EIO); |
144 | 0 | } |
145 | 0 | uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1); |
146 | 0 | if (!uc) { |
147 | 0 | err = AVERROR(ENOMEM); |
148 | 0 | goto fail; |
149 | 0 | } |
150 | 0 | uc->av_class = &url_context_class; |
151 | 0 | uc->filename = (char *)&uc[1]; |
152 | 0 | strcpy(uc->filename, filename); |
153 | 0 | uc->prot = up; |
154 | 0 | uc->flags = flags; |
155 | 0 | uc->is_streamed = 0; /* default = not streamed */ |
156 | 0 | uc->uses_network = !!(up->flags & URL_PROTOCOL_FLAG_NETWORK); |
157 | 0 | uc->max_packet_size = 0; /* default: stream file */ |
158 | 0 | if (up->priv_data_size) { |
159 | 0 | uc->priv_data = av_mallocz(up->priv_data_size); |
160 | 0 | if (!uc->priv_data) { |
161 | 0 | err = AVERROR(ENOMEM); |
162 | 0 | goto fail; |
163 | 0 | } |
164 | 0 | if (up->priv_data_class) { |
165 | 0 | char *start; |
166 | 0 | *(const AVClass **)uc->priv_data = up->priv_data_class; |
167 | 0 | av_opt_set_defaults(uc->priv_data); |
168 | 0 | if (av_strstart(uc->filename, up->name, (const char**)&start) && *start == ',') { |
169 | 0 | int ret= 0; |
170 | 0 | char *p= start; |
171 | 0 | char sep= *++p; |
172 | 0 | char *key, *val; |
173 | 0 | p++; |
174 | |
|
175 | 0 | if (strcmp(up->name, "subfile")) |
176 | 0 | ret = AVERROR(EINVAL); |
177 | |
|
178 | 0 | while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){ |
179 | 0 | *val= *key= 0; |
180 | 0 | ret = av_opt_set(uc->priv_data, p, key+1, 0); |
181 | 0 | if (ret == AVERROR_OPTION_NOT_FOUND) |
182 | 0 | av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p); |
183 | 0 | *val= *key= sep; |
184 | 0 | p= val+1; |
185 | 0 | } |
186 | 0 | if(ret<0 || p!=key){ |
187 | 0 | av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start); |
188 | 0 | err = AVERROR(EINVAL); |
189 | 0 | goto fail; |
190 | 0 | } |
191 | 0 | memmove(start, key+1, strlen(key)); |
192 | 0 | } |
193 | 0 | } |
194 | 0 | } |
195 | 0 | if (int_cb) |
196 | 0 | uc->interrupt_callback = *int_cb; |
197 | |
|
198 | 0 | *puc = uc; |
199 | 0 | return 0; |
200 | 0 | fail: |
201 | 0 | *puc = NULL; |
202 | 0 | if (uc) |
203 | 0 | av_freep(&uc->priv_data); |
204 | 0 | av_freep(&uc); |
205 | 0 | #if CONFIG_NETWORK |
206 | 0 | if (up->flags & URL_PROTOCOL_FLAG_NETWORK) |
207 | 0 | ff_network_close(); |
208 | 0 | #endif |
209 | 0 | return err; |
210 | 0 | } |
211 | | |
212 | | int ffurl_connect(URLContext *uc, AVDictionary **options) |
213 | 0 | { |
214 | 0 | int err; |
215 | 0 | AVDictionary *tmp_opts = NULL; |
216 | 0 | AVDictionaryEntry *e; |
217 | |
|
218 | 0 | if (!options) |
219 | 0 | options = &tmp_opts; |
220 | | |
221 | | // Check that URLContext was initialized correctly and lists are matching if set |
222 | 0 | av_assert0(!(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) || |
223 | 0 | (uc->protocol_whitelist && !strcmp(uc->protocol_whitelist, e->value))); |
224 | 0 | av_assert0(!(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) || |
225 | 0 | (uc->protocol_blacklist && !strcmp(uc->protocol_blacklist, e->value))); |
226 | | |
227 | 0 | if (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, ',') <= 0) { |
228 | 0 | av_log(uc, AV_LOG_ERROR, "Protocol '%s' not on whitelist '%s'!\n", uc->prot->name, uc->protocol_whitelist); |
229 | 0 | return AVERROR(EINVAL); |
230 | 0 | } |
231 | | |
232 | 0 | if (uc->protocol_blacklist && av_match_list(uc->prot->name, uc->protocol_blacklist, ',') > 0) { |
233 | 0 | av_log(uc, AV_LOG_ERROR, "Protocol '%s' on blacklist '%s'!\n", uc->prot->name, uc->protocol_blacklist); |
234 | 0 | return AVERROR(EINVAL); |
235 | 0 | } |
236 | | |
237 | 0 | if (!uc->protocol_whitelist && uc->prot->default_whitelist) { |
238 | 0 | av_log(uc, AV_LOG_DEBUG, "Setting default whitelist '%s'\n", uc->prot->default_whitelist); |
239 | 0 | uc->protocol_whitelist = av_strdup(uc->prot->default_whitelist); |
240 | 0 | if (!uc->protocol_whitelist) { |
241 | 0 | return AVERROR(ENOMEM); |
242 | 0 | } |
243 | 0 | } else if (!uc->protocol_whitelist) |
244 | 0 | av_log(uc, AV_LOG_DEBUG, "No default whitelist set\n"); // This should be an error once all declare a default whitelist |
245 | | |
246 | 0 | if ((err = av_dict_set(options, "protocol_whitelist", uc->protocol_whitelist, 0)) < 0) |
247 | 0 | return err; |
248 | 0 | if ((err = av_dict_set(options, "protocol_blacklist", uc->protocol_blacklist, 0)) < 0) |
249 | 0 | goto fail; |
250 | | |
251 | 0 | err = |
252 | 0 | uc->prot->url_open2 ? uc->prot->url_open2(uc, |
253 | 0 | uc->filename, |
254 | 0 | uc->flags, |
255 | 0 | options) : |
256 | 0 | uc->prot->url_open(uc, uc->filename, uc->flags); |
257 | |
|
258 | 0 | av_dict_set(options, "protocol_whitelist", NULL, 0); |
259 | 0 | av_dict_set(options, "protocol_blacklist", NULL, 0); |
260 | |
|
261 | 0 | if (err) |
262 | 0 | return err; |
263 | 0 | uc->is_connected = 1; |
264 | | /* We must be careful here as ffurl_seek() could be slow, |
265 | | * for example for http */ |
266 | 0 | if ((uc->flags & AVIO_FLAG_WRITE) || !strcmp(uc->prot->name, "file")) |
267 | 0 | if (!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0) |
268 | 0 | uc->is_streamed = 1; |
269 | 0 | return 0; |
270 | | |
271 | 0 | fail: |
272 | 0 | if (options == &tmp_opts) |
273 | 0 | av_dict_free(&tmp_opts); |
274 | 0 | return err; |
275 | 0 | } |
276 | | |
277 | | int ffurl_accept(URLContext *s, URLContext **c) |
278 | 0 | { |
279 | 0 | av_assert0(!*c); |
280 | 0 | if (s->prot->url_accept) |
281 | 0 | return s->prot->url_accept(s, c); |
282 | 0 | return AVERROR(EBADF); |
283 | 0 | } |
284 | | |
285 | | int avio_accept(AVIOContext *s, AVIOContext **c) |
286 | 0 | { |
287 | 0 | int ret; |
288 | 0 | URLContext *sc = s->opaque; |
289 | 0 | URLContext *cc = NULL; |
290 | 0 | ret = ffurl_accept(sc, &cc); |
291 | 0 | if (ret < 0) |
292 | 0 | return ret; |
293 | 0 | return ffio_fdopen(c, cc); |
294 | 0 | } |
295 | | |
296 | | int ffurl_handshake(URLContext *c) |
297 | 0 | { |
298 | 0 | int ret; |
299 | 0 | if (c->prot->url_handshake) { |
300 | 0 | ret = c->prot->url_handshake(c); |
301 | 0 | if (ret) |
302 | 0 | return ret; |
303 | 0 | } |
304 | 0 | c->is_connected = 1; |
305 | 0 | return 0; |
306 | 0 | } |
307 | | |
308 | | int avio_handshake(AVIOContext *c) |
309 | 0 | { |
310 | 0 | URLContext *cc = c->opaque; |
311 | 0 | return ffurl_handshake(cc); |
312 | 0 | } |
313 | | |
314 | | #define URL_SCHEME_CHARS \ |
315 | 692 | "abcdefghijklmnopqrstuvwxyz" \ |
316 | 692 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ |
317 | 692 | "0123456789+-." |
318 | | |
319 | | static const struct URLProtocol *url_find_protocol(const char *filename) |
320 | 692 | { |
321 | 692 | const URLProtocol **protocols; |
322 | 692 | char proto_str[128], proto_nested[128], *ptr; |
323 | 692 | size_t proto_len = strspn(filename, URL_SCHEME_CHARS); |
324 | 692 | int i; |
325 | | |
326 | 692 | if (filename[proto_len] != ':' && |
327 | 692 | (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) || |
328 | 0 | is_dos_path(filename)) |
329 | 692 | strcpy(proto_str, "file"); |
330 | 0 | else |
331 | 0 | av_strlcpy(proto_str, filename, |
332 | 0 | FFMIN(proto_len + 1, sizeof(proto_str))); |
333 | | |
334 | 692 | av_strlcpy(proto_nested, proto_str, sizeof(proto_nested)); |
335 | 692 | if ((ptr = strchr(proto_nested, '+'))) |
336 | 0 | *ptr = '\0'; |
337 | | |
338 | 692 | protocols = ffurl_get_protocols(NULL, NULL); |
339 | 692 | if (!protocols) |
340 | 0 | return NULL; |
341 | 692 | for (i = 0; protocols[i]; i++) { |
342 | 0 | const URLProtocol *up = protocols[i]; |
343 | 0 | if (!strcmp(proto_str, up->name)) { |
344 | 0 | av_freep(&protocols); |
345 | 0 | return up; |
346 | 0 | } |
347 | 0 | if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME && |
348 | 0 | !strcmp(proto_nested, up->name)) { |
349 | 0 | av_freep(&protocols); |
350 | 0 | return up; |
351 | 0 | } |
352 | 0 | } |
353 | 692 | av_freep(&protocols); |
354 | 692 | if (av_strstart(filename, "https:", NULL) || av_strstart(filename, "tls:", NULL) || |
355 | 692 | av_strstart(filename, "dtls:", NULL)) |
356 | 0 | av_log(NULL, AV_LOG_WARNING, "https or dtls protocol not found, recompile FFmpeg with " |
357 | 0 | "openssl, gnutls or securetransport enabled.\n"); |
358 | | |
359 | 692 | return NULL; |
360 | 692 | } |
361 | | |
362 | | int ffurl_alloc(URLContext **puc, const char *filename, int flags, |
363 | | const AVIOInterruptCB *int_cb) |
364 | 692 | { |
365 | 692 | const URLProtocol *p = NULL; |
366 | | |
367 | 692 | p = url_find_protocol(filename); |
368 | 692 | if (p) |
369 | 0 | return url_alloc_for_protocol(puc, p, filename, flags, int_cb); |
370 | | |
371 | 692 | *puc = NULL; |
372 | 692 | return AVERROR_PROTOCOL_NOT_FOUND; |
373 | 692 | } |
374 | | |
375 | | #if CONFIG_LIBCURL_PROTOCOL |
376 | | extern const URLProtocol ff_libcurl_protocol; |
377 | | |
378 | | /* Decide whether an http(s) URL should be routed to the libcurl protocol instead |
379 | | * of the native one. Controlled by the per-open "prefer_libcurl" option. */ |
380 | | static int prefer_libcurl(const char *filename, AVDictionary **options, |
381 | | URLContext *parent) |
382 | | { |
383 | | URLContext dummy = { .av_class = &url_context_class }; |
384 | | AVDictionaryEntry *e; |
385 | | |
386 | | if (!av_strstart(filename, "http://", NULL) && |
387 | | !av_strstart(filename, "https://", NULL)) |
388 | | return 0; |
389 | | |
390 | | if (parent && parent->prefer_libcurl) |
391 | | return 1; |
392 | | |
393 | | if (!options || !(e = av_dict_get(*options, "prefer_libcurl", NULL, 0))) |
394 | | return 0; |
395 | | if (av_opt_set(&dummy, "prefer_libcurl", e->value, 0) < 0) |
396 | | return 0; |
397 | | |
398 | | return dummy.prefer_libcurl; |
399 | | } |
400 | | #endif |
401 | | |
402 | | static int url_open_whitelist(URLContext **puc, const char *filename, int flags, |
403 | | const AVIOInterruptCB *int_cb, AVDictionary **options, |
404 | | const char *whitelist, const char* blacklist, |
405 | | URLContext *parent, AVFormatContext *avfc) |
406 | 692 | { |
407 | 692 | AVDictionary *tmp_opts = NULL; |
408 | 692 | AVDictionaryEntry *e; |
409 | 692 | int ret; |
410 | | |
411 | | #if CONFIG_LIBCURL_PROTOCOL |
412 | | /* The option itself is applied to the URLContext further down; here it only |
413 | | * picks the protocol, before the context exists. */ |
414 | | if (prefer_libcurl(filename, options, parent)) |
415 | | ret = url_alloc_for_protocol(puc, &ff_libcurl_protocol, filename, flags, |
416 | | int_cb); |
417 | | else |
418 | | #endif |
419 | 692 | ret = ffurl_alloc(puc, filename, flags, int_cb); |
420 | 692 | if (ret < 0) |
421 | 692 | return ret; |
422 | 0 | (*puc)->avfc = avfc; |
423 | 0 | if (parent) { |
424 | 0 | ret = av_opt_copy(*puc, parent); |
425 | 0 | if (ret < 0) |
426 | 0 | goto fail; |
427 | 0 | } |
428 | 0 | if (options && |
429 | 0 | (ret = av_opt_set_dict(*puc, options)) < 0) |
430 | 0 | goto fail; |
431 | 0 | if (options && (*puc)->prot->priv_data_class && |
432 | 0 | (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0) |
433 | 0 | goto fail; |
434 | | |
435 | 0 | if (!options) |
436 | 0 | options = &tmp_opts; |
437 | |
|
438 | 0 | av_assert0(!whitelist || |
439 | 0 | !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) || |
440 | 0 | !strcmp(whitelist, e->value)); |
441 | 0 | av_assert0(!blacklist || |
442 | 0 | !(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) || |
443 | 0 | !strcmp(blacklist, e->value)); |
444 | | |
445 | 0 | if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0) |
446 | 0 | goto fail; |
447 | | |
448 | 0 | if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0) |
449 | 0 | goto fail; |
450 | | |
451 | 0 | if ((ret = av_opt_set_dict(*puc, options)) < 0) |
452 | 0 | goto fail; |
453 | | |
454 | 0 | ret = ffurl_connect(*puc, options); |
455 | |
|
456 | 0 | if (!ret) { |
457 | 0 | if (parent) |
458 | 0 | parent->uses_network |= (*puc)->uses_network; |
459 | 0 | return 0; |
460 | 0 | } |
461 | 0 | fail: |
462 | 0 | ffurl_closep(puc); |
463 | 0 | return ret; |
464 | 0 | } |
465 | | |
466 | | int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, |
467 | | const AVIOInterruptCB *int_cb, AVDictionary **options, |
468 | | const char *whitelist, const char* blacklist, |
469 | | URLContext *parent) |
470 | 0 | { |
471 | 0 | return url_open_whitelist(puc, filename, flags, int_cb, options, |
472 | 0 | whitelist, blacklist, parent, NULL); |
473 | 0 | } |
474 | | |
475 | | int ffio_fdopen(AVIOContext **sp, URLContext *h) |
476 | 0 | { |
477 | 0 | AVIOContext *s; |
478 | 0 | uint8_t *buffer = NULL; |
479 | 0 | int buffer_size, max_packet_size; |
480 | |
|
481 | 0 | max_packet_size = h->max_packet_size; |
482 | 0 | if (max_packet_size) { |
483 | 0 | buffer_size = max_packet_size; /* no need to bufferize more than one packet */ |
484 | 0 | } else if (h->uses_network && !(h->flags & AVIO_FLAG_WRITE)) { |
485 | 0 | buffer_size = NETWORK_IO_BUFFER_SIZE; |
486 | 0 | } else { |
487 | 0 | buffer_size = IO_BUFFER_SIZE; |
488 | 0 | } |
489 | 0 | if (!(h->flags & AVIO_FLAG_WRITE) && h->is_streamed) { |
490 | 0 | if (buffer_size > INT_MAX/2) |
491 | 0 | return AVERROR(EINVAL); |
492 | 0 | buffer_size *= 2; |
493 | 0 | } |
494 | 0 | buffer = av_malloc(buffer_size); |
495 | 0 | if (!buffer) |
496 | 0 | return AVERROR(ENOMEM); |
497 | | |
498 | 0 | *sp = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h, |
499 | 0 | ffurl_read2, ffurl_write2, ffurl_seek2); |
500 | 0 | if (!*sp) { |
501 | 0 | av_freep(&buffer); |
502 | 0 | return AVERROR(ENOMEM); |
503 | 0 | } |
504 | 0 | s = *sp; |
505 | 0 | if (h->protocol_whitelist) { |
506 | 0 | s->protocol_whitelist = av_strdup(h->protocol_whitelist); |
507 | 0 | if (!s->protocol_whitelist) { |
508 | 0 | avio_closep(sp); |
509 | 0 | return AVERROR(ENOMEM); |
510 | 0 | } |
511 | 0 | } |
512 | 0 | if (h->protocol_blacklist) { |
513 | 0 | s->protocol_blacklist = av_strdup(h->protocol_blacklist); |
514 | 0 | if (!s->protocol_blacklist) { |
515 | 0 | avio_closep(sp); |
516 | 0 | return AVERROR(ENOMEM); |
517 | 0 | } |
518 | 0 | } |
519 | 0 | s->direct = h->flags & AVIO_FLAG_DIRECT; |
520 | |
|
521 | 0 | s->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL; |
522 | 0 | s->max_packet_size = max_packet_size; |
523 | 0 | s->min_packet_size = h->min_packet_size; |
524 | 0 | if(h->prot) { |
525 | 0 | s->read_pause = h->prot->url_read_pause; |
526 | 0 | s->read_seek = h->prot->url_read_seek; |
527 | |
|
528 | 0 | if (h->prot->url_read_seek) |
529 | 0 | s->seekable |= AVIO_SEEKABLE_TIME; |
530 | 0 | } |
531 | 0 | ((FFIOContext*)s)->short_seek_get = ffurl_get_short_seek; |
532 | 0 | s->av_class = &ff_avio_class; |
533 | 0 | return 0; |
534 | 0 | } |
535 | | |
536 | | int ffio_open_whitelist2(AVIOContext **s, const char *filename, int flags, |
537 | | const AVIOInterruptCB *int_cb, AVDictionary **options, |
538 | | const char *whitelist, const char *blacklist, |
539 | | AVFormatContext *avfc) |
540 | 692 | { |
541 | 692 | URLContext *h; |
542 | 692 | int err; |
543 | | |
544 | 692 | *s = NULL; |
545 | | |
546 | 692 | err = url_open_whitelist(&h, filename, flags, int_cb, options, whitelist, |
547 | 692 | blacklist, NULL, avfc); |
548 | 692 | if (err < 0) |
549 | 692 | return err; |
550 | 0 | err = ffio_fdopen(s, h); |
551 | 0 | if (err < 0) { |
552 | 0 | ffurl_close(h); |
553 | 0 | return err; |
554 | 0 | } |
555 | 0 | return 0; |
556 | 0 | } |
557 | | |
558 | | int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags, |
559 | | const AVIOInterruptCB *int_cb, AVDictionary **options, |
560 | | const char *whitelist, const char *blacklist) |
561 | 0 | { |
562 | 0 | return ffio_open_whitelist2(s, filename, flags, int_cb, options, |
563 | 0 | whitelist, blacklist, NULL); |
564 | 0 | } |
565 | | |
566 | | int avio_open2(AVIOContext **s, const char *filename, int flags, |
567 | | const AVIOInterruptCB *int_cb, AVDictionary **options) |
568 | 0 | { |
569 | 0 | return ffio_open_whitelist(s, filename, flags, int_cb, options, NULL, NULL); |
570 | 0 | } |
571 | | |
572 | | int avio_open(AVIOContext **s, const char *filename, int flags) |
573 | 0 | { |
574 | 0 | return avio_open2(s, filename, flags, NULL, NULL); |
575 | 0 | } |
576 | | |
577 | | |
578 | | static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf, |
579 | | const uint8_t *cbuf, |
580 | | int size, int size_min, |
581 | | int read) |
582 | 0 | { |
583 | 0 | int ret, len; |
584 | 0 | int fast_retries = 5; |
585 | 0 | int64_t wait_since = 0; |
586 | |
|
587 | 0 | len = 0; |
588 | 0 | while (len < size_min) { |
589 | 0 | if (ff_check_interrupt(&h->interrupt_callback)) |
590 | 0 | return AVERROR_EXIT; |
591 | 0 | ret = read ? h->prot->url_read (h, buf + len, size - len): |
592 | 0 | h->prot->url_write(h, cbuf + len, size - len); |
593 | 0 | if (ret == AVERROR(EINTR)) |
594 | 0 | continue; |
595 | 0 | if (h->flags & AVIO_FLAG_NONBLOCK) |
596 | 0 | return ret; |
597 | 0 | if (ret == AVERROR(EAGAIN)) { |
598 | 0 | ret = 0; |
599 | 0 | if (fast_retries) { |
600 | 0 | fast_retries--; |
601 | 0 | } else { |
602 | 0 | if (h->rw_timeout) { |
603 | 0 | if (!wait_since) |
604 | 0 | wait_since = av_gettime_relative(); |
605 | 0 | else if (av_gettime_relative() > wait_since + h->rw_timeout) |
606 | 0 | return AVERROR(EIO); |
607 | 0 | } |
608 | 0 | av_usleep(1000); |
609 | 0 | } |
610 | 0 | } else if (ret == AVERROR_EOF) |
611 | 0 | return (len > 0) ? len : AVERROR_EOF; |
612 | 0 | else if (ret < 0) |
613 | 0 | return ret; |
614 | 0 | if (ret) { |
615 | 0 | fast_retries = FFMAX(fast_retries, 2); |
616 | 0 | wait_since = 0; |
617 | 0 | } |
618 | 0 | len += ret; |
619 | 0 | } |
620 | 0 | return len; |
621 | 0 | } |
622 | | |
623 | | int ffurl_read2(void *urlcontext, uint8_t *buf, int size) |
624 | 0 | { |
625 | 0 | URLContext *h = urlcontext; |
626 | |
|
627 | 0 | if (!(h->flags & AVIO_FLAG_READ)) |
628 | 0 | return AVERROR(EIO); |
629 | 0 | return retry_transfer_wrapper(h, buf, NULL, size, 1, 1); |
630 | 0 | } |
631 | | |
632 | | int ffurl_read_complete(URLContext *h, unsigned char *buf, int size) |
633 | 0 | { |
634 | 0 | if (!(h->flags & AVIO_FLAG_READ)) |
635 | 0 | return AVERROR(EIO); |
636 | 0 | return retry_transfer_wrapper(h, buf, NULL, size, size, 1); |
637 | 0 | } |
638 | | |
639 | | int ffurl_write2(void *urlcontext, const uint8_t *buf, int size) |
640 | 0 | { |
641 | 0 | URLContext *h = urlcontext; |
642 | |
|
643 | 0 | if (!(h->flags & AVIO_FLAG_WRITE)) |
644 | 0 | return AVERROR(EIO); |
645 | | /* avoid sending too big packets */ |
646 | 0 | if (h->max_packet_size && size > h->max_packet_size) |
647 | 0 | return AVERROR(EIO); |
648 | | |
649 | 0 | return retry_transfer_wrapper(h, NULL, buf, size, size, 0); |
650 | 0 | } |
651 | | |
652 | | int64_t ffurl_seek2(void *urlcontext, int64_t pos, int whence) |
653 | 0 | { |
654 | 0 | URLContext *h = urlcontext; |
655 | 0 | int64_t ret; |
656 | |
|
657 | 0 | if (!h->prot->url_seek) |
658 | 0 | return AVERROR(ENOSYS); |
659 | 0 | ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE); |
660 | 0 | return ret; |
661 | 0 | } |
662 | | |
663 | | int ffurl_closep(URLContext **hh) |
664 | 0 | { |
665 | 0 | URLContext *h= *hh; |
666 | 0 | int ret = 0; |
667 | 0 | if (!h) |
668 | 0 | return 0; /* can happen when ffurl_open fails */ |
669 | | |
670 | 0 | if (h->is_connected && h->prot->url_close) |
671 | 0 | ret = h->prot->url_close(h); |
672 | 0 | #if CONFIG_NETWORK |
673 | 0 | if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK) |
674 | 0 | ff_network_close(); |
675 | 0 | #endif |
676 | 0 | if (h->prot->priv_data_size) { |
677 | 0 | if (h->prot->priv_data_class) |
678 | 0 | av_opt_free(h->priv_data); |
679 | 0 | av_freep(&h->priv_data); |
680 | 0 | } |
681 | 0 | av_opt_free(h); |
682 | 0 | av_freep(hh); |
683 | 0 | return ret; |
684 | 0 | } |
685 | | |
686 | | int ffurl_close(URLContext *h) |
687 | 0 | { |
688 | 0 | return ffurl_closep(&h); |
689 | 0 | } |
690 | | |
691 | | int avio_close(AVIOContext *s) |
692 | 0 | { |
693 | 0 | FFIOContext *const ctx = ffiocontext(s); |
694 | 0 | URLContext *h; |
695 | 0 | int ret, error; |
696 | |
|
697 | 0 | if (!s) |
698 | 0 | return 0; |
699 | | |
700 | 0 | avio_flush(s); |
701 | 0 | h = s->opaque; |
702 | 0 | s->opaque = NULL; |
703 | |
|
704 | 0 | av_freep(&s->buffer); |
705 | 0 | if (s->write_flag) |
706 | 0 | av_log(s, AV_LOG_VERBOSE, |
707 | 0 | "Statistics: %"PRId64" bytes written, %d seeks, %d writeouts\n", |
708 | 0 | ctx->bytes_written, ctx->seek_count, ctx->writeout_count); |
709 | 0 | else |
710 | 0 | av_log(s, AV_LOG_VERBOSE, "Statistics: %"PRId64" bytes read, %d seeks\n", |
711 | 0 | ctx->bytes_read, ctx->seek_count); |
712 | 0 | av_opt_free(s); |
713 | |
|
714 | 0 | error = s->error; |
715 | 0 | avio_context_free(&s); |
716 | |
|
717 | 0 | ret = ffurl_close(h); |
718 | 0 | if (ret < 0) |
719 | 0 | return ret; |
720 | | |
721 | 0 | return error; |
722 | 0 | } |
723 | | |
724 | | int avio_closep(AVIOContext **s) |
725 | 0 | { |
726 | 0 | int ret = avio_close(*s); |
727 | 0 | *s = NULL; |
728 | 0 | return ret; |
729 | 0 | } |
730 | | |
731 | | |
732 | | const char *avio_find_protocol_name(const char *url) |
733 | 0 | { |
734 | 0 | const URLProtocol *p = url_find_protocol(url); |
735 | |
|
736 | 0 | return p ? p->name : NULL; |
737 | 0 | } |
738 | | |
739 | | int avio_check(const char *url, int flags) |
740 | 0 | { |
741 | 0 | URLContext *h; |
742 | 0 | int ret = ffurl_alloc(&h, url, flags, NULL); |
743 | 0 | if (ret < 0) |
744 | 0 | return ret; |
745 | | |
746 | 0 | if (h->prot->url_check) { |
747 | 0 | ret = h->prot->url_check(h, flags); |
748 | 0 | } else { |
749 | 0 | ret = ffurl_connect(h, NULL); |
750 | 0 | if (ret >= 0) |
751 | 0 | ret = flags; |
752 | 0 | } |
753 | |
|
754 | 0 | ffurl_close(h); |
755 | 0 | return ret; |
756 | 0 | } |
757 | | |
758 | | int ffurl_move(const char *url_src, const char *url_dst) |
759 | 0 | { |
760 | 0 | URLContext *h_src, *h_dst; |
761 | 0 | int ret = ffurl_alloc(&h_src, url_src, AVIO_FLAG_READ_WRITE, NULL); |
762 | 0 | if (ret < 0) |
763 | 0 | return ret; |
764 | 0 | ret = ffurl_alloc(&h_dst, url_dst, AVIO_FLAG_WRITE, NULL); |
765 | 0 | if (ret < 0) { |
766 | 0 | ffurl_close(h_src); |
767 | 0 | return ret; |
768 | 0 | } |
769 | | |
770 | 0 | if (h_src->prot == h_dst->prot && h_src->prot->url_move) |
771 | 0 | ret = h_src->prot->url_move(h_src, h_dst); |
772 | 0 | else |
773 | 0 | ret = AVERROR(ENOSYS); |
774 | |
|
775 | 0 | ffurl_close(h_src); |
776 | 0 | ffurl_close(h_dst); |
777 | 0 | return ret; |
778 | 0 | } |
779 | | |
780 | | int ffurl_delete(const char *url) |
781 | 0 | { |
782 | 0 | URLContext *h; |
783 | 0 | int ret = ffurl_alloc(&h, url, AVIO_FLAG_WRITE, NULL); |
784 | 0 | if (ret < 0) |
785 | 0 | return ret; |
786 | | |
787 | 0 | if (h->prot->url_delete) |
788 | 0 | ret = h->prot->url_delete(h); |
789 | 0 | else |
790 | 0 | ret = AVERROR(ENOSYS); |
791 | |
|
792 | 0 | ffurl_close(h); |
793 | 0 | return ret; |
794 | 0 | } |
795 | | |
796 | | struct AVIODirContext { |
797 | | struct URLContext *url_context; |
798 | | }; |
799 | | |
800 | | int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options) |
801 | 0 | { |
802 | 0 | URLContext *h = NULL; |
803 | 0 | AVIODirContext *ctx = NULL; |
804 | 0 | int ret; |
805 | 0 | av_assert0(s); |
806 | | |
807 | 0 | ctx = av_mallocz(sizeof(*ctx)); |
808 | 0 | if (!ctx) { |
809 | 0 | ret = AVERROR(ENOMEM); |
810 | 0 | goto fail; |
811 | 0 | } |
812 | | |
813 | 0 | if ((ret = ffurl_alloc(&h, url, AVIO_FLAG_READ, NULL)) < 0) |
814 | 0 | goto fail; |
815 | | |
816 | 0 | if (h->prot->url_open_dir && h->prot->url_read_dir && h->prot->url_close_dir) { |
817 | 0 | if (options && h->prot->priv_data_class && |
818 | 0 | (ret = av_opt_set_dict(h->priv_data, options)) < 0) |
819 | 0 | goto fail; |
820 | 0 | ret = h->prot->url_open_dir(h); |
821 | 0 | } else |
822 | 0 | ret = AVERROR(ENOSYS); |
823 | 0 | if (ret < 0) |
824 | 0 | goto fail; |
825 | | |
826 | 0 | h->is_connected = 1; |
827 | 0 | ctx->url_context = h; |
828 | 0 | *s = ctx; |
829 | 0 | return 0; |
830 | | |
831 | 0 | fail: |
832 | 0 | av_free(ctx); |
833 | 0 | *s = NULL; |
834 | 0 | ffurl_close(h); |
835 | 0 | return ret; |
836 | 0 | } |
837 | | |
838 | | int avio_read_dir(AVIODirContext *s, AVIODirEntry **next) |
839 | 0 | { |
840 | 0 | URLContext *h; |
841 | 0 | int ret; |
842 | |
|
843 | 0 | if (!s || !s->url_context) |
844 | 0 | return AVERROR(EINVAL); |
845 | 0 | h = s->url_context; |
846 | 0 | if ((ret = h->prot->url_read_dir(h, next)) < 0) |
847 | 0 | avio_free_directory_entry(next); |
848 | 0 | return ret; |
849 | 0 | } |
850 | | |
851 | | int avio_close_dir(AVIODirContext **s) |
852 | 0 | { |
853 | 0 | URLContext *h; |
854 | |
|
855 | 0 | av_assert0(s); |
856 | 0 | if (!(*s) || !(*s)->url_context) |
857 | 0 | return AVERROR(EINVAL); |
858 | 0 | h = (*s)->url_context; |
859 | 0 | h->prot->url_close_dir(h); |
860 | 0 | ffurl_close(h); |
861 | 0 | av_freep(s); |
862 | 0 | *s = NULL; |
863 | 0 | return 0; |
864 | 0 | } |
865 | | |
866 | | void avio_free_directory_entry(AVIODirEntry **entry) |
867 | 0 | { |
868 | 0 | if (!entry || !*entry) |
869 | 0 | return; |
870 | 0 | av_free((*entry)->name); |
871 | 0 | av_freep(entry); |
872 | 0 | } |
873 | | |
874 | | int64_t ffurl_size(URLContext *h) |
875 | 0 | { |
876 | 0 | int64_t pos, size; |
877 | |
|
878 | 0 | size = ffurl_seek(h, 0, AVSEEK_SIZE); |
879 | 0 | if (size < 0) { |
880 | 0 | pos = ffurl_seek(h, 0, SEEK_CUR); |
881 | 0 | if ((size = ffurl_seek(h, -1, SEEK_END)) < 0) |
882 | 0 | return size; |
883 | 0 | size++; |
884 | 0 | ffurl_seek(h, pos, SEEK_SET); |
885 | 0 | } |
886 | 0 | return size; |
887 | 0 | } |
888 | | |
889 | | int ffurl_get_file_handle(URLContext *h) |
890 | 0 | { |
891 | 0 | if (!h || !h->prot || !h->prot->url_get_file_handle) |
892 | 0 | return -1; |
893 | 0 | return h->prot->url_get_file_handle(h); |
894 | 0 | } |
895 | | |
896 | | int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles) |
897 | 0 | { |
898 | 0 | if (!h || !h->prot) |
899 | 0 | return AVERROR(ENOSYS); |
900 | 0 | if (!h->prot->url_get_multi_file_handle) { |
901 | 0 | if (!h->prot->url_get_file_handle) |
902 | 0 | return AVERROR(ENOSYS); |
903 | 0 | *handles = av_malloc(sizeof(**handles)); |
904 | 0 | if (!*handles) |
905 | 0 | return AVERROR(ENOMEM); |
906 | 0 | *numhandles = 1; |
907 | 0 | *handles[0] = h->prot->url_get_file_handle(h); |
908 | 0 | return 0; |
909 | 0 | } |
910 | 0 | return h->prot->url_get_multi_file_handle(h, handles, numhandles); |
911 | 0 | } |
912 | | |
913 | | int ffurl_get_short_seek(void *urlcontext) |
914 | 0 | { |
915 | 0 | URLContext *h = urlcontext; |
916 | |
|
917 | 0 | if (!h || !h->prot || !h->prot->url_get_short_seek) |
918 | 0 | return AVERROR(ENOSYS); |
919 | 0 | return h->prot->url_get_short_seek(h); |
920 | 0 | } |
921 | | |
922 | | int ffurl_shutdown(URLContext *h, int flags) |
923 | 0 | { |
924 | 0 | if (!h || !h->prot || !h->prot->url_shutdown) |
925 | 0 | return AVERROR(ENOSYS); |
926 | 0 | return h->prot->url_shutdown(h, flags); |
927 | 0 | } |
928 | | |
929 | | int ff_check_interrupt(AVIOInterruptCB *cb) |
930 | 0 | { |
931 | 0 | if (cb && cb->callback) |
932 | 0 | return cb->callback(cb->opaque); |
933 | 0 | return 0; |
934 | 0 | } |
935 | | |
936 | | int ff_rename(const char *url_src, const char *url_dst, void *logctx) |
937 | 0 | { |
938 | 0 | int ret = ffurl_move(url_src, url_dst); |
939 | 0 | if (ret < 0) |
940 | 0 | av_log(logctx, AV_LOG_ERROR, "failed to rename file %s to %s: %s\n", url_src, url_dst, av_err2str(ret)); |
941 | 0 | return ret; |
942 | 0 | } |