[vlc-commits] access: archive: add multiple volume support

Francois Cartegnie git at videolan.org
Thu Jul 31 10:01:44 CEST 2014


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Thu Jul 31 17:00:42 2014 +0900| [5e962044a34bcc4cd5494d295474356ef9961b27] | committer: Francois Cartegnie

access: archive: add multiple volume support

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=5e962044a34bcc4cd5494d295474356ef9961b27
---

 modules/access/archive/access.c |  169 +++++++++++++++++++++++++++++++++++----
 1 file changed, 153 insertions(+), 16 deletions(-)

diff --git a/modules/access/archive/access.c b/modules/access/archive/access.c
index 667b0eb..d8e0bc7 100644
--- a/modules/access/archive/access.c
+++ b/modules/access/archive/access.c
@@ -27,15 +27,23 @@
 #include <archive.h>
 #include <archive_entry.h>
 
+typedef struct callback_data_t
+{
+    char *psz_uri;
+    access_t *p_access;
+} callback_data_t;
+
 struct access_sys_t
 {
     struct archive *p_archive;
     bool b_source_canseek;
     uint8_t buffer[ARCHIVE_READ_SIZE];
 
+    callback_data_t *p_callback_data;
+    unsigned int i_callback_data;
+
     struct archive_entry *p_entry;
     stream_t *p_stream;
-    char *psz_uri;
     bool b_seekable; /* Is our archive type seekable ? */
 };
 
@@ -71,11 +79,78 @@ static int Seek(access_t *p_access, uint64_t i_pos)
     return VLC_SUCCESS;
 }
 
