[PR] swresample/rematrix: handle missing channels from 22.2 layout (PR #24038)

James Almer via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178607284338.59.9363911312752315947@29965ddac10e>
PR #24038 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24038
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24038.patch


>From 9a41feea6e33a77f6aa1e80467a485e17c60d3af Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Thu, 6 Aug 2026 22:57:27 -0300
Subject: [PATCH] swresample/rematrix: handle missing channels from 22.2 layout

Signed-off-by: James Almer <[email protected]>
---
 libswresample/rematrix.c            | 137 ++++++++++++++++++++++++++--
 libswresample/swresample_internal.h |   2 +
 libswresample/tests/rematrix.c      |  31 +++++++
 3 files changed, 160 insertions(+), 10 deletions(-)

diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c
index 269849a427..c92d2f4523 100644
--- a/libswresample/rematrix.c
+++ b/libswresample/rematrix.c
@@ -60,7 +60,13 @@
 #define TOP_BACK_LEFT          15
 #define TOP_BACK_CENTER        16
 #define TOP_BACK_RIGHT         17
-#define NUM_NAMED_CHANNELS     18
+#define LOW_FREQUENCY_2        35
+#define TOP_SIDE_LEFT          36
+#define TOP_SIDE_RIGHT         37
+#define BOTTOM_FRONT_CENTER    38
+#define BOTTOM_FRONT_LEFT      39
+#define BOTTOM_FRONT_RIGHT     40
+#define NUM_NAMED_CHANNELS     41
 
 int swr_set_matrix(struct SwrContext *s, const double *matrix, int stride)
 {
@@ -129,6 +135,10 @@ static int sane_layout(AVChannelLayout *ch_layout) {
         return 0;
     if(!even(av_channel_layout_subset(ch_layout, (AV_CH_TOP_BACK_LEFT | AV_CH_TOP_BACK_RIGHT))))
         return 0;
+    if(!even(av_channel_layout_subset(ch_layout, (AV_CH_TOP_SIDE_LEFT | AV_CH_TOP_SIDE_RIGHT))))
+        return 0;
+    if(!even(av_channel_layout_subset(ch_layout, (AV_CH_BOTTOM_FRONT_LEFT | AV_CH_BOTTOM_FRONT_RIGHT))))
+        return 0;
 
     return 1;
 }
@@ -299,6 +309,19 @@ static void build_matrix(const AVChannelLayout *in_ch_layout, const AVChannelLay
             av_assert0(0);
     }
 
+    if (unaccounted & AV_CH_TOP_FRONT_CENTER) {
+        if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_TOP_FRONT_LEFT) >= 0) {
+            matrix[TOP_FRONT_LEFT ][TOP_FRONT_CENTER]+= M_SQRT1_2;
+            matrix[TOP_FRONT_RIGHT][TOP_FRONT_CENTER]+= M_SQRT1_2;
+	    } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) {
+	        matrix[FRONT_CENTER][TOP_FRONT_CENTER] += 1.0;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) {
+	        matrix[FRONT_LEFT ][TOP_FRONT_CENTER] += center_mix_level;
+	        matrix[FRONT_RIGHT][TOP_FRONT_CENTER] += center_mix_level;
+        } else
+            av_assert0(0);
+    }
+
     if (unaccounted & AV_CH_TOP_BACK_LEFT) {
         if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_TOP_BACK_CENTER) >= 0) {
             matrix[TOP_BACK_CENTER][TOP_BACK_LEFT ] += M_SQRT1_2;
@@ -343,6 +366,96 @@ static void build_matrix(const AVChannelLayout *in_ch_layout, const AVChannelLay
             av_assert0(0);
     }
 
+
+    if (unaccounted & AV_CH_TOP_SIDE_LEFT) {
+        if (av_channel_layout_subset(out_ch_layout, (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_CENTER)) ==
+                                                    (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_CENTER)) {
+            matrix[TOP_FRONT_LEFT ][TOP_SIDE_LEFT ] += SQRT2_3;
+            matrix[TOP_FRONT_RIGHT][TOP_SIDE_RIGHT] += SQRT2_3;
+            matrix[TOP_BACK_CENTER][TOP_SIDE_LEFT ] += SQRT1_3;
+            matrix[TOP_BACK_CENTER][TOP_SIDE_RIGHT] += SQRT1_3;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_TOP_BACK_LEFT) >= 0) {
+            matrix[TOP_BACK_LEFT ][TOP_SIDE_LEFT ] += M_SQRT1_2;
+            matrix[TOP_BACK_RIGHT][TOP_SIDE_RIGHT] += M_SQRT1_2;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_TOP_FRONT_LEFT) >= 0) {
+            matrix[TOP_FRONT_LEFT ][TOP_SIDE_LEFT ] += M_SQRT1_2;
+            matrix[TOP_FRONT_RIGHT][TOP_SIDE_RIGHT] += M_SQRT1_2;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_SIDE_LEFT) >= 0) {
+            matrix[SIDE_LEFT ][TOP_SIDE_LEFT ] += 1.0;
+            matrix[SIDE_RIGHT][TOP_SIDE_RIGHT] += 1.0;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_BACK_LEFT) >= 0) {
+            matrix[BACK_LEFT ][TOP_SIDE_LEFT ] += 1.0;
+            matrix[BACK_RIGHT][TOP_SIDE_RIGHT] += 1.0;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) {
+            matrix[FRONT_LEFT ][TOP_SIDE_LEFT ] += surround_mix_level;
+            matrix[FRONT_RIGHT][TOP_SIDE_RIGHT] += surround_mix_level;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) {
+            matrix[FRONT_CENTER][TOP_SIDE_LEFT ] += surround_mix_level*M_SQRT1_2;
+            matrix[FRONT_CENTER][TOP_SIDE_RIGHT] += surround_mix_level*M_SQRT1_2;
+        } else
+            av_assert0(0);
+    }
+
+    if (unaccounted & AV_CH_TOP_CENTER) {
+        if (av_channel_layout_subset(out_ch_layout, (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_LEFT)) ==
+                                                    (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_LEFT)) {
+            matrix[TOP_FRONT_LEFT ][TOP_CENTER] += 0.5;
+            matrix[TOP_FRONT_RIGHT][TOP_CENTER] += 0.5;
+            matrix[TOP_BACK_LEFT ][TOP_CENTER] += 0.5;
+            matrix[TOP_BACK_RIGHT][TOP_CENTER] += 0.5;
+        } else if (av_channel_layout_subset(out_ch_layout, (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_CENTER)) ==
+                                                           (AV_CH_TOP_FRONT_LEFT|AV_CH_TOP_BACK_CENTER)) {
+            matrix[TOP_FRONT_LEFT ][TOP_CENTER] += SQRT1_3;
+            matrix[TOP_FRONT_RIGHT][TOP_CENTER] += SQRT1_3;
+            matrix[TOP_BACK_CENTER][TOP_CENTER] += SQRT1_3;
+        } else if (av_channel_layout_subset(out_ch_layout, (AV_CH_TOP_FRONT_LEFT|AV_CH_BACK_LEFT)) ==
+                                                           (AV_CH_TOP_FRONT_LEFT|AV_CH_BACK_LEFT)) {
+            matrix[TOP_FRONT_LEFT ][TOP_CENTER] += 0.5;
+            matrix[TOP_FRONT_RIGHT][TOP_CENTER] += 0.5;
+            matrix[BACK_LEFT ][TOP_CENTER] += 0.5;
+            matrix[BACK_RIGHT][TOP_CENTER] += 0.5;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) {
+            matrix[FRONT_LEFT ][TOP_CENTER] += 0.5;
+            matrix[FRONT_RIGHT][TOP_CENTER] += 0.5;
+            if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_BACK_LEFT) >= 0) {
+                matrix[BACK_LEFT ][TOP_CENTER] += 0.5;
+                matrix[BACK_RIGHT][TOP_CENTER] += 0.5;
+            } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_SIDE_LEFT) >= 0) {
+                matrix[SIDE_LEFT ][TOP_CENTER] += 0.5;
+                matrix[SIDE_RIGHT][TOP_CENTER] += 0.5;
+            }
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) {
+            matrix[FRONT_CENTER][TOP_CENTER] += 0.5;
+        } else
+            av_assert0(0);
+    }
+
+    if (unaccounted & AV_CH_BOTTOM_FRONT_CENTER) {
+        if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) {
+            matrix[FRONT_CENTER][BOTTOM_FRONT_CENTER] += 1.0;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) {
+            matrix[FRONT_LEFT ][BOTTOM_FRONT_CENTER] += center_mix_level;
+            matrix[FRONT_RIGHT][BOTTOM_FRONT_CENTER] += center_mix_level;
+        } else
+            av_assert0(0);
+    }
+
+    if (unaccounted & AV_CH_BOTTOM_FRONT_LEFT) {
+        if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_BOTTOM_FRONT_CENTER) >= 0) {
+            matrix[BOTTOM_FRONT_CENTER][BOTTOM_FRONT_LEFT ] += M_SQRT1_2;
+            matrix[BOTTOM_FRONT_CENTER][BOTTOM_FRONT_RIGHT] += M_SQRT1_2;
+            if (av_channel_layout_index_from_channel(in_ch_layout, AV_CHAN_BOTTOM_FRONT_CENTER) >= 0)
+                matrix[BOTTOM_FRONT_CENTER][BOTTOM_FRONT_CENTER] = center_mix_level * sqrt(2);
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) {
+            matrix[FRONT_LEFT ][BOTTOM_FRONT_LEFT ] += 1.0;
+            matrix[FRONT_RIGHT][BOTTOM_FRONT_RIGHT] += 1.0;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) {
+            matrix[FRONT_CENTER][BOTTOM_FRONT_LEFT ] += 1.0;
+            matrix[FRONT_CENTER][BOTTOM_FRONT_RIGHT] += 1.0;
+        } else
+            av_assert0(0);
+    }
+
     /* mix LFE into front left/right or center */
     if (unaccounted & AV_CH_LOW_FREQUENCY) {
         if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) {
@@ -354,6 +467,19 @@ static void build_matrix(const AVChannelLayout *in_ch_layout, const AVChannelLay
             av_assert0(0);
     }
 
+    /* mix LFE2 into LFE, front left/right or center */
+    if (unaccounted & AV_CH_LOW_FREQUENCY_2) {
+        if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_LOW_FREQUENCY) >= 0) {
+            matrix[LOW_FREQUENCY][LOW_FREQUENCY_2] += M_SQRT1_2;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_CENTER) >= 0) {
+            matrix[FRONT_CENTER][LOW_FREQUENCY_2] += lfe_mix_level;
+        } else if (av_channel_layout_index_from_channel(out_ch_layout, AV_CHAN_FRONT_LEFT) >= 0) {
+            matrix[FRONT_LEFT ][LOW_FREQUENCY_2] += lfe_mix_level * M_SQRT1_2;
+            matrix[FRONT_RIGHT][LOW_FREQUENCY_2] += lfe_mix_level * M_SQRT1_2;
+        } else
+            av_assert0(0);
+    }
+
 
     for (i = 0; i < 64; i++) {
         double sum=0;
@@ -413,15 +539,6 @@ av_cold int swr_build_matrix2(const AVChannelLayout *in_layout, const AVChannelL
         av_channel_layout_uninit(&in_ch_layout);
         in_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO;
     }
-    if (!av_channel_layout_compare(&in_ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_22POINT2) &&
-        av_channel_layout_compare(&out_ch_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_22POINT2)) {
-        av_channel_layout_from_mask(&in_ch_layout, (AV_CH_LAYOUT_7POINT1_WIDE_BACK|AV_CH_BACK_CENTER));
-        av_channel_layout_describe(&in_ch_layout, buf, sizeof(buf));
-        av_log(log_context, AV_LOG_WARNING,
-               "Full-on remixing from 22.2 has not yet been implemented! "
-               "Processing the input as '%s'\n",
-               buf);
-    }
 
     if(!av_channel_layout_check(&in_ch_layout)) {
         av_log(log_context, AV_LOG_ERROR, "Input channel layout is invalid\n");
diff --git a/libswresample/swresample_internal.h b/libswresample/swresample_internal.h
index ca2e0d7534..0cde2760bb 100644
--- a/libswresample/swresample_internal.h
+++ b/libswresample/swresample_internal.h
@@ -27,6 +27,8 @@
 
 #define SWR_CH_MAX 64
 
+#define SQRT1_3      0.57735026918962576451  /* sqrt(1/3) */
+#define SQRT2_3      0.81649658092772603273  /* sqrt(2/3) */
 #define SQRT3_2      1.22474487139158904909  /* sqrt(3/2) */
 
 #define NS_TAPS 20
diff --git a/libswresample/tests/rematrix.c b/libswresample/tests/rematrix.c
index 4caf6d9678..4691823b82 100644
--- a/libswresample/tests/rematrix.c
+++ b/libswresample/tests/rematrix.c
@@ -97,12 +97,19 @@ int main(void)
     const AVChannelLayout surround_2    = AV_CHANNEL_LAYOUT_5POINT1POINT2;
     const AVChannelLayout surround_4    = AV_CHANNEL_LAYOUT_5POINT1POINT4_BACK;
     const AVChannelLayout surround_tbc  = AV_CHANNEL_LAYOUT_7POINT2POINT3;
+    const AVChannelLayout surround_22_2 = AV_CHANNEL_LAYOUT_22POINT2;
     int ret = 0;
 
     ret |= check_coefficient(&surround_2, &stereo,
                              AV_CHAN_TOP_FRONT_LEFT, AV_CHAN_FRONT_LEFT, 1.0);
     ret |= check_coefficient(&surround_2, &stereo,
                              AV_CHAN_TOP_FRONT_RIGHT, AV_CHAN_FRONT_RIGHT, 1.0);
+    ret |= check_coefficient(&surround_22_2, &surround_2,
+                             AV_CHAN_TOP_FRONT_CENTER, AV_CHAN_TOP_FRONT_LEFT,
+                             M_SQRT1_2);
+    ret |= check_coefficient(&surround_22_2, &surround_2,
+                             AV_CHAN_TOP_FRONT_CENTER, AV_CHAN_TOP_FRONT_RIGHT,
+                             M_SQRT1_2);
     ret |= check_coefficient(&surround_4, &surround_2,
                              AV_CHAN_TOP_BACK_LEFT, AV_CHAN_TOP_FRONT_LEFT,
                              M_SQRT1_2);
@@ -165,6 +172,30 @@ int main(void)
                              AV_CHAN_TOP_BACK_CENTER, AV_CHAN_FRONT_RIGHT, 0.5);
     ret |= check_coefficient(&surround_tbc, &mono,
                              AV_CHAN_TOP_BACK_CENTER, AV_CHAN_FRONT_CENTER, 0.5);
+    ret |= check_coefficient(&surround_22_2, &surround_2,
+                             AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_FRONT_LEFT,
+                             M_SQRT1_2);
+    ret |= check_coefficient(&surround_22_2, &surround_2,
+                             AV_CHAN_TOP_SIDE_RIGHT, AV_CHAN_TOP_FRONT_RIGHT,
+                             M_SQRT1_2);
+    ret |= check_coefficient(&surround_22_2, &surround_tbc,
+                             AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_BACK_CENTER,
+                             1.0/sqrt(3));
+    ret |= check_coefficient(&surround_22_2, &surround_tbc,
+                             AV_CHAN_TOP_SIDE_RIGHT, AV_CHAN_TOP_BACK_CENTER,
+                             1.0/sqrt(3));
+    ret |= check_coefficient(&surround_22_2, &stereo,
+                             AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_FRONT_LEFT,
+                             M_SQRT1_2);
+    ret |= check_coefficient(&surround_22_2, &stereo,
+                             AV_CHAN_TOP_SIDE_RIGHT, AV_CHAN_FRONT_RIGHT,
+                             M_SQRT1_2);
+    ret |= check_coefficient_with_slev(&surround_22_2, &stereo,
+                                       AV_CHAN_TOP_SIDE_LEFT,
+                                       AV_CHAN_FRONT_LEFT, 0.5, 0.5);
+    ret |= check_coefficient_with_slev(&surround_22_2, &stereo,
+                                       AV_CHAN_TOP_SIDE_RIGHT,
+                                       AV_CHAN_FRONT_RIGHT, 0.5, 0.5);
 
     return ret;
 }
-- 
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.