blob: 1e79c9dd5c33f3d3283d21236339ca6533516437
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/opt.h" |
25 | #include "libavutil/time.h" |
26 | #include "libavutil/avassert.h" |
27 | #include "os_support.h" |
28 | #include "avformat.h" |
29 | #if CONFIG_NETWORK |
30 | #include "network.h" |
31 | #endif |
32 | #include "url.h" |
33 | |
34 | /** @name Logging context. */ |
35 | /*@{*/ |
36 | static const char *urlcontext_to_name(void *ptr) |
37 | { |
38 | URLContext *h = (URLContext *)ptr; |
39 | if (h->prot) |
40 | return h->prot->name; |
41 | else |
42 | return "NULL"; |
43 | } |
44 | |
45 | static void *urlcontext_child_next(void *obj, void *prev) |
46 | { |
47 | URLContext *h = obj; |
48 | if (!prev && h->priv_data && h->prot->priv_data_class) |
49 | return h->priv_data; |
50 | return NULL; |
51 | } |
52 | |
53 | #define OFFSET(x) offsetof(URLContext,x) |
54 | #define E AV_OPT_FLAG_ENCODING_PARAM |
55 | #define D AV_OPT_FLAG_DECODING_PARAM |
56 | static const AVOption options[] = { |
57 | {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, |
58 | {"protocol_blacklist", "List of protocols that are not allowed to be used", OFFSET(protocol_blacklist), AV_OPT_TYPE_STRING, { .str = NULL }, CHAR_MIN, CHAR_MAX, D }, |
59 | {"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 }, |
60 | { NULL } |
61 | }; |
62 | |
63 | const AVClass ffurl_context_class = { |
64 | .class_name = "URLContext", |
65 | .item_name = urlcontext_to_name, |
66 | .option = options, |
67 | .version = LIBAVUTIL_VERSION_INT, |
68 | .child_next = urlcontext_child_next, |
69 | .child_class_next = ff_urlcontext_child_class_next, |
70 | }; |
71 | /*@}*/ |
72 | |
73 | static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up, |
74 | const char *filename, int flags, |
75 | const AVIOInterruptCB *int_cb) |
76 | { |
77 | URLContext *uc; |
78 | int err; |
79 | |
80 | #if CONFIG_NETWORK |
81 | if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init()) |
82 | return AVERROR(EIO); |
83 | #endif |
84 | if ((flags & AVIO_FLAG_READ) && !up->url_read) { |
85 | av_log(NULL, AV_LOG_ERROR, |
86 | "Impossible to open the '%s' protocol for reading\n", up->name); |
87 | return AVERROR(EIO); |
88 | } |
89 | if ((flags & AVIO_FLAG_WRITE) && !up->url_write) { |
90 | av_log(NULL, AV_LOG_ERROR, |
91 | "Impossible to open the '%s' protocol for writing\n", up->name); |
92 | return AVERROR(EIO); |
93 | } |
94 | uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1); |
95 | if (!uc) { |
96 | err = AVERROR(ENOMEM); |
97 | goto fail; |
98 | } |
99 | uc->av_class = &ffurl_context_class; |
100 | uc->filename = (char *)&uc[1]; |
101 | strcpy(uc->filename, filename); |
102 | uc->prot = up; |
103 | uc->flags = flags; |
104 | uc->is_streamed = 0; /* default = not streamed */ |
105 | uc->max_packet_size = 0; /* default: stream file */ |
106 | if (up->priv_data_size) { |
107 | uc->priv_data = av_mallocz(up->priv_data_size); |
108 | if (!uc->priv_data) { |
109 | err = AVERROR(ENOMEM); |
110 | goto fail; |
111 | } |
112 | if (up->priv_data_class) { |
113 | int proto_len= strlen(up->name); |
114 | char *start = strchr(uc->filename, ','); |
115 | *(const AVClass **)uc->priv_data = up->priv_data_class; |
116 | av_opt_set_defaults(uc->priv_data); |
117 | if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){ |
118 | int ret= 0; |
119 | char *p= start; |
120 | char sep= *++p; |
121 | char *key, *val; |
122 | p++; |
123 | |
124 | if (strcmp(up->name, "subfile")) |
125 | ret = AVERROR(EINVAL); |
126 | |
127 | while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){ |
128 | *val= *key= 0; |
129 | if (strcmp(p, "start") && strcmp(p, "end")) { |
130 | ret = AVERROR_OPTION_NOT_FOUND; |
131 | } else |
132 | ret= av_opt_set(uc->priv_data, p, key+1, 0); |
133 | if (ret == AVERROR_OPTION_NOT_FOUND) |
134 | av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p); |
135 | *val= *key= sep; |
136 | p= val+1; |
137 | } |
138 | if(ret<0 || p!=key){ |
139 | av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start); |
140 | av_freep(&uc->priv_data); |
141 | av_freep(&uc); |
142 | err = AVERROR(EINVAL); |
143 | goto fail; |
144 | } |
145 | memmove(start, key+1, strlen(key)); |
146 | } |
147 | } |
148 | } |
149 | if (int_cb) |
150 | uc->interrupt_callback = *int_cb; |
151 | |
152 | *puc = uc; |
153 | return 0; |
154 | fail: |
155 | *puc = NULL; |
156 | if (uc) |
157 | av_freep(&uc->priv_data); |
158 | av_freep(&uc); |
159 | #if CONFIG_NETWORK |
160 | if (up->flags & URL_PROTOCOL_FLAG_NETWORK) |
161 | ff_network_close(); |
162 | #endif |
163 | return err; |
164 | } |
165 | |
166 | int ffurl_connect(URLContext *uc, AVDictionary **options) |
167 | { |
168 | int err; |
169 | AVDictionary *tmp_opts = NULL; |
170 | AVDictionaryEntry *e; |
171 | |
172 | if (!options) |
173 | options = &tmp_opts; |
174 | |
175 | // Check that URLContext was initialized correctly and lists are matching if set |
176 | av_assert0(!(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) || |
177 | (uc->protocol_whitelist && !strcmp(uc->protocol_whitelist, e->value))); |
178 | av_assert0(!(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) || |
179 | (uc->protocol_blacklist && !strcmp(uc->protocol_blacklist, e->value))); |
180 | |
181 | if (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, ',') <= 0) { |
182 | av_log(uc, AV_LOG_ERROR, "Protocol '%s' not on whitelist '%s'!\n", uc->prot->name, uc->protocol_whitelist); |
183 | return AVERROR(EINVAL); |
184 | } |
185 | |
186 | if (uc->protocol_blacklist && av_match_list(uc->prot->name, uc->protocol_blacklist, ',') > 0) { |
187 | av_log(uc, AV_LOG_ERROR, "Protocol '%s' on blacklist '%s'!\n", uc->prot->name, uc->protocol_blacklist); |
188 | return AVERROR(EINVAL); |
189 | } |
190 | |
191 | if (!uc->protocol_whitelist && uc->prot->default_whitelist) { |
192 | av_log(uc, AV_LOG_DEBUG, "Setting default whitelist '%s'\n", uc->prot->default_whitelist); |
193 | uc->protocol_whitelist = av_strdup(uc->prot->default_whitelist); |
194 | if (!uc->protocol_whitelist) { |
195 | return AVERROR(ENOMEM); |
196 | } |
197 | } else if (!uc->protocol_whitelist) |
198 | av_log(uc, AV_LOG_DEBUG, "No default whitelist set\n"); // This should be an error once all declare a default whitelist |
199 | |
200 | if ((err = av_dict_set(options, "protocol_whitelist", uc->protocol_whitelist, 0)) < 0) |
201 | return err; |
202 | if ((err = av_dict_set(options, "protocol_blacklist", uc->protocol_blacklist, 0)) < 0) |
203 | return err; |
204 | |
205 | err = |
206 | uc->prot->url_open2 ? uc->prot->url_open2(uc, |
207 | uc->filename, |
208 | uc->flags, |
209 | options) : |
210 | uc->prot->url_open(uc, uc->filename, uc->flags); |
211 | |
212 | av_dict_set(options, "protocol_whitelist", NULL, 0); |
213 | av_dict_set(options, "protocol_blacklist", NULL, 0); |
214 | |
215 | if (err) |
216 | return err; |
217 | uc->is_connected = 1; |
218 | /* We must be careful here as ffurl_seek() could be slow, |
219 | * for example for http */ |
220 | if ((uc->flags & AVIO_FLAG_WRITE) || !strcmp(uc->prot->name, "file")) |
221 | if (!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0) |
222 | uc->is_streamed = 1; |
223 | return 0; |
224 | } |
225 | |
226 | int ffurl_accept(URLContext *s, URLContext **c) |
227 | { |
228 | av_assert0(!*c); |
229 | if (s->prot->url_accept) |
230 | return s->prot->url_accept(s, c); |
231 | return AVERROR(EBADF); |
232 | } |
233 | |
234 | int ffurl_handshake(URLContext *c) |
235 | { |
236 | int ret; |
237 | if (c->prot->url_handshake) { |
238 | ret = c->prot->url_handshake(c); |
239 | if (ret) |
240 | return ret; |
241 | } |
242 | c->is_connected = 1; |
243 | return 0; |
244 | } |
245 | |
246 | #define URL_SCHEME_CHARS \ |
247 | "abcdefghijklmnopqrstuvwxyz" \ |
248 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ |
249 | "0123456789+-." |
250 | |
251 | static const struct URLProtocol *url_find_protocol(const char *filename) |
252 | { |
253 | const URLProtocol **protocols; |
254 | char proto_str[128], proto_nested[128], *ptr; |
255 | size_t proto_len = strspn(filename, URL_SCHEME_CHARS); |
256 | int i; |
257 | |
258 | if (filename[proto_len] != ':' && |
259 | (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) || |
260 | is_dos_path(filename)) |
261 | strcpy(proto_str, "file"); |
262 | else |
263 | av_strlcpy(proto_str, filename, |
264 | FFMIN(proto_len + 1, sizeof(proto_str))); |
265 | |
266 | if ((ptr = strchr(proto_str, ','))) |
267 | *ptr = '\0'; |
268 | av_strlcpy(proto_nested, proto_str, sizeof(proto_nested)); |
269 | if ((ptr = strchr(proto_nested, '+'))) |
270 | *ptr = '\0'; |
271 | |
272 | protocols = ffurl_get_protocols(NULL, NULL); |
273 | if (!protocols) |
274 | return NULL; |
275 | for (i = 0; protocols[i]; i++) { |
276 | const URLProtocol *up = protocols[i]; |
277 | if (!strcmp(proto_str, up->name)) { |
278 | av_freep(&protocols); |
279 | return up; |
280 | } |
281 | if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME && |
282 | !strcmp(proto_nested, up->name)) { |
283 | av_freep(&protocols); |
284 | return up; |
285 | } |
286 | } |
287 | av_freep(&protocols); |
288 | |
289 | return NULL; |
290 | } |
291 | |
292 | int ffurl_alloc(URLContext **puc, const char *filename, int flags, |
293 | const AVIOInterruptCB *int_cb) |
294 | { |
295 | const URLProtocol *p = NULL; |
296 | |
297 | p = url_find_protocol(filename); |
298 | if (p) |
299 | return url_alloc_for_protocol(puc, p, filename, flags, int_cb); |
300 | |
301 | *puc = NULL; |
302 | if (av_strstart(filename, "https:", NULL)) |
303 | av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile FFmpeg with " |
304 | "openssl, gnutls " |
305 | "or securetransport enabled.\n"); |
306 | return AVERROR_PROTOCOL_NOT_FOUND; |
307 | } |
308 | |
309 | int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, |
310 | const AVIOInterruptCB *int_cb, AVDictionary **options, |
311 | const char *whitelist, const char* blacklist, |
312 | URLContext *parent) |
313 | { |
314 | AVDictionary *tmp_opts = NULL; |
315 | AVDictionaryEntry *e; |
316 | int ret = ffurl_alloc(puc, filename, flags, int_cb); |
317 | if (ret < 0) |
318 | return ret; |
319 | if (parent) |
320 | av_opt_copy(*puc, parent); |
321 | if (options && |
322 | (ret = av_opt_set_dict(*puc, options)) < 0) |
323 | goto fail; |
324 | if (options && (*puc)->prot->priv_data_class && |
325 | (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0) |
326 | goto fail; |
327 | |
328 | if (!options) |
329 | options = &tmp_opts; |
330 | |
331 | av_assert0(!whitelist || |
332 | !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) || |
333 | !strcmp(whitelist, e->value)); |
334 | av_assert0(!blacklist || |
335 | !(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) || |
336 | !strcmp(blacklist, e->value)); |
337 | |
338 | if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0) |
339 | goto fail; |
340 | |
341 | if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0) |
342 | goto fail; |
343 | |
344 | if ((ret = av_opt_set_dict(*puc, options)) < 0) |
345 | goto fail; |
346 | |
347 | ret = ffurl_connect(*puc, options); |
348 | |
349 | if (!ret) |
350 | return 0; |
351 | fail: |
352 | ffurl_close(*puc); |
353 | *puc = NULL; |
354 | return ret; |
355 | } |
356 | |
357 | int ffurl_open(URLContext **puc, const char *filename, int flags, |
358 | const AVIOInterruptCB *int_cb, AVDictionary **options) |
359 | { |
360 | return ffurl_open_whitelist(puc, filename, flags, |
361 | int_cb, options, NULL, NULL, NULL); |
362 | } |
363 | |
364 | static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf, |
365 | int size, int size_min, |
366 | int (*transfer_func)(URLContext *h, |
367 | uint8_t *buf, |
368 | int size)) |
369 | { |
370 | int ret, len; |
371 | int fast_retries = 5; |
372 | int64_t wait_since = 0; |
373 | |
374 | len = 0; |
375 | while (len < size_min) { |
376 | if (ff_check_interrupt(&h->interrupt_callback)) |
377 | return AVERROR_EXIT; |
378 | ret = transfer_func(h, buf + len, size - len); |
379 | if (ret == AVERROR(EINTR)) |
380 | continue; |
381 | if (h->flags & AVIO_FLAG_NONBLOCK) |
382 | return ret; |
383 | if (ret == AVERROR(EAGAIN)) { |
384 | ret = 0; |
385 | if (fast_retries) { |
386 | fast_retries--; |
387 | } else { |
388 | if (h->rw_timeout) { |
389 | if (!wait_since) |
390 | wait_since = av_gettime_relative(); |
391 | else if (av_gettime_relative() > wait_since + h->rw_timeout) |
392 | return AVERROR(EIO); |
393 | } |
394 | av_usleep(1000); |
395 | } |
396 | } else if (ret < 1) |
397 | return (ret < 0 && ret != AVERROR_EOF) ? ret : len; |
398 | if (ret) { |
399 | fast_retries = FFMAX(fast_retries, 2); |
400 | wait_since = 0; |
401 | } |
402 | len += ret; |
403 | } |
404 | return len; |
405 | } |
406 | |
407 | int ffurl_read(URLContext *h, unsigned char *buf, int size) |
408 | { |
409 | if (!(h->flags & AVIO_FLAG_READ)) |
410 | return AVERROR(EIO); |
411 | return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read); |
412 | } |
413 | |
414 | int ffurl_read_complete(URLContext *h, unsigned char *buf, int size) |
415 | { |
416 | if (!(h->flags & AVIO_FLAG_READ)) |
417 | return AVERROR(EIO); |
418 | return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read); |
419 | } |
420 | |
421 | int ffurl_write(URLContext *h, const unsigned char *buf, int size) |
422 | { |
423 | if (!(h->flags & AVIO_FLAG_WRITE)) |
424 | return AVERROR(EIO); |
425 | /* avoid sending too big packets */ |
426 | if (h->max_packet_size && size > h->max_packet_size) |
427 | return AVERROR(EIO); |
428 | |
429 | return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, |
430 | (int (*)(struct URLContext *, uint8_t *, int)) |
431 | h->prot->url_write); |
432 | } |
433 | |
434 | int64_t ffurl_seek(URLContext *h, int64_t pos, int whence) |
435 | { |
436 | int64_t ret; |
437 | |
438 | if (!h->prot->url_seek) |
439 | return AVERROR(ENOSYS); |
440 | ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE); |
441 | return ret; |
442 | } |
443 | |
444 | int ffurl_closep(URLContext **hh) |
445 | { |
446 | URLContext *h= *hh; |
447 | int ret = 0; |
448 | if (!h) |
449 | return 0; /* can happen when ffurl_open fails */ |
450 | |
451 | if (h->is_connected && h->prot->url_close) |
452 | ret = h->prot->url_close(h); |
453 | #if CONFIG_NETWORK |
454 | if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK) |
455 | ff_network_close(); |
456 | #endif |
457 | if (h->prot->priv_data_size) { |
458 | if (h->prot->priv_data_class) |
459 | av_opt_free(h->priv_data); |
460 | av_freep(&h->priv_data); |
461 | } |
462 | av_opt_free(h); |
463 | av_freep(hh); |
464 | return ret; |
465 | } |
466 | |
467 | int ffurl_close(URLContext *h) |
468 | { |
469 | return ffurl_closep(&h); |
470 | } |
471 | |
472 | |
473 | const char *avio_find_protocol_name(const char *url) |
474 | { |
475 | const URLProtocol *p = url_find_protocol(url); |
476 | |
477 | return p ? p->name : NULL; |
478 | } |
479 | |
480 | int avio_check(const char *url, int flags) |
481 | { |
482 | URLContext *h; |
483 | int ret = ffurl_alloc(&h, url, flags, NULL); |
484 | if (ret < 0) |
485 | return ret; |
486 | |
487 | if (h->prot->url_check) { |
488 | ret = h->prot->url_check(h, flags); |
489 | } else { |
490 | ret = ffurl_connect(h, NULL); |
491 | if (ret >= 0) |
492 | ret = flags; |
493 | } |
494 | |
495 | ffurl_close(h); |
496 | return ret; |
497 | } |
498 | |
499 | int avpriv_io_move(const char *url_src, const char *url_dst) |
500 | { |
501 | URLContext *h_src, *h_dst; |
502 | int ret = ffurl_alloc(&h_src, url_src, AVIO_FLAG_READ_WRITE, NULL); |
503 | if (ret < 0) |
504 | return ret; |
505 | ret = ffurl_alloc(&h_dst, url_dst, AVIO_FLAG_WRITE, NULL); |
506 | if (ret < 0) { |
507 | ffurl_close(h_src); |
508 | return ret; |
509 | } |
510 | |
511 | if (h_src->prot == h_dst->prot && h_src->prot->url_move) |
512 | ret = h_src->prot->url_move(h_src, h_dst); |
513 | else |
514 | ret = AVERROR(ENOSYS); |
515 | |
516 | ffurl_close(h_src); |
517 | ffurl_close(h_dst); |
518 | return ret; |
519 | } |
520 | |
521 | int avpriv_io_delete(const char *url) |
522 | { |
523 | URLContext *h; |
524 | int ret = ffurl_alloc(&h, url, AVIO_FLAG_WRITE, NULL); |
525 | if (ret < 0) |
526 | return ret; |
527 | |
528 | if (h->prot->url_delete) |
529 | ret = h->prot->url_delete(h); |
530 | else |
531 | ret = AVERROR(ENOSYS); |
532 | |
533 | ffurl_close(h); |
534 | return ret; |
535 | } |
536 | |
537 | int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options) |
538 | { |
539 | URLContext *h = NULL; |
540 | AVIODirContext *ctx = NULL; |
541 | int ret; |
542 | av_assert0(s); |
543 | |
544 | ctx = av_mallocz(sizeof(*ctx)); |
545 | if (!ctx) { |
546 | ret = AVERROR(ENOMEM); |
547 | goto fail; |
548 | } |
549 | |
550 | if ((ret = ffurl_alloc(&h, url, AVIO_FLAG_READ, NULL)) < 0) |
551 | goto fail; |
552 | |
553 | if (h->prot->url_open_dir && h->prot->url_read_dir && h->prot->url_close_dir) { |
554 | if (options && h->prot->priv_data_class && |
555 | (ret = av_opt_set_dict(h->priv_data, options)) < 0) |
556 | goto fail; |
557 | ret = h->prot->url_open_dir(h); |
558 | } else |
559 | ret = AVERROR(ENOSYS); |
560 | if (ret < 0) |
561 | goto fail; |
562 | |
563 | h->is_connected = 1; |
564 | ctx->url_context = h; |
565 | *s = ctx; |
566 | return 0; |
567 | |
568 | fail: |
569 | av_free(ctx); |
570 | *s = NULL; |
571 | ffurl_close(h); |
572 | return ret; |
573 | } |
574 | |
575 | int avio_read_dir(AVIODirContext *s, AVIODirEntry **next) |
576 | { |
577 | URLContext *h; |
578 | int ret; |
579 | |
580 | if (!s || !s->url_context) |
581 | return AVERROR(EINVAL); |
582 | h = s->url_context; |
583 | if ((ret = h->prot->url_read_dir(h, next)) < 0) |
584 | avio_free_directory_entry(next); |
585 | return ret; |
586 | } |
587 | |
588 | int avio_close_dir(AVIODirContext **s) |
589 | { |
590 | URLContext *h; |
591 | |
592 | av_assert0(s); |
593 | if (!(*s) || !(*s)->url_context) |
594 | return AVERROR(EINVAL); |
595 | h = (*s)->url_context; |
596 | h->prot->url_close_dir(h); |
597 | ffurl_close(h); |
598 | av_freep(s); |
599 | *s = NULL; |
600 | return 0; |
601 | } |
602 | |
603 | void avio_free_directory_entry(AVIODirEntry **entry) |
604 | { |
605 | if (!entry || !*entry) |
606 | return; |
607 | av_free((*entry)->name); |
608 | av_freep(entry); |
609 | } |
610 | |
611 | int64_t ffurl_size(URLContext *h) |
612 | { |
613 | int64_t pos, size; |
614 | |
615 | size = ffurl_seek(h, 0, AVSEEK_SIZE); |
616 | if (size < 0) { |
617 | pos = ffurl_seek(h, 0, SEEK_CUR); |
618 | if ((size = ffurl_seek(h, -1, SEEK_END)) < 0) |
619 | return size; |
620 | size++; |
621 | ffurl_seek(h, pos, SEEK_SET); |
622 | } |
623 | return size; |
624 | } |
625 | |
626 | int ffurl_get_file_handle(URLContext *h) |
627 | { |
628 | if (!h->prot->url_get_file_handle) |
629 | return -1; |
630 | return h->prot->url_get_file_handle(h); |
631 | } |
632 | |
633 | int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles) |
634 | { |
635 | if (!h->prot->url_get_multi_file_handle) { |
636 | if (!h->prot->url_get_file_handle) |
637 | return AVERROR(ENOSYS); |
638 | *handles = av_malloc(sizeof(**handles)); |
639 | if (!*handles) |
640 | return AVERROR(ENOMEM); |
641 | *numhandles = 1; |
642 | *handles[0] = h->prot->url_get_file_handle(h); |
643 | return 0; |
644 | } |
645 | return h->prot->url_get_multi_file_handle(h, handles, numhandles); |
646 | } |
647 | |
648 | int ffurl_get_short_seek(URLContext *h) |
649 | { |
650 | if (!h->prot->url_get_short_seek) |
651 | return AVERROR(ENOSYS); |
652 | return h->prot->url_get_short_seek(h); |
653 | } |
654 | |
655 | int ffurl_shutdown(URLContext *h, int flags) |
656 | { |
657 | if (!h->prot->url_shutdown) |
658 | return AVERROR(EINVAL); |
659 | return h->prot->url_shutdown(h, flags); |
660 | } |
661 | |
662 | int ff_check_interrupt(AVIOInterruptCB *cb) |
663 | { |
664 | int ret; |
665 | if (cb && cb->callback && (ret = cb->callback(cb->opaque))) |
666 | return ret; |
667 | return 0; |
668 | } |
669 |