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