PR #24735 opened by Peter Ross (pross) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24735 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24735.patch
Citrix ADPCM decoder and fate test case. ```fate-samples citrix/citrix-mono-2bit-8000.wav ``` >From 0b57af56337b74fe1ac53ccfea61f3e07797fed1 Mon Sep 17 00:00:00 2001 From: Peter Ross <[email protected]> Date: Tue, 8 Jul 2025 14:06:47 +1000 Subject: [PATCH 1/3] avcodec/adpcm: Citrix ADPCM decoder --- libavcodec/Makefile | 1 + libavcodec/adpcm.c | 56 +++++++++++++++++++++++++++++++++++++++++ libavcodec/allcodecs.c | 1 + libavcodec/codec_desc.c | 7 ++++++ libavcodec/codec_id.h | 1 + 5 files changed, 66 insertions(+) diff --git a/libavcodec/Makefile b/libavcodec/Makefile index e2c60e9051..6c5e17427d 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -990,6 +990,7 @@ OBJS-$(CONFIG_ADPCM_IMA_ALP_ENCODER) += adpcmenc.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_APC_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_APM_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_APM_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_CITRIX_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_CUNNING_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_DAT4_DECODER) += adpcm.o adpcm_data.o OBJS-$(CONFIG_ADPCM_IMA_DK3_DECODER) += adpcm.o adpcm_data.o diff --git a/libavcodec/adpcm.c b/libavcodec/adpcm.c index 74af2cb4d3..a60f1103db 100644 --- a/libavcodec/adpcm.c +++ b/libavcodec/adpcm.c @@ -18,6 +18,7 @@ * High Voltage Software ALP decoder by Zane van Iperen ([email protected]) * Cunning Developments decoder by Zane van Iperen ([email protected]) * Sanyo LD-ADPCM decoder by Peter Ross ([email protected]) + * Citrix ADPCM decoder by Peter Ross ([email protected]) * * This file is part of FFmpeg. * @@ -241,6 +242,10 @@ static const int8_t mtf_index_table[16] = { -1, -1, -1, -1, 2, 4, 6, 8, }; +static const int8_t citrix_index_table[4] = { + -1, 1, -1, 1, +}; + /* end of tables */ typedef struct ADPCMDecodeContext { @@ -332,6 +337,10 @@ static av_cold int adpcm_decode_init(AVCodecContext * avctx) if (avctx->bits_per_coded_sample != 8) return AVERROR_INVALIDDATA; break; + case AV_CODEC_ID_ADPCM_IMA_CITRIX: + if (avctx->bits_per_coded_sample != 2) + return AVERROR_INVALIDDATA; + break; default: break; } @@ -1149,6 +1158,19 @@ static int adpcm_sanyo_expand5(ADPCMChannelStatus *c, int bits) return c->predictor; } +static int adpcm_citrix_expand(ADPCMChannelStatus *c, int bits) +{ + int step = ff_adpcm_step_table[c->step_index]; + int add = step >> 1; + if ((bits & 1)) + add += step; + if ((bits & 2)) + add = -add; + c->step_index = av_clip(c->step_index + citrix_index_table[bits], 0, 88); + c->predictor = av_clip_int16(c->predictor + add); + return c->predictor; +} + /** * Get the number of samples (per channel) that will be decoded from the packet. * In one case, this is actually the maximum number of samples possible to @@ -1430,6 +1452,9 @@ static int get_nb_samples(AVCodecContext *avctx, GetByteContext *gb, return AVERROR_INVALIDDATA; nb_samples = AV_RL16(avctx->extradata); break; + case AV_CODEC_ID_ADPCM_IMA_CITRIX: + nb_samples = (buf_size / avctx->block_align) * (1 + ((avctx->block_align / ch) - 4) * 4); + break; } /* validate coded sample count */ @@ -2886,6 +2911,36 @@ static int adpcm_decode_frame(AVCodecContext *avctx, AVFrame *frame, samples += channels; } ) /* End of CASE */ + CASE(ADPCM_IMA_CITRIX, + while (bytestream2_get_bytes_left(&gb) >= avctx->block_align) { + for (int ch = 0; ch < channels; ch++) { + *samples++ = c->status[ch].predictor = sign_extend(bytestream2_get_le16(&gb), 16); + c->status[ch].step_index = bytestream2_get_byte(&gb); + if (c->status[ch].step_index > 88) + return AVERROR_INVALIDDATA; + bytestream2_skip(&gb, 1); + } + if (channels == 1) { + for (int block = 0; block < avctx->block_align - 4; block++) { + uint8_t bits = bytestream2_get_byteu(&gb); + samples[0] = adpcm_citrix_expand(&c->status[0], bits & 3); + samples[1] = adpcm_citrix_expand(&c->status[0], (bits >> 2) & 3); + samples[2] = adpcm_citrix_expand(&c->status[0], (bits >> 4) & 3); + samples[3] = adpcm_citrix_expand(&c->status[0], (bits >> 6) & 3); + samples += 4; + } + } else { // channels == 2 + for (int block = 0; block < (avctx->block_align - 4*channels) / (4*channels); block++) { + for (int ch = 0; ch < channels; ch++) { + uint32_t bits = bytestream2_get_le32(&gb); + for (int j = 0; j < 16; j++) + samples[j*channels + ch] = adpcm_citrix_expand(&c->status[ch], (bits >> (2*j)) & 3); + } + samples += 16*channels; + } + } + } + ) /* End of CASE */ default: av_unreachable("There are cases for all codec ids using adpcm_decode_frame"); } @@ -2987,6 +3042,7 @@ ADPCM_DECODER(ADPCM_IMA_ACORN, adpcm_ima_acorn, "ADPCM IMA Acorn Replay") ADPCM_DECODER(ADPCM_IMA_AMV, adpcm_ima_amv, "ADPCM IMA AMV") ADPCM_DECODER(ADPCM_IMA_APC, adpcm_ima_apc, "ADPCM IMA CRYO APC") ADPCM_DECODER(ADPCM_IMA_APM, adpcm_ima_apm, "ADPCM IMA Ubisoft APM") +ADPCM_DECODER(ADPCM_IMA_CITRIX, adpcm_ima_citrix, "ADPCM IMA Citrix") ADPCM_DECODER(ADPCM_IMA_CUNNING, adpcm_ima_cunning, "ADPCM IMA Cunning Developments") ADPCM_DECODER(ADPCM_IMA_DAT4, adpcm_ima_dat4, "ADPCM IMA Eurocom DAT4") ADPCM_DECODER(ADPCM_IMA_DK3, adpcm_ima_dk3, "ADPCM IMA Duck DK3") diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index ca20c76fe6..46630133ae 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -667,6 +667,7 @@ extern const FFCodec ff_adpcm_ima_alp_encoder; extern const FFCodec ff_adpcm_ima_apc_decoder; extern const FFCodec ff_adpcm_ima_apm_decoder; extern const FFCodec ff_adpcm_ima_apm_encoder; +extern const FFCodec ff_adpcm_ima_citrix_decoder; extern const FFCodec ff_adpcm_ima_cunning_decoder; extern const FFCodec ff_adpcm_ima_dat4_decoder; extern const FFCodec ff_adpcm_ima_dk3_decoder; diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index dc20b57df4..7ee20b3b6e 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -2698,6 +2698,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Acorn Escape"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_ADPCM_IMA_CITRIX, + .type = AVMEDIA_TYPE_AUDIO, + .name = "adpcm_citrix", + .long_name = NULL_IF_CONFIG_SMALL("ADPCM IMA Citrix"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* AMR */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index 841aa28d20..2757afcdbf 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -430,6 +430,7 @@ enum AVCodecID { AV_CODEC_ID_ADPCM_PSXC, AV_CODEC_ID_ADPCM_CIRCUS, AV_CODEC_ID_ADPCM_IMA_ESCAPE, + AV_CODEC_ID_ADPCM_IMA_CITRIX, /* AMR */ AV_CODEC_ID_AMR_NB = 0x12000, -- 2.52.0 >From 868c315e1dc4f298289e239c1487b77f3fb66697 Mon Sep 17 00:00:00 2001 From: Peter Ross <[email protected]> Date: Sun, 27 Sep 2026 09:36:05 +1000 Subject: [PATCH 2/3] avformat/riff: Citrix ADPCM decoder --- libavformat/riff.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libavformat/riff.c b/libavformat/riff.c index 21de951e3a..4cb2763827 100644 --- a/libavformat/riff.c +++ b/libavformat/riff.c @@ -582,6 +582,7 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_MSNSIREN, 0x028E }, { AV_CODEC_ID_ADPCM_G722, 0x028F }, { AV_CODEC_ID_MISC4, 0x0350 }, + { AV_CODEC_ID_ADPCM_IMA_CITRIX, 0x0400 }, { AV_CODEC_ID_IMC, 0x0401 }, { AV_CODEC_ID_IAC, 0x0402 }, { AV_CODEC_ID_ON2AVC, 0x0500 }, -- 2.52.0 >From 696edeec49e915a12b3ba8f085c5a4df7b7886ba Mon Sep 17 00:00:00 2001 From: Peter Ross <[email protected]> Date: Tue, 8 Jul 2025 14:29:00 +1000 Subject: [PATCH 3/3] tests/fate/adpcm: Citrix ADPCM test case --- tests/fate/adpcm.mak | 3 +++ tests/ref/fate/adpcm-ima-citrix | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tests/ref/fate/adpcm-ima-citrix diff --git a/tests/fate/adpcm.mak b/tests/fate/adpcm.mak index 7ad62312dd..c66c9c831a 100644 --- a/tests/fate/adpcm.mak +++ b/tests/fate/adpcm.mak @@ -43,6 +43,9 @@ fate-adpcm-ima-amv: CMD = framecrc -i $(TARGET_SAMPLES)/amv/MTV_high_res_320x240 FATE_ADPCM-$(call DEMDEC, APC, ADPCM_IMA_APC) += fate-adpcm-ima-apc fate-adpcm-ima-apc: CMD = md5 -i $(TARGET_SAMPLES)/cryo-apc/cine007.APC -f s16le +FATE_ADPCM-$(call FRAMECRC, WAV, ADPCM_IMA_CITRIX, ARESAMPLE_FILTER) += fate-adpcm-ima-citrix +fate-adpcm-ima-citrix: CMD = framecrc -i $(TARGET_SAMPLES)/citrix/citrix-mono-2bit-8000.wav -f s16le -af aresample + FATE_ADPCM-$(call DEMDEC, AVI, ADPCM_IMA_DK3) += fate-adpcm-ima-dk3 fate-adpcm-ima-dk3: CMD = md5 -i $(TARGET_SAMPLES)/duck/sop-audio-only.avi -f s16le diff --git a/tests/ref/fate/adpcm-ima-citrix b/tests/ref/fate/adpcm-ima-citrix new file mode 100644 index 0000000000..348b6127c4 --- /dev/null +++ b/tests/ref/fate/adpcm-ima-citrix @@ -0,0 +1,47 @@ +#tb 0: 1/8000 +#media_type 0: audio +#codec_id 0: pcm_s16le +#sample_rate 0: 8000 +#channel_layout_name 0: mono +0, 0, 0, 497, 994, 0xa79b02e8 +0, 497, 497, 497, 994, 0x265cad20 +0, 994, 994, 497, 994, 0xc9f50298 +0, 1491, 1491, 497, 994, 0xe838ef24 +0, 1988, 1988, 497, 994, 0x07f02c62 +0, 2485, 2485, 497, 994, 0x4adc0334 +0, 2982, 2982, 497, 994, 0x6a9deb05 +0, 3479, 3479, 497, 994, 0xbf4bfc73 +0, 3976, 3976, 497, 994, 0xa942ff2d +0, 4473, 4473, 497, 994, 0x7538ea62 +0, 4970, 4970, 497, 994, 0x228ed53a +0, 5467, 5467, 497, 994, 0x3538e336 +0, 5964, 5964, 497, 994, 0xebec17fb +0, 6461, 6461, 497, 994, 0x7ba516a4 +0, 6958, 6958, 497, 994, 0x6ed0e896 +0, 7455, 7455, 497, 994, 0x0fd6e65e +0, 7952, 7952, 497, 994, 0x2a1803a3 +0, 8449, 8449, 497, 994, 0x7ac50c89 +0, 8946, 8946, 497, 994, 0xd0360d8b +0, 9443, 9443, 497, 994, 0x425605bd +0, 9940, 9940, 497, 994, 0x0c6ffb20 +0, 10437, 10437, 497, 994, 0x2341f1f9 +0, 10934, 10934, 497, 994, 0xaa98e622 +0, 11431, 11431, 497, 994, 0x6081f62a +0, 11928, 11928, 497, 994, 0x2b0ecbb5 +0, 12425, 12425, 497, 994, 0x595300fa +0, 12922, 12922, 497, 994, 0x3c310aae +0, 13419, 13419, 497, 994, 0x98d213a2 +0, 13916, 13916, 497, 994, 0x38672288 +0, 14413, 14413, 497, 994, 0xd8920cd3 +0, 14910, 14910, 497, 994, 0xc1acef44 +0, 15407, 15407, 497, 994, 0x304ce24a +0, 15904, 15904, 497, 994, 0x0f7ffba8 +0, 16401, 16401, 497, 994, 0x7981e376 +0, 16898, 16898, 497, 994, 0xbd7bf915 +0, 17395, 17395, 497, 994, 0x67b9ecdc +0, 17892, 17892, 497, 994, 0xb849ff06 +0, 18389, 18389, 497, 994, 0x1987ca8f +0, 18886, 18886, 497, 994, 0xacd1ab92 +0, 19383, 19383, 497, 994, 0x72b1ee2b +0, 19880, 19880, 497, 994, 0xb2acc537 +0, 20377, 20377, 497, 994, 0xa79b02e8 -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
