[PR] swresample/rematrix: accept AV_CHAN_UNUSED input channels (PR #24133)

AYOUB NABIL BOUBAGRAT via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178664750740.59.14434505218910224845@29965ddac10e>
PR #24133 opened by AYOUB NABIL BOUBAGRAT (ayoubnabil)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24133
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24133.patch

custom input layouts containing AV_CHAN_UNUSED were rejected by
swr_build_matrix2(), so streams with unused input positions could not
be rematrixed.

allow AV_CHAN_UNUSED in custom input layouts, preserve its physical
matrix position, and clear the corresponding matrix column. output
layouts still reject unused channels.

add a fate test for a FL+FR+UNSD+UNSD to stereo matrix. 
this fixes #24094



>From 15eafdf713823fe652c3facca2db4a0f3f702353 Mon Sep 17 00:00:00 2001
From: Ayoub Nabil Boubagrat <[email protected]>
Date: Thu, 13 Aug 2026 20:23:07 +0200
Subject: [PATCH 1/2] libswresample/tests/rematrix: uninit channel layouts

release parsed channel layouts before exiting.

Signed-off-by: Ayoub Nabil Boubagrat <[email protected]>
---
 libswresample/tests/rematrix.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/libswresample/tests/rematrix.c b/libswresample/tests/rematrix.c
index eaa78784c6..464da7638b 100644
--- a/libswresample/tests/rematrix.c
+++ b/libswresample/tests/rematrix.c
@@ -82,23 +82,30 @@ int main(int argc, char **argv)
     if (ret < 0) {
         if (ret == AVERROR(EINVAL))
             fprintf(stderr, "Invalid input layout %s\n", in);
-        return 1;
+        ret = 1;
+        goto end;
     }
 
     ret = av_channel_layout_from_string(&out_layout, out);
     if (ret < 0) {
         if (ret == AVERROR(EINVAL))
             fprintf(stderr, "Invalid output layout %s\n", out);
-        return 1;
+        ret = 1;
+        goto end;
     }
 
     if (in_layout.nb_channels > MATRIX_STRIDE ||
         out_layout.nb_channels > MATRIX_STRIDE) {
         fprintf(stderr, "channel layout exceeds matrix capacity\n");
-        return 1;
+        ret = 1;
+        goto end;
     }
 
     ret = print_matrix(&in_layout, &out_layout);
 
+end:
+    av_channel_layout_uninit(&in_layout);
+    av_channel_layout_uninit(&out_layout);
+
     return ret;
 }
-- 
2.52.0


>From a5261e4a1a7fb45a7e9f0b73126faf8fc651372a Mon Sep 17 00:00:00 2001
From: Ayoub Nabil Boubagrat <[email protected]>
Date: Thu, 13 Aug 2026 20:23:19 +0200
Subject: [PATCH 2/2] swresample/rematrix: accept unused input channels

ignore AV_CHAN_UNUSED input channels while preserving their physical
matrix positions. add a fate test covering #24094.

Signed-off-by: Ayoub Nabil Boubagrat <[email protected]>
---
 libswresample/rematrix.c           | 23 +++++++++++++++++++----
 libswresample/swresample.h         |  3 +++
 libswresample/tests/rematrix.c     | 26 ++++++++++++++++++++++++++
 tests/fate/libswresample.mak       |  4 ++++
 tests/ref/fate/swr-rematrix-unused |  2 ++
 5 files changed, 54 insertions(+), 4 deletions(-)
 create mode 100644 tests/ref/fate/swr-rematrix-unused

diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c
index 67d9b48339..e3f91aede2 100644
--- a/libswresample/rematrix.c
+++ b/libswresample/rematrix.c
@@ -111,12 +111,16 @@ static int clean_layout(AVChannelLayout *out, const AVChannelLayout *in, void *s
     return ret;
 }
 
