[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]