PR #24739 opened by Kacper Michajłow (kasper93) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24739 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24739.patch
Some small fixes to remixing found when I was testing mpv changes. Each change has own FATE test, so it's clear what has changed. From c4000578dd8ca6a47973753ecbd0ed7aec99a3f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sun, 27 Sep 2026 16:02:49 +0200 Subject: [PATCH 1/5] swresample/rematrix: treat a single channel next to unused ones as mono MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A layout of one channel is mixed like mono, whatever the channel is. Unused channels carry no audio, so the same holds for a custom layout that has a single channel next to them. It was rejected for being asymmetric. Signed-off-by: Kacper Michajłow <[email protected]> --- libswresample/rematrix.c | 37 +++++++++++++++---- tests/fate/libswresample.mak | 8 ++++ tests/ref/fate/swr-rematrix-unused-mono-input | 6 +++ .../ref/fate/swr-rematrix-unused-mono-output | 2 + 4 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 tests/ref/fate/swr-rematrix-unused-mono-input create mode 100644 tests/ref/fate/swr-rematrix-unused-mono-output diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c index 582b2b38e3..fa976b7f7c 100644 --- a/libswresample/rematrix.c +++ b/libswresample/rematrix.c @@ -98,17 +98,38 @@ static int even(int64_t layout){ static int clean_layout(AVChannelLayout *out, const AVChannelLayout *in, void *s) { - int ret = 0; + int used = in->nb_channels, idx = 0; - if (av_channel_layout_index_from_channel(in, AV_CHAN_FRONT_CENTER) < 0 && in->nb_channels == 1) { - char buf[128]; - av_channel_layout_describe(in, buf, sizeof(buf)); - av_log(s, AV_LOG_VERBOSE, "Treating %s as mono\n", buf); + if (in->order == AV_CHANNEL_ORDER_CUSTOM && in->nb_channels > 1) { + used = 0; + for (int i = 0; i < in->nb_channels; i++) { + if (in->u.map[i].id != AV_CHAN_UNUSED) { + idx = i; + used++; + } + } + } + + if (used != 1 || + av_channel_layout_channel_from_index(in, idx) == AV_CHAN_FRONT_CENTER) + return av_channel_layout_copy(out, in); + + char buf[128]; + av_channel_layout_describe(in, buf, sizeof(buf)); + av_log(s, AV_LOG_VERBOSE, "Treating %s as mono\n", buf); + + if (in->nb_channels == 1) { *out = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; - } else - ret = av_channel_layout_copy(out, in); + return 0; + } - return ret; + /* the channel keeps its place among the unused ones */ + int ret = av_channel_layout_copy(out, in); + if (ret < 0) + return ret; + out->u.map[idx].id = AV_CHAN_FRONT_CENTER; + + return 0; } static int sane_layout(const AVChannelLayout *ch_layout) { diff --git a/tests/fate/libswresample.mak b/tests/fate/libswresample.mak index d23525edbc..ca915a3764 100644 --- a/tests/fate/libswresample.mak +++ b/tests/fate/libswresample.mak @@ -1146,6 +1146,14 @@ FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-unused-output fate-swr-rematrix-unused-output: libswresample/tests/rematrix$(EXESUF) fate-swr-rematrix-unused-output: CMD = run libswresample/tests/rematrix$(EXESUF) 5.1 FL+UNSD+FR+UNSD +FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-unused-mono-input +fate-swr-rematrix-unused-mono-input: libswresample/tests/rematrix$(EXESUF) +fate-swr-rematrix-unused-mono-input: CMD = run libswresample/tests/rematrix$(EXESUF) BL+UNSD+UNSD 5.1 + +FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-unused-mono-output +fate-swr-rematrix-unused-mono-output: libswresample/tests/rematrix$(EXESUF) +fate-swr-rematrix-unused-mono-output: CMD = run libswresample/tests/rematrix$(EXESUF) stereo UNSD+FR + FATE_SWR += $(FATE_SWR_REMATRIX-yes) fate-swr-rematrix: $(FATE_SWR_REMATRIX-yes) diff --git a/tests/ref/fate/swr-rematrix-unused-mono-input b/tests/ref/fate/swr-rematrix-unused-mono-input new file mode 100644 index 0000000000..57ca24ebf0 --- /dev/null +++ b/tests/ref/fate/swr-rematrix-unused-mono-input @@ -0,0 +1,6 @@ +[FL] = { .BL = 0.000000, .UNSD1 = 0.000000, .UNSD2 = 0.000000, }, +[FR] = { .BL = 0.000000, .UNSD1 = 0.000000, .UNSD2 = 0.000000, }, +[FC] = { .BL = 1.000000, .UNSD1 = 0.000000, .UNSD2 = 0.000000, }, +[LFE] = { .BL = 0.000000, .UNSD1 = 0.000000, .UNSD2 = 0.000000, }, +[BL] = { .BL = 0.000000, .UNSD1 = 0.000000, .UNSD2 = 0.000000, }, +[BR] = { .BL = 0.000000, .UNSD1 = 0.000000, .UNSD2 = 0.000000, }, diff --git a/tests/ref/fate/swr-rematrix-unused-mono-output b/tests/ref/fate/swr-rematrix-unused-mono-output new file mode 100644 index 0000000000..21a67e6d94 --- /dev/null +++ b/tests/ref/fate/swr-rematrix-unused-mono-output @@ -0,0 +1,2 @@ +[UNSD0] = { .FL = 0.000000, .FR = 0.000000, }, +[FR] = { .FL = 0.707107, .FR = 0.707107, }, -- 2.52.0 From 3763c1a5ab4ea7946f73e75f20f93d02cac51088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sun, 27 Sep 2026 16:03:32 +0200 Subject: [PATCH 2/5] swresample/rematrix: recognize the stereo downmix pair in custom layouts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pair is mixed like stereo if the other side has no such channels. It was only recognized as the native layout, so a custom layout with the pair in another order or next to unused channels was rejected for the lack of a front channel. Signed-off-by: Kacper Michajłow <[email protected]> --- libswresample/rematrix.c | 36 ++++++++++++++-------- tests/fate/libswresample.mak | 8 +++++ tests/ref/fate/swr-rematrix-downmix-input | 6 ++++ tests/ref/fate/swr-rematrix-downmix-output | 3 ++ 4 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 tests/ref/fate/swr-rematrix-downmix-input create mode 100644 tests/ref/fate/swr-rematrix-downmix-output diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c index fa976b7f7c..7a0ea88682 100644 --- a/libswresample/rematrix.c +++ b/libswresample/rematrix.c @@ -132,6 +132,28 @@ static int clean_layout(AVChannelLayout *out, const AVChannelLayout *in, void *s return 0; } +/** + * Stereo downmix channels are mixed like stereo, unless the other layout has + * such channels as well. The channels keep their places. + */ +static void clean_downmix(AVChannelLayout *ch_layout, const AVChannelLayout *other) +{ + if (av_channel_layout_subset(ch_layout, ~(uint64_t)0) != AV_CH_LAYOUT_STEREO_DOWNMIX || + av_channel_layout_subset(other, AV_CH_LAYOUT_STEREO_DOWNMIX)) + return; + + if (ch_layout->order == AV_CHANNEL_ORDER_NATIVE) { + ch_layout->u.mask = AV_CH_LAYOUT_STEREO; + } else if (ch_layout->order == AV_CHANNEL_ORDER_CUSTOM) { + for (int i = 0; i < ch_layout->nb_channels; i++) { + if (ch_layout->u.map[i].id == AV_CHAN_STEREO_LEFT) + ch_layout->u.map[i].id = AV_CHAN_FRONT_LEFT; + else if (ch_layout->u.map[i].id == AV_CHAN_STEREO_RIGHT) + ch_layout->u.map[i].id = AV_CHAN_FRONT_RIGHT; + } + } +} + static int sane_layout(const AVChannelLayout *ch_layout) { if(ch_layout->nb_channels >= SWR_CH_MAX) return 0; @@ -605,18 +627,8 @@ av_cold int swr_build_matrix2(const AVChannelLayout *in_layout, const AVChannelL if (ret < 0) goto fail; - if( !av_channel_layout_compare(&out_ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO_DOWNMIX) - && !av_channel_layout_subset(&in_ch_layout, AV_CH_LAYOUT_STEREO_DOWNMIX) - ) { - av_channel_layout_uninit(&out_ch_layout); - out_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; - } - if( !av_channel_layout_compare(&in_ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO_DOWNMIX) - && !av_channel_layout_subset(&out_ch_layout, AV_CH_LAYOUT_STEREO_DOWNMIX) - ) { - av_channel_layout_uninit(&in_ch_layout); - in_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; - } + clean_downmix(&out_ch_layout, &in_ch_layout); + clean_downmix(&in_ch_layout, &out_ch_layout); if(!av_channel_layout_check(&in_ch_layout)) { av_log(log_context, AV_LOG_ERROR, "Input channel layout is invalid\n"); diff --git a/tests/fate/libswresample.mak b/tests/fate/libswresample.mak index ca915a3764..c98176a238 100644 --- a/tests/fate/libswresample.mak +++ b/tests/fate/libswresample.mak @@ -1154,6 +1154,14 @@ FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-unused-mono-output fate-swr-rematrix-unused-mono-output: libswresample/tests/rematrix$(EXESUF) fate-swr-rematrix-unused-mono-output: CMD = run libswresample/tests/rematrix$(EXESUF) stereo UNSD+FR +FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-downmix-input +fate-swr-rematrix-downmix-input: libswresample/tests/rematrix$(EXESUF) +fate-swr-rematrix-downmix-input: CMD = run libswresample/tests/rematrix$(EXESUF) DR+DL 5.1 + +FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-downmix-output +fate-swr-rematrix-downmix-output: libswresample/tests/rematrix$(EXESUF) +fate-swr-rematrix-downmix-output: CMD = run libswresample/tests/rematrix$(EXESUF) 5.1 DR+UNSD+DL + FATE_SWR += $(FATE_SWR_REMATRIX-yes) fate-swr-rematrix: $(FATE_SWR_REMATRIX-yes) diff --git a/tests/ref/fate/swr-rematrix-downmix-input b/tests/ref/fate/swr-rematrix-downmix-input new file mode 100644 index 0000000000..254405fb7a --- /dev/null +++ b/tests/ref/fate/swr-rematrix-downmix-input @@ -0,0 +1,6 @@ +[FL] = { .DR = 0.000000, .DL = 1.000000, }, +[FR] = { .DR = 1.000000, .DL = 0.000000, }, +[FC] = { .DR = 0.000000, .DL = 0.000000, }, +[LFE] = { .DR = 0.000000, .DL = 0.000000, }, +[BL] = { .DR = 0.000000, .DL = 0.000000, }, +[BR] = { .DR = 0.000000, .DL = 0.000000, }, diff --git a/tests/ref/fate/swr-rematrix-downmix-output b/tests/ref/fate/swr-rematrix-downmix-output new file mode 100644 index 0000000000..a96b879ca9 --- /dev/null +++ b/tests/ref/fate/swr-rematrix-downmix-output @@ -0,0 +1,3 @@ +[DR] = { .FL = 0.000000, .FR = 1.000000, .FC = 0.707107, .LFE = 0.000000, .BL = 0.000000, .BR = 0.707107, }, +[UNSD1] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[DL] = { .FL = 1.000000, .FR = 0.000000, .FC = 0.707107, .LFE = 0.000000, .BL = 0.707107, .BR = 0.000000, }, -- 2.52.0 From 3e62290e67bebecdc18afccb1f4aa1ccdc08dfd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sun, 27 Sep 2026 16:04:09 +0200 Subject: [PATCH 3/5] swresample/rematrix: allow layouts of SWR_CH_MAX channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Classic off by one check :) Signed-off-by: Kacper Michajłow <[email protected]> --- libswresample/rematrix.c | 2 +- tests/fate/libswresample.mak | 7 +++ tests/ref/fate/swr-rematrix-unused-64 | 64 +++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 tests/ref/fate/swr-rematrix-unused-64 diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c index 7a0ea88682..252a28603e 100644 --- a/libswresample/rematrix.c +++ b/libswresample/rematrix.c @@ -155,7 +155,7 @@ static void clean_downmix(AVChannelLayout *ch_layout, const AVChannelLayout *oth } static int sane_layout(const AVChannelLayout *ch_layout) { - if(ch_layout->nb_channels >= SWR_CH_MAX) + if(ch_layout->nb_channels > SWR_CH_MAX) return 0; if(ch_layout->order == AV_CHANNEL_ORDER_CUSTOM) for (int i = 0; i < ch_layout->nb_channels; i++) { diff --git a/tests/fate/libswresample.mak b/tests/fate/libswresample.mak index c98176a238..82500d1cec 100644 --- a/tests/fate/libswresample.mak +++ b/tests/fate/libswresample.mak @@ -1162,6 +1162,13 @@ FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-downmix-output fate-swr-rematrix-downmix-output: libswresample/tests/rematrix$(EXESUF) fate-swr-rematrix-downmix-output: CMD = run libswresample/tests/rematrix$(EXESUF) 5.1 DR+UNSD+DL +FATE_SWR_UNUSED_8 = UNSD+UNSD+UNSD+UNSD+UNSD+UNSD+UNSD+UNSD +FATE_SWR_UNUSED_31 = $(FATE_SWR_UNUSED_8)+$(FATE_SWR_UNUSED_8)+$(FATE_SWR_UNUSED_8)+UNSD+UNSD+UNSD+UNSD+UNSD+UNSD+UNSD + +FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-unused-64 +fate-swr-rematrix-unused-64: libswresample/tests/rematrix$(EXESUF) +fate-swr-rematrix-unused-64: CMD = run libswresample/tests/rematrix$(EXESUF) 5.1 $(FATE_SWR_UNUSED_31)+FL+FR+$(FATE_SWR_UNUSED_31) + FATE_SWR += $(FATE_SWR_REMATRIX-yes) fate-swr-rematrix: $(FATE_SWR_REMATRIX-yes) diff --git a/tests/ref/fate/swr-rematrix-unused-64 b/tests/ref/fate/swr-rematrix-unused-64 new file mode 100644 index 0000000000..242a611f38 --- /dev/null +++ b/tests/ref/fate/swr-rematrix-unused-64 @@ -0,0 +1,64 @@ +[UNSD0] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD1] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD2] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD3] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD4] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD5] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD6] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD7] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD8] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD9] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD10] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD11] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD12] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD13] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD14] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD15] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD16] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD17] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD18] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD19] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD20] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD21] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD22] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD23] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD24] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD25] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD26] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD27] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD28] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD29] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD30] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[FL] = { .FL = 1.000000, .FR = 0.000000, .FC = 0.707107, .LFE = 0.000000, .BL = 0.707107, .BR = 0.000000, }, +[FR] = { .FL = 0.000000, .FR = 1.000000, .FC = 0.707107, .LFE = 0.000000, .BL = 0.000000, .BR = 0.707107, }, +[UNSD33] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD34] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD35] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD36] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD37] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD38] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD39] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD40] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD41] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD42] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD43] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD44] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD45] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD46] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD47] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD48] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD49] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD50] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD51] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD52] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD53] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD54] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD55] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD56] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD57] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD58] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD59] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD60] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD61] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD62] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, +[UNSD63] = { .FL = 0.000000, .FR = 0.000000, .FC = 0.000000, .LFE = 0.000000, .BL = 0.000000, .BR = 0.000000, }, -- 2.52.0 From 057148b6005c39e3fe946603ec856edb7552cb1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sun, 27 Sep 2026 16:04:59 +0200 Subject: [PATCH 4/5] swresample/rematrix: don't check channels that are only reordered MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Layouts with the same channels in another order need a matrix, but nothing is mixed. The checks for channels that can be mixed rejected them anyway, for example BL+BR to BR+BL. Signed-off-by: Kacper Michajłow <[email protected]> --- libswresample/rematrix.c | 14 ++++++++++---- tests/fate/libswresample.mak | 8 ++++++++ tests/ref/fate/swr-rematrix-reorder | 2 ++ tests/ref/fate/swr-rematrix-reorder-unused | 3 +++ 4 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 tests/ref/fate/swr-rematrix-reorder create mode 100644 tests/ref/fate/swr-rematrix-reorder-unused diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c index 252a28603e..3bec10360e 100644 --- a/libswresample/rematrix.c +++ b/libswresample/rematrix.c @@ -154,7 +154,7 @@ static void clean_downmix(AVChannelLayout *ch_layout, const AVChannelLayout *oth } } -static int sane_layout(const AVChannelLayout *ch_layout) { +static int sane_layout(const AVChannelLayout *ch_layout, int mixed) { if(ch_layout->nb_channels > SWR_CH_MAX) return 0; if(ch_layout->order == AV_CHANNEL_ORDER_CUSTOM) @@ -168,6 +168,8 @@ static int sane_layout(const AVChannelLayout *ch_layout) { } else if (ch_layout->order != AV_CHANNEL_ORDER_NATIVE) return 0; + if (!mixed) + return 1; uint64_t mask = av_channel_layout_subset(ch_layout, ~(uint64_t)0); if(!(mask & AV_CH_LAYOUT_SURROUND)) // at least 1 front speaker return 0; @@ -618,7 +620,7 @@ av_cold int swr_build_matrix2(const AVChannelLayout *in_layout, const AVChannelL double rematrix_volume, double *matrix_param, ptrdiff_t stride, enum AVMatrixEncoding matrix_encoding, void *log_context) { - int i, j, ret; + int i, j, ret, mixed; AVChannelLayout in_ch_layout = { 0 }, out_ch_layout = { 0 }; char buf[128]; @@ -630,12 +632,16 @@ av_cold int swr_build_matrix2(const AVChannelLayout *in_layout, const AVChannelL clean_downmix(&out_ch_layout, &in_ch_layout); clean_downmix(&in_ch_layout, &out_ch_layout); + /* the same channels on both sides change their places at most */ + mixed = av_channel_layout_subset(&in_ch_layout, ~(uint64_t)0) != + av_channel_layout_subset(&out_ch_layout, ~(uint64_t)0); + if(!av_channel_layout_check(&in_ch_layout)) { av_log(log_context, AV_LOG_ERROR, "Input channel layout is invalid\n"); ret = AVERROR(EINVAL); goto fail; } - if(!sane_layout(&in_ch_layout)) { + if(!sane_layout(&in_ch_layout, mixed)) { av_channel_layout_describe(&in_ch_layout, buf, sizeof(buf)); av_log(log_context, AV_LOG_ERROR, "Input channel layout '%s' is not supported\n", buf); ret = AVERROR(EINVAL); @@ -647,7 +653,7 @@ av_cold int swr_build_matrix2(const AVChannelLayout *in_layout, const AVChannelL ret = AVERROR(EINVAL); goto fail; } - if(!sane_layout(&out_ch_layout)) { + if(!sane_layout(&out_ch_layout, mixed)) { av_channel_layout_describe(&out_ch_layout, buf, sizeof(buf)); av_log(log_context, AV_LOG_ERROR, "Output channel layout '%s' is not supported\n", buf); ret = AVERROR(EINVAL); diff --git a/tests/fate/libswresample.mak b/tests/fate/libswresample.mak index 82500d1cec..335750b552 100644 --- a/tests/fate/libswresample.mak +++ b/tests/fate/libswresample.mak @@ -1169,6 +1169,14 @@ FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-unused-64 fate-swr-rematrix-unused-64: libswresample/tests/rematrix$(EXESUF) fate-swr-rematrix-unused-64: CMD = run libswresample/tests/rematrix$(EXESUF) 5.1 $(FATE_SWR_UNUSED_31)+FL+FR+$(FATE_SWR_UNUSED_31) +FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-reorder +fate-swr-rematrix-reorder: libswresample/tests/rematrix$(EXESUF) +fate-swr-rematrix-reorder: CMD = run libswresample/tests/rematrix$(EXESUF) BL+BR BR+BL + +FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-reorder-unused +fate-swr-rematrix-reorder-unused: libswresample/tests/rematrix$(EXESUF) +fate-swr-rematrix-reorder-unused: CMD = run libswresample/tests/rematrix$(EXESUF) FC+UNSD+FL FL+FC+UNSD + FATE_SWR += $(FATE_SWR_REMATRIX-yes) fate-swr-rematrix: $(FATE_SWR_REMATRIX-yes) diff --git a/tests/ref/fate/swr-rematrix-reorder b/tests/ref/fate/swr-rematrix-reorder new file mode 100644 index 0000000000..b80b9f3c05 --- /dev/null +++ b/tests/ref/fate/swr-rematrix-reorder @@ -0,0 +1,2 @@ +[BR] = { .BL = 0.000000, .BR = 1.000000, }, +[BL] = { .BL = 1.000000, .BR = 0.000000, }, diff --git a/tests/ref/fate/swr-rematrix-reorder-unused b/tests/ref/fate/swr-rematrix-reorder-unused new file mode 100644 index 0000000000..2db5a874a8 --- /dev/null +++ b/tests/ref/fate/swr-rematrix-reorder-unused @@ -0,0 +1,3 @@ +[FL] = { .FC = 0.000000, .UNSD1 = 0.000000, .FL = 1.000000, }, +[FC] = { .FC = 1.000000, .UNSD1 = 0.000000, .FL = 0.000000, }, +[UNSD2] = { .FC = 0.000000, .UNSD1 = 0.000000, .FL = 0.000000, }, -- 2.52.0 From 87c02d2ed062870013197e732dfd9e78b93ae00f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sun, 27 Sep 2026 16:05:52 +0200 Subject: [PATCH 5/5] swresample: keep integer samples if channels are only copied MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With a matrix, samples of more than 16 bits are mixed in floating point, which takes the lowest bits of s32. A matrix that only copies channels computes nothing, so select the internal format like without a matrix. Layouts that differ in the order of their channels or in unused ones get such a matrix. The matrix is built before the format is selected for this. The limit for its coefficients no longer looks at the selected format. For mixing that is an integer format only if the output has one or if it was set, which is what the limit looks at now. s64 still goes through double, the matrix has no functions for it. Bump the micro version, as this and the previous commits change which custom layouts can be used. Signed-off-by: Kacper Michajłow <[email protected]> --- libswresample/rematrix.c | 36 +++++++++++++++++++++++++---- libswresample/swresample.c | 34 ++++++++++++++++----------- libswresample/swresample_internal.h | 2 ++ libswresample/version.h | 2 +- tests/fate/libswresample.mak | 8 +++++++ tests/filtergraphs/custom_reorder | 1 + 6 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 tests/filtergraphs/custom_reorder diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c index 3bec10360e..31b1078bcc 100644 --- a/libswresample/rematrix.c +++ b/libswresample/rematrix.c @@ -697,7 +697,8 @@ av_cold static int auto_matrix(SwrContext *s) if (s->rematrix_maxval > 0) { maxval = s->rematrix_maxval; } else if ( av_get_packed_sample_fmt(s->out_sample_fmt) < AV_SAMPLE_FMT_FLT - || av_get_packed_sample_fmt(s->int_sample_fmt) < AV_SAMPLE_FMT_FLT) { + || (s->user_int_sample_fmt != AV_SAMPLE_FMT_NONE && + av_get_packed_sample_fmt(s->user_int_sample_fmt) < AV_SAMPLE_FMT_FLT)) { maxval = 1.0; } else maxval = INT_MAX; @@ -709,13 +710,12 @@ av_cold static int auto_matrix(SwrContext *s) s->matrix[1] - s->matrix[0], s->matrix_encoding, s); } -av_cold int swri_rematrix_init(SwrContext *s){ +av_cold int swri_rematrix_build(SwrContext *s) +{ int i, j; int nb_in = s->used_ch_layout.nb_channels; int nb_out = s->out.ch_count; - s->mix_any_f = NULL; - if (!s->rematrix_custom) { int r = auto_matrix(s); if (r) @@ -735,6 +735,34 @@ av_cold int swri_rematrix_init(SwrContext *s){ av_log(s, AV_LOG_DEBUG, "\n"); } } + + /* a channel that is one channel of the input, or silent, is not mixed */ + for (i = 0; i < nb_out; i++) { + int sources = 0; + for (j = 0; j < nb_in; j++) { + if (s->matrix[i][j] == 0.0) + continue; + if (s->matrix[i][j] != 1.0 || sources++) + return 1; + } + } + + return 0; +} + +av_cold int swri_rematrix_init(SwrContext *s){ + int i, j; + int nb_in = s->used_ch_layout.nb_channels; + int nb_out = s->out.ch_count; + + s->mix_any_f = NULL; + + /* a matrix that is applied was built by swri_rematrix_build() */ + if (!s->rematrix) { + int r = auto_matrix(s); + if (r) + return r; + } if (s->midbuf.fmt == AV_SAMPLE_FMT_S16P){ int maxsum = 0; s->native_matrix = av_calloc(nb_in * nb_out, sizeof(int)); diff --git a/libswresample/swresample.c b/libswresample/swresample.c index 3fa2f3bf6c..cd84c731d9 100644 --- a/libswresample/swresample.c +++ b/libswresample/swresample.c @@ -154,7 +154,7 @@ av_cold void swr_close(SwrContext *s){ } av_cold int swr_init(struct SwrContext *s){ - int ret; + int ret, mixing; char l1[1024], l2[1024]; clear_context(s); @@ -232,6 +232,24 @@ av_cold int swr_init(struct SwrContext *s){ s->rematrix_volume!=1.0 || s->rematrix_custom; + av_channel_layout_describe(&s->out_ch_layout, l2, sizeof(l2)); + av_channel_layout_describe(&s->in_ch_layout, l1, sizeof(l1)); + if (( s->out_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC + || s-> in_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) && s->used_ch_layout.nb_channels != s->out.ch_count && !s->rematrix_custom) { + av_log(s, AV_LOG_ERROR, "Rematrix is needed between %s and %s " + "but there is not enough information to do it\n", l1, l2); + return AVERROR(EINVAL); + } + + /* a matrix that only copies channels leaves the samples as they are, + * so the format in between is selected like without a matrix */ + mixing = 0; + if (s->rematrix) { + mixing = swri_rematrix_build(s); + if (mixing < 0) + return mixing; + } + if(s->int_sample_fmt == AV_SAMPLE_FMT_NONE){ // DSD to PCM conversion is done in floating point if( s->in_sample_fmt == AV_SAMPLE_FMT_DSD @@ -247,13 +265,13 @@ av_cold int swr_init(struct SwrContext *s){ +av_get_bytes_per_sample(s->out_sample_fmt) <= 3 ) { s->int_sample_fmt= AV_SAMPLE_FMT_S16P; }else if( av_get_bytes_per_sample(s-> in_sample_fmt) <= 2 - && !s->rematrix + && !mixing && s->out_sample_rate==s->in_sample_rate && !(s->flags & SWR_FLAG_RESAMPLE)){ s->int_sample_fmt= AV_SAMPLE_FMT_S16P; }else if( av_get_planar_sample_fmt(s-> in_sample_fmt) == AV_SAMPLE_FMT_S32P && av_get_planar_sample_fmt(s->out_sample_fmt) == AV_SAMPLE_FMT_S32P - && !s->rematrix + && !mixing && s->out_sample_rate == s->in_sample_rate && !(s->flags & SWR_FLAG_RESAMPLE) && s->engine != SWR_ENGINE_SOXR){ @@ -328,22 +346,12 @@ av_cold int swr_init(struct SwrContext *s){ goto fail; } - av_channel_layout_describe(&s->out_ch_layout, l2, sizeof(l2)); - av_channel_layout_describe(&s->in_ch_layout, l1, sizeof(l1)); if (s->in_ch_layout.order != AV_CHANNEL_ORDER_UNSPEC && s->used_ch_layout.nb_channels != s->in_ch_layout.nb_channels) { av_log(s, AV_LOG_ERROR, "Input channel layout %s mismatches specified channel count %d\n", l1, s->used_ch_layout.nb_channels); ret = AVERROR(EINVAL); goto fail; } - if (( s->out_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC - || s-> in_ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) && s->used_ch_layout.nb_channels != s->out.ch_count && !s->rematrix_custom) { - av_log(s, AV_LOG_ERROR, "Rematrix is needed between %s and %s " - "but there is not enough information to do it\n", l1, l2); - ret = AVERROR(EINVAL); - goto fail; - } - av_assert0(s->used_ch_layout.nb_channels); av_assert0(s->out.ch_count); s->resample_first= RSC*s->out.ch_count/s->used_ch_layout.nb_channels - RSC < s->out_sample_rate/(float)s-> in_sample_rate - 1.0; diff --git a/libswresample/swresample_internal.h b/libswresample/swresample_internal.h index 0cde2760bb..dcfb5335e7 100644 --- a/libswresample/swresample_internal.h +++ b/libswresample/swresample_internal.h @@ -207,6 +207,8 @@ void swri_noise_shaping_int32 (SwrContext *s, AudioData *dsts, const AudioData * void swri_noise_shaping_float (SwrContext *s, AudioData *dsts, const AudioData *srcs, const AudioData *noises, int count); void swri_noise_shaping_double(SwrContext *s, AudioData *dsts, const AudioData *srcs, const AudioData *noises, int count); +av_warn_unused_result +int swri_rematrix_build(SwrContext *s); av_warn_unused_result int swri_rematrix_init(SwrContext *s); void swri_rematrix_free(SwrContext *s); diff --git a/libswresample/version.h b/libswresample/version.h index d1795b5545..403be6227a 100644 --- a/libswresample/version.h +++ b/libswresample/version.h @@ -31,7 +31,7 @@ #include "version_major.h" #define LIBSWRESAMPLE_VERSION_MINOR 3 -#define LIBSWRESAMPLE_VERSION_MICRO 100 +#define LIBSWRESAMPLE_VERSION_MICRO 101 #define LIBSWRESAMPLE_VERSION_INT AV_VERSION_INT(LIBSWRESAMPLE_VERSION_MAJOR, \ LIBSWRESAMPLE_VERSION_MINOR, \ diff --git a/tests/fate/libswresample.mak b/tests/fate/libswresample.mak index 335750b552..f09f06b5d3 100644 --- a/tests/fate/libswresample.mak +++ b/tests/fate/libswresample.mak @@ -1106,6 +1106,14 @@ fate-swr-custom-rematrix: REF = 2a14a44deb4ae26e3b474ddbfbc048f8 FATE_SWR += $(FATE_SWR_CUSTOM_REMATRIX-yes) +FATE_SWR_CUSTOM_REORDER-$(call FILTERDEMDECENCMUX, ARESAMPLE AFORMAT, PCM_S32LE, PCM_S32LE, PCM_S32LE, PCM_S32LE) += fate-swr-custom-reorder +fate-swr-custom-reorder: tests/data/asynth-44100-8.wav tests/data/filtergraphs/custom_reorder +fate-swr-custom-reorder: CMD = md5 -f s32le -ar 44100 -ch_layout 7.1 -i $(TARGET_PATH)/tests/data/asynth-44100-8.wav -/filter_complex $(TARGET_PATH)/tests/data/filtergraphs/custom_reorder -map [OUT] -f s32le +fate-swr-custom-reorder: CMP = oneline +fate-swr-custom-reorder: REF = e9518a955a68bde2945dfd1994675101 + +FATE_SWR += $(FATE_SWR_CUSTOM_REORDER-yes) + FATE_SWR_22_2_LAYOUTS = 9.1.6 9.1.4 7.2.3 7.1.4 7.1.2 5.1.4 5.1.2 7.1 5.1 stereo mono FATE_SWR_9_1_6_LAYOUTS = 9.1.4 7.2.3 7.1.4 7.1.2 5.1.4 5.1.2 7.1 5.1 stereo mono FATE_SWR_9_1_4_LAYOUTS = 7.2.3 7.1.4 7.1.2 5.1.4 5.1.2 7.1 5.1 stereo mono diff --git a/tests/filtergraphs/custom_reorder b/tests/filtergraphs/custom_reorder new file mode 100644 index 0000000000..8f8a6b92cf --- /dev/null +++ b/tests/filtergraphs/custom_reorder @@ -0,0 +1 @@ +[0:a]aresample,aformat=f=s32:cl=FR+FL+FC+LFE+SL+SR+BL+BR[OUT]; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
