mirror of
https://github.com/mpv-player/mpv
synced 2024-12-25 00:02:13 +00:00
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
This commit is contained in:
parent
657ce1b15c
commit
04bde06095
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user