XMA1 and XMA2 stereo decoders
authorPaul B Mahol <onemda@gmail.com>
Sun, 1 Nov 2015 12:15:36 +0000 (13:15 +0100)
committerPaul B Mahol <onemda@gmail.com>
Fri, 6 Nov 2015 12:45:50 +0000 (13:45 +0100)
Signed-off-by: Paul B Mahol <onemda@gmail.com>
Changelog
doc/general.texi
libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/avcodec.h
libavcodec/codec_desc.c
libavcodec/version.h
libavcodec/wmaprodec.c
libavformat/riff.c
libavformat/riffdec.c

index 91955da..04f094b 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -30,6 +30,7 @@ version <next>:
 - innoHeim/Rsupport Screen Capture Codec decoder
 - ADPCM AICA decoder
 - Interplay ACM demuxer and audio decoder
+- XMA1 & XMA2 decoder
 
 
 version 2.8:
index 4d25224..d1c26dc 100644 (file)
@@ -1076,6 +1076,8 @@ following image formats are supported:
 @item Windows Media Audio Lossless @tab  @tab  X
 @item Windows Media Audio Pro @tab    @tab  X
 @item Windows Media Audio Voice @tab  @tab  X
+@item Xbox Media Audio 1     @tab     @tab  X
+@item Xbox Media Audio 2     @tab     @tab  X
 @end multitable
 
 @code{X} means that encoding (resp. decoding) is supported.
index 67fb72a..06d3ddd 100644 (file)
@@ -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
index 095f812..4a21fe7 100644 (file)
@@ -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);
index 91cd6a8..95cdf5a 100644 (file)
@@ -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.
index c611740..9cad3e6 100644 (file)
@@ -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 */
     {
index cc4e236..1e21f15 100644 (file)
@@ -29,7 +29,7 @@
 #include "libavutil/version.h"
 
 #define LIBAVCODEC_VERSION_MAJOR  57
-#define LIBAVCODEC_VERSION_MINOR  14
+#define LIBAVCODEC_VERSION_MINOR  15
 #define LIBAVCODEC_VERSION_MICRO 100
 
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
index 19dc335..3b759f6 100644 (file)
@@ -206,9 +206,11 @@ 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;
 
     /* 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
@@ -268,6 +270,21 @@ static av_cold int decode_end(AVCodecContext *avctx)
     return 0;
 }
 
+static av_cold int get_rate(AVCodecContext *avctx)
+{
+    if (avctx->codec_id != AV_CODEC_ID_WMAPRO) { // XXX: is this really only for XMA?
+        if (avctx->sample_rate > 44100)
+            return 48000;
+        else if (avctx->sample_rate > 32000)
+            return 44100;
+        else if (avctx->sample_rate > 24000)
+            return 32000;
+        return 24000;
+    }
+
+    return avctx->sample_rate;
+}
+
 /**
  *@brief Initialize the decoder.
  *@param avctx codec context
@@ -282,6 +299,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 +316,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 +348,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,17 +361,25 @@ 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 */
+
     s->packet_loss = 1;
     s->len_prefix  = (s->decode_flags & 0x40);
 
     /** get frame len */
-    bits = ff_wma_get_frame_len_bits(avctx->sample_rate, 3, s->decode_flags);
-    if (bits > WMAPRO_BLOCK_MAX_BITS) {
-        avpriv_request_sample(avctx, "14-bit block sizes");
-        return AVERROR_PATCHWELCOME;
+    if (avctx->codec_id == AV_CODEC_ID_WMAPRO) {
+        bits = ff_wma_get_frame_len_bits(avctx->sample_rate, 3, s->decode_flags);
+        if (bits > WMAPRO_BLOCK_MAX_BITS) {
+            avpriv_request_sample(avctx, "14-bit block sizes");
+            return AVERROR_PATCHWELCOME;
+        }
+        s->samples_per_frame = 1 << bits;
+    } else {
+        s->samples_per_frame = 512;
     }
-    s->samples_per_frame = 1 << bits;
 
     /** subframe info */
     log2_max_num_subframes       = ((s->decode_flags & 0x38) >> 3);
@@ -417,12 +468,12 @@ static av_cold int decode_init(AVCodecContext *avctx)
         int subframe_len = s->samples_per_frame >> i;
         int x;
         int band = 1;
+        int rate = get_rate(avctx);
 
         s->sfb_offsets[i][0] = 0;
 
         for (x = 0; x < MAX_BANDS-1 && s->sfb_offsets[i][band - 1] < subframe_len; x++) {
-            int offset = (subframe_len * 2 * critical_freq[x])
-                          / s->avctx->sample_rate + 2;
+            int offset = (subframe_len * 2 * critical_freq[x]) / rate + 2;
             offset &= ~3;
             if (offset > s->sfb_offsets[i][band - 1])
                 s->sfb_offsets[i][band++] = offset;
@@ -1535,32 +1586,52 @@ 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;
 
         /** sanity check for the buffer length */
-        if (buf_size < avctx->block_align) {
+        if (avctx->codec_id == AV_CODEC_ID_WMAPRO && buf_size < avctx->block_align) {
             av_log(avctx, AV_LOG_ERROR, "Input packet too small (%d < %d)\n",
                    buf_size, avctx->block_align);
             return AVERROR_INVALIDDATA;
         }
 
-        s->next_packet_start = buf_size - avctx->block_align;
-        buf_size = avctx->block_align;
+        if (avctx->codec_id == AV_CODEC_ID_WMAPRO) {
+            s->next_packet_start = buf_size - avctx->block_align;
+            buf_size = avctx->block_align;
+        } else {
+            s->next_packet_start = buf_size - FFMIN(buf_size, avctx->block_align);
+            buf_size = FFMIN(buf_size, avctx->block_align);
+        }
         s->buf_bit_size = buf_size << 3;
 
         /** 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) {
+            skip_bits(gb, 3);
+            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 +1742,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 },
+};
index 3eec611..f297dd4 100644 (file)
@@ -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 },
index 26779e1..e75aee5 100644 (file)
@@ -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,21 @@ 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;
+        if (size < 8 + nb_streams * 20)
+            return AVERROR_INVALIDDATA;
+        for (i = 0; i < nb_streams; i++)
+            codec->channels += codec->extradata[8 + i * 20 + 17];
     }
 
     if (bitrate > INT_MAX) {