Commit 9b6f47c4 authored by Anton Khirnov's avatar Anton Khirnov

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.
parent 5074f454
......@@ -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",
......
......@@ -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,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment