From b456ece55731c545d0bf62641fee2da437861987 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Sun, 1 Nov 2015 13:15:36 +0100 Subject: [PATCH] XMA1 and XMA2 stereo decoders Signed-off-by: Paul B Mahol --- Changelog | 1 + doc/general.texi | 2 + libavcodec/Makefile | 2 + libavcodec/allcodecs.c | 2 + libavcodec/avcodec.h | 2 + libavcodec/codec_desc.c | 14 +++++ libavcodec/version.h | 2 +- libavcodec/wmaprodec.c | 131 +++++++++++++++++++++++++++++++++++----- libavformat/riff.c | 2 + libavformat/riffdec.c | 27 +++++++-- 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 : - 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) {