Message ID | 20211228053734.151556-1-zane@zanevaniperen.com |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel] avcodec: add ADPCM IMA Ubisoft decoder | expand |
Context | Check | Description |
---|---|---|
yinshiyou/configure_loongarch64 | warning | Failed to run configure |
yinshiyou/make_loongarch64 | fail | Make failed |
andriy/make_x86 | success | Make finished |
andriy/make_fate_x86 | success | Make fate finished |
andriy/make_ppc | success | Make finished |
andriy/make_fate_ppc | fail | Make fate failed |
Ping. Will apply (with the minor version bump I forgot) in a few days. On 28/12/21 15:37, Zane van Iperen wrote: > 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,
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,
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(+)