PR #23340 opened by James Almer (jamrial)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23340
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23340.patch

Only for core streams for now.

`$ ./ffmpeg -downmix stereo -i 
$fate-suite/dts/dcadec-suite/core_51_24_48_768_1.dtshd -y native.wav`

Before:
```
$ ./ffmpeg -i $fate-suite/dts/dcadec-suite/core_51_24_48_768_1.dtshd -af 
aresample,aformat=channel_layouts=stereo -y swr.wav
$ tests/tiny_psnr.exe native.wav swr.wav
stddev:  129.18 PSNR:  5.91 MAXDIFF:  253 bytes:     8192/     8192
```

After:
```
$ ./ffmpeg -i $fate-suite/dts/dcadec-suite/core_51_24_48_768_1.dtshd -af 
aresample,aformat=channel_layouts=stereo -y swr.wav
$ tests/tiny_psnr.exe native.wav swr.wav
stddev:    0.01 PSNR: 84.25 MAXDIFF:    1 bytes:     8192/     8192
```


>From fff7c7214316ff996501f911e821869646cf842a Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Thu, 4 Jun 2026 14:40:58 -0300
Subject: [PATCH 1/7] avutil/downmix_info: add a new API to propagate pre-made
 downmix matrixes

Useful for formats where they provide the downmix matrix in the bistream rather 
than
scale factors for channel groups.

Signed-off-by: James Almer <[email protected]>
---
 libavutil/downmix_info.c | 44 +++++++++++++++++++++++++++
 libavutil/downmix_info.h | 66 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+)

diff --git a/libavutil/downmix_info.c b/libavutil/downmix_info.c
index 7e6c3e854d..94858f24dc 100644
--- a/libavutil/downmix_info.c
+++ b/libavutil/downmix_info.c
@@ -20,6 +20,7 @@
 
 #include "downmix_info.h"
 #include "frame.h"
+#include "mem.h"
 
 AVDownmixInfo *av_downmix_info_update_side_data(AVFrame *frame)
 {
@@ -39,3 +40,46 @@ AVDownmixInfo *av_downmix_info_update_side_data(AVFrame 
*frame)
 
     return (AVDownmixInfo*)side_data->data;
 }
+
+#define MAX_CHANNELS 64
+
+static const int type_map[AV_DOWNMIX_TYPE_NB] = {
+    [AV_DOWNMIX_TYPE_UNKNOWN] = 0,
+    [AV_DOWNMIX_TYPE_LORO]    = 2,
+    [AV_DOWNMIX_TYPE_LTRT]    = 2,
+    [AV_DOWNMIX_TYPE_DPLII]   = 2,
+};
+
+AVDownmixMatrix *av_downmix_matrix_alloc(enum AVDownmixType type,
+                                         int in_ch_count, size_t *out_size)
+{
+    struct TestStruct {
+        AVDownmixMatrix p;
+        AVDownmixCoeff  b;
+    };
+    const size_t coeffs_offset = offsetof(struct TestStruct, b);
+    size_t size = coeffs_offset;
+    unsigned int nb_coeffs;
+    AVDownmixMatrix *dc;
+
+    if (type >= AV_DOWNMIX_TYPE_NB || !type_map[type] ||
+        (unsigned)in_ch_count > MAX_CHANNELS)
+        return NULL;
+
+    nb_coeffs = type_map[type] * in_ch_count;
+    size += sizeof(AVDownmixCoeff) * nb_coeffs;
+
+    dc = av_mallocz(size);
+    if (!dc)
+        return NULL;
+
+    dc->downmix_type  = type;
+    dc->nb_coeffs     = nb_coeffs;
+    dc->in_ch_count   = in_ch_count;
+    dc->coeffs_offset = coeffs_offset;
+
+    if (out_size)
+        *out_size = size;
+
+    return dc;
+}
diff --git a/libavutil/downmix_info.h b/libavutil/downmix_info.h
index 221cf5bf9b..8e11990b45 100644
--- a/libavutil/downmix_info.h
+++ b/libavutil/downmix_info.h
@@ -21,6 +21,7 @@
 #ifndef AVUTIL_DOWNMIX_INFO_H
 #define AVUTIL_DOWNMIX_INFO_H
 