+static int FindVolumes(access_t *p_access, struct archive *p_archive, const char *psz_uri,
+                       char *** const pppsz_files, unsigned int * const pi_files)
+{
+    VLC_UNUSED(p_archive);
+    *pppsz_files = NULL;
+    *pi_files = 0;
+
+    static const struct
+    {
+        char const * const psz_match;
+        char const * const psz_format;
+        uint16_t i_min;
+        uint16_t i_max;
+    } patterns[] = {
+        { ".part1.rar",   ".part%.1d.rar", 2,   9 },
+        { ".part01.rar",  ".part%.2d.rar", 2,  99 },
+        { ".part001.rar", ".part%.3d.rar", 2, 999 },
+        { ".001",         ".%.3d",         2, 999 },
+        { ".000",         ".%.3d",         1, 999 },
+    };
+
+    const size_t i_uri_size = strlen(psz_uri);
+    const int i_patterns = ARRAY_SIZE(patterns);
+    for (int i=0; i<i_patterns; i++)
+    {
+        const size_t i_match_size = strlen(patterns[i].psz_match);
+        if (i_uri_size < i_match_size)
+            continue;
+
+        if (!strcmp(&psz_uri[i_uri_size - i_match_size], patterns[i].psz_match))
+        {
+            char **ppsz_files = malloc(sizeof(char *) * patterns[i].i_max);
+
+            for(int j=patterns[i].i_min; j<patterns[i].i_max; j++)
+            {
+                char *psz_newuri = strdup(psz_uri);
+                if (!psz_newuri ||
+                    !sprintf(&psz_newuri[i_uri_size - i_match_size], patterns[i].psz_format, j))
+                    break;
+
+                /* Probe URI */
+                /* FIXME: no warnings ! */
+                stream_t *p_stream = stream_UrlNew(p_access, psz_newuri);
+                if (p_stream)
+                {
+                    ppsz_files[*pi_files] = psz_newuri;
+                    (*pi_files)++;
+                    stream_Delete(p_stream);
+                }
+                else
+                {
+                    free(psz_newuri);
+                    break;
+                }
+            }
+
+            if (*pi_files == 0)
+                FREENULL(ppsz_files);
+             *pppsz_files = ppsz_files;
+
+            return VLC_SUCCESS;
+        }
+    }
+
+    return VLC_SUCCESS;
+}
+
 static ssize_t ReadCallback(struct archive *p_archive, void *p_object, const void **pp_buffer)
 {
     VLC_UNUSED(p_archive);
-    access_t *p_access = (access_t*)p_object;
-    access_sys_t *p_sys = p_access->p_sys;
+    callback_data_t *p_data = (callback_data_t *) p_object;
+    access_sys_t *p_sys = p_data->p_access->p_sys;
 
     *pp_buffer = &p_sys->buffer;
     return stream_Read(p_sys->p_stream, &p_sys->buffer, ARCHIVE_READ_SIZE);
@@ -84,8 +159,8 @@ static ssize_t ReadCallback(struct archive *p_archive, void *p_object, const voi
 static ssize_t SkipCallback(struct archive *p_archive, void *p_object, ssize_t i_request)
 {
     VLC_UNUSED(p_archive);
-    access_t *p_access = (access_t*)p_object;
-    access_sys_t *p_sys = p_access->p_sys;
+    callback_data_t *p_data = (callback_data_t *) p_object;
+    access_sys_t *p_sys = p_data->p_access->p_sys;
     ssize_t i_skipped = 0;
 
     /* be smart as small seeks converts to reads */
@@ -113,8 +188,8 @@ static ssize_t SkipCallback(struct archive *p_archive, void *p_object, ssize_t i
 static ssize_t SeekCallback(struct archive *p_archive, void *p_object, ssize_t i_offset, int i_whence)
 {
     VLC_UNUSED(p_archive);
-    access_t *p_access = (access_t*)p_object;
-    access_sys_t *p_sys = p_access->p_sys;
+    callback_data_t *p_data = (callback_data_t *) p_object;
+    access_sys_t *p_sys = p_data->p_access->p_sys;
 
     ssize_t i_pos;
 
@@ -140,13 +215,26 @@ static ssize_t SeekCallback(struct archive *p_archive, void *p_object, ssize_t i
     return stream_Tell(p_sys->p_stream);
 }
 
+static int SwitchCallback(struct archive *p_archive, void *p_object, void *p_object2)
+{
+    VLC_UNUSED(p_archive);
+    callback_data_t *p_data = (callback_data_t *) p_object;
+    callback_data_t *p_nextdata = (callback_data_t *) p_object2;
+    access_sys_t *p_sys = p_data->p_access->p_sys;
+
+    msg_Dbg(p_data->p_access, "opening next volume %s", p_nextdata->psz_uri);
+    stream_Delete(p_sys->p_stream);
+    p_sys->p_stream = stream_UrlNew(p_nextdata->p_access, p_nextdata->psz_uri);
+    return p_sys->p_stream ? ARCHIVE_OK : ARCHIVE_FATAL;
+}
+
 static int OpenCallback(struct archive *p_archive, void *p_object)
 {
     VLC_UNUSED(p_archive);
-    access_t *p_access = (access_t*)p_object;
-    access_sys_t *p_sys = p_access->p_sys;
+    callback_data_t *p_data = (callback_data_t *) p_object;
+    access_sys_t *p_sys = p_data->p_access->p_sys;
 
-    p_sys->p_stream = stream_UrlNew( p_access, p_sys->psz_uri );
+    p_sys->p_stream = stream_UrlNew( p_data->p_access, p_data->psz_uri );
     if(!p_sys->p_stream)
         return ARCHIVE_FATAL;
 
@@ -161,10 +249,14 @@ static int OpenCallback(struct archive *p_archive, void *p_object)
 static int CloseCallback(struct archive *p_archive, void *p_object)
 {
     VLC_UNUSED(p_archive);
-    access_t *p_access = (access_t*)p_object;
+    callback_data_t *p_data = (callback_data_t *) p_object;
+    access_sys_t *p_sys = p_data->p_access->p_sys;
 
-    if (p_access->p_sys->p_stream)
-        stream_Delete(p_access->p_sys->p_stream);
+    if (p_sys->p_stream)
+    {
+        stream_Delete(p_sys->p_stream);
+        p_sys->p_stream = NULL;
+    }
 
     return ARCHIVE_OK;
 }
@@ -242,9 +334,48 @@ int AccessOpen(vlc_object_t *p_object)
 
     EnableArchiveFormats(p_sys->p_archive);
 
-    p_sys->psz_uri = psz_base;
+    /* Set up the switch callback for multiple volumes handling */
+    archive_read_set_switch_callback(p_sys->p_archive, SwitchCallback);
 
-    if (archive_read_open2(p_sys->p_archive, p_access, OpenCallback, ReadCallback, SkipCallback, CloseCallback) != ARCHIVE_OK)
+    /* !Warn: sucks because libarchive can't guess format without reading 1st header
+     *        and it can't tell either if volumes are missing neither set following
+     *        volumes after the first Open().
+     *        We need to know volumes uri in advance then :/
+     */
+
+    /* Try to list existing volumes */
+    char **ppsz_files = NULL;
+    unsigned int i_files = 0;
+    FindVolumes(p_access, p_sys->p_archive, psz_base, &ppsz_files, &i_files);
+
+    p_sys->i_callback_data = 1 + i_files;
+    p_sys->p_callback_data = malloc(sizeof(callback_data_t) * p_sys->i_callback_data);
+    if (!p_sys->p_callback_data)
+    {
+        for(unsigned int i=0; i<i_files; i++)
+            free(ppsz_files[i]);
+        free(ppsz_files);
+        free(psz_base);
+        AccessClose(p_object);
+        return VLC_ENOMEM;
+    }
+
+    /* set up our callback struct for our main uri */
+    p_sys->p_callback_data[0].psz_uri = psz_base;
+    p_sys->p_callback_data[0].p_access = p_access;
+    archive_read_append_callback_data(p_sys->p_archive, &p_sys->p_callback_data[0]);
+
+    /* and register other volumes */
+    for(unsigned int i=0; i<i_files; i++)
+    {
+        p_sys->p_callback_data[1+i].psz_uri = ppsz_files[i];
+        p_sys->p_callback_data[1+i].p_access = p_access;
+        archive_read_append_callback_data(p_sys->p_archive, &p_sys->p_callback_data[1+i]);
+    }
+    free(ppsz_files);
+
+    if (archive_read_open2(p_sys->p_archive, &p_sys->p_callback_data[0],
+                           OpenCallback, ReadCallback, SkipCallback, CloseCallback) != ARCHIVE_OK)
     {
         msg_Err(p_access, "can't open archive: %s",
                 archive_error_string(p_sys->p_archive));
@@ -301,6 +432,12 @@ void AccessClose(vlc_object_t *p_object)
         archive_read_free(p_sys->p_archive);
     }
 
-    free(p_sys->psz_uri);
+    if (p_sys->p_callback_data)
+    {
+        for(unsigned int i=0; i<p_sys->i_callback_data; i++)
+            free(p_sys->p_callback_data[i].psz_uri);
+        free(p_sys->p_callback_data);
+    }
+
     free(p_sys);
 }



More information about the vlc-commits mailing list