access.c 8.6 KB
Newer Older
1 2 3
/*****************************************************************************
 * access.c
 *****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
4
 * Copyright (C) 1999-2008 VLC authors and VideoLAN
Antoine Cellerier's avatar
Antoine Cellerier committed
5
 * $Id$
6
 *
7
 * Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
9 10 11
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
12 13 14 15
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
16 17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
18
 *
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
19 20 21
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23
 *****************************************************************************/

24 25 26 27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

28
#include <assert.h>
29 30
#include <stdlib.h>
#include <string.h>
31

32
#include <vlc_common.h>
33
#include <vlc_url.h>
34
#include <vlc_modules.h>
35 36 37 38 39
#include <vlc_interrupt.h>

#include <libvlc.h>
#include "stream.h"
#include "input_internal.h"
40 41

/* Decode URL (which has had its scheme stripped earlier) to a file path. */
42
char *get_path(const char *location)
43 44 45
{
    char *url, *path;

Pierre Ynard's avatar
Pierre Ynard committed
46
    /* Prepending "file://" is a bit hackish. But then again, we do not want
47
     * to hard-code the list of schemes that use file paths in vlc_uri2path().
48 49 50 51
     */
    if (asprintf(&url, "file://%s", location) == -1)
        return NULL;

52
    path = vlc_uri2path (url);
53 54 55
    free (url);
    return path;
}
56

57 58 59 60 61 62 63
static void vlc_access_Destroy(stream_t *access)
{
    module_unneed(access, access->p_module);
    free(access->psz_filepath);
    free(access->psz_name);
}

64 65
#define MAX_REDIR 5

66
/*****************************************************************************
67
 * access_New:
68
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
69
static stream_t *access_New(vlc_object_t *parent, input_thread_t *input,
70
                            bool preparsing, const char *mrl)
71
{
72 73
    char *redirv[MAX_REDIR];
    unsigned redirc = 0;
74

75
    stream_t *access = vlc_stream_CommonNew(parent, vlc_access_Destroy);
76 77 78
    if (unlikely(access == NULL))
        return NULL;

79
    access->p_input = input;
80
    access->psz_name = NULL;
81 82
    access->psz_url = strdup(mrl);
    access->psz_filepath = NULL;
83
    access->b_preparsing = preparsing;
84

85 86
    if (unlikely(access->psz_url == NULL))
        goto error;
87

88
    while (redirc < MAX_REDIR)
89
    {
90 91 92 93 94 95 96
        char *url = access->psz_url;
        msg_Dbg(access, "creating access: %s", url);

        const char *p = strstr(url, "://");
        if (p == NULL)
            goto error;

97 98
        access->psz_name = strndup(url, p - url);
        if (unlikely(access->psz_name == NULL))
99 100 101 102 103 104 105
            goto error;

        access->psz_location = p + 3;
        access->psz_filepath = get_path(access->psz_location);
        if (access->psz_filepath != NULL)
            msg_Dbg(access, " (path: %s)", access->psz_filepath);

106
        access->p_module = module_need(access, "access", access->psz_name,
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
                                       true);
        if (access->p_module != NULL) /* success */
        {
            while (redirc > 0)
                free(redirv[--redirc]);

            assert(access->pf_control != NULL);
            return access;
        }

        if (access->psz_url == url) /* failure (no redirection) */
            goto error;

        /* redirection */
        msg_Dbg(access, "redirecting to: %s", access->psz_url);
        redirv[redirc++] = url;

        for (unsigned j = 0; j < redirc; j++)
            if (!strcmp(redirv[j], access->psz_url))
            {
                msg_Err(access, "redirection loop");
                goto error;
            }
130 131 132 133

        free(access->psz_filepath);
        free(access->psz_name);
        access->psz_filepath = access->psz_name = NULL;
134
    }
135 136 137 138 139 140

    msg_Err(access, "too many redirections");
error:
    while (redirc > 0)
        free(redirv[--redirc]);
    free(access->psz_filepath);
141
    free(access->psz_name);
142
    stream_CommonDelete(access);
143
    return NULL;
144 145
}

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
146
stream_t *vlc_access_NewMRL(vlc_object_t *parent, const char *mrl)
147
{
148
    return access_New(parent, NULL, false, mrl);
149 150
}

151 152 153
/*****************************************************************************
 * access_vaDirectoryControlHelper:
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
154
int access_vaDirectoryControlHelper( stream_t *p_access, int i_query, va_list args )
155 156 157 158 159
{
    VLC_UNUSED( p_access );

    switch( i_query )
    {
160 161 162 163
        case STREAM_CAN_SEEK:
        case STREAM_CAN_FASTSEEK:
        case STREAM_CAN_PAUSE:
        case STREAM_CAN_CONTROL_PACE:
164 165
            *va_arg( args, bool* ) = false;
            break;
166
        case STREAM_GET_PTS_DELAY:
167 168
            *va_arg( args, int64_t * ) = 0;
            break;
169
        case STREAM_IS_DIRECTORY:
170
            break;
171 172 173 174 175
        default:
            return VLC_EGENERIC;
     }
     return VLC_SUCCESS;
}
176

177
static int AStreamNoReadDir(stream_t *s, input_item_node_t *p_node)
178
{
179 180
    (void) s; (void) p_node;
    return VLC_EGENERIC;;
181 182
}

183
/* Block access */
184
static block_t *AStreamReadBlock(stream_t *s, bool *restrict eof)
185
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
186
    stream_t *access = s->p_sys;
