summaryrefslogtreecommitdiffstats
path: root/demux/demux_lavf.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2018-09-01 15:28:40 +0200
committerAnton Kindestam <antonki@kth.se>2018-12-06 10:30:57 +0100
commit02756c3735f8e7e8ac6b5ca1168b68a6a325122d (patch)
treee52f97ce5b6b5a546c0689f9de78924fe3f49982 /demux/demux_lavf.c
parent4dfaa373846e79f1bc34b50426c1584b948c0eb6 (diff)
downloadmpv-02756c3735f8e7e8ac6b5ca1168b68a6a325122d.tar.bz2
mpv-02756c3735f8e7e8ac6b5ca1168b68a6a325122d.tar.xz
demux_lavf: to get effective HLS bitrate
In theory, this could be easily done with custom I/O. In practice, all the halfassed garbage in FFmpeg shits itself and fucks up like there's no tomorrow. There are several problems: 1. FFmpeg pretends you can do custom I/O, but in reality there's a lot that custom I/O can do. hls.c even contains explicit checks to disable important things if custom I/O is used! In particular, you can't use the HTTP keepalive functionality (needed for somewhat decent HLS performance), because some cranky asshole in the cursed FFmpeg dev. community blocked it. 2. The implementation of nested I/O callbacks (io_open/io_close) is bogus and halfassed (like everything in FFmpeg, really). It will call io_open on some URLs without ever calling io_close. Instead, it'll call avio_close() on the context directly. From what I can tell, avio_close() is incompable to custom I/O anyway (overwhelmed by their own garbage, the fFmpeg devs created the io_close callback for this reason, because they couldn't fix their own fucking garbage). This commit adds some shitty workaround for this (technically triggers UB, but with that garbage heap of a library we depend on it's not like it matters). 3. Even then, you can't proxy I/O contexts (see 1.), but we can just keep track of the opened nested I/O contexts. The bytes_read is documented as not public, but reading it is literally the only way to get what we want. A more reasonable approach would probably be using curl. It could transparently handle the keep-alive thing, as well as propagating cookies etc. (which doesn't work with the FFmpeg approach if you use custom I/O). Of course even better if there were an independent HLS implementation anywhere. FFmpeg's HLS support is so embarrassing pathetic and just goes to show that they belong into the past (multimedia from 2000-2010) and should either modernize or fuck off. With FFmpeg's shit-crusted structures, todic communities, and retarded assholes denying progress, probably the latter. Did I already mention that FFmpeg is a shit fucked steaming pile of garbage shit? And all just to get some basic I/O stats, that any proper HLS consumer requires in order to implement adaptive streaming correctly (i.e. browser based players, and nothing FFmshit based).
Diffstat (limited to 'demux/demux_lavf.c')
-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);