From f8bb195c3668fe49b70ff210e5904e131a357159 Mon Sep 17 00:00:00 2001 From: Francois Cartegnie Date: Wed, 16 Jul 2014 13:25:56 +0900 Subject: [PATCH] access/stream_filter: add libarchive Allows decompression and access through rar, lha, tar, ... Mostly unseekable. --- NEWS | 1 + configure.ac | 5 + modules/MODULES_LIST | 1 + modules/access/Makefile.am | 6 + modules/access/archive/access.c | 303 +++++++++++++++++++++++++++++++ modules/access/archive/archive.c | 111 +++++++++++ modules/access/archive/archive.h | 39 ++++ modules/access/archive/stream.c | 223 +++++++++++++++++++++++ po/POTFILES.in | 1 + 9 files changed, 690 insertions(+) create mode 100644 modules/access/archive/access.c create mode 100644 modules/access/archive/archive.c create mode 100644 modules/access/archive/archive.h create mode 100644 modules/access/archive/stream.c diff --git a/NEWS b/NEWS index 78c10ce6df..1088a51675 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ Changes between 2.2.x and 3.0.0-git: Access: * Support HDS (Http Dynamic Streaming) from Adobe (f4m, f4v, etc.) * New SMB access module using libdsm + * Can now decompress and extract through libarchive Service Discovery: * New NetBios service discovery using libdsm diff --git a/configure.ac b/configure.ac index 677cc67851..065454160c 100644 --- a/configure.ac +++ b/configure.ac @@ -1595,6 +1595,11 @@ dnl EXTEND_HELP_STRING([Input plugins:]) +dnl +dnl libarchive access module +dnl +PKG_ENABLE_MODULES_VLC([ARCHIVE], [access_archive], [libarchive >= 2.8.5], (libarchive support), [auto]) + dnl dnl live555 input dnl diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST index ae980e716c..6fb7014ee8 100644 --- a/modules/MODULES_LIST +++ b/modules/MODULES_LIST @@ -36,6 +36,7 @@ $Id$ * android_surface: video output for Android, based on Surface * antiflicker: anti-flicker video filter * araw: Pseudo audio decoder for raw PCM + * archive: libarchive based access and stream filter * asf: ASF demuxer * atmo: Ambilight-like video-output * attachment: Attachment access module diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am index 9116ee1b7e..c2818432ae 100644 --- a/modules/access/Makefile.am +++ b/modules/access/Makefile.am @@ -64,6 +64,12 @@ libzip_plugin_la_LIBADD += libunzip.la endif endif +libaccess_archive_plugin_la_SOURCES = access/archive/access.c access/archive/stream.c \ + access/archive/archive.h access/archive/archive.c +libaccess_archive_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(accessdir)' +libaccess_archive_plugin_la_LIBADD = $(ARCHIVE_LIBS) +access_LTLIBRARIES += $(LTLIBaccess_archive) +EXTRA_LTLIBRARIES += libaccess_archive_plugin.la ### Audio capture ### diff --git a/modules/access/archive/access.c b/modules/access/archive/access.c new file mode 100644 index 0000000000..789d347f49 --- /dev/null +++ b/modules/access/archive/access.c @@ -0,0 +1,303 @@ +/***************************************************************************** + * access.c: libarchive based access + ***************************************************************************** + * Copyright (C) 2014 Videolan Team + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "archive.h" + +#include +#include +#include + +#include +#include + +struct access_sys_t +{ + struct archive *p_archive; + bool b_source_canseek; + uint8_t buffer[ARCHIVE_READ_SIZE]; + + struct archive_entry *p_entry; + stream_t *p_stream; + char *psz_uri; + bool b_seekable; /* Is our archive type seekable ? */ +}; + +static ssize_t Read(access_t *p_access, uint8_t *p_data, size_t i_size) +{ + access_sys_t *p_sys = p_access->p_sys; + + size_t i_read = 0; + + i_read = archive_read_data(p_sys->p_archive, p_data, i_size); + + if (i_read > 0) + p_access->info.i_pos += i_read; + + if (i_size > 0 && i_read <= 0) + p_access->info.b_eof = true; + + return i_read; +} + +static int Seek(access_t *p_access, uint64_t i_pos) +{ + access_sys_t *p_sys = p_access->p_sys; + + if (!p_sys->b_seekable) + return VLC_EGENERIC; + + int64_t i_ret = archive_seek_data(p_sys->p_archive, i_pos, SEEK_SET); + if ( i_ret < ARCHIVE_OK ) + return VLC_EGENERIC; + p_access->info.i_pos = i_ret; + + return VLC_SUCCESS; +} + +static 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; + + *pp_buffer = &p_sys->buffer; + return stream_Read(p_sys->p_stream, &p_sys->buffer, ARCHIVE_READ_SIZE); +} + +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; + ssize_t i_skipped = 0; + + /* be smart as small seeks converts to reads */ + if (p_sys->b_source_canseek) + { + int64_t i_pos = stream_Tell(p_sys->p_stream); + if (i_pos >=0) + stream_Seek(p_sys->p_stream, i_pos + i_request); + i_skipped = stream_Tell(p_sys->p_stream) - i_pos; + } + else while(i_request) + { + int i_skip = __MIN(INT32_MAX, i_request); + int i_read = stream_Read(p_sys->p_stream, NULL, i_skip); + if (i_read > 0) + i_skipped += i_read; + else + break; + i_request -= i_read; + } + + return i_skipped; +} + +static ssize_t SeekCallback(struct archive *p_archive, void *p_object, ssize_t i_offset, int i_whence) +{ + VLC_UNUSED(p_archive); + access_t *p_access = (access_t*)p_object; + access_sys_t *p_sys = p_access->p_sys; + + ssize_t i_pos; + + switch(i_whence) + { + case SEEK_CUR: + i_pos = stream_Tell(p_sys->p_stream); + break; + case SEEK_SET: + i_pos = 0; + break; + case SEEK_END: + i_pos = stream_Size(p_sys->p_stream) - 1; + break; + default: + return -1; + } + + if (i_pos < 0) + return -1; + + stream_Seek(p_sys->p_stream, i_pos + i_offset); /* We don't care about return val */ + return stream_Tell(p_sys->p_stream); +} + +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; + + p_sys->p_stream = stream_UrlNew( p_access, p_sys->psz_uri ); + if(!p_sys->p_stream) + return ARCHIVE_FATAL; + + /* Seek callback must only be set if calls are guaranteed to succeed */ + stream_Control(p_sys->p_stream, STREAM_CAN_SEEK, &p_sys->b_source_canseek); + if(p_sys->b_source_canseek) + archive_read_set_seek_callback(p_sys->p_archive, SeekCallback); + + return ARCHIVE_OK; +} + +static int CloseCallback(struct archive *p_archive, void *p_object) +{ + VLC_UNUSED(p_archive); + access_t *p_access = (access_t*)p_object; + + if (p_access->p_sys->p_stream) + stream_Delete(p_access->p_sys->p_stream); + + return ARCHIVE_OK; +} + +static int Control(access_t *p_access, int i_query, va_list args) +{ + access_sys_t *p_sys = p_access->p_sys; + + switch (i_query) + { + + case ACCESS_CAN_SEEK: + *va_arg(args, bool *)= p_sys->b_seekable; + break; + + case ACCESS_CAN_FASTSEEK: + if (!p_sys->b_seekable || !p_sys->p_stream) + { + *va_arg( args, bool* ) = false; + break; + } + else + return stream_vaControl( p_sys->p_stream, i_query, args ); + + case ACCESS_SET_PAUSE_STATE: + break; + + case ACCESS_CAN_PAUSE: + case ACCESS_CAN_CONTROL_PACE: + *va_arg(args, bool *) = true; + break; + + case ACCESS_GET_SIZE: + *va_arg(args, uint64_t *) = archive_entry_size(p_sys->p_entry); + break; + + case ACCESS_GET_PTS_DELAY: + *va_arg(args, int64_t *) = DEFAULT_PTS_DELAY; + break; + + default: + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + +int AccessOpen(vlc_object_t *p_object) +{ + access_t *p_access = (access_t*)p_object; + + if (!strchr(p_access->psz_location, ARCHIVE_SEP_CHAR)) + return VLC_EGENERIC; + + char *psz_base = strdup(p_access->psz_location); + if (!psz_base) + return VLC_EGENERIC; + char *psz_name = strchr(psz_base, ARCHIVE_SEP_CHAR); + *psz_name++ = '\0'; + + if (decode_URI(psz_base) == NULL) + { + free(psz_base); + return VLC_EGENERIC; + } + + access_sys_t *p_sys = p_access->p_sys = calloc(1, sizeof(access_sys_t)); + p_sys->p_archive = archive_read_new(); + if (!p_sys->p_archive) + { + msg_Err(p_access, "can't create libarchive instance: %s", + archive_error_string(p_sys->p_archive)); + free(psz_base); + goto error; + } + + EnableArchiveFormats(p_sys->p_archive); + + p_sys->psz_uri = psz_base; + + if (archive_read_open2(p_sys->p_archive, p_access, OpenCallback, ReadCallback, SkipCallback, CloseCallback) != ARCHIVE_OK) + { + msg_Err(p_access, "can't open archive: %s", + archive_error_string(p_sys->p_archive)); + AccessClose(p_object); + return VLC_EGENERIC; + } + + bool b_present = false; + while(archive_read_next_header(p_sys->p_archive, &p_sys->p_entry) == ARCHIVE_OK) + { + if (!strcmp(archive_entry_pathname(p_sys->p_entry), psz_name)) + { + b_present = true; + break; + } + msg_Dbg(p_access, "skipping entry %s != %s", archive_entry_pathname(p_sys->p_entry), psz_name); + } + + if (!b_present) + { + msg_Err(p_access, "entry '%s' not found in archive", psz_name); + /* entry not found */ + goto error; + } + + msg_Dbg(p_access, "reading entry %s %"PRId64, archive_entry_pathname(p_sys->p_entry), + archive_entry_size(p_sys->p_entry)); + + /* try to guess if it is seekable or not (does not depend on backend) */ + p_sys->b_seekable = (archive_seek_data(p_sys->p_archive, 0, SEEK_SET) >= 0); + + p_access->pf_read = Read; + p_access->pf_block = NULL; /* libarchive's zerocopy keeps owning block :/ */ + p_access->pf_control = Control; + p_access->pf_seek = Seek; + + access_InitFields(p_access); + + return VLC_SUCCESS; + +error: + AccessClose(p_object); + return VLC_EGENERIC; +} + +void AccessClose(vlc_object_t *p_object) +{ + access_t *p_access = (access_t*)p_object; + access_sys_t *p_sys = p_access->p_sys; + + if (p_sys->p_archive) + archive_read_free(p_sys->p_archive); + + free(p_sys->psz_uri); + free(p_sys); +} diff --git a/modules/access/archive/archive.c b/modules/access/archive/archive.c new file mode 100644 index 0000000000..212937af49 --- /dev/null +++ b/modules/access/archive/archive.c @@ -0,0 +1,111 @@ +/***************************************************************************** + * archive.c: libarchive based stream filter + ***************************************************************************** + * Copyright (C) 2014 Videolan Team + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "archive.h" + +#include +#include + +#include + +/**************************************************************************** + * Module descriptor + *****************************************************************************/ +vlc_module_begin() + set_shortname( "libarchive" ) + set_category( CAT_INPUT ) + set_subcategory( SUBCAT_INPUT_ACCESS ) + set_description( N_( "libarchive access" ) ) + set_capability( "access", 0 ) + add_shortcut( "archive" ) + set_callbacks( AccessOpen, AccessClose ) + add_submodule() + set_shortname( "libarchive" ) + set_subcategory( SUBCAT_INPUT_STREAM_FILTER ) + set_description( N_( "libarchive stream filter" ) ) + set_capability( "stream_filter", 14 ) /* less than rar and gzip */ + set_callbacks( StreamOpen, StreamClose ) +vlc_module_end() + +bool ProbeArchiveFormat(stream_t *p_stream) +{ + struct + { + const uint16_t i_offset; + const uint8_t i_length; + const char * const p_bytes; + } const magicbytes[9] = { + /* keep heaviest at top */ + { 257, 5, "ustar" }, //TAR + { 0, 7, "Rar!\x1A\x07" }, //RAR + { 0, 4, "xar!" }, //XAR + { 2, 3, "-lh" }, //LHA/LHZ + { 0, 3, "PAX" }, //PAX + { 0, 6, "070707" }, //CPIO + { 0, 6, "070701" }, //CPIO + { 0, 6, "070702" }, //CPIO + { 0, 4, "MSCH" }, //CAB + }; + + const uint8_t *p_peek; + int i_peek = stream_Peek(p_stream, &p_peek, magicbytes[0].i_offset + magicbytes[0].i_length); + + for(int i=0; i<9;i++) + { + if (i_peek <= magicbytes[i].i_offset + magicbytes[i].i_length) + continue; + else if ( !memcmp(p_peek + magicbytes[i].i_offset, + magicbytes[i].p_bytes, + magicbytes[i].i_length) ) + return true; + } + + return false; +} + +void EnableArchiveFormats(struct archive *p_archive) +{ + // archive_read_support_filter_bzip2(p_archive); + // archive_read_support_filter_compress(p_archive); + // archive_read_support_filter_gzip(p_archive); + // archive_read_support_filter_grzip(p_archive); + // archive_read_support_filter_lrzip(p_archive); + // archive_read_support_filter_lzip(p_archive); + archive_read_support_filter_lzma(p_archive); + archive_read_support_filter_lzop(p_archive); + archive_read_support_filter_none(p_archive); + archive_read_support_filter_rpm(p_archive); + archive_read_support_filter_uu(p_archive); + archive_read_support_filter_xz(p_archive); + + // archive_read_support_format_7zip(p_archive); + archive_read_support_format_ar(p_archive); + archive_read_support_format_cab(p_archive); + archive_read_support_format_cpio(p_archive); + archive_read_support_format_gnutar(p_archive); + // archive_read_support_format_iso9660(p_archive); + archive_read_support_format_lha(p_archive); + archive_read_support_format_mtree(p_archive); + archive_read_support_format_rar(p_archive); + archive_read_support_format_raw(p_archive); + archive_read_support_format_tar(p_archive); + archive_read_support_format_xar(p_archive); + // archive_read_support_format_zip(p_archive); +} diff --git a/modules/access/archive/archive.h b/modules/access/archive/archive.h new file mode 100644 index 0000000000..4e6d7ccfea --- /dev/null +++ b/modules/access/archive/archive.h @@ -0,0 +1,39 @@ +/***************************************************************************** + * archive.h: libarchive access & stream filter + ***************************************************************************** + * Copyright (C) 2014 Videolan Team + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +int AccessOpen(vlc_object_t *object); +void AccessClose(vlc_object_t *object); + +int StreamOpen(vlc_object_t *object); +void StreamClose(vlc_object_t *object); + +bool ProbeArchiveFormat(stream_t *p_stream); + +struct archive; +void EnableArchiveFormats(struct archive *p_archive); + +#define ARCHIVE_READ_SIZE 8192 +#define ARCHIVE_SEP_CHAR '|' diff --git a/modules/access/archive/stream.c b/modules/access/archive/stream.c new file mode 100644 index 0000000000..f39b46e784 --- /dev/null +++ b/modules/access/archive/stream.c @@ -0,0 +1,223 @@ +/***************************************************************************** + * stream.c: libarchive based stream filter + ***************************************************************************** + * Copyright (C) 2014 Videolan Team + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#include "archive.h" + +#include +#include +#include + +#include +#include + +struct stream_sys_t +{ + struct archive *p_archive; + bool b_source_canseek; + uint8_t buffer[ARCHIVE_READ_SIZE]; +}; + +static int Peek(stream_t *p_stream, const uint8_t **pp_peek, unsigned int i_peek) +{ + VLC_UNUSED(p_stream); + VLC_UNUSED(pp_peek); + VLC_UNUSED(i_peek); + return 0; +} + +static int Control(stream_t *p_stream, int i_query, va_list args) +{ + switch( i_query ) + { + case STREAM_IS_DIRECTORY: + *va_arg( args, bool* ) = true; + break; + + case STREAM_CAN_SEEK: + case STREAM_CAN_FASTSEEK: + case STREAM_GET_SIZE: + case STREAM_GET_POSITION: + case STREAM_SET_POSITION: + case STREAM_UPDATE_SIZE: + case STREAM_SET_RECORD_STATE: + case STREAM_GET_CONTENT_TYPE: + return VLC_EGENERIC; + + default: + return stream_vaControl( p_stream->p_source, i_query, args ); + } + + return VLC_SUCCESS; +} + +static ssize_t ReadCallback(struct archive *p_archive, void *p_object, const void **pp_buffer) +{ + VLC_UNUSED(p_archive); + stream_t *p_stream = (stream_t*)p_object; + + *pp_buffer = &p_stream->p_sys->buffer; + return stream_Read(p_stream->p_source, &p_stream->p_sys->buffer, ARCHIVE_READ_SIZE); +} + +static ssize_t SkipCallback(struct archive *p_archive, void *p_object, ssize_t i_request) +{ + VLC_UNUSED(p_archive); + stream_t *p_stream = (stream_t*)p_object; + ssize_t i_skipped = 0; + + /* be smart as small seeks converts to reads */ + if (p_stream->p_sys->b_source_canseek) + { + int64_t i_pos = stream_Tell(p_stream->p_source); + if (i_pos >=0) + stream_Seek(p_stream->p_source, i_pos + i_request); + i_skipped = stream_Tell(p_stream->p_source) - i_pos; + } + else while(i_request) + { + int i_skip = __MIN(INT32_MAX, i_request); + int i_read = stream_Read(p_stream->p_source, NULL, i_skip); + if (i_read > 0) + i_skipped += i_read; + else + break; + i_request -= i_read; + } + + return i_skipped; +} + +static ssize_t SeekCallback(struct archive *p_archive, void *p_object, ssize_t i_offset, int i_whence) +{ + VLC_UNUSED(p_archive); + stream_t *p_stream = (stream_t*)p_object; + ssize_t i_pos; + + switch(i_whence) + { + case SEEK_CUR: + i_pos = stream_Tell(p_stream->p_source); + break; + case SEEK_SET: + i_pos = 0; + break; + case SEEK_END: + i_pos = stream_Size(p_stream->p_source) - 1; + break; + default: + return -1; + } + + if (i_pos < 0) + return -1; + + stream_Seek(p_stream->p_source, i_pos + i_offset); /* We don't care about return val */ + return stream_Tell(p_stream->p_source); +} + +static int Browse(stream_t *p_stream, input_item_node_t *p_node) +{ + stream_sys_t *p_sys = p_stream->p_sys; + struct archive_entry *p_entry; + + while(archive_read_next_header(p_sys->p_archive, &p_entry) == ARCHIVE_OK) + { + char *psz_uri = NULL; + char *psz_access_uri = NULL; + int i_ret = asprintf(&psz_access_uri, "%s://%s%c%s", p_stream->psz_access, + p_stream->psz_path, ARCHIVE_SEP_CHAR, archive_entry_pathname(p_entry)); + if (i_ret == -1) + goto error; + i_ret = asprintf(&psz_uri, "archive://%s", psz_access_uri); + free(psz_access_uri); + if( i_ret == -1 ) + goto error; + + input_item_t *p_item = input_item_New(psz_uri, archive_entry_pathname(p_entry)); + free( psz_uri ); + if(p_item == NULL) + goto error; + + input_item_CopyOptions(p_node->p_item, p_item); + input_item_node_AppendItem(p_node, p_item); + msg_Dbg(p_stream, "declaring playlist entry %s", archive_entry_pathname(p_entry)); + input_item_Release(p_item); + } + + return VLC_SUCCESS; + +error: + return VLC_ENOITEM; +} + +int StreamOpen(vlc_object_t *p_object) +{ + stream_t *p_stream = (stream_t*) p_object; + stream_sys_t *p_sys = p_stream->p_sys; + + if (!ProbeArchiveFormat(p_stream->p_source)) + return VLC_EGENERIC; + + p_stream->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) ); + if( !p_sys ) + return VLC_ENOMEM; + + p_sys->p_archive = archive_read_new(); + if (!p_sys->p_archive) + { + msg_Err(p_stream, "can't create libarchive instance: %s", + archive_error_string(p_sys->p_archive)); + StreamClose(p_object); + return VLC_EGENERIC; + } + + EnableArchiveFormats(p_sys->p_archive); + + /* Seek callback must only be set if calls are guaranteed to succeed */ + stream_Control(p_stream->p_source, STREAM_CAN_SEEK, &p_sys->b_source_canseek); + if(p_sys->b_source_canseek) + archive_read_set_seek_callback(p_sys->p_archive, SeekCallback); + + if (archive_read_open2(p_sys->p_archive, p_stream, NULL, ReadCallback, SkipCallback, NULL) != ARCHIVE_OK) + { + msg_Err(p_stream, "can't open archive: %s", + archive_error_string(p_sys->p_archive)); + StreamClose(p_object); + return VLC_EGENERIC; + } + + p_stream->pf_read = NULL; + p_stream->pf_peek = Peek; + p_stream->pf_control = Control; + p_stream->pf_readdir = Browse; + + return VLC_SUCCESS; +} + +void StreamClose(vlc_object_t *object) +{ + stream_t *p_stream = (stream_t*)object; + stream_sys_t *p_sys = p_stream->p_sys; + + if (p_sys->p_archive) + archive_read_free(p_sys->p_archive); + + free(p_sys); +} diff --git a/po/POTFILES.in b/po/POTFILES.in index 09b236979e..5d4742d87f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -179,6 +179,7 @@ lib/vlm.c # modules modules/access/alsa.c +modules/access/archive/archive.c modules/access/attachment.c modules/access/avio.h modules/access/bd/bd.c -- 2.39.2