avformat/wavdec: parse XMA2 tag
authorPaul B Mahol <onemda@gmail.com>
Sat, 7 Nov 2015 16:32:19 +0000 (17:32 +0100)
committerPaul B Mahol <onemda@gmail.com>
Tue, 10 Nov 2015 18:42:21 +0000 (19:42 +0100)
Signed-off-by: Paul B Mahol <onemda@gmail.com>
libavformat/wavdec.c

index ef24e16..4190fc6 100644 (file)
@@ -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;