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]

Reply via email to