mirror of https://code.videolan.org/videolan/vlc
335 lines
9.4 KiB
C
335 lines
9.4 KiB
C
/*****************************************************************************
|
|
* access.c
|
|
*****************************************************************************
|
|
* Copyright (C) 1999-2008 VLC authors and VideoLAN
|
|
*
|
|
* Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
|
|
*
|
|
* 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
|
|
* (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
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* 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.
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <vlc_common.h>
|
|
#include <vlc_url.h>
|
|
#include <vlc_modules.h>
|
|
#include <vlc_interrupt.h>
|
|
|
|
#include <libvlc.h>
|
|
#include "stream.h"
|
|
#include "input_internal.h"
|
|
|
|
struct vlc_access_private
|
|
{
|
|
module_t *module;
|
|
};
|
|
|
|
struct vlc_access_stream_private
|
|
{
|
|
input_thread_t *input;
|
|
};
|
|
|
|
static void vlc_access_Destroy(stream_t *access)
|
|
{
|
|
struct vlc_access_private *priv = vlc_stream_Private(access);
|
|
|
|
module_unneed(access, priv->module);
|
|
free(access->psz_filepath);
|
|
free(access->psz_name);
|
|
}
|
|
|
|
#define MAX_REDIR 5
|
|
|
|
static stream_t *accessNewAttachment(vlc_object_t *parent,
|
|
input_thread_t *input, const char *mrl)
|
|
{
|
|
if (!input)
|
|
return NULL;
|
|
|
|
input_attachment_t *attachment = input_GetAttachment(input, mrl + 13);
|
|
if (!attachment)
|
|
return NULL;
|
|
stream_t *stream = vlc_stream_AttachmentNew(parent, attachment);
|
|
if (!stream)
|
|
{
|
|
vlc_input_attachment_Release(attachment);
|
|
return NULL;
|
|
}
|
|
stream->psz_url = strdup(mrl);
|
|
if (!stream->psz_url)
|
|
{
|
|
vlc_stream_Delete(stream);
|
|
return NULL;
|
|
}
|
|
stream->psz_location = stream->psz_url + strlen("attachment://");
|
|
return stream;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* access_New:
|
|
*****************************************************************************/
|
|
static stream_t *access_New(vlc_object_t *parent, input_thread_t *input,
|
|
es_out_t *out, bool preparsing, const char *mrl)
|
|
{
|
|
struct vlc_access_private *priv;
|
|
char *redirv[MAX_REDIR];
|
|
unsigned redirc = 0;
|
|
|
|
if (strncmp(mrl, "attachment://", 13) == 0)
|
|
return accessNewAttachment(parent, input, mrl);
|
|
|
|
stream_t *access = vlc_stream_CustomNew(parent, vlc_access_Destroy,
|
|
sizeof (*priv), "access");
|
|
if (unlikely(access == NULL))
|
|
return NULL;
|
|
|
|
access->p_input_item = input ? input_GetItem(input) : NULL;
|
|
access->out = out;
|
|
access->psz_name = NULL;
|
|
access->psz_url = strdup(mrl);
|
|
access->psz_filepath = NULL;
|
|
access->b_preparsing = preparsing;
|
|
priv = vlc_stream_Private(access);
|
|
|
|
if (unlikely(access->psz_url == NULL))
|
|
goto error;
|
|
|
|
while (redirc < MAX_REDIR)
|
|
{
|
|
char *url = access->psz_url;
|
|
msg_Dbg(access, "creating access: %s", url);
|
|
|
|
const char *p = strchr(url, ':');
|
|
if (p == NULL)
|
|
goto error;
|
|
|
|
access->psz_name = strndup(url, p - url);
|
|
if (unlikely(access->psz_name == NULL))
|
|
goto error;
|
|
|
|
access->psz_location = p + (strncmp(p + 1, "//", 2) ? 1 : 3);
|
|
access->psz_filepath = vlc_uri2path(url);
|
|
if (access->psz_filepath == NULL)
|
|
{ /* FIXME: some access plugins want a file path for non-file MRLs */
|
|
char *file_url;
|
|
if (asprintf(&file_url, "file://%s", access->psz_location) >= 0)
|
|
{
|
|
access->psz_filepath = vlc_uri2path(file_url);
|
|
free(file_url);
|
|
}
|
|
}
|
|
if (access->psz_filepath != NULL)
|
|
msg_Dbg(access, " (path: %s)", access->psz_filepath);
|
|
|
|
priv->module = module_need(access, "access", access->psz_name, true);
|
|
if (priv->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;
|
|
}
|
|
|
|
free(access->psz_filepath);
|
|
free(access->psz_name);
|
|
access->psz_filepath = access->psz_name = NULL;
|
|
}
|
|
|
|
msg_Err(access, "too many redirections");
|
|
error:
|
|
while (redirc > 0)
|
|
free(redirv[--redirc]);
|
|
free(access->psz_filepath);
|
|
free(access->psz_name);
|
|
stream_CommonDelete(access);
|
|
return NULL;
|
|
}
|
|
|
|
stream_t *vlc_access_NewMRL(vlc_object_t *parent, const char *mrl)
|
|
{
|
|
return access_New(parent, NULL, NULL, false, mrl);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* access_vaDirectoryControlHelper:
|
|
*****************************************************************************/
|
|
int access_vaDirectoryControlHelper( stream_t *p_access, int i_query, va_list args )
|
|
{
|
|
VLC_UNUSED( p_access );
|
|
|
|
switch( i_query )
|
|
{
|
|
case STREAM_CAN_SEEK:
|
|
case STREAM_CAN_FASTSEEK:
|
|
case STREAM_CAN_PAUSE:
|
|
case STREAM_CAN_CONTROL_PACE:
|
|
*va_arg( args, bool* ) = false;
|
|
break;
|
|
case STREAM_GET_PTS_DELAY:
|
|
*va_arg( args, vlc_tick_t * ) = 0;
|
|
break;
|
|
default:
|
|
return VLC_EGENERIC;
|
|
}
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
/* Block access */
|
|
static block_t *AStreamReadBlock(stream_t *s, bool *restrict eof)
|
|
{
|
|
stream_t *access = s->p_sys;
|
|
block_t * block;
|
|
|
|
if (vlc_stream_Eof(access))
|
|
{
|
|
*eof = true;
|
|
return NULL;
|
|
}
|
|
if (vlc_killed())
|
|
return NULL;
|
|
|
|
block = vlc_stream_ReadBlock(access);
|
|
|
|
if (block != NULL)
|
|
{
|
|
struct vlc_access_stream_private *priv = vlc_stream_Private(s);
|
|
struct input_stats *stats =
|
|
priv->input ? input_priv(priv->input)->stats : NULL;
|
|
if (stats != NULL)
|
|
input_rate_Add(&stats->input_bitrate, block->i_buffer);
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
/* Read access */
|
|
static ssize_t AStreamReadStream(stream_t *s, void *buf, size_t len)
|
|
{
|
|
stream_t *access = s->p_sys;
|
|
|
|
if (vlc_stream_Eof(access))
|
|
return 0;
|
|
if (vlc_killed())
|
|
return -1;
|
|
|
|
ssize_t val = vlc_stream_ReadPartial(access, buf, len);
|
|
|
|
if (val > 0)
|
|
{
|
|
struct vlc_access_stream_private *priv = vlc_stream_Private(s);
|
|
struct input_stats *stats =
|
|
priv->input ? input_priv(priv->input)->stats : NULL;
|
|
if (stats != NULL)
|
|
input_rate_Add(&stats->input_bitrate, val);
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* Common */
|
|
static int AStreamSeek(stream_t *s, uint64_t offset)
|
|
{
|
|
stream_t *access = s->p_sys;
|
|
|
|
return vlc_stream_Seek(access, offset);
|
|
}
|
|
|
|
static int AStreamControl(stream_t *s, int cmd, va_list args)
|
|
{
|
|
stream_t *access = s->p_sys;
|
|
|
|
return vlc_stream_vaControl(access, cmd, args);
|
|
}
|
|
|
|
static void AStreamDestroy(stream_t *s)
|
|
{
|
|
stream_t *access = s->p_sys;
|
|
|
|
vlc_stream_Delete(access);
|
|
}
|
|
|
|
stream_t *stream_AccessNew(vlc_object_t *parent, input_thread_t *input,
|
|
es_out_t *out, bool preparsing, const char *url)
|
|
{
|
|
stream_t *access = access_New(parent, input, out, preparsing, url);
|
|
if (access == NULL)
|
|
return NULL;
|
|
|
|
stream_t *s;
|
|
|
|
// TODO: handle access->ops != NULL
|
|
// if ((access->ops != NULL && (access->ops->stream.block != NULL || access->ops->stream.read != NULL))
|
|
// || (access->ops == NULL && (access->pf_block != NULL || access->pf_read != NULL)))
|
|
if (access->ops == NULL && (access->pf_block != NULL || access->pf_read != NULL))
|
|
{
|
|
struct vlc_access_stream_private *priv;
|
|
s = vlc_stream_CustomNew(VLC_OBJECT(access), AStreamDestroy,
|
|
sizeof(*priv), "stream");
|
|
|
|
if (unlikely(s == NULL))
|
|
{
|
|
vlc_stream_Delete(access);
|
|
return NULL;
|
|
}
|
|
priv = vlc_stream_Private(s);
|
|
priv->input = input;
|
|
|
|
s->p_input_item = input ? input_GetItem(input) : NULL;
|
|
s->psz_url = strdup(access->psz_url);
|
|
if (unlikely(s->psz_url == NULL))
|
|
{
|
|
vlc_object_delete(s);
|
|
vlc_stream_Delete(access);
|
|
return NULL;
|
|
}
|
|
|
|
if (access->pf_block != NULL)
|
|
s->pf_block = AStreamReadBlock;
|
|
if (access->pf_read != NULL)
|
|
s->pf_read = AStreamReadStream;
|
|
|
|
s->pf_seek = AStreamSeek;
|
|
s->pf_control = AStreamControl;
|
|
s->p_sys = access;
|
|
|
|
s = stream_FilterChainNew(s, "prefetch,cache");
|
|
}
|
|
else
|
|
s = access;
|
|
|
|
return s;
|
|
}
|