[PR] add AV_SAMPLE_FMT_DSD and use it (PR #24076)

Kacper Michajłow via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <178644944505.59.5564993189166596748@29965ddac10e>
PR #24076 opened by Kacper Michajłow (kasper93)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24076
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24076.patch


From d36730dd17bbd8244b936af57e493c20fae3118a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 28 Jul 2026 01:41:36 +0200
Subject: [PATCH 01/14] avcodec/dstdec: fix decoding of uncompressed frames
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The DSD payload of an uncompressed frame was copied packed into the
output buffer, but the in-place DSD to PCM conversion expects the DSD
bytes in every 4th byte, in the place of the float sample they produce.

This was always broken, but I guess, the uncompressed DST is something
that exists only on paper.

Signed-off-by: Kacper Michajłow <[email protected]>
---
 libavcodec/dstdec.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/libavcodec/dstdec.c b/libavcodec/dstdec.c
index d747670141..1db742d193 100644
--- a/libavcodec/dstdec.c
+++ b/libavcodec/dstdec.c
@@ -268,10 +268,17 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
         return ret;
 
     if (!get_bits1(gb)) {
+        unsigned total = frame->nb_samples * channels;
+        unsigned n = FFMIN(avpkt->size - 1, total);
         skip_bits1(gb);
         if (get_bits(gb, 6))
             return AVERROR_INVALIDDATA;
-        memcpy(frame->data[0], avpkt->data + 1, FFMIN(avpkt->size - 1, frame->nb_samples * channels));
+        // DSD bytes are stored in every 4th byte, as expected by the
+        // in-place DSD to PCM conversion. Pad short frames with silence.
+        for (i = 0; i < n; i++)
+            dsd[i * 4] = avpkt->data[1 + i];
+        for (; i < total; i++)
+            dsd[i * 4] = 0x69;
         goto dsd;
     }
 
-- 
2.52.0


From b52ea2596805ac3a389c5c81d78fc74e7018fe97 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 11:24:12 +0200
Subject: [PATCH 02/14] avutil/samplefmt: add AV_SAMPLE_FMT_DSD
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

DSD (Direct Stream Digital) is one-bit sigma-delta modulated audio,
found on Super Audio CDs and in DSF/DSDIFF files. libavcodec has
several decoders producing it internally (dsd_*, dst, wavpack DSD),
each converting to float PCM as part of decoding, which makes the
bitstream itself inaccessible to API users.

Add a dedicated sample format so raw DSD can be carried through the
pipeline, e.g. for bit-perfect pass-through to DSD capable DACs.

The format has no planar variant, altform references itself.

Signed-off-by: Kacper Michajłow <[email protected]>
---
 doc/APIchanges        |  3 +++
 libavutil/samplefmt.c | 12 +++++++++---
 libavutil/samplefmt.h |  8 ++++++++
 libavutil/version.h   |  2 +-
 4 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index 7907af9290..147d0d0b79 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2026-06-23.
 
 API changes, most recent first:
 
+2026-08-xx - xxxxxxxxxx - lavu 61.6.100 - samplefmt.h
+  Add AV_SAMPLE_FMT_DSD.
+
 2026-07-04 - xxxxxxxxxx - lavc 63.7.100 - codec_id.h
   Add AV_CODEC_ID_PCM_DVDA.
 
diff --git a/libavutil/samplefmt.c b/libavutil/samplefmt.c
index e1be5f0547..663b6ca433 100644
--- a/libavutil/samplefmt.c
+++ b/libavutil/samplefmt.c
@@ -46,6 +46,7 @@ static const SampleFmtInfo sample_fmt_info[AV_SAMPLE_FMT_NB] = {
     [AV_SAMPLE_FMT_S64P] = { .name = "s64p", .bits = 64, .planar = 1, .altform = AV_SAMPLE_FMT_S64  },
     [AV_SAMPLE_FMT_FLTP] = { .name = "fltp", .bits = 32, .planar = 1, .altform = AV_SAMPLE_FMT_FLT  },
     [AV_SAMPLE_FMT_DBLP] = { .name = "dblp", .bits = 64, .planar = 1, .altform = AV_SAMPLE_FMT_DBL  },
+    [AV_SAMPLE_FMT_DSD]  = { .name =  "dsd", .bits =  8, .planar = 0, .altform = AV_SAMPLE_FMT_DSD  },
 };
 
 const char *av_get_sample_fmt_name(enum AVSampleFormat sample_fmt)
@@ -250,9 +251,14 @@ int av_samples_set_silence(uint8_t * const *audio_data, int offset, int nb_sampl
     int planes      = planar ? nb_channels : 1;
     int block_align = av_get_bytes_per_sample(sample_fmt) * (planar ? 1 : nb_channels);
     int data_size   = nb_samples * block_align;
-    int fill_char   = (sample_fmt == AV_SAMPLE_FMT_U8 ||
-                     sample_fmt == AV_SAMPLE_FMT_U8P) ? 0x80 : 0x00;
-    int i;
+    int fill_char, i;
+
+    if (sample_fmt == AV_SAMPLE_FMT_U8 || sample_fmt == AV_SAMPLE_FMT_U8P)
+        fill_char = 0x80;
+    else if (sample_fmt == AV_SAMPLE_FMT_DSD)
+        fill_char = 0x69; // only ultrasonic tones, filtered out on playback
+    else
+        fill_char = 0x00;
 
     offset *= block_align;
 
diff --git a/libavutil/samplefmt.h b/libavutil/samplefmt.h
index 6e55d71140..8240d18eff 100644
--- a/libavutil/samplefmt.h
+++ b/libavutil/samplefmt.h
@@ -68,6 +68,14 @@ enum AVSampleFormat {
     AV_SAMPLE_FMT_S64,         ///< signed 64 bits
     AV_SAMPLE_FMT_S64P,        ///< signed 64 bits, planar
 
+    /**
+     * DSD (Direct Stream Digital) bitstream, interleaved. Each byte
+     * carries 8 consecutive one-bit samples, most significant bit first.
+     * One sample in the API sense is one such byte, so the sample rate
+     * is 1/8th of the DSD bit rate.
+     */
+    AV_SAMPLE_FMT_DSD,
+
     AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
 };
 
diff --git a/libavutil/version.h b/libavutil/version.h
index d5bf20cf89..94b5e920b9 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  61
-#define LIBAVUTIL_VERSION_MINOR   5
+#define LIBAVUTIL_VERSION_MINOR   6
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
-- 
2.52.0


From a1d580c5333af0775877dd982a6c838130b1338c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 11:24:49 +0200
Subject: [PATCH 03/14] swresample: support AV_SAMPLE_FMT_DSD input
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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 +-
 libswresample/dsd2pcm.c      | 130 +++++++++++++++++++++++++++++++++++
 libswresample/dsd2pcm.h      |  56 +++++++++++++++
 libswresample/reverse.c      |   1 +
 libswresample/swresample.c   |  36 ++++++++--
 libswresample/version.h      |   2 +-
 8 files changed, 238 insertions(+), 12 deletions(-)
 create mode 100644 libswresample/dsd2pcm.c
 create mode 100644 libswresample/dsd2pcm.h
 create mode 100644 libswresample/reverse.c

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/libswresample/dsd2pcm.c b/libswresample/dsd2pcm.c
new file mode 100644
index 0000000000..adf093ebfa
--- /dev/null
+++ b/libswresample/dsd2pcm.c
@@ -0,0 +1,130 @@
+/*
+ * 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
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+#include "libavutil/attributes.h"
+#include "libavutil/reverse.h"
+#include "libavutil/thread.h"
+#include "dsd2pcm.h"
+
+#define CTABLES ((HTAPS + 7) / 8) /** number of "8 MACs" lookup tables */
+
+/*
+ * Properties of this 96-tap lowpass filter when applied on a signal
+ * with sampling rate of 44100*64 Hz:
+ *
+ * () has a delay of 17 microseconds.
+ *
+ * () flat response up to 48 kHz
+ *
+ * () if you downsample afterwards by a factor of 8, the
+ *    spectrum below 70 kHz is practically alias-free.
+ *
+ * () stopband rejection is about 160 dB
+ *
+ * The coefficient tables ("ctables") take only 6 Kibi Bytes and
+ * should fit into a modern processor's fast cache.
+ */
+
+/**
+ * The 2nd half (48 coeffs) of a 96-tap symmetric lowpass filter
+ */
+static const double htaps[HTAPS] = {
+     0.09950731974056658,    0.09562845727714668,    0.08819647126516944,
+     0.07782552527068175,    0.06534876523171299,    0.05172629311427257,
+     0.0379429484910187,     0.02490921351762261,    0.0133774746265897,
+     0.003883043418804416,  -0.003284703416210726,  -0.008080250212687497,
+    -0.01067241812471033,   -0.01139427235000863,   -0.0106813877974587,
+    -0.009007905078766049,  -0.006828859761015335,  -0.004535184322001496,
+    -0.002425035959059578,  -0.0006922187080790708,  0.0005700762133516592,
+     0.001353838005269448,   0.001713709169690937,   0.001742046839472948,
+     0.001545601648013235,   0.001226696225277855,   0.0008704322683580222,
+     0.0005381636200535649,  0.000266446345425276,   7.002968738383528e-05,
+    -5.279407053811266e-05, -0.0001140625650874684, -0.0001304796361231895,
+    -0.0001189970287491285, -9.396247155265073e-05, -6.577634378272832e-05,
+    -4.07492895872535e-05,  -2.17407957554587e-05,  -9.163058931391722e-06,
+    -2.017460145032201e-06,  1.249721855219005e-06,  2.166655190537392e-06,
+     1.930520892991082e-06,  1.319400334374195e-06,  7.410039764949091e-07,
+     3.423230509967409e-07,  1.244182214744588e-07,  3.130441005359396e-08
+};
+
+static double ctables[CTABLES][256];
+
+static av_cold void dsd2pcm_ctables_tableinit(void)
+{
+    int t, e, m, sign;
+    double acc[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)
+                acc[t] += sign * htaps[t * 8 + m];
+        }
+        for (t = 0; t < CTABLES; ++t)
+            ctables[CTABLES - 1 - t][e] = acc[t];
+    }
+}
+
+av_cold void swri_dsd2pcm_init(void)
+{
+    static AVOnce init_static_once = AV_ONCE_INIT;
+    ff_thread_once(&init_static_once, dsd2pcm_ctables_tableinit);
+}
+
+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];
+    unsigned pos, i;
+    uint8_t* p;
+    double sum;
+
+    pos = s->pos;
+
+    memcpy(buf, s->buf, sizeof(buf));
+
+    while (samples-- > 0) {
+        buf[pos] = *src;
+        src += src_stride;
+
+        p = buf + ((pos - CTABLES) & 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];
+            sum += ctables[i][a] + ctables[i][b];
+        }
+
+        *dst = (float)sum;
+        dst += dst_stride;
+
+        pos = (pos + 1) & FIFOMASK;
+    }
+
+    s->pos = pos;
+    memcpy(s->buf, buf, sizeof(buf));
+}
diff --git a/libswresample/dsd2pcm.h b/libswresample/dsd2pcm.h
new file mode 100644
index 0000000000..9da5012589
--- /dev/null
+++ b/libswresample/dsd2pcm.h
@@ -0,0 +1,56 @@
+/*
+ * 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
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#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 */
+
+#if FIFOSIZE * 8 < HTAPS * 2
+#error "FIFOSIZE too small"
+#endif
+
+/**
+ * Per-channel buffer
+ */
+typedef struct DSDContext {
+    uint8_t buf[FIFOSIZE];
+    unsigned pos;
+} DSDContext;
+
+void swri_dsd2pcm_init(void);
+
+/**
+ * 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/libswresample/reverse.c b/libswresample/reverse.c
new file mode 100644
index 0000000000..440badaf34
--- /dev/null
+++ b/libswresample/reverse.c
@@ -0,0 +1 @@
+#include "libavutil/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, \
-- 
2.52.0


From 098043ce096d5b44340c2cec9140314f30a08176 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 12:10:03 +0200
Subject: [PATCH 04/14] avcodec/dsddec: support raw DSD output
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Output the raw bitstream as AV_SAMPLE_FMT_DSD when requested. The
default float PCM output is unchanged.

Signed-off-by: Kacper Michajłow <[email protected]>
---
 libavcodec/dsddec.c  | 42 +++++++++++++++++++++++++++++++++++-------
 libavcodec/version.h |  2 +-
 2 files changed, 36 insertions(+), 8 deletions(-)

diff --git a/libavcodec/dsddec.c b/libavcodec/dsddec.c
index 2d337a7d04..503589380a 100644
--- a/libavcodec/dsddec.c
+++ b/libavcodec/dsddec.c
@@ -27,6 +27,7 @@
  */
 
 #include "libavutil/mem.h"
+#include "libavutil/reverse.h"
 
 #include "avcodec.h"
 #include "codec_internal.h"
@@ -50,6 +51,11 @@ static av_cold int decode_init(AVCodecContext *avctx)
     if (!avctx->ch_layout.nb_channels)
         return AVERROR_INVALIDDATA;
 
+    if (avctx->request_sample_fmt == AV_SAMPLE_FMT_DSD) {
+        avctx->sample_fmt = AV_SAMPLE_FMT_DSD;
+        return 0;
+    }
+
     ff_init_dsd_data();
 
     s = av_malloc_array(avctx->ch_layout.nb_channels, sizeof(*s));
