summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2020-01-04 18:59:23 +0100
committerwm4 <wm4@nowhere>2020-01-04 18:59:23 +0100
commit04bde06095215f27cef0be01ce4d831ab2719591 (patch)
tree67c353219977f03182015a9427a0f2a4a300892b
parent657ce1b15c2e6dca0ed1ea1b67ffa123843eca5b (diff)
downloadmpv-04bde06095215f27cef0be01ce4d831ab2719591.tar.bz2
mpv-04bde06095215f27cef0be01ce4d831ab2719591.tar.xz
stream_libarchive: some more hacks to improve multi-volume archives
Instead of opening every volume on start just to see if it's there, all all volumes that could possibly exist, and "handle" it on opening. This requires working around some of libarchive's amazing stupidity and using some empirically determined behavior. Will possibly break if libarchive changes some of this behavior. See: #7182
-rw-r--r--demux/demux_libarchive.c4
-rw-r--r--stream/stream_libarchive.c65
-rw-r--r--stream/stream_libarchive.h3
3 files changed, 39 insertions, 33 deletions
diff --git a/demux/demux_libarchive.c b/demux/demux_libarchive.c
index b038e149d9..31e0c2b418 100644
--- a/demux/demux_libarchive.c
+++ b/demux/demux_libarchive.c
@@ -49,7 +49,7 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
int probe_got = stream_read_peek(demuxer->stream, probe, probe_size);
struct stream *probe_stream =
stream_memory_open(demuxer->global, probe, probe_got);
- struct mp_archive *mpa = mp_archive_new(mp_null_log, probe_stream, flags);
+ struct mp_archive *mpa = mp_archive_new(mp_null_log, probe_stream, flags, 0);
bool ok = !!mpa;
free_stream(probe_stream);
mp_archive_free(mpa);
@@ -57,7 +57,7 @@ static int open_file(struct demuxer *demuxer, enum demux_check check)
if (!ok)
return -1;
- mpa = mp_archive_new(demuxer->log, demuxer->stream, flags);
+ mpa = mp_archive_new(demuxer->log, demuxer->stream, flags, 0);
if (!mpa)
return -1;
diff --git a/stream/stream_libarchive.c b/stream/stream_libarchive.c
index 7ae5a8be38..781b34ad0d 100644
--- a/stream/stream_libarchive.c
+++ b/stream/stream_libarchive.c
@@ -27,14 +27,15 @@
struct mp_archive_volume {
struct mp_archive *mpa;
- struct stream *src;
+ int index; // volume number (starting with 0, mp_archive.primary_src)
+ struct stream *src; // NULL => not current volume, or 0 sized dummy stream
int64_t seek_to;
char *url;
};
static bool volume_seek(struct mp_archive_volume *vol)
{
- if (vol->seek_to < 0)
+ if (!vol->src || vol->seek_to < 0)
return true;
bool r = stream_seek(vol->src, vol->seek_to);
vol->seek_to = -1;
@@ -44,6 +45,8 @@ static bool volume_seek(struct mp_archive_volume *vol)
static ssize_t read_cb(struct archive *arch, void *priv, const void **buffer)
{
struct mp_archive_volume *vol = priv;
+ if (!vol->src)
+ return 0;
if (!volume_seek(vol))
return -1;
int res = stream_read_partial(vol->src, vol->mpa->buffer,
@@ -57,6 +60,8 @@ static int64_t seek_cb(struct archive *arch, void *priv,
int64_t offset, int whence)
{
struct mp_archive_volume *vol = priv;
+ if (!vol->src)
+ return 0;
switch (whence) {
case SEEK_SET:
vol->seek_to = offset;
@@ -81,6 +86,8 @@ static int64_t seek_cb(struct archive *arch, void *priv,
static int64_t skip_cb(struct archive *arch, void *priv, int64_t request)
{
struct mp_archive_volume *vol = priv;
+ if (!vol->src)
+ return request;
if (!volume_seek(vol))
return -1;
int64_t old = stream_tell(vol->src);
@@ -93,12 +100,26 @@ static int open_cb(struct archive *arch, void *priv)
struct mp_archive_volume *vol = priv;
vol->seek_to = -1;
if (!vol->src) {
+ // Avoid annoying warnings/latency for known dummy volumes.
+ if (vol->index >= vol->mpa->num_volumes)
+ return ARCHIVE_OK;
vol->src = stream_create(vol->url,
STREAM_READ |
vol->mpa->primary_src->stream_origin,
vol->mpa->primary_src->cancel,
vol->mpa->primary_src->global);
- return vol->src ? ARCHIVE_OK : ARCHIVE_FATAL;
+ // We pretend that failure to open a stream means it was not found,
+ // we assume in turn means that the volume doesn't exist (since
+ // libarchive is too fucking stupid to detect when a multi-volume
+ // archive really ends). libarchive also throws up permanently when
+ // a volume could not be opened (because it's fucking stupid), but
+ // somehow accepts 0-sized volumes (because it's fucking stupid), which
+ // we simulate whenever vol->src==NULL for an opened volume.
+ if (!vol->src) {
+ vol->mpa->num_volumes = MPMIN(vol->mpa->num_volumes, vol->index);
+ MP_INFO(vol->mpa, "Assuming the volume above was not needed.\n");
+ }
+ return ARCHIVE_OK;
}
// just rewind the primary stream
@@ -118,17 +139,9 @@ static int close_cb(struct archive *arch, void *priv)
{
struct mp_archive_volume *vol = priv;
volume_close(vol);
- talloc_free(vol);
return ARCHIVE_OK;
}
-static int switch_cb(struct archive *arch, void *oldpriv, void *newpriv)
-{
- struct mp_archive_volume *oldvol = oldpriv;
- volume_close(oldvol);
- return open_cb(arch, newpriv);
-}
-
static void mp_archive_close(struct mp_archive *mpa)
{
if (mpa && mpa->arch) {
@@ -206,19 +219,9 @@ static char **find_volumes(struct stream *primary_stream)
if (!pattern->match)
goto done;
- struct bstr base = bstr_splice(primary_url, 0, -strlen(pattern->match));
+ struct bstr base = bstr_splice(primary_url, 0, -(int)strlen(pattern->match));
for (int i = pattern->start; i <= pattern->stop; i++) {
char* url = pattern->volume_url(res, pattern->format, base, i);
- struct stream *s = stream_create(url,
- STREAM_READ |
- primary_stream->stream_origin,
- primary_stream->cancel,
- primary_stream->global);
- if (!s) {
- talloc_free(url);
- goto done;
- }
- free_stream(s);
MP_TARRAY_APPEND(res, res, num, url);
}
@@ -229,10 +232,10 @@ done:
static bool add_volume(struct mp_log *log, struct mp_archive *mpa,
- struct stream *src, const char* url)
+ struct stream *src, const char* url, int index)
{
struct mp_archive_volume *vol = talloc_zero(mpa, struct mp_archive_volume);
- mp_verbose(log, "Adding volume %s\n", url);
+ vol->index = index;
vol->mpa = mpa;
vol->src = src;
vol->url = talloc_strdup(vol, url);
@@ -243,7 +246,7 @@ static bool add_volume(struct mp_log *log, struct mp_archive *mpa,
}
struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
- int flags)
+ int flags, int max_volumes)
{
struct mp_archive *mpa = talloc_zero(NULL, struct mp_archive);
mpa->log = log;
@@ -257,15 +260,16 @@ struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
mpa->primary_src = src;
if (!mpa->arch)
goto err;
+ mpa->num_volumes = max_volumes ? max_volumes : INT_MAX;
- // first volume is the primary streame
- if (!add_volume(log ,mpa, src, src->url))
+ // first volume is the primary stream
+ if (!add_volume(log, mpa, src, src->url, 0))
goto err;
// try to open other volumes
char** volumes = find_volumes(src);
for (int i = 0; volumes[i]; i++) {
- if (!add_volume(log, mpa, NULL, volumes[i])) {
+ if (!add_volume(log, mpa, NULL, volumes[i], i + 1)) {
talloc_free(volumes);
goto err;
}
@@ -289,8 +293,8 @@ struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
archive_read_set_read_callback(mpa->arch, read_cb);
archive_read_set_skip_callback(mpa->arch, skip_cb);
- archive_read_set_switch_callback(mpa->arch, switch_cb);
archive_read_set_open_callback(mpa->arch, open_cb);
+ // Allow it to close a volume.
archive_read_set_close_callback(mpa->arch, close_cb);
if (mpa->primary_src->seekable)
archive_read_set_seek_callback(mpa->arch, seek_cb);
@@ -365,9 +369,10 @@ 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);
+ p->mpa = mp_archive_new(s->log, p->src, MP_ARCHIVE_FLAG_UNSAFE, num_volumes);
if (!p->mpa)
return STREAM_ERROR;
diff --git a/stream/stream_libarchive.h b/stream/stream_libarchive.h
index 8884834189..bb5dee432b 100644
--- a/stream/stream_libarchive.h
+++ b/stream/stream_libarchive.h
@@ -14,6 +14,7 @@ struct mp_archive {
struct archive *arch;
struct stream *primary_src;
char buffer[4096];
+ int num_volumes; // INT_MAX if unknown (initial state)
// Current entry, as set by mp_archive_next_entry().
struct archive_entry *entry;
@@ -25,6 +26,6 @@ void mp_archive_free(struct mp_archive *mpa);
#define MP_ARCHIVE_FLAG_UNSAFE 1
struct mp_archive *mp_archive_new(struct mp_log *log, struct stream *src,
- int flags);
+ int flags, int max_volumes);
bool mp_archive_next_entry(struct mp_archive *mpa);