+#include "avassert.h"
 #include "frame.h"
 
 /**
@@ -104,6 +105,71 @@ typedef struct AVDownmixInfo {
  */
 AVDownmixInfo *av_downmix_info_update_side_data(AVFrame *frame);
 
+/**
+ * This structure describes optional metadata relevant to a downmix procedure
+ * in the form of a remixing matrix allocated as an array of AVDownmixCoeff.
+ * Must be allocated with @ref av_downmix_matrix_alloc.
+ *
+ * sizeof(AVDownmixMatrix) is not a part of the ABI and new fields may be
+ * added to it.
+ */
+typedef struct AVDownmixMatrix {
+    /**
+     * Type of downmix the coeffs will produce.
+     * Output channel count is derived from this value.
+     */
+    enum AVDownmixType downmix_type;
+
+    /**
+     * Input channel count.
+     */
+    int in_ch_count;
+
+    /**
+     * Amount of coefficients in the matrix.
+     */
+    unsigned int nb_coeffs;
+
+    /**
+     * Offset in bytes from the beginning of this structure at which the array
+     * of coefficients starts.
+     */
+    size_t coeffs_offset;
+} AVDownmixMatrix;
+
+/**
+ * Data type for storing coefficients, which are allocated as a part of
+ * AVDownmixMatrix and should be retrieved with @ref av_downmix_matrix_coeff.
+ */
+typedef double AVDownmixCoeff;
+
+/**
+ * Get a pointer to the coeff that represents the weight of input channel
+ * {@code in} in output channel {@code out}.
+ * in must be between 0 and @ref AVDownmixMatrix.in_ch_count "in_ch_count" - 1.
+ * out must be between 0 and the implicit output channel count from
+ * @ref AVDownmixMatrix.downmix_type "downmix_type" - 1.
+ */
+static av_always_inline AVDownmixCoeff*
+av_downmix_matrix_coeff(AVDownmixMatrix *dm, unsigned int out, unsigned int in)
+{
+    av_assert0(in < dm->in_ch_count && in * out < dm->nb_coeffs);
+    AVDownmixCoeff *coeff = (AVDownmixCoeff *)((uint8_t *)dm + 
dm->coeffs_offset);
+    return &coeff[in + dm->in_ch_count * out];
+}
+
+/**
+ * Allocates memory for AVDownmixMatrix of the given type, plus an array of
+ * {@code in_ch_count} times the implicit output channel count from {@code 
type}
+ * of AVDownmixCoeff and initializes the variables. Can be freed with a normal
+ * av_free() call.
+ *
+ * @param out_size if non-NULL, the size in bytes of the resulting data array 
is
+ * written here.
+ */
+AVDownmixMatrix *av_downmix_matrix_alloc(enum AVDownmixType type,
+                                         int in_ch_count, size_t *out_size);
+
 /**
  * @}
  */
-- 
2.52.0


>From 6c4be9438dcdcbf7a68df5c38216118e27eeeb80 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Thu, 4 Jun 2026 14:41:55 -0300
Subject: [PATCH 2/7] avutil/frame: add a new side data type for downmix matrix

Signed-off-by: James Almer <[email protected]>
---
 libavutil/frame.h     | 7 +++++++
 libavutil/side_data.c | 1 +
 2 files changed, 8 insertions(+)

