0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 03:52:22 +02:00

player: add external audio file auto-loading

Apparently some people want this. Not enabled by default.

Fixes #967.
This commit is contained in:
wm4 2015-02-02 21:23:12 +01:00
parent cf8fa2bdd4
commit c07e046bfa
6 changed files with 134 additions and 84 deletions

View File

@ -1039,6 +1039,16 @@ Audio
maximum amplification, i.e. amplify by 200%. The default volume (no maximum amplification, i.e. amplify by 200%. The default volume (no
change in volume) will be ``50`` in this case. change in volume) will be ``50`` in this case.
``--audio-file-auto=<no|exact|fuzzy|all>``, ``--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=<name>`` ``--audio-client-name=<name>``
The application name the player reports to the audio API. Can be useful 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), if you want to force a different audio profile (e.g. with PulseAudio),

View File

@ -328,6 +328,8 @@ const m_option_t mp_opts[] = {
OPT_FLAG("sub-fix-timing", sub_fix_timing, 0), OPT_FLAG("sub-fix-timing", sub_fix_timing, 0),
OPT_CHOICE("sub-auto", sub_auto, 0, OPT_CHOICE("sub-auto", sub_auto, 0,
({"no", -1}, {"exact", 0}, {"fuzzy", 1}, {"all", 2})), ({"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_INTRANGE("sub-pos", sub_pos, 0, 0, 100),
OPT_FLOATRANGE("sub-gauss", sub_gauss, 0, 0.0, 3.0), OPT_FLOATRANGE("sub-gauss", sub_gauss, 0, 0.0, 3.0),
OPT_FLAG("sub-gray", sub_gray, 0), OPT_FLAG("sub-gray", sub_gray, 0),
@ -764,6 +766,7 @@ const struct MPOpts mp_default_opts = {
.movie_aspect = -1., .movie_aspect = -1.,
.field_dominance = -1, .field_dominance = -1,
.sub_auto = 0, .sub_auto = 0,
.audiofile_auto = -1,
.osd_bar_visible = 1, .osd_bar_visible = 1,
#if HAVE_LIBASS #if HAVE_LIBASS
.ass_enabled = 1, .ass_enabled = 1,

View File

@ -231,6 +231,7 @@ typedef struct MPOpts {
char **sub_name; char **sub_name;
char **sub_paths; char **sub_paths;
int sub_auto; int sub_auto;
int audiofile_auto;
int use_text_osd; int use_text_osd;
int osd_bar_visible; int osd_bar_visible;
float osd_bar_align_x; float osd_bar_align_x;

View File

@ -646,57 +646,32 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track)
return true; 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, static struct track *open_external_file(struct MPContext *mpctx, char *filename,
char *demuxer_name,
enum stream_type filter) enum stream_type filter)
{ {
struct MPOpts *opts = mpctx->opts; struct MPOpts *opts = mpctx->opts;
if (!filename) if (!filename)
return NULL; return NULL;
char *disp_filename = filename; char *disp_filename = filename;
if (strncmp(disp_filename, "memory://", 9) == 0) if (strncmp(disp_filename, "memory://", 9) == 0)
disp_filename = "memory://"; // avoid noise disp_filename = "memory://"; // avoid noise
struct stream *stream = stream_open(filename, mpctx->global); struct stream *stream = stream_open(filename, mpctx->global);
if (!stream) if (!stream)
goto err_out; 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); stream_enable_cache(&stream, &opts->stream_cache);
break;
}
struct demuxer_params params = { struct demuxer_params params = {
.expect_subtitle = filter == STREAM_SUB, .expect_subtitle = filter == STREAM_SUB,
}; };
@ -706,6 +681,7 @@ static struct track *open_external_file(struct MPContext *mpctx, char *filename,
free_stream(stream); free_stream(stream);
goto err_out; goto err_out;
} }
struct track *first = NULL; struct track *first = NULL;
for (int n = 0; n < demuxer->num_streams; n++) { for (int n = 0; n < demuxer->num_streams; n++) {
struct sh_stream *sh = demuxer->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); disp_filename);
goto err_out; goto err_out;
} }
MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, demuxer); MP_TARRAY_APPEND(NULL, mpctx->sources, mpctx->num_sources, demuxer);
return first; return first;
@ -736,17 +713,53 @@ err_out:
static void open_audiofiles_from_options(struct MPContext *mpctx) static void open_audiofiles_from_options(struct MPContext *mpctx)
{ {
struct MPOpts *opts = mpctx->opts; struct MPOpts *opts = mpctx->opts;
for (int n = 0; opts->audio_files && opts->audio_files[n]; n++) { for (int n = 0; opts->audio_files && opts->audio_files[n]; n++)
open_external_file(mpctx, opts->audio_files[n], opts->audio_demuxer_name, open_external_file(mpctx, opts->audio_files[n], STREAM_AUDIO);
STREAM_AUDIO);
}
} }
struct track *mp_add_subtitles(struct MPContext *mpctx, char *filename) 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; struct MPOpts *opts = mpctx->opts;
return open_external_file(mpctx, filename, opts->sub_demuxer_name, for (int i = 0; opts->sub_name && opts->sub_name[i] != NULL; i++)
STREAM_SUB); 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 // 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_subtitles_from_options(mpctx);
open_audiofiles_from_options(mpctx); open_audiofiles_from_options(mpctx);
autoload_external_files(mpctx);
check_previous_track_selection(mpctx); check_previous_track_selection(mpctx);

