Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 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 "os_support.h"
27
#include "avformat.h"
28
#if CONFIG_NETWORK
29
#include "network.h"
30
#endif
31
#include "url.h"
32
 
33
static URLProtocol *first_protocol = NULL;
34
 
35
URLProtocol *ffurl_protocol_next(URLProtocol *prev)
36
{
37
    return prev ? prev->next : first_protocol;
38
}
39
 
40
/** @name Logging context. */
41
/*@{*/
42
static const char *urlcontext_to_name(void *ptr)
43
{
44
    URLContext *h = (URLContext *)ptr;
45
    if(h->prot) return h->prot->name;
46
    else        return "NULL";
47
}
48
 
49
static void *urlcontext_child_next(void *obj, void *prev)
50
{
51
    URLContext *h = obj;
52
    if (!prev && h->priv_data && h->prot->priv_data_class)
53
        return h->priv_data;
54
    return NULL;
55
}
56
 
57
static const AVClass *urlcontext_child_class_next(const AVClass *prev)
58
{
59
    URLProtocol *p = NULL;
60
 
61
    /* find the protocol that corresponds to prev */
62
    while (prev && (p = ffurl_protocol_next(p)))
63
        if (p->priv_data_class == prev)
64
            break;
65
 
66
    /* find next protocol with priv options */
67
    while (p = ffurl_protocol_next(p))
68
        if (p->priv_data_class)
69
            return p->priv_data_class;
70
    return NULL;
71
 
72
}
73
 
74
static const AVOption options[] = {{NULL}};
75
const AVClass ffurl_context_class = {
76
    .class_name     = "URLContext",
77
    .item_name      = urlcontext_to_name,
78
    .option         = options,
79
    .version        = LIBAVUTIL_VERSION_INT,
80
    .child_next     = urlcontext_child_next,
81
    .child_class_next = urlcontext_child_class_next,
82
};
83
/*@}*/
84
 
85
 
86
const char *avio_enum_protocols(void **opaque, int output)
87
{
88
    URLProtocol *p;
89
    *opaque = ffurl_protocol_next(*opaque);
90
    if (!(p = *opaque)) return NULL;
91
    if ((output && p->url_write) || (!output && p->url_read))
92
        return p->name;
93
    return avio_enum_protocols(opaque, output);
94
}
95
 
96
int ffurl_register_protocol(URLProtocol *protocol, int size)
97
{
98
    URLProtocol **p;
99
    if (size < sizeof(URLProtocol)) {
100
        URLProtocol* temp = av_mallocz(sizeof(URLProtocol));
101
        memcpy(temp, protocol, size);
102
        protocol = temp;
103
    }
104
    p = &first_protocol;
105
    while (*p != NULL) p = &(*p)->next;
106
    *p = protocol;
107
    protocol->next = NULL;
108
    return 0;
109
}
110
 
111
static int url_alloc_for_protocol (URLContext **puc, struct URLProtocol *up,
112
                                   const char *filename, int flags,
113
                                   const AVIOInterruptCB *int_cb)
114
{
115
    URLContext *uc;
116
    int err;
117
 
118
#if CONFIG_NETWORK
119
    if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init())
120
        return AVERROR(EIO);
121
#endif
122
    if ((flags & AVIO_FLAG_READ) && !up->url_read) {
123
        av_log(NULL, AV_LOG_ERROR,
124
               "Impossible to open the '%s' protocol for reading\n", up->name);
125
        return AVERROR(EIO);
126
    }
127
    if ((flags & AVIO_FLAG_WRITE) && !up->url_write) {
128
        av_log(NULL, AV_LOG_ERROR,
129
               "Impossible to open the '%s' protocol for writing\n", up->name);
130
        return AVERROR(EIO);
131
    }
132
    uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);
133
    if (!uc) {
134
        err = AVERROR(ENOMEM);
135
        goto fail;
136
    }
137
    uc->av_class = &ffurl_context_class;
138
    uc->filename = (char *) &uc[1];
139
    strcpy(uc->filename, filename);
140
    uc->prot = up;
141
    uc->flags = flags;
142
    uc->is_streamed = 0; /* default = not streamed */
143
    uc->max_packet_size = 0; /* default: stream file */
144
    if (up->priv_data_size) {
145
        uc->priv_data = av_mallocz(up->priv_data_size);
146
        if (!uc->priv_data) {
147
            err = AVERROR(ENOMEM);
148
            goto fail;
149
        }
150
        if (up->priv_data_class) {
151
            int proto_len= strlen(up->name);
152
            char *start = strchr(uc->filename, ',');
153
            *(const AVClass**)uc->priv_data = up->priv_data_class;
154
            av_opt_set_defaults(uc->priv_data);
155
            if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){
156
                int ret= 0;
157
                char *p= start;
158
                char sep= *++p;
159
                char *key, *val;
160
                p++;
161
                while(ret >= 0 && (key= strchr(p, sep)) && p
162
                    *val= *key= 0;
163
                    ret= av_opt_set(uc->priv_data, p, key+1, 0);
164
                    if (ret == AVERROR_OPTION_NOT_FOUND)
165
                        av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p);
166
                    *val= *key= sep;
167
                    p= val+1;
168
                }
169
                if(ret<0 || p!=key){
170
                    av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start);
171
                    av_freep(&uc->priv_data);
172
                    av_freep(&uc);
173
                    err = AVERROR(EINVAL);
174
                    goto fail;
175
                }
