From 0f110ad0e27583c5161cf27a3c17d1563253b368 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 26 Sep 2016 14:45:55 +0200 Subject: [PATCH] stream_lavf: fix determining seekability demux_lavf.c forces seek to being determined as supported if STREAM_CTRL_HAS_AVSEEK is returned as success. But it always succeeds with current FFmpeg versions. (Seems like Libav commit cae448cf broke this in early 2016.) Now we can't determine via private API whether the underlying protocol supports read_seek anymore. The affected protocols (mostly rtmp) also set seekable=0, meaning they signal they're not seekable, even though read_seek would work. (My guess is that this can't be fixed because even though seekable is in theory a combination of elaborate flags [of which only 1 is defined, AVIO_SEEKABLE_NORMAL], a seekable!=0 always means it's byte-seekable in some way.) So the FFmpeg API is being garbage _again_, and all what we can do is determining this via protocol name and a whitelist. Should fix the behavior reported in #1701. --- stream/stream_lavf.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/stream/stream_lavf.c b/stream/stream_lavf.c index bd4e629136..67681d3c43 100644 --- a/stream/stream_lavf.c +++ b/stream/stream_lavf.c @@ -141,10 +141,30 @@ static int control(stream_t *s, int cmd, void *arg) } break; } - case STREAM_CTRL_HAS_AVSEEK: - if (avio->read_seek) - return 1; + case STREAM_CTRL_HAS_AVSEEK: { + // Starting at some point, read_seek is always available, and runtime + // behavior decides whether it exists or not. FFmpeg's API doesn't + // return anything helpful to determine seekability upfront, so here's + // a hardcoded whitelist. Not our fault. + // In addition we also have to jump through ridiculous hoops just to + // get the fucking protocol name. + const char *proto = NULL; + if (avio->av_class && avio->av_class->child_next) { + // This usually yields the URLContext (why does it even exist?), + // which holds the name of the actual protocol implementation. + void *child = avio->av_class->child_next(avio, NULL); + AVClass *cl = *(AVClass **)child; + if (cl && cl->item_name) + proto = cl->item_name(child); + } + static const char *const has_read_seek[] = { + "rtmp", "rtmpt", "rtmpe", "rtmpte", "rtmps", "rtmpts", "mmsh", 0}; + for (int n = 0; has_read_seek[n]; n++) { + if (avio->read_seek && proto && strcmp(proto, has_read_seek[n]) == 0) + return 1; + } break; + } case STREAM_CTRL_GET_METADATA: { *(struct mp_tags **)arg = read_icy(s); if (!*(struct mp_tags **)arg)