View File

@ -18,15 +18,27 @@ static const char *const sub_exts[] = {"utf", "utf8", "utf-8", "idx", "sub", "sr
"smi", "rt", "txt", "ssa", "aqt", "jss", "smi", "rt", "txt", "ssa", "aqt", "jss",
"js", "ass", "mks", NULL}; "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++) { for (int n = 0; list[n]; n++) {
if (bstrcasecmp(bstr0(sub_exts[n]), ext) == 0) if (bstrcasecmp(bstr0(list[n]), ext) == 0)
return true; return true;
} }
return false; 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) static struct bstr strip_ext(struct bstr str)
{ {
int dotpos = bstrrchr(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) 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) 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}; 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, static void append_dir_subtitles(struct mpv_global *global,
struct subfn **slist, int *nsub, struct subfn **slist, int *nsub,
struct bstr path, const char *fname, struct bstr path, const char *fname,
@ -103,7 +106,7 @@ static void append_dir_subtitles(struct mpv_global *global,
{ {
void *tmpmem = talloc_new(NULL); void *tmpmem = talloc_new(NULL);
struct MPOpts *opts = global->opts; 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))) if (mp_is_url(bstr0(fname)))
goto out; goto out;
@ -121,7 +124,7 @@ static void append_dir_subtitles(struct mpv_global *global,
DIR *d = opendir(path0); DIR *d = opendir(path0);
if (!d) if (!d)
goto out; 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; struct dirent *de;
while ((de = readdir(d))) { while ((de = readdir(d))) {
struct bstr dename = bstr0(de->d_name); 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_ext = get_ext(dename);
struct bstr tmp_fname_trim = bstr_strip(tmp_fname_noext); struct bstr tmp_fname_trim = bstr_strip(tmp_fname_noext);
// does it end with a subtitle extension? // check what it is (most likely)
if (!is_sub_ext(tmp_fname_ext)) 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; goto next_sub;
// we have a (likely) subtitle file // we have a (likely) subtitle file
int prio = 0; int prio = 0;
char *found_lang = NULL; char *found_lang = NULL;
if (opts->sub_lang) { if (langs) {
if (bstr_startswith(tmp_fname_trim, f_fname_trim)) { if (bstr_startswith(tmp_fname_trim, f_fname_trim)) {
struct bstr lang = guess_lang_from_filename(tmp_fname_trim); struct bstr lang = guess_lang_from_filename(tmp_fname_trim);
if (lang.len) { if (lang.len) {
for (int n = 0; opts->sub_lang[n]; n++) { for (int n = 0; langs[n]; n++) {
if (bstr_startswith0(lang, opts->sub_lang[n])) { if (bstr_startswith0(lang, langs[n])) {
prio = 4; // matches the movie name + lang extension prio = 4; // matches the movie name + lang extension
found_lang = opts->sub_lang[n]; found_lang = langs[n];
break; break;
} }
} }
@ -156,18 +173,19 @@ static void append_dir_subtitles(struct mpv_global *global,
} }
if (!prio && bstrcmp(tmp_fname_trim, f_fname_trim) == 0) if (!prio && bstrcmp(tmp_fname_trim, f_fname_trim) == 0)
prio = 3; // matches the movie name prio = 3; // matches the movie name
if (!prio && bstr_find(tmp_fname_trim, f_fname_trim) >= 0 if (!prio && bstr_find(tmp_fname_trim, f_fname_trim) >= 0 && fuzz >= 1)
&& opts->sub_auto >= 1)
prio = 2; // contains the movie name prio = 2; // contains the movie name
if (!prio) { if (!prio) {
// doesn't contain the movie name // doesn't contain the movie name
// don't try in the mplayer subtitle directory // don't try in the mplayer subtitle directory
if (!limit_fuzziness && opts->sub_auto >= 2) { if (!limit_fuzziness && fuzz >= 2) {
prio = 1; 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) { if (prio) {
prio += prio; prio += prio;
char *subpath = mp_path_join(*slist, path, dename); 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) if (strncmp(subpath, "./", 2) == 0)
subpath += 2; subpath += 2;
sub->type = type;
sub->priority = prio; sub->priority = prio;
sub->fname = subpath; sub->fname = subpath;
sub->lang = found_lang; 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. // 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 MPOpts *opts = global->opts;
struct subfn *slist = talloc_array_ptrtype(NULL, slist, 1); 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 // Load subtitles from current media directory
append_dir_subtitles(global, &slist, &n, mp_dirname(fname), fname, 0); append_dir_subtitles(global, &slist, &n, mp_dirname(fname), fname, 0);
// Load subtitles in dirs specified by sub-paths option if (opts->sub_auto >= 0) {
if (opts->sub_paths) { // Load subtitles in dirs specified by sub-paths option
for (int i = 0; opts->sub_paths[i]; i++) { if (opts->sub_paths) {
char *path = mp_path_join(slist, mp_dirname(fname), for (int i = 0; opts->sub_paths[i]; i++) {
bstr0(opts->sub_paths[i])); char *path = mp_path_join(slist, mp_dirname(fname),
append_dir_subtitles(global, &slist, &n, bstr0(path), fname, 0); bstr0(opts->sub_paths[i]));
append_dir_subtitles(global, &slist, &n, bstr0(path), fname, 0);
}
} }
}
// Load subtitles in ~/.mpv/sub limiting sub fuzziness // Load subtitles in ~/.mpv/sub limiting sub fuzziness
char *mp_subdir = mp_find_config_file(NULL, global, "sub/"); char *mp_subdir = mp_find_config_file(NULL, global, "sub/");
if (mp_subdir) if (mp_subdir)
append_dir_subtitles(global, &slist, &n, bstr0(mp_subdir), fname, 1); append_dir_subtitles(global, &slist, &n, bstr0(mp_subdir), fname, 1);
talloc_free(mp_subdir); talloc_free(mp_subdir);
}
// Sort by name for filter_subidx() // Sort by name for filter_subidx()
qsort(slist, n, sizeof(*slist), compare_sub_filename); qsort(slist, n, sizeof(*slist), compare_sub_filename);

View File

@ -22,13 +22,14 @@
#include <stdbool.h> #include <stdbool.h>
struct subfn { struct subfn {
int type; // STREAM_SUB/STREAM_AUDIO
int priority; int priority;
char *fname; char *fname;
char *lang; char *lang;
}; };
struct mpv_global; 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); bool mp_might_be_subtitle_file(const char *filename);