]> git.sesse.net Git - vlc/commitdiff
Added support for multiple files in RAR archives.
authorLaurent Aimar <fenrir@videolan.org>
Wed, 30 Jun 2010 19:47:27 +0000 (21:47 +0200)
committerLaurent Aimar <fenrir@videolan.org>
Thu, 1 Jul 2010 18:21:18 +0000 (20:21 +0200)
It is not yet perfect as it should probably create a XSPF playlist by
reusing some code from the zip modules.

modules/access/Modules.am
modules/access/rar/access.c [new file with mode: 0644]
modules/access/rar/rar.c [new file with mode: 0644]
modules/access/rar/rar.h [new file with mode: 0644]
modules/access/rar/stream.c [new file with mode: 0644]
modules/stream_filter/Modules.am
modules/stream_filter/rar.c [deleted file]

index b7eefdf0ac9629d76284a754d1a33a12878aac04..03ea1574576120e28da7912cb2d976990bf79851 100644 (file)
@@ -53,6 +53,9 @@ SOURCES_access_imem = imem.c
 SOURCES_access_avio = avio.c avio.h
 SOURCES_access_attachment = attachment.c
 
+libstream_filter_rar_plugin_la_SOURCES = rar/rar.c rar/rar.h rar/stream.c
+libaccess_rar_plugin_la_SOURCES = rar/rar.c rar/rar.h rar/access.c
+
 libaccess_rtmp_plugin_la_SOURCES = \
         rtmp/access.c \
         rtmp/rtmp_amf_flv.c \
@@ -71,6 +74,8 @@ libvlc_LTLIBRARIES += \
        libaccess_ftp_plugin.la \
        libaccess_imem_plugin.la \
        libaccess_attachment_plugin.la \
+       libaccess_rar_plugin.la \
+       libstream_filter_rar_plugin.la \
        $(NULL)
 
 libxcb_screen_plugin_la_SOURCES = screen/xcb.c
