summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--demux/demux_lavf.c81
1 files changed, 80 insertions, 1 deletions
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index 058f13cabf..ad71937580 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -191,6 +191,11 @@ static const struct format_hack format_hacks[] = {
{0}
};
+struct nested_stream {
+ AVIOContext *id;
+ int64_t last_bytes;
+};
+
typedef struct lavf_priv {
struct stream *stream;
bool own_stream;
@@ -210,8 +215,29 @@ typedef struct lavf_priv {
struct demux_lavf_opts *opts;
double mf_fps;
+
+ // Proxying nested streams.
+ struct nested_stream *nested;
+ int num_nested;
+ int (*default_io_open)(struct AVFormatContext *s, AVIOContext **pb,
+ const char *url, int flags, AVDictionary **options);
+ void (*default_io_close)(struct AVFormatContext *s, AVIOContext *pb);
} lavf_priv_t;
+static void update_read_stats(struct demuxer *demuxer)
+{
+ lavf_priv_t *priv = demuxer->priv;
+
+ for (int n = 0; n < priv->num_nested; n++) {
+ struct nested_stream *nest = &priv->nested[n];
+
+ int64_t cur = nest->id->bytes_read;
+ int64_t new = cur - nest->last_bytes;
+ nest->last_bytes = cur;
+ demuxer->total_unbuffered_read_bytes += new;
+ }
+}
+
// At least mp4 has name="mov,mp4,m4a,3gp,3g2,mj2", so we split the name
// on "," in general.
static bool matches_avinputformat_name(struct lavf_priv *priv,
@@ -793,6 +819,38 @@ static int block_io_open(struct AVFormatContext *s, AVIOContext **pb,
return AVERROR(EACCES);
}
+static int nested_io_open(struct AVFormatContext *s, AVIOContext **pb,
+ const char *url, int flags, AVDictionary **options)
+{
+ struct demuxer *demuxer = s->opaque;
+ lavf_priv_t *priv = demuxer->priv;
+
+ int r = priv->default_io_open(s, pb, url, flags, options);
+ if (r >= 0) {
+ struct nested_stream nest = {
+ .id = *pb,
+ };
+ MP_TARRAY_APPEND(priv, priv->nested, priv->num_nested, nest);
+ }
+ return r;
+}
+
+static void nested_io_close(struct AVFormatContext *s, AVIOContext *pb)
+{
+ struct demuxer *demuxer = s->opaque;
+ lavf_priv_t *priv = demuxer->priv;
+
+ for (int n = 0; n < priv->num_nested; n++) {
+ if (priv->nested[n].id == pb) {
+ MP_TARRAY_REMOVE_AT(priv->nested, priv->num_nested, n);
+ break;
+ }
+ }
+
+
+ priv->default_io_close(s, pb);
+}
+
static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
{
AVFormatContext *avfc;
@@ -887,8 +945,14 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
};
avfc->opaque = demuxer;
- if (!demuxer->access_references)
+ if (demuxer->access_references) {
+ priv->default_io_open = avfc->io_open;
+ priv->default_io_close = avfc->io_close;
+ avfc->io_open = nested_io_open;
+ avfc->io_close = nested_io_close;
+ } else {
avfc->io_open = block_io_open;
+ }
mp_set_avdict(&dopts, lavfdopts->avopts);
@@ -990,6 +1054,7 @@ static int demux_lavf_fill_buffer(demuxer_t *demux)
AVPacket *pkt = &(AVPacket){0};
int r = av_read_frame(priv->avfc, pkt);
+ update_read_stats(demux);
if (r < 0) {
av_packet_unref(pkt);
if (r == AVERROR(EAGAIN))
@@ -1080,6 +1145,8 @@ static void demux_seek_lavf(demuxer_t *demuxer, double seek_pts, int flags)
av_strerror(r, buf, sizeof(buf));
MP_VERBOSE(demuxer, "Seek failed (%s)\n", buf);
}
+
+ update_read_stats(demuxer);
}
static int demux_lavf_control(demuxer_t *demuxer, int cmd, void *arg)
@@ -1176,7 +1243,19 @@ static void demux_close_lavf(demuxer_t *demuxer)
{
lavf_priv_t *priv = demuxer->priv;
if (priv) {
+ // This will be a dangling pointer; but see below.
+ AVIOContext *leaking = priv->avfc ? priv->avfc->pb : NULL;
avformat_close_input(&priv->avfc);
+ // The ffmpeg garbage breaks its own API yet again: hls.c will call
+ // io_open on the main playlist, but never calls io_close. This happens
+ // to work out for us (since we don't really use custom I/O), but it's
+ // still weird. Compensate.
+ if (priv->num_nested == 1 && priv->nested[0].id == leaking)
+ priv->num_nested = 0;
+ if (priv->num_nested) {
+ MP_WARN(demuxer, "Leaking %d nested connections (FFmpeg bug).\n",
+ priv->num_nested);
+ }
if (priv->pb)
av_freep(&priv->pb->buffer);
av_freep(&priv->pb);