From 9b6f47c44807f14788e909c4c71db58eebba8277 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Mon, 27 May 2013 09:44:27 +0200 Subject: [PATCH] matroskadec: export full wavpack blocks. 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 | 52 ++++++------------------- libavformat/matroskadec.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 40 deletions(-) diff --git a/libavcodec/wavpack.c b/libavcodec/wavpack.c index 3a0fe272a8..4cf9478b8b 100644 --- a/libavcodec/wavpack.c +++ b/libavcodec/wavpack.c @@ -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", diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index 3b801e0845..d88b34fef7 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -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, -- 2.11.0