diff --git a/libavutil/frame.h b/libavutil/frame.h
index e123668124..6473ca69c4 100644
--- a/libavutil/frame.h
+++ b/libavutil/frame.h
@@ -292,6 +292,13 @@ enum AVFrameSideDataType {
     * and Formats standard.
     */
     AV_FRAME_DATA_IAMF_RECON_GAIN_INFO_PARAM,
+    /**
+     * Metadata relevant to a downmix procedure.
+     * Alternative to AV_FRAME_DATA_DOWNMIX_INFO where a pre-made remix matrix 
is
+     * provided instead of generalized scale factors.
+     * The data is the AVDownmixMatrix struct defined in 
libavutil/downmix_info.h.
+     */
+    AV_FRAME_DATA_DOWNMIX_MATRIX,
 };
 
 enum AVActiveFormatDescription {
diff --git a/libavutil/side_data.c b/libavutil/side_data.c
index 64c4220ed9..8e88bd7476 100644
--- a/libavutil/side_data.c
+++ b/libavutil/side_data.c
@@ -61,6 +61,7 @@ static const AVSideDataDescriptor sd_props[] = {
     [AV_FRAME_DATA_IAMF_MIX_GAIN_PARAM]         = { "IAMF Mix Gain Parameter 
Data" },
     [AV_FRAME_DATA_IAMF_DEMIXING_INFO_PARAM]    = { "IAMF Demixing Info 
Parameter Data",            AV_SIDE_DATA_PROP_CHANNEL_DEPENDENT },
     [AV_FRAME_DATA_IAMF_RECON_GAIN_INFO_PARAM]  = { "IAMF Recon Gain Info 
Parameter Data" },
+    [AV_FRAME_DATA_DOWNMIX_MATRIX]              = { "Downmix Matrix",          
                     AV_SIDE_DATA_PROP_CHANNEL_DEPENDENT },
 };
 
 const AVSideDataDescriptor *av_frame_side_data_desc(enum AVFrameSideDataType 
type)
-- 
2.52.0


>From 7f2afa3eaeef1da284585dd5845e09ef6f9d5ae0 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Thu, 4 Jun 2026 14:44:10 -0300
Subject: [PATCH 3/7] avfilter/af_ashowinfo: support printing downmix matrix
 side data

Signed-off-by: James Almer <[email protected]>
---
 libavfilter/af_ashowinfo.c | 39 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/libavfilter/af_ashowinfo.c b/libavfilter/af_ashowinfo.c
index ebcd5c996f..c64249537b 100644
--- a/libavfilter/af_ashowinfo.c
+++ b/libavfilter/af_ashowinfo.c
@@ -98,12 +98,48 @@ static void dump_downmix(AVFilterContext *ctx, 
AVFrameSideData *sd)
     }
 
     av_log(ctx, AV_LOG_INFO, " Mix levels: center %f (%f ltrt) - "
-           "surround %f (%f ltrt) - lfe %f",
+           "surround %f (%f ltrt) - lfe %f\n",
            di->center_mix_level, di->center_mix_level_ltrt,
            di->surround_mix_level, di->surround_mix_level_ltrt,
            di->lfe_mix_level);
 }
 
+static const int downmix_type_map[AV_DOWNMIX_TYPE_NB] = {
+    [AV_DOWNMIX_TYPE_UNKNOWN] = 0,
+    [AV_DOWNMIX_TYPE_LORO]    = 2,
+    [AV_DOWNMIX_TYPE_LTRT]    = 2,
+    [AV_DOWNMIX_TYPE_DPLII]   = 2,
+};
+
+static void dump_downmix_matrix(AVFilterContext *ctx, AVFrameSideData *sd, 
AVChannelLayout *ch_layout)
+{
+    AVDownmixMatrix *dm = (AVDownmixMatrix *)sd->data;
+    char buf[128];
+
+    av_log(ctx, AV_LOG_INFO, "downmix matrix: ");
+    if (dm->in_ch_count != ch_layout->nb_channels) {
+        av_log(ctx, AV_LOG_INFO, "invalid data");
+        return;
+    }
+
+    av_log(ctx, AV_LOG_INFO, "downmix type - ");
+    switch (dm->downmix_type) {
+    case AV_DOWNMIX_TYPE_LORO:    av_log(ctx, AV_LOG_INFO, "Lo/Ro\n");         
     break;
+    case AV_DOWNMIX_TYPE_LTRT:    av_log(ctx, AV_LOG_INFO, "Lt/Rt\n");         
     break;
+    case AV_DOWNMIX_TYPE_DPLII:   av_log(ctx, AV_LOG_INFO, "Dolby Pro Logic 
II\n"); break;
+    default:                      av_log(ctx, AV_LOG_WARNING, "invalid data"); 
    return;
+    }
+
+    for (int i = 0; i < downmix_type_map[dm->downmix_type]; i++) {
+        av_log(ctx, AV_LOG_INFO, "[%s] = { ", i ? "FR" : "FL");
+        for (int j = 0; j < dm->in_ch_count; j++) {
+            av_channel_name(buf, sizeof(buf), 
av_channel_layout_channel_from_index(ch_layout, j));
+            av_log(ctx, AV_LOG_INFO, ".%s = %f, ", buf, 
*av_downmix_matrix_coeff(dm, i, j));
+        }
+        av_log(ctx, AV_LOG_INFO, "}%s", i == 
downmix_type_map[dm->downmix_type] - 1 ? "" : ",\n");
+    }
+}
+
 static void print_gain(AVFilterContext *ctx, const char *str, int32_t gain)
 {
     av_log(ctx, AV_LOG_INFO, "%s - ", str);
@@ -222,6 +258,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
         switch (sd->type) {
         case AV_FRAME_DATA_MATRIXENCODING: dump_matrixenc (ctx, sd); break;
         case AV_FRAME_DATA_DOWNMIX_INFO:   dump_downmix   (ctx, sd); break;
+        case AV_FRAME_DATA_DOWNMIX_MATRIX: dump_downmix_matrix(ctx, sd, 
&buf->ch_layout); break;
         case AV_FRAME_DATA_REPLAYGAIN:     dump_replaygain(ctx, sd); break;
         case AV_FRAME_DATA_AUDIO_SERVICE_TYPE: dump_audio_service_type(ctx, 
sd); break;
         default:                           dump_unknown   (ctx, sd); break;
-- 
2.52.0


>From 3267a10ec8d80c7a0bac4a8d6e0dd089cb958c3b Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Thu, 4 Jun 2026 14:44:15 -0300
Subject: [PATCH 4/7] swresample/rematrix: print custom downmix matrix
 coefficients

Similar to how it's done to the matrix built in auto_matrix().

Signed-off-by: James Almer <[email protected]>
---
 libswresample/rematrix.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c
index a4911a2922..6f4c16ee45 100644
--- a/libswresample/rematrix.c
+++ b/libswresample/rematrix.c
@@ -465,6 +465,20 @@ av_cold int swri_rematrix_init(SwrContext *s){
         int r = auto_matrix(s);
         if (r)
             return r;
+    } else {
+        char buf[128];
+        av_log(s, AV_LOG_DEBUG, "Custom matrix coefficients:\n");
+            double *matrix_param = (double*)s->matrix;
+            ptrdiff_t stride = s->matrix[1] - s->matrix[0];
+        for (i = 0; i < s->out_ch_layout.nb_channels; i++) {
+            av_channel_name(buf, sizeof(buf), 
av_channel_layout_channel_from_index(&s->out_ch_layout, i));
+            av_log(s, AV_LOG_DEBUG, "%s: ", buf);
+            for (j = 0; j < s->in_ch_layout.nb_channels; j++){
+                av_channel_name(buf, sizeof(buf), 
av_channel_layout_channel_from_index(&s->in_ch_layout, j));
+                av_log(s, AV_LOG_DEBUG, "%s:%f ", buf, matrix_param[stride*i + 
j]);
+            }
+            av_log(s, AV_LOG_DEBUG, "\n");
+        }
     }
     if (s->midbuf.fmt == AV_SAMPLE_FMT_S16P){
         int maxsum = 0;
-- 
2.52.0


>From ef868712e6d0ef675075c5d57965b4767a5bfd42 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Thu, 4 Jun 2026 14:46:07 -0300
Subject: [PATCH 5/7] avfilter/af_aresample: also look for downmix matrix side
 data

Signed-off-by: James Almer <[email protected]>
---
 libavfilter/af_aresample.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/libavfilter/af_aresample.c b/libavfilter/af_aresample.c
index 15b14b27f7..8e7f5bfc56 100644
--- a/libavfilter/af_aresample.c
+++ b/libavfilter/af_aresample.c
@@ -145,6 +145,30 @@ static int config_output(AVFilterLink *outlink)
     if (ret < 0)
         return ret;
 
+    sd = av_frame_side_data_get(inlink->side_data, inlink->nb_side_data,
+                                AV_FRAME_DATA_DOWNMIX_MATRIX);
+    if (sd) {
+        const AVDownmixMatrix *dm = (AVDownmixMatrix *)sd->data;
+
+        if (inlink->ch_layout.nb_channels == dm->in_ch_count &&
+            !av_channel_layout_compare(&outlink->ch_layout, 
&(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO)) {
+            swr_set_matrix(aresample->swr,
+                           (double *)((uint8_t *)dm + dm->coeffs_offset),
+                           dm->in_ch_count);
+
+            switch (dm->downmix_type) {
+            case AV_DOWNMIX_TYPE_LTRT:
+                matrix_encoding = AV_MATRIX_ENCODING_DOLBY;
+                break;
+            case AV_DOWNMIX_TYPE_DPLII:
+                matrix_encoding = AV_MATRIX_ENCODING_DPLII;
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
     sd = av_frame_side_data_get(inlink->side_data, inlink->nb_side_data,
                                 AV_FRAME_DATA_DOWNMIX_INFO);
     if (sd) {
-- 
2.52.0


>From 5e3e166a1aa21cb1849ff7413b0076d4c91cfae2 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Thu, 4 Jun 2026 14:46:51 -0300
Subject: [PATCH 6/7] fftools/ffmpeg_filter: propagate downmix matrix side data
 to the filterchain

Signed-off-by: James Almer <[email protected]>
---
 fftools/ffmpeg_filter.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c
index 8ea89e2ec2..29521a0e87 100644
--- a/fftools/ffmpeg_filter.c
+++ b/fftools/ffmpeg_filter.c
@@ -145,6 +145,10 @@ typedef struct InputFilterPriv {
     int                 downmixinfo_present;
     AVDownmixInfo       downmixinfo;
 
+    AVBufferRef        *downmixmatrix;
+    size_t              downmixmatrix_size;
+    int                 downmixmatrix_present;
+
     struct {
         AVFrame *frame;
 
@@ -1040,6 +1044,7 @@ void fg_free(FilterGraph **pfg)
         av_channel_layout_uninit(&ifp->ch_layout);
         av_freep(&ifilter->linklabel);
         av_freep(&ifp->opts.name);
+        av_buffer_unref(&ifp->downmixmatrix);
         av_frame_side_data_free(&ifp->side_data, &ifp->nb_side_data);
         av_freep(&ifilter->name);
         av_freep(&ifilter->input_name);
@@ -2285,6 +2290,18 @@ static int ifilter_parameters_from_frame(InputFilter 
*ifilter, const AVFrame *fr
         memcpy(&ifp->downmixinfo, sd->data, sizeof(ifp->downmixinfo));
     }
     ifp->downmixinfo_present = !!sd;
+    sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DOWNMIX_MATRIX);
+    if (sd) {
+        ret = av_frame_side_data_clone(&ifp->side_data,
+                                       &ifp->nb_side_data, sd, 0);
+        if (ret < 0)
+            return ret;
+        ret = av_buffer_replace(&ifp->downmixmatrix, sd->buf);
+        if (ret < 0)
+            return ret;
+        ifp->downmixmatrix_size = sd->size;
+    }
+    ifp->downmixmatrix_present = !!sd;
 
     return 0;
 }
@@ -3108,6 +3125,13 @@ static int send_frame(FilterGraph *fg, FilterGraphThread 
*fgt,
     } else if (ifp->downmixinfo_present)
         need_reinit |= DOWNMIX_CHANGED;
 
+    if (sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DOWNMIX_MATRIX)) {
+        if (!ifp->downmixmatrix_present ||
+            sd->size != ifp->downmixmatrix_size || memcmp(sd->data, 
ifp->downmixmatrix->data, sd->size))
+            need_reinit |= DOWNMIX_CHANGED;
+    } else if (ifp->downmixmatrix_present)
+        need_reinit |= DOWNMIX_CHANGED;
+
     if (need_reinit && fgt->graph && (ifp->opts.flags & 
IFILTER_FLAG_DROPCHANGED)) {
             ifp->nb_dropped++;
             av_log_once(fg, AV_LOG_WARNING, AV_LOG_DEBUG, &ifp->drop_warned, 
"Avoiding reinit; dropping frame pts: %s bound for %s\n", 
av_ts2str(frame->pts), ifilter->name);
-- 
2.52.0


>From 4cb7f1198a1fd81169bad9dab3afd9557d1456f8 Mon Sep 17 00:00:00 2001
From: James Almer <[email protected]>
Date: Thu, 4 Jun 2026 14:51:57 -0300
Subject: [PATCH 7/7] avcodec/dca_core: export downmix matrix coefficients

Only for core streams for now.

$ ./ffmpeg -downmix stereo -i 
$fate-suite/dts/dcadec-suite/core_51_24_48_768_1.dtshd -y native.wav

Before:
$ ./ffmpeg -i $fate-suite/dts/dcadec-suite/core_51_24_48_768_1.dtshd -af 
aresample,aformat=channel_layouts=stereo -y swr.wav
$ tests/tiny_psnr.exe native.wav swr.wav
stddev:  129.18 PSNR:  5.91 MAXDIFF:  253 bytes:     8192/     8192

After:
$ ./ffmpeg -i $fate-suite/dts/dcadec-suite/core_51_24_48_768_1.dtshd -af 
aresample,aformat=channel_layouts=stereo -y swr.wav
$ tests/tiny_psnr.exe native.wav swr.wav
stddev:    0.01 PSNR: 84.25 MAXDIFF:    1 bytes:     8192/     8192

Signed-off-by: James Almer <[email protected]>
---
 libavcodec/dca_core.c | 50 +++++++++++++++++++++++++++++++++++++++++++
 libavcodec/dcadata.c  | 10 +++++++++
 libavcodec/dcadata.h  |  3 +++
 libavcodec/dcadec.c   | 15 +++----------
 4 files changed, 66 insertions(+), 12 deletions(-)

diff --git a/libavcodec/dca_core.c b/libavcodec/dca_core.c
index cb1f7b7bbf..f087a6f68d 100644
--- a/libavcodec/dca_core.c
+++ b/libavcodec/dca_core.c
@@ -19,6 +19,7 @@
  */
 
 #include "libavutil/channel_layout.h"
+#include "libavutil/downmix_info.h"
 #include "libavutil/mem.h"
 #include "dcaadpcm.h"
 #include "dcadec.h"
@@ -2384,6 +2385,55 @@ int ff_dca_core_filter_frame(DCACoreDecoder *s, AVFrame 
*frame)
     else
         avctx->bit_rate = 0;
 
+    if (s->request_mask == s->ch_mask && s->prim_dmix_embedded &&
+        !s->sumdiff_front && !s->sumdiff_surround &&
+        !(s->ext_audio_mask & (DCA_CSS_XXCH | DCA_CSS_XCH | DCA_EXSS_XXCH)) &&
+        s->audio_mode >= DCA_AMODE_2F2R && (s->prim_dmix_type == 
DCA_DMIX_TYPE_LoRo ||
+                                            s->prim_dmix_type == 
DCA_DMIX_TYPE_LtRt)) {
+        size_t size;
+        enum AVDownmixType dmix_type = (s->prim_dmix_type == 
DCA_DMIX_TYPE_LoRo) ?
+                                       AV_DOWNMIX_TYPE_LORO : 
AV_DOWNMIX_TYPE_LTRT;
+        AVDownmixMatrix *dm = av_downmix_matrix_alloc(dmix_type, 
av_popcount(s->ch_mask), &size);
+        const int *coeff_l = s->prim_dmix_coeff;
+        const int *coeff_r = coeff_l + av_popcount(s->ch_mask);
+        AVDownmixCoeff *matrix_l, *matrix_r;
+        const double scale = 1.0 / (1 << 15);
+
+        if (!dm)
+            return AVERROR(ENOMEM);
+
+        matrix_l = av_downmix_matrix_coeff(dm, 0, 0);
+        matrix_r = av_downmix_matrix_coeff(dm, 1, 0);
+
+        for (int i = 0; i <= av_log2(s->ch_mask); i++) {
+            if (!(s->ch_mask & (1U << i)))
+                continue;
+
+            int idx = av_channel_layout_index_from_channel(&avctx->ch_layout, 
ff_dca2wav_norm[i]);
+            av_assert0(idx >= 0);
+
+            if (*coeff_l)
+                matrix_l[idx] = *coeff_l * scale;
+
+            if (*coeff_r)
+                matrix_r[idx] = *coeff_r * scale;
+
+            coeff_l++;
+            coeff_r++;
+        }
+
+        AVBufferRef *buf = av_buffer_create((uint8_t *)dm, size, NULL, NULL, 
0);
+        if (!buf) {
+            av_free(dm);
+            return AVERROR(ENOMEM);
+        }
+
+        if (!av_frame_new_side_data_from_buf(frame, 
AV_FRAME_DATA_DOWNMIX_MATRIX, buf)) {
+            av_buffer_unref(&buf);
+            return AVERROR(ENOMEM);
+        }
+    }
+
     if (s->audio_mode == DCA_AMODE_STEREO_TOTAL || (s->request_mask != 
s->ch_mask &&
                                                     s->prim_dmix_type == 
DCA_DMIX_TYPE_LtRt))
         matrix_encoding = AV_MATRIX_ENCODING_DOLBY;
diff --git a/libavcodec/dcadata.c b/libavcodec/dcadata.c
index 643b0cc025..76e5750e72 100644
--- a/libavcodec/dcadata.c
+++ b/libavcodec/dcadata.c
@@ -46,6 +46,16 @@ const uint8_t ff_dca_dmix_primary_nch[8] = {
     1, 2, 2, 3, 3, 4, 4, 0
 };
 
+const uint8_t ff_dca2wav_norm[28] = {
+     2,  0, 1, 9, 10,  3,  8,  4,  5,  9, 10, 6, 7, 12,
+    13, 14, 3, 6,  7, 11, 12, 14, 16, 15, 17, 8, 4,  5,
+};
+
+const uint8_t ff_dca2wav_wide[28] = {
+     2,  0, 1, 4,  5,  3,  8,  4,  5,  9, 10, 6, 7, 12,
+    13, 14, 3, 9, 10, 11, 12, 14, 16, 15, 17, 8, 4,  5,
+};
+
 const uint8_t ff_dca_quant_index_sel_nbits[DCA_CODE_BOOKS] = {
     1, 2, 2, 2, 2, 3, 3, 3, 3, 3
 };
diff --git a/libavcodec/dcadata.h b/libavcodec/dcadata.h
index 5aa85b3414..6bdc52ea3a 100644
--- a/libavcodec/dcadata.h
+++ b/libavcodec/dcadata.h
@@ -34,6 +34,9 @@ extern const uint8_t ff_dca_channels[16];
 
 extern const uint8_t ff_dca_dmix_primary_nch[8];
 
+extern const uint8_t ff_dca2wav_norm[28];
+extern const uint8_t ff_dca2wav_wide[28];
+
 extern const uint8_t ff_dca_quant_index_sel_nbits[DCA_CODE_BOOKS];
 extern const uint8_t ff_dca_quant_index_group_size[DCA_CODE_BOOKS];
 
diff --git a/libavcodec/dcadec.c b/libavcodec/dcadec.c
index f47a694746..ba89510e94 100644
--- a/libavcodec/dcadec.c
+++ b/libavcodec/dcadec.c
@@ -25,6 +25,7 @@
 
 #include "codec_internal.h"
 #include "dcadec.h"
+#include "dcadata.h"
 #include "dcahuff.h"
 #include "dca_syncwords.h"
 #include "profiles.h"
@@ -34,16 +35,6 @@
 
 int ff_dca_set_channel_layout(AVCodecContext *avctx, int *ch_remap, int 
dca_mask)
 {
-    static const uint8_t dca2wav_norm[28] = {
-         2,  0, 1, 9, 10,  3,  8,  4,  5,  9, 10, 6, 7, 12,
-        13, 14, 3, 6,  7, 11, 12, 14, 16, 15, 17, 8, 4,  5,
-    };
-
-    static const uint8_t dca2wav_wide[28] = {
-         2,  0, 1, 4,  5,  3,  8,  4,  5,  9, 10, 6, 7, 12,
-        13, 14, 3, 9, 10, 11, 12, 14, 16, 15, 17, 8, 4,  5,
-    };
-
     DCAContext *s = avctx->priv_data;
 
     int dca_ch, wav_ch, nchannels = 0;
@@ -51,9 +42,9 @@ int ff_dca_set_channel_layout(AVCodecContext *avctx, int 
*ch_remap, int dca_mask
 
     if (dca_mask == DCA_SPEAKER_LAYOUT_7POINT0_WIDE ||
         dca_mask == DCA_SPEAKER_LAYOUT_7POINT1_WIDE)
-        dca2wav = dca2wav_wide;
+        dca2wav = ff_dca2wav_wide;
     else
-        dca2wav = dca2wav_norm;
+        dca2wav = ff_dca2wav_norm;
 
     av_channel_layout_uninit(&avctx->ch_layout);
     if (s->output_channel_order == CHANNEL_ORDER_CODED) {
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to