From patchwork Tue Dec 28 05:37:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zane van Iperen X-Patchwork-Id: 32931 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:cd86:0:0:0:0:0 with SMTP id d128csp10867393iog; Mon, 27 Dec 2021 21:37:54 -0800 (PST) X-Google-Smtp-Source: ABdhPJzEuWNF5DiOr5rQY/JPY1xHw8ixnYDh96pJk7JLqtqyWaR8hvEF3blFEy1m5JGTOQcQak5G X-Received: by 2002:a17:906:794d:: with SMTP id l13mr17013299ejo.206.1640669874642; Mon, 27 Dec 2021 21:37:54 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1640669874; cv=none; d=google.com; s=arc-20160816; b=PMxJg8S22lV7DEiNKEZMCnHQlFZZIS1D8pIuIbyu3HtD3oO8xAbSMlT3Ntb228jazN 28gRopnG5qRWiNXupoAtzTw39RLazmHVh8CjyOsBnx8CK9Bq8yoHMn4YXh13iYxn7ZCy umV8KziBAZXryjB85NEHLyaGrWaOhw9OHNrfskQN2BK9V+XeFcZuQrLf9i10pFvDmSUc NkdWrrb7Z+9+0rHaR7+a0+J/ly0TrfiIy6UFXHdRQqzz5CCmL0rWGQu3jwFEI23VLrBZ Z8ZrKlB0lorUqvVNuoaJ4BVT4MM2doA34wWuinpFb9LrvP4RVaMQTX0baP5D2pPl7LV9 901g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:message-id:date:to:from :delivered-to; bh=eqb+/KxxXgBN62gX+ZsNdS+7hy93bf18E5V0KwJ8/Bo=; b=CZYLvVC8WWqN2Mpmp0jW9T5dOD5RMseOmXj6K341ZOxVEWHpEjqpUQkI3jWqpfU9ft R4sAgkD+v/AYNFnSN+/q9HQUkgizX41cnPWUMx57F5lA8j2+0CspEKjGQhXYgkjX/XRn UF/qfryNsCY1I5qfZHfP2gtAfOv35Ye9YMsepRXH9IJZyF4YKqHGU+s2UB8XPs9n6uGM 5AvNvIX9zWY7XKsPF3565m6+i0UU6zRa2DmJPPoJW1jGN3Yl1TDwo5CFG4IgPIGeqKHp D5baqvfm5O0hqwRzCtqdwLVc42rhyA3SMnmvUs9Ltw9wD8BKFWUfE79pCtCrO/GXHrd6 x6pg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=zanevaniperen.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id u29si8168542eda.501.2021.12.27.21.37.54; Mon, 27 Dec 2021 21:37:54 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=zanevaniperen.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 7A00968AF87; Tue, 28 Dec 2021 07:37:51 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from out0.migadu.com (out0.migadu.com [94.23.1.103]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id F1F7868AF52 for ; Tue, 28 Dec 2021 07:37:43 +0200 (EET) X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Zane van Iperen To: ffmpeg-devel@ffmpeg.org Date: Tue, 28 Dec 2021 15:37:34 +1000 Message-Id: <20211228053734.151556-1-zane@zanevaniperen.com> MIME-Version: 1.0 X-Migadu-Flow: FLOW_OUT X-Migadu-Auth-User: cadance.vs49688.net Subject: [FFmpeg-devel] [PATCH] avcodec: add ADPCM IMA Ubisoft decoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 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" X-TUID: REcjSnqcWxe4 A simple, interleaved variant, but with initial state and extra, uncompressed samples. Found in Ubisoft soundbanks from early-2000's games (Splinter Cell, RS3, etc.) Signed-off-by: Zane van Iperen --- Changelog | 1 + doc/general_contents.texi | 1 + libavcodec/Makefile | 1 + libavcodec/adpcm.c | 69 +++++++++++++++++++++++++++++++++++++++ libavcodec/allcodecs.c | 1 + libavcodec/codec_desc.c | 7 ++++ libavcodec/codec_id.h | 1 + 7 files changed, 81 insertions(+) diff --git a/Changelog b/Changelog index edb4152d0f..58be0b9da5 100644 --- a/Changelog +++ b/Changelog @@ -44,6 +44,7 @@ version : - yadif_videotoolbox filter - VideoToolbox ProRes encoder - anlmf audio filter +- ADPCM IMA Ubisoft decoder version 4.4: diff --git a/doc/general_contents.texi b/doc/general_contents.texi index df1692c8df..80506e8ab4 100644 --- a/doc/general_contents.texi +++ b/doc/general_contents.texi @@ -1139,6 +1139,7 @@ following image formats are supported: @item ADPCM IMA High Voltage Software ALP @tab X @tab X @item ADPCM IMA QuickTime @tab X @tab X @item ADPCM IMA Simon & Schuster Interactive @tab X @tab X +@item ADPCM IMA Ubisoft @tab @tab X @item ADPCM IMA Ubisoft APM @tab X @tab X @item ADPCM IMA Loki SDL MJPEG @tab @tab X @item ADPCM IMA WAV @tab X @tab X diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 9577062eec..52839e1994 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -899,6 +899,7 @@ OBJS-$(CONFIG_ADPCM_IMA_RAD_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_SSI_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_SSI_ENCODER) += adpcmenc.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_SMJPEG_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_UBISOFT_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_WAV_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_WAV_ENCODER) += adpcmenc.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_WS_DECODER) += adpcm.o adpcm_data.o diff --git a/libavcodec/adpcm.c b/libavcodec/adpcm.c index cfde5f58b9..410fea8e21 100644 --- a/libavcodec/adpcm.c +++ b/libavcodec/adpcm.c @@ -239,6 +239,7 @@ static const int8_t mtf_index_table[16] = { typedef struct ADPCMDecodeContext { ADPCMChannelStatus status[14]; int vqa_version; /**< VQA version. Used for ADPCM_IMA_WS */ + int extra_count; /**< Number of raw PCM samples to send */ int has_status; /**< Status flag. Reset to 0 after a flush. */ } ADPCMDecodeContext; @@ -301,6 +302,13 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx) if (avctx->bits_per_coded_sample != 4 || avctx->block_align != 17 * avctx->channels) return AVERROR_INVALIDDATA; break; + case AV_CODEC_ID_ADPCM_IMA_UBISOFT: + if (c->extra_count < 0) + return AVERROR_INVALIDDATA; + + if (c->extra_count > 0 && c->extra_count % avctx->channels != 0) + return AVERROR_INVALIDDATA; + break; case AV_CODEC_ID_ADPCM_ZORK: if (avctx->bits_per_coded_sample != 8) return AVERROR_INVALIDDATA; @@ -877,6 +885,10 @@ static int get_nb_samples(AVCodecContext *avctx, GetByteContext *gb, case AV_CODEC_ID_ADPCM_IMA_MTF: nb_samples = buf_size * 2 / ch; break; + /* simple 4-bit adpcm, with extra uncompressed samples */ + case AV_CODEC_ID_ADPCM_IMA_UBISOFT: + nb_samples = (buf_size * 2 + s->extra_count) / ch; + break; } if (nb_samples) return nb_samples; @@ -1460,6 +1472,35 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, *samples++ = adpcm_ima_qt_expand_nibble(&c->status[st], v & 0x0F); } ) /* End of CASE */ + CASE(ADPCM_IMA_UBISOFT, + if (c->extra_count) { + int offset = avctx->extradata[0] == 6 ? 36 : 28; + nb_samples -= c->extra_count / avctx->channels; + + for (uint8_t *extra = avctx->extradata + offset; c->extra_count--; extra += 2) { + if (avctx->extradata[0] == 3) + *samples++ = AV_RB16(extra); + else + *samples++ = AV_RL16(extra); + } + + /* NB: This is enforced above. */ + if (avctx->channels == 1) { + c->status[0].predictor = samples[-1]; + } else { + c->status[0].predictor = samples[-2]; + c->status[1].predictor = samples[-1]; + } + + c->extra_count = 0; + } + + for (int n = nb_samples >> (1 - st); n > 0; n--) { + int v = bytestream2_get_byteu(&gb); + *samples++ = adpcm_ima_expand_nibble(&c->status[0], v >> 4, 3); + *samples++ = adpcm_ima_expand_nibble(&c->status[st], v & 0x0F, 3); + } + ) /* End of CASE */ CASE(ADPCM_IMA_APM, for (int n = nb_samples / 2; n > 0; n--) { for (int channel = 0; channel < avctx->channels; channel++) { @@ -2257,6 +2298,33 @@ static void adpcm_flush(AVCodecContext *avctx) if (avctx->extradata && avctx->extradata_size >= 2) c->vqa_version = AV_RL16(avctx->extradata); break; + + case AV_CODEC_ID_ADPCM_IMA_UBISOFT: { + if (avctx->extradata && avctx->extradata_size >= 28) { + uint8_t version = avctx->extradata[0]; + uint32_t sample_offset = version == 6 ? 36 : 28; + + if (version == 3) { + c->extra_count = AV_RB16(avctx->extradata + 14); + c->status[0].predictor = AV_RB16(avctx->extradata + 16); + c->status[1].predictor = AV_RB16(avctx->extradata + 20); + } else { + c->extra_count = AV_RL16(avctx->extradata + 14); + c->status[0].predictor = AV_RL16(avctx->extradata + 16); + c->status[1].predictor = AV_RL16(avctx->extradata + 20); + } + + c->status[0].step_index = av_clip(avctx->extradata[18], 0, 88); + c->status[1].step_index = av_clip(avctx->extradata[22], 0, 88); + + c->extra_count = FFMIN( + c->extra_count, + (avctx->extradata_size - sample_offset) / 2 / sizeof(int16_t) + ); + } + break; + } + default: /* Other codecs may want to handle this during decoding. */ c->has_status = 0; @@ -2330,6 +2398,7 @@ ADPCM_DECODER(ADPCM_IMA_QT, sample_fmts_s16p, adpcm_ima_qt, "ADPCM IMA ADPCM_DECODER(ADPCM_IMA_RAD, sample_fmts_s16, adpcm_ima_rad, "ADPCM IMA Radical") ADPCM_DECODER(ADPCM_IMA_SSI, sample_fmts_s16, adpcm_ima_ssi, "ADPCM IMA Simon & Schuster Interactive") ADPCM_DECODER(ADPCM_IMA_SMJPEG, sample_fmts_s16, adpcm_ima_smjpeg, "ADPCM IMA Loki SDL MJPEG") +ADPCM_DECODER(ADPCM_IMA_UBISOFT, sample_fmts_s16, adpcm_ima_ubisoft, "ADPCM IMA Ubisoft"); ADPCM_DECODER(ADPCM_IMA_ALP, sample_fmts_s16, adpcm_ima_alp, "ADPCM IMA High Voltage Software ALP") ADPCM_DECODER(ADPCM_IMA_WAV, sample_fmts_s16p, adpcm_ima_wav, "ADPCM IMA WAV") ADPCM_DECODER(ADPCM_IMA_WS, sample_fmts_both, adpcm_ima_ws, "ADPCM IMA Westwood") diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index d1e10197de..2a07888a8f 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -652,6 +652,7 @@ extern const AVCodec ff_adpcm_ima_rad_decoder; extern const AVCodec ff_adpcm_ima_ssi_decoder; extern const AVCodec ff_adpcm_ima_ssi_encoder; extern const AVCodec ff_adpcm_ima_smjpeg_decoder; +extern const AVCodec ff_adpcm_ima_ubisoft_decoder; extern const AVCodec ff_adpcm_ima_wav_encoder; extern const AVCodec ff_adpcm_ima_wav_decoder; extern const AVCodec ff_adpcm_ima_ws_encoder; diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 0974ee03de..a892d8f853 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -2475,6 +2475,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Acorn Replay"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_ADPCM_IMA_UBISOFT, + .type = AVMEDIA_TYPE_AUDIO, + .name = "adpcm_ima_ubisoft", + .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Ubisoft"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* AMR */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index ab265ec584..d7b47f88ce 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -401,6 +401,7 @@ enum AVCodecID { AV_CODEC_ID_ADPCM_IMA_CUNNING, AV_CODEC_ID_ADPCM_IMA_MOFLEX, AV_CODEC_ID_ADPCM_IMA_ACORN, + AV_CODEC_ID_ADPCM_IMA_UBISOFT, /* AMR */ AV_CODEC_ID_AMR_NB = 0x12000,