diff --git a/modules/access/rar/access.c b/modules/access/rar/access.c
new file mode 100644 (file)
index 0000000..92dd943
--- /dev/null
@@ -0,0 +1,214 @@
+/*****************************************************************************
+ * rar.c: uncompressed RAR access
+ *****************************************************************************
+ * Copyright (C) 2008-2010 Laurent Aimar
+ * $Id$
+ *
+ * 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 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
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_access.h>
+#include <vlc_stream.h>
+#include <vlc_url.h>
+
+#include <assert.h>
+#include <limits.h>
+
+#include "rar.h"
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+static int  Open (vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin()
+    set_category(CAT_INPUT)
+    set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
+    set_description(N_("Uncompressed RAR"))
+    set_capability("access", 0)
+    set_callbacks(Open, Close)
+    add_shortcut("rar")
+vlc_module_end()
+
+/*****************************************************************************
+ * Local definitions/prototypes
+ *****************************************************************************/
+struct access_sys_t {
+    stream_t               *s;
+    rar_file_t             *file;
+    const rar_file_chunk_t *chunk;
+};
+
+static int Seek(access_t *access, uint64_t position)
+{
+    access_sys_t *sys = access->p_sys;
+    const rar_file_t *file = sys->file;
+
+    if (position > file->real_size)
+        position = file->real_size;
+
+    /* Search the chunk */
+    for (int i = 0; i < file->chunk_count; i++) {
+        sys->chunk = file->chunk[i];
+        if (position < sys->chunk->cummulated_size + sys->chunk->size)
+            break;
+    }
+    access->info.i_pos = position;
+    access->info.b_eof = false;
+
+    const uint64_t offset = sys->chunk->offset +
+                            (position - sys->chunk->cummulated_size);
+    return stream_Seek(sys->s, offset);
+}
+
+static ssize_t Read(access_t *access, uint8_t *data, size_t size)
+{
+    access_sys_t *sys = access->p_sys;
+
+    size_t total = 0;
+    while (total < size) {
+        const uint64_t chunk_end = sys->chunk->cummulated_size + sys->chunk->size;
+        int max = __MIN(__MIN((int64_t)(size - total), (int64_t)(chunk_end - access->info.i_pos)), INT_MAX);
+        if (max <= 0)
+            break;
+
+        int r = stream_Read(sys->s, data, max);
+        if (r <= 0)
+            break;
+
+        total += r;
+        if( data )
+            data += r;
+        access->info.i_pos += r;
+        if (access->info.i_pos >= chunk_end &&
+            Seek(access, access->info.i_pos))
+            break;
+    }
+    if (size > 0 && total <= 0)
+        access->info.b_eof = true;
+    return total;
+
+}
+
+static int Control(access_t *access, int query, va_list args)
+{
+    stream_t *s = access->p_sys->s;
+    switch (query) {
+    case ACCESS_CAN_SEEK: {
+        bool *b = va_arg(args, bool *);
+        return stream_Control(s, STREAM_CAN_SEEK, b);
+    }
+    case ACCESS_CAN_FASTSEEK: {
+        bool *b = va_arg(args, bool *);
+        return stream_Control(s, STREAM_CAN_FASTSEEK, b);
+    }
+    /* FIXME the following request should ask the underlying access object */
+    case ACCESS_CAN_PAUSE:
+    case ACCESS_CAN_CONTROL_PACE: {
+        bool *b = va_arg(args, bool *);
+        *b = true;
+        return VLC_SUCCESS;
+    }
+    case ACCESS_GET_PTS_DELAY: {
+        int64_t *delay = va_arg(args, int64_t *);
+        *delay = DEFAULT_PTS_DELAY;
+        return VLC_SUCCESS;
+    }
+    case ACCESS_SET_PAUSE_STATE:
+        return VLC_SUCCESS;
+
+    default:
+        return VLC_EGENERIC;
+    }
+}
+
+static int Open(vlc_object_t *object)
+{
+    access_t *access = (access_t*)object;
+
+    if (!strchr(access->psz_location, '|'))
+        return VLC_EGENERIC;
+
+    char *base = strdup(access->psz_location);
+    if (!base)
+        return VLC_EGENERIC;
+    char *name = strchr(base, '|');
+    *name++ = '\0';
+    decode_URI(base);
+
+    stream_t *s = stream_UrlNew(access, base);
+    if (!s)
+        goto error;
+    int count;
+    rar_file_t **files;
+    if (RarProbe(s) || RarParse(s, &count, &files) || count <= 0)
+        goto error;
+    rar_file_t *file = NULL;
+    for (int i = 0; i < count; i++) {
+        if (!file && !strcmp(files[i]->name, name))
+            file = files[i];
+        else
+            RarFileDelete(files[i]);
+    }
+    free(files);
+    if (!file)
+        goto error;
+
+    access_sys_t *sys = access->p_sys = malloc(sizeof(*sys));
+    sys->s    = s;
+    sys->file = file;
+
+    access->pf_read    = Read;
+    access->pf_block   = NULL;
+    access->pf_control = Control;
+    access->pf_seek    = Seek;
+
+    access_InitFields(access);
+    access->info.i_size = file->size;
+
+    Seek(access, 0);
+
+    free(base);
+    return VLC_SUCCESS;
+
+error:
+    if (s)
+        stream_Delete(s);
+    free(base);
+    return VLC_EGENERIC;
+}
+
+static void Close(vlc_object_t *object)
+{
+    access_t *access = (access_t*)object;
+    access_sys_t *sys = access->p_sys;
+
+    stream_Delete(sys->s);
+    RarFileDelete(sys->file);
+    free(sys);
+}
+
diff --git a/modules/access/rar/rar.c b/modules/access/rar/rar.c
new file mode 100644 (file)
index 0000000..517c54f
--- /dev/null
@@ -0,0 +1,305 @@
+/*****************************************************************************
+ * rar.h: uncompressed RAR parser
+ *****************************************************************************
+ * Copyright (C) 2008-2010 Laurent Aimar
+ * $Id$
+ *
+ * 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 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
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_stream.h>
+
+#include <assert.h>
+#include <limits.h>
+
+#include "rar.h"
+
+static const uint8_t rar_marker[] = {
+    0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00
+};
+static const int rar_marker_size = sizeof(rar_marker);
+
+void RarFileDelete(rar_file_t *file)
+{
+    for (int i = 0; i < file->chunk_count; i++)
+        free(file->chunk[i]);
+    free(file->chunk);
+    free(file->name);
+    free(file);
+}
+
+typedef struct {
+    uint16_t crc;
+    uint8_t  type;
+    uint16_t flags;
+    uint16_t size;
+    uint32_t add_size;
+} rar_block_t;
+
+enum {
+    RAR_BLOCK_MARKER = 0x72,
+    RAR_BLOCK_ARCHIVE = 0x73,
+    RAR_BLOCK_FILE = 0x74,
+    RAR_BLOCK_END = 0x7b,
+};
+enum {
+    RAR_BLOCK_END_HAS_NEXT = 0x0001,
+};
+enum {
+    RAR_BLOCK_FILE_HAS_PREVIOUS = 0x0001,
+    RAR_BLOCK_FILE_HAS_NEXT     = 0x0002,
+    RAR_BLOCK_FILE_HAS_HIGH     = 0x0100,
+};
+
+static int PeekBlock(stream_t *s, rar_block_t *hdr)
+{
+    const uint8_t *peek;
+    int peek_size = stream_Peek(s, &peek, 11);
+
+    if (peek_size < 7)
+        return VLC_EGENERIC;
+
+    hdr->crc   = GetWLE(&peek[0]);
+    hdr->type  = peek[2];
+    hdr->flags = GetWLE(&peek[3]);
+    hdr->size  = GetWLE(&peek[5]);
+    hdr->add_size = 0;
+    if (hdr->flags & 0x8000) {
+        if (peek_size < 11)
+            return VLC_EGENERIC;
+        hdr->add_size = GetDWLE(&peek[7]);
+    }
+
+    if (hdr->size < 7)
+        return VLC_EGENERIC;
+    return VLC_SUCCESS;
+}
+static int SkipBlock(stream_t *s, const rar_block_t *hdr)
+{
+    uint64_t size = (uint64_t)hdr->size + hdr->add_size;
+
+    while (size > 0) {
+        int skip = __MIN(size, INT_MAX);
+        if (stream_Read(s, NULL, skip) < skip)
+            return VLC_EGENERIC;
+
+        size -= skip;
+    }
+    return VLC_SUCCESS;
+}
+
+static int IgnoreBlock(stream_t *s, int block)
+{
+    /* */
+    rar_block_t bk;
+    if (PeekBlock(s, &bk) || bk.type != block)
+        return VLC_EGENERIC;
+    return SkipBlock(s, &bk);
+}
+
+static int SkipEnd(stream_t *s, const rar_block_t *hdr)
+{
+    if (!(hdr->flags & RAR_BLOCK_END_HAS_NEXT))
+        return VLC_EGENERIC;
+
+    if (SkipBlock(s, hdr))
+        return VLC_EGENERIC;
+
+    /* Now, we need to look for a marker block,
+     * It seems that there is garbage at EOF */
+    for (;;) {
+        const uint8_t *peek;
+
+        if (stream_Peek(s, &peek, rar_marker_size) < rar_marker_size)
+            return VLC_EGENERIC;
+
+        if (!memcmp(peek, rar_marker, rar_marker_size))
+            break;
+
+        if (stream_Read(s, NULL, 1) != 1)
+            return VLC_EGENERIC;
+    }
+
+    /* Skip marker and archive blocks */
+    if (IgnoreBlock(s, RAR_BLOCK_MARKER))
+        return VLC_EGENERIC;
+    if (IgnoreBlock(s, RAR_BLOCK_ARCHIVE))
+        return VLC_EGENERIC;
+
+    return VLC_SUCCESS;
+}
+
+static int SkipFile(stream_t *s, int *count, rar_file_t ***file, const rar_block_t *hdr)
+{
+    const uint8_t *peek;
+
+    int min_size = 7+21;
+    if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH)
+        min_size += 8;
+    if (hdr->size < (unsigned)min_size)
+        return VLC_EGENERIC;
+
+    if (stream_Peek(s, &peek, min_size) < min_size)
+        return VLC_EGENERIC;
+
+    /* */
+    uint32_t file_size_low = GetDWLE(&peek[7+4]);
+    uint8_t  method = peek[7+18];
+    uint16_t name_size = GetWLE(&peek[7+19]);
+    uint32_t file_size_high = 0;
+    if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH)
+        file_size_high = GetDWLE(&peek[7+25]);
+    const uint64_t file_size = ((uint64_t)file_size_high << 32) | file_size_low;
+
+    char *name = calloc(1, name_size + 1);
+    if (!name)
+        return VLC_EGENERIC;
+
+    const int name_offset = (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH) ? (7+33) : (7+25);
+    if (name_offset + name_size <= hdr->size) {
+        const int max_size = name_offset + name_size;
+        if (stream_Peek(s, &peek, max_size) < max_size) {
+            free(name);
+            return VLC_EGENERIC;
+        }
+        memcpy(name, &peek[name_offset], name_size);
+    }
+
+    if (method != 0x30) {
+        msg_Warn(s, "Ignoring compressed file %s (method=0x%2.2x)", name, method);
+        goto exit;
+    }
+
+    /* */
+    rar_file_t *current = *count > 0 ? (*file)[*count - 1] : NULL;
+    if (current &&
+        (current->is_complete ||
+          current->size != file_size ||
+          strcmp(current->name, name) ||
+          (hdr->flags & RAR_BLOCK_FILE_HAS_PREVIOUS) == 0))
+        current = NULL;
+
+    if (!current) {
+        current = malloc(sizeof(*current));
+        if (!current)
+            goto exit;
+        TAB_APPEND(*count, *file, current);
+
+        current->name = name;
+        current->size = file_size;
+        current->is_complete = false;
+        current->real_size = 0;
+        TAB_INIT(current->chunk_count, current->chunk);
+
+        name = NULL;
+    }
+
+    /* Append chunks */
+    rar_file_chunk_t *chunk = malloc(sizeof(*chunk));
+    if (chunk) {
+        chunk->offset = stream_Tell(s) + hdr->size;
+        chunk->size = hdr->add_size;
+        chunk->cummulated_size = 0;
+        if (current->chunk_count > 0) {
+            rar_file_chunk_t *previous = current->chunk[current->chunk_count-1];
+
+            chunk->cummulated_size += previous->cummulated_size +
+                                      previous->size;
+        }
+
+        TAB_APPEND(current->chunk_count, current->chunk, chunk);
+
+        current->real_size += hdr->add_size;
+    }
+    if ((hdr->flags & RAR_BLOCK_FILE_HAS_NEXT) == 0)
+        current->is_complete = true;
+
+exit:
+    /* */
+    free(name);
+
+    /* We stop on the first non empty file if we cannot seek */
+    if (current) {
+        bool can_seek = false;
+        stream_Control(s, STREAM_CAN_SEEK, &can_seek);
+        if (!can_seek && current->size > 0)
+            return VLC_EGENERIC;
+    }
+
+    if (SkipBlock(s, hdr))
+        return VLC_EGENERIC;
+    return VLC_SUCCESS;
+}
+
+int RarProbe(stream_t *s)
+{
+    const uint8_t *peek;
+    if (stream_Peek(s, &peek, rar_marker_size) < rar_marker_size)
+        return VLC_EGENERIC;
+    if (memcmp(peek, rar_marker, rar_marker_size))
+        return VLC_EGENERIC;
+    return VLC_SUCCESS;
+}
+
+int RarParse(stream_t *s, int *count, rar_file_t ***file)
+{
+    *count = 0;
+    *file = NULL;
+
+    /* Skip marker */
+    if (IgnoreBlock(s, RAR_BLOCK_MARKER))
+        return VLC_EGENERIC;
+
+    /* Skip archive  */
+    if (IgnoreBlock(s, RAR_BLOCK_ARCHIVE))
+        return VLC_EGENERIC;
+
+    /* */
+    for (;;) {
+        rar_block_t bk;
+        int ret;
+
+        if (PeekBlock(s, &bk))
+            break;
+
+        switch(bk.type) {
+        case RAR_BLOCK_END:
+            ret = SkipEnd(s, &bk);
+            break;
+        case RAR_BLOCK_FILE:
+            ret = SkipFile(s, count, file, &bk);
+            break;
+        default:
+            ret = SkipBlock(s, &bk);
+            break;
+        }
+        if (ret)
+            break;
+    }
+
+    return VLC_SUCCESS;
+}
+
diff --git a/modules/access/rar/rar.h b/modules/access/rar/rar.h
new file mode 100644 (file)
index 0000000..7506934
--- /dev/null
@@ -0,0 +1,43 @@
+/*****************************************************************************
+ * rar.h: uncompressed RAR parser
+ *****************************************************************************
+ * Copyright (C) 2008-2010 Laurent Aimar
+ * $Id$
+ *
+ * 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 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.
+ *****************************************************************************/
+
+typedef struct {
+    uint64_t offset;
+    uint64_t size;
+    uint64_t cummulated_size;
+} rar_file_chunk_t;
+
+typedef struct {
+    char     *name;
+    uint64_t size;
+    bool     is_complete;
+
+    int              chunk_count;
+    rar_file_chunk_t **chunk;
+    uint64_t         real_size;  /* Gathered size */
+} rar_file_t;
+
+int  RarProbe(stream_t *);
+void RarFileDelete(rar_file_t *);
+int  RarParse(stream_t *, int *, rar_file_t ***);
+
diff --git a/modules/access/rar/stream.c b/modules/access/rar/stream.c
new file mode 100644 (file)
index 0000000..e7c92a4
--- /dev/null
@@ -0,0 +1,164 @@
+/*****************************************************************************
+ * rar.c: uncompressed RAR stream filter
+ *****************************************************************************
+ * Copyright (C) 2008-2010 Laurent Aimar
+ * $Id$
+ *
+ * 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 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
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_stream.h>
+#include <vlc_url.h>
+
+#include <assert.h>
+#include <limits.h>
+
+#include "rar.h"
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+static int  Open (vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin()
+    set_category(CAT_INPUT)
+    set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
+    set_description(N_("Uncompressed RAR"))
+    set_capability("stream_filter", 1)
+    set_callbacks(Open, Close)
+    add_shortcut("rar")
+vlc_module_end()
+
+/****************************************************************************
+ * Local definitions/prototypes
+ ****************************************************************************/
+struct stream_sys_t {
+    stream_t *payload;
+};
+
+static int Read(stream_t *s, void *data, unsigned size)
+{
+    return stream_Read(s->p_sys->payload, data, size);
+}
+
+static int Peek( stream_t *s, const uint8_t **data, unsigned size)
+{
+    return stream_Peek(s->p_sys->payload, data, size);
+}
+
+static int Control(stream_t *s, int query, va_list args)
+{
+    switch (query) {
+    case STREAM_GET_CONTENT_TYPE: {
+        char **mime = va_arg(args, char **);
+        *mime = strdup("audio/x-mpegurl");
+        return VLC_EGENERIC;
+    }
+    default:
+        return stream_vaControl(s->p_sys->payload, query, args);
+    }
+}
+
+static int Open(vlc_object_t *object)
+{
+    stream_t *s = (stream_t*)object;
+
+    if (RarProbe(s->p_source))
+        return VLC_EGENERIC;
+
+    int count;
+    rar_file_t **files;
+    if (RarParse(s->p_source, &count, &files) || count <= 0) {
+        msg_Err(s, "Invalid or unsupported RAR archive");
+        free(files);
+        return VLC_EGENERIC;
+    }
+
+    /* TODO use xspf to have node for directories
+     * Reusing WriteXSPF from the zip access is probably a good idea
+     * (becareful about '\' and '/'.
+     */
+    char *base;
+    char *encoded = encode_URI_component(s->psz_path);
+    if (!encoded || asprintf(&base, "rar://%s", encoded) < 0)
+        base = NULL;
+    free(encoded);
+
+    char *data = strdup("#EXTM3U\n");
+    for (int i = 0; i < count; i++) {
+        rar_file_t *f = files[i];
+        char *next;
+        if (base && data &&
+            asprintf(&next, "%s"
+                            "#EXTINF:,,%s\n"
+                            "%s|%s\n",
+                            data, f->name, base, f->name) >= 0) {
+            free(data);
+            data = next;
+        }
+        RarFileDelete(f);
+    }
+    free(files);
+    if (!data)
+        return VLC_EGENERIC;
+    stream_t *payload = stream_MemoryNew(s, (uint8_t*)data, strlen(data), false);
+    if (!payload) {
+        free(data);
+        return VLC_EGENERIC;
+    }
+
+    s->pf_read = Read;
+    s->pf_peek = Peek;
+    s->pf_control = Control;
+
+    stream_sys_t *sys = s->p_sys = malloc(sizeof(*sys));
+    if (!sys) {
+        stream_Delete(payload);
+        return VLC_ENOMEM;
+    }
+    sys->payload = payload;
+
+    char *tmp;
+    if (asprintf(&tmp, "%s.m3u", s->psz_path) < 0) {
+        Close(object);
+        return VLC_ENOMEM;
+    }
+    free(s->psz_path);
+    s->psz_path = tmp;
+
+    return VLC_SUCCESS;
+}
+
+static void Close(vlc_object_t *object)
+{
+    stream_t *s = (stream_t*)object;
+    stream_sys_t *sys = s->p_sys;
+
+    stream_Delete(sys->payload);
+    free(sys);
+}
+
index 14bc22f2aacbaefa587e5333b21f85debe238122..91bce81ba43da1fa05e715282f28cba210778791 100644 (file)
@@ -1,10 +1,8 @@
 SOURCES_decomp = decomp.c
 SOURCES_stream_filter_record = record.c
