PR #23939 opened by Kacper Michajłow (kasper93) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23939 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23939.patch
From 251bdffffc6e8f7c6eb8fc1a3a5caaa21c8379b6 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 1/2] avcodec/dstdec: fix decoding of uncompressed frames 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. --- 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 7648095dfea798830bf8019c44061bb1d90570c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Tue, 28 Jul 2026 01:42:02 +0200 Subject: [PATCH 2/2] avcodec/dstdec: add raw_dsd option to output the DSD bitstream When enabled, output the losslessly decompressed DSD bitstream as unsigned 8-bit samples (one byte per 8 DSD bits, MSB first, channels interleaved, at the usual DSD byte rate) instead of converting to float PCM. This allows bit-exact pass-through of DST compressed audio, e.g. as DSD-over-PCM (DoP) to DSD capable audio devices. By default the conversion to PCM is done as before. --- doc/decoders.texi | 18 +++++++++++++++ libavcodec/dstdec.c | 52 +++++++++++++++++++++++++++++++++----------- libavcodec/version.h | 2 +- 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/doc/decoders.texi b/doc/decoders.texi index c8177c15e7..ca65e37759 100644 --- a/doc/decoders.texi +++ b/doc/decoders.texi @@ -282,6 +282,24 @@ Loud sounds are fully compressed. Soft sounds are enhanced. @end table +@section dst + +DST (Direct Stream Transfer) audio decoder. + +Lossless DSD (Direct Stream Digital) compression, used in DSDIFF files and +on Super Audio CD. + +@subsection DST Decoder Options + +@table @option + +@item -raw_dsd @var{boolean} +Output the losslessly decompressed raw DSD bitstream as unsigned 8-bit +samples (one byte per 8 DSD bits, most significant bit first, channels +interleaved) instead of converting to PCM. + +@end table + @section flac FLAC audio decoder. diff --git a/libavcodec/dstdec.c b/libavcodec/dstdec.c index 1db742d193..5e7af0d71f 100644 --- a/libavcodec/dstdec.c +++ b/libavcodec/dstdec.c @@ -27,6 +27,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/mem_internal.h" +#include "libavutil/opt.h" #include "libavutil/reverse.h" #include "codec_internal.h" #include "decode.h" @@ -67,6 +68,7 @@ typedef struct Table { typedef struct DSTContext { AVClass *class; + int raw_dsd; GetBitContext gb; ArithCoder ac; @@ -96,7 +98,7 @@ static av_cold int decode_init(AVCodecContext *avctx) return AVERROR_PATCHWELCOME; } - avctx->sample_fmt = AV_SAMPLE_FMT_FLT; + avctx->sample_fmt = s->raw_dsd ? AV_SAMPLE_FMT_U8 : 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 +250,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_U8 ? 1 : 4; DSTContext *s = avctx->priv_data; GetBitContext *gb = &s->gb; ArithCoder *ac = &s->ac; @@ -273,12 +276,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 +351,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 +379,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 +387,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; @@ -390,11 +400,27 @@ dsd: return avpkt->size; } +#define OFFSET(x) offsetof(DSTContext, x) +#define AD AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM + +static const AVOption options[] = { + { "raw_dsd", "output raw DSD bitstream instead of decoding to PCM", + OFFSET(raw_dsd), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AD }, + { NULL } +}; + +static const AVClass dst_decoder_class = { + .class_name = "DST decoder", + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + const FFCodec ff_dst_decoder = { .p.name = "dst", CODEC_LONG_NAME("DST (Digital Stream Transfer)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_DST, + .p.priv_class = &dst_decoder_class, .priv_data_size = sizeof(DSTContext), .init = decode_init, FF_CODEC_DECODE_CB(decode_frame), diff --git a/libavcodec/version.h b/libavcodec/version.h index 39dbec0208..b633612341 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -30,7 +30,7 @@ #include "version_major.h" #define LIBAVCODEC_VERSION_MINOR 6 -#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 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
