mirror of https://github.com/mpv-player/mpv
stream_libarchive: remember archive headers from initial open
The header probing hacks were previously all broken. They only worked the first time the archive file was open. Since subsequent opens (on seek) occured in the middle of the source stream rather than at the beginning, the stream_read_peek calls meant to retrieve the headers were instead returning random bytes in the middle of the file. Perhaps the worst manifestation of this was when seeking within a multi-volume .rar archive with the "legacy" file naming pattern. If the seek required a reopen, the fact that the archive was multi-volume would be forgotten and the file would appear truncated terminating playback. To solve this, only perform the header probling the first time the archive is opened. Save the results and reuse them on subsequent reopens. Put this in a wrapper so this is transparent to demux_libarchive.
This commit is contained in:
parent
9285a1c05e
commit
2aa5964b43
|
@ -66,7 +66,7 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
|
|||
mp_get_config_group(demuxer, demuxer->global, demuxer->desc->options);
|
||||
|
||||
if (!opts->rar_list_all_volumes)
|
||||
flags |= MP_ARCHIVE_FLAG_NO_RAR_VOLUMES;
|
||||
flags |= MP_ARCHIVE_FLAG_NO_VOLUMES;
|
||||
|
||||
mpa = mp_archive_new(demuxer->log, demuxer->stream, flags, 0);
|
||||
if (!mpa)
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
|
||||
#include "stream_libarchive.h"
|
||||
|
||||
#define MP_ARCHIVE_FLAG_MAYBE_ZIP (MP_ARCHIVE_FLAG_PRIV << 0)
|
||||
#define MP_ARCHIVE_FLAG_MAYBE_RAR (MP_ARCHIVE_FLAG_PRIV << 1)
|
||||
#define MP_ARCHIVE_FLAG_MAYBE_VOLUMES (MP_ARCHIVE_FLAG_PRIV << 2)
|
||||
|
||||
struct mp_archive_volume {
|
||||
struct mp_archive *mpa;
|
||||
int index; // volume number (starting with 0, mp_archive.primary_src)
|
||||
|
@ -73,6 +77,21 @@ static bool probe_zip(struct stream *s)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int mp_archive_probe(struct stream *src)
|
||||
{
|
||||
int flags = 0;
|
||||
assert(stream_tell(src) == 0);
|
||||
if (probe_zip(src))
|
||||
flags |= MP_ARCHIVE_FLAG_MAYBE_ZIP;
|
||||
|
||||
if (probe_rar(src)) {
|
||||
flags |= MP_ARCHIVE_FLAG_MAYBE_RAR;
|
||||
if (probe_multi_rar(src))
|
||||
flags |= MP_ARCHIVE_FLAG_MAYBE_VOLUMES;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static bool volume_seek(struct mp_archive_volume *vol)
|
||||
{
|
||||
if (!vol->src || vol->seek_to < 0)
|
||||
|
@ -260,7 +279,7 @@ static const struct file_pattern patterns[] = {
|
|||
{ NULL, NULL, NULL, 0, 0 },
|
||||
};
|
||||
|
||||
static bool find_volumes(struct mp_archive *mpa, bool multivol_hint)
|
||||
static bool find_volumes(struct mp_archive *mpa, int flags)
|
||||
{
|
||||
struct bstr primary_url = bstr0(mpa->primary_src->url);
|
||||
|
||||
|
@ -273,7 +292,7 @@ static bool find_volumes(struct mp_archive *mpa, bool multivol_hint)
|
|||
|
||||
if (!pattern->match)
|
||||
return true;
|
||||
if (pattern->legacy && !multivol_hint)
|
||||
if (pattern->legacy && !(flags & MP_ARCHIVE_FLAG_MAYBE_VOLUMES))
|
||||
return true;
|
||||
|
||||
struct bstr base = bstr_splice(primary_url, 0, -(int)strlen(pattern->match));
|
||||
|
@ -293,7 +312,8 @@ static bool find_volumes(struct mp_archive *mpa, bool multivol_hint)
|
|||
return true;
|
||||
}
|
||||
|
||||
struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
|
||||
static struct mp_archive *mp_archive_new_raw(struct mp_log *log,
|
||||
struct stream *src,
|
||||
int flags, int max_volumes)
|
||||
{
|
||||
struct mp_archive *mpa = talloc_zero(NULL, struct mp_archive);
|
||||
|
@ -308,20 +328,17 @@ struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
|
|||
mpa->primary_src = src;
|
||||
if (!mpa->arch)
|
||||
goto err;
|
||||
|
||||
mpa->flags = flags;
|
||||
mpa->num_volumes = max_volumes ? max_volumes : INT_MAX;
|
||||
|
||||
// first volume is the primary stream
|
||||
if (!add_volume(mpa, src, src->url, 0))
|
||||
goto err;
|
||||
|
||||
bool maybe_rar = probe_rar(src);
|
||||
bool maybe_zip = probe_zip(src);
|
||||
bool probe_all = flags & MP_ARCHIVE_FLAG_UNSAFE;
|
||||
bool maybe_rar2_multivol = maybe_rar && probe_multi_rar(src);
|
||||
|
||||
if (!(flags & MP_ARCHIVE_FLAG_NO_RAR_VOLUMES)) {
|
||||
if (!(flags & MP_ARCHIVE_FLAG_NO_VOLUMES)) {
|
||||
// try to open other volumes
|
||||
if (!find_volumes(mpa, maybe_rar2_multivol))
|
||||
if (!find_volumes(mpa, flags))
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -333,7 +350,7 @@ struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
|
|||
// Exclude other formats if it's probably RAR, because other formats may
|
||||
// behave suboptimal with multiple volumes exposed, such as opening every
|
||||
// single volume by seeking at the end of the file.
|
||||
if (!maybe_rar) {
|
||||
if (!(flags & MP_ARCHIVE_FLAG_MAYBE_RAR)) {
|
||||
archive_read_support_format_7zip(mpa->arch);
|
||||
archive_read_support_format_iso9660(mpa->arch);
|
||||
archive_read_support_filter_bzip2(mpa->arch);
|
||||
|
@ -345,7 +362,7 @@ struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
|
|||
// of the file, which may be annoying (HTTP reconnect, volume skipping),
|
||||
// so use it only as last resort, or if it's relatively likely that it's
|
||||
// really zip.
|
||||
if (maybe_zip || probe_all)
|
||||
if (flags & (MP_ARCHIVE_FLAG_UNSAFE | MP_ARCHIVE_FLAG_MAYBE_ZIP))
|
||||
archive_read_support_format_zip_seekable(mpa->arch);
|
||||
}
|
||||
|
||||
|
@ -370,6 +387,13 @@ err:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
|
||||
int flags, int max_volumes)
|
||||
{
|
||||
flags |= mp_archive_probe(src);
|
||||
return mp_archive_new_raw(log, src, flags, max_volumes);
|
||||
}
|
||||
|
||||
// Iterate entries. The first call establishes the first entry. Returns false
|
||||
// if no entry found, otherwise returns true and sets mpa->entry/entry_filename.
|
||||
bool mp_archive_next_entry(struct mp_archive *mpa)
|
||||
|
@ -428,10 +452,16 @@ struct priv {
|
|||
static int reopen_archive(stream_t *s)
|
||||
{
|
||||
struct priv *p = s->priv;
|
||||
int num_volumes = p->mpa ? p->mpa->num_volumes : 0;
|
||||
mp_archive_free(p->mpa);
|
||||
s->pos = 0;
|
||||
p->mpa = mp_archive_new(s->log, p->src, MP_ARCHIVE_FLAG_UNSAFE, num_volumes);
|
||||
if (!p->mpa) {
|
||||
p->mpa = mp_archive_new(s->log, p->src, MP_ARCHIVE_FLAG_UNSAFE, 0);
|
||||
} else {
|
||||
int flags = p->mpa->flags;
|
||||
int num_volumes = p->mpa->num_volumes;
|
||||
mp_archive_free(p->mpa);
|
||||
p->mpa = mp_archive_new_raw(s->log, p->src, flags, num_volumes);
|
||||
}
|
||||
|
||||
if (!p->mpa)
|
||||
return STREAM_ERROR;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ struct mp_archive {
|
|||
struct archive *arch;
|
||||
struct stream *primary_src;
|
||||
char buffer[4096];
|
||||
int flags;
|
||||
int num_volumes; // INT_MAX if unknown (initial state)
|
||||
|
||||
// Current entry, as set by mp_archive_next_entry().
|
||||
|
@ -25,7 +26,9 @@ struct mp_archive {
|
|||
void mp_archive_free(struct mp_archive *mpa);
|
||||
|
||||
#define MP_ARCHIVE_FLAG_UNSAFE (1 << 0)
|
||||
#define MP_ARCHIVE_FLAG_NO_RAR_VOLUMES (1 << 1)
|
||||
#define MP_ARCHIVE_FLAG_NO_VOLUMES (1 << 1)
|
||||
#define MP_ARCHIVE_FLAG_PRIV (1 << 2)
|
||||
|
||||
struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
|
||||
int flags, int max_volumes);
|
||||
|
||||
|
|
Loading…
Reference in New Issue