176
                memmove(start, key+1, strlen(key));
177
            }
178
        }
179
    }
180
    if (int_cb)
181
        uc->interrupt_callback = *int_cb;
182
 
183
    *puc = uc;
184
    return 0;
185
 fail:
186
    *puc = NULL;
187
    if (uc)
188
        av_freep(&uc->priv_data);
189
    av_freep(&uc);
190
#if CONFIG_NETWORK
191
    if (up->flags & URL_PROTOCOL_FLAG_NETWORK)
192
        ff_network_close();
193
#endif
194
    return err;
195
}
196
 
197
int ffurl_connect(URLContext* uc, AVDictionary **options)
198
{
199
    int err =
200
        uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) :
201
        uc->prot->url_open(uc, uc->filename, uc->flags);
202
    if (err)
203
        return err;
204
    uc->is_connected = 1;
205
    //We must be careful here as ffurl_seek() could be slow, for example for http
206
    if(   (uc->flags & AVIO_FLAG_WRITE)
207
       || !strcmp(uc->prot->name, "file"))
208
        if(!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
209
            uc->is_streamed= 1;
210
    return 0;
211
}
212
 
213
#define URL_SCHEME_CHARS                        \
214
    "abcdefghijklmnopqrstuvwxyz"                \
215
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"                \
216
    "0123456789+-."
217
 
218
int ffurl_alloc(URLContext **puc, const char *filename, int flags,
219
                const AVIOInterruptCB *int_cb)
220
{
221
    URLProtocol *up = NULL;
222
    char proto_str[128], proto_nested[128], *ptr;
223
    size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
224
 
225
    if (!first_protocol) {
226
        av_log(NULL, AV_LOG_WARNING, "No URL Protocols are registered. "
227
                                     "Missing call to av_register_all()?\n");
228
    }
229
 
230
    if (filename[proto_len] != ':' &&
231
        (filename[proto_len] != ',' || !strchr(filename + proto_len + 1, ':')) ||
232
        is_dos_path(filename))
233
        strcpy(proto_str, "file");
234
    else
235
        av_strlcpy(proto_str, filename, FFMIN(proto_len+1, sizeof(proto_str)));
236
 
237
    if ((ptr = strchr(proto_str, ',')))
238
        *ptr = '\0';
239
    av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
240
    if ((ptr = strchr(proto_nested, '+')))
241
        *ptr = '\0';
242
 
243
    while (up = ffurl_protocol_next(up)) {
244
        if (!strcmp(proto_str, up->name))
245
            return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
246
        if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
247
            !strcmp(proto_nested, up->name))
248
            return url_alloc_for_protocol (puc, up, filename, flags, int_cb);
249
    }
250
    *puc = NULL;
251
    if (!strcmp("https", proto_str))
252
        av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile with openssl or gnutls enabled.\n");
253
    return AVERROR_PROTOCOL_NOT_FOUND;
254
}
255
 
256
int ffurl_open(URLContext **puc, const char *filename, int flags,
257
               const AVIOInterruptCB *int_cb, AVDictionary **options)
258
{
259
    int ret = ffurl_alloc(puc, filename, flags, int_cb);
260
    if (ret)
261
        return ret;
262
    if (options && (*puc)->prot->priv_data_class &&
263
        (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
264
        goto fail;
265
    ret = ffurl_connect(*puc, options);
266
    if (!ret)
267
        return 0;
268
fail:
269
    ffurl_close(*puc);
270
    *puc = NULL;
271
    return ret;
272
}
273
 
274
static inline int retry_transfer_wrapper(URLContext *h, unsigned char *buf, int size, int size_min,
275
                                         int (*transfer_func)(URLContext *h, unsigned char *buf, int size))
276
{
277
    int ret, len;
278
    int fast_retries = 5;
279
    int64_t wait_since = 0;
280
 
281
    len = 0;
282
    while (len < size_min) {
283
        if (ff_check_interrupt(&h->interrupt_callback))
284
            return AVERROR_EXIT;
285
        ret = transfer_func(h, buf+len, size-len);
286
        if (ret == AVERROR(EINTR))
287
            continue;
288
        if (h->flags & AVIO_FLAG_NONBLOCK)
289
            return ret;
290
        if (ret == AVERROR(EAGAIN)) {
291
            ret = 0;
292
            if (fast_retries) {
293
                fast_retries--;
294
            } else {
295
                if (h->rw_timeout) {
296
                    if (!wait_since)
297
                        wait_since = av_gettime();
298
                    else if (av_gettime() > wait_since + h->rw_timeout)
299
                        return AVERROR(EIO);
300
                }
301
                av_usleep(1000);
302
            }
303
        } else if (ret < 1)
304
            return (ret < 0 && ret != AVERROR_EOF) ? ret : len;
305
        if (ret)
306
           fast_retries = FFMAX(fast_retries, 2);
307
        len += ret;
308
    }
309
    return len;
310
}
311
 
312
int ffurl_read(URLContext *h, unsigned char *buf, int size)
313
{
314
    if (!(h->flags & AVIO_FLAG_READ))
315
        return AVERROR(EIO);
316
    return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);
317
}
318
 
319
int ffurl_read_complete(URLContext *h, unsigned char *buf, int size)
320
{
321
    if (!(h->flags & AVIO_FLAG_READ))
322
        return AVERROR(EIO);
323
    return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read);
324
}
325
 
