[vlc-devel] [PATCH] Zip: playlist demux and zip:// access

jpeg at videolan.org jpeg at videolan.org
Sat Jan 10 22:50:50 CET 2009


From: Jean-Philippe Andre <jpeg at via.ecp.fr>

The playlist demux creates a tree in the playlist, the first node being
the zip file and the leafs being the compressed files in the archive.
If there are folders in the archive, then there will be many nodes in
the playlist.

The zip:// access takes MRLs of the form:
  zip://<path-to-zip>!/<path-in-zip>

Where <path-to-zip> can be of the complex form
  <access>://<path>
or more simply, <path-to-local-file>

And <path-in-zip> is the path of the file in the archive. This is the
full name of the file as stored in the archive, of the form:
  <folder1>/<folder2>/<filename>
---
 modules/demux/zip/zip.h       |   76 +++++
 modules/demux/zip/zipaccess.c |  443 +++++++++++++++++++++++++++++
 modules/demux/zip/zipdemux.c  |  632 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1151 insertions(+), 0 deletions(-)
 create mode 100644 modules/demux/zip/zip.h
 create mode 100644 modules/demux/zip/zipaccess.c
 create mode 100644 modules/demux/zip/zipdemux.c

diff --git a/modules/demux/zip/zip.h b/modules/demux/zip/zip.h
new file mode 100644
index 0000000..b341456
--- /dev/null
+++ b/modules/demux/zip/zip.h
@@ -0,0 +1,76 @@
+/*****************************************************************************
+ * zip.h: Module (access+demux) to extract different archives, based on zlib
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Jean-Philippe André <jpeg at videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ *****************************************************************************/
+
+
+#ifndef ZIP_ACCESSDEMUX_H
+#define ZIP_ACCESSDEMUX_H
+
+#include <vlc/vlc.h>
+#include <vlc_playlist.h>
+#include <vlc_arrays.h>
+#include <vlc_plugin.h>
+#include "unzip.h"
+#include "ioapi.h"
+
+#include <assert.h>
+
+#define ZIP_FILENAME_LEN 512
+#define ZIP_SEP "!/"
+#define ZIP_BUFFER_LEN 32768
+
+/** **************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+int         getFilesInZip( demux_t*, unzFile, vlc_array_t*, vlc_array_t* );
+int         OpenFileInZip( access_t *p_access, int i_pos );
+
+/** **************************************************************************
+ * zipStream function headers : how to use vlc_stream to read the zip
+ *****************************************************************************/
+void       *zipStream_Open  ( void *opaque, const char *filename, int mode );
+unsigned long zipStream_Read  ( void * opaque, void * stream,
+                                void* buf, unsigned long size );
+long        zipStream_Tell  ( void * opaque, void * stream );
+long        zipStream_Seek  ( void * opaque, void * stream,
+                                 unsigned long offset, int origin );
+int         zipStream_Close ( void * opaque, void * stream );
+
+/** **************************************************************************
+ * Module access points: demux
+ *****************************************************************************/
+int         DemuxOpen ( vlc_object_t * );
+void        DemuxClose( vlc_object_t * );
+int         Demux  ( demux_t * );
+int         DemuxControl( demux_t *, int, va_list );
+
+/** **************************************************************************
+ * Module access points: access
+ *****************************************************************************/
+int         AccessOpen( vlc_object_t *p_this );
+void        AccessClose( vlc_object_t *p_this );
+int         AccessControl( access_t *p_access, int i_query, va_list args );
+ssize_t     AccessRead( access_t *, uint8_t *, size_t );
+int         AccessSeek( access_t *, int64_t );
+
+
+#endif /* ZIP_ACCESSDEMUX_H */
diff --git a/modules/demux/zip/zipaccess.c b/modules/demux/zip/zipaccess.c
new file mode 100644
index 0000000..4655f12
--- /dev/null
+++ b/modules/demux/zip/zipaccess.c
@@ -0,0 +1,443 @@
+/*****************************************************************************
+ * zipaccess.c: Module (access) to extract different archives, based on zlib
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Jean-Philippe André <jpeg at videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ *****************************************************************************/
+
+/** @todo:
+ * - use stream_UrlNew()
+ * - use another format for Zip URIs (don't use #ZIP# !)
+ * - implement crypto (using url zip://user:password@path-to-archive#ZIP#file
+ * - read files in zip with long name (use unz_file_info.size_filename)
+ * - multi-volume archive support ?
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_ZLIB_H
+
+#include "zip.h"
+#include <vlc_access.h>
+
+#include <vlc_stream.h>
+
+/** **************************************************************************
+ * This is our own access_sys_t for zip files
+ *****************************************************************************/
+struct access_sys_t
+{
+    /* zlib / unzip members */
+    unzFile            zipFile;
+    zlib_filefunc_def *fileFunctions;
+
+    /* file in zip information */
+    char              *psz_archivepath;
+    char              *psz_fileinzip;
+};
+
+void* ZCALLBACK ZipIO_Open( void* opaque, const char* filename, int mode);
+uLong ZCALLBACK ZipIO_Read( void*, void* stream, void* buf, uLong size );
+uLong ZCALLBACK ZipIO_Write( void*, void* stream, const void*, uLong );
+long ZCALLBACK ZipIO_Tell( void*, void* stream );
+long ZCALLBACK ZipIO_Seek( void*, void* stream, uLong offset, int origin );
+int ZCALLBACK ZipIO_Close( void*, void* stream );
+int ZCALLBACK ZipIO_Error( void*, void* stream );
+
+/** **************************************************************************
+ * \brief Open access
+ *****************************************************************************/
+int AccessOpen( vlc_object_t *p_this )
+{
+    access_t     *p_access = (access_t*)p_this;
+    access_sys_t *p_sys;
+    int i_ret              = VLC_EGENERIC;
+    unzFile file           = 0;
+
+    p_sys = ( access_sys_t * ) calloc( 1, sizeof( access_sys_t ) );
+    if( !p_sys )
+    {
+        msg_Err( p_access, "not enough memory" );
+        return VLC_ENOMEM;
+    }
+    p_access->p_sys = p_sys;
+
+    char *psz_fullPath = strdup( p_access->psz_path );
+    char *psz_nameInZip = strstr( psz_fullPath, ZIP_SEP );
+    char *psz_archivePath = strdup( p_access->psz_path );
+    if( !psz_nameInZip )
+    {
+        msg_Dbg( p_access, "could not split '%s'", psz_fullPath );
+        goto exit;
+    }
+
+    /* Split p_access->psz_path into zip archive path and file in zip path */
+    int i_pos = (ptrdiff_t)psz_nameInZip - (ptrdiff_t)psz_fullPath;
+    psz_archivePath[ i_pos ] = '\0';
+    psz_nameInZip += strlen( ZIP_SEP );
+
+    /*  */
+    p_sys->psz_fileinzip = strdup( psz_nameInZip );
+    p_sys->psz_archivepath = strdup( psz_nameInZip );
+
+    /* Define IO functions */
+    zlib_filefunc_def *p_func = (zlib_filefunc_def*)
+                                    calloc( 1, sizeof( zlib_filefunc_def ) );
+    p_func->zopen_file   = ZipIO_Open;
+    p_func->zread_file   = ZipIO_Read;
+    p_func->zwrite_file  = ZipIO_Write; // see comment
+    p_func->ztell_file   = ZipIO_Tell;
+    p_func->zseek_file   = ZipIO_Seek;
+    p_func->zclose_file  = ZipIO_Close;
+    p_func->zerror_file  = ZipIO_Error;
+    p_func->opaque       = p_access;
+
+    /* Open zip archive */
+    file = p_access->p_sys->zipFile = unzOpen2( psz_archivePath, p_func );
+    if( !file )
+    {
+        msg_Err( p_access, "not a valid zip archive: %s", psz_archivePath );
+        goto exit;
+    }
+
+    /* Open file in zip */
+    OpenFileInZip( p_access, 0 );
+
+    /* Set callback */
+    ACCESS_SET_CALLBACKS( AccessRead, NULL, AccessControl, AccessSeek );
+
+    /* Get some infos about current file. Maybe we could want some more ? */
+    unz_file_info z_info;
+    unzGetCurrentFileInfo( file, &z_info, NULL, 0, NULL, 0, NULL, 0 );
+
+    /* Set access informations: size is needed for AccessSeek */
+    p_access->info.i_size = z_info.uncompressed_size;
+    p_access->info.i_pos  = 0;
+    p_access->info.b_eof  = false;
+
+    i_ret = VLC_SUCCESS;
+
+exit:
+    free( psz_fullPath );
+    free( psz_archivePath );
+    if( i_ret != VLC_SUCCESS )
+    {
+        if( file )
+        {
+            unzCloseCurrentFile( file );
+            unzClose( file );
+        }
+        free( p_sys->psz_fileinzip );
+        free( p_sys->psz_archivepath );
+        free( p_sys->fileFunctions );
+        free( p_sys );
+    }
+    return i_ret;
+}
+
+/** **************************************************************************
+ * \brief Open file in zip
+ *****************************************************************************/
+int OpenFileInZip( access_t *p_access, int i_pos )
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    unzFile file = p_sys->zipFile;
+    if( !p_sys->psz_fileinzip || !p_sys->psz_archivepath )
+    {
+        return VLC_EGENERIC;
+    }
+
+    i_pos = __MIN( i_pos, 0 );
+    p_access->info.i_pos = 0;
+
+    unzCloseCurrentFile( file ); /* returns UNZ_PARAMERROR if file not opened */
+    if( unzLocateFile( file, p_sys->psz_fileinzip, 0 ) != UNZ_OK )
+    {
+        msg_Err( p_access, "could not [re]open file in zip: '%s'",
+                 p_sys->psz_fileinzip );
+        return VLC_EGENERIC;
+    }
+    if( unzOpenCurrentFile( file ) != UNZ_OK )
+    {
+        msg_Err( p_access, "could not [re]open file in zip: '%s'",
+                 p_sys->psz_fileinzip );
+        return VLC_EGENERIC;
+    }
+    if( i_pos > 0 )
+        return AccessSeek( p_access, i_pos );
+    else
+        return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief Close access: free structures
+ *****************************************************************************/
+void AccessClose( vlc_object_t *p_this )
+{
+    access_t     *p_access = (access_t*)p_this;
+    access_sys_t *p_sys = p_access->p_sys;
+    if( p_sys )
+    {
+        unzFile file = p_sys->zipFile;
+        if( file )
+        {
+            unzCloseCurrentFile( file );
+            unzClose( file );
+        }
+        free( p_sys->psz_fileinzip );
+        free( p_sys->psz_archivepath );
+        free( p_sys->fileFunctions );
+        free( p_sys );
+    }
+}
+
+/** **************************************************************************
+ * \brief Control access
+ *****************************************************************************/
+int AccessControl( access_t *p_access, int i_query, va_list args )
+{
+    bool         *pb_bool;
+    int          *pi_int;
+    int64_t      *pi_64;
+
+    switch( i_query )
+    {
+        /* */
+        case ACCESS_CAN_SEEK:
+        case ACCESS_CAN_PAUSE:
+        case ACCESS_CAN_CONTROL_PACE:
+            pb_bool = (bool*)va_arg( args, bool* );
+            *pb_bool = true;
+            break;
+
+        case ACCESS_CAN_FASTSEEK:
+            pb_bool = (bool*)va_arg( args, bool* );
+            *pb_bool = false;
+            break;
+
+        /* */
+        case ACCESS_GET_MTU:
+            pi_int = (int*)va_arg( args, int * );
+            *pi_int = 0;
+            break;
+
+        case ACCESS_GET_PTS_DELAY:
+            pi_64 = (int64_t*)va_arg( args, int64_t * );
+            *pi_64 = DEFAULT_PTS_DELAY;
+            break;
+
+        /* */
+        case ACCESS_SET_PAUSE_STATE:
+            /* Nothing to do */
+            break;
+
+        case ACCESS_GET_TITLE_INFO:
+        case ACCESS_SET_TITLE:
+        case ACCESS_SET_SEEKPOINT:
+        case ACCESS_SET_PRIVATE_ID_STATE:
+        case ACCESS_GET_META:
+        case ACCESS_GET_PRIVATE_ID_STATE:
+        case ACCESS_GET_CONTENT_TYPE:
+            return VLC_EGENERIC;
+
+        default:
+            msg_Warn( p_access, "unimplemented query %d in control", i_query );
+            return VLC_EGENERIC;
+
+    }
+    return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief Read access
+ * Reads current opened file in zip. This does not open the file in zip.
+ * Return -1 if no data yet, 0 if no more data, else real data read
+ *****************************************************************************/
+ssize_t AccessRead( access_t *p_access, uint8_t *p_buffer, size_t sz )
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    assert( p_sys );
+    unzFile file = p_sys->zipFile;
+    if( !file )
+    {
+        msg_Err( p_access, "archive not opened !" );
+        return VLC_EGENERIC;
+    }
+
+    int i_read = 0;
+    i_read = unzReadCurrentFile( file, p_buffer, sz );
+
+    p_access->info.i_pos = unztell( file );
+    return ( i_read >= 0 ? i_read : VLC_EGENERIC );
+}
+
+/** **************************************************************************
+ * \brief Seek inside zip file
+ *****************************************************************************/
+int AccessSeek( access_t *p_access, int64_t seek_len )
+{
+    access_sys_t *p_sys = p_access->p_sys;
+    assert( p_sys );
+    unzFile file = p_sys->zipFile;
+    if( !file )
+    {
+        msg_Err( p_access, "archive not opened !" );
+        return VLC_EGENERIC;
+    }
+
+    /* Reopen file in zip if needed */
+    if( p_access->info.i_pos != 0 )
+    {
+        OpenFileInZip( p_access, p_access->info.i_pos + seek_len );
+    }
+
+    /* Read seek_len data and drop it */
+    int i_seek = 0;
+    int i_read = 1;
+    char *p_buffer = ( char* ) calloc( 1, ZIP_BUFFER_LEN );
+    while( ( i_seek < seek_len ) && ( i_read > 0 ) )
+    {
+        i_read = ( seek_len - i_seek < ZIP_BUFFER_LEN )
+               ? ( seek_len - i_seek ) : ZIP_BUFFER_LEN;
+        i_read = unzReadCurrentFile( file, p_buffer, i_read );
+        if( i_read < 0 )
+        {
+            msg_Warn( p_access, "could not seek in file" );
+            free( p_buffer );
+            return VLC_EGENERIC;
+        }
+        else
+        {
+            i_seek += i_read;
+        }
+    }
+    free( p_buffer );
+
+    p_access->info.i_pos = unztell( file );
+    return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: open (read only)
+ *****************************************************************************/
+void* ZCALLBACK ZipIO_Open( void* opaque, const char* filename, int mode)
+{
+    assert(opaque != NULL);
+    assert(mode == (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING));
+
+    access_t *p_access = (access_t*) opaque;
+    return stream_UrlNew( p_access, filename );
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: read
+ *****************************************************************************/
+uLong ZCALLBACK ZipIO_Read( void* opaque,
+                            void* stream,
+                            void* buf,
+                            uLong size )
+{
+    (void)opaque;
+    //access_t *p_access = (access_t*) opaque;
+    //msg_Dbg(p_access, "read %d", size);
+    return stream_Read( (stream_t*) stream, buf, size );
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: write (assert insteadof segfault)
+ *****************************************************************************/
+uLong ZCALLBACK ZipIO_Write( void* opaque, void* stream, const void* buf,
+                             uLong size )
+{
+    (void)opaque; (void)stream; (void)buf; (void)size;
+    int zip_access_cannot_write_this_should_not_happen = 0;
+    assert(zip_access_cannot_write_this_should_not_happen);
+    return 0;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: tell
+ *****************************************************************************/
+long ZCALLBACK ZipIO_Tell( void* opaque, void* stream )
+{
+    (void)opaque;
+    int64_t i64_tell = stream_Tell( (stream_t*) stream );
+    //access_t *p_access = (access_t*) opaque;
+    //msg_Dbg(p_access, "tell %" PRIu64, i64_tell);
+    return (long)i64_tell;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: seek
+ *****************************************************************************/
+long ZCALLBACK ZipIO_Seek( void* opaque, void* stream,
+                           uLong offset, int origin )
+{
+    (void)opaque;
+    //access_t *p_access = (access_t*) opaque;
+    int64_t pos = offset;
+    switch( origin )
+    {
+        case SEEK_CUR:
+            pos += stream_Tell( (stream_t*) stream );
+            break;
+        case SEEK_SET:
+            break;
+        case SEEK_END:
+            pos += stream_Size( (stream_t*) stream );
+            break;
+        default:
+            return -1;
+    }
+    //msg_Dbg( p_access, "seek (%d,%d): %" PRIu64, offset, origin, pos );
+    stream_Seek( (stream_t*) stream, pos );
+    /* Note: in unzip.c, unzlocal_SearchCentralDir seeks to the end of
+             the stream, which is doable but returns an error in VLC.
+             That's why we always assume this was OK. FIXME */
+    return 0;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: close
+ *****************************************************************************/
+int ZCALLBACK ZipIO_Close( void* opaque, void* stream )
+{
+    (void)opaque;
+    stream_Delete( (stream_t*) stream );
+    return 0;
+}
+
+/** **************************************************************************
+ * \brief I/O functions for the ioapi: test error (man 3 ferror)
+ *****************************************************************************/
+int ZCALLBACK ZipIO_Error( void* opaque, void* stream )
+{
+    (void)opaque;
+    (void)stream;
+    //msg_Dbg( p_access, "error" );
+    return 0;
+}
+
+
+
+#else
+# error Can not compile zip demuxer without zlib support
+#endif
diff --git a/modules/demux/zip/zipdemux.c b/modules/demux/zip/zipdemux.c
new file mode 100644
index 0000000..ff126c8
--- /dev/null
+++ b/modules/demux/zip/zipdemux.c
@@ -0,0 +1,632 @@
+/*****************************************************************************
+ * zipdemux.c: Module (demux) to extract different archives, based on zlib
+ *****************************************************************************
+ * Copyright (C) 2007 the VideoLAN team
+ * $Id$
+ *
+ * Authors: Jean-Philippe André <jpeg at videolan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU 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.
+ *****************************************************************************/
+
+/** **************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_ZLIB_H
+
+#include "zip.h"
+#include <vlc_demux.h>
+#include <stddef.h>
+
+/** **************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+vlc_module_begin();
+    set_shortname( "Zip");
+    set_category( CAT_INPUT );
+    set_subcategory( SUBCAT_INPUT_DEMUX );
+    set_description( _("Zip files demuxer" ) );
+    set_capability( "demux", 70 );
+    set_callbacks( DemuxOpen, DemuxClose );
+    add_shortcut( "unzip" );
+    add_shortcut( "zip" );
+    add_submodule();
+        set_description( "Zip access");
+        set_capability( "access", 0 );
+        add_shortcut( "unzip" );
+        add_shortcut( "zip" );
+        set_callbacks( AccessOpen, AccessClose );
+        set_subcategory( SUBCAT_INPUT_ACCESS );
+vlc_module_end();
+
+/** **************************************************************************
+ * This is our own demux_sys_t for zip files
+ *****************************************************************************/
+struct demux_sys_t
+{
+    /* zlib / unzip members */
+    unzFile            zipFile;
+    zlib_filefunc_def *fileFunctions;
+
+    /* infos about files in zip */
+    vlc_array_t        fileNames; //char *
+    vlc_array_t        fileInfos; //unz_file_info *
+    vlc_dictionary_t   file_items;
+};
+
+/** **************************************************************************
+ * \brief Module entry-point
+ * \param p_this
+ *
+ * This function will read input file and if it is a .zip archive, it will
+ * fill the playlist with the list of files inside the .zip.
+ *****************************************************************************/
+int DemuxOpen( vlc_object_t *p_this )
+{
+    /* DEMUX_BY_EXTENSION_MSG */
+    demux_t *p_demux = (demux_t *)p_this;
+    if( !demux_IsPathExtension( p_demux, ".zip" ) )
+        return VLC_EGENERIC;
+
+    p_demux->pf_control = DemuxControl;
+    p_demux->pf_demux   = Demux;
+    p_demux->p_sys = malloc( sizeof( demux_sys_t ) );
+    if( !p_demux->p_sys )
+        return VLC_ENOMEM;
+    memset( p_demux->p_sys, 0, sizeof( demux_sys_t ) );
+
+    msg_Dbg( p_demux, "using ZLib to demux ZIP files" );
+
+    /* Set zlib I/O API and open zip archive */
+    stream_t *s = p_demux->s;
+    if( !s )
+    {
+        msg_Dbg( p_demux, "no stream found, using default file functions" );
+        p_demux->p_sys->fileFunctions = NULL;
+        p_demux->p_sys->zipFile       = unzOpen2( p_demux->psz_path, NULL );
+        /* Note p_demux->p_sys->fileFunctions->opaque is NULL. */
+        if( !p_demux->p_sys->zipFile )
+        {
+            msg_Err( p_demux, "unable to open file" );
+            return VLC_EGENERIC;
+        }
+    }
+    else
+    {
+        msg_Dbg( p_demux, "an open stream was found, using this access" );
+        p_demux->p_sys->fileFunctions = ( zlib_filefunc_def * )
+                                       calloc( 1, sizeof( zlib_filefunc_def ) );
+        p_demux->p_sys->fileFunctions->zopen_file   = zipStream_Open;
+        p_demux->p_sys->fileFunctions->zread_file   = zipStream_Read;
+        p_demux->p_sys->fileFunctions->zwrite_file  = NULL; /** \todo ? */
+        p_demux->p_sys->fileFunctions->ztell_file   = zipStream_Tell;
+        p_demux->p_sys->fileFunctions->zseek_file   = zipStream_Seek;
+        p_demux->p_sys->fileFunctions->zclose_file  = zipStream_Close;
+        p_demux->p_sys->fileFunctions->zerror_file  = NULL; /** \todo ? */
+        p_demux->p_sys->fileFunctions->opaque       = ( void * ) p_demux;
+        p_demux->p_sys->zipFile = unzOpen2( p_demux->psz_path,
+                                            p_demux->p_sys->fileFunctions );
+        if( !p_demux->p_sys->zipFile )
+        {
+            msg_Err( p_demux, "unable to open file" );
+            return VLC_EGENERIC;
+        }
+    }
+
+    return VLC_SUCCESS;
+}
+
+/** **************************************************************************
+ * \brief Module exit-point
+ * \param p_this
+ *****************************************************************************/
+void DemuxClose( vlc_object_t *p_this )
+{
+    demux_t *p_demux = ( demux_t * ) p_this;
+    vlc_dictionary_clear( &( p_demux->p_sys->file_items ), NULL, NULL );
+    free( p_demux->p_sys->fileFunctions );
+    free( p_demux->p_sys );
+    return;
+}
+
+/** **************************************************************************
+ * \brief Demux entry for demux modules
+ * \param p_demux
+ * \return -1 in case of error, 0 in case of EOF, 1 otherwise
+ *****************************************************************************/
+int Demux ( demux_t *p_demux )
+{
+    msg_Dbg( p_demux, "demuxing zip file..." );
+
+    /* Get and lock playlist */
+    playlist_t *p_playlist = pl_Hold( p_demux );
+    if( !p_playlist )
+        return -1;
+    /*PL_LOCK;*/
+
+    /* Convert current playlist item to node, FIXME */
+    input_thread_t  *p_input         = pl_CurrentInput( p_demux );
+    if( !p_input )
+    {
+        /*PL_UNLOCK;*/
+        return -1;
+    }
+    input_item_t    *p_input_item    = input_GetItem( p_input );
+    playlist_item_t *p_current       = playlist_ItemGetByInput( p_playlist,
+                                                                p_input_item,
+                                                                false );
+
+    vlc_object_release( p_input );
+    if( !p_current )
+    {
+        /*PL_UNLOCK;*/
+        pl_Release( p_demux );
+        return -1;
+    }
+
+    p_current->p_input->i_type = ITEM_TYPE_DIRECTORY;
+    playlist_item_t *p_root    = playlist_ItemToNode( p_playlist,
+                                                      p_current,
+                                                      false );
+    if( !p_root )
+    {
+        /*PL_UNLOCK;*/
+        pl_Release( p_demux );
+        return -1;
+    }
+
+    /* Get some infos about zip archive */
+    int i_ret = 0;
+    unzFile file = p_demux->p_sys->zipFile;
+    vlc_array_t *p_filenames = vlc_array_new(); /* Will contain char* */
+
+    /* List all file names in Zip archive */
+    i_ret = getFilesInZip( p_demux, file, p_filenames, NULL );
+    if( i_ret < 0 )
+    {
+        unzClose( file );
+        for( int i = 0; i < vlc_array_count( p_filenames ); i++ )
+            free( vlc_array_item_at_index( p_filenames, i ) );
+        vlc_array_destroy( p_filenames );
+        /*PL_UNLOCK;*/
+        pl_Release( p_demux );
+        return -1;
+    }
+
+    msg_Dbg( p_demux, "%d files found in zip", vlc_array_count( p_filenames ) );
+
+    /* Close archive */
+    unzClose( file );
+    p_demux->p_sys->zipFile = NULL;
+
+    /* This dictionary will remember of all folders/files and playlist items */
+    vlc_dictionary_t *p_dico = &( p_demux->p_sys->file_items );
+    vlc_dictionary_init( p_dico, 0 );
+    vlc_dictionary_insert( p_dico, "/", p_root->p_input );
+
+    /* Base path: path to zip archive */
+    char *psz_basefile = strdup( p_current->p_input->psz_uri );
+
+    playlist_item_t *p_parent        = NULL;
+    playlist_item_t *p_playlist_item = NULL;
+
+    i_ret = VLC_SUCCESS;
+
+    /* Create associated input items */
+    for( int i = 0; i < vlc_array_count( p_filenames ); i++ )
+    {
+        char *psz_folder = NULL;
+        char *psz_uri    = NULL;
+        char *psz_name   = strdup( vlc_array_item_at_index( p_filenames, i ) );
+        if( !psz_name )
+        {
+            vlc_array_remove( p_filenames, i );
+            continue;
+        }
+        psz_folder = strdup( psz_name );
+
+        /* Create input item */
+        char *p_foldersep = strrchr( psz_folder, '/' );
+        if( p_foldersep )
+        {
+            p_foldersep[1] = '\0';
+
+            /* This is a folder */
+            if( ( psz_name[ strlen( psz_name ) - 1 ] == '/' ) )
+            {
+                char *psz_showname = strdup( psz_name );
+                ptrdiff_t d = 0;
+
+                /* Find parent folder */
+                *p_foldersep = '\0';
+                p_foldersep = strrchr( psz_folder, '/' );
+                if( p_foldersep )
+                {
+                    d = (ptrdiff_t)p_foldersep - (ptrdiff_t)psz_folder;
+                    p_foldersep[1] = '\0';
+                    p_parent = vlc_dictionary_value_for_key( p_dico, psz_folder );
+                }
+                else
+                    p_parent = p_root;
+
+                /* Extract only folder name (no parent) */
+                if( d )
+                    memmove( psz_showname, psz_showname + d + 1,
+                             strlen( psz_showname ) - d );
+                psz_showname[ strlen( psz_showname ) - 1 ] = '\0';
+
+                /* Create node */
+                p_playlist_item = playlist_NodeCreate( p_playlist, psz_showname,
+                                                       p_parent,
+                                                       PLAYLIST_NO_REBUILD,
+                                                       NULL );
+
+                /* Add to dictionary */
+                vlc_dictionary_insert( p_dico, psz_name, p_playlist_item );
+                free( psz_showname );
+            }
+
+            /* This is a normal file in zip in folder named psz_folder */
+            else
+            {
+                p_parent = vlc_dictionary_value_for_key( p_dico, psz_folder );
+
+                if( asprintf( &psz_uri, "zip://%s" ZIP_SEP "%s",
+                              psz_basefile, psz_name )
+                    == -1 )
+                {
+                    /*PL_UNLOCK;*/
+                    pl_Release( p_demux );
+                    return -1;
+                }
+
+                if( !p_parent )
+                    p_parent = p_root;
+
+                /* Extract only filename (remove folder) */
+                int d = (int)p_foldersep - (int)psz_folder;
+                memmove( psz_name, psz_name + d + 1, strlen( psz_name ) - d );
+
+                p_input_item = input_item_NewWithType( VLC_OBJECT( p_playlist ),
+                                                       psz_uri,
+                                                       psz_name,
+                                                       0, NULL,
+                                                       0, ITEM_TYPE_FILE );
+
+                /* Insert input item as subitem of p_parent */
+                if( p_input_item )
+                {
+                    i_ret = playlist_BothAddInput( p_playlist, p_input_item,
+                                                   p_parent,
+                                                   PLAYLIST_APPEND|
+                                                   PLAYLIST_NO_REBUILD,
+                                                   PLAYLIST_END, NULL, NULL,
+                                                   false );
+                    /* No need to add to dictionary */
+//                     vlc_dictionary_insert( p_dico, psz_name,
+//                            playlist_ItemGetByInput( p_playlist,
+//                                                     p_input_item, false ) );
+
+                    vlc_gc_decref( p_input_item );
+                }
+                else
+                    i_ret = -1;
+
+                free( psz_uri );
+
+                if( i_ret != VLC_SUCCESS )
+                {
+                    /*PL_UNLOCK;*/
+                    pl_Release( p_demux );
+                    return -1;
+                }
+            }
+
+        }
+        /* this is a simple file in root of zip archive */
+        else
+        {
+            if( asprintf( &psz_uri, "zip://%s" ZIP_SEP "%s",
+                          psz_basefile, psz_name ) == -1 )
+            {
+                /*PL_UNLOCK;*/
+                pl_Release( p_demux );
+                return -1;
+            }
+
+            /* Insert input item as subitem of p_parent */
+            p_parent = p_root;
+            p_input_item = input_item_NewWithType( VLC_OBJECT( p_playlist ),
+                                                   psz_uri,
+                                                   psz_name,
+                                                   0, NULL,
+                                                   0, ITEM_TYPE_FILE );
+
+            /* Insert input item as subitem of p_parent */
+            if( p_input_item )
+            {
+                i_ret = playlist_BothAddInput( p_playlist,
+                                               p_input_item,
+                                               p_parent,
+                                               PLAYLIST_APPEND|
+                                               PLAYLIST_NO_REBUILD,
+                                               PLAYLIST_END, NULL, NULL, false );
+
+                /* No need to add to dictionary */
+//                 vlc_dictionary_insert( p_dico, psz_name, p_input_item );
+
+                vlc_gc_decref( p_input_item );
+            }
+            else
+                i_ret = -1;
+
+            free( psz_uri );
+
+            if( i_ret != VLC_SUCCESS )
+            {
+                /*PL_UNLOCK;*/
+                pl_Release( p_demux );
+                return -1;
+            }
+        }
+        free( psz_folder );
+        free( psz_name );
+    }
+
+    /* Add these items to the playlist */
+    /*PL_UNLOCK;*/
+    pl_Release( p_demux );
+
+    free( psz_basefile );
+    for( int i = 0; i < vlc_array_count( p_filenames ); i++)
+    {
+        free( vlc_array_item_at_index( p_filenames, i ) );
+    }
+    vlc_array_destroy( p_filenames );
+
+    p_demux->pf_demux = NULL; /* This function won't be called any further */
+    return ( i_ret == VLC_SUCCESS ? 0 : -1 );
+}
+
+/** **************************************************************************
+ * \brief Control entry for demux modules
+ * \param p_demux
+ * \param i_query
+ * \param args
+ * \return demux_vaControlHelper()
+ *****************************************************************************/
+int DemuxControl( demux_t *p_demux, int i_query, va_list args)
+{
+    return demux_vaControlHelper( p_demux->s, -1, -1, -1, -1, i_query, args );
+}
+
+/** **************************************************************************
+ * Zip utility functions
+ *****************************************************************************/
+
+/** **************************************************************************
+ * \brief List files in zip and append their names to p_array
+ * \param p_demux
+ * \param file Opened zip file
+ * \param p_array vlc_array_t which will receive all filenames
+ *
+ * In case of error, returns VLC_EGENERIC.
+ * In case of success, returns number of files found, and goes back to first file.
+ *****************************************************************************/
+int getFilesInZip( demux_t *p_demux,
+                          unzFile file,
+                          vlc_array_t *p_filenames,
+                          vlc_array_t *p_fileinfos )
+{
+    if( !p_filenames || !p_demux )
+        return VLC_EGENERIC;
+
+    int i_ret = 0;
+
+    /* Get global info */
+    unz_global_info info;
+
+    if( unzGetGlobalInfo( file, &info ) != UNZ_OK )
+    {
+        msg_Warn( p_demux, "this is not a valid zip archive" );
+        return VLC_EGENERIC;
+    }
+
+    /* Go to first file in archive */
+    unzGoToFirstFile( file );
+
+    /* Get info about each file */
+    for( unsigned long i = 0; i < info.number_entry; i++ )
+    {
+        char *psz_fileName = calloc( ZIP_FILENAME_LEN, 1 );
+        unz_file_info *p_fileInfo = calloc( 1, sizeof( unz_file_info ) );
+
+        if( !p_fileInfo || !psz_fileName )
+        {
+            msg_Warn( p_demux, "not enough memory" );
+            return VLC_ENOMEM;
+        }
+
+        if( unzGetCurrentFileInfo( file, p_fileInfo, psz_fileName,
+                                   ZIP_FILENAME_LEN, NULL, 0, NULL, 0 )
+            != UNZ_OK )
+        {
+            msg_Warn( p_demux, "can't get info about file in zip" );
+            return VLC_EGENERIC;
+        }
+
+        if( p_filenames )
+            vlc_array_append( p_filenames, strdup( psz_fileName ) );
+        free( psz_fileName );
+
+        if( p_fileinfos )
+            vlc_array_append( p_fileinfos, p_fileInfo );
+        else
+            free( p_fileInfo );
+
+        if( i < ( info.number_entry - 1 ) )
+        {
+            /* Go the next file in the archive */
+            if( unzGoToNextFile( file ) != UNZ_OK )
+            {
+                msg_Warn( p_demux, "can't go to next file in zip" );
+                return VLC_EGENERIC;
+            }
+        }
+
+        i_ret++;
+    }
+
+    /* i_ret should be equal to info.number_entry */
+    unzGoToFirstFile( file );
+    return i_ret;
+}
+
+/** **************************************************************************
+ * zipStream function definitions : how to use vlc_stream to read the zip
+ *****************************************************************************/
+
+/** **************************************************************************
+ * \brief interface for unzip module to open a file using a vlc_stream
+ * \param opaque must be p_demux
+ * \param filename
+ * \param mode how to open the file (read/write ?). We support only read
+ * \return handle to opened file : zipFile->filestream == p_demux->s
+ *****************************************************************************/
+void *zipStream_Open( void *opaque, const char *filename, int mode )
+{
+    VLC_UNUSED( filename );
+    demux_t *p_demux = ( demux_t * ) opaque;
+    if( !p_demux )
+    {
+        msg_Err( p_demux, "zipStream_Open: p_demux is not set !" );
+        return NULL;
+    }
+    if( mode & ( ZLIB_FILEFUNC_MODE_CREATE | ZLIB_FILEFUNC_MODE_WRITE ) )
+    {
+        msg_Err( p_demux, "zipStream_Open: we cannot write into zip files" );
+        return NULL;
+    }
+
+    return p_demux->s;
+}
+
+/** **************************************************************************
+ * \brief read something from stream into buffer
+ * \param opaque should be p_demux (warning, or segfault if not NULL)
+ * \param stream stream created by zipStream_Open
+ * \param buf buffer to read the file
+ * \param size length of this buffer
+ * \return return the number of bytes read (<= size)
+ *****************************************************************************/
+unsigned long zipStream_Read( void *opaque, void *stream,
+                              void *buf, unsigned long size )
+{
+    demux_t  *p_demux = ( demux_t * ) opaque;
+    stream_t *s = ( stream_t * ) stream;
+    unsigned long l_ret;
+
+    if( !p_demux )
+        msg_Warn( p_demux, "zipStream_Read: p_demux is not set !" );
+
+    else if( s != p_demux->s )
+        msg_Warn( p_demux, "zipStream_Read: "
+                           "stream is not equal to p_demux->stream" );
+
+    l_ret = ( unsigned long ) stream_Read( s, buf, ( int ) size );
+
+    return l_ret;
+}
+
+/** **************************************************************************
+ * \brief tell size of stream
+ * \param opaque should be p_demux (warning, or segfault if not NULL)
+ * \param stream stream created by zipStream_Open
+ * \return size of the file / stream
+ * ATTENTION: this is not stream_Tell, but stream_Size !
+ *****************************************************************************/
+long zipStream_Tell( void * opaque, void * stream )
+{
+    demux_t  *p_demux = ( demux_t * ) opaque;
+    stream_t *s = ( stream_t * ) stream;
+    long l_ret;
+
+    if( !p_demux )
+        msg_Warn( p_demux, "zipStream_Tell: p_demux is not set !" );
+
+    else if( s != p_demux->s )
+        msg_Warn( p_demux, "zipStream_Tell: "
+                           "stream is not equal to p_demux->stream" );
+
+    l_ret = ( long ) stream_Size( s ); /* ATTENTION: not stream_Tell ! */
+    return l_ret;
+}
+
+/** **************************************************************************
+ * \brief seek in the stream
+ * \param opaque should be p_demux (warning, or segfault if not NULL)
+ * \param stream stream created by zipStream_Open
+ * \param offset positive offset to seek
+ * \param origin current position in stream
+ * \return ¿ VLC_SUCCESS or an error code ?
+ *****************************************************************************/
+long zipStream_Seek ( void * opaque, void * stream,
+                      unsigned long offset, int origin )
+{
+    demux_t  *p_demux = ( demux_t * ) opaque;
+    stream_t *s = ( stream_t * ) stream;
+    long l_ret;
+
+    if( !p_demux )
+        msg_Warn( p_demux, "zipStream_Seek: p_demux is not set !" );
+
+    else if( s != p_demux->s )
+        msg_Warn( p_demux, "zipStream_Tell: "
+                           "stream is not equal to p_demux->stream" );
+
+    l_ret = ( long ) stream_Seek( s, offset + origin ); /* this is an int64 */
+    return l_ret;
+}
+
+/** **************************************************************************
+ * \brief close the stream
+ * \param opaque should be p_demux (warning, or segfault if not NULL)
+ * \param stream stream created by zipStream_Open
+ * \return always VLC_SUCCESS
+ * This closes zip archive
+ *****************************************************************************/
+int zipStream_Close ( void * opaque, void * stream )
+{
+    demux_t  *p_demux = ( demux_t * ) opaque;
+    stream_t *s = ( stream_t * ) stream;
+    if( !p_demux )
+        msg_Warn( p_demux, "zipStream_Close: p_demux is not set !" );
+
+    if( p_demux->p_sys && p_demux->p_sys->zipFile )
+        p_demux->p_sys->zipFile = NULL;
+
+    stream_Seek( s, 0 );
+    return VLC_SUCCESS;
+}
+
+
+#else
+# error Can not compile zip demuxer without zlib support
+#endif
-- 
1.6.0.6





More information about the vlc-devel mailing list