XMA1 and XMA2 stereo decoders

Signed-off-by: Paul B Mahol <onemda@gmail.com>
This commit is contained in:
Paul B Mahol 2015-11-01 13:15:36 +01:00
parent c8780822ba
commit b456ece557
10 changed files with 164 additions and 21 deletions

View File

@ -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:

View 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.

View 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

View 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);

View 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.

View 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 */
{

View 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, \

View 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 },
};

View 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 },

View 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) {