matroskadec: export full wavpack blocks.
authorAnton Khirnov <anton@khirnov.net>
Mon, 27 May 2013 07:44:27 +0000 (09:44 +0200)
committerAnton Khirnov <anton@khirnov.net>
Tue, 28 May 2013 06:14:35 +0000 (08:14 +0200)
This allows us to get rid of demuxer-specific hacks in the decoder and
will allow streamcopy from matroska once we have a wavpack muxer.

libavcodec/wavpack.c
libavformat/matroskadec.c

index 3a0fe27..4cf9478 100644 (file)
@@ -135,7 +135,6 @@ typedef struct WavpackContext {
     int fdec_num;
 
     int multichannel;
-    int mkv_mode;
     int block;
     int samples;
     int ch_offset;
@@ -724,14 +723,6 @@ static av_cold int wavpack_decode_init(AVCodecContext *avctx)
                                                        : AV_CH_LAYOUT_MONO;
 
     s->multichannel = avctx->channels > 2;
-    /* lavf demuxer does not provide extradata, Matroska stores 0x403
-     * there, use this to detect decoding mode for multichannel */
-    s->mkv_mode = 0;
-    if (s->multichannel && avctx->extradata && avctx->extradata_size == 2) {
-        int ver = AV_RL16(avctx->extradata);
-        if (ver >= 0x402 && ver <= 0x410)
-            s->mkv_mode = 1;
-    }
 
     s->fdec_num = 0;
 
@@ -783,15 +774,11 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
 
     bytestream2_init(&gb, buf, buf_size);
 
-    if (!wc->mkv_mode) {
-        s->samples = bytestream2_get_le32(&gb);
-        if (s->samples != wc->samples) {
-            av_log(avctx, AV_LOG_ERROR, "Mismatching number of samples in "
-                   "a sequence: %d and %d\n", wc->samples, s->samples);
-            return AVERROR_INVALIDDATA;
-        }
-    } else {
-        s->samples = wc->samples;
+    s->samples = bytestream2_get_le32(&gb);
+    if (s->samples != wc->samples) {
+        av_log(avctx, AV_LOG_ERROR, "Mismatching number of samples in "
+               "a sequence: %d and %d\n", wc->samples, s->samples);
+        return AVERROR_INVALIDDATA;
     }
     s->frame_flags = bytestream2_get_le32(&gb);
     bpp            = av_get_bytes_per_sample(avctx->sample_fmt);
@@ -811,9 +798,6 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
     if (s->stereo)
         samples_r = data[wc->ch_offset + 1];
 
-    if (wc->mkv_mode)
-        bytestream2_skip(&gb, 4);  // skip block size;
-
     wc->ch_offset += 1 + s->stereo;
 
     // parse metadata blocks
@@ -1154,14 +1138,8 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data,
     s->ch_offset = 0;
 
     /* determine number of samples */
-    if (s->mkv_mode) {
-        s->samples  = AV_RL32(buf);
-        buf        += 4;
-        frame_flags = AV_RL32(buf);
-    } else {
-        s->samples  = AV_RL32(buf + 20);
-        frame_flags = AV_RL32(buf + 24);
-    }
+    s->samples  = AV_RL32(buf + 20);
+    frame_flags = AV_RL32(buf + 24);
     if (s->samples <= 0) {
         av_log(avctx, AV_LOG_ERROR, "Invalid number of samples: %d\n",
                s->samples);
@@ -1185,17 +1163,11 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data,
     }
 
     while (buf_size > 0) {
-        if (!s->mkv_mode) {
-            if (buf_size <= WV_HEADER_SIZE)
-                break;
-            frame_size = AV_RL32(buf + 4) - 12;
-            buf       += 20;
-            buf_size  -= 20;
-        } else {
-            if (buf_size < 12) // MKV files can have zero flags after last block
-                break;
-            frame_size = AV_RL32(buf + 8) + 12;
-        }
+        if (buf_size <= WV_HEADER_SIZE)
+            break;
+        frame_size = AV_RL32(buf + 4) - 12;
+        buf       += 20;
+        buf_size  -= 20;
         if (frame_size <= 0 || frame_size > buf_size) {
             av_log(avctx, AV_LOG_ERROR,
                    "Block %d has invalid size (size %d vs. %d bytes left)\n",
index 3b801e0..d88b34f 100644 (file)
@@ -1929,6 +1929,88 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska,
 
     return 0;
 }
+
+/* reconstruct full wavpack blocks from mangled matroska ones */
+static int matroska_parse_wavpack(MatroskaTrack *track, uint8_t *src,
+                                  uint8_t **pdst, int *size)
+{
+    uint8_t *dst = NULL;
+    int dstlen   = 0;
+    int srclen   = *size;
+    uint32_t samples;
+    uint16_t ver;
+    int ret, offset = 0;
+
+    if (srclen < 12 || track->stream->codec->extradata_size < 2)
+        return AVERROR_INVALIDDATA;
+
+    ver = AV_RL16(track->stream->codec->extradata);
+
+    samples = AV_RL32(src);
+    src    += 4;
+    srclen -= 4;
+
+    while (srclen >= 8) {
+        int multiblock;
+        uint32_t blocksize;
+        uint8_t *tmp;
+
+        uint32_t flags = AV_RL32(src);
+        uint32_t crc   = AV_RL32(src + 4);
+        src    += 8;
+        srclen -= 8;
+
+        multiblock = (flags & 0x1800) != 0x1800;
+        if (multiblock) {
+            if (srclen < 4) {
+                ret = AVERROR_INVALIDDATA;
+                goto fail;
+            }
+            blocksize = AV_RL32(src);
+            src    += 4;
+            srclen -= 4;
+        } else
+            blocksize = srclen;
+
+        if (blocksize > srclen) {
+            ret = AVERROR_INVALIDDATA;
+            goto fail;
+        }
+
+        tmp = av_realloc(dst, dstlen + blocksize + 32);
+        if (!tmp) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+        dst     = tmp;
+        dstlen += blocksize + 32;
+
+        AV_WL32(dst + offset,      MKTAG('w', 'v', 'p', 'k')); // tag
+        AV_WL32(dst + offset + 4,  blocksize + 24);            // blocksize - 8
+        AV_WL16(dst + offset + 8,  ver);                       // version
+        AV_WL16(dst + offset + 10, 0);                         // track/index_no
+        AV_WL32(dst + offset + 12, 0);                         // total samples
+        AV_WL32(dst + offset + 16, 0);                         // block index
+        AV_WL32(dst + offset + 20, samples);                   // number of samples
+        AV_WL32(dst + offset + 24, flags);                     // flags
+        AV_WL32(dst + offset + 28, crc);                       // crc
+        memcpy (dst + offset + 32, src, blocksize);            // block data
+
+        src    += blocksize;
+        srclen -= blocksize;
+        offset += blocksize + 32;
+    }
+
+    *pdst = dst;
+    *size = dstlen;
+
+    return 0;
+
+fail:
+    av_freep(&dst);
+    return ret;
+}
+
 static int matroska_parse_frame(MatroskaDemuxContext *matroska,
                                 MatroskaTrack *track,
                                 AVStream *st,
@@ -1947,6 +2029,18 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska,
             return res;
     }
 
+    if (st->codec->codec_id == AV_CODEC_ID_WAVPACK) {
+        uint8_t *wv_data;
+        res = matroska_parse_wavpack(track, pkt_data, &wv_data, &pkt_size);
+        if (res < 0) {
+            av_log(matroska->ctx, AV_LOG_ERROR, "Error parsing a wavpack block.\n");
+            goto fail;
+        }
+        if (pkt_data != data)
+            av_freep(&pkt_data);
+        pkt_data = wv_data;
+    }
+
     if (st->codec->codec_id == AV_CODEC_ID_PRORES)
         offset = 8;
 
@@ -1996,6 +2090,10 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska,
     }
 
     return 0;
+fail:
+    if (pkt_data != data)
+        av_freep(&pkt_data);
+    return res;
 }
 
 static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,