187
    input_thread_t *input = s->p_input;
188
    block_t * block;
189

190
    if (vlc_stream_Eof(access))
191
    {
192 193
        *eof = true;
        return NULL;
194
    }
195 196
    if (vlc_killed())
        return NULL;
197

198 199 200
    block = vlc_stream_ReadBlock(access);

    if (block != NULL && input != NULL)
201 202 203
    {
        uint64_t total;

204 205 206 207 208 209
        vlc_mutex_lock(&input_priv(input)->counters.counters_lock);
        stats_Update(input_priv(input)->counters.p_read_bytes,
                     block->i_buffer, &total);
        stats_Update(input_priv(input)->counters.p_input_bitrate, total, NULL);
        stats_Update(input_priv(input)->counters.p_read_packets, 1, NULL);
        vlc_mutex_unlock(&input_priv(input)->counters.counters_lock);
210 211
    }

212
    return block;
213 214 215 216 217
}

/* Read access */
static ssize_t AStreamReadStream(stream_t *s, void *buf, size_t len)
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
218
    stream_t *access = s->p_sys;
219
    input_thread_t *input = s->p_input;
220

221 222 223 224
    if (vlc_stream_Eof(access))
        return 0;
    if (vlc_killed())
        return -1;
225

226
    ssize_t val = vlc_stream_ReadPartial(access, buf, len);
227

228
    if (val > 0 && input != NULL)
229 230 231
    {
        uint64_t total;

232 233 234 235 236
        vlc_mutex_lock(&input_priv(input)->counters.counters_lock);
        stats_Update(input_priv(input)->counters.p_read_bytes, val, &total);
        stats_Update(input_priv(input)->counters.p_input_bitrate, total, NULL);
        stats_Update(input_priv(input)->counters.p_read_packets, 1, NULL);
        vlc_mutex_unlock(&input_priv(input)->counters.counters_lock);
237 238 239 240 241 242
    }

    return val;
}

/* Directory */
243
static int AStreamReadDir(stream_t *s, input_item_node_t *p_node)
244
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
245
    stream_t *access = s->p_sys;
246

247
    return access->pf_readdir(access, p_node);
248 249 250
}

/* Common */
251 252
static int AStreamSeek(stream_t *s, uint64_t offset)
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
253
    stream_t *access = s->p_sys;
254

255
    return vlc_stream_Seek(access, offset);
256 257
}

258 259
static int AStreamControl(stream_t *s, int cmd, va_list args)
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
260
    stream_t *access = s->p_sys;
261

262
    return vlc_stream_vaControl(access, cmd, args);
263 264 265 266
}

static void AStreamDestroy(stream_t *s)
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
267
    stream_t *access = s->p_sys;
268

269
    vlc_stream_Delete(access);
270 271 272
}

stream_t *stream_AccessNew(vlc_object_t *parent, input_thread_t *input,
273
                           bool preparsing, const char *url)
274
{
275
    stream_t *s = vlc_stream_CommonNew(parent, AStreamDestroy);
276 277 278
    if (unlikely(s == NULL))
        return NULL;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
279
    stream_t *access = access_New(VLC_OBJECT(s), input, preparsing, url);
280 281 282 283 284
    if (access == NULL)
    {
        stream_CommonDelete(s);
        return NULL;
    }
285

286
    s->p_input = input;
287
    s->psz_url = strdup(access->psz_url);
288

289
    const char *cachename;
290

291
    if (access->pf_block != NULL)
292
    {
293
        s->pf_block = AStreamReadBlock;
294
        cachename = "prefetch,cache_block";
295
    }
296
    else
297
    if (access->pf_read != NULL)
298
    {
299
        s->pf_read = AStreamReadStream;
300
        cachename = "prefetch,cache_read";
301
    }
302 303 304 305 306
    else
    {
        cachename = NULL;
    }

307
    if (access->pf_readdir != NULL)
308 309 310 311
        s->pf_readdir = AStreamReadDir;
    else
        s->pf_readdir = AStreamNoReadDir;

312
    s->pf_seek    = AStreamSeek;
313
    s->pf_control = AStreamControl;
314
    s->p_sys      = access;
315

316 317
    if (cachename != NULL)
        s = stream_FilterChainNew(s, cachename);
318
    return stream_FilterAutoNew(s);
319
}