@@ -81,7 +87,6 @@ static int dsd_channel(AVCodecContext *avctx, void *tdata, int j, int threadnr)
     AVFrame *frame = td->frame;
     const AVPacket *avpkt = td->avpkt;
     int src_next, src_stride;
-    float *dst = ((float **)frame->extended_data)[j];
 
     if (avctx->codec_id == AV_CODEC_ID_DSD_LSBF_PLANAR || avctx->codec_id == AV_CODEC_ID_DSD_MSBF_PLANAR) {
         src_next   = frame->nb_samples;
@@ -91,9 +96,26 @@ static int dsd_channel(AVCodecContext *avctx, void *tdata, int j, int threadnr)
         src_stride = avctx->ch_layout.nb_channels;
     }
 
-    ff_dsd2pcm_translate(&s[j], frame->nb_samples, lsbf,
-                         avpkt->data + j * src_next, src_stride,
-                         dst, 1);
+    if (avctx->sample_fmt == AV_SAMPLE_FMT_DSD) {
+        // repack to interleaved DSD MSBF
+        const uint8_t *src = avpkt->data + j * src_next;
+        uint8_t *dst = frame->data[0] + j;
+        const int channels = avctx->ch_layout.nb_channels;
+
+        if (lsbf) {
+            for (int i = 0; i < frame->nb_samples; i++)
+                dst[i * channels] = ff_reverse[src[i * src_stride]];
+        } else {
+            for (int i = 0; i < frame->nb_samples; i++)
+                dst[i * channels] = src[i * src_stride];
+        }
+    } else {
+        float *dst = ((float **)frame->extended_data)[j];
+
+        ff_dsd2pcm_translate(&s[j], frame->nb_samples, lsbf,
+                             avpkt->data + j * src_next, src_stride,
+                             dst, 1);
+    }
 
     return 0;
 }
@@ -109,9 +131,15 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
     if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
         return ret;
 
-    td.frame = frame;
-    td.avpkt = avpkt;
-    avctx->execute2(avctx, dsd_channel, &td, NULL, avctx->ch_layout.nb_channels);
+    if (avctx->sample_fmt == AV_SAMPLE_FMT_DSD &&
+        avctx->codec_id == AV_CODEC_ID_DSD_MSBF) {
+        memcpy(frame->data[0], avpkt->data,
+               frame->nb_samples * avctx->ch_layout.nb_channels);
+    } else {
+        td.frame = frame;
+        td.avpkt = avpkt;
+        avctx->execute2(avctx, dsd_channel, &td, NULL, avctx->ch_layout.nb_channels);
+    }
 
     *got_frame_ptr = 1;
     return frame->nb_samples * avctx->ch_layout.nb_channels;
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 7acb261bb3..ecb096f38b 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -30,7 +30,7 @@
 #include "version_major.h"
 
 #define LIBAVCODEC_VERSION_MINOR   7
-#define LIBAVCODEC_VERSION_MICRO 100
+#define LIBAVCODEC_VERSION_MICRO 101
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \
-- 
2.52.0


From 72b8ea02fd9ae59667cc6f93badf3a9c521b6e5c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 12:10:29 +0200
Subject: [PATCH 05/14] avcodec/dstdec: support raw DSD output
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Output the losslessly decompressed bitstream as AV_SAMPLE_FMT_DSD. The
default float output is unchanged.

Signed-off-by: Kacper Michajłow <[email protected]>
---
 libavcodec/dstdec.c  | 35 ++++++++++++++++++++++-------------
 libavcodec/version.h |  2 +-
 2 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/libavcodec/dstdec.c b/libavcodec/dstdec.c
index 1db742d193..7551453726 100644
--- a/libavcodec/dstdec.c
+++ b/libavcodec/dstdec.c
@@ -96,7 +96,8 @@ static av_cold int decode_init(AVCodecContext *avctx)
         return AVERROR_PATCHWELCOME;
     }
 
-    avctx->sample_fmt = AV_SAMPLE_FMT_FLT;
+    avctx->sample_fmt = avctx->request_sample_fmt == AV_SAMPLE_FMT_DSD
+                        ? AV_SAMPLE_FMT_DSD : AV_SAMPLE_FMT_FLT;
 
     for (i = 0; i < avctx->ch_layout.nb_channels; i++)
         memset(s->dsdctx[i].buf, 0x69, sizeof(s->dsdctx[i].buf));
@@ -248,6 +249,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
     unsigned i, ch, same_map, dst_x_bit;
     unsigned half_prob[DST_MAX_CHANNELS];
     const int channels = avctx->ch_layout.nb_channels;
+    const int bps = avctx->sample_fmt == AV_SAMPLE_FMT_DSD ? 1 : 4;
     DSTContext *s = avctx->priv_data;
     GetBitContext *gb = &s->gb;
     ArithCoder *ac = &s->ac;
@@ -273,12 +275,17 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
         skip_bits1(gb);
         if (get_bits(gb, 6))
             return AVERROR_INVALIDDATA;
-        // DSD bytes are stored in every 4th byte, as expected by the
-        // in-place DSD to PCM conversion. Pad short frames with silence.
-        for (i = 0; i < n; i++)
-            dsd[i * 4] = avpkt->data[1 + i];
-        for (; i < total; i++)
-            dsd[i * 4] = 0x69;
+        if (bps == 1) {
+            memcpy(dsd, avpkt->data + 1, n);
+            memset(dsd + n, 0x69, total - n);
+        } else {
+            // DSD bytes are stored in every 4th byte, as expected by the
+            // in-place DSD to PCM conversion. Pad short frames with silence.
+            for (i = 0; i < n; i++)
+                dsd[i * 4] = avpkt->data[1 + i];
+            for (; i < total; i++)
+                dsd[i * 4] = 0x69;
+        }
         goto dsd;
     }
 
@@ -343,7 +350,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
         return ret;
 
     memset(s->status, 0xAA, sizeof(s->status));
-    memset(dsd, 0, frame->nb_samples * 4 * channels);
+    memset(dsd, 0, frame->nb_samples * bps * channels);
 
     ac_get(ac, gb, prob_dst_x_bit(s->fsets.coeff[0][0]), &dst_x_bit);
 
@@ -371,7 +378,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
 
             ac_get(ac, gb, prob, &residual);
             v = ((predict >> 15) ^ residual) & 1;
-            dsd[((i >> 3) * channels + ch) << 2] |= v << (7 - (i & 0x7 ));
+            dsd[((i >> 3) * channels + ch) * bps] |= v << (7 - (i & 0x7 ));
 
             AV_WL64A(status + 8, (AV_RL64A(status + 8) << 1) | ((AV_RL64A(status) >> 63) & 1));
             AV_WL64A(status, (AV_RL64A(status) << 1) | v);
@@ -379,10 +386,12 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
     }
 
 dsd:
