[FFmpeg-devel] [PATCH] XMA1 and XMA2 stereo decoders

Paul B Mahol onemda at gmail.com
Wed Nov 4 14:18:17 CET 2015


Signed-off-by: Paul B Mahol <onemda at gmail.com>
---
 libavcodec/Makefile     |  2 ++
 libavcodec/allcodecs.c  |  2 ++
 libavcodec/avcodec.h    |  2 ++
 libavcodec/codec_desc.c | 14 ++++++++
 libavcodec/wmaprodec.c  | 94 ++++++++++++++++++++++++++++++++++++++++++++++---
 libavformat/riff.c      |  2 ++
 libavformat/riffdec.c   | 25 ++++++++++---
 7 files changed, 131 insertions(+), 10 deletions(-)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 67fb72a..06d3ddd 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -586,6 +586,8 @@ OBJS-$(CONFIG_XBM_ENCODER)             += xbmenc.o
 OBJS-$(CONFIG_XFACE_DECODER)           += xfacedec.o xface.o
 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_XSUB_DECODER)            += xsubdec.o
 OBJS-$(CONFIG_XSUB_ENCODER)            += xsubenc.o
 OBJS-$(CONFIG_XWD_DECODER)             += xwddec.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 095f812..4a21fe7 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -445,6 +445,8 @@ void avcodec_register_all(void)
     REGISTER_ENCDEC (WMAV2,             wmav2);
     REGISTER_DECODER(WMAVOICE,          wmavoice);
     REGISTER_DECODER(WS_SND1,           ws_snd1);
