3 * Copyright (c) 2001 Fabrice Bellard
5 * This file is part of FFmpeg.
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.
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.
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
22 #include "libavutil/avstring.h"
23 #include "libavutil/dict.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/time.h"
26 #include "os_support.h"
33 static URLProtocol
*first_protocol
= NULL
;
35 URLProtocol
*ffurl_protocol_next(const URLProtocol
*prev
)
37 return prev
? prev
->next
: first_protocol
;
40 /** @name Logging context. */
42 static const char *urlcontext_to_name(void *ptr
)
44 URLContext
*h
= (URLContext
*)ptr
;
51 static void *urlcontext_child_next(void *obj
, void *prev
)
54 if (!prev
&& h
->priv_data
&& h
->prot
->priv_data_class
)
59 static const AVClass
*urlcontext_child_class_next(const AVClass
*prev
)
61 URLProtocol
*p
= NULL
;
63 /* find the protocol that corresponds to prev */
64 while (prev
&& (p
= ffurl_protocol_next(p
)))
65 if (p
->priv_data_class
== prev
)
68 /* find next protocol with priv options */
69 while (p
= ffurl_protocol_next(p
))
70 if (p
->priv_data_class
)
71 return p
->priv_data_class
;
75 static const AVOption options
[] = { { NULL
} };
76 const AVClass ffurl_context_class
= {
77 .class_name
= "URLContext",
78 .item_name
= urlcontext_to_name
,
80 .version
= LIBAVUTIL_VERSION_INT
,
81 .child_next
= urlcontext_child_next
,
82 .child_class_next
= urlcontext_child_class_next
,
86 const char *avio_enum_protocols(void **opaque
, int output
)
89 *opaque
= ffurl_protocol_next(*opaque
);
92 if ((output
&& p
->url_write
) || (!output
&& p
->url_read
))
94 return avio_enum_protocols(opaque
, output
);
97 int ffurl_register_protocol(URLProtocol
*protocol
)
104 protocol
->next
= NULL
;
108 static int url_alloc_for_protocol(URLContext
**puc
, struct URLProtocol
*up
,
109 const char *filename
, int flags
,
110 const AVIOInterruptCB
*int_cb
)
116 if (up
->flags
& URL_PROTOCOL_FLAG_NETWORK
&& !ff_network_init())
119 if ((flags
& AVIO_FLAG_READ
) && !up
->url_read
) {
120 av_log(NULL
, AV_LOG_ERROR
,
121 "Impossible to open the '%s' protocol for reading\n", up
->name
);
124 if ((flags
& AVIO_FLAG_WRITE
) && !up
->url_write
) {
125 av_log(NULL
, AV_LOG_ERROR
,
126 "Impossible to open the '%s' protocol for writing\n", up
->name
);
129 uc
= av_mallocz(sizeof(URLContext
) + strlen(filename
) + 1);
131 err
= AVERROR(ENOMEM
);
134 uc
->av_class
= &ffurl_context_class
;
135 uc
->filename
= (char *)&uc
[1];
136 strcpy(uc
->filename
, filename
);
139 uc
->is_streamed
= 0; /* default = not streamed */
140 uc
->max_packet_size
= 0; /* default: stream file */
141 if (up
->priv_data_size
) {
142 uc
->priv_data
= av_mallocz(up
->priv_data_size
);
143 if (!uc
->priv_data
) {
144 err
= AVERROR(ENOMEM
);
147 if (up
->priv_data_class
) {
148 int proto_len
= strlen(up
->name
);
149 char *start
= strchr(uc
->filename
, ',');
150 *(const AVClass
**)uc
->priv_data
= up
->priv_data_class
;
151 av_opt_set_defaults(uc
->priv_data
);
152 if(!strncmp(up
->name
, uc
->filename
, proto_len
) && uc
->filename
+ proto_len
== start
){
158 while(ret
>= 0 && (key
= strchr(p
, sep
)) && p
<key
&& (val
= strchr(key
+1, sep
))){
160 ret
= av_opt_set(uc
->priv_data
, p
, key
+1, 0);
161 if (ret
== AVERROR_OPTION_NOT_FOUND
)
162 av_log(uc
, AV_LOG_ERROR
, "Key '%s' not found.\n", p
);
167 av_log(uc
, AV_LOG_ERROR
, "Error parsing options string %s\n", start
);
168 av_freep(&uc
->priv_data
);
170 err
= AVERROR(EINVAL
);
173 memmove(start
, key
+1, strlen(key
));
178 uc
->interrupt_callback
= *int_cb
;
185 av_freep(&uc
->priv_data
);
188 if (up
->flags
& URL_PROTOCOL_FLAG_NETWORK
)
194 int ffurl_connect(URLContext
*uc
, AVDictionary
**options
)
197 uc
->prot
->url_open2
? uc
->prot
->url_open2(uc
,
201 uc
->prot
->url_open(uc
, uc
->filename
, uc
->flags
);
204 uc
->is_connected
= 1;
205 /* We must be careful here as ffurl_seek() could be slow,
206 * for example for http */
207 if ((uc
->flags
& AVIO_FLAG_WRITE
) || !strcmp(uc
->prot
->name
, "file"))
208 if (!uc
->is_streamed
&& ffurl_seek(uc
, 0, SEEK_SET
) < 0)
213 #define URL_SCHEME_CHARS \
214 "abcdefghijklmnopqrstuvwxyz" \
215 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
218 static struct URLProtocol
*url_find_protocol(const char *filename
)
220 URLProtocol
*up
= NULL
;
221 char proto_str
[128], proto_nested
[128], *ptr
;
222 size_t proto_len
= strspn(filename
, URL_SCHEME_CHARS
);
224 if (filename
[proto_len
] != ':' &&
225 (filename
[proto_len
] != ',' || !strchr(filename
+ proto_len
+ 1, ':')) ||
226 is_dos_path(filename
))
227 strcpy(proto_str
, "file");
229 av_strlcpy(proto_str
, filename
,
230 FFMIN(proto_len
+ 1, sizeof(proto_str
)));
232 if ((ptr
= strchr(proto_str
, ',')))
234 av_strlcpy(proto_nested
, proto_str
, sizeof(proto_nested
));
235 if ((ptr
= strchr(proto_nested
, '+')))
238 while (up
= ffurl_protocol_next(up
)) {
239 if (!strcmp(proto_str
, up
->name
))
241 if (up
->flags
& URL_PROTOCOL_FLAG_NESTED_SCHEME
&&
242 !strcmp(proto_nested
, up
->name
))
249 int ffurl_alloc(URLContext
**puc
, const char *filename
, int flags
,
250 const AVIOInterruptCB
*int_cb
)
252 URLProtocol
*p
= NULL
;
254 if (!first_protocol
) {
255 av_log(NULL
, AV_LOG_WARNING
, "No URL Protocols are registered. "
256 "Missing call to av_register_all()?\n");
259 p
= url_find_protocol(filename
);
261 return url_alloc_for_protocol(puc
, p
, filename
, flags
, int_cb
);
264 if (av_strstart(filename
, "https:", NULL
))
265 av_log(NULL
, AV_LOG_WARNING
, "https protocol not found, recompile with openssl or gnutls enabled.\n");
266 return AVERROR_PROTOCOL_NOT_FOUND
;
269 int ffurl_open(URLContext
**puc
, const char *filename
, int flags
,
270 const AVIOInterruptCB
*int_cb
, AVDictionary
**options
)
272 int ret
= ffurl_alloc(puc
, filename
, flags
, int_cb
);
275 if (options
&& (*puc
)->prot
->priv_data_class
&&
276 (ret
= av_opt_set_dict((*puc
)->priv_data
, options
)) < 0)
278 if ((ret
= av_opt_set_dict(*puc
, options
)) < 0)
280 ret
= ffurl_connect(*puc
, options
);
289 static inline int retry_transfer_wrapper(URLContext
*h
, uint8_t *buf
,
290 int size
, int size_min
,
291 int (*transfer_func
)(URLContext
*h
,
296 int fast_retries
= 5;
297 int64_t wait_since
= 0;
300 while (len
< size_min
) {
301 if (ff_check_interrupt(&h
->interrupt_callback
))
303 ret
= transfer_func(h
, buf
+ len
, size
- len
);
304 if (ret
== AVERROR(EINTR
))
306 if (h
->flags
& AVIO_FLAG_NONBLOCK
)
308 if (ret
== AVERROR(EAGAIN
)) {
315 wait_since
= av_gettime_relative();
316 else if (av_gettime_relative() > wait_since
+ h
->rw_timeout
)
322 return (ret
< 0 && ret
!= AVERROR_EOF
) ? ret
: len
;
324 fast_retries
= FFMAX(fast_retries
, 2);
330 int ffurl_read(URLContext
*h
, unsigned char *buf
, int size
)
332 if (!(h
->flags
& AVIO_FLAG_READ
))
334 return retry_transfer_wrapper(h
, buf
, size
, 1, h
->prot
->url_read
);
337 int ffurl_read_complete(URLContext
*h
, unsigned char *buf
, int size
)
339 if (!(h
->flags
& AVIO_FLAG_READ
))
341 return retry_transfer_wrapper(h
, buf
, size
, size
, h
->prot
->url_read
);
344 int ffurl_write(URLContext
*h
, const unsigned char *buf
, int size
)
346 if (!(h
->flags
& AVIO_FLAG_WRITE
))
348 /* avoid sending too big packets */
349 if (h
->max_packet_size
&& size
> h
->max_packet_size
)
352 return retry_transfer_wrapper(h
, (unsigned char *)buf
, size
, size
, (void*)h
->prot
->url_write
);
355 int64_t ffurl_seek(URLContext
*h
, int64_t pos
, int whence
)
359 if (!h
->prot
->url_seek
)
360 return AVERROR(ENOSYS
);
361 ret
= h
->prot
->url_seek(h
, pos
, whence
& ~AVSEEK_FORCE
);
365 int ffurl_closep(URLContext
**hh
)
370 return 0; /* can happen when ffurl_open fails */
372 if (h
->is_connected
&& h
->prot
->url_close
)
373 ret
= h
->prot
->url_close(h
);
375 if (h
->prot
->flags
& URL_PROTOCOL_FLAG_NETWORK
)
378 if (h
->prot
->priv_data_size
) {
379 if (h
->prot
->priv_data_class
)
380 av_opt_free(h
->priv_data
);
381 av_freep(&h
->priv_data
);
387 int ffurl_close(URLContext
*h
)
389 return ffurl_closep(&h
);
393 const char *avio_find_protocol_name(const char *url
)
395 URLProtocol
*p
= url_find_protocol(url
);
397 return p
? p
->name
: NULL
;
400 int avio_check(const char *url
, int flags
)
403 int ret
= ffurl_alloc(&h
, url
, flags
, NULL
);
407 if (h
->prot
->url_check
) {
408 ret
= h
->prot
->url_check(h
, flags
);
410 ret
= ffurl_connect(h
, NULL
);
419 int64_t ffurl_size(URLContext
*h
)
423 size
= ffurl_seek(h
, 0, AVSEEK_SIZE
);
425 pos
= ffurl_seek(h
, 0, SEEK_CUR
);
426 if ((size
= ffurl_seek(h
, -1, SEEK_END
)) < 0)
429 ffurl_seek(h
, pos
, SEEK_SET
);
434 int ffurl_get_file_handle(URLContext
*h
)
436 if (!h
->prot
->url_get_file_handle
)
438 return h
->prot
->url_get_file_handle(h
);
441 int ffurl_get_multi_file_handle(URLContext
*h
, int **handles
, int *numhandles
)
443 if (!h
->prot
->url_get_multi_file_handle
) {
444 if (!h
->prot
->url_get_file_handle
)
445 return AVERROR(ENOSYS
);
446 *handles
= av_malloc(sizeof(**handles
));
448 return AVERROR(ENOMEM
);
450 *handles
[0] = h
->prot
->url_get_file_handle(h
);
453 return h
->prot
->url_get_multi_file_handle(h
, handles
, numhandles
);
456 int ffurl_shutdown(URLContext
*h
, int flags
)
458 if (!h
->prot
->url_shutdown
)
459 return AVERROR(EINVAL
);
460 return h
->prot
->url_shutdown(h
, flags
);
463 int ff_check_interrupt(AVIOInterruptCB
*cb
)
466 if (cb
&& cb
->callback
&& (ret
= cb
->callback(cb
->opaque
)))