-SOURCES_stream_filter_rar = rar.c
 
 libvlc_LTLIBRARIES += \
    libstream_filter_record_plugin.la \
-   libstream_filter_rar_plugin.la \
    $(NULL)
 if !HAVE_WIN32
 if !HAVE_WINCE
diff --git a/modules/stream_filter/rar.c b/modules/stream_filter/rar.c
deleted file mode 100644 (file)
index ddc3c46..0000000
+++ /dev/null
@@ -1,608 +0,0 @@
-/*****************************************************************************
- * rar.c: uncompressed RAR stream filter (only the biggest file is extracted)
- *****************************************************************************
- * Copyright (C) 2008 Laurent Aimar
- * $Id$
- *
- * 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 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
-
-#include <vlc_common.h>
-#include <vlc_plugin.h>
-#include <vlc_stream.h>
-
-#include <assert.h>
-#include <limits.h>
-
-/*****************************************************************************
- * Module descriptor
- *****************************************************************************/
-static int  Open ( vlc_object_t * );
-static void Close( vlc_object_t * );
-
-vlc_module_begin()
-    set_category( CAT_INPUT )
-    set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
-    set_description( N_("Uncompressed RAR") )
-    set_capability( "stream_filter", 1 )
-    set_callbacks( Open, Close )
-    add_shortcut( "rar" )
-vlc_module_end()
-
-/*****************************************************************************
- *
- *****************************************************************************/
-static const uint8_t p_rar_marker[] = {
-    0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00
-};
-static const int i_rar_marker = sizeof(p_rar_marker);
-
-typedef struct
-{
-    uint64_t i_offset;
-    uint64_t i_size;
-    uint64_t i_cummulated_size;
-} rar_file_chunk_t;
-typedef struct
-{
-    char     *psz_name;
-    uint64_t i_size;
-    bool     b_complete;
-
-    int              i_chunk;
-    rar_file_chunk_t **pp_chunk;
-    uint64_t         i_real_size;  /* Gathered size */
-} rar_file_t;
-
-static void RarFileDelete( rar_file_t * );
-static int  RarParse( stream_t *, int *pi_count, rar_file_t ***ppp_file );
-
-struct stream_sys_t
-{
-    rar_file_t *p_file;
-    const rar_file_chunk_t *p_chunk;
-
-    uint64_t i_position;
-
-    uint8_t *p_peek_alloc;
-    uint8_t *p_peek;
-    unsigned int i_peek;
-};
-
-
-/****************************************************************************
- * Local prototypes
- ****************************************************************************/
-static int  Read   ( stream_t *, void *p_read, unsigned int i_read );
-static int  Peek   ( stream_t *, const uint8_t **pp_peek, unsigned int i_peek );
-static int  Control( stream_t *, int i_query, va_list );
-static int  Seek   ( stream_t *s, uint64_t i_position );
-
-/****************************************************************************
- * Open
- ****************************************************************************/
-static int Open ( vlc_object_t *p_this )
-{
-    stream_t *s = (stream_t*)p_this;
-    stream_sys_t *p_sys;
-
-    /* */
-    const uint8_t *p_peek;
-    if( stream_Peek( s->p_source, &p_peek, i_rar_marker ) < i_rar_marker )
-        return VLC_EGENERIC;
-    if( memcmp( p_peek, p_rar_marker, i_rar_marker ) )
-        return VLC_EGENERIC;
-
-    /* */
-    s->pf_read = Read;
-    s->pf_peek = Peek;
-    s->pf_control = Control;
-
-    s->p_sys = p_sys = malloc( sizeof( *p_sys ) );
-    if( !p_sys )
-        return VLC_ENOMEM;
-
-    /* */
-    p_sys->p_file = NULL;
-    p_sys->i_position = 0;
-    p_sys->p_chunk = NULL;
-
-    p_sys->p_peek_alloc = NULL;
-    p_sys->p_peek = NULL;
-    p_sys->i_peek = 0;
-
-    /* */
-    int i_count;
-    rar_file_t **pp_file;
-    if( RarParse( s, &i_count, &pp_file ) )
-    {
-        i_count = 0;
-        pp_file = NULL;
-    }
-
-    /* Select the longest file */
-    p_sys->p_file = NULL;
-    for( int i = 0; i < i_count; i++ )
-    {
-        if( !p_sys->p_file || p_sys->p_file->i_size < pp_file[i]->i_size )
-            p_sys->p_file = pp_file[i];
-    }
-    for( int i = 0; i < i_count; i++ )
-    {
-        if( pp_file[i] != p_sys->p_file )
-            RarFileDelete( pp_file[i] );
-    }
-    free( pp_file );
-
-    if( !p_sys->p_file || p_sys->p_file->i_chunk <= 0 || p_sys->p_file->i_size <= 0 )
-    {
-        msg_Err( s, "Invalid or unsupported RAR archive" );
-        if( p_sys->p_file )
-            RarFileDelete( p_sys->p_file );
-        free( p_sys );
-        return VLC_EGENERIC;
-    }
-
-    /* */
-    Seek( s, 0 );
-
-    /* */
-    const rar_file_t *p_file = p_sys->p_file;
-    msg_Dbg( s, "Using RAR stream filter for '%s' %"PRId64"(expected %"PRId64") bytes in %d chunks",
-             p_file->psz_name, p_file->i_real_size, p_file->i_size, p_file->i_chunk );
-
-    return VLC_SUCCESS;
-}
-
-/****************************************************************************
- * Close
- ****************************************************************************/
-static void Close( vlc_object_t *p_this )
-{
-    stream_t *s = (stream_t*)p_this;
-    stream_sys_t *p_sys = s->p_sys;
-
-    RarFileDelete( p_sys->p_file );
-    free( p_sys->p_peek_alloc );
-    free( p_sys );
-}
-
-/****************************************************************************
- * Stream filters functions
- ****************************************************************************/
-static int Read( stream_t *s, void *p_read, unsigned int i_read )
-{
-    stream_sys_t *p_sys = s->p_sys;
-    uint8_t *p_data = p_read;
-    unsigned int i_total = 0;
-
-    if( p_sys->i_peek > 0 && i_read > 0 )
-    {
-        const unsigned int i_copy = __MIN( i_read, p_sys->i_peek );
-
-        if( p_data )
-        {
-            memcpy( p_data, p_sys->p_peek, i_copy );
-            p_data += i_copy;
-        }
-
-        p_sys->i_peek -= i_copy;
-        p_sys->p_peek += i_copy;
-        i_total += i_copy;
-    }
-
-    while( i_total < i_read )
-    {
-        const uint64_t i_chunk_end = p_sys->p_chunk->i_cummulated_size + p_sys->p_chunk->i_size;
-
-        int i_max = __MIN( i_read - i_total, i_chunk_end - p_sys->i_position );
-        if( i_max <= 0 )
-            break;
-
-        int i_real = stream_Read( s->p_source, p_data, i_max );
-        if( i_real <= 0 )
-            break;
-
-        i_total += i_real;
-        if( p_data )
-            p_data += i_real;
-        p_sys->i_position += i_real;
-        if( p_sys->i_position >= i_chunk_end )
-        {
-            if( Seek( s, p_sys->i_position ) )
-                break;
-        }
-    }
-    return i_total;
-}
-
-static int Peek( stream_t *s, const uint8_t **pp_peek, unsigned int i_peek )
-{
-    stream_sys_t *p_sys = s->p_sys;
-
-    if( i_peek <= p_sys->i_peek )
-    {
-        *pp_peek = p_sys->p_peek;
-        return i_peek;
-    }
-
-    /* */
-    uint8_t *p_peek = malloc( i_peek );
-    if( !p_peek )
-        return 0;
-
-    /* XXX yes stream_Read on ourself */
-    int i_read = stream_Read( s, p_peek, i_peek );
-    if( i_read <= 0 )
-    {
-        free( p_peek );
-        return i_read;
-    }
-
-    free( p_sys->p_peek_alloc );
-
-    p_sys->p_peek_alloc =
-    p_sys->p_peek       = p_peek;
-    p_sys->i_peek       = i_read;
-
-    *pp_peek = p_sys->p_peek;
-    return p_sys->i_peek;
-}
-
-static int Control( stream_t *s, int i_query, va_list args )
-{
-    stream_sys_t *p_sys = s->p_sys;
-
-    switch( i_query )
-    {
-    /* */
-    case STREAM_SET_POSITION:
-    {
-        uint64_t i_position = va_arg( args, uint64_t );
-        return Seek( s, i_position );
-    }
-
-    case STREAM_GET_POSITION:
-    {
-        uint64_t *pi_position = va_arg( args, uint64_t* );
-        *pi_position = p_sys->i_position - p_sys->i_peek;
-        return VLC_SUCCESS;
-    }
-
-    case STREAM_GET_SIZE:
-    {
-        uint64_t *pi_size = (uint64_t*)va_arg( args, uint64_t* );
-        *pi_size = p_sys->p_file->i_real_size;
-        return VLC_SUCCESS;
-    }
-
-    /* */
-    case STREAM_GET_CONTENT_TYPE: /* arg1= char ** */
-        return VLC_EGENERIC;
-
-    case STREAM_UPDATE_SIZE: /* TODO maybe we should update i_real_size from file size and chunk offset ? */
-    case STREAM_CONTROL_ACCESS:
-    case STREAM_CAN_SEEK:
-    case STREAM_CAN_FASTSEEK:
-    case STREAM_SET_RECORD_STATE:
-        return stream_vaControl( s->p_source, i_query, args );
-    default:
-        return VLC_EGENERIC;
-    }
-}
-
-/****************************************************************************
- * Helpers
- ****************************************************************************/
-static int Seek( stream_t *s, uint64_t i_position )
-{
-    stream_sys_t *p_sys = s->p_sys;
-
-    if( i_position > p_sys->p_file->i_real_size )
-        i_position = p_sys->p_file->i_real_size;
-
-    /* Search the chunk */
-    const rar_file_t *p_file = p_sys->p_file;
-    for( int i = 0; i < p_file->i_chunk; i++ )
-    {
-        p_sys->p_chunk = p_file->pp_chunk[i];
-        if( i_position < p_sys->p_chunk->i_cummulated_size + p_sys->p_chunk->i_size )
-            break;
-    }
-    p_sys->i_position = i_position;
-    p_sys->i_peek     = 0;
-
-    const uint64_t i_seek = p_sys->p_chunk->i_offset +
-                            ( i_position - p_sys->p_chunk->i_cummulated_size );
-    return stream_Seek( s->p_source, i_seek );
-}
-
-/* Rar parser */
-static void RarFileDelete( rar_file_t *p_file )
-{
-    for( int i = 0; i < p_file->i_chunk; i++ )
-        free( p_file->pp_chunk[i] );
-    free( p_file->pp_chunk );
-    free( p_file->psz_name );
-    free( p_file );
-}
-
-typedef struct
-{
-    uint16_t i_crc;
-    uint8_t  i_type;
-    uint16_t i_flags;
-    uint16_t i_size;
-    uint32_t i_add_size;
-} rar_block_t;
-
-enum
-{
-    RAR_BLOCK_MARKER = 0x72,
-    RAR_BLOCK_ARCHIVE = 0x73,
-    RAR_BLOCK_FILE = 0x74,
-    RAR_BLOCK_END = 0x7b,
-};
-enum
-{
-    RAR_BLOCK_END_HAS_NEXT = 0x0001,
-};
-enum
-{
-    RAR_BLOCK_FILE_HAS_PREVIOUS = 0x0001,
-    RAR_BLOCK_FILE_HAS_NEXT     = 0x0002,
-    RAR_BLOCK_FILE_HAS_HIGH     = 0x0100,
-};
-
-static int PeekBlock( stream_t *s, rar_block_t *p_hdr )
-{
-    const uint8_t *p_peek;
-    int i_peek = stream_Peek( s->p_source, &p_peek, 11 );
-
-    if( i_peek < 7 )
-        return VLC_EGENERIC;
-
-    p_hdr->i_crc   = GetWLE( &p_peek[0] );
-    p_hdr->i_type  = p_peek[2];
-    p_hdr->i_flags = GetWLE( &p_peek[3] );
-    p_hdr->i_size  = GetWLE( &p_peek[5] );
-    p_hdr->i_add_size = 0;
-    if( p_hdr->i_flags & 0x8000 )
-    {
-        if( i_peek < 11 )
-            return VLC_EGENERIC;
-        p_hdr->i_add_size = GetDWLE( &p_peek[7] );
-    }
-
-    if( p_hdr->i_size < 7 )
-        return VLC_EGENERIC;
-    return VLC_SUCCESS;
-}
-static int SkipBlock( stream_t *s, const rar_block_t *p_hdr )
-{
-    uint64_t i_size = (uint64_t)p_hdr->i_size + p_hdr->i_add_size;
-
-    while( i_size > 0 )
-    {
-        int i_skip = __MIN( i_size, INT_MAX );
-        if( stream_Read( s->p_source, NULL, i_skip ) < i_skip )
-            return VLC_EGENERIC;
-
-        i_size -= i_skip;
-    }
-    return VLC_SUCCESS;
-}
-
-static int IgnoreBlock( stream_t *s, int i_block )
-{
-    /* */
-    rar_block_t bk;
-    if( PeekBlock( s, &bk ) || bk.i_type != i_block )
-        return VLC_EGENERIC;
-    return SkipBlock( s, &bk );
-}
-
-static int SkipEnd( stream_t *s, const rar_block_t *p_hdr )
-{
-    if( !(p_hdr->i_flags & RAR_BLOCK_END_HAS_NEXT) )
-        return VLC_EGENERIC;
-
-    if( SkipBlock( s, p_hdr ) )
-        return VLC_EGENERIC;
-
-    /* Now, we need to look for a marker block,
-     * It seems that there is garbage at EOF */
-    for( ;; )
-    {
-        const uint8_t *p_peek;
-
-        if( stream_Peek( s->p_source, &p_peek, i_rar_marker ) < i_rar_marker )
-            return VLC_EGENERIC;
-
-        if( !memcmp( p_peek, p_rar_marker, i_rar_marker ) )
-            break;
-
-        if( stream_Read( s->p_source, NULL, 1 ) != 1 )
-            return VLC_EGENERIC;
-    }
-
-    /* Skip marker and archive blocks */
-    if( IgnoreBlock( s, RAR_BLOCK_MARKER ) )
-        return VLC_EGENERIC;
-    if( IgnoreBlock( s, RAR_BLOCK_ARCHIVE ) )
-        return VLC_EGENERIC;
-
-    return VLC_SUCCESS;
-}
-
-static int SkipFile( stream_t *s, int *pi_count, rar_file_t ***ppp_file, const rar_block_t *p_hdr )
-{
-    const uint8_t *p_peek;
-
-    int i_min_size = 7+21;
-    if( p_hdr->i_flags & RAR_BLOCK_FILE_HAS_HIGH )
-        i_min_size += 8;
-    if( p_hdr->i_size < (unsigned)i_min_size )
-        return VLC_EGENERIC;
-
-    if( stream_Peek( s->p_source, &p_peek, i_min_size ) < i_min_size )
-        return VLC_EGENERIC;
-
-    /* */
-    uint32_t i_file_size_low = GetDWLE( &p_peek[7+4] );
-    uint8_t  i_method = p_peek[7+18];
-    uint16_t i_name_size = GetWLE( &p_peek[7+19] );
-    uint32_t i_file_size_high = 0;
-    if( p_hdr->i_flags & RAR_BLOCK_FILE_HAS_HIGH )
-        i_file_size_high = GetDWLE( &p_peek[7+25] );
-    const uint64_t i_file_size = ((uint64_t)i_file_size_high << 32) | i_file_size_low;
-
-    char *psz_name = calloc( 1, i_name_size + 1 );
-    if( !psz_name )
-        return VLC_EGENERIC;
-
-    const int i_name_offset = (p_hdr->i_flags & RAR_BLOCK_FILE_HAS_HIGH) ? (7+33) : (7+25);
-    if( i_name_offset + i_name_size <= p_hdr->i_size )
-    {
-        const int i_max_size = i_name_offset + i_name_size;
-        if( stream_Peek( s->p_source, &p_peek, i_max_size ) < i_max_size )
-        {
-            free( psz_name );
-            return VLC_EGENERIC;
-        }
-        memcpy( psz_name, &p_peek[i_name_offset], i_name_size );
-    }
-
-    if( i_method != 0x30 )
-    {
-        msg_Warn( s, "Ignoring compressed file %s (method=0x%2.2x)", psz_name, i_method );
-        goto exit;
-    }
-
-    /* */
-    rar_file_t *p_current = *pi_count > 0 ? (*ppp_file)[*pi_count - 1] : NULL;
-    if( p_current &&
-        ( p_current->b_complete ||
-          p_current->i_size != i_file_size ||
-          strcmp( p_current->psz_name, psz_name ) ||
-          ( p_hdr->i_flags & RAR_BLOCK_FILE_HAS_PREVIOUS ) == 0 ) )
-        p_current = NULL;
-
-    if( !p_current )
-    {
-        p_current = malloc( sizeof( *p_current ) );
-        if( !p_current )
-            goto exit;
-        TAB_APPEND( *pi_count, *ppp_file, p_current );
-
-        p_current->psz_name = psz_name;
-        p_current->i_size = i_file_size;
-        p_current->b_complete = false;
-        p_current->i_real_size = 0;
-        TAB_INIT( p_current->i_chunk, p_current->pp_chunk );
-
-        psz_name = NULL;
-    }
-
-    /* Append chunks */
-    rar_file_chunk_t *p_chunk = malloc( sizeof( *p_chunk ) );
-    if( p_chunk )
-    {
-        p_chunk->i_offset = stream_Tell( s->p_source ) + p_hdr->i_size;
-        p_chunk->i_size = p_hdr->i_add_size;
-        p_chunk->i_cummulated_size = 0;
-        if( p_current->i_chunk > 0 )
-        {
-            rar_file_chunk_t *p_previous = p_current->pp_chunk[p_current->i_chunk-1];
-
-            p_chunk->i_cummulated_size += p_previous->i_cummulated_size +
-                                          p_previous->i_size;
-        }
-
-        TAB_APPEND( p_current->i_chunk, p_current->pp_chunk, p_chunk );
-
-        p_current->i_real_size += p_hdr->i_add_size;
-    }
-    if( ( p_hdr->i_flags & RAR_BLOCK_FILE_HAS_NEXT ) == 0 )
-        p_current->b_complete = true;
-
-exit:
-    /* */
-    free( psz_name );
-
-    /* We stop on the first non empty file if we cannot seek */
-    if( p_current )
-    {
-        bool b_can_seek = false;
-        stream_Control( s->p_source, STREAM_CAN_SEEK, &b_can_seek );
-        if( !b_can_seek && p_current->i_size > 0 )
-            return VLC_EGENERIC;
-    }
-
-    if( SkipBlock( s, p_hdr ) )
-        return VLC_EGENERIC;
-    return VLC_SUCCESS;
-}
-
-static int RarParse( stream_t *s, int *pi_count, rar_file_t ***ppp_file )
-{
-    *pi_count = 0;
-    *ppp_file = NULL;
-
-    /* Skip marker */
-    if( IgnoreBlock( s, RAR_BLOCK_MARKER ) )
-        return VLC_EGENERIC;
-
-    /* Skip archive  */
-    if( IgnoreBlock( s, RAR_BLOCK_ARCHIVE ) )
-        return VLC_EGENERIC;
-
-    /* */
-    for( ;; )
-    {
-        rar_block_t bk;
-        int i_ret;
-
-        if( PeekBlock( s, &bk ) )
-            break;
-
-        switch( bk.i_type )
-        {
-        case RAR_BLOCK_END:
-            i_ret = SkipEnd( s, &bk );
-            break;
-        case RAR_BLOCK_FILE:
-            i_ret = SkipFile( s, pi_count, ppp_file, &bk );
-            break;
-        default:
-            i_ret = SkipBlock( s, &bk );
-            break;
-        }
-        if( i_ret )
-            break;
-    }
-
-    return VLC_SUCCESS;
-}
-