1 /*****************************************************************************
2 * stream.c: libarchive based stream filter
3 *****************************************************************************
4 * Copyright (C) 2014 Videolan Team
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
23 #include <vlc_stream.h>
25 #include <vlc_input_item.h>
28 #include <archive_entry.h>
32 struct archive *p_archive;
33 bool b_source_canseek;
34 uint8_t buffer[ARCHIVE_READ_SIZE];
37 static int Peek(stream_t *p_stream, const uint8_t **pp_peek, unsigned int i_peek)
45 static int Control(stream_t *p_stream, int i_query, va_list args)
49 case STREAM_IS_DIRECTORY:
50 *va_arg( args, bool* ) = true;
54 case STREAM_CAN_FASTSEEK:
56 case STREAM_GET_POSITION:
57 case STREAM_SET_POSITION:
58 case STREAM_UPDATE_SIZE:
59 case STREAM_SET_RECORD_STATE:
60 case STREAM_GET_CONTENT_TYPE:
64 return stream_vaControl( p_stream->p_source, i_query, args );
70 static ssize_t ReadCallback(struct archive *p_archive, void *p_object, const void **pp_buffer)
72 VLC_UNUSED(p_archive);
73 stream_t *p_stream = (stream_t*)p_object;
75 *pp_buffer = &p_stream->p_sys->buffer;
76 return stream_Read(p_stream->p_source, &p_stream->p_sys->buffer, ARCHIVE_READ_SIZE);
79 static ssize_t SkipCallback(struct archive *p_archive, void *p_object, ssize_t i_request)
81 VLC_UNUSED(p_archive);
82 stream_t *p_stream = (stream_t*)p_object;
83 ssize_t i_skipped = 0;
85 /* be smart as small seeks converts to reads */
86 if (p_stream->p_sys->b_source_canseek)
88 int64_t i_pos = stream_Tell(p_stream->p_source);
90 stream_Seek(p_stream->p_source, i_pos + i_request);
91 i_skipped = stream_Tell(p_stream->p_source) - i_pos;
95 int i_skip = __MIN(INT32_MAX, i_request);
96 int i_read = stream_Read(p_stream->p_source, NULL, i_skip);
107 static ssize_t SeekCallback(struct archive *p_archive, void *p_object, ssize_t i_offset, int i_whence)
109 VLC_UNUSED(p_archive);
110 stream_t *p_stream = (stream_t*)p_object;
116 i_pos = stream_Tell(p_stream->p_source);
122 i_pos = stream_Size(p_stream->p_source) - 1;
131 stream_Seek(p_stream->p_source, i_pos + i_offset); /* We don't care about return val */
132 return stream_Tell(p_stream->p_source);
135 static int Browse(stream_t *p_stream, input_item_node_t *p_node)
137 stream_sys_t *p_sys = p_stream->p_sys;
138 struct archive_entry *p_entry;
140 while(archive_read_next_header(p_sys->p_archive, &p_entry) == ARCHIVE_OK)
142 char *psz_uri = NULL;
143 char *psz_access_uri = NULL;
144 int i_ret = asprintf(&psz_access_uri, "%s://%s%c%s", p_stream->psz_access,
145 p_stream->psz_path, ARCHIVE_SEP_CHAR, archive_entry_pathname(p_entry));
148 i_ret = asprintf(&psz_uri, "archive://%s", psz_access_uri);
149 free(psz_access_uri);
153 input_item_t *p_item = input_item_New(psz_uri, archive_entry_pathname(p_entry));
158 input_item_CopyOptions(p_node->p_item, p_item);
159 input_item_node_AppendItem(p_node, p_item);
160 msg_Dbg(p_stream, "declaring playlist entry %s", archive_entry_pathname(p_entry));
161 input_item_Release(p_item);
170 int StreamOpen(vlc_object_t *p_object)
172 stream_t *p_stream = (stream_t*) p_object;
173 stream_sys_t *p_sys = p_stream->p_sys;
175 if (!ProbeArchiveFormat(p_stream->p_source))
178 p_stream->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) );
182 p_sys->p_archive = archive_read_new();
183 if (!p_sys->p_archive)
185 msg_Err(p_stream, "can't create libarchive instance: %s",
186 archive_error_string(p_sys->p_archive));
187 StreamClose(p_object);
191 EnableArchiveFormats(p_sys->p_archive);
193 /* Seek callback must only be set if calls are guaranteed to succeed */
194 stream_Control(p_stream->p_source, STREAM_CAN_SEEK, &p_sys->b_source_canseek);
195 if(p_sys->b_source_canseek)
196 archive_read_set_seek_callback(p_sys->p_archive, SeekCallback);
198 if (archive_read_open2(p_sys->p_archive, p_stream, NULL, ReadCallback, SkipCallback, NULL) != ARCHIVE_OK)
200 msg_Err(p_stream, "can't open archive: %s",
201 archive_error_string(p_sys->p_archive));
202 StreamClose(p_object);
206 p_stream->pf_read = NULL;
207 p_stream->pf_peek = Peek;
208 p_stream->pf_control = Control;
209 p_stream->pf_readdir = Browse;
214 void StreamClose(vlc_object_t *object)
216 stream_t *p_stream = (stream_t*)object;
217 stream_sys_t *p_sys = p_stream->p_sys;
219 if (p_sys->p_archive)
221 archive_read_close(p_sys->p_archive);
222 archive_read_free(p_sys->p_archive);