326
int ffurl_write(URLContext *h, const unsigned char *buf, int size)
327
{
328
    if (!(h->flags & AVIO_FLAG_WRITE))
329
        return AVERROR(EIO);
330
    /* avoid sending too big packets */
331
    if (h->max_packet_size && size > h->max_packet_size)
332
        return AVERROR(EIO);
333
 
334
    return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, (void*)h->prot->url_write);
335
}
336
 
337
int64_t ffurl_seek(URLContext *h, int64_t pos, int whence)
338
{
339
    int64_t ret;
340
 
341
    if (!h->prot->url_seek)
342
        return AVERROR(ENOSYS);
343
    ret = h->prot->url_seek(h, pos, whence & ~AVSEEK_FORCE);
344
    return ret;
345
}
346
 
347
int ffurl_closep(URLContext **hh)
348
{
349
    URLContext *h= *hh;
350
    int ret = 0;
351
    if (!h) return 0; /* can happen when ffurl_open fails */
352
 
353
    if (h->is_connected && h->prot->url_close)
354
        ret = h->prot->url_close(h);
355
#if CONFIG_NETWORK
356
    if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK)
357
        ff_network_close();
358
#endif
359
    if (h->prot->priv_data_size) {
360
        if (h->prot->priv_data_class)
361
            av_opt_free(h->priv_data);
362
        av_freep(&h->priv_data);
363
    }
364
    av_freep(hh);
365
    return ret;
366
}
367
 
368
int ffurl_close(URLContext *h)
369
{
370
    return ffurl_closep(&h);
371
}
372
 
373
 
374
int avio_check(const char *url, int flags)
375
{
376
    URLContext *h;
377
    int ret = ffurl_alloc(&h, url, flags, NULL);
378
    if (ret)
379
        return ret;
380
 
381
    if (h->prot->url_check) {
382
        ret = h->prot->url_check(h, flags);
383
    } else {
384
        ret = ffurl_connect(h, NULL);
385
        if (ret >= 0)
386
            ret = flags;
387
    }
388
 
389
    ffurl_close(h);
390
    return ret;
391
}
392
 
393
int64_t ffurl_size(URLContext *h)
394
{
395
    int64_t pos, size;
396
 
397
    size= ffurl_seek(h, 0, AVSEEK_SIZE);
398
    if(size<0){
399
        pos = ffurl_seek(h, 0, SEEK_CUR);
400
        if ((size = ffurl_seek(h, -1, SEEK_END)) < 0)
401
            return size;
402
        size++;
403
        ffurl_seek(h, pos, SEEK_SET);
404
    }
405
    return size;
406
}
407
 
408
int ffurl_get_file_handle(URLContext *h)
409
{
410
    if (!h->prot->url_get_file_handle)
411
        return -1;
412
    return h->prot->url_get_file_handle(h);
413
}
414
 
415
int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles)
416
{
417
    if (!h->prot->url_get_multi_file_handle) {
418
        if (!h->prot->url_get_file_handle)
419
            return AVERROR(ENOSYS);
420
        *handles = av_malloc(sizeof(**handles));
421
        if (!*handles)
422
            return AVERROR(ENOMEM);
423
        *numhandles = 1;
424
        *handles[0] = h->prot->url_get_file_handle(h);
425
        return 0;
426
    }
427
    return h->prot->url_get_multi_file_handle(h, handles, numhandles);
428
}
429
 
430
int ffurl_shutdown(URLContext *h, int flags)
431
{
432
    if (!h->prot->url_shutdown)
433
        return AVERROR(EINVAL);
434
    return h->prot->url_shutdown(h, flags);
435
}
436
 
437
int ff_check_interrupt(AVIOInterruptCB *cb)
438
{
439
    int ret;
440
    if (cb && cb->callback && (ret = cb->callback(cb->opaque)))
441
        return ret;
442
    return 0;
443
}