static int archive_entry_seek(stream_t *s, int64_t newpos) { struct priv *p = s->priv; if (!p->mpa) return -1; if (archive_seek_data(p->mpa->arch, newpos, SEEK_SET) >= 0) return 1; // libarchive can't seek in most formats. if (newpos < s->pos) { // Hack seeking backwards into working by reopening the archive and // starting over. MP_VERBOSE(s, "trying to reopen archive for performing seek\n"); if (reopen_archive(s) < STREAM_OK) return -1; s->pos = 0; } if (newpos > s->pos) { // For seeking forwards, just keep reading data (there's no libarchive // skip function either). char buffer[4096]; while (newpos > s->pos) { if (mp_cancel_test(s->cancel)) return -1; int size = MPMIN(newpos - s->pos, sizeof(buffer)); int r = archive_read_data(p->mpa->arch, buffer, size); if (r < 0) { MP_ERR(s, "%s\n", archive_error_string(p->mpa->arch)); return -1; } s->pos += r; } } return 1; }
static int archive_entry_seek(stream_t *s, int64_t newpos) { struct priv *p = s->priv; if (p->mpa && !p->broken_seek) { locale_t oldlocale = uselocale(p->mpa->locale); int r = archive_seek_data(p->mpa->arch, newpos, SEEK_SET); uselocale(oldlocale); if (r >= 0) return 1; MP_WARN(s, "possibly unsupported seeking - switching to reopening\n"); p->broken_seek = true; if (reopen_archive(s) < STREAM_OK) return -1; } // libarchive can't seek in most formats. if (newpos < s->pos) { // Hack seeking backwards into working by reopening the archive and // starting over. MP_VERBOSE(s, "trying to reopen archive for performing seek\n"); if (reopen_archive(s) < STREAM_OK) return -1; s->pos = 0; } if (newpos > s->pos) { // For seeking forwards, just keep reading data (there's no libarchive // skip function either). char buffer[4096]; while (newpos > s->pos) { if (mp_cancel_test(s->cancel)) return -1; int size = MPMIN(newpos - s->pos, sizeof(buffer)); locale_t oldlocale = uselocale(p->mpa->locale); int r = archive_read_data(p->mpa->arch, buffer, size); if (r <= 0) { if (r == 0 && newpos > p->entry_size) { MP_ERR(s, "demuxer trying to seek beyond end of archive " "entry\n"); } else if (r == 0) { MP_ERR(s, "end of archive entry reached while seeking\n"); } else { MP_ERR(s, "%s\n", archive_error_string(p->mpa->arch)); } uselocale(oldlocale); if (mp_archive_check_fatal(p->mpa, r)) { mp_archive_free(p->mpa); p->mpa = NULL; } return -1; } uselocale(oldlocale); s->pos += r; } } return 1; }
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); return ( i_ret < ARCHIVE_OK ) ? VLC_EGENERIC : VLC_SUCCESS; }
static int archive_seek_subentry( private_sys_t* p_sys, char const* psz_subentry ) { libarchive_t* p_arc = p_sys->p_archive; struct archive_entry* entry; int archive_status; while( !( archive_status = archive_read_next_header( p_arc, &entry ) ) ) { char const* entry_path = archive_entry_pathname( entry ); if( strcmp( entry_path, psz_subentry ) == 0 ) { p_sys->p_entry = archive_entry_clone( entry ); if( unlikely( !p_sys->p_entry ) ) return VLC_ENOMEM; break; } archive_read_data_skip( p_arc ); } switch( archive_status ) { case ARCHIVE_WARN: msg_Warn( p_sys->p_obj, "libarchive: %s", archive_error_string( p_arc ) ); /* fall through */ case ARCHIVE_EOF: case ARCHIVE_FATAL: case ARCHIVE_RETRY: archive_set_error( p_arc, ARCHIVE_FATAL, "archive does not contain >>> %s <<<", psz_subentry ); return VLC_EGENERIC; } /* check if seeking is supported */ if( p_sys->b_seekable_source ) { if( archive_seek_data( p_sys->p_archive, 0, SEEK_CUR ) >= 0 ) p_sys->b_seekable_archive = true; } return VLC_SUCCESS; }
static int Seek( stream_extractor_t* p_extractor, uint64_t i_req ) { private_sys_t* p_sys = p_extractor->p_sys; if( !p_sys->p_entry || !p_sys->b_seekable_source ) return VLC_EGENERIC; if( archive_entry_size_is_set( p_sys->p_entry ) && (uint64_t)archive_entry_size( p_sys->p_entry ) <= i_req ) { p_sys->b_eof = true; return VLC_SUCCESS; } p_sys->b_eof = false; if( !p_sys->b_seekable_archive || p_sys->b_dead || archive_seek_data( p_sys->p_archive, i_req, SEEK_SET ) < 0 ) { msg_Dbg( p_extractor, "intrinsic seek failed: '%s' (falling back to dumb seek)", archive_error_string( p_sys->p_archive ) ); uint64_t i_skip = i_req - p_sys->i_offset; /* RECREATE LIBARCHIVE HANDLE IF WE ARE SEEKING BACKWARDS */ if( i_req < p_sys->i_offset ) { if( archive_extractor_reset( p_extractor ) ) { msg_Err( p_extractor, "unable to reset libarchive handle" ); return VLC_EGENERIC; } i_skip = i_req; } if( archive_skip_decompressed( p_extractor, i_skip ) ) msg_Dbg( p_extractor, "failed to skip to seek position" ); } p_sys->i_offset = i_req; return VLC_SUCCESS; }
int AccessOpen(vlc_object_t *p_object) { access_t *p_access = (access_t*)p_object; const char *sep = strchr(p_access->psz_location, ARCHIVE_SEP_CHAR); if (sep == NULL) return VLC_EGENERIC; char *psz_base = strdup(p_access->psz_location); if (unlikely(psz_base == NULL)) return VLC_ENOMEM; char *psz_name = psz_base + (sep - p_access->psz_location); *(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); /* Set up the switch callback for multiple volumes handling */ archive_read_set_switch_callback(p_sys->p_archive, SwitchCallback); /* !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)); 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; }