Commit 09f879b074 for ffmpeg

commit 09f879b074280a42e3ff211db6cdf077fe61a55d
Author: Kacper Michajłow <kasper93@gmail.com>
Date:   Sun Sep 27 16:05:52 2026 +0200

    swresample: keep integer samples if channels are only copied

    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 <kasper93@gmail.com>

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 27a48a790b..19408e6567 100644
--- a/tests/fate/libswresample.mak
+++ b/tests/fate/libswresample.mak
@@ -1106,6 +1106,16 @@ fate-swr-custom-rematrix: REF = 2a14a44deb4ae26e3b474ddbfbc048f8

 FATE_SWR += $(FATE_SWR_CUSTOM_REMATRIX-yes)

+FATE_SWR_S32_SRC = mod(n*$(1)\,4294967296)/2147483648-1
+FATE_SWR_S32_7_1 = $(call FATE_SWR_S32_SRC,1103515245)|$(call FATE_SWR_S32_SRC,22695477)|$(call FATE_SWR_S32_SRC,214013)|$(call FATE_SWR_S32_SRC,1664525)|$(call FATE_SWR_S32_SRC,134775813)|$(call FATE_SWR_S32_SRC,69069)|$(call FATE_SWR_S32_SRC,1812433253)|$(call FATE_SWR_S32_SRC,2654435761)
+
+FATE_SWR_CUSTOM_REORDER-$(call ENCMUX, PCM_S32LE, PCM_S32LE, AEVALSRC_FILTER ARESAMPLE_FILTER AFORMAT_FILTER FILE_PROTOCOL) += fate-swr-custom-reorder
+fate-swr-custom-reorder: CMD = md5 -filter_complex "aevalsrc=$(FATE_SWR_S32_7_1):c=7.1:d=0.5,aresample,aformat=f=s32:cl=7.1,aresample,aformat=f=s32:cl=FR+FL+FC+LFE+SL+SR+BL+BR[OUT]" -map [OUT] -f s32le
+fate-swr-custom-reorder: CMP = oneline
+fate-swr-custom-reorder: REF = 3621f90f05eb313858791b3915b9ca18
+
+FATE_SWR += $(FATE_SWR_CUSTOM_REORDER-yes)
+
 FATE_SWR_22_2_LAYOUTS   = 9.1.6 9.1.4 7.2.3 7.1.6 7.1.4 7.1.2 5.1.6 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.6 7.1.4 7.1.2 5.1.6 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