Adds support for the ADPCM variant used by some Argonaut Games' games, such as 'Croc! Legend of the Gobbos', and 'Croc 2'.
Signed-off-by: Zane van Iperen <z...@zanevaniperen.com> --- Changelog | 1 + doc/general.texi | 1 + libavcodec/Makefile | 1 + libavcodec/adpcm.c | 152 ++++++++++++++++++++++++++++++++++++++++ libavcodec/allcodecs.c | 1 + libavcodec/avcodec.h | 1 + libavcodec/codec_desc.c | 7 ++ libavcodec/version.h | 2 +- 8 files changed, 165 insertions(+), 1 deletion(-) diff --git a/Changelog b/Changelog index 2ccd2645fc..c029d73c72 100644 --- a/Changelog +++ b/Changelog @@ -30,6 +30,7 @@ version <next>: - MPEG-H 3D Audio support in mp4 - thistogram filter - freezeframes filter +- Argonaut Games ADPCM decoder version 4.2: diff --git a/doc/general.texi b/doc/general.texi index 4bd4b4f6b9..85db50462c 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -1079,6 +1079,7 @@ following image formats are supported: @item ACELP.KELVIN @tab @tab X @item ADPCM 4X Movie @tab @tab X @item APDCM Yamaha AICA @tab @tab X +@item ADPCM Argonaut Games @tab @tab X @item ADPCM CDROM XA @tab @tab X @item ADPCM Creative Technology @tab @tab X @tab 16 -> 4, 8 -> 4, 8 -> 3, 8 -> 2 diff --git a/libavcodec/Makefile b/libavcodec/Makefile index c1f35b40d8..a2fbb910a0 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -817,6 +817,7 @@ OBJS-$(CONFIG_ADPCM_ADX_ENCODER) += adxenc.o adx.o OBJS-$(CONFIG_ADPCM_AFC_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_AGM_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_AICA_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_ARGO_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_CT_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_DTK_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_EA_DECODER) += adpcm.o adpcm_data.o diff --git a/libavcodec/adpcm.c b/libavcodec/adpcm.c index 7b5b3d9698..af9830d9ff 100644 --- a/libavcodec/adpcm.c +++ b/libavcodec/adpcm.c @@ -12,6 +12,7 @@ * EA ADPCM XAS decoder by Peter Ross (pr...@xvid.org) * MAXIS EA ADPCM decoder by Robert Marston (rmars...@gmail.com) * THP ADPCM decoder by Marco Gerards (mgera...@xs4all.nl) + * Argonaut Games ADPCM decoder by Zane van Iperen (z...@zanevaniperen.com) * * This file is part of FFmpeg. * @@ -148,6 +149,10 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx) if (avctx->extradata && avctx->extradata_size >= 2) c->vqa_version = AV_RL16(avctx->extradata); break; + case AV_CODEC_ID_ADPCM_ARGO: + if (avctx->bits_per_coded_sample != 4) + return AVERROR_INVALIDDATA; + break; default: break; } @@ -546,6 +551,118 @@ static void adpcm_swf_decode(AVCodecContext *avctx, const uint8_t *buf, int buf_ } } +/* + * Argonaut Decoder 1: (prev0 + (s << (c + 2))) + */ +static int16_t *adpcm_argo_decoder_1(uint8_t c, int16_t *dst, const uint8_t *src, + const int16_t *prev_, int nsamples, int stride) +{ + int16_t prev; + int8_t s; + + av_assert0(stride == 0 || stride == 1); + ++stride; + + prev = prev_[stride]; + + c += 2; + for (int i = 0; i < nsamples / 2; i++, src++) { + s = (int8_t)((*src & 0xF0u) << 0u); + *dst = prev = ((prev * (1u << 6u)) + (s * (1u << c))) >> 6; + dst += stride; + + s = (int8_t)((*src & 0x0Fu) << 4u); + *dst = prev = ((prev * (1u << 6u)) + (s * (1u << c))) >> 6; + dst += stride; + } + + return dst; +} + +/* + * Argonaut Decoder 2: (2 * prev0) - (1 * prev1) + (s << (c + 2)) + */ +static int16_t *adpcm_argo_decoder_2(uint8_t c, int16_t *dst, const uint8_t *src, + const int16_t *prev_, int nsamples, int stride) +{ + int16_t cprev[2]; + int8_t s; + + av_assert0(stride == 0 || stride == 1); + ++stride; + + /* [t-1, t-2] */ + cprev[0] = prev_[stride]; + cprev[1] = prev_[0]; + + c += 2; + for (int i = 0; i < nsamples / 2; i++, src++) { + s = (int8_t)((*src & 0xF0u) << 0u); + *dst = ((2 * cprev[0] * (1u << 6u)) - (cprev[1] * (1u << 6u)) + (s * (1u << c))) >> 6; + cprev[1] = cprev[0]; + cprev[0] = *dst; + dst += stride; + + s = (int8_t)((*src & 0x0Fu) << 4u); + *dst = ((2 * cprev[0] * (1u << 6u)) - (cprev[1] * (1u << 6u)) + (s * (1u << c))) >> 6; + cprev[1] = cprev[0]; + cprev[0] = *dst; + dst += stride; + } + + return dst; +} + +/** + * Decode a block of Argonaut ADPCM samples. + * + * The format of each block: + * uint8_t left_control; + * uint4_t left_samples[]; + * ---- and if stereo ---- + * uint8_t right_control; + * uint4_t right_samples[]; + * + * Format of the control byte: + * MSB [SSSSDRRR] LSB + * S = (Shift Amount - 2) + * D = Decoder flag. If set, use decoder 2, otherwise use decoder 1 + * R = Reserved + * + * @param dst A pointer to the buffer to write the samples. + * This must be at least `nsamples * nchannels` + * @param src A pointer to the current block. + * @param prev A pointer to the previous two decoded samples, one per channel. + * For mono this is: [Lt-2, Lt-1] + * For stereo this is: [Lt-2, Rt-2, Lt-1, Rt-1] + * @param nsamples The number of samples per channel in the block. + * Must be divisible by and greater than 2. + * @param nchannels The number of channels. Must be 1 or 2. + * + */ +static int16_t *adpcm_argo_decode_block(int16_t *dst, const uint8_t *src, + const int16_t *prev, int nsamples, + int nchannels) +{ + uint8_t c; + + av_assert0(nsamples > 2 && (nsamples & 0x1) == 0); + av_assert0(nchannels == 1 || nchannels == 2); + + /* NB: nsamples/2 because samples are 4 bits, not 8. */ + for (int i = 0; i < nchannels; i++, src += nsamples / 2) { + /* Get the control byte and run the samples through the decoder. */ + c = *src++; + + if (c & 0x04) + adpcm_argo_decoder_2(c >> 4, dst + i, src, prev + i, nsamples, nchannels - 1); + else + adpcm_argo_decoder_1(c >> 4, dst + i, src, prev + i, nsamples, nchannels - 1); + } + + return dst + (nsamples * nchannels); +} + /** * Get the number of samples that will be decoded from the packet. * In one case, this is actually the maximum number of samples possible to @@ -584,6 +701,11 @@ static int get_nb_samples(AVCodecContext *avctx, GetByteContext *gb, return 0; nb_samples = 64; break; + case AV_CODEC_ID_ADPCM_ARGO: + if (buf_size < 17 * ch) + return 0; + nb_samples = 32; + break; /* simple 4-bit adpcm */ case AV_CODEC_ID_ADPCM_CT: case AV_CODEC_ID_ADPCM_IMA_APC: @@ -1770,6 +1892,35 @@ static int adpcm_decode_frame(AVCodecContext *avctx, void *data, } } break; + case AV_CODEC_ID_ADPCM_ARGO: + { + int16_t prev[4]; + + if (avctx->channels == 1) { + prev[0] = c->status[0].sample1; + prev[1] = c->status[0].sample2; + } else { + prev[0] = c->status[0].sample1; + prev[1] = c->status[1].sample1; + prev[2] = c->status[0].sample2; + prev[3] = c->status[1].sample2; + } + + samples = adpcm_argo_decode_block(samples, buf, prev, nb_samples, avctx->channels); + + if (avctx->channels == 1) { + c->status[0].sample1 = *(samples - 2); + c->status[0].sample2 = *(samples - 1); + } else { + c->status[0].sample1 = *(samples - 4); + c->status[1].sample1 = *(samples - 3); + c->status[0].sample2 = *(samples - 2); + c->status[1].sample2 = *(samples - 1); + } + + bytestream2_seek(&gb, 0, SEEK_END); + break; + } default: av_assert0(0); // unsupported codec_id should not happen @@ -1824,6 +1975,7 @@ ADPCM_DECODER(AV_CODEC_ID_ADPCM_4XM, sample_fmts_s16p, adpcm_4xm, ADPCM_DECODER(AV_CODEC_ID_ADPCM_AFC, sample_fmts_s16p, adpcm_afc, "ADPCM Nintendo Gamecube AFC"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_AGM, sample_fmts_s16, adpcm_agm, "ADPCM AmuseGraphics Movie"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_AICA, sample_fmts_s16p, adpcm_aica, "ADPCM Yamaha AICA"); +ADPCM_DECODER(AV_CODEC_ID_ADPCM_ARGO, sample_fmts_s16, adpcm_argo, "ADPCM Argonaut Games"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_CT, sample_fmts_s16, adpcm_ct, "ADPCM Creative Technology"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_DTK, sample_fmts_s16p, adpcm_dtk, "ADPCM Nintendo Gamecube DTK"); ADPCM_DECODER(AV_CODEC_ID_ADPCM_EA, sample_fmts_s16, adpcm_ea, "ADPCM Electronic Arts"); diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index ec7366144f..01a083d06b 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -582,6 +582,7 @@ extern AVCodec ff_adpcm_adx_decoder; extern AVCodec ff_adpcm_afc_decoder; extern AVCodec ff_adpcm_agm_decoder; extern AVCodec ff_adpcm_aica_decoder; +extern AVCodec ff_adpcm_argo_decoder; extern AVCodec ff_adpcm_ct_decoder; extern AVCodec ff_adpcm_dtk_decoder; extern AVCodec ff_adpcm_ea_decoder; diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 4b0e7c0853..ce126353b3 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -545,6 +545,7 @@ enum AVCodecID { AV_CODEC_ID_ADPCM_IMA_DAT4, AV_CODEC_ID_ADPCM_MTAF, AV_CODEC_ID_ADPCM_AGM, + AV_CODEC_ID_ADPCM_ARGO, /* AMR */ AV_CODEC_ID_AMR_NB = 0x12000, diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 529b838e5b..32f573d58c 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -2297,6 +2297,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("ADPCM AmuseGraphics Movie AGM"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_ADPCM_ARGO, + .type = AVMEDIA_TYPE_AUDIO, + .name = "adpcm_argo", + .long_name = NULL_IF_CONFIG_SMALL("ADPCM Argonaut Games"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* AMR */ { diff --git a/libavcodec/version.h b/libavcodec/version.h index 6cf333eeb6..2fba26e8d0 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 58 -#define LIBAVCODEC_VERSION_MINOR 66 +#define LIBAVCODEC_VERSION_MINOR 67 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ -- 2.17.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".