+    REGISTER_DECODER(XMA1,              xma1);
+    REGISTER_DECODER(XMA2,              xma2);
 
     /* PCM codecs */
     REGISTER_ENCDEC (PCM_ALAW,          pcm_alaw);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 91cd6a8..95cdf5a 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -497,6 +497,8 @@ enum AVCodecID {
     AV_CODEC_ID_DSD_MSBF_PLANAR,
     AV_CODEC_ID_4GV,
     AV_CODEC_ID_INTERPLAY_ACM,
+    AV_CODEC_ID_XMA1,
+    AV_CODEC_ID_XMA2,
 
     /* subtitle codecs */
     AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,          ///< A dummy ID pointing at the start of subtitle codecs.
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index c611740..9cad3e6 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -2629,6 +2629,20 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("Interplay ACM"),
         .props     = AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_XMA1,
+        .type      = AVMEDIA_TYPE_AUDIO,
+        .name      = "xma1",
+        .long_name = NULL_IF_CONFIG_SMALL("Xbox Media Audio 1"),
+        .props     = AV_CODEC_PROP_LOSSY,
+    },
+    {
+        .id        = AV_CODEC_ID_XMA2,
+        .type      = AVMEDIA_TYPE_AUDIO,
+        .name      = "xma2",
+        .long_name = NULL_IF_CONFIG_SMALL("Xbox Media Audio 2"),
+        .props     = AV_CODEC_PROP_LOSSY,
+    },
 
     /* subtitle codecs */
     {
diff --git a/libavcodec/wmaprodec.c b/libavcodec/wmaprodec.c
index 19dc335..7471961 100644
--- a/libavcodec/wmaprodec.c
+++ b/libavcodec/wmaprodec.c
@@ -86,6 +86,8 @@
  * subframe in order to reconstruct the output samples.
  */
 
+#define DEBUG 1
+
 #include <inttypes.h>
 
 #include "libavutil/float_dsp.h"
@@ -206,9 +208,12 @@ typedef struct WMAProDecodeCtx {
     int              subframe_offset;               ///< subframe offset in the bit reservoir
     uint8_t          packet_loss;                   ///< set in case of bitstream error
     uint8_t          packet_done;                   ///< set when a packet is fully decoded
+    uint8_t          skip_packets;
+    int8_t           packet_metadata;
 
     /* frame decode state */
     uint32_t         frame_num;                     ///< current frame number (not used for decoding)
+    int              num_frames;
     GetBitContext    gb;                            ///< bitstream reader context
     int              buf_bit_size;                  ///< buffer size in bits
     uint8_t          drc_gain;                      ///< gain for the DRC tool
@@ -282,6 +287,9 @@ static av_cold int decode_init(AVCodecContext *avctx)
     int log2_max_num_subframes;
     int num_possible_block_sizes;
 
+    if (avctx->codec_id == AV_CODEC_ID_XMA1 || avctx->codec_id == AV_CODEC_ID_XMA2)
+        avctx->block_align = 2048;
+
     if (!avctx->block_align) {
         av_log(avctx, AV_LOG_ERROR, "block_align is not set\n");
         return AVERROR(EINVAL);
@@ -296,7 +304,25 @@ static av_cold int decode_init(AVCodecContext *avctx)
 
     avctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
 
-    if (avctx->extradata_size >= 18) {
+    if (avctx->codec_id == AV_CODEC_ID_XMA2 && avctx->extradata_size >= 34) {
+        s->decode_flags    = 0x10d6;
+        channel_mask       = AV_RL32(edata_ptr+2);
+        s->bits_per_sample = 16;
+        /** dump the extradata */
+        for (i = 0; i < avctx->extradata_size; i++)
+            ff_dlog(avctx, "[%x] ", avctx->extradata[i]);
+        ff_dlog(avctx, "\n");
+
+     } else if (avctx->codec_id == AV_CODEC_ID_XMA1 && avctx->extradata_size >= 28) {
+        s->decode_flags    = 0x10d6;
+        s->bits_per_sample = 16;
+        channel_mask       = 0;
+        /** dump the extradata */
+        for (i = 0; i < avctx->extradata_size; i++)
+            ff_dlog(avctx, "[%x] ", avctx->extradata[i]);
+        ff_dlog(avctx, "\n");
+
+     } else if (avctx->extradata_size >= 18) {
         s->decode_flags    = AV_RL16(edata_ptr+14);
         channel_mask       = AV_RL32(edata_ptr+2);
         s->bits_per_sample = AV_RL16(edata_ptr);
@@ -310,6 +336,11 @@ static av_cold int decode_init(AVCodecContext *avctx)
         return AVERROR_PATCHWELCOME;
     }
 
+    if (avctx->codec_id != AV_CODEC_ID_WMAPRO && avctx->channels > 2) {
+        avpriv_report_missing_feature(avctx, ">2 channels support");
+        return AVERROR_PATCHWELCOME;
+    }
+
     /** generic init */
     s->log2_frame_size = av_log2(avctx->block_align) + 4;
     if (s->log2_frame_size > 25) {
@@ -318,7 +349,14 @@ static av_cold int decode_init(AVCodecContext *avctx)
     }
 
     /** frame info */
-    s->skip_frame  = 1; /* skip first frame */
+    if (avctx->codec_id != AV_CODEC_ID_WMAPRO)
+        s->skip_frame = 0;
+    else
+        s->skip_frame = 1; /* skip first frame */
+    if (avctx->codec_id == AV_CODEC_ID_XMA1)
+        s->packet_metadata = 0;
+    else
+        s->packet_metadata = 1;
     s->packet_loss = 1;
     s->len_prefix  = (s->decode_flags & 0x40);
 
@@ -1535,6 +1573,11 @@ static int decode_packet(AVCodecContext *avctx, void *data,
 
     *got_frame_ptr = 0;
 
+    if (s->skip_packets > 0) {
+        s->skip_packets--;
+        return FFMIN(avpkt->size, avctx->block_align);
+    }
+
     if (s->packet_done || s->packet_loss) {
         s->packet_done = 0;
 
@@ -1551,16 +1594,27 @@ static int decode_packet(AVCodecContext *avctx, void *data,
 
         /** parse packet header */
         init_get_bits(gb, buf, s->buf_bit_size);
-        packet_sequence_number = get_bits(gb, 4);
-        skip_bits(gb, 2);
+        if (avctx->codec_id == AV_CODEC_ID_WMAPRO) {
+            packet_sequence_number = get_bits(gb, 4);
+            skip_bits(gb, 2);
+        } else {
+            s->num_frames = get_bits(gb, 6);
+            packet_sequence_number = 0;
+        }
 
         /** get number of bits that need to be added to the previous frame */
         num_bits_prev_frame = get_bits(gb, s->log2_frame_size);
+        if (avctx->codec_id != AV_CODEC_ID_WMAPRO) {
+            if (get_bits(gb, 3) != s->packet_metadata)
+                return AVERROR_INVALIDDATA;
+            s->skip_packets = get_bits(gb, 8);
+        }
+
         ff_dlog(avctx, "packet[%d]: nbpf %x\n", avctx->frame_number,
                 num_bits_prev_frame);
 
         /** check for packet loss */
-        if (!s->packet_loss &&
+        if (avctx->codec_id == AV_CODEC_ID_WMAPRO && !s->packet_loss &&
             ((s->packet_sequence_number + 1) & 0xF) != packet_sequence_number) {
             s->packet_loss = 1;
             av_log(avctx, AV_LOG_ERROR,
@@ -1671,3 +1725,33 @@ AVCodec ff_wmapro_decoder = {
     .sample_fmts    = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP,
                                                       AV_SAMPLE_FMT_NONE },
 };
+
+AVCodec ff_xma1_decoder = {
+    .name           = "xma1",
+    .long_name      = NULL_IF_CONFIG_SMALL("Xbox Media Audio 1"),
+    .type           = AVMEDIA_TYPE_AUDIO,
+    .id             = AV_CODEC_ID_XMA1,
+    .priv_data_size = sizeof(WMAProDecodeCtx),
+    .init           = decode_init,
+    .close          = decode_end,
+    .decode         = decode_packet,
+    .capabilities   = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1,
+    .flush          = flush,
+    .sample_fmts    = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP,
+                                                      AV_SAMPLE_FMT_NONE },
+};
+
+AVCodec ff_xma2_decoder = {
+    .name           = "xma2",
+    .long_name      = NULL_IF_CONFIG_SMALL("Xbox Media Audio 2"),
+    .type           = AVMEDIA_TYPE_AUDIO,
+    .id             = AV_CODEC_ID_XMA2,
+    .priv_data_size = sizeof(WMAProDecodeCtx),
+    .init           = decode_init,
+    .close          = decode_end,
+    .decode         = decode_packet,
+    .capabilities   = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1,
+    .flush          = flush,
+    .sample_fmts    = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP,
+                                                      AV_SAMPLE_FMT_NONE },
+};
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 3eec611..f297dd4 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -424,6 +424,8 @@ const AVCodecTag ff_codec_wav_tags[] = {
     { AV_CODEC_ID_WMAV2,           0x0161 },
     { AV_CODEC_ID_WMAPRO,          0x0162 },
     { AV_CODEC_ID_WMALOSSLESS,     0x0163 },
+    { AV_CODEC_ID_XMA1,            0x0165 },
+    { AV_CODEC_ID_XMA2,            0x0166 },
     { AV_CODEC_ID_ADPCM_CT,        0x0200 },
     { AV_CODEC_ID_ATRAC3,          0x0270 },
     { AV_CODEC_ID_ADPCM_G722,      0x028F },
diff --git a/libavformat/riffdec.c b/libavformat/riffdec.c
index 26779e1..5022312 100644
--- a/libavformat/riffdec.c
+++ b/libavformat/riffdec.c
@@ -99,10 +99,12 @@ int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb,
     codec->codec_type  = AVMEDIA_TYPE_AUDIO;
     if (!big_endian) {
         id                 = avio_rl16(pb);
-        codec->channels    = avio_rl16(pb);
-        codec->sample_rate = avio_rl32(pb);
-        bitrate            = avio_rl32(pb) * 8LL;
-        codec->block_align = avio_rl16(pb);
+        if (id != 0x0165) {
+            codec->channels    = avio_rl16(pb);
+            codec->sample_rate = avio_rl32(pb);
+            bitrate            = avio_rl32(pb) * 8LL;
+            codec->block_align = avio_rl16(pb);
+        }
     } else {
         id                 = avio_rb16(pb);
         codec->channels    = avio_rb16(pb);
@@ -126,7 +128,7 @@ int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb,
         codec->codec_id  = ff_wav_codec_get_id(id,
                                                codec->bits_per_coded_sample);
     }
-    if (size >= 18) {  /* We're obviously dealing with WAVEFORMATEX */
+    if (size >= 18 && id != 0x0165) {  /* We're obviously dealing with WAVEFORMATEX */
         int cbSize = avio_rl16(pb); /* cbSize */
         if (big_endian) {
             avpriv_report_missing_feature(codec, "WAVEFORMATEX support for RIFX files\n");
@@ -149,6 +151,19 @@ int ff_get_wav_header(AVFormatContext *s, AVIOContext *pb,
         /* It is possible for the chunk to contain garbage at the end */
         if (size > 0)
             avio_skip(pb, size);
+    } else if (id == 0x0165 && size >= 32) {
+        int nb_streams, i;
+
+        size -= 4;
+        av_freep(&codec->extradata);
+        if (ff_get_extradata(codec, pb, size) < 0)
+            return AVERROR(ENOMEM);
+        nb_streams         = AV_RL16(codec->extradata + 4);
+        codec->sample_rate = AV_RL32(codec->extradata + 12);
+        codec->channels    = 0;
+        bitrate            = 0;
+        for (i = 0; i < nb_streams; i++)
+            codec->channels += codec->extradata[8 + i * 20 + 17];
     }
 
     if (bitrate > INT_MAX) {
-- 
1.9.1



More information about the ffmpeg-devel mailing list