From 4e358a963604af8746a059d7388cb202be0f919d Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 3 Nov 2015 00:25:20 +0100 Subject: af_lavrresample: don't drop sl/sr channels for 7.1 on ALSA ao_alsa: attempt to fix 7.1 over HDMI The last 2 channels of 7.1 (RLC/RRC in ALSA) were exported as sdl/sdr instead of sl/sr (I don't even know why I chose sdl/sdr, but SL/SR and RLC/RRC are different in the ALSA API). libsw/avresample do not move the sl/sr channels to sdl/sdr when rematrixing, so silence was sent for 2 channels. If my selection of sdl/sdr is essentially API abuse, there's no reason why they should do this differently. The mess here is really that ALSa doesn't map the HDMI layouts cleanly. Most ALSA drivers export 7.1 in a way compatible to our expectations, but Intel HDA/HDMI does not: mpv/ffmpeg: fl-fr-fc-lfe-bl-br-sl-sr ALSA/generic: FL FR FC LFE RL RR SL SR [1] ALSA/HDMI: FL FR LFE FC RL RR RLC RRC [2] The HDMI layout is layout 0x13 (going by CEA-861-B). The comment in the kernel code has to be correct too. The early standard defines only 1 other layout, which replaces RLC/RRC with FRC/FLC - this probably corresponds to what we call "7.1(wide)". So it appears when ALSA requests RLC/RRC, we should feed it sl/sr. To make it more complicated, Kodi/xbmc apparently also have to deal with ALSA being special, but instead of sending sl/sr to RLC/RRC, they swap the last two pairs of the layout, and send sl/sr to RL/RR and bl/br to RLC/RRC. Or I might have misunderstood their code. I don't have a 7.1-capable A/V receiver, so I can't test this. For now, go with the simpler solution, and wait until someone tests it. If the speakers end up swapped, a completely different solution will be needed. [1] https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/sound/core/pcm_lib.c?id=refs/tags/v4.3#n2434 [2] https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/sound/pci/hda/patch_hdmi.c?id=refs/tags/v4.3#n307 --- audio/filter/af_lavrresample.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'audio/filter') diff --git a/audio/filter/af_lavrresample.c b/audio/filter/af_lavrresample.c index 721bb30165..6cdefe56c6 100644 --- a/audio/filter/af_lavrresample.c +++ b/audio/filter/af_lavrresample.c @@ -197,6 +197,24 @@ static void transpose_order(int *map, int num) memcpy(map, nmap, sizeof(nmap)); } +static uint64_t fudge_output_channel_layout(uint64_t in, uint64_t out) +{ + // If it tries to convert SR/SL to SDR/SLD, keep it SR/SL. + // This effectively makes it not rematrix at all when outputting + // 7.1 on ALSA. It could also be considered messing with FFmpeg's + // matrix generation. + uint64_t sp[][2] = { + {AV_CH_SIDE_LEFT, AV_CH_SURROUND_DIRECT_LEFT}, + {AV_CH_SIDE_RIGHT, AV_CH_SURROUND_DIRECT_RIGHT}, + }; + for (int n = 0; n < MP_ARRAY_SIZE(sp); n++) { + if ((in & sp[n][0]) && !(out & sp[n][0]) && + !(in & sp[n][1]) && (out & sp[n][1])) + out = (out & ~sp[n][1]) | sp[n][0]; + } + return out; +} + static int configure_lavrr(struct af_instance *af, struct mp_audio *in, struct mp_audio *out, bool verbose) { @@ -260,11 +278,6 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in, mp_chmap_from_lavc(&in_lavc, in_ch_layout); mp_chmap_from_lavc(&out_lavc, out_ch_layout); - if (verbose && !mp_chmap_equals(&in_lavc, &out_lavc)) { - MP_VERBOSE(af, "Remix: %s -> %s\n", mp_chmap_to_str(&in_lavc), - mp_chmap_to_str(&out_lavc)); - } - if (in_lavc.num != map_in.num) { // For handling NA channels, we would have to add a planarization step. MP_FATAL(af, "Unsupported channel remapping.\n"); @@ -301,6 +314,16 @@ static int configure_lavrr(struct af_instance *af, struct mp_audio *in, if (map_out.num > out_lavc.num) mp_audio_set_channels(&s->pool_fmt, &map_out); + out_ch_layout = fudge_output_channel_layout(in_ch_layout, out_ch_layout); + + struct mp_chmap out_lavc_actual; + mp_chmap_from_lavc(&out_lavc_actual, out_ch_layout); + + if (verbose && !mp_chmap_equals(&in_lavc, &out_lavc)) { + MP_VERBOSE(af, "Remix: %s -> %s\n", mp_chmap_to_str(&in_lavc), + mp_chmap_to_str(&out_lavc_actual)); + } + // Real conversion; output is input to avrctx_out. av_opt_set_int(s->avrctx, "in_channel_layout", in_ch_layout, 0); av_opt_set_int(s->avrctx, "out_channel_layout", out_ch_layout, 0); -- cgit v1.2.3