summaryrefslogtreecommitdiff
path: root/libavcodec/wmaprodec.c
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 /libavcodec/wmaprodec.c
parentc8780822bacc38a8d84c882d564b07dd152366ed (diff)
XMA1 and XMA2 stereo decoders
Signed-off-by: Paul B Mahol <onemda@gmail.com>
Diffstat (limited to 'libavcodec/wmaprodec.c')
-rw-r--r--libavcodec/wmaprodec.c131
1 files changed, 116 insertions, 15 deletions
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 },
+};