This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 72b81d5b64c13e36ea9a87f8e096616a20944098
Author:     Kacper Michajłow <[email protected]>
AuthorDate: Tue Aug 11 11:24:49 2026 +0200
Commit:     Kacper Michajłow <[email protected]>
CommitDate: Sun Sep 6 00:48:24 2026 +0200

    swresample: support AV_SAMPLE_FMT_DSD input
    
    Convert DSD to PCM with the same 96-tap symmetric lowpass filter the
    libavcodec DSD decoders use. The implementation is copied from
    libavcodec/dsd.c, minus the LSBF table variants, since AV_SAMPLE_FMT_DSD
    is defined as MSBF.
    
    The libavcodec copy will be removed in future commits as decoders are
    transitioned to SWR.
    
    The conversion is stateful (per-channel filter history), so the audio
    conversion functions gain a per-channel state argument.
    
    DSD silence is the 0x69 bit pattern instead of the 0x80 used for PCM.
    
    Only a direct DSD to float conversion function is provided. For format
    pairs without a direct conversion function (e.g. DSD to s16 with equal
    rates), swr_init() now falls back from the full_convert shortcut to
    the generic two-stage path instead of failing, and reports unsupported
    conversions with a proper error message instead of ENOMEM.
    
    Signed-off-by: Kacper Michajłow <[email protected]>
---
 libswresample/Makefile                      |  3 +-
 libswresample/audioconvert.c                | 18 +++++++++--
 libswresample/audioconvert.h                |  4 ++-
 libavcodec/dsd.c => libswresample/dsd2pcm.c | 46 +++++++++++++----------------
 libavcodec/dsd.h => libswresample/dsd2pcm.h | 33 ++++++++++++---------
 {libavcodec => libswresample}/reverse.c     |  0
 libswresample/swresample.c                  | 36 +++++++++++++++++-----
 libswresample/version.h                     |  2 +-
 tests/ref/fate/source                       |  1 +
 9 files changed, 92 insertions(+), 51 deletions(-)

diff --git a/libswresample/Makefile b/libswresample/Makefile
index 8149de069f..f5125e29b1 100644
--- a/libswresample/Makefile
+++ b/libswresample/Makefile
@@ -7,6 +7,7 @@ HEADERS = swresample.h                       \
           version_major.h                    \
 
 OBJS = audioconvert.o                        \
+       dsd2pcm.o                             \
        dither.o                              \
        options.o                             \
        rematrix.o                            \
@@ -19,7 +20,7 @@ OBJS = audioconvert.o                        \
 OBJS-$(CONFIG_LIBSOXR) += soxr_resample.o
 
 # Objects duplicated from other libraries for shared builds
-SHLIBOBJS              += log2_tab.o
+SHLIBOBJS              += log2_tab.o reverse.o
 
 # Windows resource file
 SHLIBOBJS-$(HAVE_GNU_WINDRES) += swresampleres.o
diff --git a/libswresample/audioconvert.c b/libswresample/audioconvert.c
index f8bac98ca5..2085385cce 100644
--- a/libswresample/audioconvert.c
+++ b/libswresample/audioconvert.c
@@ -36,7 +36,7 @@
 
 //FIXME rounding ?
 #define CONV_FUNC(ofmt, otype, ifmt, expr)\
