Here are samples of many stream configurations, generated with Microsoft's encoder: https://mega.nz/#!aQRUnTIT!gWUAfGNLIP62VKV2JEOGoyQ1jbVc3M7_qu7ChXQwb7w
For real XMA used in games, I've only seen 2ch+..+2ch+1/2ch (usual, what FFmpeg supported before) and 1ch+..+1ch (rare, what motivates this patch). This adds support for all possible combinations though. I don't know enough about FATE to set up tests so I hope somebody could help, as the learning curve seems steep. 2017-10-03 23:49 GMT+02:00 <bananaman...@gmail.com>: > From: bnnm <bananaman...@gmail.com> > > Signed-off-by: bnnm <bananaman...@gmail.com> > > Now accepts any combination of 1/2ch streams, described in the RIFF > chunks/extradata > --- > libavcodec/wmaprodec.c | 151 ++++++++++++++++++++++++++++++ > ++----------------- > libavformat/wavdec.c | 20 ++++--- > 2 files changed, 112 insertions(+), 59 deletions(-) > > diff --git a/libavcodec/wmaprodec.c b/libavcodec/wmaprodec.c > index 5c99628c52..77a49c9db8 100644 > --- a/libavcodec/wmaprodec.c > +++ b/libavcodec/wmaprodec.c > @@ -106,6 +106,9 @@ > #define MAX_SUBFRAMES 32 ///< max > number of subframes per channel > #define MAX_BANDS 29 ///< max > number of scale factor bands > #define MAX_FRAMESIZE 32768 ///< maximum > compressed frame size > +#define XMA_MAX_STREAMS 8 > +#define XMA_MAX_CHANNELS 8 > +#define XMA_MAX_CHANNELS_STREAM 2 > > #define WMAPRO_BLOCK_MIN_BITS 6 > ///< log2 of min block size > #define WMAPRO_BLOCK_MAX_BITS 13 > ///< log2 of max block size > @@ -215,7 +218,7 @@ typedef struct WMAProDecodeCtx { > uint8_t drc_gain; ///< gain for the DRC > tool > int8_t skip_frame; ///< skip output step > int8_t parsed_all_subframes; ///< all subframes > decoded? > - uint8_t skip_packets; > + uint8_t skip_packets; ///< packets to skip > to find next packet in a stream (XMA1/2) > > /* subframe/block decode state */ > int16_t subframe_len; ///< current subframe > length > @@ -235,11 +238,13 @@ typedef struct WMAProDecodeCtx { > } WMAProDecodeCtx; > > typedef struct XMADecodeCtx { > - WMAProDecodeCtx xma[4]; > - AVFrame *frames[4]; > + WMAProDecodeCtx xma[XMA_MAX_STREAMS]; > + AVFrame *frames[XMA_MAX_STREAMS]; > int current_stream; > - float samples[8][512 * 64]; > - int offset[4]; > + int num_streams; > + float samples[XMA_MAX_CHANNELS][512 * 64]; > + int offset[XMA_MAX_STREAMS]; > + int start_channel[XMA_MAX_STREAMS]; > } XMADecodeCtx; > > /** > @@ -306,7 +311,7 @@ static av_cold int get_rate(AVCodecContext *avctx) > *@param avctx codec context > *@return 0 on success, -1 otherwise > */ > -static av_cold int decode_init(WMAProDecodeCtx *s, AVCodecContext *avctx) > +static av_cold int decode_init(WMAProDecodeCtx *s, AVCodecContext *avctx, > int num_stream) > { > uint8_t *edata_ptr = avctx->extradata; > unsigned int channel_mask; > @@ -333,18 +338,30 @@ static av_cold int decode_init(WMAProDecodeCtx *s, > AVCodecContext *avctx) > for (i = 0; i < avctx->extradata_size; i++) > av_log(avctx, AV_LOG_DEBUG, "[%x] ", avctx->extradata[i]); > av_log(avctx, AV_LOG_DEBUG, "\n"); > - if (avctx->codec_id == AV_CODEC_ID_XMA2 && (!avctx->extradata || > avctx->extradata_size >= 6)) { > + > + if (avctx->codec_id == AV_CODEC_ID_XMA2 && avctx->extradata_size == > 34) { /* XMA2WAVEFORMATEX */ > + s->decode_flags = 0x10d6; > + s->bits_per_sample = 16; > + channel_mask = 0; //AV_RL32(edata_ptr+2); /* not always in > expected order */ > + if ((num_stream+1) * XMA_MAX_CHANNELS_STREAM > avctx->channels) > /* stream config is 2ch + 2ch + ... + 1/2ch */ > + s->nb_channels = 1; > + else > + s->nb_channels = 2; > + } else if (avctx->codec_id == AV_CODEC_ID_XMA2) { /* XMA2WAVEFORMAT */ > s->decode_flags = 0x10d6; > - channel_mask = avctx->extradata ? AV_RL32(edata_ptr+2) : 0; > s->bits_per_sample = 16; > - } else if (avctx->codec_id == AV_CODEC_ID_XMA1) { > + channel_mask = 0; /* would need to aggregate from all > streams */ > + s->nb_channels = edata_ptr[32 + ((edata_ptr[0]==3)?0:8) + > 4*num_stream + 0]; /* nth stream config */ > + } else if (avctx->codec_id == AV_CODEC_ID_XMA1) { /* XMAWAVEFORMAT */ > s->decode_flags = 0x10d6; > s->bits_per_sample = 16; > - channel_mask = 0; > - } else if (avctx->codec_id == AV_CODEC_ID_WMAPRO && > avctx->extradata_size >= 18) { > + channel_mask = 0; /* would need to aggregate from all > streams */ > + s->nb_channels = edata_ptr[8 + 20*num_stream + 17]; /* nth > stream config */ > + } else if (avctx->codec_id == AV_CODEC_ID_WMAPRO && > 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); > + s->nb_channels = avctx->channels; > > if (s->bits_per_sample > 32 || s->bits_per_sample < 1) { > avpriv_request_sample(avctx, "bits per sample is %d", > s->bits_per_sample); > @@ -355,12 +372,6 @@ static av_cold int decode_init(WMAProDecodeCtx *s, > AVCodecContext *avctx) > return AVERROR_PATCHWELCOME; > } > > - if (avctx->codec_id != AV_CODEC_ID_WMAPRO && avctx->channels > 2) { > - s->nb_channels = 2; > - } else { > - s->nb_channels = avctx->channels; > - } > - > /** generic init */ > s->log2_frame_size = av_log2(avctx->block_align) + 4; > if (s->log2_frame_size > 25) { > @@ -421,6 +432,10 @@ static av_cold int decode_init(WMAProDecodeCtx *s, > AVCodecContext *avctx) > av_log(avctx, AV_LOG_ERROR, "invalid number of channels %d\n", > s->nb_channels); > return AVERROR_INVALIDDATA; > + } else if (avctx->codec_id != AV_CODEC_ID_WMAPRO && s->nb_channels > > XMA_MAX_CHANNELS_STREAM) { > + av_log(avctx, AV_LOG_ERROR, "invalid number of channels per XMA > stream %d\n", > + s->nb_channels); > + return AVERROR_INVALIDDATA; > } else if (s->nb_channels > WMAPRO_MAX_CHANNELS) { > avpriv_request_sample(avctx, > "More than %d channels", > WMAPRO_MAX_CHANNELS); > @@ -566,7 +581,7 @@ static av_cold int wmapro_decode_init(AVCodecContext > *avctx) > { > WMAProDecodeCtx *s = avctx->priv_data; > > - return decode_init(s, avctx); > + return decode_init(s, avctx, 0); > } > > /** > @@ -1750,14 +1765,17 @@ static int xma_decode_packet(AVCodecContext > *avctx, void *data, > AVFrame *frame = data; > int i, ret, offset = INT_MAX; > > + /* decode current stream packet */ > ret = decode_packet(avctx, &s->xma[s->current_stream], > s->frames[s->current_stream], > &got_stream_frame_ptr, avpkt); > > + /* copy stream samples (1/2ch) to sample buffer (Nch) */ > if (got_stream_frame_ptr) { > - memcpy(&s->samples[s->current_stream * 2 + > 0][s->offset[s->current_stream] * 512], > + int start_ch = s->start_channel[s->current_stream]; > + memcpy(&s->samples[start_ch + 0][s->offset[s->current_stream] * > 512], > s->frames[s->current_stream]->extended_data[0], 512 * 4); > - if (avctx->channels > 1) > - memcpy(&s->samples[s->current_stream * 2 + > 1][s->offset[s->current_stream] * 512], > + if (s->xma[s->current_stream].nb_channels > 1) > + memcpy(&s->samples[start_ch + 1][s->offset[s->current_stream] > * 512], > s->frames[s->current_stream]->extended_data[1], 512 * > 4); > s->offset[s->current_stream]++; > } else if (ret < 0) { > @@ -1766,58 +1784,57 @@ static int xma_decode_packet(AVCodecContext > *avctx, void *data, > return ret; > } > > + /* find next XMA packet's owner stream, and update. > + * XMA streams find their packets following packet_skips > + * (at start there is one packet per stream, then interleave > non-linearly). */ > if (s->xma[s->current_stream].packet_done || > s->xma[s->current_stream].packet_loss) { > - int bret; > - > - if (s->xma[s->current_stream].skip_packets == 0) { > - ; > - } else if (s->xma[0].skip_packets == 0 && avctx->channels >= 2) { > - s->current_stream = 0; > - } else if (s->xma[1].skip_packets == 0 && avctx->channels >= 4) { > - s->current_stream = 1; > - } else if (s->xma[2].skip_packets == 0 && avctx->channels >= 6) { > - s->current_stream = 2; > - } else if (s->xma[3].skip_packets == 0 && avctx->channels == 8) { > - s->current_stream = 3; > - } else { > + > + /* select stream with 0 skip_packets (= uses next packet) */ > + if (s->xma[s->current_stream].skip_packets != 0) { > int min[2]; > > min[0] = s->xma[0].skip_packets; > min[1] = i = 0; > > - for (i = 1; i < avctx->channels / 2; i++) { > + for (i = 1; i < s->num_streams; i++) { > if (s->xma[i].skip_packets < min[0]) { > - min[1] = i; > min[0] = s->xma[i].skip_packets; > + min[1] = i; > } > } > > s->current_stream = min[1]; > } > > - for (i = 0; i < avctx->channels / 2; i++) { > + /* all other streams skip next packet */ > + for (i = 0; i < s->num_streams; i++) { > s->xma[i].skip_packets = FFMAX(0, s->xma[i].skip_packets - 1); > } > > - for (i = 0; i < (avctx->channels + 1) / 2; i++) { > + /* copy samples from buffer to output if possible */ > + for (i = 0; i < s->num_streams; i++) { > offset = FFMIN(offset, s->offset[i]); > } > - > if (offset > 0) { > + int bret; > + > frame->nb_samples = 512 * offset; > if ((bret = ff_get_buffer(avctx, frame, 0)) < 0) > return bret; > > - for (i = 0; i < (avctx->channels + 1) / 2; i++) { > - memcpy(frame->extended_data[i * 2 + 0], s->samples[i * 2 > + 0], frame->nb_samples * 4); > - if (avctx->channels > 1) > - memcpy(frame->extended_data[i * 2 + 1], s->samples[i > * 2 + 1], frame->nb_samples * 4); > + /* copy samples buffer (Nch) to frame samples (Nch), move > unconsumed samples */ > + for (i = 0; i < s->num_streams; i++) { > + int start_ch = s->start_channel[i]; > + memcpy(frame->extended_data[start_ch + 0], > s->samples[start_ch + 0], frame->nb_samples * 4); > + if (s->xma[i].nb_channels > 1) > + memcpy(frame->extended_data[start_ch + 1], > s->samples[start_ch + 1], frame->nb_samples * 4); > + > s->offset[i] -= offset; > if (s->offset[i]) { > - memmove(s->samples[i * 2 + 0], s->samples[i * 2 + 0] > + frame->nb_samples, s->offset[i] * 4 * 512); > - if (avctx->channels > 1) > - memmove(s->samples[i * 2 + 1], s->samples[i * 2 + > 1] + frame->nb_samples, s->offset[i] * 4 * 512); > + memmove(s->samples[start_ch + 0], s->samples[start_ch > + 0] + frame->nb_samples, s->offset[i] * 4 * 512); > + if (s->xma[i].nb_channels > 1) > + memmove(s->samples[start_ch + 1], > s->samples[start_ch + 1] + frame->nb_samples, s->offset[i] * 4 * 512); > } > } > > @@ -1831,13 +1848,40 @@ static int xma_decode_packet(AVCodecContext > *avctx, void *data, > static av_cold int xma_decode_init(AVCodecContext *avctx) > { > XMADecodeCtx *s = avctx->priv_data; > - int i, ret; > + int i, ret, start_channels = 0; > > - if (avctx->channels <= 0 || avctx->channels > 8) > + if (avctx->channels <= 0 || avctx->extradata_size == 0) > return AVERROR_INVALIDDATA; > > - for (i = 0; i < (avctx->channels + 1) / 2; i++) { > - ret = decode_init(&s->xma[i], avctx); > + /* get stream config */ > + if (avctx->codec_id == AV_CODEC_ID_XMA2 && avctx->extradata_size == > 34) { /* XMA2WAVEFORMATEX */ > + s->num_streams = (avctx->channels + 1) / 2; > + } else if (avctx->codec_id == AV_CODEC_ID_XMA2 && > avctx->extradata_size >= 2) { /* XMA2WAVEFORMAT */ > + s->num_streams = avctx->extradata[1]; > + if (avctx->extradata_size != (32 + ((avctx->extradata[0]==3)?0:8) > + 4*s->num_streams)) { > + av_log(avctx, AV_LOG_ERROR, "Incorrect XMA2 extradata > size\n"); > + return AVERROR(EINVAL); > + } > + } else if (avctx->codec_id == AV_CODEC_ID_XMA1 && > avctx->extradata_size >= 4) { /* XMAWAVEFORMAT */ > + s->num_streams = avctx->extradata[4]; > + if (avctx->extradata_size != (8 + 20*s->num_streams)) { > + av_log(avctx, AV_LOG_ERROR, "Incorrect XMA1 extradata > size\n"); > + return AVERROR(EINVAL); > + } > + } else { > + av_log(avctx, AV_LOG_ERROR, "Incorrect XMA config\n"); > + return AVERROR(EINVAL); > + } > + > + /* encoder supports up to 64 streams / 64*2 channels (would have to > alloc arrays) */ > + if (avctx->channels > XMA_MAX_CHANNELS || s->num_streams > > XMA_MAX_STREAMS) { > + avpriv_request_sample(avctx, "More than %d channels in %d > streams", XMA_MAX_CHANNELS, s->num_streams); > + return AVERROR_PATCHWELCOME; > + } > + > + /* init all streams (several streams of 1/2ch make Nch files) */ > + for (i = 0; i < s->num_streams; i++) { > + ret = decode_init(&s->xma[i], avctx, i); > if (ret < 0) > return ret; > s->frames[i] = av_frame_alloc(); > @@ -1848,6 +1892,8 @@ static av_cold int xma_decode_init(AVCodecContext > *avctx) > return AVERROR(ENOMEM); > } > > + s->start_channel[i] = start_channels; > + start_channels += s->xma[i].nb_channels; > } > > return ret; > @@ -1858,7 +1904,7 @@ static av_cold int xma_decode_end(AVCodecContext > *avctx) > XMADecodeCtx *s = avctx->priv_data; > int i; > > - for (i = 0; i < avctx->channels / 2; i++) { > + for (i = 0; i < s->num_streams; i++) { > decode_end(&s->xma[i]); > av_frame_free(&s->frames[i]); > } > @@ -1894,7 +1940,8 @@ static void xma_flush(AVCodecContext *avctx) > { > XMADecodeCtx *s = avctx->priv_data; > int i; > - for (i = 0; i < (avctx->channels + 1) / 2; i++) > + > + for (i = 0; i < s->num_streams; i++) > flush(&s->xma[i]); > > memset(s->offset, 0, sizeof(s->offset)); > diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c > index 81dbc9f16e..b016185a1b 100644 > --- a/libavformat/wavdec.c > +++ b/libavformat/wavdec.c > @@ -180,9 +180,9 @@ static int wav_parse_fmt_tag(AVFormatContext *s, > int64_t size, AVStream **st) > static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream > **st) > { > AVIOContext *pb = s->pb; > - int num_streams, i, channels = 0; > + int version, num_streams, i, channels = 0; > > - if (size < 44) > + if (size < 36) > return AVERROR_INVALIDDATA; > > *st = avformat_new_stream(s, NULL); > @@ -193,13 +193,17 @@ static int wav_parse_xma2_tag(AVFormatContext *s, > int64_t size, AVStream **st) > (*st)->codecpar->codec_id = AV_CODEC_ID_XMA2; > (*st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; > > - avio_skip(pb, 1); > + version = avio_r8(pb); > + if (version != 3 && version != 4) > + return AVERROR_INVALIDDATA; > num_streams = avio_r8(pb); > - if (size < 40 + num_streams * 4) > + if (size != (32 + ((version==3)?0:8) + 4*num_streams)) > return AVERROR_INVALIDDATA; > avio_skip(pb, 10); > (*st)->codecpar->sample_rate = avio_rb32(pb); > - avio_skip(pb, 12); > + if (version == 4) > + avio_skip(pb, 8); > + avio_skip(pb, 4); > (*st)->duration = avio_rb32(pb); > avio_skip(pb, 8); > > @@ -213,9 +217,11 @@ static int wav_parse_xma2_tag(AVFormatContext *s, > int64_t size, AVStream **st) > return AVERROR_INVALIDDATA; > > avpriv_set_pts_info(*st, 64, 1, (*st)->codecpar->sample_rate); > - if (ff_alloc_extradata((*st)->codecpar, 34)) > + > + avio_seek(pb, -size, SEEK_CUR); > + av_freep(&(*st)->codecpar->extradata); > + if (ff_get_extradata(s, (*st)->codecpar, pb, size) < 0) > return AVERROR(ENOMEM); > - memset((*st)->codecpar->extradata, 0, 34); > > return 0; > } > -- > 2.11.0.windows.3 > > _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel