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