diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 6497d14684..456c3c3f94 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -1150,30 +1150,51 @@ Audio This and enabling passthrough via ``--ad`` are deprecated in favor of using ``--audio-spdif=dts-hd``. -``--audio-channels=`` - Request a channel layout for audio output (default: stereo). This will ask - the AO to open a device with the given channel layout. It's up to the AO - to accept this layout, or to pick a fallback or to error out if the - requested layout is not supported. +``--audio-channels=`` + Control which audio channels are output (e.g. surround vs. stereo). There + are the following possibilities: - The ``--audio-channels`` option either takes a channel number or an explicit - channel layout. Channel numbers refer to default layouts, e.g. 2 channels - refer to stereo, 6 refers to 5.1. + - ``--audio-channels=auto-safe`` + Use the system's preferred channel layout. If there is none (such + as when accessing a hardware device instead of the system mixer), + force stereo. Some audio outputs might simply accept any layout and + do downmixing on their own. + + This is the default. + - ``--audio-channels=auto`` + Send the audio device whatever it accepts, preferring the audio's + original channel layout. Can cause issues with HDMI (see the warning + below). + - ``--audio-channels=layout1,layout2,...`` + List of ``,``-separated channel layouts which should be allowed. + Technically, this only adjusts the filter chain output to the best + matching layout in the list, and passes the result to the audio API. + It's possible that the audio API will select a different channel + layout. + + Using this mode is recommended for direct hardware output, especially + over HDMI (see HDMI warning below). + - ``--audio-channels=stereo`` + Force a plain stereo downmix. This is a special-case of the previous + item. (See paragraphs below for implications.) + + If a list of layouts is given, each item can be either an explicit channel + layout name (like ``5.1``), or a channel number. Channel numbers refer to + default layouts, e.g. 2 channels refer to stereo, 6 refers to 5.1. See ``--audio-channels=help`` output for defined default layouts. This also lists speaker names, which can be used to express arbitrary channel layouts (e.g. ``fl-fr-lfe`` is 2.1). - ``--audio-channels=auto`` tries to play audio using the input file's - channel layout. There is no guarantee that the audio API handles this - correctly. See the HDMI warning below. - (``empty`` is an accepted obsolete alias for ``auto``.) - - This will also request the channel layout from the decoder. If the decoder - does not support the layout, it will fall back to its native channel layout. - (You can use ``--ad-lavc-downmix=no`` to make the decoder always output - its native layout.) Note that only some decoders support remixing audio. - Some that do include AC-3, AAC or DTS audio. + If the list of channel layouts has only 1 item, the decoder is asked to + produce according output. This sometimes triggers decoder-downmix, which + might be different from the normal mpv downmix. (Only some decoders support + remixing audio, like AC-3, AAC or DTS. You can use ``--ad-lavc-downmix=no`` + to make the decoder always output its native layout.) One consequence is + that ``--audio-channels=stereo`` triggers decoder downmix, while ``auto`` + or ``auto-safe`` never will, even if they end up selecting stereo. This + happens because the decision whether to use decoder downmix happens long + before the audio device is opened. If the channel layout of the media file (i.e. the decoder) and the AO's channel layout don't match, mpv will attempt to insert a conversion filter. @@ -1186,6 +1207,10 @@ Audio channel layout, random things can happen, such as dropping the additional channels, or adding noise. + You are recommended to set an explicit whitelist of the layouts you + want. For example, most A/V receivers connected via HDMI and that can + do 7.1 would be served by: ``--audio-channels=7.1,5.1,stereo`` + ``--audio-normalize-downmix=`` Enable/disable normalization if surround audio is downmixed to stereo (default: no). If this is disabled, downmix can cause clipping. If it's 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); diff --git a/demux/demux_raw.c b/demux/demux_raw.c index e4cf596768..bd8e11306d 100644 --- a/demux/demux_raw.c +++ b/demux/demux_raw.c @@ -36,7 +36,7 @@ #include "osdep/endian.h" struct demux_rawaudio_opts { - struct mp_chmap channels; + struct m_channels channels; int samplerate; int aformat; }; @@ -49,7 +49,7 @@ struct demux_rawaudio_opts { #define OPT_BASE_STRUCT struct demux_rawaudio_opts const struct m_sub_options demux_rawaudio_conf = { .opts = (const m_option_t[]) { - OPT_CHMAP("channels", channels, CONF_MIN, .min = 1), + OPT_CHANNELS("channels", channels, 0, .min = 1), OPT_INTRANGE("rate", samplerate, 0, 1000, 8 * 48000), OPT_CHOICE("format", aformat, 0, ({"u8", PCM(0, 0, 8, 0)}, @@ -75,7 +75,11 @@ const struct m_sub_options demux_rawaudio_conf = { .size = sizeof(struct demux_rawaudio_opts), .defaults = &(const struct demux_rawaudio_opts){ // Note that currently, stream_cdda expects exactly these parameters! - .channels = MP_CHMAP_INIT_STEREO, + .channels = { + .set = 1, + .chmaps = (struct mp_chmap[]){ MP_CHMAP_INIT_STEREO, }, + .num_chmaps = 1, + }, .samplerate = 44100, .aformat = PCM(1, 0, 16, 0), // s16le }, @@ -130,9 +134,14 @@ static int demux_rawaudio_open(demuxer_t *demuxer, enum demux_check check) if (check != DEMUX_CHECK_REQUEST && check != DEMUX_CHECK_FORCE) return -1; + if (opts->channels.num_chmaps != 1) { + MP_ERR(demuxer, "Invalid channels option given.\n"); + return -1; + } + struct sh_stream *sh = demux_alloc_sh_stream(STREAM_AUDIO); struct mp_codec_params *c = sh->codec; - c->channels = opts->channels; + c->channels = opts->channels.chmaps[0]; c->force_channels = true; c->samplerate = opts->samplerate; diff --git a/options/m_option.c b/options/m_option.c index b7b76634f8..231b9aa5d2 100644 --- a/options/m_option.c +++ b/options/m_option.c @@ -2287,51 +2287,105 @@ const m_option_type_t m_option_type_afmt = { #include "audio/chmap.h" -static int parse_chmap(struct mp_log *log, const m_option_t *opt, - struct bstr name, struct bstr param, void *dst) +static int parse_channels(struct mp_log *log, const m_option_t *opt, + struct bstr name, struct bstr param, void *dst) { - // min>0: at least min channels, min=0: empty ok - int min_ch = (opt->flags & M_OPT_MIN) ? opt->min : 1; - assert(min_ch >= 0); + // see OPT_CHANNELS for semantics. + bool limited = opt->min; + + struct m_channels res = {0}; if (bstr_equals0(param, "help")) { mp_chmap_print_help(log); + if (!limited) { + mp_info(log, "\nOther values:\n" + " auto-safe\n"); + } return M_OPT_EXIT - 1; } - if (param.len == 0 && min_ch >= 1) - return M_OPT_MISSING_PARAM; - - struct mp_chmap res = {0}; - if (!mp_chmap_from_str(&res, param)) { - mp_err(log, "Error parsing channel layout: %.*s\n", BSTR_P(param)); - return M_OPT_INVALID; + bool auto_safe = bstr_equals0(param, "auto-safe"); + if (bstr_equals0(param, "auto") || bstr_equals0(param, "empty") || auto_safe) { + if (limited) { + mp_err(log, "Disallowed parameter.\n"); + return M_OPT_INVALID; + } + param.len = 0; + res.set = true; + res.auto_safe = auto_safe; } - if (!mp_chmap_is_valid(&res) && !(min_ch == 0 && mp_chmap_is_empty(&res))) { - mp_err(log, "Invalid channel layout: %.*s\n", BSTR_P(param)); - return M_OPT_INVALID; + while (param.len) { + bstr item; + if (limited) { + item = param; + param.len = 0; + } else { + bstr_split_tok(param, ",", &item, ¶m); + } + + struct mp_chmap map = {0}; + if (!mp_chmap_from_str(&map, item) || !mp_chmap_is_valid(&map)) { + mp_err(log, "Invalid channel layout: %.*s\n", BSTR_P(item)); + talloc_free(res.chmaps); + return M_OPT_INVALID; + } + + MP_TARRAY_APPEND(NULL, res.chmaps, res.num_chmaps, map); + res.set = true; } - if (dst) - *(struct mp_chmap *)dst = res; + if (dst) { + *(struct m_channels *)dst = res; + } else { + talloc_free(res.chmaps); + } return 1; } -static char *print_chmap(const m_option_t *opt, const void *val) +static char *print_channels(const m_option_t *opt, const void *val) { - const struct mp_chmap *chmap = val; - return talloc_strdup(NULL, mp_chmap_to_str(chmap)); + const struct m_channels *ch = val; + if (!ch->set) + return talloc_strdup(NULL, ""); + if (ch->auto_safe) + return talloc_strdup(NULL, "auto-safe"); + if (ch->num_chmaps > 0) { + char *res = talloc_strdup(NULL, ""); + for (int n = 0; n < ch->num_chmaps; n++) { + if (n > 0) + res = talloc_strdup_append(res, ","); + res = talloc_strdup_append(res, mp_chmap_to_str(&ch->chmaps[n])); + } + return res; + } + return talloc_strdup(NULL, "auto"); } +static void free_channels(void *src) +{ + struct m_channels *ch = src; + talloc_free(ch->chmaps); + *ch = (struct m_channels){0}; +} -const m_option_type_t m_option_type_chmap = { +static void copy_channels(const m_option_t *opt, void *dst, const void *src) +{ + struct m_channels *ch = dst; + free_channels(dst); + *ch = *(struct m_channels *)src; + ch->chmaps = + talloc_memdup(NULL, ch->chmaps, sizeof(ch->chmaps[0]) * ch->num_chmaps); +} + +const m_option_type_t m_option_type_channels = { .name = "Audio channels or channel map", - .size = sizeof(struct mp_chmap), - .parse = parse_chmap, - .print = print_chmap, - .copy = copy_opt, + .size = sizeof(struct m_channels), + .parse = parse_channels, + .print = print_channels, + .copy = copy_channels, + .free = free_channels, }; static int parse_timestring(struct bstr str, double *time, char endchar) diff --git a/options/m_option.h b/options/m_option.h index e77452af69..80be447c42 100644 --- a/options/m_option.h +++ b/options/m_option.h @@ -61,7 +61,7 @@ extern const m_option_type_t m_option_type_afmt; extern const m_option_type_t m_option_type_color; extern const m_option_type_t m_option_type_geometry; extern const m_option_type_t m_option_type_size_box; -extern const m_option_type_t m_option_type_chmap; +extern const m_option_type_t m_option_type_channels; extern const m_option_type_t m_option_type_node; // Used internally by m_config.c @@ -98,6 +98,13 @@ struct m_geometry { void m_geometry_apply(int *xpos, int *ypos, int *widw, int *widh, int scrw, int scrh, struct m_geometry *gm); +struct m_channels { + bool set : 1; + bool auto_safe : 1; + struct mp_chmap *chmaps; + int num_chmaps; +}; + struct m_obj_desc { // Name which will be used in the option string const char *name; @@ -218,7 +225,7 @@ union m_option_value { struct m_color color; struct m_geometry geometry; struct m_geometry size_box; - struct mp_chmap chmap; + struct m_channels channels; }; //////////////////////////////////////////////////////////////////////////// @@ -626,9 +633,10 @@ extern const char m_option_path_separator; #define OPT_AUDIOFORMAT(...) \ OPT_GENERAL(int, __VA_ARGS__, .type = &m_option_type_afmt) -#define OPT_CHMAP(...) \ - OPT_GENERAL(struct mp_chmap, __VA_ARGS__, .type = &m_option_type_chmap) - +// If .min==1, then passing auto is disallowed, but "" is still accepted, and +// limit channel list to 1 item. +#define OPT_CHANNELS(...) \ + OPT_GENERAL(struct m_channels, __VA_ARGS__, .type = &m_option_type_channels) #define M_CHOICES(choices) \ .priv = (void *)&(const struct m_opt_choice_alternatives[]){ \ diff --git a/options/options.c b/options/options.c index 0ad9d71b40..9fd853ffda 100644 --- a/options/options.c +++ b/options/options.c @@ -279,7 +279,7 @@ const m_option_t mp_opts[] = { // force video/audio rate: OPT_DOUBLE("fps", force_fps, CONF_MIN, .min = 0), OPT_INTRANGE("audio-samplerate", force_srate, 0, 1000, 16*48000), - OPT_CHMAP("audio-channels", audio_output_channels, CONF_MIN, .min = 0), + OPT_CHANNELS("audio-channels", audio_output_channels, 0), OPT_AUDIOFORMAT("audio-format", audio_output_format, 0), OPT_FLAG("audio-normalize-downmix", audio_normalize, 0), OPT_DOUBLE("speed", playback_speed, M_OPT_RANGE | M_OPT_FIXED, @@ -805,7 +805,6 @@ const struct MPOpts mp_default_opts = { .sub_visibility = 1, .sub_pos = 100, .sub_speed = 1.0, - .audio_output_channels = MP_CHMAP_INIT_STEREO, .audio_output_format = 0, // AF_FORMAT_UNKNOWN .playback_speed = 1., .pitch_correction = 1, diff --git a/options/options.h b/options/options.h index 7690823aab..ef72865f56 100644 --- a/options/options.h +++ b/options/options.h @@ -226,7 +226,7 @@ typedef struct MPOpts { double force_fps; int index_mode; - struct mp_chmap audio_output_channels; + struct m_channels audio_output_channels; int audio_output_format; int audio_normalize; int force_srate; diff --git a/player/audio.c b/player/audio.c index bcaeb13e9f..2f2bc07f0f 100644 --- a/player/audio.c +++ b/player/audio.c @@ -353,7 +353,10 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx) } else if (af_fmt_is_pcm(in_format.format)) { afs->output.rate = opts->force_srate; mp_audio_set_format(&afs->output, opts->audio_output_format); - mp_audio_set_channels(&afs->output, &opts->audio_output_channels); + if (opts->audio_output_channels.num_chmaps == 1) { + mp_audio_set_channels(&afs->output, + &opts->audio_output_channels.chmaps[0]); + } } // filter input format: same as codec's output format: @@ -368,13 +371,22 @@ static void reinit_audio_filters_and_output(struct MPContext *mpctx) } if (!mpctx->ao) { + int ao_flags = 0; bool spdif_fallback = af_fmt_is_spdif(afs->output.format) && ao_c->spdif_passthrough; - bool ao_null_fallback = opts->ao_null_fallback && !spdif_fallback; + + if (opts->ao_null_fallback && !spdif_fallback) + ao_flags |= AO_INIT_NULL_FALLBACK; + + if (!opts->audio_output_channels.set || opts->audio_output_channels.auto_safe) + ao_flags |= AO_INIT_SAFE_MULTICHANNEL_ONLY; + + mp_chmap_sel_list(&afs->output.channels, opts->audio_output_channels.chmaps, + opts->audio_output_channels.num_chmaps); mp_audio_set_channels(&afs->output, &afs->output.channels); - mpctx->ao = ao_init_best(mpctx->global, ao_null_fallback, mpctx->input, + mpctx->ao = ao_init_best(mpctx->global, ao_flags, mpctx->input, mpctx->encode_lavc_ctx, afs->output.rate, afs->output.format, afs->output.channels); ao_c->ao = mpctx->ao; diff --git a/player/main.c b/player/main.c index 78652fe562..67dde3449f 100644 --- a/player/main.c +++ b/player/main.c @@ -451,11 +451,6 @@ int mp_initialize(struct MPContext *mpctx, char **options) return -1; } m_config_set_profile(mpctx->mconfig, "encoding", 0); - // never use auto - if (!opts->audio_output_channels.num) { - m_config_set_option_ext(mpctx->mconfig, bstr0("audio-channels"), - bstr0("stereo"), M_SETOPT_PRESERVE_CMDLINE); - } mp_input_enable_section(mpctx->input, "encode", MP_INPUT_EXCLUSIVE); } #endif