-    for (i = 0; i < channels; i++) {
-        ff_dsd2pcm_translate(&s->dsdctx[i], frame->nb_samples, 0,
-                             frame->data[0] + i * 4,
-                             channels * 4, pcm + i, channels);
+    if (avctx->sample_fmt == AV_SAMPLE_FMT_FLT) {
+        for (i = 0; i < channels; i++) {
+            ff_dsd2pcm_translate(&s->dsdctx[i], frame->nb_samples, 0,
+                                 frame->data[0] + i * 4,
+                                 channels * 4, pcm + i, channels);
+        }
     }
 
     *got_frame_ptr = 1;
diff --git a/libavcodec/version.h b/libavcodec/version.h
index ecb096f38b..2837dc86b7 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -30,7 +30,7 @@
 #include "version_major.h"
 
 #define LIBAVCODEC_VERSION_MINOR   7
-#define LIBAVCODEC_VERSION_MICRO 101
+#define LIBAVCODEC_VERSION_MICRO 102
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \
-- 
2.52.0


From 9425b8d942a7aac6e847e891fbb74b6dbbf03688 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 12:12:54 +0200
Subject: [PATCH 06/14] avcodec/wavpack: support raw DSD output

Output DSD frames as AV_SAMPLE_FMT_DSD. The default float output is
unchanged.

Raw frames carry no state between each other, so this mode does not
allocate the shared DSD context.
---
 libavcodec/version.h |  2 +-
 libavcodec/wavpack.c | 93 +++++++++++++++++++++++++++++---------------
 2 files changed, 63 insertions(+), 32 deletions(-)

diff --git a/libavcodec/version.h b/libavcodec/version.h
index 2837dc86b7..3f5ccd0db6 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -30,7 +30,7 @@
 #include "version_major.h"
 
 #define LIBAVCODEC_VERSION_MINOR   7
-#define LIBAVCODEC_VERSION_MICRO 102
+#define LIBAVCODEC_VERSION_MICRO 103
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \
diff --git a/libavcodec/wavpack.c b/libavcodec/wavpack.c
index 341315373f..868f403939 100644
--- a/libavcodec/wavpack.c
+++ b/libavcodec/wavpack.c
@@ -107,6 +107,7 @@ typedef struct WavpackContext {
     int ch_offset;
 
     Modulation modulation;
+    int dsd_raw;            ///< output the raw DSD bitstream instead of PCM
 
     DSDContext *dsdctx; ///< RefStruct reference
     ThreadProgress *curr_progress, *prev_progress; ///< RefStruct references
@@ -432,7 +433,14 @@ typedef struct {
     unsigned int byte;
 } DSDfilters;
 
-static int wv_unpack_dsd_high(WavpackFrameContext *s, uint8_t *dst_left, uint8_t *dst_right)
+static void wv_dsd_silence(uint8_t *dst, int samples, ptrdiff_t stride)
+{
+    for (int i = 0; i < samples; i++)
+        dst[i * stride] = 0x69;
+}
+
+static int wv_unpack_dsd_high(WavpackFrameContext *s, uint8_t *dst_left, uint8_t *dst_right,
+                              ptrdiff_t stride)
 {
     uint32_t checksum = 0xFFFFFFFF;
     uint8_t *dst_l = dst_left, *dst_r = dst_right;
@@ -553,12 +561,12 @@ static int wv_unpack_dsd_high(WavpackFrameContext *s, uint8_t *dst_left, uint8_t
 
         checksum += (checksum << 1) + (*dst_l = sp[0].byte & 0xff);
         sp[0].factor -= (sp[0].factor + 512) >> 10;
-        dst_l += 4;
+        dst_l += stride;
 
         if (stereo) {
             checksum += (checksum << 1) + (*dst_r = filters[1].byte & 0xff);
             filters[1].factor -= (filters[1].factor + 512) >> 10;
-            dst_r += 4;
+            dst_r += stride;
         }
     }
 
@@ -566,16 +574,17 @@ static int wv_unpack_dsd_high(WavpackFrameContext *s, uint8_t *dst_left, uint8_t
         if (s->avctx->err_recognition & AV_EF_CRCCHECK)
             return AVERROR_INVALIDDATA;
 
-        memset(dst_left, 0x69, s->samples * 4);
+        wv_dsd_silence(dst_left, s->samples, stride);
 
         if (dst_r)
-            memset(dst_right, 0x69, s->samples * 4);
+            wv_dsd_silence(dst_right, s->samples, stride);
     }
 
     return 0;
 }
 
-static int wv_unpack_dsd_fast(WavpackFrameContext *s, uint8_t *dst_left, uint8_t *dst_right)
+static int wv_unpack_dsd_fast(WavpackFrameContext *s, uint8_t *dst_left, uint8_t *dst_right,
+                              ptrdiff_t stride)
 {
     uint8_t *dst_l = dst_left, *dst_r = dst_right;
     uint8_t history_bits, max_probability;
@@ -689,18 +698,18 @@ static int wv_unpack_dsd_fast(WavpackFrameContext *s, uint8_t *dst_left, uint8_t
             if ((*dst_l = code = s->value_lookup[p0][index]))
                 low += s->summed_probabilities[p0][code-1] * mult;
 
-            dst_l += 4;
+            dst_l += stride;
         } else {
             if ((code = s->value_lookup[p0][index]))
                 low += s->summed_probabilities[p0][code-1] * mult;
 
             if (chan) {
                 *dst_r = code;
-                dst_r += 4;
+                dst_r += stride;
             }
             else {
                 *dst_l = code;
-                dst_l += 4;
+                dst_l += stride;
             }
 
             chan ^= 1;
@@ -727,16 +736,17 @@ static int wv_unpack_dsd_fast(WavpackFrameContext *s, uint8_t *dst_left, uint8_t
         if (s->avctx->err_recognition & AV_EF_CRCCHECK)
             return AVERROR_INVALIDDATA;
 
-        memset(dst_left, 0x69, s->samples * 4);
+        wv_dsd_silence(dst_left, s->samples, stride);
 
         if (dst_r)
-            memset(dst_right, 0x69, s->samples * 4);
+            wv_dsd_silence(dst_right, s->samples, stride);
     }
 
     return 0;
 }
 
-static int wv_unpack_dsd_copy(WavpackFrameContext *s, uint8_t *dst_left, uint8_t *dst_right)
+static int wv_unpack_dsd_copy(WavpackFrameContext *s, uint8_t *dst_left, uint8_t *dst_right,
+                              ptrdiff_t stride)
 {
     uint8_t *dst_l = dst_left, *dst_r = dst_right;
     int total_samples           = s->samples;
@@ -747,11 +757,11 @@ static int wv_unpack_dsd_copy(WavpackFrameContext *s, uint8_t *dst_left, uint8_t
 
     while (total_samples--) {
         checksum += (checksum << 1) + (*dst_l = bytestream2_get_byte(&s->gbyte));
-        dst_l += 4;
+        dst_l += stride;
 
         if (dst_r) {
             checksum += (checksum << 1) + (*dst_r = bytestream2_get_byte(&s->gbyte));
-            dst_r += 4;
+            dst_r += stride;
         }
     }
 
@@ -759,10 +769,10 @@ static int wv_unpack_dsd_copy(WavpackFrameContext *s, uint8_t *dst_left, uint8_t
         if (s->avctx->err_recognition & AV_EF_CRCCHECK)
             return AVERROR_INVALIDDATA;
 
-        memset(dst_left, 0x69, s->samples * 4);
+        wv_dsd_silence(dst_left, s->samples, stride);
 
         if (dst_r)
-            memset(dst_right, 0x69, s->samples * 4);
+            wv_dsd_silence(dst_right, s->samples, stride);
     }
 
     return 0;
@@ -1056,6 +1066,8 @@ static av_cold int wavpack_decode_init(AVCodecContext *avctx)
 
     s->fdec_num = 0;
 
+    s->dsd_raw = avctx->request_sample_fmt == AV_SAMPLE_FMT_DSD;
+
 #if HAVE_THREADS
     if (ff_thread_sync_ref(avctx, offsetof(WavpackContext, progress_pool)) == FF_THREAD_IS_FIRST_THREAD) {
         s->progress_pool = av_refstruct_pool_alloc_ext(sizeof(*s->curr_progress),
@@ -1094,6 +1106,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
     GetByteContext gb;
     enum AVSampleFormat sample_fmt;
     void *samples_l = NULL, *samples_r = NULL;
+    ptrdiff_t stride = 4; // the in-place DSD to PCM conversion reads at this stride
     int ret;
     int got_terms   = 0, got_weights = 0, got_samples = 0,
         got_entropy = 0, got_pcm     = 0, got_float   = 0, got_hybrid = 0;
@@ -1126,7 +1139,9 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
     }
     s->frame_flags = bytestream2_get_le32(&gb);
 
-    if (s->frame_flags & (WV_FLOAT_DATA | WV_DSD_DATA))
+    if (s->frame_flags & WV_DSD_DATA)
+        sample_fmt = wc->dsd_raw ? AV_SAMPLE_FMT_DSD : AV_SAMPLE_FMT_FLTP;
+    else if (s->frame_flags & WV_FLOAT_DATA)
         sample_fmt = AV_SAMPLE_FMT_FLTP;
     else if ((s->frame_flags & 0x03) <= 1)
         sample_fmt = AV_SAMPLE_FMT_S16P;
@@ -1145,9 +1160,10 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
     s->joint          =   s->frame_flags & WV_JOINT_STEREO;
     s->hybrid         =   s->frame_flags & WV_HYBRID_MODE;
     s->hybrid_bitrate =   s->frame_flags & WV_HYBRID_BITRATE;
-    s->post_shift     = bpp * 8 - orig_bpp + ((s->frame_flags >> 13) & 0x1f);
-    if (s->post_shift < 0 || s->post_shift > 31) {
-        return AVERROR_INVALIDDATA;
+    if (!(s->frame_flags & WV_DSD_DATA)) {
+        s->post_shift = bpp * 8 - orig_bpp + ((s->frame_flags >> 13) & 0x1f);
+        if (s->post_shift < 0 || s->post_shift > 31)
+            return AVERROR_INVALIDDATA;
     }
     s->hybrid_maxclip =  ((1LL << (orig_bpp - 1)) - 1);
     s->hybrid_minclip = ((-1UL << (orig_bpp - 1)));
@@ -1529,6 +1545,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
 
         /* clear DSD state if stream properties change */
         if ((wc->dsdctx && !got_dsd) ||
+            !wc->dsd_raw &&
             got_dsd && (new_ch_layout.nb_channels != wc->dsd_channels ||
                         av_channel_layout_compare(&new_ch_layout, &avctx->ch_layout) ||
                         new_samplerate != avctx->sample_rate)) {
@@ -1569,20 +1586,28 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
         return ((avctx->err_recognition & AV_EF_EXPLODE) || !wc->ch_offset) ? AVERROR_INVALIDDATA : 0;
     }
 
-    samples_l = frame->extended_data[wc->ch_offset];
-    if (s->stereo)
-        samples_r = frame->extended_data[wc->ch_offset + 1];
+    if (got_dsd && wc->dsd_raw) {
+        // raw DSD output is interleaved
+        stride    = avctx->ch_layout.nb_channels;
+        samples_l = frame->data[0] + wc->ch_offset;
+        if (s->stereo)
+            samples_r = (uint8_t *)samples_l + 1;
+    } else {
+        samples_l = frame->extended_data[wc->ch_offset];
+        if (s->stereo)
+            samples_r = frame->extended_data[wc->ch_offset + 1];
+    }
 
     wc->ch_offset += 1 + s->stereo;
 
     if (s->stereo_in) {
         if (got_dsd) {
             if (dsd_mode == 3) {
-                ret = wv_unpack_dsd_high(s, samples_l, samples_r);
+                ret = wv_unpack_dsd_high(s, samples_l, samples_r, stride);
             } else if (dsd_mode == 1) {
-                ret = wv_unpack_dsd_fast(s, samples_l, samples_r);
+                ret = wv_unpack_dsd_fast(s, samples_l, samples_r, stride);
             } else {
-                ret = wv_unpack_dsd_copy(s, samples_l, samples_r);
+                ret = wv_unpack_dsd_copy(s, samples_l, samples_r, stride);
             }
         } else {
             ret = wv_unpack_stereo(s, &s->gb, samples_l, samples_r, avctx->sample_fmt);
@@ -1592,11 +1617,11 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
     } else {
         if (got_dsd) {
             if (dsd_mode == 3) {
-                ret = wv_unpack_dsd_high(s, samples_l, NULL);
+                ret = wv_unpack_dsd_high(s, samples_l, NULL, stride);
             } else if (dsd_mode == 1) {
-                ret = wv_unpack_dsd_fast(s, samples_l, NULL);
+                ret = wv_unpack_dsd_fast(s, samples_l, NULL, stride);
             } else {
-                ret = wv_unpack_dsd_copy(s, samples_l, NULL);
+                ret = wv_unpack_dsd_copy(s, samples_l, NULL, stride);
             }
         } else {
             ret = wv_unpack_mono(s, &s->gb, samples_l, avctx->sample_fmt);
@@ -1604,8 +1629,14 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
         if (ret < 0)
             return ret;
 
-        if (s->stereo)
-            memcpy(samples_r, samples_l, bpp * s->samples);
+        if (s->stereo) {
+            if (got_dsd && wc->dsd_raw) {
+                for (int i = 0; i < s->samples; i++)
+                    ((uint8_t *)samples_r)[i * stride] =
+                        ((const uint8_t *)samples_l)[i * stride];
+            } else
+                memcpy(samples_r, samples_l, bpp * s->samples);
+        }
     }
 
     return 0;
-- 
2.52.0


From 621aba53690f7753097f54e7c0bfb79eadbb04c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 12:14:02 +0200
Subject: [PATCH 07/14] avcodec/dsd: add helper for DSD to PCM conversion via
 libswresample
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

libswresample now supports AV_SAMPLE_FMT_DSD input using the same
conversion as the local dsd2pcm implementation. This will be used in
following commits and replace dsd2pcm impl.

Signed-off-by: Kacper Michajłow <[email protected]>
---
 configure        |  1 +
 libavcodec/dsd.c | 30 ++++++++++++++++++++++++++++++
 libavcodec/dsd.h | 11 +++++++++++
 3 files changed, 42 insertions(+)

diff --git a/configure b/configure
index f2896b81c7..1a25e711b4 100755
--- a/configure
+++ b/configure
@@ -8368,6 +8368,7 @@ enabled zoompan_filter      && prepend avfilter_deps "swscale"
 enabled lavfi_indev         && prepend avdevice_deps "avfilter"
 
 enabled opus_decoder    && prepend avcodec_deps "swresample"
+enabled swresample      && enabled_any dsd_lsbf_decoder dsd_lsbf_planar_decoder dsd_msbf_decoder dsd_msbf_planar_decoder dst_decoder wavpack_decoder && prepend avcodec_deps "swresample"
 
 # reorder the items at var $1 to align with the items order at var $2 .
 # die if an item at $1 is not at $2 .
diff --git a/libavcodec/dsd.c b/libavcodec/dsd.c
index 1093c5e2dd..bca8b3e3a4 100644
--- a/libavcodec/dsd.c
+++ b/libavcodec/dsd.c
@@ -21,6 +21,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "config.h"
+
 #include <string.h>
 #include "libavutil/attributes.h"
 #include "libavutil/reverse.h"
@@ -132,3 +134,31 @@ void ff_dsd2pcm_translate(DSDContext* s, size_t samples, int lsbf,
     s->pos = pos;
     memcpy(s->buf, buf, sizeof(buf));
 }
+
+#if CONFIG_SWRESAMPLE
+#include "libswresample/swresample.h"
+#include "avcodec.h"
+
+av_cold int ff_dsd_to_pcm_init(AVCodecContext *avctx, struct SwrContext **swrp)
+{
+    SwrContext *swr = NULL;
+    int ret;
+
+    swr_free(swrp);
+
+    ret = swr_alloc_set_opts2(&swr, &avctx->ch_layout, avctx->sample_fmt,
+                              avctx->sample_rate, &avctx->ch_layout,
+                              AV_SAMPLE_FMT_DSD, avctx->sample_rate, 0, avctx);
+    if (ret < 0)
+        return ret;
+
+    ret = swr_init(swr);
+    if (ret < 0) {
+        swr_free(&swr);
+        return ret;
+    }
+
+    *swrp = swr;
+    return 0;
+}
+#endif
diff --git a/libavcodec/dsd.h b/libavcodec/dsd.h
index 74da74fccc..69c6c5bd43 100644
--- a/libavcodec/dsd.h
+++ b/libavcodec/dsd.h
@@ -48,4 +48,15 @@ void ff_init_dsd_data(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);
+
+struct AVCodecContext;
+struct SwrContext;
+
+/**
+ * (Re)create a libswresample context converting AV_SAMPLE_FMT_DSD to
+ * avctx->sample_fmt at the same sample rate.
+ * Only available if CONFIG_SWRESAMPLE.
+ */
+int ff_dsd_to_pcm_init(struct AVCodecContext *avctx, struct SwrContext **swrp);
+
 #endif /* AVCODEC_DSD_H */
-- 
2.52.0


From cbe632106ae65b29a4b3522052ba5760978b8fe5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 12:14:40 +0200
Subject: [PATCH 08/14] avcodec/dsddec: use libswresample for the PCM output
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Repack the input to raw DSD unconditionally and convert it to the
default AV_SAMPLE_FMT_FLTP output with libswresample instead of the
local dsd2pcm copy.

If libswresample is disabled, the PCM output is not offered and the
decoder outputs only AV_SAMPLE_FMT_DSD.

The per-channel slice threading of the float conversion is gone. The
conversion now runs in a single swr_convert() call.

Signed-off-by: Kacper Michajłow <[email protected]>
---
 libavcodec/dsddec.c | 180 +++++++++++++++++++++++---------------------
 1 file changed, 96 insertions(+), 84 deletions(-)

diff --git a/libavcodec/dsddec.c b/libavcodec/dsddec.c
index 503589380a..aa5ccf07c8 100644
--- a/libavcodec/dsddec.c
+++ b/libavcodec/dsddec.c
@@ -1,7 +1,5 @@
 /*
  * Direct Stream Digital (DSD) decoder
- * based on BSD licensed dsd2pcm by Sebastian Gesemann
- * Copyright (c) 2009, 2011 Sebastian Gesemann. All rights reserved.
  * Copyright (c) 2014 Peter Ross
  *
  * This file is part of FFmpeg.
@@ -26,6 +24,11 @@
  * Direct Stream Digital (DSD) decoder
  */
 
+#include "config.h"
+
+#include <string.h>
+
+#include "libavutil/avassert.h"
 #include "libavutil/mem.h"
 #include "libavutil/reverse.h"
 
@@ -34,115 +37,122 @@
 #include "decode.h"
 #include "dsd.h"
 
-#define DSD_SILENCE 0x69
-#define DSD_SILENCE_REVERSED 0x96
-/* 0x69 = 01101001
- * This pattern "on repeat" makes a low energy 352.8 kHz tone
- * and a high energy 1.0584 MHz tone which should be filtered
- * out completely by any playback system --> silence
- */
+#if CONFIG_SWRESAMPLE
+#include "libswresample/swresample.h"
+
+typedef struct DSDDecContext {
+    struct SwrContext *swr;
+    uint8_t *scratch;
+    unsigned scratch_size;
+} DSDDecContext;
+
+#define PRIV_DATA_SIZE sizeof(DSDDecContext)
+#else
+#define PRIV_DATA_SIZE 0
+#endif
 
 static av_cold int decode_init(AVCodecContext *avctx)
 {
-    DSDContext * s;
-    int i;
-    uint8_t silence;
-
     if (!avctx->ch_layout.nb_channels)
         return AVERROR_INVALIDDATA;
 
-    if (avctx->request_sample_fmt == AV_SAMPLE_FMT_DSD) {
-        avctx->sample_fmt = AV_SAMPLE_FMT_DSD;
-        return 0;
+    avctx->sample_fmt = AV_SAMPLE_FMT_DSD;
+
+#if CONFIG_SWRESAMPLE
+    if (avctx->request_sample_fmt != AV_SAMPLE_FMT_DSD) {
+        DSDDecContext *s = avctx->priv_data;
+        int ret;
+
+        avctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
+        ret = ff_dsd_to_pcm_init(avctx, &s->swr);
+        if (ret < 0)
+            return ret;
     }
+#endif
 
-    ff_init_dsd_data();
-
-    s = av_malloc_array(avctx->ch_layout.nb_channels, sizeof(*s));
-    if (!s)
-        return AVERROR(ENOMEM);
-
-    silence = avctx->codec_id == AV_CODEC_ID_DSD_LSBF_PLANAR ||
-              avctx->codec_id == AV_CODEC_ID_DSD_LSBF ? DSD_SILENCE_REVERSED : DSD_SILENCE;
-    for (i = 0; i < avctx->ch_layout.nb_channels; i++) {
-        s[i].pos = 0;
-        memset(s[i].buf, silence, sizeof(s[i].buf));
-    }
-
-    avctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
-    avctx->priv_data  = s;
     return 0;
 }
 
-typedef struct ThreadData {
-    AVFrame *frame;
-    const AVPacket *avpkt;
-} ThreadData;
-
-static int dsd_channel(AVCodecContext *avctx, void *tdata, int j, int threadnr)
+static av_cold int decode_close(AVCodecContext *avctx)
 {
-    int lsbf = avctx->codec_id == AV_CODEC_ID_DSD_LSBF || avctx->codec_id == AV_CODEC_ID_DSD_LSBF_PLANAR;
-    DSDContext *s = avctx->priv_data;
-    ThreadData *td = tdata;
-    AVFrame *frame = td->frame;
-    const AVPacket *avpkt = td->avpkt;
-    int src_next, src_stride;
-
-    if (avctx->codec_id == AV_CODEC_ID_DSD_LSBF_PLANAR || avctx->codec_id == AV_CODEC_ID_DSD_MSBF_PLANAR) {
-        src_next   = frame->nb_samples;
-        src_stride = 1;
-    } else {
-        src_next   = 1;
-        src_stride = avctx->ch_layout.nb_channels;
-    }
-
-    if (avctx->sample_fmt == AV_SAMPLE_FMT_DSD) {
-        // repack to interleaved DSD MSBF
-        const uint8_t *src = avpkt->data + j * src_next;
-        uint8_t *dst = frame->data[0] + j;
-        const int channels = avctx->ch_layout.nb_channels;
-
-        if (lsbf) {
-            for (int i = 0; i < frame->nb_samples; i++)
-                dst[i * channels] = ff_reverse[src[i * src_stride]];
-        } else {
-            for (int i = 0; i < frame->nb_samples; i++)
-                dst[i * channels] = src[i * src_stride];
-        }
-    } else {
-        float *dst = ((float **)frame->extended_data)[j];
-
-        ff_dsd2pcm_translate(&s[j], frame->nb_samples, lsbf,
-                             avpkt->data + j * src_next, src_stride,
-                             dst, 1);
-    }
+#if CONFIG_SWRESAMPLE
+    DSDDecContext *s = avctx->priv_data;
 
+    swr_free(&s->swr);
+    av_freep(&s->scratch);
+#endif
     return 0;
 }
 
+// repack the input to interleaved DSD bytes, most significant bit first
+static void repack(AVCodecContext *avctx, uint8_t *dst, const uint8_t *src,
+                   int nb_samples)
+{
+    const int channels = avctx->ch_layout.nb_channels;
+
+    switch (avctx->codec_id) {
+    case AV_CODEC_ID_DSD_MSBF:
+        memcpy(dst, src, nb_samples * channels);
+        break;
+    case AV_CODEC_ID_DSD_LSBF:
+        for (int i = 0; i < nb_samples * channels; i++)
+            dst[i] = ff_reverse[src[i]];
+        break;
+    case AV_CODEC_ID_DSD_MSBF_PLANAR:
+        for (int ch = 0; ch < channels; ch++) {
+            const uint8_t *plane = src + ch * nb_samples;
+            for (int i = 0; i < nb_samples; i++)
+                dst[i * channels + ch] = plane[i];
+        }
+        break;
+    case AV_CODEC_ID_DSD_LSBF_PLANAR:
+        for (int ch = 0; ch < channels; ch++) {
+            const uint8_t *plane = src + ch * nb_samples;
+            for (int i = 0; i < nb_samples; i++)
+                dst[i * channels + ch] = ff_reverse[plane[i]];
+        }
+        break;
+    default:
+        av_assert1(0);
+    }
+}
+
 static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
                         int *got_frame_ptr, AVPacket *avpkt)
 {
-    ThreadData td;
+    const int channels = avctx->ch_layout.nb_channels;
     int ret;
 
-    frame->nb_samples = avpkt->size / avctx->ch_layout.nb_channels;
+    frame->nb_samples = avpkt->size / channels;
 
     if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
         return ret;
 
-    if (avctx->sample_fmt == AV_SAMPLE_FMT_DSD &&
-        avctx->codec_id == AV_CODEC_ID_DSD_MSBF) {
-        memcpy(frame->data[0], avpkt->data,
-               frame->nb_samples * avctx->ch_layout.nb_channels);
-    } else {
-        td.frame = frame;
-        td.avpkt = avpkt;
-        avctx->execute2(avctx, dsd_channel, &td, NULL, avctx->ch_layout.nb_channels);
+#if CONFIG_SWRESAMPLE
+    DSDDecContext *s = avctx->priv_data;
+    if (s->swr) {
+        av_fast_malloc(&s->scratch, &s->scratch_size,
+                       frame->nb_samples * channels);
+        if (!s->scratch)
+            return AVERROR(ENOMEM);
+
+        repack(avctx, s->scratch, avpkt->data, frame->nb_samples);
+
+        ret = swr_convert(s->swr, frame->extended_data, frame->nb_samples,
+                          (const uint8_t *const []){ s->scratch },
+                          frame->nb_samples);
+        if (ret != frame->nb_samples)
+            return ret < 0 ? ret : AVERROR_BUG;
+
+        *got_frame_ptr = 1;
+        return frame->nb_samples * channels;
     }
+#endif
+
+    repack(avctx, frame->data[0], avpkt->data, frame->nb_samples);
 
     *got_frame_ptr = 1;
-    return frame->nb_samples * avctx->ch_layout.nb_channels;
+    return frame->nb_samples * channels;
 }
 
 #define DSD_DECODER(id_, name_, long_name_) \
@@ -151,9 +161,11 @@ const FFCodec ff_ ## name_ ## _decoder = { \
     CODEC_LONG_NAME(long_name_), \
     .p.type       = AVMEDIA_TYPE_AUDIO, \
     .p.id         = AV_CODEC_ID_##id_, \
+    .priv_data_size = PRIV_DATA_SIZE, \
     .init         = decode_init, \
+    .close        = decode_close, \
     FF_CODEC_DECODE_CB(decode_frame), \
-    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS, \
+    .p.capabilities = AV_CODEC_CAP_DR1, \
 };
 
 DSD_DECODER(DSD_LSBF, dsd_lsbf, "DSD (Direct Stream Digital), least significant bit first")
-- 
2.52.0


From 38d6813c0d970a35bc0f74952dcd57ac447e00b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 12:14:55 +0200
Subject: [PATCH 09/14] avcodec/dstdec: use libswresample for the PCM output
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Decode into packed DSD bytes unconditionally and convert to the default
AV_SAMPLE_FMT_FLT output with libswresample instead of the local
dsd2pcm copy.

If libswresample is disabled, the PCM output is not offered and the
decoder outputs only AV_SAMPLE_FMT_DSD.

Signed-off-by: Kacper Michajłow <[email protected]>
---
 libavcodec/dstdec.c  | 93 ++++++++++++++++++++++++++++----------------
 tests/fate/audio.mak |  2 +-
 2 files changed, 61 insertions(+), 34 deletions(-)

diff --git a/libavcodec/dstdec.c b/libavcodec/dstdec.c
index 7551453726..eb030560b8 100644
--- a/libavcodec/dstdec.c
+++ b/libavcodec/dstdec.c
@@ -25,7 +25,10 @@
  * ISO/IEC 14496-3 Part 3 Subpart 10: Technical description of lossless coding of oversampled audio
  */
 
+#include "config.h"
+
 #include "libavutil/intreadwrite.h"
+#include "libavutil/mem.h"
 #include "libavutil/mem_internal.h"
 #include "libavutil/reverse.h"
 #include "codec_internal.h"
@@ -35,6 +38,10 @@
 #include "golomb.h"
 #include "dsd.h"
 
+#if CONFIG_SWRESAMPLE
+#include "libswresample/swresample.h"
+#endif
+
 #define DST_MAX_CHANNELS 6
 #define DST_MAX_ELEMENTS (2 * DST_MAX_CHANNELS)
 
@@ -73,14 +80,15 @@ typedef struct DSTContext {
     Table fsets, probs;
     DECLARE_ALIGNED(16, uint8_t, status)[DST_MAX_CHANNELS][16];
     DECLARE_ALIGNED(16, int16_t, filter)[DST_MAX_ELEMENTS][16][256];
-    DSDContext dsdctx[DST_MAX_CHANNELS];
+#if CONFIG_SWRESAMPLE
+    struct SwrContext *swr;
+    uint8_t *scratch;
+    unsigned scratch_size;
+#endif
 } DSTContext;
 
 static av_cold int decode_init(AVCodecContext *avctx)
 {
-    DSTContext *s = avctx->priv_data;
-    int i;
-
     if (avctx->ch_layout.nb_channels > DST_MAX_CHANNELS) {
         avpriv_request_sample(avctx, "Channel count %d", avctx->ch_layout.nb_channels);
         return AVERROR_PATCHWELCOME;
@@ -96,17 +104,34 @@ static av_cold int decode_init(AVCodecContext *avctx)
         return AVERROR_PATCHWELCOME;
     }
 
-    avctx->sample_fmt = avctx->request_sample_fmt == AV_SAMPLE_FMT_DSD
-                        ? AV_SAMPLE_FMT_DSD : AV_SAMPLE_FMT_FLT;
+    avctx->sample_fmt = AV_SAMPLE_FMT_DSD;
 
-    for (i = 0; i < avctx->ch_layout.nb_channels; i++)
-        memset(s->dsdctx[i].buf, 0x69, sizeof(s->dsdctx[i].buf));
+#if CONFIG_SWRESAMPLE
+    if (avctx->request_sample_fmt != AV_SAMPLE_FMT_DSD) {
+        DSTContext *s = avctx->priv_data;
+        int ret;
 
-    ff_init_dsd_data();
+        avctx->sample_fmt = AV_SAMPLE_FMT_FLT;
+        ret = ff_dsd_to_pcm_init(avctx, &s->swr);
+        if (ret < 0)
+            return ret;
+    }
+#endif
 
     return 0;
 }
 
+static av_cold int decode_close(AVCodecContext *avctx)
+{
+#if CONFIG_SWRESAMPLE
+    DSTContext *s = avctx->priv_data;
+
+    swr_free(&s->swr);
+    av_freep(&s->scratch);
+#endif
+    return 0;
+}
+
 static int read_map(GetBitContext *gb, Table *t, unsigned int map[DST_MAX_CHANNELS], int channels)
 {
     int ch;
@@ -249,12 +274,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
     unsigned i, ch, same_map, dst_x_bit;
     unsigned half_prob[DST_MAX_CHANNELS];
     const int channels = avctx->ch_layout.nb_channels;
-    const int bps = avctx->sample_fmt == AV_SAMPLE_FMT_DSD ? 1 : 4;
     DSTContext *s = avctx->priv_data;
     GetBitContext *gb = &s->gb;
     ArithCoder *ac = &s->ac;
     uint8_t *dsd;
-    float *pcm;
     int ret;
 
     if (avpkt->size <= 1)
@@ -264,7 +287,16 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
     if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
         return ret;
     dsd = frame->data[0];
-    pcm = (float *)frame->data[0];
+
+#if CONFIG_SWRESAMPLE
+    if (s->swr) {
+        av_fast_malloc(&s->scratch, &s->scratch_size,
+                       frame->nb_samples * channels);
+        if (!s->scratch)
+            return AVERROR(ENOMEM);
+        dsd = s->scratch;
+    }
+#endif
 
     if ((ret = init_get_bits8(gb, avpkt->data, avpkt->size)) < 0)
         return ret;
@@ -275,18 +307,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
         skip_bits1(gb);
         if (get_bits(gb, 6))
             return AVERROR_INVALIDDATA;
-        if (bps == 1) {
-            memcpy(dsd, avpkt->data + 1, n);
-            memset(dsd + n, 0x69, total - n);
-        } else {
-            // DSD bytes are stored in every 4th byte, as expected by the
-            // in-place DSD to PCM conversion. Pad short frames with silence.
-            for (i = 0; i < n; i++)
-                dsd[i * 4] = avpkt->data[1 + i];
-            for (; i < total; i++)
-                dsd[i * 4] = 0x69;
-        }
-        goto dsd;
+        // pad short frames with silence
+        memcpy(dsd, avpkt->data + 1, n);
+        memset(dsd + n, 0x69, total - n);
+        goto done;
     }
 
     /* Segmentation (10.4, 10.5, 10.6) */
@@ -350,7 +374,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
         return ret;
 
     memset(s->status, 0xAA, sizeof(s->status));
-    memset(dsd, 0, frame->nb_samples * bps * channels);
+    memset(dsd, 0, frame->nb_samples * channels);
 
     ac_get(ac, gb, prob_dst_x_bit(s->fsets.coeff[0][0]), &dst_x_bit);
 
@@ -378,21 +402,23 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
 
             ac_get(ac, gb, prob, &residual);
             v = ((predict >> 15) ^ residual) & 1;
-            dsd[((i >> 3) * channels + ch) * bps] |= v << (7 - (i & 0x7 ));
+            dsd[(i >> 3) * channels + ch] |= v << (7 - (i & 0x7 ));
 
             AV_WL64A(status + 8, (AV_RL64A(status + 8) << 1) | ((AV_RL64A(status) >> 63) & 1));
             AV_WL64A(status, (AV_RL64A(status) << 1) | v);
         }
     }
 
-dsd:
-    if (avctx->sample_fmt == AV_SAMPLE_FMT_FLT) {
-        for (i = 0; i < channels; i++) {
-            ff_dsd2pcm_translate(&s->dsdctx[i], frame->nb_samples, 0,
-                                 frame->data[0] + i * 4,
-                                 channels * 4, pcm + i, channels);
-        }
+done:
+#if CONFIG_SWRESAMPLE
+    if (s->swr) {
+        ret = swr_convert(s->swr, &frame->data[0], frame->nb_samples,
+                          (const uint8_t *const []){ s->scratch },
+                          frame->nb_samples);
+        if (ret != frame->nb_samples)
+            return ret < 0 ? ret : AVERROR_BUG;
     }
+#endif
 
     *got_frame_ptr = 1;
 
@@ -406,6 +432,7 @@ const FFCodec ff_dst_decoder = {
     .p.id           = AV_CODEC_ID_DST,
     .priv_data_size = sizeof(DSTContext),
     .init           = decode_init,
+    .close          = decode_close,
     FF_CODEC_DECODE_CB(decode_frame),
     .p.capabilities = AV_CODEC_CAP_DR1,
 };
diff --git a/tests/fate/audio.mak b/tests/fate/audio.mak
index c21578947a..c14365942f 100644
--- a/tests/fate/audio.mak
+++ b/tests/fate/audio.mak
@@ -37,7 +37,7 @@ fate-dss-lp: CMD = framecrc -i $(TARGET_SAMPLES)/dss/lp.dss -frames 30 -af aresa
 FATE_SAMPLES_AUDIO-$(call FRAMECRC, DSS, DSS_SP) += fate-dss-sp
 fate-dss-sp: CMD = framecrc -i $(TARGET_SAMPLES)/dss/sp.dss -frames 30
 
-FATE_SAMPLES_AUDIO-$(call PCM, DSF, DST, ARESAMPLE_FILTER) += fate-dsf-dst
+FATE_SAMPLES_AUDIO-$(call PCM, DSF, DST, ARESAMPLE_FILTER SWRESAMPLE) += fate-dsf-dst
 fate-dsf-dst: CMD = pcm -i $(TARGET_SAMPLES)/dst/dst-64fs44-2ch.dff
 fate-dsf-dst: CMP = oneoff
 fate-dsf-dst: REF = $(SAMPLES)/dst/dst-64fs44-2ch.pcm
-- 
2.52.0


From c2b90bda7419e5b44aa9a4edbb0f23728a58e637 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 12:14:55 +0200
Subject: [PATCH 10/14] avcodec/wavpack: use libswresample for the PCM output
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Unpack DSD frames into an interleaved scratch buffer and convert to
the default AV_SAMPLE_FMT_FLTP output with libswresample instead of
the in-place local dsd2pcm conversion.

The SwrContext carries the DSD to PCM filter state across frames and
replaces the DSDContext array as the RefStruct-shared state for frame
threading.

If libswresample is disabled, the PCM output is not offered and the
decoder outputs only AV_SAMPLE_FMT_DSD. The per-channel slice
threading of the float conversion is gone, the conversion now runs in
a single swr_convert() call.

Signed-off-by: Kacper Michajłow <[email protected]>
---
 libavcodec/wavpack.c   | 165 ++++++++++++++++++++++++++---------------
 tests/fate/wavpack.mak |   2 +-
 2 files changed, 107 insertions(+), 60 deletions(-)

diff --git a/libavcodec/wavpack.c b/libavcodec/wavpack.c
index 868f403939..606cf07af2 100644
--- a/libavcodec/wavpack.c
+++ b/libavcodec/wavpack.c
@@ -20,6 +20,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "config.h"
+
 #include "libavutil/channel_layout.h"
 #include "libavutil/mem.h"
 
@@ -35,6 +37,10 @@
 #include "wavpack.h"
 #include "dsd.h"
 
+#if CONFIG_SWRESAMPLE
+#include "libswresample/swresample.h"
+#endif
+
 /**
  * @file
  * WavPack lossless audio decoder
@@ -109,12 +115,25 @@ typedef struct WavpackContext {
     Modulation modulation;
     int dsd_raw;            ///< output the raw DSD bitstream instead of PCM
 
-    DSDContext *dsdctx; ///< RefStruct reference
+#if CONFIG_SWRESAMPLE
+    struct WvDSDSwr *dsd_swr; ///< RefStruct reference, shared between threads
+    uint8_t *dsd_scratch;   ///< per-thread frame sized raw DSD buffer
+    unsigned dsd_scratch_size;
+#endif
     ThreadProgress *curr_progress, *prev_progress; ///< RefStruct references
     AVRefStructPool *progress_pool; ///< RefStruct reference
     int dsd_channels;
 } WavpackContext;
 
+#if CONFIG_SWRESAMPLE
+typedef struct WvDSDSwr {
+    struct SwrContext *swr;
+} WvDSDSwr;
+#define WV_DSD_SWR(wc) ((wc)->dsd_swr)
+#else
+#define WV_DSD_SWR(wc) (NULL)
+#endif
+
 #define LEVEL_DECAY(a)  (((a) + 0x80) >> 8)
 
 static av_always_inline unsigned get_tail(GetBitContext *gb, unsigned k)
@@ -997,33 +1016,47 @@ static av_cold int wv_alloc_frame_context(WavpackContext *c)
     return 0;
 }
 
-static int wv_dsd_reset(WavpackContext *s, int channels)
+#if CONFIG_SWRESAMPLE
+static void wv_dsd_swr_free(AVRefStructOpaque opaque, void *obj)
 {
-    int i;
+    WvDSDSwr *h = obj;
+
+    swr_free(&h->swr);
+}
+#endif
+
+static int wv_dsd_reset(AVCodecContext *avctx, int channels)
+{
+    WavpackContext *s = avctx->priv_data;
 
     s->dsd_channels = 0;
-    av_refstruct_unref(&s->dsdctx);
+#if CONFIG_SWRESAMPLE
+    av_refstruct_unref(&s->dsd_swr);
+#endif
     av_refstruct_unref(&s->curr_progress);
     av_refstruct_unref(&s->prev_progress);
 
     if (!channels)
         return 0;
 
-    if (WV_MAX_CHANNELS > SIZE_MAX / sizeof(*s->dsdctx) &&
-        channels > SIZE_MAX / sizeof(*s->dsdctx))
-        return AVERROR(EINVAL);
-
-    s->dsdctx = av_refstruct_allocz(channels * sizeof(*s->dsdctx));
-    if (!s->dsdctx)
-        return AVERROR(ENOMEM);
-    s->dsd_channels = channels;
-
-    for (i = 0; i < channels; i++)
-        memset(s->dsdctx[i].buf, 0x69, sizeof(s->dsdctx[i].buf));
-
-    ff_init_dsd_data();
+#if CONFIG_SWRESAMPLE
+    {
+        s->dsd_swr = av_refstruct_alloc_ext(sizeof(*s->dsd_swr), 0, NULL,
+                                            wv_dsd_swr_free);
+        if (!s->dsd_swr)
+            return AVERROR(ENOMEM);
 
+        int ret = ff_dsd_to_pcm_init(avctx, &s->dsd_swr->swr);
+        if (ret < 0) {
+            av_refstruct_unref(&s->dsd_swr);
+            return ret;
+        }
+        s->dsd_channels = channels;
+    }
     return 0;
+#else
+    return AVERROR_BUG;
+#endif
 }
 
 #if HAVE_THREADS
@@ -1033,7 +1066,9 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
     WavpackContext *fdst = dst->priv_data;
 
     av_refstruct_replace(&fdst->curr_progress, fsrc->curr_progress);
-    av_refstruct_replace(&fdst->dsdctx, fsrc->dsdctx);
+#if CONFIG_SWRESAMPLE
+    av_refstruct_replace(&fdst->dsd_swr, fsrc->dsd_swr);
+#endif
     fdst->dsd_channels = fsrc->dsd_channels;
 
     return 0;
@@ -1066,7 +1101,10 @@ static av_cold int wavpack_decode_init(AVCodecContext *avctx)
 
     s->fdec_num = 0;
 
+    s->dsd_raw = 1;
+#if CONFIG_SWRESAMPLE
     s->dsd_raw = avctx->request_sample_fmt == AV_SAMPLE_FMT_DSD;
+#endif
 
 #if HAVE_THREADS
     if (ff_thread_sync_ref(avctx, offsetof(WavpackContext, progress_pool)) == FF_THREAD_IS_FIRST_THREAD) {
@@ -1093,7 +1131,10 @@ static av_cold int wavpack_decode_end(AVCodecContext *avctx)
     s->fdec_num = 0;
 
     av_refstruct_pool_uninit(&s->progress_pool);
-    wv_dsd_reset(s, 0);
+    wv_dsd_reset(avctx, 0);
+#if CONFIG_SWRESAMPLE
+    av_freep(&s->dsd_scratch);
+#endif
 
     return 0;
 }
@@ -1106,7 +1147,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
     GetByteContext gb;
     enum AVSampleFormat sample_fmt;
     void *samples_l = NULL, *samples_r = NULL;
-    ptrdiff_t stride = 4; // the in-place DSD to PCM conversion reads at this stride
+    ptrdiff_t stride = 0;
     int ret;
     int got_terms   = 0, got_weights = 0, got_samples = 0,
         got_entropy = 0, got_pcm     = 0, got_float   = 0, got_hybrid = 0;
@@ -1160,10 +1201,9 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
     s->joint          =   s->frame_flags & WV_JOINT_STEREO;
     s->hybrid         =   s->frame_flags & WV_HYBRID_MODE;
     s->hybrid_bitrate =   s->frame_flags & WV_HYBRID_BITRATE;
-    if (!(s->frame_flags & WV_DSD_DATA)) {
-        s->post_shift = bpp * 8 - orig_bpp + ((s->frame_flags >> 13) & 0x1f);
-        if (s->post_shift < 0 || s->post_shift > 31)
-            return AVERROR_INVALIDDATA;
+    s->post_shift     = bpp * 8 - orig_bpp + ((s->frame_flags >> 13) & 0x1f);
+    if (s->post_shift < 0 || s->post_shift > 31) {
+        return AVERROR_INVALIDDATA;
     }
     s->hybrid_maxclip =  ((1LL << (orig_bpp - 1)) - 1);
     s->hybrid_minclip = ((-1UL << (orig_bpp - 1)));
@@ -1543,22 +1583,28 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
         }
         av_assert1(new_ch_layout.nb_channels <= WV_MAX_CHANNELS);
 
+#if CONFIG_SWRESAMPLE
         /* clear DSD state if stream properties change */
-        if ((wc->dsdctx && !got_dsd) ||
-            !wc->dsd_raw &&
-            got_dsd && (new_ch_layout.nb_channels != wc->dsd_channels ||
-                        av_channel_layout_compare(&new_ch_layout, &avctx->ch_layout) ||
-                        new_samplerate != avctx->sample_rate)) {
-            ret = wv_dsd_reset(wc, got_dsd ? new_ch_layout.nb_channels : 0);
+        int reset_dsd = !wc->dsd_raw &&
+            ((wc->dsd_swr && !got_dsd) ||
+             got_dsd && (new_ch_layout.nb_channels != wc->dsd_channels ||
+                         av_channel_layout_compare(&new_ch_layout, &avctx->ch_layout) ||
+                         new_samplerate != avctx->sample_rate));
+#endif
+        av_channel_layout_copy(&avctx->ch_layout, &new_ch_layout);
+        avctx->sample_rate         = new_samplerate;
+        avctx->sample_fmt          = sample_fmt;
+        avctx->bits_per_raw_sample = orig_bpp;
+
+#if CONFIG_SWRESAMPLE
+        if (reset_dsd) {
+            ret = wv_dsd_reset(avctx, got_dsd ? new_ch_layout.nb_channels : 0);
             if (ret < 0) {
                 av_log(avctx, AV_LOG_ERROR, "Error reinitializing the DSD context\n");
                 return ret;
             }
         }
-        av_channel_layout_copy(&avctx->ch_layout, &new_ch_layout);
-        avctx->sample_rate         = new_samplerate;
-        avctx->sample_fmt          = sample_fmt;
-        avctx->bits_per_raw_sample = orig_bpp;
+#endif
 
         /* get output buffer */
         frame->nb_samples = s->samples;
@@ -1568,7 +1614,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
 
         av_assert1(!!wc->progress_pool == !!(avctx->active_thread_type & FF_THREAD_FRAME));
         if (wc->progress_pool) {
-            if (wc->dsdctx) {
+            if (WV_DSD_SWR(wc)) {
                 av_refstruct_unref(&wc->prev_progress);
                 wc->prev_progress = av_refstruct_pool_get(wc->progress_pool);
                 if (!wc->prev_progress)
@@ -1576,7 +1622,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
                 FFSWAP(ThreadProgress*, wc->prev_progress, wc->curr_progress);
                 *new_progress = 1;
             }
-            av_assert1(!!wc->dsdctx == !!wc->curr_progress);
+            av_assert1(!!WV_DSD_SWR(wc) == !!wc->curr_progress);
             ff_thread_finish_setup(avctx);
         }
     }
@@ -1586,9 +1632,18 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
         return ((avctx->err_recognition & AV_EF_EXPLODE) || !wc->ch_offset) ? AVERROR_INVALIDDATA : 0;
     }
 
-    if (got_dsd && wc->dsd_raw) {
-        // raw DSD output is interleaved
-        stride    = avctx->ch_layout.nb_channels;
+    if (got_dsd) {
+        // DSD output is interleaved
+        stride = avctx->ch_layout.nb_channels;
+#if CONFIG_SWRESAMPLE
+        if (wc->dsd_swr) {
+            av_fast_malloc(&wc->dsd_scratch, &wc->dsd_scratch_size,
+                           (size_t)s->samples * stride);
+            if (!wc->dsd_scratch)
+                return AVERROR(ENOMEM);
+            samples_l = wc->dsd_scratch + wc->ch_offset;
+        } else
+#endif
         samples_l = frame->data[0] + wc->ch_offset;
         if (s->stereo)
             samples_r = (uint8_t *)samples_l + 1;
@@ -1630,7 +1685,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
             return ret;
 
         if (s->stereo) {
-            if (got_dsd && wc->dsd_raw) {
+            if (got_dsd) {
                 for (int i = 0; i < s->samples; i++)
                     ((uint8_t *)samples_r)[i * stride] =
                         ((const uint8_t *)samples_l)[i * stride];
@@ -1644,21 +1699,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
 
 static av_cold void wavpack_decode_flush(AVCodecContext *avctx)
 {
-    WavpackContext *s = avctx->priv_data;
-
-    wv_dsd_reset(s, 0);
-}
-
-static int dsd_channel(AVCodecContext *avctx, void *frmptr, int jobnr, int threadnr)
-{
-    const WavpackContext *s  = avctx->priv_data;
-    AVFrame *frame = frmptr;
-
-    ff_dsd2pcm_translate(&s->dsdctx[jobnr], s->samples, 0,
-        (uint8_t *)frame->extended_data[jobnr], 4,
-        (float *)frame->extended_data[jobnr], 1);
-
-    return 0;
+    wv_dsd_reset(avctx, 0);
 }
 
 static int wavpack_decode_frame(AVCodecContext *avctx, AVFrame *frame,
@@ -1670,7 +1711,7 @@ static int wavpack_decode_frame(AVCodecContext *avctx, AVFrame *frame,
     int frame_size, ret, frame_flags;
     int block = 0, new_progress = 0;
 
-    av_assert1(!s->curr_progress || s->dsdctx);
+    av_assert1(!s->curr_progress || WV_DSD_SWR(s));
 
     if (avpkt->size <= WV_HEADER_SIZE)
         return AVERROR_INVALIDDATA;
@@ -1714,13 +1755,19 @@ static int wavpack_decode_frame(AVCodecContext *avctx, AVFrame *frame,
         goto error;
     }
 
-    if (s->dsdctx) {
+#if CONFIG_SWRESAMPLE
+    if (s->dsd_swr) {
         if (s->prev_progress)
             ff_thread_progress_await(s->prev_progress, INT_MAX);
-        avctx->execute2(avctx, dsd_channel, frame, NULL, avctx->ch_layout.nb_channels);
+        ret = swr_convert(s->dsd_swr->swr, frame->extended_data, s->samples,
+                          (const uint8_t *const []){ s->dsd_scratch },
+                          s->samples);
         if (s->curr_progress)
             ff_thread_progress_report(s->curr_progress, INT_MAX);
+        if (ret != s->samples)
+            return ret < 0 ? ret : AVERROR_BUG;
     }
+#endif
 
     *got_frame_ptr = 1;
 
@@ -1748,6 +1795,6 @@ const FFCodec ff_wavpack_decoder = {
     .flush          = wavpack_decode_flush,
     UPDATE_THREAD_CONTEXT(update_thread_context),
     .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS |
-                      AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_CHANNEL_CONF,
+                      AV_CODEC_CAP_CHANNEL_CONF,
     .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
 };
diff --git a/tests/fate/wavpack.mak b/tests/fate/wavpack.mak
index d4eb345383..1a37fc5365 100644
--- a/tests/fate/wavpack.mak
+++ b/tests/fate/wavpack.mak
@@ -18,7 +18,7 @@ fate-wavpack-lossless-32bit: CMD = md5pipe -i $(TARGET_SAMPLES)/wavpack/lossless
 FATE_WAVPACK_F32 += fate-wavpack-lossless-float
 fate-wavpack-lossless-float: CMD = md5pipe -i $(TARGET_SAMPLES)/wavpack/lossless/32bit_float-partial.wv -f f32le -af aresample
 
-FATE_WAVPACK_F32 += fate-wavpack-lossless-dsd
+FATE_WAVPACK-$(call FILTERDEMDECENCMUX, ARESAMPLE, WV, WAVPACK, PCM_F32LE, PCM_F32LE, MD5_PROTOCOL SWRESAMPLE) += fate-wavpack-lossless-dsd
 fate-wavpack-lossless-dsd: CMD = md5pipe -i $(TARGET_SAMPLES)/wavpack/lossless/dsd.wv -f f32le -af aresample
 
 # lossy
-- 
2.52.0


From 6f2fa6373c3e1d6f3a277b8750b415fc20994a1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 12:16:43 +0200
Subject: [PATCH 11/14] avcodec/dsd: remove unused dsd2pcm implementation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

All decoders convert their DSD output to PCM with libswresample now.

Signed-off-by: Kacper Michajłow <[email protected]>
---
 libavcodec/dsd.c | 112 +----------------------------------------------
 libavcodec/dsd.h |  27 ------------
 2 files changed, 1 insertion(+), 138 deletions(-)

diff --git a/libavcodec/dsd.c b/libavcodec/dsd.c
index bca8b3e3a4..3d705dc7a3 100644
--- a/libavcodec/dsd.c
+++ b/libavcodec/dsd.c
@@ -1,7 +1,5 @@
 /*
  * Direct Stream Digital (DSD) decoder
- * based on BSD licensed dsd2pcm by Sebastian Gesemann
- * Copyright (c) 2009, 2011 Sebastian Gesemann. All rights reserved.
  * Copyright (c) 2014 Peter Ross
  *
  * This file is part of FFmpeg.
@@ -23,118 +21,10 @@
 
 #include "config.h"
 
-#include <string.h>
+#include "version_major.h"
 #include "libavutil/attributes.h"
-#include "libavutil/reverse.h"
-#include "libavutil/thread.h"
 #include "dsd.h"
 
-#define CTABLES ((HTAPS + 7) / 8) /** number of "8 MACs" lookup tables */
-
-/*
- * Properties of this 96-tap lowpass filter when applied on a signal
- * with sampling rate of 44100*64 Hz:
- *
- * () has a delay of 17 microseconds.
- *
- * () flat response up to 48 kHz
- *
- * () if you downsample afterwards by a factor of 8, the
- *    spectrum below 70 kHz is practically alias-free.
- *
- * () stopband rejection is about 160 dB
- *
- * The coefficient tables ("ctables") take only 6 Kibi Bytes and
- * should fit into a modern processor's fast cache.
- */
-
-/**
- * The 2nd half (48 coeffs) of a 96-tap symmetric lowpass filter
- */
-static const double htaps[HTAPS] = {
-     0.09950731974056658,    0.09562845727714668,    0.08819647126516944,
-     0.07782552527068175,    0.06534876523171299,    0.05172629311427257,
-     0.0379429484910187,     0.02490921351762261,    0.0133774746265897,
-     0.003883043418804416,  -0.003284703416210726,  -0.008080250212687497,
-    -0.01067241812471033,   -0.01139427235000863,   -0.0106813877974587,
-    -0.009007905078766049,  -0.006828859761015335,  -0.004535184322001496,
-    -0.002425035959059578,  -0.0006922187080790708,  0.0005700762133516592,
-     0.001353838005269448,   0.001713709169690937,   0.001742046839472948,
-     0.001545601648013235,   0.001226696225277855,   0.0008704322683580222,
-     0.0005381636200535649,  0.000266446345425276,   7.002968738383528e-05,
-    -5.279407053811266e-05, -0.0001140625650874684, -0.0001304796361231895,
-    -0.0001189970287491285, -9.396247155265073e-05, -6.577634378272832e-05,
-    -4.07492895872535e-05,  -2.17407957554587e-05,  -9.163058931391722e-06,
-    -2.017460145032201e-06,  1.249721855219005e-06,  2.166655190537392e-06,
-     1.930520892991082e-06,  1.319400334374195e-06,  7.410039764949091e-07,
-     3.423230509967409e-07,  1.244182214744588e-07,  3.130441005359396e-08
-};
-
-static double ctables_lsbf[CTABLES][256];
-static double ctables_msbf[CTABLES][256];
-
-static av_cold void dsd_ctables_tableinit(void)
-{
-    int t, e, m, sign;
-    double acc[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)
-                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];
-        }
-    }
-}
-
-av_cold void ff_init_dsd_data(void)
-{
-    static AVOnce init_static_once = AV_ONCE_INIT;
-    ff_thread_once(&init_static_once, dsd_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)
-{
-    uint8_t buf[FIFOSIZE];
-    unsigned pos, i;
-    uint8_t* p;
-    double sum;
-    const double (*const ctables)[256] = lsbf ? ctables_lsbf : ctables_msbf;
-
-    pos = s->pos;
-
-    memcpy(buf, s->buf, sizeof(buf));
-
-    while (samples-- > 0) {
-        buf[pos] = *src;
-        src += src_stride;
-
-        p = buf + ((pos - CTABLES) & 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];
-            sum += ctables[i][a] + ctables[i][b];
-        }
-
-        *dst = (float)sum;
-        dst += dst_stride;
-
-        pos = (pos + 1) & FIFOMASK;
-    }
-
-    s->pos = pos;
-    memcpy(s->buf, buf, sizeof(buf));
-}
-
 #if CONFIG_SWRESAMPLE
 #include "libswresample/swresample.h"
 #include "avcodec.h"
diff --git a/libavcodec/dsd.h b/libavcodec/dsd.h
index 69c6c5bd43..a65d7d7757 100644
--- a/libavcodec/dsd.h
+++ b/libavcodec/dsd.h
@@ -1,7 +1,5 @@
 /*
  * Direct Stream Digital (DSD) decoder
- * based on BSD licensed dsd2pcm by Sebastian Gesemann
- * Copyright (c) 2009, 2011 Sebastian Gesemann. All rights reserved.
  * Copyright (c) 2014 Peter Ross
  *
  * This file is part of FFmpeg.
@@ -24,31 +22,6 @@
 #ifndef AVCODEC_DSD_H
 #define AVCODEC_DSD_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 */
-
-#if FIFOSIZE * 8 < HTAPS * 2
-#error "FIFOSIZE too small"
-#endif
-
-/**
- * Per-channel buffer
- */
-typedef struct DSDContext {
-    uint8_t buf[FIFOSIZE];
-    unsigned pos;
-} DSDContext;
-
-void ff_init_dsd_data(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);
-
 struct AVCodecContext;
 struct SwrContext;
 
-- 
2.52.0


From 88fc5fb6b3fb108c3f90b7cdb4df034fdb1fdcf2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 12:30:34 +0200
Subject: [PATCH 12/14] avcodec: add a raw DSD encoder
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

AV_SAMPLE_FMT_DSD frames are already the bitstream, so the dsd_msbf
"encoder" is a plain copy of the samples into packets.

Signed-off-by: Kacper Michajłow <[email protected]>
---
 Changelog              |  1 +
 libavcodec/Makefile    |  1 +
 libavcodec/allcodecs.c |  1 +
 libavcodec/dsdenc.c    | 65 ++++++++++++++++++++++++++++++++++++++++++
 libavcodec/version.h   |  2 +-
 5 files changed, 69 insertions(+), 1 deletion(-)
 create mode 100644 libavcodec/dsdenc.c

diff --git a/Changelog b/Changelog
index 38f1e10263..3a0d1e02ba 100644
--- a/Changelog
+++ b/Changelog
@@ -10,6 +10,7 @@ version <next>:
 - latticepal filter
 - DVD-Audio LPCM decoder and demuxing support
 - AVFoundation input device selection by unique ID and USB serial number
+- DSD (dsd_msbf) encoder
 
 
 version 9.0:
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index e464811af6..b418d53b54 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -348,6 +348,7 @@ OBJS-$(CONFIG_DPX_DECODER)             += dpx.o
 OBJS-$(CONFIG_DPX_ENCODER)             += dpxenc.o
 OBJS-$(CONFIG_DSD_LSBF_DECODER)        += dsddec.o dsd.o
 OBJS-$(CONFIG_DSD_MSBF_DECODER)        += dsddec.o dsd.o
+OBJS-$(CONFIG_DSD_MSBF_ENCODER)        += dsdenc.o
 OBJS-$(CONFIG_DSD_LSBF_PLANAR_DECODER) += dsddec.o dsd.o
 OBJS-$(CONFIG_DSD_MSBF_PLANAR_DECODER) += dsddec.o dsd.o
 OBJS-$(CONFIG_DSICINAUDIO_DECODER)     += dsicinaudio.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 2d7496a20d..12aaa3c9ff 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -456,6 +456,7 @@ extern const FFCodec ff_cook_decoder;
 extern const FFCodec ff_dca_encoder;
 extern const FFCodec ff_dca_decoder;
 extern const FFCodec ff_dfpwm_encoder;
+extern const FFCodec ff_dsd_msbf_encoder;
 extern const FFCodec ff_dfpwm_decoder;
 extern const FFCodec ff_dolby_e_decoder;
 extern const FFCodec ff_dsd_lsbf_decoder;
diff --git a/libavcodec/dsdenc.c b/libavcodec/dsdenc.c
new file mode 100644
index 0000000000..1b5cca6aa2
--- /dev/null
+++ b/libavcodec/dsdenc.c
@@ -0,0 +1,65 @@
+/*
+ * Direct Stream Digital (DSD) encoder
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * DSD (Direct Stream Digital) "encoder": AV_SAMPLE_FMT_DSD is
+ * already the bitstream, so packets are a plain copy of the samples.
+ */
+
+#include <string.h>
+
+#include "avcodec.h"
+#include "codec_internal.h"
+#include "encode.h"
+
+static av_cold int dsd_encode_init(AVCodecContext *avctx)
+{
+    avctx->bits_per_coded_sample = 8;
+    avctx->block_align           = avctx->ch_layout.nb_channels;
+    avctx->bit_rate              = 8LL * avctx->block_align * avctx->sample_rate;
+    return 0;
+}
+
+static int dsd_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+                            const AVFrame *frame, int *got_packet_ptr)
+{
+    int64_t size = frame->nb_samples * (int64_t)avctx->ch_layout.nb_channels;
+    int ret;
+
+    if ((ret = ff_get_encode_buffer(avctx, avpkt, size, 0)) < 0)
+        return ret;
+
+    memcpy(avpkt->data, frame->data[0], size);
+
+    *got_packet_ptr = 1;
+    return 0;
+}
+
+const FFCodec ff_dsd_msbf_encoder = {
+    .p.name         = "dsd_msbf",
+    CODEC_LONG_NAME("DSD (Direct Stream Digital), most significant bit first"),
+    .p.type         = AVMEDIA_TYPE_AUDIO,
+    .p.id           = AV_CODEC_ID_DSD_MSBF,
+    .p.capabilities = AV_CODEC_CAP_VARIABLE_FRAME_SIZE,
+    CODEC_SAMPLEFMTS(AV_SAMPLE_FMT_DSD),
+    .init           = dsd_encode_init,
+    FF_CODEC_ENCODE_CB(dsd_encode_frame),
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 3f5ccd0db6..0353c37a2f 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -30,7 +30,7 @@
 #include "version_major.h"
 
 #define LIBAVCODEC_VERSION_MINOR   7
-#define LIBAVCODEC_VERSION_MICRO 103
+#define LIBAVCODEC_VERSION_MICRO 104
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \
-- 
2.52.0


From 31ee9f7506a7b486528fd4416a45348b1923d977 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 12:31:22 +0200
Subject: [PATCH 13/14] tests/fate: cover raw DSD decoder output
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Kacper Michajłow <[email protected]>
---
 tests/fate/audio.mak           |  8 ++++
 tests/fate/wavpack.mak         |  6 +++
 tests/ref/fate/dsf-dst-dsd     | 15 ++++++
 tests/ref/fate/wavpack-dsd     | 85 ++++++++++++++++++++++++++++++++++
 tests/ref/fate/wavpack-dsd-pcm |  1 +
 5 files changed, 115 insertions(+)
 create mode 100644 tests/ref/fate/dsf-dst-dsd
 create mode 100644 tests/ref/fate/wavpack-dsd
 create mode 100644 tests/ref/fate/wavpack-dsd-pcm

diff --git a/tests/fate/audio.mak b/tests/fate/audio.mak
index c14365942f..60d9b229ac 100644
--- a/tests/fate/audio.mak
+++ b/tests/fate/audio.mak
@@ -42,6 +42,14 @@ fate-dsf-dst: CMD = pcm -i $(TARGET_SAMPLES)/dst/dst-64fs44-2ch.dff
 fate-dsf-dst: CMP = oneoff
 fate-dsf-dst: REF = $(SAMPLES)/dst/dst-64fs44-2ch.pcm
 
+FATE_SAMPLES_AUDIO-$(call FRAMECRC, DSF, DST, DSD_MSBF_ENCODER) += fate-dsf-dst-dsd
+fate-dsf-dst-dsd: CMD = framecrc -request_sample_fmt dsd -i $(TARGET_SAMPLES)/dst/dst-64fs44-2ch.dff -c:a dsd_msbf
+
+FATE_SAMPLES_AUDIO-$(call PCM, DSF, DST, ARESAMPLE_FILTER) += fate-dsf-dst-dsd-pcm
+fate-dsf-dst-dsd-pcm: CMD = pcm -request_sample_fmt dsd -i $(TARGET_SAMPLES)/dst/dst-64fs44-2ch.dff
+fate-dsf-dst-dsd-pcm: CMP = oneoff
+fate-dsf-dst-dsd-pcm: REF = $(SAMPLES)/dst/dst-64fs44-2ch.pcm
+
 FATE_SAMPLES_AUDIO-$(call PCM, G728, G728, ARESAMPLE_FILTER) += fate-g728
 fate-g728: CMD = pcm -i $(TARGET_SAMPLES)/g728/CW3.g728
 fate-g728: REF = $(SAMPLES)/g728/OUTA3.BIN
diff --git a/tests/fate/wavpack.mak b/tests/fate/wavpack.mak
index 1a37fc5365..314b6f649b 100644
--- a/tests/fate/wavpack.mak
+++ b/tests/fate/wavpack.mak
@@ -21,6 +21,12 @@ fate-wavpack-lossless-float: CMD = md5pipe -i $(TARGET_SAMPLES)/wavpack/lossless
 FATE_WAVPACK-$(call FILTERDEMDECENCMUX, ARESAMPLE, WV, WAVPACK, PCM_F32LE, PCM_F32LE, MD5_PROTOCOL SWRESAMPLE) += fate-wavpack-lossless-dsd
 fate-wavpack-lossless-dsd: CMD = md5pipe -i $(TARGET_SAMPLES)/wavpack/lossless/dsd.wv -f f32le -af aresample
 
+FATE_WAVPACK-$(call FRAMECRC, WV, WAVPACK, DSD_MSBF_ENCODER) += fate-wavpack-dsd
+fate-wavpack-dsd: CMD = framecrc -request_sample_fmt dsd -i $(TARGET_SAMPLES)/wavpack/lossless/dsd.wv -c:a dsd_msbf
+
+FATE_WAVPACK_F32 += fate-wavpack-dsd-pcm
+fate-wavpack-dsd-pcm: CMD = md5pipe -request_sample_fmt dsd -i $(TARGET_SAMPLES)/wavpack/lossless/dsd.wv -f f32le -af aresample
+
 # lossy
 
 FATE_WAVPACK_S8 += fate-wavpack-lossy-8bit
diff --git a/tests/ref/fate/dsf-dst-dsd b/tests/ref/fate/dsf-dst-dsd
new file mode 100644
index 0000000000..bdaae87c05
--- /dev/null
+++ b/tests/ref/fate/dsf-dst-dsd
@@ -0,0 +1,15 @@
+#tb 0: 1/352800
+#media_type 0: audio
+#codec_id 0: dsd_msbf
+#sample_rate 0: 352800
+#channel_layout_name 0: stereo
+0,          0,          0,     4704,     9408, 0x64267602
+0,       4704,       4704,     4704,     9408, 0x36a99044
+0,       9408,       9408,     4704,     9408, 0xe48a6960
+0,      14112,      14112,     4704,     9408, 0xbb6574f0
+0,      18816,      18816,     4704,     9408, 0x5fea8668
+0,      23520,      23520,     4704,     9408, 0x58ed6d06
+0,      28224,      28224,     4704,     9408, 0xa3357762
+0,      32928,      32928,     4704,     9408, 0x66359578
+0,      37632,      37632,     4704,     9408, 0x5eb774ce
+0,      42336,      42336,     4704,     9408, 0xb69d525e
diff --git a/tests/ref/fate/wavpack-dsd b/tests/ref/fate/wavpack-dsd
new file mode 100644
index 0000000000..71bf16ce8b
--- /dev/null
+++ b/tests/ref/fate/wavpack-dsd
@@ -0,0 +1,85 @@
+#tb 0: 1/352800
+#media_type 0: audio
+#codec_id 0: dsd_msbf
+#sample_rate 0: 352800
+#channel_layout_name 0: stereo
+0,          0,          0,    22050,    44100, 0x7b3071bf
+0,      22050,      22050,    22050,    44100, 0x3e2cf712
+0,      44100,      44100,    22050,    44100, 0x3823dcf9
+0,      66150,      66150,    22050,    44100, 0x5b1dfaca
+0,      88200,      88200,    22050,    44100, 0xdfc7ff9b
+0,     110250,     110250,    22050,    44100, 0xdcab9382
+0,     132300,     132300,    22050,    44100, 0x558abb0f
+0,     154350,     154350,    22050,    44100, 0x5e44bb16
+0,     176400,     176400,    22050,    44100, 0xdcee9ff1
+0,     198450,     198450,    22050,    44100, 0x86a10caa
+0,     220500,     220500,    22050,    44100, 0x9435073a
+0,     242550,     242550,    22050,    44100, 0xad3e0d43
+0,     264600,     264600,    22050,    44100, 0x66206caf
+0,     286650,     286650,    22050,    44100, 0xed87ac56
+0,     308700,     308700,    22050,    44100, 0x081e0753
+0,     330750,     330750,    22050,    44100, 0x925ded93
+0,     352800,     352800,    22050,    44100, 0xd060cfc2
+0,     374850,     374850,    22050,    44100, 0x7d3708b7
+0,     396900,     396900,    22050,    44100, 0x4d74f16a
+0,     418950,     418950,    22050,    44100, 0xf36cfe18
+0,     441000,     441000,    22050,    44100, 0x58fd0c45
+0,     463050,     463050,    22050,    44100, 0xf503a7bd
+0,     485100,     485100,    22050,    44100, 0x51b7ac60
+0,     507150,     507150,    22050,    44100, 0xd971fbf6
+0,     529200,     529200,    22050,    44100, 0xf011e259
+0,     551250,     551250,    22050,    44100, 0xb264b5e9
+0,     573300,     573300,    22050,    44100, 0x1895a1f8
+0,     595350,     595350,    22050,    44100, 0x5512c049
+0,     617400,     617400,    22050,    44100, 0x5e8a8804
+0,     639450,     639450,    22050,    44100, 0x3c7ee762
+0,     661500,     661500,    22050,    44100, 0x6b4bc894
+0,     683550,     683550,    22050,    44100, 0x7de5d77d
+0,     705600,     705600,    22050,    44100, 0x49b8abe7
+0,     727650,     727650,    22050,    44100, 0x8c06f4ac
+0,     749700,     749700,    22050,    44100, 0x35f69f39
+0,     771750,     771750,    22050,    44100, 0x847b1018
+0,     793800,     793800,    22050,    44100, 0xfd22a0e2
+0,     815850,     815850,    22050,    44100, 0xf0454afc
+0,     837900,     837900,    22050,    44100, 0xc0cb5db6
+0,     859950,     859950,    22050,    44100, 0x3b578c75
+0,     882000,     882000,    22050,    44100, 0xf31bb512
+0,     904050,     904050,    22050,    44100, 0x37e6caa0
+0,     926100,     926100,    22050,    44100, 0x2e56f496
+0,     948150,     948150,    22050,    44100, 0x15de2486
+0,     970200,     970200,    22050,    44100, 0xe0eccb02
+0,     992250,     992250,    22050,    44100, 0x6d5f0dde
+0,    1014300,    1014300,    22050,    44100, 0x62d768d2
+0,    1036350,    1036350,    22050,    44100, 0x375dae67
+0,    1058400,    1058400,    22050,    44100, 0x35389a32
+0,    1080450,    1080450,    22050,    44100, 0x3b94c577
+0,    1102500,    1102500,    22050,    44100, 0xc2dae059
+0,    1124550,    1124550,    22050,    44100, 0x4f5b06df
+0,    1146600,    1146600,    22050,    44100, 0x2f7f893e
+0,    1168650,    1168650,    22050,    44100, 0xe5f42ebf
+0,    1190700,    1190700,    22050,    44100, 0xdb4eac24
+0,    1212750,    1212750,    22050,    44100, 0x7147aa14
+0,    1234800,    1234800,    22050,    44100, 0x603615f7
+0,    1256850,    1256850,    22050,    44100, 0x0c07f6b5
+0,    1278900,    1278900,    22050,    44100, 0x9a9bbc91
+0,    1300950,    1300950,    22050,    44100, 0x7e54aa91
+0,    1323000,    1323000,    22050,    44100, 0x28e4d1d1
+0,    1345050,    1345050,    22050,    44100, 0xa4745a75
+0,    1367100,    1367100,    22050,    44100, 0x547f2b18
+0,    1389150,    1389150,    22050,    44100, 0xe098a81c
+0,    1411200,    1411200,    22050,    44100, 0xdb85339d
+0,    1433250,    1433250,    22050,    44100, 0xf13b1df6
+0,    1455300,    1455300,    22050,    44100, 0x7c2bdb41
+0,    1477350,    1477350,    22050,    44100, 0x332ca290
+0,    1499400,    1499400,    22050,    44100, 0x923bdd66
+0,    1521450,    1521450,    22050,    44100, 0xdc17c695
+0,    1543500,    1543500,    22050,    44100, 0xea59ed5b
+0,    1565550,    1565550,    22050,    44100, 0x5fc6e76a
+0,    1587600,    1587600,    22050,    44100, 0x161ea31f
+0,    1609650,    1609650,    22050,    44100, 0x5955f50d
+0,    1631700,    1631700,    22050,    44100, 0x6967bf3c
+0,    1653750,    1653750,    22050,    44100, 0xdbaab462
+0,    1675800,    1675800,    22050,    44100, 0x46e6d415
+0,    1697850,    1697850,    22050,    44100, 0x6c96e376
+0,    1719900,    1719900,    22050,    44100, 0x7f9ef037
+0,    1741950,    1741950,    22050,    44100, 0x39eefe1f
diff --git a/tests/ref/fate/wavpack-dsd-pcm b/tests/ref/fate/wavpack-dsd-pcm
new file mode 100644
index 0000000000..eba76df518
--- /dev/null
+++ b/tests/ref/fate/wavpack-dsd-pcm
@@ -0,0 +1 @@
+0b33207f1ec7e47333878cb8420c21ce
-- 
2.52.0


From 0e5c5cab3e8460f8b4afba76d9bdf602c42eba7c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]>
Date: Tue, 11 Aug 2026 12:16:59 +0200
Subject: [PATCH 14/14] avcodec: deprecate the PCM output of the DSD decoders

Now that AV_SAMPLE_FMT_DSD exists, the decoders will prefer this output
and PCM conversion is deprecated and will be removed. Users can use
libswresample directly.
---
 libavcodec/dsd.c           |  8 +++++++-
 libavcodec/dsd.h           |  5 +++--
 libavcodec/dsddec.c        |  8 ++++----
 libavcodec/dstdec.c        | 12 ++++++------
 libavcodec/version.h       |  4 ++--
 libavcodec/version_major.h |  1 +
 libavcodec/wavpack.c       | 26 +++++++++++++-------------
 7 files changed, 36 insertions(+), 28 deletions(-)

diff --git a/libavcodec/dsd.c b/libavcodec/dsd.c
index 3d705dc7a3..bae0db8285 100644
--- a/libavcodec/dsd.c
+++ b/libavcodec/dsd.c
@@ -25,7 +25,7 @@
 #include "libavutil/attributes.h"
 #include "dsd.h"
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
 #include "libswresample/swresample.h"
 #include "avcodec.h"
 
@@ -48,6 +48,12 @@ av_cold int ff_dsd_to_pcm_init(AVCodecContext *avctx, struct SwrContext **swrp)
         return ret;
     }
 
+    av_log(avctx, AV_LOG_WARNING,
+           "Converting DSD to PCM in the decoder is deprecated and will be "
+           "removed. Set request_sample_fmt to AV_SAMPLE_FMT_DSD to receive "
+           "the raw bitstream, and use libswresample to convert it to PCM "
+           "when needed.\n");
+
     *swrp = swr;
     return 0;
 }
diff --git a/libavcodec/dsd.h b/libavcodec/dsd.h
index a65d7d7757..dcabafe5b2 100644
--- a/libavcodec/dsd.h
+++ b/libavcodec/dsd.h
@@ -27,8 +27,9 @@ struct SwrContext;
 
 /**
  * (Re)create a libswresample context converting AV_SAMPLE_FMT_DSD to
- * avctx->sample_fmt at the same sample rate.
- * Only available if CONFIG_SWRESAMPLE.
+ * avctx->sample_fmt at the same sample rate. This is a transitional helper for
+ * the deprecated in-decoder DSD to PCM conversion.
+ * Only available if CONFIG_SWRESAMPLE && FF_API_DSD_PCM.
  */
 int ff_dsd_to_pcm_init(struct AVCodecContext *avctx, struct SwrContext **swrp);
 
diff --git a/libavcodec/dsddec.c b/libavcodec/dsddec.c
index aa5ccf07c8..c41d29d61e 100644
--- a/libavcodec/dsddec.c
+++ b/libavcodec/dsddec.c
@@ -37,7 +37,7 @@
 #include "decode.h"
 #include "dsd.h"
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
 #include "libswresample/swresample.h"
 
 typedef struct DSDDecContext {
@@ -58,7 +58,7 @@ static av_cold int decode_init(AVCodecContext *avctx)
 
     avctx->sample_fmt = AV_SAMPLE_FMT_DSD;
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     if (avctx->request_sample_fmt != AV_SAMPLE_FMT_DSD) {
         DSDDecContext *s = avctx->priv_data;
         int ret;
@@ -75,7 +75,7 @@ static av_cold int decode_init(AVCodecContext *avctx)
 
 static av_cold int decode_close(AVCodecContext *avctx)
 {
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     DSDDecContext *s = avctx->priv_data;
 
     swr_free(&s->swr);
@@ -128,7 +128,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
     if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
         return ret;
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     DSDDecContext *s = avctx->priv_data;
     if (s->swr) {
         av_fast_malloc(&s->scratch, &s->scratch_size,
diff --git a/libavcodec/dstdec.c b/libavcodec/dstdec.c
index eb030560b8..3958648617 100644
--- a/libavcodec/dstdec.c
+++ b/libavcodec/dstdec.c
@@ -38,7 +38,7 @@
 #include "golomb.h"
 #include "dsd.h"
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
 #include "libswresample/swresample.h"
 #endif
 
@@ -80,7 +80,7 @@ typedef struct DSTContext {
     Table fsets, probs;
     DECLARE_ALIGNED(16, uint8_t, status)[DST_MAX_CHANNELS][16];
     DECLARE_ALIGNED(16, int16_t, filter)[DST_MAX_ELEMENTS][16][256];
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     struct SwrContext *swr;
     uint8_t *scratch;
     unsigned scratch_size;
@@ -106,7 +106,7 @@ static av_cold int decode_init(AVCodecContext *avctx)
 
     avctx->sample_fmt = AV_SAMPLE_FMT_DSD;
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     if (avctx->request_sample_fmt != AV_SAMPLE_FMT_DSD) {
         DSTContext *s = avctx->priv_data;
         int ret;
@@ -123,7 +123,7 @@ static av_cold int decode_init(AVCodecContext *avctx)
 
 static av_cold int decode_close(AVCodecContext *avctx)
 {
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     DSTContext *s = avctx->priv_data;
 
     swr_free(&s->swr);
@@ -288,7 +288,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
         return ret;
     dsd = frame->data[0];
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     if (s->swr) {
         av_fast_malloc(&s->scratch, &s->scratch_size,
                        frame->nb_samples * channels);
@@ -410,7 +410,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
     }
 
 done:
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     if (s->swr) {
         ret = swr_convert(s->swr, &frame->data[0], frame->nb_samples,
                           (const uint8_t *const []){ s->scratch },
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 0353c37a2f..37c4c39451 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,8 +29,8 @@
 
 #include "version_major.h"
 
-#define LIBAVCODEC_VERSION_MINOR   7
-#define LIBAVCODEC_VERSION_MICRO 104
+#define LIBAVCODEC_VERSION_MINOR   8
+#define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
                                                LIBAVCODEC_VERSION_MINOR, \
diff --git a/libavcodec/version_major.h b/libavcodec/version_major.h
index 166646eb6d..f30563c117 100644
--- a/libavcodec/version_major.h
+++ b/libavcodec/version_major.h
@@ -41,5 +41,6 @@
 
 #define FF_API_INTRA_DC_PRECISION  (LIBAVCODEC_VERSION_MAJOR < 64)
 #define FF_API_MJPEG_EXTERN_HUFF   (LIBAVCODEC_VERSION_MAJOR < 64)
+#define FF_API_DSD_PCM             (LIBAVCODEC_VERSION_MAJOR < 64)
 
 #endif /* AVCODEC_VERSION_MAJOR_H */
diff --git a/libavcodec/wavpack.c b/libavcodec/wavpack.c
index 606cf07af2..4b33200ea3 100644
--- a/libavcodec/wavpack.c
+++ b/libavcodec/wavpack.c
@@ -37,7 +37,7 @@
 #include "wavpack.h"
 #include "dsd.h"
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
 #include "libswresample/swresample.h"
 #endif
 
@@ -115,7 +115,7 @@ typedef struct WavpackContext {
     Modulation modulation;
     int dsd_raw;            ///< output the raw DSD bitstream instead of PCM
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     struct WvDSDSwr *dsd_swr; ///< RefStruct reference, shared between threads
     uint8_t *dsd_scratch;   ///< per-thread frame sized raw DSD buffer
     unsigned dsd_scratch_size;
@@ -125,7 +125,7 @@ typedef struct WavpackContext {
     int dsd_channels;
 } WavpackContext;
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
 typedef struct WvDSDSwr {
     struct SwrContext *swr;
 } WvDSDSwr;
@@ -1016,7 +1016,7 @@ static av_cold int wv_alloc_frame_context(WavpackContext *c)
     return 0;
 }
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
 static void wv_dsd_swr_free(AVRefStructOpaque opaque, void *obj)
 {
     WvDSDSwr *h = obj;
@@ -1030,7 +1030,7 @@ static int wv_dsd_reset(AVCodecContext *avctx, int channels)
     WavpackContext *s = avctx->priv_data;
 
     s->dsd_channels = 0;
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     av_refstruct_unref(&s->dsd_swr);
 #endif
     av_refstruct_unref(&s->curr_progress);
@@ -1039,7 +1039,7 @@ static int wv_dsd_reset(AVCodecContext *avctx, int channels)
     if (!channels)
         return 0;
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     {
         s->dsd_swr = av_refstruct_alloc_ext(sizeof(*s->dsd_swr), 0, NULL,
                                             wv_dsd_swr_free);
@@ -1066,7 +1066,7 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
     WavpackContext *fdst = dst->priv_data;
 
     av_refstruct_replace(&fdst->curr_progress, fsrc->curr_progress);
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     av_refstruct_replace(&fdst->dsd_swr, fsrc->dsd_swr);
 #endif
     fdst->dsd_channels = fsrc->dsd_channels;
@@ -1102,7 +1102,7 @@ static av_cold int wavpack_decode_init(AVCodecContext *avctx)
     s->fdec_num = 0;
 
     s->dsd_raw = 1;
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     s->dsd_raw = avctx->request_sample_fmt == AV_SAMPLE_FMT_DSD;
 #endif
 
@@ -1132,7 +1132,7 @@ static av_cold int wavpack_decode_end(AVCodecContext *avctx)
 
     av_refstruct_pool_uninit(&s->progress_pool);
     wv_dsd_reset(avctx, 0);
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     av_freep(&s->dsd_scratch);
 #endif
 
@@ -1583,7 +1583,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
         }
         av_assert1(new_ch_layout.nb_channels <= WV_MAX_CHANNELS);
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
         /* clear DSD state if stream properties change */
         int reset_dsd = !wc->dsd_raw &&
             ((wc->dsd_swr && !got_dsd) ||
@@ -1596,7 +1596,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
         avctx->sample_fmt          = sample_fmt;
         avctx->bits_per_raw_sample = orig_bpp;
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
         if (reset_dsd) {
             ret = wv_dsd_reset(avctx, got_dsd ? new_ch_layout.nb_channels : 0);
             if (ret < 0) {
@@ -1635,7 +1635,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, AVFrame *frame, int block
     if (got_dsd) {
         // DSD output is interleaved
         stride = avctx->ch_layout.nb_channels;
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
         if (wc->dsd_swr) {
             av_fast_malloc(&wc->dsd_scratch, &wc->dsd_scratch_size,
                            (size_t)s->samples * stride);
@@ -1755,7 +1755,7 @@ static int wavpack_decode_frame(AVCodecContext *avctx, AVFrame *frame,
         goto error;
     }
 
-#if CONFIG_SWRESAMPLE
+#if CONFIG_SWRESAMPLE && FF_API_DSD_PCM
     if (s->dsd_swr) {
         if (s->prev_progress)
             ff_thread_progress_await(s->prev_progress, INT_MAX);
-- 
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.