-static int sane_layout(AVChannelLayout *ch_layout) {
+static int sane_layout(const AVChannelLayout *ch_layout, int allow_unused) {
     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++) {
-            if (ch_layout->u.map[i].id >= 64)
+            enum AVChannel id = ch_layout->u.map[i].id;
+
+            if (id == AV_CHAN_UNUSED && allow_unused)
+                continue;
+            if (id >= 64)
                 return 0;
         }
     else if (ch_layout->order != AV_CHANNEL_ORDER_NATIVE)
@@ -156,6 +160,17 @@ static void build_matrix(const AVChannelLayout *in_ch_layout, const AVChannelLay
     double maxcoef=0;
     int i, j;
 
+    if (in_ch_layout->order == AV_CHANNEL_ORDER_CUSTOM) {
+        for (j = 0; j < in_ch_layout->nb_channels; j++) {
+            if (in_ch_layout->u.map[j].id == AV_CHAN_UNUSED) {
+                /* the named-channel loop below cannot visit AV_CHAN_UNUSED.
+                 * explicitly clear its column so callers may reuse a matrix. */
+                for (i = 0; i < out_ch_layout->nb_channels; i++)
+                    matrix_param[stride * i + j] = 0.0;
+            }
+        }
+    }
+
     for(i=0; i<FF_ARRAY_ELEMS(matrix); i++){
         if (in_mask & out_mask & (1ULL << i))
             matrix[i][i]= 1.0;
@@ -576,7 +591,7 @@ av_cold int swr_build_matrix2(const AVChannelLayout *in_layout, const AVChannelL
         ret = AVERROR(EINVAL);
         goto fail;
     }
-    if(!sane_layout(&in_ch_layout)) {
+    if(!sane_layout(&in_ch_layout, 1)) {
         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);
@@ -588,7 +603,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, 0)) {
         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/libswresample/swresample.h b/libswresample/swresample.h
index 052089acca..9b70719b48 100644
--- a/libswresample/swresample.h
+++ b/libswresample/swresample.h
@@ -377,6 +377,9 @@ int swr_set_channel_mapping(struct SwrContext *s, const int *channel_map);
  * default mixing matrix. It is made public just as a utility function for
  * building custom matrices.
  *
+ * AV_CHAN_UNUSED entries in custom input layouts are ignored and their matrix
+ * columns are set to zero.
+ *
  * @param in_layout           input channel layout
  * @param out_layout          output channel layout
  * @param center_mix_level    mix level for the center channel
diff --git a/libswresample/tests/rematrix.c b/libswresample/tests/rematrix.c
index 464da7638b..c8d3f285d4 100644
--- a/libswresample/tests/rematrix.c
+++ b/libswresample/tests/rematrix.c
@@ -28,6 +28,11 @@
 /* swr_build_matrix2() accesses an internal SWR_CH_MAX by SWR_CH_MAX matrix. */
 #define MATRIX_STRIDE 64
 
+static int channel_is_unused(const AVChannelLayout *layout, int index)
+{
+    return av_channel_layout_channel_from_index(layout, index) == AV_CHAN_UNUSED;
+}
+
 static int print_matrix(const AVChannelLayout *in_layout,
                         const AVChannelLayout *out_layout)
 {
@@ -35,6 +40,12 @@ static int print_matrix(const AVChannelLayout *in_layout,
     char in_name[16], out_name[16];
     int ret;
 
+    /* ensure swr_build_matrix2() overwrites unused input columns with zero. */
+    for (int out = 0; out < out_layout->nb_channels; out++)
+        for (int in = 0; in < in_layout->nb_channels; in++)
+            if (channel_is_unused(in_layout, in))
+                matrix[out * MATRIX_STRIDE + in] = 1.0;
+
     /* Disable normalization so the raw downmix gains can be checked. */
     ret = swr_build_matrix2(in_layout, out_layout, M_SQRT1_2,
                             M_SQRT1_2,
@@ -45,6 +56,16 @@ static int print_matrix(const AVChannelLayout *in_layout,
         return 1;
     }
 
+    for (int out = 0; out < out_layout->nb_channels; out++) {
+        for (int in = 0; in < in_layout->nb_channels; in++) {
+            if (channel_is_unused(in_layout, in) &&
+                matrix[out * MATRIX_STRIDE + in] != 0.0) {
+                fprintf(stderr, "unused input %d has a non-zero coefficient\n", in);
+                return 1;
+            }
+        }
+    }
+
     for (int i = 0; i < 64; i++) {
         int out_i = av_channel_layout_index_from_channel(out_layout, i);
         if (out_i < 0)
@@ -58,6 +79,11 @@ static int print_matrix(const AVChannelLayout *in_layout,
             av_channel_name(in_name, sizeof(in_name), j);
             printf(".%s = %f, ", in_name, matrix[out_i * MATRIX_STRIDE + in_i]);
         }
+        for (int in_i = 0; in_i < in_layout->nb_channels; in_i++) {
+            if (channel_is_unused(in_layout, in_i))
+                printf(".UNSD%d = %f, ", in_i,
+                       matrix[out_i * MATRIX_STRIDE + in_i]);
+        }
         printf("},\n");
     }
 
diff --git a/tests/fate/libswresample.mak b/tests/fate/libswresample.mak
index 7959aa87cd..315c05b5be 100644
--- a/tests/fate/libswresample.mak
+++ b/tests/fate/libswresample.mak
@@ -1138,6 +1138,10 @@ $(call SWR_REMATRIX_TEST,$(FATE_SWR_7_1_LAYOUTS),7.1)
 $(call SWR_REMATRIX_TEST,$(FATE_SWR_5_1_LAYOUTS),5.1)
 $(call SWR_REMATRIX_TEST,$(FATE_SWR_STEREO_LAYOUTS),stereo)
 
+FATE_SWR_REMATRIX-$(CONFIG_SWRESAMPLE) += fate-swr-rematrix-unused
+fate-swr-rematrix-unused: libswresample/tests/rematrix$(EXESUF)
+fate-swr-rematrix-unused: CMD = run libswresample/tests/rematrix$(EXESUF) FL+FR+UNSD+UNSD stereo
+
 FATE_SWR += $(FATE_SWR_REMATRIX-yes)
 fate-swr-rematrix: $(FATE_SWR_REMATRIX-yes)
 
diff --git a/tests/ref/fate/swr-rematrix-unused b/tests/ref/fate/swr-rematrix-unused
new file mode 100644
index 0000000000..605caf3b70
--- /dev/null
+++ b/tests/ref/fate/swr-rematrix-unused
@@ -0,0 +1,2 @@
+[FL] = { .FL = 1.000000, .FR = 0.000000, .UNSD2 = 0.000000, .UNSD3 = 0.000000, },
+[FR] = { .FL = 0.000000, .FR = 1.000000, .UNSD2 = 0.000000, .UNSD3 = 0.000000, },
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.