/***************************************************************************** * access.c ***************************************************************************** * Copyright (C) 1999-2008 VLC authors and VideoLAN * * Author: Laurent Aimar * * 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 #include #include #include #include #include #include #include #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; }