summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2016-08-04 20:49:20 +0200
committerwm4 <wm4@nowhere>2016-08-04 20:49:20 +0200
commit0b144eac39bbae51c3f2b7683f6982eb91b66393 (patch)
tree63f0585f4dece4195da5745a4969159d9b5d8122 /audio
parentc30aa2340132e50994cdf168aaba011ecb4f6b0c (diff)
downloadmpv-0b144eac39bbae51c3f2b7683f6982eb91b66393.tar.bz2
mpv-0b144eac39bbae51c3f2b7683f6982eb91b66393.tar.xz
audio: use --audio-channels=auto behavior, except on ALSA
This commit adds an --audio-channel=auto-safe mode, and makes it the default. This mode behaves like "auto" with most AOs, except with ao_alsa. The intention is to allow multichannel output by default on sane APIs. ALSA is not sane as in it's so low level that it will e.g. configure any layout over HDMI, even if the connected A/V receiver does not support it. The HDMI fuckup is of course not ALSA's fault, but other audio APIs normally isolate applications from dealing with this and require the user to globally configure the correct output layout. This will help with other AOs too. ao_lavc (encoding) is changed to the new semantics as well, because it used to force stereo (perhaps because encoding mode is supposed to produce safe files for crap devices?). Exclusive mode output on Windows might need to be adjusted accordingly, as it grants the same kind of low level access as ALSA (requires more research). In addition to the things mentioned above, the --audio-channels option is extended to accept a set of channel layouts. This is supposed to be the correct way to configure mpv ALSA multichannel output. You need to put a list of channel layouts that your A/V receiver supports.
Diffstat (limited to 'audio')
-rw-r--r--audio/chmap_sel.c13
-rw-r--r--audio/chmap_sel.h2
-rw-r--r--audio/decode/ad_lavc.c4
-rw-r--r--audio/filter/af_format.c16
-rw-r--r--audio/out/ao.c34
-rw-r--r--audio/out/ao.h10
-rw-r--r--audio/out/ao_alsa.c2
-rw-r--r--audio/out/ao_lavc.c2
-rw-r--r--audio/out/internal.h3
9 files changed, 68 insertions, 18 deletions
diff --git a/audio/chmap_sel.c b/audio/chmap_sel.c
index 45b696c924..4fb7544f20 100644
--- a/audio/chmap_sel.c
+++ b/audio/chmap_sel.c
@@ -374,3 +374,16 @@ void mp_chmal_sel_log(const struct mp_chmap_sel *s, struct mp_log *log, int lev)
if (s->allow_any)
mp_msg(log, lev, " - anything\n");
}
+
+// Select a channel map from the given list that fits best to c. Don't change
+// *c if there's no match, or the list is empty.
+void mp_chmap_sel_list(struct mp_chmap *c, struct mp_chmap *maps, int num_maps)
+{
+ // This is a separate function to keep messing with mp_chmap_sel internals
+ // within this source file.
+ struct mp_chmap_sel sel = {
+ .chmaps = maps,
+ .num_chmaps = num_maps,
+ };
+ mp_chmap_sel_fallback(&sel, c);
+}
diff --git a/audio/chmap_sel.h b/audio/chmap_sel.h
index 5bd8783b83..4b11557a2b 100644
--- a/audio/chmap_sel.h
+++ b/audio/chmap_sel.h
@@ -47,4 +47,6 @@ bool mp_chmap_sel_get_def(const struct mp_chmap_sel *s, struct mp_chmap *map,
struct mp_log;
void mp_chmal_sel_log(const struct mp_chmap_sel *s, struct mp_log *log, int lev);
+void mp_chmap_sel_list(struct mp_chmap *c, struct mp_chmap *maps, int num_maps);
+
#endif
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c
index 0316f6b7d1..c785c62c90 100644
--- a/audio/decode/ad_lavc.c
+++ b/audio/decode/ad_lavc.c
@@ -104,9 +104,9 @@ static int init(struct dec_audio *da, const char *decoder)
lavc_context->codec_type = AVMEDIA_TYPE_AUDIO;
lavc_context->codec_id = lavc_codec->id;
- if (opts->downmix) {
+ if (opts->downmix && mpopts->audio_output_channels.num_chmaps == 1) {
lavc_context->request_channel_layout =
- mp_chmap_to_lavc(&mpopts->audio_output_channels);
+ mp_chmap_to_lavc(&mpopts->audio_output_channels.chmaps[0]);
}
// Always try to set - option only exists for AC3 at the moment
diff --git a/audio/filter/af_format.c b/audio/filter/af_format.c
index c0fe354a39..748c5cbd52 100644
--- a/audio/filter/af_format.c
+++ b/audio/filter/af_format.c
@@ -29,10 +29,10 @@ struct priv {
int in_format;
int in_srate;
- struct mp_chmap in_channels;
+ struct m_channels in_channels;
int out_format;
int out_srate;
- struct mp_chmap out_channels;
+ struct m_channels out_channels;
int fail;
};
@@ -44,8 +44,8 @@ static void force_in_params(struct af_instance *af, struct mp_audio *in)
if (priv->in_format != AF_FORMAT_UNKNOWN)
mp_audio_set_format(in, priv->in_format);
- if (priv->in_channels.num)
- mp_audio_set_channels(in, &priv->in_channels);
+ if (priv->in_channels.num_chmaps > 0)
+ mp_audio_set_channels(in, &priv->in_channels.chmaps[0]);
if (priv->in_srate)
in->rate = priv->in_srate;
@@ -58,8 +58,8 @@ static void force_out_params(struct af_instance *af, struct mp_audio *out)
if (priv->out_format != AF_FORMAT_UNKNOWN)
mp_audio_set_format(out, priv->out_format);
- if (priv->out_channels.num)
- mp_audio_set_channels(out, &priv->out_channels);
+ if (priv->out_channels.num_chmaps > 0)
+ mp_audio_set_channels(out, &priv->out_channels.chmaps[0]);
if (priv->out_srate)
out->rate = priv->out_srate;
@@ -124,10 +124,10 @@ const struct af_info af_info_format = {
.options = (const struct m_option[]) {
OPT_AUDIOFORMAT("format", in_format, 0),
OPT_INTRANGE("srate", in_srate, 0, 1000, 8*48000),
- OPT_CHMAP("channels", in_channels, CONF_MIN, .min = 0),
+ OPT_CHANNELS("channels", in_channels, 0, .min = 1),
OPT_AUDIOFORMAT("out-format", out_format, 0),
OPT_INTRANGE("out-srate", out_srate, 0, 1000, 8*48000),
- OPT_CHMAP("out-channels", out_channels, CONF_MIN, .min = 0),
+ OPT_CHANNELS("out-channels", out_channels, 0, .min = 1),
OPT_FLAG("fail", fail, 0),
{0}
},
diff --git a/audio/out/ao.c b/audio/out/ao.c
index c9d8f42b4a..0647067e50 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -158,7 +158,7 @@ error:
static struct ao *ao_init(bool probing, struct mpv_global *global,
struct input_ctx *input_ctx,
- struct encode_lavc_context *encode_lavc_ctx,
+ struct encode_lavc_context *encode_lavc_ctx, int flags,
int samplerate, int format, struct mp_chmap channels,
char *dev, char *name, char **args)
{
@@ -169,6 +169,7 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
ao->channels = channels;
ao->format = format;
ao->encode_lavc_ctx = encode_lavc_ctx;
+ ao->init_flags = flags;
if (ao->driver->encode != !!ao->encode_lavc_ctx)
goto fail;
@@ -190,7 +191,7 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
snprintf(redirect, sizeof(redirect), "%s", ao->redirect);
snprintf(rdevice, sizeof(rdevice), "%s", ao->device ? ao->device : "");
talloc_free(ao);
- return ao_init(probing, global, input_ctx, encode_lavc_ctx,
+ return ao_init(probing, global, input_ctx, encode_lavc_ctx, flags,
samplerate, format, channels, rdevice, redirect, NULL);
}
goto fail;
@@ -240,7 +241,7 @@ static void split_ao_device(void *tmp, char *opt, char **out_ao, char **out_dev)
}
struct ao *ao_init_best(struct mpv_global *global,
- bool ao_null_fallback,
+ int init_flags,
struct input_ctx *input_ctx,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels)
@@ -283,7 +284,7 @@ struct ao *ao_init_best(struct mpv_global *global,
}
}
- if (ao_null_fallback) {
+ if (init_flags & AO_INIT_NULL_FALLBACK) {
MP_TARRAY_APPEND(tmp, ao_list, ao_num,
(struct m_obj_settings){.name = "null"});
}
@@ -297,7 +298,7 @@ struct ao *ao_init_best(struct mpv_global *global,
dev = pref_dev;
mp_verbose(log, "Using preferred device '%s'\n", dev);
}
- ao = ao_init(probing, global, input_ctx, encode_lavc_ctx,
+ ao = ao_init(probing, global, input_ctx, encode_lavc_ctx, init_flags,
samplerate, format, channels, dev,
entry->name, entry->attribs);
if (ao)
@@ -429,6 +430,29 @@ bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
return r;
}
+// safe_multichannel=true behaves like ao_chmap_sel_adjust.
+// safe_multichannel=false is a helper for callers which do not support safe
+// handling of arbitrary channel layouts. If the multichannel layouts are not
+// considered "always safe" (e.g. HDMI), then allow only stereo or mono, if
+// they are part of the list in *s.
+bool ao_chmap_sel_adjust2(struct ao *ao, const struct mp_chmap_sel *s,
+ struct mp_chmap *map, bool safe_multichannel)
+{
+ if (!safe_multichannel && (ao->init_flags & AO_INIT_SAFE_MULTICHANNEL_ONLY)) {
+ struct mp_chmap res = *map;
+ if (mp_chmap_sel_adjust(s, &res)) {
+ if (!mp_chmap_equals(&res, &(struct mp_chmap)MP_CHMAP_INIT_MONO) &&
+ !mp_chmap_equals(&res, &(struct mp_chmap)MP_CHMAP_INIT_STEREO))
+ {
+ MP_WARN(ao, "Disabling multichannel output.\n");
+ *map = (struct mp_chmap)MP_CHMAP_INIT_STEREO;
+ }
+ }
+ }
+
+ return ao_chmap_sel_adjust(ao, s, map);
+}
+
bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map, int num)
{
diff --git a/audio/out/ao.h b/audio/out/ao.h
index e8e64e33eb..3c16cef0e5 100644
--- a/audio/out/ao.h
+++ b/audio/out/ao.h
@@ -50,6 +50,14 @@ enum {
AO_EVENT_HOTPLUG = 2,
};
+enum {
+ // Allow falling back to ao_null if nothing else works.
+ AO_INIT_NULL_FALLBACK = 1 << 0,
+ // Only accept multichannel configurations that are guaranteed to work
+ // (i.e. not sending arbitrary layouts over HDMI).
+ AO_INIT_SAFE_MULTICHANNEL_ONLY = 1 << 1,
+};
+
typedef struct ao_control_vol {
float left;
float right;
@@ -72,7 +80,7 @@ struct encode_lavc_context;
struct mp_audio;
struct ao *ao_init_best(struct mpv_global *global,
- bool ao_null_fallback,
+ int init_flags,
struct input_ctx *input_ctx,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels);
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index d09f5fc499..4a55d19656 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -360,7 +360,7 @@ static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
snd_pcm_free_chmaps(maps);
- return ao_chmap_sel_adjust(ao, &chmap_sel, chmap);
+ return ao_chmap_sel_adjust2(ao, &chmap_sel, chmap, false);
}
// Map back our selected channel layout to an ALSA one. This is done this way so
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
index 6b4279ca87..8ae1317407 100644
--- a/audio/out/ao_lavc.c
+++ b/audio/out/ao_lavc.c
@@ -123,7 +123,7 @@ static int init(struct ao *ao)
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_any(&sel);
- if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
+ if (!ao_chmap_sel_adjust2(ao, &sel, &ao->channels, false))
goto fail;
mp_chmap_reorder_to_lavc(&ao->channels);
ac->codec->channels = ao->channels.num;
diff --git a/audio/out/internal.h b/audio/out/internal.h
index 49131ba293..88160bbeb6 100644
--- a/audio/out/internal.h
+++ b/audio/out/internal.h
@@ -43,6 +43,7 @@ struct ao {
struct encode_lavc_context *encode_lavc_ctx;
struct input_ctx *input_ctx;
struct mp_log *log; // Using e.g. "[ao/coreaudio]" as prefix
+ int init_flags; // AO_INIT_* flags
// The device as selected by the user, usually using ao_device_desc.name
// from an entry from the list returned by driver->list_devices. If the
@@ -191,6 +192,8 @@ void ao_wakeup_poll(struct ao *ao);
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map);
+bool ao_chmap_sel_adjust2(struct ao *ao, const struct mp_chmap_sel *s,
+ struct mp_chmap *map, bool safe_multichannel);
bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map, int num);