-static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t *po, const uint8_t *pi, int is, 
int os, uint8_t *end)\
+static void CONV_FUNC_NAME(ofmt, ifmt)(DSDContext *st, uint8_t *po, const 
uint8_t *pi, int is, int os, uint8_t *end)\
 {\
     uint8_t *end2 = end - 3*os;\
     while(po < end2){\
@@ -87,6 +87,12 @@ CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, 
av_clipl_int32(llrint(*
 CONV_FUNC(AV_SAMPLE_FMT_S64, int64_t, AV_SAMPLE_FMT_DBL, llrint(*(const 
double*)pi * (UINT64_C(1)<<63)))
 CONV_FUNC(AV_SAMPLE_FMT_FLT, float  , AV_SAMPLE_FMT_DBL, *(const double*)pi)
 CONV_FUNC(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_DBL, *(const double*)pi)
+CONV_FUNC(AV_SAMPLE_FMT_DSD, uint8_t, AV_SAMPLE_FMT_DSD, *(const uint8_t*)pi)
+
+static void CONV_FUNC_NAME(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DSD)(DSDContext 
*st, uint8_t *po, const uint8_t *pi, int is, int os, uint8_t *end)
+{
+    swri_dsd2pcm_translate(st, (end - po) / os, pi, is, (float *)po, os / 
sizeof(float));
+}
 
 #define FMT_PAIR_FUNC(out, in) [(out) + AV_SAMPLE_FMT_NB*(in)] = 
CONV_FUNC_NAME(out, in)
 
@@ -127,6 +133,8 @@ static conv_func_type * const 
fmt_pair_to_conv_functions[AV_SAMPLE_FMT_NB*AV_SAM
     FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S64),
     FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S64),
     FMT_PAIR_FUNC(AV_SAMPLE_FMT_S64, AV_SAMPLE_FMT_S64),
+    FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DSD),
+    FMT_PAIR_FUNC(AV_SAMPLE_FMT_DSD, AV_SAMPLE_FMT_DSD),
 };
 
 static void cpy1(uint8_t **dst, const uint8_t **src, int len){
@@ -166,6 +174,12 @@ AudioConvert *swri_audio_convert_alloc(enum AVSampleFormat 
out_fmt,
     ctx->ch_map   = ch_map;
     if (in_fmt == AV_SAMPLE_FMT_U8 || in_fmt == AV_SAMPLE_FMT_U8P)
         memset(ctx->silence, 0x80, sizeof(ctx->silence));
+    if (in_fmt == AV_SAMPLE_FMT_DSD) {
+        swri_dsd2pcm_init();
+        memset(ctx->silence, 0x69, sizeof(ctx->silence));
+        for (int ch = 0; ch < FF_ARRAY_ELEMS(ctx->dsd_state); ch++)
+            memset(ctx->dsd_state[ch].buf, 0x69, 
sizeof(ctx->dsd_state[ch].buf));
+    }
 
     if(out_fmt == in_fmt && !ch_map) {
         switch(av_get_bytes_per_sample(in_fmt)){
@@ -245,7 +259,7 @@ int swri_audio_convert(AudioConvert *ctx, AudioData *out, 
AudioData *in, int len
         if(!po)
             continue;
         end = po + os * len;
-        ctx->conv_f(po+off*os, pi+off*is, is, os, end);
+        ctx->conv_f(&ctx->dsd_state[ch], po+off*os, pi+off*is, is, os, end);
     }
     return 0;
 }
diff --git a/libswresample/audioconvert.h b/libswresample/audioconvert.h
index bb143a876d..0dd43866be 100644
--- a/libswresample/audioconvert.h
+++ b/libswresample/audioconvert.h
@@ -30,9 +30,10 @@
 
 
 #include "swresample_internal.h"
+#include "dsd2pcm.h"
 
 
-typedef void (conv_func_type)(uint8_t *po, const uint8_t *pi, int is, int os, 
uint8_t *end);
+typedef void (conv_func_type)(DSDContext *st, uint8_t *po, const uint8_t *pi, 
int is, int os, uint8_t *end);
 typedef void (simd_func_type)(uint8_t **dst, const uint8_t **src, int len);
 
 typedef struct AudioConvert {
@@ -43,6 +44,7 @@ typedef struct AudioConvert {
     simd_func_type *simd_f;
     const int *ch_map;
     uint8_t silence[8]; ///< silence input sample
+    DSDContext dsd_state[SWR_CH_MAX]; ///< per-channel state for DSD input
 }AudioConvert;
 
 /**
diff --git a/libavcodec/dsd.c b/libswresample/dsd2pcm.c
similarity index 74%
copy from libavcodec/dsd.c
copy to libswresample/dsd2pcm.c
index 1093c5e2dd..bfc99e942e 100644
--- a/libavcodec/dsd.c
+++ b/libswresample/dsd2pcm.c
@@ -1,5 +1,5 @@
 /*
- * Direct Stream Digital (DSD) decoder
+ * DSD to PCM conversion
  * based on BSD licensed dsd2pcm by Sebastian Gesemann
  * Copyright (c) 2009, 2011 Sebastian Gesemann. All rights reserved.
  * Copyright (c) 2014 Peter Ross
@@ -25,9 +25,9 @@
 #include "libavutil/attributes.h"
 #include "libavutil/reverse.h"
 #include "libavutil/thread.h"
-#include "dsd.h"
+#include "dsd2pcm.h"
 
-#define CTABLES ((HTAPS + 7) / 8) /** number of "8 MACs" lookup tables */
+#define DSD_CTABLES ((DSD_HTAPS + 7) / 8) /** number of "8 MACs" lookup tables 
*/
 
 /*
  * Properties of this 96-tap lowpass filter when applied on a signal
@@ -49,7 +49,7 @@
 /**
  * The 2nd half (48 coeffs) of a 96-tap symmetric lowpass filter
  */
-static const double htaps[HTAPS] = {
+static const double htaps[DSD_HTAPS] = {
      0.09950731974056658,    0.09562845727714668,    0.08819647126516944,
      0.07782552527068175,    0.06534876523171299,    0.05172629311427257,
      0.0379429484910187,     0.02490921351762261,    0.0133774746265897,
@@ -68,42 +68,38 @@ static const double htaps[HTAPS] = {
      3.423230509967409e-07,  1.244182214744588e-07,  3.130441005359396e-08
 };
 
-static double ctables_lsbf[CTABLES][256];
-static double ctables_msbf[CTABLES][256];
+static double ctables[DSD_CTABLES][256];
 
-static av_cold void dsd_ctables_tableinit(void)
+static av_cold void dsd2pcm_ctables_tableinit(void)
 {
     int t, e, m, sign;
-    double acc[CTABLES];
+    double acc[DSD_CTABLES];
     for (e = 0; e < 256; ++e) {
         memset(acc, 0, sizeof(acc));
         for (m = 0; m < 8; ++m) {
             sign = (((e >> (7 - m)) & 1) * 2 - 1);
-            for (t = 0; t < CTABLES; ++t)
+            for (t = 0; t < DSD_CTABLES; ++t)
                 acc[t] += sign * htaps[t * 8 + m];
         }
-        for (t = 0; t < CTABLES; ++t) {
-            ctables_msbf[CTABLES - 1 - t][e] = acc[t];
-            ctables_lsbf[CTABLES - 1 - t][ff_reverse[e]] = acc[t];
-        }
+        for (t = 0; t < DSD_CTABLES; ++t)
+            ctables[DSD_CTABLES - 1 - t][e] = acc[t];
     }
 }
 
-av_cold void ff_init_dsd_data(void)
+av_cold void swri_dsd2pcm_init(void)
 {
     static AVOnce init_static_once = AV_ONCE_INIT;
-    ff_thread_once(&init_static_once, dsd_ctables_tableinit);
+    ff_thread_once(&init_static_once, dsd2pcm_ctables_tableinit);
 }
 
-void ff_dsd2pcm_translate(DSDContext* s, size_t samples, int lsbf,
-                          const uint8_t *src, ptrdiff_t src_stride,
-                          float *dst, ptrdiff_t dst_stride)
+void swri_dsd2pcm_translate(DSDContext *s, size_t samples,
+                            const uint8_t *src, ptrdiff_t src_stride,
+                            float *dst, ptrdiff_t dst_stride)
 {
-    uint8_t buf[FIFOSIZE];
+    uint8_t buf[DSD_FIFOSIZE];
     unsigned pos, i;
     uint8_t* p;
     double sum;
-    const double (*const ctables)[256] = lsbf ? ctables_lsbf : ctables_msbf;
 
     pos = s->pos;
 
@@ -113,20 +109,20 @@ void ff_dsd2pcm_translate(DSDContext* s, size_t samples, 
int lsbf,
         buf[pos] = *src;
         src += src_stride;
 
-        p = buf + ((pos - CTABLES) & FIFOMASK);
+        p = buf + ((pos - DSD_CTABLES) & DSD_FIFOMASK);
         *p = ff_reverse[*p];
 
         sum = 0.0;
-        for (i = 0; i < CTABLES; i++) {
-            uint8_t a = buf[(pos                   - i) & FIFOMASK];
-            uint8_t b = buf[(pos - (CTABLES*2 - 1) + i) & FIFOMASK];
+        for (i = 0; i < DSD_CTABLES; i++) {
+            uint8_t a = buf[(pos                   - i) & DSD_FIFOMASK];
+            uint8_t b = buf[(pos - (DSD_CTABLES*2 - 1) + i) & DSD_FIFOMASK];
             sum += ctables[i][a] + ctables[i][b];
         }
 
         *dst = (float)sum;
         dst += dst_stride;
 
-        pos = (pos + 1) & FIFOMASK;
+        pos = (pos + 1) & DSD_FIFOMASK;
     }
 
     s->pos = pos;
diff --git a/libavcodec/dsd.h b/libswresample/dsd2pcm.h
similarity index 57%
copy from libavcodec/dsd.h
copy to libswresample/dsd2pcm.h
index 74da74fccc..ca00dcee77 100644
--- a/libavcodec/dsd.h
+++ b/libswresample/dsd2pcm.h
@@ -1,5 +1,5 @@
 /*
- * Direct Stream Digital (DSD) decoder
+ * DSD to PCM conversion
  * based on BSD licensed dsd2pcm by Sebastian Gesemann
  * Copyright (c) 2009, 2011 Sebastian Gesemann. All rights reserved.
  * Copyright (c) 2014 Peter Ross
@@ -21,31 +21,36 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#ifndef AVCODEC_DSD_H
-#define AVCODEC_DSD_H
+#ifndef SWRESAMPLE_DSD2PCM_H
+#define SWRESAMPLE_DSD2PCM_H
 
 #include <stddef.h>
 #include <stdint.h>
 
-#define HTAPS   48               /** number of FIR constants */
-#define FIFOSIZE 16              /** must be a power of two */
-#define FIFOMASK (FIFOSIZE - 1)  /** bit mask for FIFO offsets */
+#define DSD_HTAPS   48               /** number of FIR constants */
+#define DSD_FIFOSIZE 16              /** must be a power of two */
+#define DSD_FIFOMASK (DSD_FIFOSIZE - 1)  /** bit mask for FIFO offsets */
 
-#if FIFOSIZE * 8 < HTAPS * 2
-#error "FIFOSIZE too small"
+#if DSD_FIFOSIZE * 8 < DSD_HTAPS * 2
+#error "DSD_FIFOSIZE too small"
 #endif
 
 /**
  * Per-channel buffer
  */
 typedef struct DSDContext {
-    uint8_t buf[FIFOSIZE];
+    uint8_t buf[DSD_FIFOSIZE];
     unsigned pos;
 } DSDContext;
 
-void ff_init_dsd_data(void);
+void swri_dsd2pcm_init(void);
 
-void ff_dsd2pcm_translate(DSDContext* s, size_t samples, int lsbf,
-                          const uint8_t *src, ptrdiff_t src_stride,
-                          float *dst, ptrdiff_t dst_stride);
-#endif /* AVCODEC_DSD_H */
+/**
+ * Convert one channel of MSB-first DSD data (one byte = 8 samples) to
+ * float PCM at 1/8th of the DSD bit rate. Strides are in elements.
+ */
+void swri_dsd2pcm_translate(DSDContext *s, size_t samples,
+                            const uint8_t *src, ptrdiff_t src_stride,
+                            float *dst, ptrdiff_t dst_stride);
+
+#endif /* SWRESAMPLE_DSD2PCM_H */
diff --git a/libavcodec/reverse.c b/libswresample/reverse.c
similarity index 100%
copy from libavcodec/reverse.c
copy to libswresample/reverse.c
diff --git a/libswresample/swresample.c b/libswresample/swresample.c
index d777efd802..3fa2f3bf6c 100644
--- a/libswresample/swresample.c
+++ b/libswresample/swresample.c
@@ -177,6 +177,14 @@ av_cold int swr_init(struct SwrContext *s){
         return AVERROR(EINVAL);
     }
 
+    if (s->out_sample_fmt == AV_SAMPLE_FMT_DSD &&
+        !(s->in_sample_fmt == AV_SAMPLE_FMT_DSD &&
+          s->in_sample_rate == s->out_sample_rate &&
+          !(s->flags & SWR_FLAG_RESAMPLE))) {
+        av_log(s, AV_LOG_ERROR, "Conversion to DSD is not supported\n");
+        return AVERROR(EINVAL);
+    }
+
     s->out.ch_count  = s-> user_out_chlayout.nb_channels;
     s-> in.ch_count  = s->  user_in_chlayout.nb_channels;
 
@@ -225,8 +233,12 @@ av_cold int swr_init(struct SwrContext *s){
                  s->rematrix_custom;
 
     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
+           && s->out_sample_fmt != AV_SAMPLE_FMT_DSD) {
+            s->int_sample_fmt= AV_SAMPLE_FMT_FLTP;
         // 16bit or less to 16bit or less with the same sample rate
-        if(   av_get_bytes_per_sample(s-> in_sample_fmt) <= 2
+        } else if(   av_get_bytes_per_sample(s-> in_sample_fmt) <= 2
            && av_get_bytes_per_sample(s->out_sample_fmt) <= 2
            && s->out_sample_rate==s->in_sample_rate) {
             s->int_sample_fmt= AV_SAMPLE_FMT_S16P;
@@ -346,7 +358,10 @@ av_assert0(s->out.ch_count);
     if(!s->resample && !s->rematrix && !s->channel_map && !s->dither.method){
         s->full_convert = swri_audio_convert_alloc(s->out_sample_fmt,
                                                    s-> in_sample_fmt, s-> 
in.ch_count, NULL, 0);
-        return 0;
+        // fall through to the generic path for conversions that have no
+        // direct implementation (e.g. DSD input to non-float output)
+        if (s->full_convert)
+            return 0;
     }
 
     s->in_convert = swri_audio_convert_alloc(s->int_sample_fmt,
@@ -355,7 +370,10 @@ av_assert0(s->out.ch_count);
                                              s->int_sample_fmt, 
s->out.ch_count, NULL, 0);
 
     if (!s->in_convert || !s->out_convert) {
-        ret = AVERROR(ENOMEM);
+        av_log(s, AV_LOG_ERROR, "Cannot convert %s sample format to %s sample 
format\n",
+               av_get_sample_fmt_name(!s->in_convert ? s->in_sample_fmt : 
s->int_sample_fmt),
+               av_get_sample_fmt_name(!s->in_convert ? s->int_sample_fmt : 
s->out_sample_fmt));
+        ret = AVERROR(EINVAL);
         goto fail;
     }
 
@@ -865,10 +883,14 @@ int swr_inject_silence(struct SwrContext *s, int count){
     if((ret=swri_realloc_audio(&s->silence, count))<0)
         return ret;
 
-    if(s->silence.planar) for(i=0; i<s->silence.ch_count; i++) {
-        memset(s->silence.ch[i], s->silence.bps==1 ? 0x80 : 0, 
count*s->silence.bps);
-    } else
-        memset(s->silence.ch[0], s->silence.bps==1 ? 0x80 : 0, 
count*s->silence.bps*s->silence.ch_count);
+    {
+        int fill = s->silence.fmt == AV_SAMPLE_FMT_DSD ? 0x69 :
+                   s->silence.bps == 1                 ? 0x80 : 0;
+        if(s->silence.planar) for(i=0; i<s->silence.ch_count; i++) {
+            memset(s->silence.ch[i], fill, count*s->silence.bps);
+        } else
+            memset(s->silence.ch[0], fill, 
count*s->silence.bps*s->silence.ch_count);
+    }
 
     reversefill_audiodata(&s->silence, tmp_arg);
     av_log(s, AV_LOG_VERBOSE, "adding %d audio samples of silence\n", count);
diff --git a/libswresample/version.h b/libswresample/version.h
index 057ac4b19e..d1795b5545 100644
--- a/libswresample/version.h
+++ b/libswresample/version.h
@@ -30,7 +30,7 @@
 
 #include "version_major.h"
 
-#define LIBSWRESAMPLE_VERSION_MINOR   2
+#define LIBSWRESAMPLE_VERSION_MINOR   3
 #define LIBSWRESAMPLE_VERSION_MICRO 100
 
 #define LIBSWRESAMPLE_VERSION_INT  AV_VERSION_INT(LIBSWRESAMPLE_VERSION_MAJOR, 
\
diff --git a/tests/ref/fate/source b/tests/ref/fate/source
index d5eb31d887..693018fdbc 100644
--- a/tests/ref/fate/source
+++ b/tests/ref/fate/source
@@ -16,6 +16,7 @@ libavformat/log2_tab.c
 libavformat/rangecoder_dec.c
 libavformat/riscv/cpu_common.c
 libswresample/log2_tab.c
+libswresample/reverse.c
 libswscale/aarch64/ops_entries.c
 libswscale/log2_tab.c
 libswscale/riscv/cpu_common.c

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to