diff --git a/libavformat/wavdec.c b/libavformat/wavdec.c index ef24e1672c..4190fc6093 100644 --- a/libavformat/wavdec.c +++ b/libavformat/wavdec.c @@ -146,6 +146,49 @@ static int wav_parse_fmt_tag(AVFormatContext *s, int64_t size, AVStream **st) return 0; } +static int wav_parse_xma2_tag(AVFormatContext *s, int64_t size, AVStream **st) +{ + AVIOContext *pb = s->pb; + int num_streams, i, channels = 0; + + if (size < 44) + return AVERROR_INVALIDDATA; + + *st = avformat_new_stream(s, NULL); + if (!*st) + return AVERROR(ENOMEM); + + (*st)->codec->codec_type = AVMEDIA_TYPE_AUDIO; + (*st)->codec->codec_id = AV_CODEC_ID_XMA2; + (*st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; + + avio_skip(pb, 1); + num_streams = avio_r8(pb); + if (size < 40 + num_streams * 4) + return AVERROR_INVALIDDATA; + avio_skip(pb, 10); + (*st)->codec->sample_rate = avio_rb32(pb); + avio_skip(pb, 12); + (*st)->duration = avio_rb32(pb); + avio_skip(pb, 8); + + for (i = 0; i < num_streams; i++) { + channels += avio_r8(pb); + avio_skip(pb, 3); + } + (*st)->codec->channels = channels; + + if ((*st)->codec->channels <= 0 || (*st)->codec->sample_rate <= 0) + return AVERROR_INVALIDDATA; + + avpriv_set_pts_info(*st, 64, 1, (*st)->codec->sample_rate); + if (ff_alloc_extradata((*st)->codec, 34)) + return AVERROR(ENOMEM); + memset((*st)->codec->extradata, 0, 34); + + return 0; +} + static inline int wav_parse_bext_string(AVFormatContext *s, const char *key, int length) { @@ -254,7 +297,7 @@ static int wav_read_header(AVFormatContext *s) AVIOContext *pb = s->pb; AVStream *st = NULL; WAVDemuxContext *wav = s->priv_data; - int ret, got_fmt = 0; + int ret, got_fmt = 0, got_xma2 = 0; int64_t next_tag_ofs, data_ofs = -1; wav->unaligned = avio_tell(s->pb) & 1; @@ -319,15 +362,24 @@ static int wav_read_header(AVFormatContext *s) switch (tag) { case MKTAG('f', 'm', 't', ' '): /* only parse the first 'fmt ' tag found */ - if (!got_fmt && (ret = wav_parse_fmt_tag(s, size, &st)) < 0) { + if (!got_xma2 && !got_fmt && (ret = wav_parse_fmt_tag(s, size, &st)) < 0) { return ret; } else if (got_fmt) av_log(s, AV_LOG_WARNING, "found more than one 'fmt ' tag\n"); got_fmt = 1; break; + case MKTAG('X', 'M', 'A', '2'): + /* only parse the first 'XMA2' tag found */ + if (!got_fmt && !got_xma2 && (ret = wav_parse_xma2_tag(s, size, &st)) < 0) { + return ret; + } else if (got_xma2) + av_log(s, AV_LOG_WARNING, "found more than one 'XMA2' tag\n"); + + got_xma2 = 1; + break; case MKTAG('d', 'a', 't', 'a'): - if (!got_fmt) { + if (!got_fmt && !got_xma2) { av_log(s, AV_LOG_ERROR, "found no 'fmt ' tag before the 'data' tag\n"); return AVERROR_INVALIDDATA;