aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul B Mahol <onemda@gmail.com>2015-11-01 13:15:36 +0100
committerPaul B Mahol <onemda@gmail.com>2015-11-06 13:45:50 +0100
commitb456ece55731c545d0bf62641fee2da437861987 (patch)
treec022717fc064f9c7b3b7f5dd3f13e51085b2e7a3
parentc8780822bacc38a8d84c882d564b07dd152366ed (diff)
downloadffmpeg-b456ece55731c545d0bf62641fee2da437861987.tar.gz
XMA1 and XMA2 stereo decoders
Signed-off-by: Paul B Mahol <onemda@gmail.com>
-rw-r--r--Changelog1
-rw-r--r--doc/general.texi2
-rw-r--r--libavcodec/Makefile2
-rw-r--r--libavcodec/allcodecs.c2
-rw-r--r--libavcodec/avcodec.h2
-rw-r--r--libavcodec/codec_desc.c14
-rw-r--r--libavcodec/version.h2
-rw-r--r--libavcodec/wmaprodec.c131
-rw-r--r--libavformat/riff.c2
-rw-r--r--libavformat/riffdec.c27
10 files changed, 164 insertions, 21 deletions
diff --git a/Changelog b/Changelog
index 91955dabe2..04f094b615 100644
--- 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:
diff --git a/doc/general.texi b/doc/general.texi
index 4d25224e37..d1c26dcaf3 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -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.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 67fb72ad9d..06d3ddd918 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 095f8128e2..4a21fe7b4b 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 91cd6a829f..95cdf5a823 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 c6117400a8..9cad3e6e47 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/version.h b/libavcodec/version.h
index cc4e2367b6..1e21f155fc 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -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, \
diff --git a/libavcodec/wmaprodec.c b/libavcodec/wmaprodec.c
index 19dc335b32..3b759f6158 100644
--- a/libavcodec/wmaprodec.c
+++ b/libavcodec/wmaprodec.c
@@ -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 },
+};
diff --git a/libavformat/riff.c b/libavformat/riff.c
index 3eec611b1e..f297dd4a3b 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 26779e1bfb..e75aee5de9 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,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) {