[vlc-devel] [PATCH 1/2] access/stream_filter: add libarchive

Rémi Denis-Courmont remi at remlab.net
Thu Jul 24 18:41:35 CEST 2014


Le vendredi 25 juillet 2014, 00:24:43 Francois Cartegnie a écrit :
> Allows decompression and access through rar, lha, tar, ...
> Mostly unseekable.
> 
> ---
>  configure.ac                     |   5 +
>  modules/access/Makefile.am       |   4 +
>  modules/access/archive/access.c  | 179 ++++++++++++++++++++
>  modules/access/archive/archive.h |  36 ++++
>  modules/access/archive/module.c  | 110 ++++++++++++
>  modules/access/archive/stream.c  | 354
> +++++++++++++++++++++++++++++++++++++++ po/POTFILES.in                   | 
>  1 +
>  7 files changed, 689 insertions(+)
>  create mode 100644 modules/access/archive/access.c
>  create mode 100644 modules/access/archive/archive.h
>  create mode 100644 modules/access/archive/module.c
>  create mode 100644 modules/access/archive/stream.c
> 
> diff --git a/configure.ac b/configure.ac
> index de07e28..416b21b 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -1584,6 +1584,11 @@ dnl
>  EXTEND_HELP_STRING([Input plugins:])
> 
>  dnl
> +dnl  libarchive access module
> +dnl
> +PKG_ENABLE_MODULES_VLC([ARCHIVE], [access_archive], [libarchive >= 2.8.5],
> (libarchive support), [auto]) +
> +dnl
>  dnl  live555 input
>  dnl
>  AC_ARG_ENABLE(live555,
> diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am
> index 1094192..0d77c87 100644
> --- a/modules/access/Makefile.am
> +++ b/modules/access/Makefile.am
> @@ -64,6 +64,10 @@ libzip_plugin_la_LIBADD += libunzip.la
>  endif
>  endif
> 
> +libaccess_archive_plugin_la_SOURCES = access/archive/access.c
> access/archive/stream.c \
> +					access/archive/archive.h

I don't really get why you need both a stream and an access. 

> access/archive/module.c
> +libaccess_archive_plugin_la_LIBADD = $(ARCHIVE_LIBS)
> +access_LTLIBRARIES += libaccess_archive_plugin.la

Test without libarchive...

> 
>  ### Audio capture ###
> 
> diff --git a/modules/access/archive/access.c
> b/modules/access/archive/access.c new file mode 100644
> index 0000000..c8d4532
> --- /dev/null
> +++ b/modules/access/archive/access.c
> @@ -0,0 +1,179 @@
> +/**************************************************************************
> *** + * access.c: libarchive based access
> +
> ***************************************************************************
> ** + * Copyright (C) 2014 Videolan Team
> + *
> + * 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. +
> ***************************************************************************
> **/ +
> +#include "archive.h"
> +
> +#include <vlc_access.h>
> +#include <vlc_url.h>
> +
> +#include <archive.h>
> +#include <archive_entry.h>
> +
> +struct access_sys_t
> +{
> +    struct archive *p_archive;
> +    struct archive_entry *p_entry;
> +    bool b_seekable;
> +};
> +
> +static ssize_t Read(access_t *p_access, uint8_t *p_data, size_t i_size)
> +{
> +    access_sys_t *p_sys = p_access->p_sys;
> +
> +    size_t i_read = 0;
> +
> +    i_read = archive_read_data(p_sys->p_archive, p_data, i_size);
> +
> +    if (i_read > 0)
> +        p_access->info.i_pos += i_read;
> +
> +    if (i_size > 0 && i_read <= 0)
> +        p_access->info.b_eof = true;
> +
> +    return i_read;
> +}
> +
> +static int Seek(access_t *p_access, uint64_t i_pos)
> +{
> +    access_sys_t *p_sys = p_access->p_sys;
> +
> +    int64_t i_ret = archive_seek_data(p_sys->p_archive, i_pos, SEEK_SET);
> +    if ( i_ret < ARCHIVE_OK )
> +        return VLC_EGENERIC;
> +    p_access->info.i_pos = i_ret;
> +
> +    return VLC_SUCCESS;
> +}
> +
> +static int Control(access_t *p_access, int i_query, va_list args)
> +{
> +    access_sys_t *p_sys = p_access->p_sys;
> +
> +    switch (i_query)
> +    {
> +    case ACCESS_CAN_SEEK:
> +    case ACCESS_CAN_FASTSEEK:
> +        *va_arg(args, bool *)= p_sys->b_seekable;
> +        break;
> +    case ACCESS_CAN_PAUSE:
> +    case ACCESS_CAN_CONTROL_PACE:
> +        *va_arg(args, bool *) = true;
> +        break;
> +    case ACCESS_GET_SIZE:
> +        *va_arg(args, uint64_t *) = archive_entry_size(p_sys->p_entry);
> +        break;
> +    case ACCESS_GET_PTS_DELAY:
> +        *va_arg(args, int64_t *) = DEFAULT_PTS_DELAY;
> +        break;
> +    case ACCESS_SET_PAUSE_STATE:
> +        break;
> +
> +    default:
> +        return VLC_EGENERIC;
> +    }
> +    return VLC_SUCCESS;
> +}
> +
> +int AccessOpen(vlc_object_t *p_object)
> +{
> +    access_t *p_access = (access_t*)p_object;
> +
> +    if (!strchr(p_access->psz_location, '|'))

This should always return NULL since pipe is not an URL character. I'd say 
pick another separator.

> +        return VLC_EGENERIC;
> +
> +    char *psz_base = strdup(p_access->psz_location);
> +    if (!psz_base)
> +        return VLC_EGENERIC;
> +    char *psz_name = strchr(psz_base, '|');
> +    *psz_name++ = '\0';
> +
> +    if (decode_URI(psz_base) == NULL)
> +    {
> +        free(psz_base);
> +        return VLC_EGENERIC;
> +    }

Isn't that computing the same value as p_access->psz_filepath ?

> +
> +    access_sys_t *p_sys = p_access->p_sys = calloc(1,
> sizeof(access_sys_t));
> +    p_sys->p_archive = archive_read_new();
> +    if (!p_sys->p_archive)
> +    {
> +        msg_Err(p_access, archive_error_string(p_sys->p_archive));

"%s". Better safe than sorry.

> +        goto error;
> +    }
> +
> +    EnableArchiveFormats(p_sys->p_archive);
> +
> +    if (archive_read_open_filename(p_sys->p_archive, psz_base, 4096) !=
> ARCHIVE_OK) +    {
> +        msg_Err(p_access, archive_error_string(p_sys->p_archive));
> +        goto error;
> +    }
> +
> +    bool b_present = false;
> +    while(archive_read_next_header(p_sys->p_archive, &p_sys->p_entry) ==
> ARCHIVE_OK) +    {
> +        if (!strcmp(archive_entry_pathname(p_sys->p_entry), psz_name))
> +        {
> +            b_present = true;
> +            break;
> +        }
> +        msg_Dbg(p_access, "skipping entry %s != %s",
> archive_entry_pathname(p_sys->p_entry), psz_name); +    }
> +
> +    if (!b_present)
> +    {
> +        msg_Err(p_access, "entry '%s' not found in archive", psz_name);
> +        /* entry not found */
> +        goto error;
> +    }
> +
> +    msg_Dbg(p_access, "reading entry %s %"PRId64,
> archive_entry_pathname(p_sys->p_entry), +                                  
>                archive_entry_size(p_sys->p_entry)); +
> +    /* try to guess if it is seekable or not */
> +    p_sys->b_seekable = (archive_seek_data(p_sys->p_archive, 0, SEEK_SET)
> >= 0); +
> +    p_access->pf_read    = Read;
> +    p_access->pf_block   = NULL; /* libarchive's zerocopy keeps owning
> block :/ */ +    p_access->pf_control = Control;
> +    p_access->pf_seek    = Seek;
> +
> +    access_InitFields(p_access);
> +
> +    free(psz_base);
> +
> +    return VLC_SUCCESS;
> +
> +error:
> +    free(psz_base);
> +    AccessClose(p_object);
> +    return VLC_EGENERIC;
> +}
> +
> +void AccessClose(vlc_object_t *p_object)
> +{
> +    access_t *p_access = (access_t*)p_object;
> +    access_sys_t *p_sys = p_access->p_sys;
> +
> +    if (p_sys->p_archive)
> +        archive_read_free(p_sys->p_archive);
> +
> +    free(p_sys);
> +}
> diff --git a/modules/access/archive/archive.h
> b/modules/access/archive/archive.h new file mode 100644
> index 0000000..e716440
> --- /dev/null
> +++ b/modules/access/archive/archive.h
> @@ -0,0 +1,36 @@
> +/**************************************************************************
> *** + * archive.h: libarchive access & stream filter
> +
> ***************************************************************************
> ** + * Copyright (C) 2014 Videolan Team
> + *
> + * 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 <vlc_common.h>
> +
> +int AccessOpen(vlc_object_t *object);
> +void AccessClose(vlc_object_t *object);
> +
> +int StreamOpen(vlc_object_t *object);
> +void StreamClose(vlc_object_t *object);
> +
> +bool ProbeArchiveFormat(stream_t *p_stream);
> +
> +struct archive;
> +void EnableArchiveFormats(struct archive *p_archive);
> diff --git a/modules/access/archive/module.c
> b/modules/access/archive/module.c new file mode 100644
> index 0000000..df81ec5
> --- /dev/null
> +++ b/modules/access/archive/module.c
> @@ -0,0 +1,110 @@
> +/**************************************************************************
> *** + * module.c: libarchive based stream filter
> +
> ***************************************************************************
> ** + * Copyright (C) 2014 Videolan Team
> + *
> + * 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. +
> ***************************************************************************
> **/ +
> +#include "archive.h"
> +
> +#include <vlc_plugin.h>
> +#include <vlc_stream.h>
> +
> +#include <archive.h>
> +
> +/**************************************************************************
> ** + * Module descriptor
> +
> ***************************************************************************
> **/ +vlc_module_begin()
> +    set_shortname( "libarchive" )
> +    set_category( CAT_INPUT )
> +    set_subcategory( SUBCAT_INPUT_ACCESS )
> +    set_description( N_( "libarchive access" ) )
> +    set_capability( "access", 0 )
> +    add_shortcut( "archive" )
> +    set_callbacks( AccessOpen, AccessClose )
> +    add_submodule()
> +        set_shortname( "libarchive" )
> +        set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
> +        set_description( N_( "libarchive stream filter" ) )
> +        set_capability( "stream_filter", 14 ) /* less than rar and gzip */
> +        set_callbacks( StreamOpen, StreamClose )
> +vlc_module_end()
> +
> +bool ProbeArchiveFormat(stream_t *p_stream)
> +{
> +    struct
> +    {
> +        const uint16_t i_offset;
> +        const uint8_t  i_length;
> +        const char * const p_bytes;
> +    } const magicbytes[9] = {
> +        /* keep heaviest at top */
> +        { 257, 5, "ustar" },        //TAR
> +        { 0,   7, "Rar!\x1A\x07" }, //RAR
> +        { 0,   4, "xar!" },         //XAR
> +        { 2,   3, "-lh" },          //LHA/LHZ
> +        { 0,   3, "PAX" },          //PAX
> +        { 0,   6, "070707" },       //CPIO
> +        { 0,   6, "070701" },       //CPIO
> +        { 0,   6, "070702" },       //CPIO
> +        { 0,   4, "MSCH" },         //CAB
> +    };
> +
> +    const uint8_t *p_peek;
> +    int i_peek = stream_Peek(p_stream, &p_peek, magicbytes[0].i_offset +
> magicbytes[0].i_length); +    for(int i=0; i<9;i++)
> +    {
> +        if (i_peek <= magicbytes[i].i_offset + magicbytes[i].i_length)
> +            continue;
> +        else if ( !memcmp(p_peek + magicbytes[i].i_offset,
> +                          magicbytes[i].p_bytes,
> +                          magicbytes[i].i_length) )
> +            return true;
> +    }
> +
> +    return false;
> +}
> +
> +void EnableArchiveFormats(struct archive *p_archive)
> +{
> +    //    archive_read_support_filter_bzip2(p_archive);
> +    //    archive_read_support_filter_compress(p_archive);
> +    //    archive_read_support_filter_gzip(p_archive);
> +    //    archive_read_support_filter_grzip(p_archive);
> +    //    archive_read_support_filter_lrzip(p_archive);
> +    //    archive_read_support_filter_lzip(p_archive);
> +    archive_read_support_filter_lzma(p_archive);
> +    archive_read_support_filter_lzop(p_archive);
> +    archive_read_support_filter_none(p_archive);
> +    archive_read_support_filter_rpm(p_archive);
> +    archive_read_support_filter_uu(p_archive);
> +    archive_read_support_filter_xz(p_archive);
> +
> +    //    archive_read_support_format_7zip(p_archive);
> +    archive_read_support_format_ar(p_archive);
> +    archive_read_support_format_cab(p_archive);
> +    archive_read_support_format_cpio(p_archive);
> +    archive_read_support_format_gnutar(p_archive);
> +    //    archive_read_support_format_iso9660(p_archive);
> +    archive_read_support_format_lha(p_archive);
> +    archive_read_support_format_mtree(p_archive);
> +    archive_read_support_format_rar(p_archive);
> +    archive_read_support_format_raw(p_archive);
> +    archive_read_support_format_tar(p_archive);
> +    archive_read_support_format_xar(p_archive);
> +    //    archive_read_support_format_zip(p_archive);
> +}
> diff --git a/modules/access/archive/stream.c
> b/modules/access/archive/stream.c new file mode 100644
> index 0000000..9c52b7e
> --- /dev/null
> +++ b/modules/access/archive/stream.c
> @@ -0,0 +1,354 @@
> +/**************************************************************************
> *** + * stream.c: libarchive based stream filter
> +
> ***************************************************************************
> ** + * Copyright (C) 2014 Videolan Team
> + *
> + * 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. +
> ***************************************************************************
> **/ +
> +#include "archive.h"
> +
> +#include <vlc_stream.h>
> +#include <vlc_url.h>
> +
> +#include <archive.h>
> +#include <archive_entry.h>
> +
> +#define READ_SIZE 8192
> +
> +struct stream_sys_t
> +{
> +    struct archive *p_archive;
> +    struct archive_entry *p_entry;
> +    size_t i_pos;
> +    size_t i_len;
> +    void  *p_peek_buffer;
> +    size_t i_peek_buffer;
> +    bool b_seekable;
> +    bool b_source_canseek;
> +    uint8_t buffer[READ_SIZE];
> +};
> +
> +static int Read(stream_t *p_stream, void *p_data, unsigned int i_size)
> +{
> +    stream_sys_t *p_sys = p_stream->p_sys;
> +
> +    if (p_sys->i_len && p_sys->i_len - p_sys->i_pos < UINT32_MAX)
> +        i_size = __MIN( i_size, p_sys->i_len - p_sys->i_pos );
> +
> +    unsigned int i_total = 0;
> +
> +    if (p_sys->p_peek_buffer)
> +    {
> +        if (likely(i_size >= p_sys->i_peek_buffer))
> +        {
> +            if(p_data)
> +                memcpy(p_data, p_sys->p_peek_buffer, p_sys->i_peek_buffer);
> +            i_size -= p_sys->i_peek_buffer;
> +            i_total += p_sys->i_peek_buffer;
> +            p_sys->i_peek_buffer = 0;
> +            FREENULL(p_sys->p_peek_buffer);
> +        }
> +        else
> +        {
> +            if (p_data)
> +                memcpy(p_data, p_sys->p_peek_buffer, i_size);
> +            memmove(p_sys->p_peek_buffer,
> (uint8_t*)p_sys->p_peek_buffer+i_size, p_sys->i_peek_buffer - i_size); +   
>         i_total += i_size;
> +            p_sys->i_peek_buffer -= i_size;
> +            i_size = 0;
> +        }
> +    }
> +
> +    if (i_size)
> +    {
> +        uint8_t *p_dummy_buffer = (p_data) ? NULL : malloc(i_size); /*
> required for skip reads */ +        uint8_t *p_buffer = (p_data) ? (uint8_t
> *)p_data + i_total : p_dummy_buffer; +
> +        if (!p_buffer)
> +            return 0;
> +
> +        ssize_t i_read = archive_read_data(p_sys->p_archive, p_buffer,
> i_size); +        if (i_read > 0)
> +            i_total += i_read;
> +
> +        free(p_dummy_buffer);
> +    }
> +
> +    p_sys->i_pos += i_total;
> +
> +    return i_total;
> +}
> +
> +static int Peek(stream_t *p_stream, const uint8_t **pp_peek, unsigned int
> i_peek) +{
> +    stream_sys_t *p_sys = p_stream->p_sys;
> +
> +    if (!p_sys->p_peek_buffer || i_peek > p_sys->i_peek_buffer)
> +    {
> +        void *p_realloced = realloc(p_sys->p_peek_buffer, i_peek);
> +        if (!p_realloced)
> +            return 0;
> +
> +        p_sys->p_peek_buffer = p_realloced;
> +    }
> +
> +    if (i_peek > p_sys->i_peek_buffer)
> +    {
> +        ssize_t i_read = archive_read_data(p_sys->p_archive,
> +                                           (uint8_t*)p_sys->p_peek_buffer +
> p_sys->i_peek_buffer, +                                           i_peek -
> p_sys->i_peek_buffer); +        if (i_read < 0)
> +            return 0;
> +
> +        p_sys->i_peek_buffer += i_read;
> +    }
> +
> +    *pp_peek = p_sys->p_peek_buffer;
> +
> +    return p_sys->i_peek_buffer;
> +}
> +
> +static int Control(stream_t *p_stream, int i_query, va_list args)
> +{
> +    stream_sys_t *p_sys = p_stream->p_sys;
> +
> +    switch( i_query )
> +    {
> +        case STREAM_GET_POSITION:
> +            *va_arg( args, uint64_t* ) = p_sys->i_pos;
> +            break;
> +
> +        case STREAM_GET_SIZE:
> +            *va_arg( args, uint64_t* ) = p_sys->i_len;
> +            break;
> +
> +        case STREAM_CAN_SEEK:
> +            *va_arg( args, bool* ) = p_sys->b_seekable;
> +            break;
> +
> +        case STREAM_CAN_FASTSEEK:
> +            if (!p_sys->b_seekable)
> +            {
> +                *va_arg( args, bool* ) = false;
> +                break;
> +            }
> +            else
> +                return stream_vaControl( p_stream->p_source, i_query, args
> ); +
> +        case STREAM_SET_POSITION:
> +        {
> +            if (!p_sys->b_seekable)
> +                return VLC_EGENERIC;
> +            uint64_t i_pos = va_arg( args, uint64_t );
> +            int64_t i_ret = archive_seek_data( p_sys->p_archive, i_pos,
> SEEK_SET ); +            if (i_ret < 0)
> +                return VLC_EGENERIC;
> +            p_sys->i_pos = i_ret;
> +            break;
> +        }
> +
> +        case STREAM_UPDATE_SIZE:
> +        case STREAM_SET_RECORD_STATE:
> +        case STREAM_GET_CONTENT_TYPE:
> +            return VLC_EGENERIC;
> +
> +        default:
> +            return stream_vaControl( p_stream->p_source, i_query, args );
> +    }
> +
> +    return VLC_SUCCESS;
> +}
> +
> +static int OpenCallback(struct archive *p_archive, void *p_object)
> +{
> +    VLC_UNUSED(p_archive);
> +    VLC_UNUSED(p_object);
> +
> +    return ARCHIVE_OK;
> +}
> +
> +static int CloseCallback(struct archive *p_archive, void *p_object)
> +{
> +    VLC_UNUSED(p_archive);
> +    VLC_UNUSED(p_object);
> +
> +    return ARCHIVE_OK;
> +}
> +
> +static ssize_t ReadCallback(struct archive *p_archive, void *p_object,
> const void **pp_buffer) +{
> +    VLC_UNUSED(p_archive);
> +    stream_t *p_stream = (stream_t*)p_object;
> +
> +    *pp_buffer = &p_stream->p_sys->buffer;
> +    return stream_Read(p_stream->p_source, &p_stream->p_sys->buffer,
> READ_SIZE); +}
> +
> +static ssize_t SkipCallback(struct archive *p_archive, void *p_object,
> ssize_t i_request) +{
> +    VLC_UNUSED(p_archive);
> +    stream_t *p_stream = (stream_t*)p_object;
> +    ssize_t i_skipped = 0;
> +
> +    /* be smart as small seeks converts to reads */
> +    if (p_stream->p_sys->b_source_canseek)
> +    {
> +        int64_t i_pos = stream_Tell(p_stream->p_source);
> +        if (i_pos >=0)
> +            stream_Seek(p_stream->p_source, i_pos + i_request);
> +        i_skipped = stream_Tell(p_stream->p_source) - i_pos;
> +    }
> +    else while(i_request)
> +    {
> +        int i_skip = __MIN(INT32_MAX, i_request);
> +        int i_read = stream_Read(p_stream->p_source, NULL, i_skip);
> +        if (i_read > 0)
> +            i_skipped += i_read;
> +        else
> +            break;
> +        i_request -= i_read;
> +    }
> +
> +    return i_skipped;
> +}
> +
> +static ssize_t SeekCallback(struct archive *p_archive, void *p_object,
> ssize_t i_offset, int i_whence) +{
> +    VLC_UNUSED(p_archive);
> +    stream_t *p_stream = (stream_t*)p_object;
> +    ssize_t i_pos;
> +
> +    switch(i_whence)
> +    {
> +    case SEEK_CUR:
> +        i_pos = stream_Tell(p_stream->p_source);
> +        break;
> +    case SEEK_SET:
> +        i_pos = 0;
> +        break;
> +    case SEEK_END:
> +        i_pos = stream_Size(p_stream->p_source) - 1;
> +        break;
> +    default:
> +        return -1;
> +    }
> +
> +    if (i_pos < 0)
> +        return -1;
> +
> +    stream_Seek(p_stream->p_source, i_pos + i_offset); /* We don't care
> about return val */ +    return stream_Tell(p_stream->p_source);
> +}
> +
> +int StreamOpen(vlc_object_t *p_object)
> +{
> +    stream_t *p_stream = (stream_t*) p_object;
> +    stream_sys_t *p_sys = p_stream->p_sys;
> +
> +    if (!ProbeArchiveFormat(p_stream->p_source))
> +        return VLC_EGENERIC;
> +
> +    p_stream->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) );
> +    if( !p_sys )
> +        return VLC_ENOMEM;
> +
> +    p_sys->p_archive = archive_read_new();
> +    if (!p_sys->p_archive)
> +    {
> +        msg_Err(p_stream, "can't create libarchive instance: %s",
> +                archive_error_string(p_sys->p_archive));
> +        StreamClose(p_object);
> +        return VLC_EGENERIC;
> +    }
> +
> +    EnableArchiveFormats(p_sys->p_archive);
> +
> +    /* Seek callback must only be set if it is guaranteed to succeed */
> +    stream_Control(p_stream->p_source, STREAM_CAN_SEEK,
> &p_sys->b_source_canseek); +    if(p_sys->b_source_canseek)
> +        archive_read_set_seek_callback(p_sys->p_archive, SeekCallback);
> +
> +    if (archive_read_open2(p_sys->p_archive, p_stream, OpenCallback,
> ReadCallback, SkipCallback, CloseCallback) != ARCHIVE_OK) +    {
> +        msg_Err(p_stream, "can't create libarchive instance: %s",
> +                archive_error_string(p_sys->p_archive));
> +        StreamClose(p_object);
> +        return VLC_EGENERIC;
> +    }
> +
> +    char *psz_base = strdup(p_stream->psz_path);
> +    if (!psz_base)
> +    {
> +        archive_read_free(p_sys->p_archive);
> +        free(p_sys);
> +        return VLC_ENOMEM;
> +    }
> +    char *psz_name = strchr(psz_base, '|');
> +    if (psz_name)
> +        *psz_name++ = '\0';
> +
> +    if (decode_URI(psz_base) == NULL)
> +    {
> +        free(psz_base);
> +        archive_read_free(p_sys->p_archive);
> +        free(p_sys);
> +        return VLC_EGENERIC;
> +    }
> +
> +    bool b_present = false;
> +    while(archive_read_next_header(p_sys->p_archive, &p_sys->p_entry) ==
> ARCHIVE_OK) +    {
> +        if (!psz_name || !strcmp(archive_entry_pathname(p_sys->p_entry),
> psz_name)) +        {
> +            b_present = true;
> +            break;
> +        }
> +        msg_Dbg(p_stream, "skipping entry %s != %s",
> archive_entry_pathname(p_sys->p_entry), psz_name); +    }
> +
> +    if (!b_present)
> +    {
> +        msg_Err(p_stream, "entry '%s' not found in archive '%s'", psz_name,
> psz_base); +        /* entry not found */
> +        free(psz_base);
> +        archive_read_free(p_sys->p_archive);
> +        free(p_sys);
> +        return VLC_EGENERIC;
> +    }
> +    free(psz_base);
> +
> +    p_sys->b_seekable = p_sys->b_source_canseek &&
> +                        (archive_seek_data(p_sys->p_archive, 0, SEEK_SET)
> >= 0); +
> +    p_sys->i_len = archive_entry_size(p_sys->p_entry);
> +
> +    p_stream->pf_read = Read;
> +    p_stream->pf_peek = Peek;
> +    p_stream->pf_control = Control;
> +
> +    return VLC_SUCCESS;
> +}
> +
> +void StreamClose(vlc_object_t *object)
> +{
> +    stream_t *p_stream = (stream_t*)object;
> +    stream_sys_t *p_sys = p_stream->p_sys;
> +
> +    if (p_sys->p_archive)
> +        archive_read_free(p_sys->p_archive);
> +
> +    free(p_sys);
> +}
> diff --git a/po/POTFILES.in b/po/POTFILES.in
> index 09b2369..d10e537 100644
> --- a/po/POTFILES.in
> +++ b/po/POTFILES.in
> @@ -179,6 +179,7 @@ lib/vlm.c
> 
>  # modules
>  modules/access/alsa.c
> +modules/access/archive/module.c
>  modules/access/attachment.c
>  modules/access/avio.h
>  modules/access/bd/bd.c

-- 
Rémi Denis-Courmont
http://www.remlab.net/




More information about the vlc-devel mailing list