From b4a7ea306e4f3e311060c5aacec7a81140d71f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Thu, 30 Apr 2026 03:29:01 +0200 Subject: [PATCH] demux_{lavf,timeline}: add `--flatten-editions` Lavf exposes programs/renditions as a flat track list tagged with program_id. We respect this metadata and group tracks into their respective programs, as tracks from different programs may be completely unrelated to each other. This commits adds an option to flatten the editions and ignore the grouping. Fixes: https://github.com/mpv-player/mpv/issues/17836#issuecomment-4345930205 --- DOCS/interface-changes/program-editions.txt | 1 + DOCS/man/options.rst | 11 +++++++++++ demux/demux_lavf.c | 15 ++++++++++----- demux/demux_timeline.c | 15 ++++++++++----- options/options.c | 1 + options/options.h | 1 + player/loadfile.c | 11 +++++++++++ 7 files changed, 45 insertions(+), 10 deletions(-) diff --git a/DOCS/interface-changes/program-editions.txt b/DOCS/interface-changes/program-editions.txt index 476b88d216..f9ce685f08 100644 --- a/DOCS/interface-changes/program-editions.txt +++ b/DOCS/interface-changes/program-editions.txt @@ -1,3 +1,4 @@ ``track-list`` is now filtered by the currently selected edition add ``edition-list/N/metadata`` sub-property add ``program_id`` support to EDL ``!track_meta`` header +add ``--flatten-editions`` option to disable program-based edition grouping diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 62c5b6afbc..b4858328a1 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -120,6 +120,17 @@ Track Selection to ``auto`` (the default), mpv will choose the first edition declared as a default, or if there is no default, the first edition defined. +``--flatten-editions=`` + Some container formats (such as HLS or MPEG-TS with multiple programs) + expose multiple programs or rendition groups. By default, mpv respects the + format and groups tracks into editions, filtering the track list to only + show tracks belonging to the currently selected edition. + + Setting this option to ``yes`` ignores the program structure of the file. + No editions are created, and all tracks from all programs are shown as a + flat list. Note that depending on the file, tracks from different programs + may be completely unrelated to each other. + ``--track-auto-selection=`` Enable the default track auto-selection (default: yes). Enabling this will make the player select streams according to ``--aid``, ``--alang``, and diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c index d2b571563a..f7b4ff989d 100644 --- a/demux/demux_lavf.c +++ b/demux/demux_lavf.c @@ -981,6 +981,16 @@ static void build_editions(demuxer_t *demuxer) if (avfc->nb_programs <= 1) return; + struct MPOpts *mp_opts = mp_get_config_group(NULL, demuxer->global, &mp_opt_root); + int hls_bitrate = mp_opts->hls_bitrate; + int edition_id = mp_opts->edition_id; + bool flatten_editions = mp_opts->flatten_editions; + TA_FREEP(&mp_opts); + if (flatten_editions) { + MP_VERBOSE(demuxer, "Flattening track-based editions.\n"); + return; + } + for (unsigned i = 0; i < avfc->nb_programs; i++) { AVProgram *prog = avfc->programs[i]; @@ -1037,11 +1047,6 @@ static void build_editions(demuxer_t *demuxer) demuxer->edition_is_track_mapping = true; - struct MPOpts *mp_opts = mp_get_config_group(priv, demuxer->global, &mp_opt_root); - int hls_bitrate = mp_opts->hls_bitrate; - int edition_id = mp_opts->edition_id; - TA_FREEP(&mp_opts); - int selected = -1; if (edition_id >= 0 && edition_id < demuxer->num_editions) selected = edition_id; diff --git a/demux/demux_timeline.c b/demux/demux_timeline.c index 8333d8c37b..0dc7ca0e66 100644 --- a/demux/demux_timeline.c +++ b/demux/demux_timeline.c @@ -653,6 +653,16 @@ static void build_editions(struct demuxer *demuxer) if (demuxer->num_editions > 0) return; + struct MPOpts *mp_opts = mp_get_config_group(NULL, demuxer->global, &mp_opt_root); + int hls_bitrate = mp_opts->hls_bitrate; + int edition_id = mp_opts->edition_id; + bool flatten_editions = mp_opts->flatten_editions; + TA_FREEP(&mp_opts); + if (flatten_editions) { + MP_VERBOSE(demuxer, "Flattening track-based editions.\n"); + return; + } + int num_streams = demux_get_num_stream(demuxer); for (int n = 0; n < num_streams; n++) { struct sh_stream *sh = demux_get_stream(demuxer, n); @@ -693,11 +703,6 @@ static void build_editions(struct demuxer *demuxer) demuxer->edition_is_track_mapping = true; - struct MPOpts *mp_opts = mp_get_config_group(demuxer, demuxer->global, &mp_opt_root); - int hls_bitrate = mp_opts->hls_bitrate; - int edition_id = mp_opts->edition_id; - TA_FREEP(&mp_opts); - int selected = -1; if (edition_id >= 0 && edition_id < demuxer->num_editions) selected = edition_id; diff --git a/options/options.c b/options/options.c index 9f3edebdd1..64b75be38b 100644 --- a/options/options.c +++ b/options/options.c @@ -587,6 +587,7 @@ static const m_option_t mp_opts[] = { {"dvd", OPT_SUBSTRUCT(dvd_opts, dvd_conf)}, #endif {"edition", OPT_CHOICE(edition_id, {"auto", -1}), M_RANGE(0, 8190)}, + {"flatten-editions", OPT_BOOL(flatten_editions)}, #if HAVE_LIBBLURAY {"bluray", OPT_SUBSTRUCT(stream_bluray_opts, stream_bluray_conf)}, #endif /* HAVE_LIBBLURAY */ diff --git a/options/options.h b/options/options.h index 82717baf51..7beb99572d 100644 --- a/options/options.h +++ b/options/options.h @@ -254,6 +254,7 @@ typedef struct MPOpts { bool use_filedir_conf; int hls_bitrate; int edition_id; + bool flatten_editions; bool initial_audio_sync; double sync_max_video_change; double sync_max_audio_change; diff --git a/player/loadfile.c b/player/loadfile.c index 59f2de1790..56609b2ddb 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -607,6 +607,14 @@ struct track *select_default_track(struct MPContext *mpctx, int order, { struct MPOpts *opts = mpctx->opts; int tid = opts->stream_id[order][type]; + const struct demuxer *demuxer = mpctx->demuxer; + // Prefer tracks from the same program as the selected video when not using editions. + int preferred_program = -1; + if (type != STREAM_VIDEO && mpctx->current_track[0][STREAM_VIDEO] && + (!demuxer || !demuxer->edition_is_track_mapping || demuxer->num_editions <= 1)) + { + preferred_program = mpctx->current_track[0][STREAM_VIDEO]->program_id; + } if (tid == -2) return NULL; char **langs = process_langs(opts->stream_lang[type]); @@ -636,6 +644,9 @@ struct track *select_default_track(struct MPContext *mpctx, int order, continue; if (!opts->autoload_files && track->is_external) continue; + if (preferred_program != -1 && !track->is_external && + track->program_id != -1 && track->program_id != preferred_program) + continue; if (duplicate_track(mpctx, order, type, track)) continue; if (sub) {