From: Zane van Iperen <zane@zanevaniperen.com> To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH] avcodec: add ADPCM IMA Ubisoft decoder Date: Tue, 28 Dec 2021 15:37:34 +1000 Message-ID: <20211228053734.151556-1-zane@zanevaniperen.com> (raw) 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 <zane@zanevaniperen.com> --- 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 <next>: - 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, -- I do have a demuxer for this, but it's a mess. I'll send it through eventually... _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
next reply other threads:[~2021-12-28 5:37 UTC|newest] Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top 2021-12-28 5:37 Zane van Iperen [this message] 2021-12-31 7:09 ` Zane van Iperen
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20211228053734.151556-1-zane@zanevaniperen.com \ --to=zane@zanevaniperen.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git