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:
parent
cf8fa2bdd4
commit
c07e046bfa
@ -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|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>``
|
||||
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),
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -22,13 +22,14 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user