1 /*****************************************************************************
2 * access.c: libarchive based access
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_access.h>
25 #include <vlc_stream.h>
28 #include <archive_entry.h>
32 struct archive *p_archive;
33 bool b_source_canseek;
34 uint8_t buffer[ARCHIVE_READ_SIZE];
36 struct archive_entry *p_entry;
39 bool b_seekable; /* Is our archive type seekable ? */
42 static ssize_t Read(access_t *p_access, uint8_t *p_data, size_t i_size)
44 access_sys_t *p_sys = p_access->p_sys;
48 i_read = archive_read_data(p_sys->p_archive, p_data, i_size);
51 p_access->info.i_pos += i_read;
53 if (i_size > 0 && i_read <= 0)
54 p_access->info.b_eof = true;
59 static int Seek(access_t *p_access, uint64_t i_pos)
61 access_sys_t *p_sys = p_access->p_sys;
63 if (!p_sys->b_seekable)
66 int64_t i_ret = archive_seek_data(p_sys->p_archive, i_pos, SEEK_SET);
67 if ( i_ret < ARCHIVE_OK )
69 p_access->info.i_pos = i_ret;
74 static ssize_t ReadCallback(struct archive *p_archive, void *p_object, const void **pp_buffer)
76 VLC_UNUSED(p_archive);
77 access_t *p_access = (access_t*)p_object;
78 access_sys_t *p_sys = p_access->p_sys;
80 *pp_buffer = &p_sys->buffer;
81 return stream_Read(p_sys->p_stream, &p_sys->buffer, ARCHIVE_READ_SIZE);
84 static ssize_t SkipCallback(struct archive *p_archive, void *p_object, ssize_t i_request)
86 VLC_UNUSED(p_archive);
87 access_t *p_access = (access_t*)p_object;
88 access_sys_t *p_sys = p_access->p_sys;
89 ssize_t i_skipped = 0;
91 /* be smart as small seeks converts to reads */
92 if (p_sys->b_source_canseek)
94 int64_t i_pos = stream_Tell(p_sys->p_stream);
96 stream_Seek(p_sys->p_stream, i_pos + i_request);
97 i_skipped = stream_Tell(p_sys->p_stream) - i_pos;
101 int i_skip = __MIN(INT32_MAX, i_request);
102 int i_read = stream_Read(p_sys->p_stream, NULL, i_skip);
113 static ssize_t SeekCallback(struct archive *p_archive, void *p_object, ssize_t i_offset, int i_whence)
115 VLC_UNUSED(p_archive);
116 access_t *p_access = (access_t*)p_object;
117 access_sys_t *p_sys = p_access->p_sys;
124 i_pos = stream_Tell(p_sys->p_stream);
130 i_pos = stream_Size(p_sys->p_stream) - 1;
139 stream_Seek(p_sys->p_stream, i_pos + i_offset); /* We don't care about return val */
140 return stream_Tell(p_sys->p_stream);
143 static int OpenCallback(struct archive *p_archive, void *p_object)
145 VLC_UNUSED(p_archive);
146 access_t *p_access = (access_t*)p_object;
147 access_sys_t *p_sys = p_access->p_sys;
149 p_sys->p_stream = stream_UrlNew( p_access, p_sys->psz_uri );
151 return ARCHIVE_FATAL;
153 /* Seek callback must only be set if calls are guaranteed to succeed */
154 stream_Control(p_sys->p_stream, STREAM_CAN_SEEK, &p_sys->b_source_canseek);
155 if(p_sys->b_source_canseek)
156 archive_read_set_seek_callback(p_sys->p_archive, SeekCallback);
161 static int CloseCallback(struct archive *p_archive, void *p_object)
163 VLC_UNUSED(p_archive);
164 access_t *p_access = (access_t*)p_object;
166 if (p_access->p_sys->p_stream)
167 stream_Delete(p_access->p_sys->p_stream);
172 static int Control(access_t *p_access, int i_query, va_list args)
174 access_sys_t *p_sys = p_access->p_sys;
179 case ACCESS_CAN_SEEK:
180 *va_arg(args, bool *)= p_sys->b_seekable;
183 case ACCESS_CAN_FASTSEEK:
184 if (!p_sys->b_seekable || !p_sys->p_stream)
186 *va_arg( args, bool* ) = false;
190 return stream_vaControl( p_sys->p_stream, i_query, args );
192 case ACCESS_SET_PAUSE_STATE:
195 case ACCESS_CAN_PAUSE:
196 case ACCESS_CAN_CONTROL_PACE:
197 *va_arg(args, bool *) = true;
200 case ACCESS_GET_SIZE:
201 *va_arg(args, uint64_t *) = archive_entry_size(p_sys->p_entry);
204 case ACCESS_GET_PTS_DELAY:
205 *va_arg(args, int64_t *) = DEFAULT_PTS_DELAY;
214 int AccessOpen(vlc_object_t *p_object)
216 access_t *p_access = (access_t*)p_object;
218 if (!strchr(p_access->psz_location, ARCHIVE_SEP_CHAR))
221 char *psz_base = strdup(p_access->psz_location);
224 char *psz_name = strchr(psz_base, ARCHIVE_SEP_CHAR);
227 if (decode_URI(psz_base) == NULL)
233 access_sys_t *p_sys = p_access->p_sys = calloc(1, sizeof(access_sys_t));
234 p_sys->p_archive = archive_read_new();
235 if (!p_sys->p_archive)
237 msg_Err(p_access, "can't create libarchive instance: %s",
238 archive_error_string(p_sys->p_archive));
243 EnableArchiveFormats(p_sys->p_archive);
245 p_sys->psz_uri = psz_base;
247 if (archive_read_open2(p_sys->p_archive, p_access, OpenCallback, ReadCallback, SkipCallback, CloseCallback) != ARCHIVE_OK)
249 msg_Err(p_access, "can't open archive: %s",
250 archive_error_string(p_sys->p_archive));
251 AccessClose(p_object);
255 bool b_present = false;
256 while(archive_read_next_header(p_sys->p_archive, &p_sys->p_entry) == ARCHIVE_OK)
258 if (!strcmp(archive_entry_pathname(p_sys->p_entry), psz_name))
263 msg_Dbg(p_access, "skipping entry %s != %s", archive_entry_pathname(p_sys->p_entry), psz_name);
268 msg_Err(p_access, "entry '%s' not found in archive", psz_name);
269 /* entry not found */
273 msg_Dbg(p_access, "reading entry %s %"PRId64, archive_entry_pathname(p_sys->p_entry),
274 archive_entry_size(p_sys->p_entry));
276 /* try to guess if it is seekable or not (does not depend on backend) */
277 p_sys->b_seekable = (archive_seek_data(p_sys->p_archive, 0, SEEK_SET) >= 0);
279 p_access->pf_read = Read;
280 p_access->pf_block = NULL; /* libarchive's zerocopy keeps owning block :/ */
281 p_access->pf_control = Control;
282 p_access->pf_seek = Seek;
284 access_InitFields(p_access);
289 AccessClose(p_object);
293 void AccessClose(vlc_object_t *p_object)
295 access_t *p_access = (access_t*)p_object;
296 access_sys_t *p_sys = p_access->p_sys;
298 if (p_sys->p_archive)
299 archive_read_free(p_sys->p_archive);
301 free(p_sys->psz_uri);