Commit | Line | Data |
---|---|---|
de6d9b64 FB |
1 | /* |
2 | * Unbuffered io for ffmpeg system | |
19720f15 | 3 | * Copyright (c) 2001 Fabrice Bellard |
de6d9b64 | 4 | * |
b78e7197 DB |
5 | * This file is part of FFmpeg. |
6 | * | |
7 | * FFmpeg is free software; you can redistribute it and/or | |
19720f15 FB |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either | |
b78e7197 | 10 | * version 2.1 of the License, or (at your option) any later version. |
de6d9b64 | 11 | * |
b78e7197 | 12 | * FFmpeg is distributed in the hope that it will be useful, |
de6d9b64 | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19720f15 FB |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. | |
de6d9b64 | 16 | * |
19720f15 | 17 | * You should have received a copy of the GNU Lesser General Public |
b78e7197 | 18 | * License along with FFmpeg; if not, write to the Free Software |
5509bffa | 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
de6d9b64 | 20 | */ |
245976da | 21 | |
f1d2b572 RD |
22 | /* needed for usleep() */ |
23 | #define _XOPEN_SOURCE 600 | |
24 | #include <unistd.h> | |
245976da DB |
25 | #include "libavutil/avstring.h" |
26 | #include "libavcodec/opt.h" | |
bc574408 | 27 | #include "os_support.h" |
de6d9b64 | 28 | #include "avformat.h" |
67d4b3f2 MS |
29 | #if CONFIG_NETWORK |
30 | #include "network.h" | |
31 | #endif | |
5acef35f BA |
32 | |
33 | #if LIBAVFORMAT_VERSION_MAJOR >= 53 | |
34 | /** @name Logging context. */ | |
35 | /*@{*/ | |
36 | static const char *urlcontext_to_name(void *ptr) | |
37 | { | |
38 | URLContext *h = (URLContext *)ptr; | |
39 | if(h->prot) return h->prot->name; | |
40 | else return "NULL"; | |
41 | } | |
42 | static const AVOption options[] = {{NULL}}; | |
43 | static const AVClass urlcontext_class = | |
2308b6c1 | 44 | { "URLContext", urlcontext_to_name, options, LIBAVUTIL_VERSION_INT }; |
5acef35f BA |
45 | /*@}*/ |
46 | #endif | |
de6d9b64 | 47 | |
019ac05a FB |
48 | static int default_interrupt_cb(void); |
49 | ||
de6d9b64 | 50 | URLProtocol *first_protocol = NULL; |
019ac05a | 51 | URLInterruptCB *url_interrupt_cb = default_interrupt_cb; |
de6d9b64 | 52 | |
84be6e72 MN |
53 | URLProtocol *av_protocol_next(URLProtocol *p) |
54 | { | |
55 | if(p) return p->next; | |
56 | else return first_protocol; | |
57 | } | |
58 | ||
9b07a2dc | 59 | int av_register_protocol2(URLProtocol *protocol, int size) |
de6d9b64 FB |
60 | { |
61 | URLProtocol **p; | |
9b07a2dc MS |
62 | if (size < sizeof(URLProtocol)) { |
63 | URLProtocol* temp = av_mallocz(sizeof(URLProtocol)); | |
64 | memcpy(temp, protocol, size); | |
65 | protocol = temp; | |
66 | } | |
de6d9b64 FB |
67 | p = &first_protocol; |
68 | while (*p != NULL) p = &(*p)->next; | |
69 | *p = protocol; | |
70 | protocol->next = NULL; | |
71 | return 0; | |
72 | } | |
73 | ||
65c40e4e | 74 | #if LIBAVFORMAT_VERSION_MAJOR < 53 |
9b07a2dc MS |
75 | /* The layout of URLProtocol as of when major was bumped to 52 */ |
76 | struct URLProtocol_compat { | |
77 | const char *name; | |
78 | int (*url_open)(URLContext *h, const char *filename, int flags); | |
79 | int (*url_read)(URLContext *h, unsigned char *buf, int size); | |
80 | int (*url_write)(URLContext *h, unsigned char *buf, int size); | |
81 | int64_t (*url_seek)(URLContext *h, int64_t pos, int whence); | |
82 | int (*url_close)(URLContext *h); | |
83 | struct URLProtocol *next; | |
84 | }; | |
85 | ||
86 | int av_register_protocol(URLProtocol *protocol) | |
87 | { | |
88 | return av_register_protocol2(protocol, sizeof(struct URLProtocol_compat)); | |
89 | } | |
90 | ||
65c40e4e SS |
91 | int register_protocol(URLProtocol *protocol) |
92 | { | |
93 | return av_register_protocol(protocol); | |
94 | } | |
95 | #endif | |
96 | ||
ffbb289a | 97 | static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up, |
02174293 | 98 | const char *filename, int flags) |
de6d9b64 FB |
99 | { |
100 | URLContext *uc; | |
de6d9b64 FB |
101 | int err; |
102 | ||
67d4b3f2 MS |
103 | #if CONFIG_NETWORK |
104 | if (!ff_network_init()) | |
105 | return AVERROR(EIO); | |
106 | #endif | |
31277aeb | 107 | uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1); |
8a9488b5 | 108 | if (!uc) { |
8fa36ae0 | 109 | err = AVERROR(ENOMEM); |
8a9488b5 FB |
110 | goto fail; |
111 | } | |
5acef35f BA |
112 | #if LIBAVFORMAT_VERSION_MAJOR >= 53 |
113 | uc->av_class = &urlcontext_class; | |
114 | #endif | |
62181004 | 115 | uc->filename = (char *) &uc[1]; |
f746a046 | 116 | strcpy(uc->filename, filename); |
de6d9b64 FB |
117 | uc->prot = up; |
118 | uc->flags = flags; | |
119 | uc->is_streamed = 0; /* default = not streamed */ | |
8a9488b5 | 120 | uc->max_packet_size = 0; /* default: stream file */ |
735cf6b2 MS |
121 | if (up->priv_data_size) { |
122 | uc->priv_data = av_mallocz(up->priv_data_size); | |
123 | if (up->priv_data_class) { | |
805488f0 | 124 | *(const AVClass**)uc->priv_data = up->priv_data_class; |
735cf6b2 MS |
125 | av_opt_set_defaults(uc->priv_data); |
126 | } | |
127 | } | |
abbae514 | 128 | |
de6d9b64 FB |
129 | *puc = uc; |
130 | return 0; | |
8a9488b5 FB |
131 | fail: |
132 | *puc = NULL; | |
67d4b3f2 MS |
133 | #if CONFIG_NETWORK |
134 | ff_network_close(); | |
135 | #endif | |
8a9488b5 | 136 | return err; |
de6d9b64 FB |
137 | } |
138 | ||
ffbb289a MS |
139 | int url_connect(URLContext* uc) |
140 | { | |
141 | int err = uc->prot->url_open(uc, uc->filename, uc->flags); | |
142 | if (err) | |
143 | return err; | |
144 | uc->is_connected = 1; | |
145 | //We must be careful here as url_seek() could be slow, for example for http | |
146 | if( (uc->flags & (URL_WRONLY | URL_RDWR)) | |
147 | || !strcmp(uc->prot->name, "file")) | |
148 | if(!uc->is_streamed && url_seek(uc, 0, SEEK_SET) < 0) | |
149 | uc->is_streamed= 1; | |
150 | return 0; | |
151 | } | |
152 | ||
153 | int url_open_protocol (URLContext **puc, struct URLProtocol *up, | |
154 | const char *filename, int flags) | |
155 | { | |
156 | int ret; | |
157 | ||
158 | ret = url_alloc_for_protocol(puc, up, filename, flags); | |
159 | if (ret) | |
160 | goto fail; | |
161 | ret = url_connect(*puc); | |
162 | if (!ret) | |
163 | return 0; | |
164 | fail: | |
165 | url_close(*puc); | |
166 | *puc = NULL; | |
167 | return ret; | |
168 | } | |
169 | ||
170 | int url_alloc(URLContext **puc, const char *filename, int flags) | |
ba99cfc2 RB |
171 | { |
172 | URLProtocol *up; | |
173 | const char *p; | |
174 | char proto_str[128], *q; | |
175 | ||
176 | p = filename; | |
177 | q = proto_str; | |
178 | while (*p != '\0' && *p != ':') { | |
179 | /* protocols can only contain alphabetic chars */ | |
180 | if (!isalpha(*p)) | |
181 | goto file_proto; | |
182 | if ((q - proto_str) < sizeof(proto_str) - 1) | |
183 | *q++ = *p; | |
184 | p++; | |
185 | } | |
186 | /* if the protocol has length 1, we consider it is a dos drive */ | |
bc574408 | 187 | if (*p == '\0' || is_dos_path(filename)) { |
ba99cfc2 RB |
188 | file_proto: |
189 | strcpy(proto_str, "file"); | |
190 | } else { | |
191 | *q = '\0'; | |
192 | } | |
193 | ||
194 | up = first_protocol; | |
195 | while (up != NULL) { | |
196 | if (!strcmp(proto_str, up->name)) | |
ffbb289a | 197 | return url_alloc_for_protocol (puc, up, filename, flags); |
ba99cfc2 RB |
198 | up = up->next; |
199 | } | |
200 | *puc = NULL; | |
201 | return AVERROR(ENOENT); | |
202 | } | |
203 | ||
ffbb289a MS |
204 | int url_open(URLContext **puc, const char *filename, int flags) |
205 | { | |
206 | int ret = url_alloc(puc, filename, flags); | |
207 | if (ret) | |
208 | return ret; | |
209 | ret = url_connect(*puc); | |
210 | if (!ret) | |
211 | return 0; | |
212 | fail: | |
213 | url_close(*puc); | |
214 | *puc = NULL; | |
215 | return ret; | |
216 | } | |
217 | ||
de6d9b64 FB |
218 | int url_read(URLContext *h, unsigned char *buf, int size) |
219 | { | |
220 | int ret; | |
221 | if (h->flags & URL_WRONLY) | |
6f3e0b21 | 222 | return AVERROR(EIO); |
de6d9b64 FB |
223 | ret = h->prot->url_read(h, buf, size); |
224 | return ret; | |
225 | } | |
226 | ||
0e848977 KS |
227 | int url_read_complete(URLContext *h, unsigned char *buf, int size) |
228 | { | |
229 | int ret, len; | |
f1d2b572 | 230 | int fast_retries = 5; |
0e848977 KS |
231 | |
232 | len = 0; | |
233 | while (len < size) { | |
234 | ret = url_read(h, buf+len, size-len); | |
ddb901b7 RD |
235 | if (ret == AVERROR(EAGAIN)) { |
236 | ret = 0; | |
f1d2b572 RD |
237 | if (fast_retries) |
238 | fast_retries--; | |
239 | else | |
240 | usleep(1000); | |
ddb901b7 RD |
241 | } else if (ret < 1) |
242 | return ret < 0 ? ret : len; | |
f1d2b572 RD |
243 | if (ret) |
244 | fast_retries = FFMAX(fast_retries, 2); | |
0e848977 KS |
245 | len += ret; |
246 | } | |
247 | return len; | |
248 | } | |
249 | ||
27241cbf | 250 | int url_write(URLContext *h, const unsigned char *buf, int size) |
de6d9b64 FB |
251 | { |
252 | int ret; | |
8a9488b5 | 253 | if (!(h->flags & (URL_WRONLY | URL_RDWR))) |
6f3e0b21 | 254 | return AVERROR(EIO); |
8a9488b5 FB |
255 | /* avoid sending too big packets */ |
256 | if (h->max_packet_size && size > h->max_packet_size) | |
6f3e0b21 | 257 | return AVERROR(EIO); |
de6d9b64 FB |
258 | ret = h->prot->url_write(h, buf, size); |
259 | return ret; | |
260 | } | |
261 | ||
bc5c918e | 262 | int64_t url_seek(URLContext *h, int64_t pos, int whence) |
de6d9b64 | 263 | { |
bc5c918e | 264 | int64_t ret; |
de6d9b64 FB |
265 | |
266 | if (!h->prot->url_seek) | |
28894105 | 267 | return AVERROR(ENOSYS); |
493f54ad | 268 | ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE); |
de6d9b64 FB |
269 | return ret; |
270 | } | |
271 | ||
de6d9b64 FB |
272 | int url_close(URLContext *h) |
273 | { | |
7a813b36 | 274 | int ret = 0; |
346db900 | 275 | if (!h) return 0; /* can happen when url_open fails */ |
de6d9b64 | 276 | |
ffbb289a | 277 | if (h->is_connected && h->prot->url_close) |
7a813b36 | 278 | ret = h->prot->url_close(h); |
67d4b3f2 MS |
279 | #if CONFIG_NETWORK |
280 | ff_network_close(); | |
281 | #endif | |
735cf6b2 MS |
282 | if (h->prot->priv_data_size) |
283 | av_free(h->priv_data); | |
1ea4f593 | 284 | av_free(h); |
de6d9b64 FB |
285 | return ret; |
286 | } | |
287 | ||
288 | int url_exist(const char *filename) | |
289 | { | |
290 | URLContext *h; | |
291 | if (url_open(&h, filename, URL_RDONLY) < 0) | |
292 | return 0; | |
293 | url_close(h); | |
294 | return 1; | |
295 | } | |
296 | ||
bc5c918e | 297 | int64_t url_filesize(URLContext *h) |
de6d9b64 | 298 | { |
bc5c918e | 299 | int64_t pos, size; |
115329f1 | 300 | |
8e287af0 MN |
301 | size= url_seek(h, 0, AVSEEK_SIZE); |
302 | if(size<0){ | |
1ae2c5f2 | 303 | pos = url_seek(h, 0, SEEK_CUR); |
eabbae73 RB |
304 | if ((size = url_seek(h, -1, SEEK_END)) < 0) |
305 | return size; | |
306 | size++; | |
1ae2c5f2 | 307 | url_seek(h, pos, SEEK_SET); |
8e287af0 | 308 | } |
de6d9b64 FB |
309 | return size; |
310 | } | |
8a9488b5 | 311 | |
f0a80394 RB |
312 | int url_get_file_handle(URLContext *h) |
313 | { | |
314 | if (!h->prot->url_get_file_handle) | |
315 | return -1; | |
316 | return h->prot->url_get_file_handle(h); | |
317 | } | |
318 | ||
8a9488b5 FB |
319 | int url_get_max_packet_size(URLContext *h) |
320 | { | |
321 | return h->max_packet_size; | |
322 | } | |
f746a046 FB |
323 | |
324 | void url_get_filename(URLContext *h, char *buf, int buf_size) | |
325 | { | |
75e61b0e | 326 | av_strlcpy(buf, h->filename, buf_size); |
f746a046 | 327 | } |
019ac05a FB |
328 | |
329 | ||
330 | static int default_interrupt_cb(void) | |
331 | { | |
332 | return 0; | |
333 | } | |
334 | ||
019ac05a FB |
335 | void url_set_interrupt_cb(URLInterruptCB *interrupt_cb) |
336 | { | |
337 | if (!interrupt_cb) | |
338 | interrupt_cb = default_interrupt_cb; | |
339 | url_interrupt_cb = interrupt_cb; | |
340 | } | |
536333a0 | 341 | |
502bdf68 | 342 | int av_url_read_pause(URLContext *h, int pause) |
536333a0 BA |
343 | { |
344 | if (!h->prot->url_read_pause) | |
345 | return AVERROR(ENOSYS); | |
502bdf68 | 346 | return h->prot->url_read_pause(h, pause); |
536333a0 BA |
347 | } |
348 | ||
bc5c918e | 349 | int64_t av_url_read_seek(URLContext *h, |
536333a0 BA |
350 | int stream_index, int64_t timestamp, int flags) |
351 | { | |
352 | if (!h->prot->url_read_seek) | |
353 | return AVERROR(ENOSYS); | |
354 | return h->prot->url_read_seek(h, stream_index, timestamp, flags); | |
355 | } |