From c07e046bfa5a736ddf6c185b48cd1c6929840186 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 2 Feb 2015 21:23:12 +0100 Subject: player: add external audio file auto-loading Apparently some people want this. Not enabled by default. Fixes #967. --- DOCS/man/options.rst | 10 +++++ options/options.c | 3 ++ options/options.h | 1 + player/loadfile.c | 102 +++++++++++++++++++++++++++++---------------------- sub/find_subfiles.c | 99 +++++++++++++++++++++++++++++-------------------- sub/find_subfiles.h | 3 +- 6 files changed, 134 insertions(+), 84 deletions(-) diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index a60302c27d..852752e5ff 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -1039,6 +1039,16 @@ Audio maximum amplification, i.e. amplify by 200%. The default volume (no change in volume) will be ``50`` in this case. +``--audio-file-auto=``, ``--no-audio-file-auto`` + Load additional audio files matching the video filename. The parameter + specifies how external audio files are matched. This is disabled by + default. + + :no: Don't automatically load external audio files (default). + :exact: Load the media filename with audio file extension. + :fuzzy: Load all audio files containing media filename. + :all: Load all audio files in the current directory. + ``--audio-client-name=`` The application name the player reports to the audio API. Can be useful if you want to force a different audio profile (e.g. with PulseAudio), diff --git a/options/options.c b/options/options.c index e5e5975224..4c2cde9a2d 100644 --- a/options/options.c +++ b/options/options.c @@ -328,6 +328,8 @@ const m_option_t mp_opts[] = { OPT_FLAG("sub-fix-timing", sub_fix_timing, 0), OPT_CHOICE("sub-auto", sub_auto, 0, ({"no", -1}, {"exact", 0}, {"fuzzy", 1}, {"all", 2})), + OPT_CHOICE("audio-file-auto", audiofile_auto, 0, + ({"no", -1}, {"exact", 0}, {"fuzzy", 1}, {"all", 2})), OPT_INTRANGE("sub-pos", sub_pos, 0, 0, 100), OPT_FLOATRANGE("sub-gauss", sub_gauss, 0, 0.0, 3.0), OPT_FLAG("sub-gray", sub_gray, 0), @@ -764,6 +766,7 @@ const struct MPOpts mp_default_opts = { .movie_aspect = -1., .field_dominance = -1, .sub_auto = 0, + .audiofile_auto = -1, .osd_bar_visible = 1, #if HAVE_LIBASS .ass_enabled = 1, diff --git a/options/options.h b/options/options.h index 8313ce1448..5d9504902e 100644 --- a/options/options.h +++ b/options/options.h @@ -231,6 +231,7 @@ typedef struct MPOpts { char **sub_name; char **sub_paths; int sub_auto; + int audiofile_auto; int use_text_osd; int osd_bar_visible; float osd_bar_align_x; diff --git a/player/loadfile.c b/player/loadfile.c index 549aacc8de..bf4121ba00 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -646,57 +646,32 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track) return true; } -static void open_subtitles_from_options(struct MPContext *mpctx) -{ - if (mpctx->opts->sub_name) { - for (int i = 0; mpctx->opts->sub_name[i] != NULL; ++i) - mp_add_subtitles(mpctx, mpctx->opts->sub_name[i]); - } - if (mpctx->opts->sub_auto >= 0) { // auto load sub file ... - void *tmp = talloc_new(NULL); - char *base_filename = mpctx->filename; - char *stream_filename = NULL; - if (mpctx->demuxer) { - if (demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_BASE_FILENAME, - &stream_filename) > 0) - base_filename = talloc_steal(tmp, stream_filename); - } - struct subfn *list = find_text_subtitles(mpctx->global, base_filename); - talloc_steal(tmp, list); - for (int i = 0; list && list[i].fname; i++) { - char *filename = list[i].fname; - char *lang = list[i].lang; - for (int n = 0; n < mpctx->num_sources; n++) { - if (strcmp(mpctx->sources[n]->stream->url, filename) == 0) - goto skip; - } - struct track *track = mp_add_subtitles(mpctx, filename); - if (track) { - track->auto_loaded = true; - if (!track->lang) - track->lang = talloc_strdup(track, lang); - } - skip:; - } - talloc_free(tmp); - } -} - static struct track *open_external_file(struct MPContext *mpctx, char *filename, - char *demuxer_name, enum stream_type filter) { struct MPOpts *opts = mpctx->opts; if (!filename) return NULL; + char *disp_filename = filename; if (strncmp(disp_filename, "memory://", 9) == 0) disp_filename = "memory://"; // avoid noise + struct stream *stream = stream_open(filename, mpctx->global); if (!stream) goto err_out; - if (filter != STREAM_SUB) + + char *demuxer_name = NULL; + switch (filter) { + case STREAM_SUB: + demuxer_name = opts->sub_demuxer_name; + break; + case STREAM_AUDIO: + demuxer_name = opts->audio_demuxer_name; stream_enable_cache(&stream, &opts->stream_cache); + break; + } + struct demuxer_params params = { .expect_subtitle = filter == STREAM_SUB, }; @@ -706,6 +681,7 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename, free_stream(stream); goto err_out; } + struct track *first = NULL; for (int n = 0; n < demuxer->num_streams; n++) { struct sh_stream *sh = demuxer->streams[n]; @@ -724,6 +700,7 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename, disp_filename); goto err_out; } + MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, demuxer); return first; @@ -736,17 +713,53 @@ err_out: static void open_audiofiles_from_options(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; - for (int n = 0; opts->audio_files && opts->audio_files[n]; n++) { - open_external_file(mpctx, opts->audio_files[n], opts->audio_demuxer_name, - STREAM_AUDIO); - } + for (int n = 0; opts->audio_files && opts->audio_files[n]; n++) + open_external_file(mpctx, opts->audio_files[n], STREAM_AUDIO); } struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename) +{ + return open_external_file(mpctx, filename, STREAM_SUB); +} + +static void open_subtitles_from_options(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; - return open_external_file(mpctx, filename, opts->sub_demuxer_name, - STREAM_SUB); + for (int i = 0; opts->sub_name && opts->sub_name[i] != NULL; i++) + mp_add_subtitles(mpctx, opts->sub_name[i]); +} + +static void autoload_external_files(struct MPContext *mpctx) +{ + if (mpctx->opts->sub_auto < 0 && mpctx->opts->audiofile_auto < 0) + return; + + void *tmp = talloc_new(NULL); + char *base_filename = mpctx->filename; + char *stream_filename = NULL; + if (mpctx->demuxer) { + if (demux_stream_control(mpctx->demuxer, STREAM_CTRL_GET_BASE_FILENAME, + &stream_filename) > 0) + base_filename = talloc_steal(tmp, stream_filename); + } + struct subfn *list = find_external_files(mpctx->global, base_filename); + talloc_steal(tmp, list); + for (int i = 0; list && list[i].fname; i++) { + char *filename = list[i].fname; + char *lang = list[i].lang; + for (int n = 0; n < mpctx->num_sources; n++) { + if (strcmp(mpctx->sources[n]->stream->url, filename) == 0) + goto skip; + } + struct track *track = open_external_file(mpctx, filename, list[i].type); + if (track) { + track->auto_loaded = true; + if (!track->lang) + track->lang = talloc_strdup(track, lang); + } + skip:; + } + talloc_free(tmp); } // Do stuff to a newly loaded playlist. This includes any processing that may @@ -1086,6 +1099,7 @@ goto_reopen_demuxer: ; open_subtitles_from_options(mpctx); open_audiofiles_from_options(mpctx); + autoload_external_files(mpctx); check_previous_track_selection(mpctx); diff --git a/sub/find_subfiles.c b/sub/find_subfiles.c index f703c8d910..e6116d1b91 100644 --- a/sub/find_subfiles.c +++ b/sub/find_subfiles.c @@ -18,15 +18,27 @@ static const char *const sub_exts[] = {"utf", "utf8", "utf-8", "idx", "sub", "sr "smi", "rt", "txt", "ssa", "aqt", "jss", "js", "ass", "mks", NULL}; -static bool is_sub_ext(bstr ext) +static const char *const audio_exts[] = {"mp3", "aac", "mka", "dts", "flac", + "ogg", "m4a", NULL}; + +static bool test_ext_list(bstr ext, const char *const *list) { - for (int n = 0; sub_exts[n]; n++) { - if (bstrcasecmp(bstr0(sub_exts[n]), ext) == 0) + for (int n = 0; list[n]; n++) { + if (bstrcasecmp(bstr0(list[n]), ext) == 0) return true; } return false; } +static int test_ext(bstr ext) +{ + if (test_ext_list(ext, sub_exts)) + return STREAM_SUB; + if (test_ext_list(ext, audio_exts)) + return STREAM_AUDIO; + return -1; +} + static struct bstr strip_ext(struct bstr str) { int dotpos = bstrrchr(str, '.'); @@ -45,7 +57,7 @@ static struct bstr get_ext(struct bstr s) bool mp_might_be_subtitle_file(const char *filename) { - return is_sub_ext(get_ext(bstr0(filename))); + return test_ext(get_ext(bstr0(filename))) == STREAM_SUB; } static int compare_sub_filename(const void *a, const void *b) @@ -87,15 +99,6 @@ static struct bstr guess_lang_from_filename(struct bstr name) return (struct bstr){name.start + i + 1, n}; } -/** - * @brief Append all the subtitles in the given path matching fname - * @param opts MPlayer options - * @param slist pointer to the subtitles list tallocated - * @param nsub pointer to the number of subtitles - * @param path Look for subtitles in this directory - * @param fname Subtitle filename (pattern) - * @param limit_fuzziness Ignore flag when sub_fuziness == 2 - */ static void append_dir_subtitles(struct mpv_global *global, struct subfn **slist, int *nsub, struct bstr path, const char *fname, @@ -103,7 +106,7 @@ static void append_dir_subtitles(struct mpv_global *global, { void *tmpmem = talloc_new(NULL); struct MPOpts *opts = global->opts; - struct mp_log *log = mp_log_new(tmpmem, global->log, "find_subfiles"); + struct mp_log *log = mp_log_new(tmpmem, global->log, "find_files"); if (mp_is_url(bstr0(fname))) goto out; @@ -121,7 +124,7 @@ static void append_dir_subtitles(struct mpv_global *global, DIR *d = opendir(path0); if (!d) goto out; - mp_verbose(log, "Load subtitles in %.*s\n", BSTR_P(path)); + mp_verbose(log, "Loading external files in %.*s\n", BSTR_P(path)); struct dirent *de; while ((de = readdir(d))) { struct bstr dename = bstr0(de->d_name); @@ -133,21 +136,35 @@ static void append_dir_subtitles(struct mpv_global *global, struct bstr tmp_fname_ext = get_ext(dename); struct bstr tmp_fname_trim = bstr_strip(tmp_fname_noext); - // does it end with a subtitle extension? - if (!is_sub_ext(tmp_fname_ext)) + // check what it is (most likely) + int type = test_ext(tmp_fname_ext); + char **langs = NULL; + int fuzz = -1; + switch (type) { + case STREAM_SUB: + langs = opts->sub_lang; + fuzz = opts->sub_auto; + break; + case STREAM_AUDIO: + langs = opts->audio_lang; + fuzz = opts->audiofile_auto; + break; + } + + if (fuzz < 0) goto next_sub; // we have a (likely) subtitle file int prio = 0; char *found_lang = NULL; - if (opts->sub_lang) { + if (langs) { if (bstr_startswith(tmp_fname_trim, f_fname_trim)) { struct bstr lang = guess_lang_from_filename(tmp_fname_trim); if (lang.len) { - for (int n = 0; opts->sub_lang[n]; n++) { - if (bstr_startswith0(lang, opts->sub_lang[n])) { + for (int n = 0; langs[n]; n++) { + if (bstr_startswith0(lang, langs[n])) { prio = 4; // matches the movie name + lang extension - found_lang = opts->sub_lang[n]; + found_lang = langs[n]; break; } } @@ -156,18 +173,19 @@ static void append_dir_subtitles(struct mpv_global *global, } if (!prio && bstrcmp(tmp_fname_trim, f_fname_trim) == 0) prio = 3; // matches the movie name - if (!prio && bstr_find(tmp_fname_trim, f_fname_trim) >= 0 - && opts->sub_auto >= 1) + if (!prio && bstr_find(tmp_fname_trim, f_fname_trim) >= 0 && fuzz >= 1) prio = 2; // contains the movie name if (!prio) { // doesn't contain the movie name // don't try in the mplayer subtitle directory - if (!limit_fuzziness && opts->sub_auto >= 2) { + if (!limit_fuzziness && fuzz >= 2) { prio = 1; } } - mp_dbg(log, "Potential sub file: \"%s\" Priority: %d\n", de->d_name, prio); + mp_dbg(log, "Potential external file: \"%s\" Priority: %d\n", + de->d_name, prio); + if (prio) { prio += prio; char *subpath = mp_path_join(*slist, path, dename); @@ -179,6 +197,7 @@ static void append_dir_subtitles(struct mpv_global *global, if (strncmp(subpath, "./", 2) == 0) subpath += 2; + sub->type = type; sub->priority = prio; sub->fname = subpath; sub->lang = found_lang; @@ -222,9 +241,9 @@ static void filter_subidx(struct subfn **slist, int *nsub) } } -// Return a list of subtitles found, sorted by priority. +// Return a list of subtitles and audio files found, sorted by priority. // Last element is terminated with a fname==NULL entry. -struct subfn *find_text_subtitles(struct mpv_global *global, const char *fname) +struct subfn *find_external_files(struct mpv_global *global, const char *fname) { struct MPOpts *opts = global->opts; struct subfn *slist = talloc_array_ptrtype(NULL, slist, 1); @@ -233,20 +252,22 @@ struct subfn *find_text_subtitles(struct mpv_global *global, const char *fname) // Load subtitles from current media directory append_dir_subtitles(global, &slist, &n, mp_dirname(fname), fname, 0); - // Load subtitles in dirs specified by sub-paths option - if (opts->sub_paths) { - for (int i = 0; opts->sub_paths[i]; i++) { - char *path = mp_path_join(slist, mp_dirname(fname), - bstr0(opts->sub_paths[i])); - append_dir_subtitles(global, &slist, &n, bstr0(path), fname, 0); + if (opts->sub_auto >= 0) { + // Load subtitles in dirs specified by sub-paths option + if (opts->sub_paths) { + for (int i = 0; opts->sub_paths[i]; i++) { + char *path = mp_path_join(slist, mp_dirname(fname), + bstr0(opts->sub_paths[i])); + append_dir_subtitles(global, &slist, &n, bstr0(path), fname, 0); + } } - } - // Load subtitles in ~/.mpv/sub limiting sub fuzziness - char *mp_subdir = mp_find_config_file(NULL, global, "sub/"); - if (mp_subdir) - append_dir_subtitles(global, &slist, &n, bstr0(mp_subdir), fname, 1); - talloc_free(mp_subdir); + // Load subtitles in ~/.mpv/sub limiting sub fuzziness + char *mp_subdir = mp_find_config_file(NULL, global, "sub/"); + if (mp_subdir) + append_dir_subtitles(global, &slist, &n, bstr0(mp_subdir), fname, 1); + talloc_free(mp_subdir); + } // Sort by name for filter_subidx() qsort(slist, n, sizeof(*slist), compare_sub_filename); diff --git a/sub/find_subfiles.h b/sub/find_subfiles.h index 85b491f1f9..a531b3e36a 100644 --- a/sub/find_subfiles.h +++ b/sub/find_subfiles.h @@ -22,13 +22,14 @@ #include struct subfn { + int type; // STREAM_SUB/STREAM_AUDIO int priority; char *fname; char *lang; }; struct mpv_global; -struct subfn *find_text_subtitles(struct mpv_global *global, const char *fname); +struct subfn *find_external_files(struct mpv_global *global, const char *fname); bool mp_might_be_subtitle_file(const char *filename); -- cgit v1.2.3