From patchwork Sun Jul 21 23:03:14 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Bryant X-Patchwork-Id: 14022 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 57453447B80 for ; Mon, 22 Jul 2019 02:03:28 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 2F46C68A7D5; Mon, 22 Jul 2019 02:03:28 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from zoserelay01.impulse.net (zoserelay01.impulse.net [207.154.70.55]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 41067689EED for ; Mon, 22 Jul 2019 02:03:20 +0300 (EEST) Received: from localhost (localhost [127.0.0.1]) by zoserelay01.impulse.net (Postfix) with ESMTP id 09B7B21F7A60 for ; Sun, 21 Jul 2019 16:03:18 -0700 (PDT) Received: from zoserelay01.impulse.net ([127.0.0.1]) by localhost (zoserelay01.impulse.net [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id EIkC7xaCse8y for ; Sun, 21 Jul 2019 16:03:15 -0700 (PDT) Received: from localhost (localhost [127.0.0.1]) by zoserelay01.impulse.net (Postfix) with ESMTP id 4C4A521F7A61 for ; Sun, 21 Jul 2019 16:03:15 -0700 (PDT) X-Virus-Scanned: amavisd-new at zoserelay01.impulse.net Received: from zoserelay01.impulse.net ([127.0.0.1]) by localhost (zoserelay01.impulse.net [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id MPqUqLeHc0qJ for ; Sun, 21 Jul 2019 16:03:15 -0700 (PDT) Received: from [192.168.1.81] (unknown [108.208.199.113]) by zoserelay01.impulse.net (Postfix) with ESMTPA id A7C8221F7A60 for ; Sun, 21 Jul 2019 16:03:14 -0700 (PDT) To: ffmpeg-devel@ffmpeg.org From: David Bryant Message-ID: Date: Sun, 21 Jul 2019 16:03:14 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.6.1 MIME-Version: 1.0 Content-Language: en-US Subject: [FFmpeg-devel] avcodec: add a WavPack DSD decoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Hi, As I promised late last year, here is a patch to add a WavPack DSD decoder. Thanks! -David Bryant From d80f4fb59e4afec1e2c539391a7b4484f5ae8436 Mon Sep 17 00:00:00 2001 From: David Bryant Date: Sun, 21 Jul 2019 15:43:25 -0700 Subject: [PATCH] avcodec: add a WavPack DSD decoder Signed-off-by: David Bryant --- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/avcodec.h | 1 + libavcodec/codec_desc.c | 7 + libavcodec/wavpack.h | 2 + libavcodec/wavpack_dsd.c | 792 +++++++++++++++++++++++++++++++++++++++++++++++ libavformat/wvdec.c | 32 +- 7 files changed, 824 insertions(+), 12 deletions(-) create mode 100644 libavcodec/wavpack_dsd.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 3cd73fb..b94327e 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -686,6 +686,7 @@ OBJS-$(CONFIG_VPLAYER_DECODER) += textdec.o ass.o OBJS-$(CONFIG_VP9_V4L2M2M_DECODER) += v4l2_m2m_dec.o OBJS-$(CONFIG_VQA_DECODER) += vqavideo.o OBJS-$(CONFIG_WAVPACK_DECODER) += wavpack.o +OBJS-$(CONFIG_WAVPACK_DSD_DECODER) += wavpack_dsd.o dsd.o OBJS-$(CONFIG_WAVPACK_ENCODER) += wavpackenc.o OBJS-$(CONFIG_WCMV_DECODER) += wcmv.o OBJS-$(CONFIG_WEBP_DECODER) += webp.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index d2f9a39..a2f414b 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -485,6 +485,7 @@ extern AVCodec ff_vorbis_encoder; extern AVCodec ff_vorbis_decoder; extern AVCodec ff_wavpack_encoder; extern AVCodec ff_wavpack_decoder; +extern AVCodec ff_wavpack_dsd_decoder; extern AVCodec ff_wmalossless_decoder; extern AVCodec ff_wmapro_decoder; extern AVCodec ff_wmav1_encoder; diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index d234271..8d3a551 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -652,6 +652,7 @@ enum AVCodecID { AV_CODEC_ID_SBC, AV_CODEC_ID_ATRAC9, AV_CODEC_ID_HCOM, + AV_CODEC_ID_WAVPACK_DSD, /* subtitle codecs */ AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 4d033c2..bee88b8 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -2985,6 +2985,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("HCOM Audio"), .props = AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_WAVPACK_DSD, + .type = AVMEDIA_TYPE_AUDIO, + .name = "wavpack_dsd", + .long_name = NULL_IF_CONFIG_SMALL("WavPack DSD"), + .props = AV_CODEC_PROP_LOSSLESS, + }, /* subtitle codecs */ { diff --git a/libavcodec/wavpack.h b/libavcodec/wavpack.h index 6caad03..43aaac8 100644 --- a/libavcodec/wavpack.h +++ b/libavcodec/wavpack.h @@ -35,6 +35,7 @@ #define WV_FLOAT_DATA 0x00000080 #define WV_INT32_DATA 0x00000100 #define WV_FALSE_STEREO 0x40000000 +#define WV_DSD_DATA 0x80000000 #define WV_HYBRID_MODE 0x00000008 #define WV_HYBRID_SHAPE 0x00000008 @@ -77,6 +78,7 @@ enum WP_ID { WP_ID_CORR, WP_ID_EXTRABITS, WP_ID_CHANINFO, + WP_ID_DSD_DATA, WP_ID_SAMPLE_RATE = 0x27, }; diff --git a/libavcodec/wavpack_dsd.c b/libavcodec/wavpack_dsd.c new file mode 100644 index 0000000..0754314 --- /dev/null +++ b/libavcodec/wavpack_dsd.c @@ -0,0 +1,792 @@ +/* + * WavPack lossless DSD audio decoder + * Copyright (c) 2006,2011 Konstantin Shishkov + * Copyright (c) 2019 David Bryant + * + * 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 "libavutil/channel_layout.h" + +#include "avcodec.h" +#include "bytestream.h" +#include "internal.h" +#include "wavpack.h" +#include "dsd.h" + +/** + * @file + * WavPack lossless DSD audio decoder + */ + +#define DSD_BYTE_READY(low,high) (!(((low) ^ (high)) & 0xff000000)) + +#define PTABLE_BITS 8 +#define PTABLE_BINS (1<CRC) { + av_log(s->avctx, AV_LOG_ERROR, "CRC error\n"); + return AVERROR_INVALIDDATA; + } + + return 0; +} + +static void init_ptable (int *table, int rate_i, int rate_s) +{ + int value = 0x808000, rate = rate_i << 8, c, i; + + for (c = (rate + 128) >> 8; c--;) + value += (DOWN - value) >> DECAY; + + for (i = 0; i < PTABLE_BINS/2; ++i) { + table [i] = value; + table [PTABLE_BINS-1-i] = 0x100ffff - value; + + if (value > 0x010000) { + rate += (rate * rate_s + 128) >> 8; + + for (c = (rate + 64) >> 7; c--;) + value += (DOWN - value) >> DECAY; + } + } +} + +typedef struct { + int32_t value, fltr0, fltr1, fltr2, fltr3, fltr4, fltr5, fltr6, factor, byte; +} DSDfilters; + +static int wv_unpack_dsd_high(WavpackFrameContext *s, void *dst_l, void *dst_r) +{ + uint8_t *dsd_l = dst_l; + uint8_t *dsd_r = dst_r; + uint32_t crc = 0xFFFFFFFF; + int total_samples = s->samples, stereo = dst_r ? 1 : 0; + DSDfilters filters [2], *sp = filters; + int channel, rate_i, rate_s, i; + uint32_t low, high, value; + + if (bytestream2_get_bytes_left(&s->gb) < (stereo ? 20 : 13)) + return AVERROR_INVALIDDATA; + + rate_i = bytestream2_get_byte(&s->gb); + rate_s = bytestream2_get_byte(&s->gb); + + if (rate_s != RATE_S) + return AVERROR_INVALIDDATA; + + init_ptable (s->ptable, rate_i, rate_s); + + for (channel = 0; channel < stereo + 1; ++channel) { + DSDfilters *sp = filters + channel; + + sp->fltr1 = bytestream2_get_byte(&s->gb) << (PRECISION - 8); + sp->fltr2 = bytestream2_get_byte(&s->gb) << (PRECISION - 8); + sp->fltr3 = bytestream2_get_byte(&s->gb) << (PRECISION - 8); + sp->fltr4 = bytestream2_get_byte(&s->gb) << (PRECISION - 8); + sp->fltr5 = bytestream2_get_byte(&s->gb) << (PRECISION - 8); + sp->fltr6 = 0; + sp->factor = bytestream2_get_byte(&s->gb) & 0xff; + sp->factor |= (bytestream2_get_byte(&s->gb) << 8) & 0xff00; + sp->factor = (sp->factor << 16) >> 16; + } + + high = 0xffffffff; + low = 0x0; + + for (i = 4; i--;) + value = (value << 8) | bytestream2_get_byte(&s->gb); + + memset (dst_l, 0x69, total_samples * 4); + + if (stereo) + memset (dst_r, 0x69, total_samples * 4); + + while (total_samples--) { + int bitcount = 8; + + sp [0].value = sp [0].fltr1 - sp [0].fltr5 + ((sp [0].fltr6 * sp [0].factor) >> 2); + + if (stereo) + sp [1].value = sp [1].fltr1 - sp [1].fltr5 + ((sp [1].fltr6 * sp [1].factor) >> 2); + + while (bitcount--) { + int32_t *pp = s->ptable + ((sp [0].value >> (PRECISION - PRECISION_USE)) & PTABLE_MASK); + uint32_t split = low + ((high - low) >> 8) * (*pp >> 16); + + if (value <= split) { + high = split; + *pp += (UP - *pp) >> DECAY; + sp [0].fltr0 = -1; + } + else { + low = split + 1; + *pp += (DOWN - *pp) >> DECAY; + sp [0].fltr0 = 0; + } + + while (DSD_BYTE_READY (high, low) && bytestream2_get_bytes_left(&s->gb)) { + value = (value << 8) | bytestream2_get_byte(&s->gb); + high = (high << 8) | 0xff; + low <<= 8; + } + + sp [0].value += sp [0].fltr6 << 3; + sp [0].byte = (sp [0].byte << 1) | (sp [0].fltr0 & 1); + sp [0].factor += (((sp [0].value ^ sp [0].fltr0) >> 31) | 1) & + ((sp [0].value ^ (sp [0].value - (sp [0].fltr6 << 4))) >> 31); + sp [0].fltr1 += ((sp [0].fltr0 & VALUE_ONE) - sp [0].fltr1) >> 6; + sp [0].fltr2 += ((sp [0].fltr0 & VALUE_ONE) - sp [0].fltr2) >> 4; + sp [0].fltr3 += (sp [0].fltr2 - sp [0].fltr3) >> 4; + sp [0].fltr4 += (sp [0].fltr3 - sp [0].fltr4) >> 4; + sp [0].value = (sp [0].fltr4 - sp [0].fltr5) >> 4; + sp [0].fltr5 += sp [0].value; + sp [0].fltr6 += (sp [0].value - sp [0].fltr6) >> 3; + sp [0].value = sp [0].fltr1 - sp [0].fltr5 + ((sp [0].fltr6 * sp [0].factor) >> 2); + + if (!stereo) + continue; + + pp = s->ptable + ((sp [1].value >> (PRECISION - PRECISION_USE)) & PTABLE_MASK); + split = low + ((high - low) >> 8) * (*pp >> 16); + + if (value <= split) { + high = split; + *pp += (UP - *pp) >> DECAY; + sp [1].fltr0 = -1; + } + else { + low = split + 1; + *pp += (DOWN - *pp) >> DECAY; + sp [1].fltr0 = 0; + } + + while (DSD_BYTE_READY (high, low) && bytestream2_get_bytes_left(&s->gb)) { + value = (value << 8) | bytestream2_get_byte(&s->gb); + high = (high << 8) | 0xff; + low <<= 8; + } + + sp [1].value += sp [1].fltr6 << 3; + sp [1].byte = (sp [1].byte << 1) | (sp [1].fltr0 & 1); + sp [1].factor += (((sp [1].value ^ sp [1].fltr0) >> 31) | 1) & + ((sp [1].value ^ (sp [1].value - (sp [1].fltr6 << 4))) >> 31); + sp [1].fltr1 += ((sp [1].fltr0 & VALUE_ONE) - sp [1].fltr1) >> 6; + sp [1].fltr2 += ((sp [1].fltr0 & VALUE_ONE) - sp [1].fltr2) >> 4; + sp [1].fltr3 += (sp [1].fltr2 - sp [1].fltr3) >> 4; + sp [1].fltr4 += (sp [1].fltr3 - sp [1].fltr4) >> 4; + sp [1].value = (sp [1].fltr4 - sp [1].fltr5) >> 4; + sp [1].fltr5 += sp [1].value; + sp [1].fltr6 += (sp [1].value - sp [1].fltr6) >> 3; + sp [1].value = sp [1].fltr1 - sp [1].fltr5 + ((sp [1].fltr6 * sp [1].factor) >> 2); + } + + crc += (crc << 1) + (*dsd_l = sp [0].byte & 0xff); + sp [0].factor -= (sp [0].factor + 512) >> 10; + dsd_l += 4; + + if (stereo) { + crc += (crc << 1) + (*dsd_r = filters [1].byte & 0xff); + filters [1].factor -= (filters [1].factor + 512) >> 10; + dsd_r += 4; + } + } + + if ((s->avctx->err_recognition & AV_EF_CRCCHECK) && wv_check_crc(s, crc)) + return AVERROR_INVALIDDATA; + + return 0; +} + +static int wv_unpack_dsd_fast(WavpackFrameContext *s, void *dst_l, void *dst_r) +{ + uint8_t *dsd_l = dst_l; + uint8_t *dsd_r = dst_r; + unsigned char history_bits, max_probability; + int total_summed_probabilities = 0; + int total_samples = s->samples; + int history_bins, p0, p1, chan; + uint32_t crc = 0xFFFFFFFF; + uint32_t low, high, value; + int ret = 0, i; + + if (!bytestream2_get_bytes_left(&s->gb)) + return AVERROR_INVALIDDATA; + + history_bits = bytestream2_get_byte(&s->gb); + + if (!bytestream2_get_bytes_left(&s->gb) || history_bits > MAX_HISTORY_BITS) + return AVERROR_INVALIDDATA; + + history_bins = 1 << history_bits; + max_probability = bytestream2_get_byte(&s->gb); + + if (max_probability < 0xff) { + unsigned char *outptr = (unsigned char *) s->probabilities; + unsigned char *outend = outptr + sizeof (*s->probabilities) * history_bins; + + while (outptr < outend && bytestream2_get_bytes_left(&s->gb)) { + int code = bytestream2_get_byte(&s->gb); + + if (code > max_probability) { + int zcount = code - max_probability; + + while (outptr < outend && zcount--) + *outptr++ = 0; + } + else if (code) + *outptr++ = code; + else + break; + } + + if (outptr < outend || + (bytestream2_get_bytes_left(&s->gb) && bytestream2_get_byte(&s->gb))) + return AVERROR_INVALIDDATA; + } + else if (bytestream2_get_bytes_left(&s->gb) > (int) sizeof (*s->probabilities) * history_bins) + bytestream2_get_buffer(&s->gb, (uint8_t *) s->probabilities, + sizeof (*s->probabilities) * history_bins); + else + return AVERROR_INVALIDDATA; + + for (p0 = 0; p0 < history_bins; ++p0) { + int32_t sum_values; + unsigned char *vp; + + for (sum_values = i = 0; i < 256; ++i) + s->summed_probabilities [p0] [i] = sum_values += s->probabilities [p0] [i]; + + if (sum_values) { + total_summed_probabilities += sum_values; + vp = s->value_lookup [p0] = av_malloc (sum_values); + + for (i = 0; i < 256; i++) { + int c = s->probabilities [p0] [i]; + + while (c--) + *vp++ = i; + } + } + } + + if (bytestream2_get_bytes_left(&s->gb) < 4 || + total_summed_probabilities > history_bins * 1280) { + ret = AVERROR_INVALIDDATA; + goto done; + } + + for (i = 4; i--;) + value = (value << 8) | bytestream2_get_byte(&s->gb); + + chan = p0 = p1 = 0; + low = 0; high = 0xffffffff; + + memset (dst_l, 0x69, total_samples * 4); + + if (dst_r) { + memset (dst_r, 0x69, total_samples * 4); + total_samples *= 2; + } + + while (total_samples--) { + int mult, index, code; + + if (!s->summed_probabilities [p0] [255]) { + ret = AVERROR_INVALIDDATA; + goto done; + } + + mult = (high - low) / s->summed_probabilities [p0] [255]; + + if (!mult) { + if (bytestream2_get_bytes_left(&s->gb) >= 4) + for (i = 4; i--;) + value = (value << 8) | bytestream2_get_byte(&s->gb); + + low = 0; + high = 0xffffffff; + mult = high / s->summed_probabilities [p0] [255]; + + if (!mult) { + ret = AVERROR_INVALIDDATA; + goto done; + } + } + + index = (value - low) / mult; + + if (index >= s->summed_probabilities [p0] [255]) { + ret = AVERROR_INVALIDDATA; + goto done; + } + + if (!dst_r) { + if ((*dsd_l = code = s->value_lookup [p0] [index])) + low += s->summed_probabilities [p0] [code-1] * mult; + + dsd_l += 4; + } + else { + if ((code = s->value_lookup [p0] [index])) + low += s->summed_probabilities [p0] [code-1] * mult; + + if (chan) { + *dsd_r = code; + dsd_r += 4; + } + else { + *dsd_l = code; + dsd_l += 4; + } + + chan ^= 1; + } + + high = low + s->probabilities [p0] [code] * mult - 1; + crc += (crc << 1) + code; + + if (!dst_r) + p0 = code & (history_bins-1); + else { + p0 = p1; + p1 = code & (history_bins-1); + } + + while (DSD_BYTE_READY (high, low) && bytestream2_get_bytes_left(&s->gb)) { + value = (value << 8) | bytestream2_get_byte(&s->gb); + high = (high << 8) | 0xff; + low <<= 8; + } + } + +done: + for (p0 = 0; p0 < history_bins; ++p0) + if (s->value_lookup [p0]) { + av_free (s->value_lookup [p0]); + s->value_lookup [p0] = NULL; + } + + if (ret < 0) + return ret; + + if ((s->avctx->err_recognition & AV_EF_CRCCHECK) && wv_check_crc(s, crc)) + ret = AVERROR_INVALIDDATA; + + return ret; +} + +static int wv_unpack_dsd_copy(WavpackFrameContext *s, void *dst_l, void *dst_r) +{ + uint8_t *dsd_l = dst_l; + uint8_t *dsd_r = dst_r; + int total_samples = s->samples; + uint32_t crc = 0xFFFFFFFF; + + if (bytestream2_get_bytes_left (&s->gb) != total_samples * (dst_r ? 2 : 1)) + return AVERROR_INVALIDDATA; + + memset (dst_l, 0x69, total_samples * 4); + + if (dst_r) + memset (dst_r, 0x69, total_samples * 4); + + while (total_samples--) { + crc += (crc << 1) + (*dsd_l = bytestream2_get_byte(&s->gb)); + dsd_l += 4; + + if (dst_r) { + crc += (crc << 1) + (*dsd_r = bytestream2_get_byte(&s->gb)); + dsd_r += 4; + } + } + + if ((s->avctx->err_recognition & AV_EF_CRCCHECK) && wv_check_crc(s, crc)) + return AVERROR_INVALIDDATA; + + return 0; +} + +static av_cold int wv_alloc_frame_context(WavpackContext *c) +{ + if (c->fdec_num == WV_MAX_FRAME_DECODERS) + return -1; + + c->fdec[c->fdec_num] = av_mallocz(sizeof(**c->fdec)); + if (!c->fdec[c->fdec_num]) + return -1; + c->fdec_num++; + c->fdec[c->fdec_num - 1]->avctx = c->avctx; + memset(c->fdec[c->fdec_num - 1]->dsdctx[0].buf, 0x69, + sizeof(c->fdec[c->fdec_num - 1]->dsdctx[0].buf)); + memset(c->fdec[c->fdec_num - 1]->dsdctx[1].buf, 0x69, + sizeof(c->fdec[c->fdec_num - 1]->dsdctx[1].buf)); + + return 0; +} + +static av_cold int wavpack_decode_init(AVCodecContext *avctx) +{ + WavpackContext *s = avctx->priv_data; + + s->avctx = avctx; + + s->fdec_num = 0; + + ff_init_dsd_data(); + + return 0; +} + +static av_cold int wavpack_decode_end(AVCodecContext *avctx) +{ + WavpackContext *s = avctx->priv_data; + + s->fdec_num = 0; + + return 0; +} + +static int wavpack_decode_block(AVCodecContext *avctx, int block_no, + AVFrame *frame, const uint8_t *buf, int buf_size) +{ + WavpackContext *wc = avctx->priv_data; + WavpackFrameContext *s; + GetByteContext gb; + void *samples_l = NULL, *samples_r = NULL; + int ret; + int got_dsd = 0; + int id, size, ssize; + int chan = 0, chmask = 0, sample_rate = 0, rate_x = 1, dsd_mode = 0; + int frame_flags, multiblock; + + if (block_no >= wc->fdec_num && wv_alloc_frame_context(wc) < 0) { + av_log(avctx, AV_LOG_ERROR, "Error creating frame decode context\n"); + return AVERROR_INVALIDDATA; + } + + s = wc->fdec[block_no]; + if (!s) { + av_log(avctx, AV_LOG_ERROR, "Context for block %d is not present\n", + block_no); + return AVERROR_INVALIDDATA; + } + + bytestream2_init(&gb, buf, buf_size); + + s->samples = bytestream2_get_le32(&gb); + if (s->samples != wc->samples) { + av_log(avctx, AV_LOG_ERROR, "Mismatching number of samples in " + "a sequence: %d and %d\n", wc->samples, s->samples); + return AVERROR_INVALIDDATA; + } + frame_flags = bytestream2_get_le32(&gb); + multiblock = (frame_flags & WV_SINGLE_BLOCK) != WV_SINGLE_BLOCK; + + s->stereo = !(frame_flags & WV_MONO); + s->stereo_in = (frame_flags & WV_FALSE_STEREO) ? 0 : s->stereo; + s->CRC = bytestream2_get_le32(&gb); + + // parse metadata blocks + while (bytestream2_get_bytes_left(&gb)) { + id = bytestream2_get_byte(&gb); + size = bytestream2_get_byte(&gb); + if (id & WP_IDF_LONG) { + size |= (bytestream2_get_byte(&gb)) << 8; + size |= (bytestream2_get_byte(&gb)) << 16; + } + size <<= 1; // size is specified in words + ssize = size; + if (id & WP_IDF_ODD) + size--; + if (size < 0) { + av_log(avctx, AV_LOG_ERROR, + "Got incorrect block %02X with size %i\n", id, size); + break; + } + if (bytestream2_get_bytes_left(&gb) < ssize) { + av_log(avctx, AV_LOG_ERROR, + "Block size %i is out of bounds\n", size); + break; + } + switch (id & WP_IDF_MASK) { + case WP_ID_DSD_DATA: + if (size < 2) { + av_log(avctx, AV_LOG_ERROR, "Invalid DSD_DATA, size = %i\n", + size); + bytestream2_skip(&gb, ssize); + continue; + } + rate_x = 1 << bytestream2_get_byte(&gb); + dsd_mode = bytestream2_get_byte(&gb); + if (dsd_mode && dsd_mode != 1 && dsd_mode != 3) { + av_log(avctx, AV_LOG_ERROR, "Invalid DSD encoding mode: %d\n", + dsd_mode); + return AVERROR_INVALIDDATA; + } + bytestream2_init(&s->gb, gb.buffer, size-2); + bytestream2_skip(&gb, size-2); + got_dsd = 1; + break; + case WP_ID_CHANINFO: + if (size <= 1) { + av_log(avctx, AV_LOG_ERROR, + "Insufficient channel information\n"); + return AVERROR_INVALIDDATA; + } + chan = bytestream2_get_byte(&gb); + switch (size - 2) { + case 0: + chmask = bytestream2_get_byte(&gb); + break; + case 1: + chmask = bytestream2_get_le16(&gb); + break; + case 2: + chmask = bytestream2_get_le24(&gb); + break; + case 3: + chmask = bytestream2_get_le32(&gb); + break; + case 4: + size = bytestream2_get_byte(&gb); + chan |= (bytestream2_get_byte(&gb) & 0xF) << 8; + chan += 1; + if (avctx->channels != chan) + av_log(avctx, AV_LOG_WARNING, "%i channels signalled" + " instead of %i.\n", chan, avctx->channels); + chmask = bytestream2_get_le24(&gb); + break; + case 5: + size = bytestream2_get_byte(&gb); + chan |= (bytestream2_get_byte(&gb) & 0xF) << 8; + chan += 1; + if (avctx->channels != chan) + av_log(avctx, AV_LOG_WARNING, "%i channels signalled" + " instead of %i.\n", chan, avctx->channels); + chmask = bytestream2_get_le32(&gb); + break; + default: + av_log(avctx, AV_LOG_ERROR, "Invalid channel info size %d\n", + size); + chan = avctx->channels; + chmask = avctx->channel_layout; + } + break; + case WP_ID_SAMPLE_RATE: + if (size != 3) { + av_log(avctx, AV_LOG_ERROR, "Invalid custom sample rate.\n"); + return AVERROR_INVALIDDATA; + } + sample_rate = bytestream2_get_le24(&gb); + break; + default: + bytestream2_skip(&gb, size); + } + if (id & WP_IDF_ODD) + bytestream2_skip(&gb, 1); + } + + if (!got_dsd) { + av_log(avctx, AV_LOG_ERROR, "Packed samples not found\n"); + return AVERROR_INVALIDDATA; + } + + if (!wc->ch_offset) { + int sr = (frame_flags >> 23) & 0xf; + if (sr == 0xf) { + if (!sample_rate) { + av_log(avctx, AV_LOG_ERROR, "Custom sample rate missing.\n"); + return AVERROR_INVALIDDATA; + } + avctx->sample_rate = sample_rate * rate_x; + } else + avctx->sample_rate = wv_rates[sr] * rate_x; + + if (multiblock) { + if (chan) + avctx->channels = chan; + if (chmask) + avctx->channel_layout = chmask; + } else { + avctx->channels = s->stereo ? 2 : 1; + avctx->channel_layout = s->stereo ? AV_CH_LAYOUT_STEREO : + AV_CH_LAYOUT_MONO; + } + + /* get output buffer */ + frame->nb_samples = s->samples + 1; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + frame->nb_samples = s->samples; + } + + if (wc->ch_offset + s->stereo >= avctx->channels) { + av_log(avctx, AV_LOG_WARNING, "Too many channels coded in a packet.\n"); + 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]; + + wc->ch_offset += 1 + s->stereo; + + if (s->stereo_in) { + if (dsd_mode == 3) + ret = wv_unpack_dsd_high(s, samples_l, samples_r); + else if (dsd_mode == 1) + ret = wv_unpack_dsd_fast(s, samples_l, samples_r); + else + ret = wv_unpack_dsd_copy(s, samples_l, samples_r); + } + else { + if (dsd_mode == 3) + ret = wv_unpack_dsd_high(s, samples_l, NULL); + else if (dsd_mode == 1) + ret = wv_unpack_dsd_fast(s, samples_l, NULL); + else + ret = wv_unpack_dsd_copy(s, samples_l, NULL); + + if (s->stereo) + memcpy(samples_r, samples_l, 4 * s->samples); + } + + ff_dsd2pcm_translate (&s->dsdctx [0], s->samples, 0, samples_l, 4, samples_l, 1); + + if (s->stereo) + ff_dsd2pcm_translate (&s->dsdctx [1], s->samples, 0, samples_r, 4, samples_r, 1); + + return ret; +} + +static int wavpack_decode_frame(AVCodecContext *avctx, void *data, + int *got_frame_ptr, AVPacket *avpkt) +{ + WavpackContext *s = avctx->priv_data; + const uint8_t *buf = avpkt->data; + int buf_size = avpkt->size; + AVFrame *frame = data; + int frame_size, ret, frame_flags; + + if (avpkt->size <= WV_HEADER_SIZE) + return AVERROR_INVALIDDATA; + + s->block = 0; + s->ch_offset = 0; + + /* determine number of samples */ + s->samples = AV_RL32(buf + 20); + frame_flags = AV_RL32(buf + 24); + if (s->samples <= 0 || s->samples > WV_MAX_SAMPLES) { + av_log(avctx, AV_LOG_ERROR, "Invalid number of samples: %d\n", + s->samples); + return AVERROR_INVALIDDATA; + } + + if (!(frame_flags & WV_DSD_DATA)) { + av_log(avctx, AV_LOG_ERROR, "Encountered a non-DSD frame\n"); + return AVERROR_INVALIDDATA; + } + + avctx->sample_fmt = AV_SAMPLE_FMT_FLTP; + + while (buf_size > 0) { + if (buf_size <= WV_HEADER_SIZE) + break; + frame_size = AV_RL32(buf + 4) - 12; + buf += 20; + buf_size -= 20; + if (frame_size <= 0 || frame_size > buf_size) { + av_log(avctx, AV_LOG_ERROR, + "Block %d has invalid size (size %d vs. %d bytes left)\n", + s->block, frame_size, buf_size); + return AVERROR_INVALIDDATA; + } + if ((ret = wavpack_decode_block(avctx, s->block, + frame, buf, frame_size)) < 0) { + return ret; + } + s->block++; + buf += frame_size; + buf_size -= frame_size; + } + + if (s->ch_offset != avctx->channels) { + av_log(avctx, AV_LOG_ERROR, "Not enough channels coded in a packet.\n"); + return AVERROR_INVALIDDATA; + } + + *got_frame_ptr = 1; + + return avpkt->size; +} + +AVCodec ff_wavpack_dsd_decoder = { + .name = "wavpack_dsd", + .long_name = NULL_IF_CONFIG_SMALL("WavPack DSD"), + .type = AVMEDIA_TYPE_AUDIO, + .id = AV_CODEC_ID_WAVPACK_DSD, + .priv_data_size = sizeof(WavpackContext), + .init = wavpack_decode_init, + .close = wavpack_decode_end, + .decode = wavpack_decode_frame, + .capabilities = AV_CODEC_CAP_DR1, +}; diff --git a/libavformat/wvdec.c b/libavformat/wvdec.c index 649791d..50f4079 100644 --- a/libavformat/wvdec.c +++ b/libavformat/wvdec.c @@ -79,7 +79,7 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) { WVContext *wc = ctx->priv_data; int ret; - int rate, bpp, chan; + int rate, rate_x, bpp, chan; uint32_t chmask, flags; wc->pos = avio_tell(pb); @@ -98,11 +98,6 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) return ret; } - if (wc->header.flags & WV_DSD) { - avpriv_report_missing_feature(ctx, "WV DSD"); - return AVERROR_PATCHWELCOME; - } - if (wc->header.version < 0x402 || wc->header.version > 0x410) { avpriv_report_missing_feature(ctx, "WV version 0x%03X", wc->header.version); @@ -115,7 +110,8 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) return 0; // parse flags flags = wc->header.flags; - bpp = ((flags & 3) + 1) << 3; + rate_x = (flags & WV_DSD) ? 4 : 1; + bpp = (flags & WV_DSD) ? 0 : ((flags & 3) + 1) << 3; chan = 1 + !(flags & WV_MONO); chmask = flags & WV_MONO ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO; rate = wv_rates[(flags >> 23) & 0xF]; @@ -124,7 +120,7 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) chan = wc->chan; chmask = wc->chmask; } - if ((rate == -1 || !chan) && !wc->block_parsed) { + if ((rate == -1 || !chan || flags & WV_DSD) && !wc->block_parsed) { int64_t block_end = avio_tell(pb) + wc->header.blocksize; if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) { av_log(ctx, AV_LOG_ERROR, @@ -177,6 +173,16 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) return AVERROR_INVALIDDATA; } break; + case 0xE: + if (size <= 1) { + av_log(ctx, AV_LOG_ERROR, + "Invalid DSD block\n"); + return AVERROR_INVALIDDATA; + } + rate_x = 1 << avio_r8(pb); + if (size) + avio_skip(pb, size-1); + break; case 0x27: rate = avio_rl24(pb); break; @@ -200,7 +206,7 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) if (!wc->chmask) wc->chmask = chmask; if (!wc->rate) - wc->rate = rate; + wc->rate = rate * rate_x; if (flags && bpp != wc->bpp) { av_log(ctx, AV_LOG_ERROR, @@ -214,10 +220,10 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb) chan, wc->chan); return AVERROR_INVALIDDATA; } - if (flags && rate != -1 && rate != wc->rate) { + if (flags && rate != -1 && !(flags & WV_DSD) && rate * rate_x != wc->rate) { av_log(ctx, AV_LOG_ERROR, "Sampling rate differ, this block: %i, header block: %i\n", - rate, wc->rate); + rate * rate_x, wc->rate); return AVERROR_INVALIDDATA; } return 0; @@ -245,7 +251,9 @@ static int wv_read_header(AVFormatContext *s) if (!st) return AVERROR(ENOMEM); st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - st->codecpar->codec_id = AV_CODEC_ID_WAVPACK; + st->codecpar->codec_id = wc->header.flags & WV_DSD ? + AV_CODEC_ID_WAVPACK_DSD : + AV_CODEC_ID_WAVPACK; st->codecpar->channels = wc->chan; st->codecpar->channel_layout = wc->chmask; st->codecpar->sample_rate = wc->rate;