PR #22416 opened by has207 URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22416 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22416.patch
Add AV_CODEC_ID_XMAFRAMES decoder for decoding individual XMA frames where packet parsing is handled externally. Each packet contains one raw XMA frame preceded by a byte of padding information. This is useful for Xbox 360 hardware decoder emulation where XMA packets have already been de-interleaved and split into individual frames before being passed to the decoder. Ported from xenia-project/FFmpeg and adapted to current FFmpeg APIs (FFCodec, FF_CODEC_DECODE_CB, ch_layout, etc). Co-Authored-By: Joel Linn <[email protected]> >From 004f50b0d7fe867955657f532558b5e6192b1fe7 Mon Sep 17 00:00:00 2001 From: "Herman S." <[email protected]> Date: Wed, 11 Feb 2026 23:19:25 +0900 Subject: [PATCH] avcodec/wmaprodec: add xmaframes raw frame decoder Add AV_CODEC_ID_XMAFRAMES decoder for decoding individual XMA frames where packet parsing is handled externally. Each packet contains one raw XMA frame preceded by a byte of padding information. This is useful for Xbox 360 hardware decoder emulation where XMA packets have already been de-interleaved and split into individual frames before being passed to the decoder. Ported from xenia-project/FFmpeg and adapted to current FFmpeg APIs (FFCodec, FF_CODEC_DECODE_CB, ch_layout, etc). Co-Authored-By: Joel Linn <[email protected]> --- configure | 1 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/codec_desc.c | 7 +++ libavcodec/codec_id.h | 1 + libavcodec/wmaprodec.c | 96 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 107 insertions(+) diff --git a/configure b/configure index 1759694274..9eb8d034ec 100755 --- a/configure +++ b/configure @@ -3325,6 +3325,7 @@ wmv3_decoder_select="vc1_decoder" wmv3image_decoder_select="wmv3_decoder" xma1_decoder_select="wmapro_decoder" xma2_decoder_select="wmapro_decoder" +xmaframes_decoder_select="wmapro_decoder" ylc_decoder_select="bswapdsp" zerocodec_decoder_select="inflate_wrapper" zlib_decoder_select="inflate_wrapper" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 7b57fe4564..fb4f248dde 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -873,6 +873,7 @@ OBJS-$(CONFIG_XFACE_ENCODER) += xfaceenc.o xface.o OBJS-$(CONFIG_XL_DECODER) += xl.o OBJS-$(CONFIG_XMA1_DECODER) += wmaprodec.o wma.o wma_common.o OBJS-$(CONFIG_XMA2_DECODER) += wmaprodec.o wma.o wma_common.o +OBJS-$(CONFIG_XMAFRAMES_DECODER) += wmaprodec.o wma.o wma_common.o OBJS-$(CONFIG_XPM_DECODER) += xpmdec.o OBJS-$(CONFIG_XSUB_DECODER) += xsubdec.o OBJS-$(CONFIG_XSUB_ENCODER) += xsubenc.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index ad22162b0e..9a011e2c85 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -562,6 +562,7 @@ extern const FFCodec ff_wmavoice_decoder; extern const FFCodec ff_ws_snd1_decoder; extern const FFCodec ff_xma1_decoder; extern const FFCodec ff_xma2_decoder; +extern const FFCodec ff_xmaframes_decoder; /* PCM codecs */ extern const FFCodec ff_pcm_alaw_encoder; diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index b3f4e73e1d..a91c89243a 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -3567,6 +3567,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("CRI AHX"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_XMAFRAMES, + .type = AVMEDIA_TYPE_AUDIO, + .name = "xmaframes", + .long_name = NULL_IF_CONFIG_SMALL("Xbox Media Audio raw frames"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* subtitle codecs */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index 6529f0a6bc..bb4d2f9e68 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -566,6 +566,7 @@ enum AVCodecID { AV_CODEC_ID_LC3, AV_CODEC_ID_G728, AV_CODEC_ID_AHX, + AV_CODEC_ID_XMAFRAMES, /* subtitle codecs */ AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. diff --git a/libavcodec/wmaprodec.c b/libavcodec/wmaprodec.c index dd33c56e54..0015a60dcc 100644 --- a/libavcodec/wmaprodec.c +++ b/libavcodec/wmaprodec.c @@ -401,6 +401,11 @@ static av_cold int decode_init(WMAProDecodeCtx *s, AVCodecContext *avctx, int nu s->bits_per_sample = 16; channel_mask = 0; /* would need to aggregate from all streams */ s->nb_channels = edata_ptr[8 + 20*num_stream + 17]; /* nth stream config */ + } else if (avctx->codec_id == AV_CODEC_ID_XMAFRAMES) { + s->decode_flags = 0x10d6; + s->bits_per_sample = 16; + channel_mask = 0; + s->nb_channels = avctx->ch_layout.nb_channels; } else if (avctx->codec_id == AV_CODEC_ID_WMAPRO && avctx->extradata_size >= 18) { s->decode_flags = AV_RL16(edata_ptr+14); channel_mask = AV_RL32(edata_ptr+2); @@ -2127,3 +2132,94 @@ const FFCodec ff_xma2_decoder = { .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; + +static av_cold int xmaframes_decode_init(AVCodecContext *avctx) +{ + WMAProDecodeCtx *s = avctx->priv_data; + + avctx->block_align = 2048; + + return decode_init(s, avctx, 0); +} + +static av_cold int xmaframes_decode_end(AVCodecContext *avctx) +{ + WMAProDecodeCtx *s = avctx->priv_data; + + decode_end(s); + + return 0; +} + +/** + *@brief Decode a single XMA frame. + * Packet parsing is handled externally; each packet contains one raw XMA + * frame preceded by a byte of padding information. + *@param avctx codec context + *@param frame the output buffer + *@param got_frame_ptr number of produced frames + *@param avpkt input packet + *@return number of bytes that were read from the input buffer + */ +static int xmaframes_decode_packet(AVCodecContext *avctx, AVFrame *frame, + int *got_frame_ptr, AVPacket *avpkt) +{ + WMAProDecodeCtx *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + int ret, xma_frame_len; + uint8_t padding_start, padding_end; + + if (avpkt->size < 3) { + av_log(avctx, AV_LOG_ERROR, "XMA frame too small, %d bytes\n", + avpkt->size); + return AVERROR_INVALIDDATA; + } + + s->buf_bit_size = avpkt->size << 3; + init_get_bits(gb, avpkt->data, s->buf_bit_size); + + /* get padding sizes from first byte */ + padding_start = get_bits(gb, 3); + padding_end = get_bits(gb, 3); + skip_bits(gb, 2); + + /* move bit reader to start of XMA frame */ + skip_bits(gb, padding_start); + + /* validate buffer size */ + xma_frame_len = show_bits(gb, s->log2_frame_size); + if (s->buf_bit_size != 8 + padding_start + xma_frame_len + padding_end) { + av_log(avctx, AV_LOG_ERROR, + "XMA frame sizing incorrect: " + "buf_bit_size %d != 8 + %d + %d + %d\n", + s->buf_bit_size, padding_start, xma_frame_len, padding_end); + return AVERROR_INVALIDDATA; + } + + save_bits(s, gb, xma_frame_len, 0); + + /* get output buffer */ + frame->nb_samples = s->samples_per_frame; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) { + s->packet_loss = 1; + return 0; + } + + decode_frame(s, frame, got_frame_ptr); + + return avpkt->size; +} + +const FFCodec ff_xmaframes_decoder = { + .p.name = "xmaframes", + CODEC_LONG_NAME("Xbox Media Audio raw frames"), + .p.type = AVMEDIA_TYPE_AUDIO, + .p.id = AV_CODEC_ID_XMAFRAMES, + .priv_data_size = sizeof(WMAProDecodeCtx), + .init = xmaframes_decode_init, + .close = xmaframes_decode_end, + FF_CODEC_DECODE_CB(xmaframes_decode_packet), + .p.capabilities = AV_CODEC_CAP_DR1, + CODEC_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP), + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
