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]

Reply via email to