diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 6a73ac7a85..aaabed7696 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -3127,6 +3127,18 @@ Demuxer the first song in the stream. Well, you won't get anything useful either way if the seek is outside of mpv's cache. +``--demuxer-lavf-propagate-opts=`` + Propagate FFmpeg-level options to recursively opened connections (default: + yes). This is needed because FFmpeg will apply these settings to nested + AVIO contexts automatically. On the other hand, this could break in certain + situations - it's the FFmpeg API, you just can't win. + + This affects in particular the ``--timeout`` option and anything passed + with ``--demuxer-lavf-o``. + + If this option is deemed unnecessary at some point in the future, it will + be removed without notice. + ``--demuxer-mkv-subtitle-preroll=``, ``--mkv-subtitle-preroll`` Try harder to show embedded soft subtitles when seeking somewhere. Normally, it can happen that the subtitle at the seek target is not shown due to how diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index 5153bfe42f..5f9d5a3781 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -79,6 +79,7 @@ struct demux_lavf_opts { char *sub_cp; int rtsp_transport; int linearize_ts; + int propagate_opts; }; const struct m_sub_options demux_lavf_conf = { @@ -104,6 +105,7 @@ const struct m_sub_options demux_lavf_conf = { {"http", 3})), OPT_CHOICE("demuxer-lavf-linearize-timestamps", linearize_ts, 0, ({"no", 0}, {"auto", -1}, {"yes", 1})), + OPT_FLAG("demuxer-lavf-propagate-opts", propagate_opts, 0), {0} }, .size = sizeof(struct demux_lavf_opts), @@ -118,6 +120,7 @@ const struct m_sub_options demux_lavf_conf = { .sub_cp = "auto", .rtsp_transport = 2, .linearize_ts = -1, + .propagate_opts = 1, }, }; @@ -232,6 +235,8 @@ typedef struct lavf_priv { int linearize_ts; bool any_ts_fixed; + AVDictionary *av_opts; + // Proxying nested streams. struct nested_stream *nested; int num_nested; @@ -851,8 +856,27 @@ static int nested_io_open(struct AVFormatContext *s, AVIOContext **pb, struct demuxer *demuxer = s->opaque; lavf_priv_t *priv = demuxer->priv; + if (priv->opts->propagate_opts) { + // Copy av_opts to options, but only entries that are not present in + // options. (Hope this will break less by not overwriting important + // settings.) + AVDictionaryEntry *cur = NULL; + while ((cur = av_dict_get(priv->av_opts, "", cur, AV_DICT_IGNORE_SUFFIX))) + { + if (!*options || !av_dict_get(*options, cur->key, NULL, 0)) { + MP_TRACE(demuxer, "Nested option: '%s'='%s'\n", + cur->key, cur->value); + av_dict_set(options, cur->key, cur->value, 0); + } else { + MP_TRACE(demuxer, "Skipping nested option: '%s'\n", cur->key); + } + } + } + int r = priv->default_io_open(s, pb, url, flags, options); if (r >= 0) { + if (options) + mp_avdict_print_unset(demuxer->log, MSGL_TRACE, *options); struct nested_stream nest = { .id = *pb, }; @@ -972,16 +996,19 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check) if (demuxer->access_references) { priv->default_io_open = avfc->io_open; priv->default_io_close = avfc->io_close; -#if !HAVE_FFMPEG_STRICT_ABI avfc->io_open = nested_io_open; avfc->io_close = nested_io_close; -#endif } else { avfc->io_open = block_io_open; } mp_set_avdict(&dopts, lavfdopts->avopts); + if (av_dict_copy(&priv->av_opts, dopts, 0) < 0) { + av_dict_free(&dopts); + return -1; + } + if (avformat_open_input(&avfc, priv->filename, priv->avif, &dopts) < 0) { MP_ERR(demuxer, "avformat_open_input() failed\n"); av_dict_free(&dopts);