diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 759b29b34d..d546d0f0b2 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -142,6 +142,15 @@ Track Selection When autoselecting a subtitle track, select a full/non-forced one even if the selected audio stream matches your preferred subtitle language (default: no). +``--subs-fallback=`` + When autoselecting a subtitle track, if no tracks match your preferred languages, + select a full track even if it doesn't match your preferred subtitle language (default: no). + Setting this to `default` means that only streams flagged as `default` will be selected. + +``--subs-fallback-forced=`` + When autoselecting a subtitle track, if no tracks match your preferred languages, + select a forced track that matches the language of the selected audio track (default: yes). + Playback Control ---------------- diff --git a/options/options.c b/options/options.c index b068e60d23..3606fde6a9 100644 --- a/options/options.c +++ b/options/options.c @@ -518,6 +518,8 @@ static const m_option_t mp_opts[] = { {"vlang", OPT_STRINGLIST(stream_lang[STREAM_VIDEO])}, {"track-auto-selection", OPT_BOOL(stream_auto_sel)}, {"subs-with-matching-audio", OPT_BOOL(subs_with_matching_audio)}, + {"subs-fallback", OPT_CHOICE(subs_fallback, {"no", 0}, {"default", 1}, {"yes", 2})}, + {"subs-fallback-forced", OPT_BOOL(subs_fallback_forced)}, {"lavfi-complex", OPT_STRING(lavfi_complex), .flags = UPDATE_LAVFI_COMPLEX}, @@ -1037,6 +1039,7 @@ static const struct MPOpts mp_default_opts = { }, .stream_auto_sel = true, .subs_with_matching_audio = false, + .subs_fallback_forced = true, .audio_display = 1, .audio_output_format = 0, // AF_FORMAT_UNKNOWN .playback_speed = 1., diff --git a/options/options.h b/options/options.h index dd67af0aa5..956fe0ab11 100644 --- a/options/options.h +++ b/options/options.h @@ -266,6 +266,8 @@ typedef struct MPOpts { char **stream_lang[STREAM_TYPE_COUNT]; bool stream_auto_sel; bool subs_with_matching_audio; + int subs_fallback; + bool subs_fallback_forced; int audio_display; char **display_tags; diff --git a/player/loadfile.c b/player/loadfile.c index 78c213094b..c2fbdd334b 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -575,17 +575,20 @@ struct track *select_default_track(struct MPContext *mpctx, int order, { struct MPOpts *opts = mpctx->opts; int tid = opts->stream_id[order][type]; - char **langs = process_langs(opts->stream_lang[type]); - int prefer_forced = type != STREAM_SUB || - (!opts->subs_with_matching_audio && - mpctx->current_track[0][STREAM_AUDIO] && - match_lang(langs, mpctx->current_track[0][STREAM_AUDIO]->lang)); int preferred_program = (type != STREAM_VIDEO && mpctx->current_track[0][STREAM_VIDEO]) ? mpctx->current_track[0][STREAM_VIDEO]->program_id : -1; if (tid == -2) return NULL; - bool select_fallback = type == STREAM_VIDEO || type == STREAM_AUDIO; + char **langs = process_langs(opts->stream_lang[type]); + const char *audio_lang = mpctx->current_track[0][STREAM_AUDIO] ? + mpctx->current_track[0][STREAM_AUDIO]->lang : + NULL; + bool audio_matches = match_lang(langs, audio_lang); + int prefer_forced = type == STREAM_SUB && !opts->subs_with_matching_audio && audio_matches; + bool select_fallback = type == STREAM_VIDEO || type == STREAM_AUDIO || (type == STREAM_SUB && opts->subs_fallback == 2); + bool fallback_forced = (type == STREAM_SUB && !prefer_forced && opts->subs_fallback_forced); struct track *pick = NULL; + struct track *forced_pick = NULL; for (int n = 0; n < mpctx->num_tracks; n++) { struct track *track = mpctx->tracks[n]; if (track->type != type) @@ -600,13 +603,38 @@ struct track *select_default_track(struct MPContext *mpctx, int order, continue; if (duplicate_track(mpctx, order, type, track)) continue; - if (!pick || compare_track(track, pick, langs, prefer_forced, mpctx->opts, preferred_program)) + if (!pick || compare_track(track, pick, langs, false, mpctx->opts, preferred_program)) pick = track; + + // We only try to autoselect forced tracks if they match the audio language + if ((prefer_forced || fallback_forced) && mp_match_lang_single(audio_lang, track->lang) && + (!forced_pick || compare_track(track, forced_pick, langs, true, mpctx->opts, preferred_program))) + forced_pick = track; } + + // If we're trying for a forced track, and found something that matches the audio, go with that + if (prefer_forced) + pick = forced_pick; + + // If our best pick for a subtitle track isn't suitable, we'll fall back on forced, + // or clear it out altogether. if (pick && !select_fallback && !(pick->is_external && !pick->no_default) - && !match_lang(langs, pick->lang) && !pick->default_track - && !pick->forced_track) - pick = NULL; + && (!match_lang(langs, pick->lang) || (prefer_forced && !pick->forced_track)) + && (!opts->subs_fallback || !pick->default_track)) { + if (fallback_forced) { + prefer_forced = 1; + // If we found a suitable forced track (matching the audio), fallback on that. + // Otherwise, if our currently-selected track matches the audio, + // we'll try using it in forced-only mode. + // If it doesn't, none of the available tracks make sense, so we give up. + if (forced_pick) + pick = forced_pick; + else if (!match_lang(langs, pick->lang)) + pick = NULL; + } else { + pick = NULL; + } + } if (pick && pick->attached_picture && !mpctx->opts->audio_display) pick = NULL; if (pick && !opts->autoload_files && pick->is_external)