1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00

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.
This commit is contained in:
Anton Khirnov 2013-05-27 09:44:27 +02:00
parent 5074f4545c
commit 9b6f47c448
2 changed files with 110 additions and 40 deletions

View File

@ -135,7 +135,6 @@ typedef struct WavpackContext {
int fdec_num; int fdec_num;
int multichannel; int multichannel;
int mkv_mode;
int block; int block;
int samples; int samples;
int ch_offset; int ch_offset;
@ -724,14 +723,6 @@ static av_cold int wavpack_decode_init(AVCodecContext *avctx)
: AV_CH_LAYOUT_MONO; : AV_CH_LAYOUT_MONO;
s->multichannel = avctx->channels > 2; 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; s->fdec_num = 0;
@ -783,16 +774,12 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
bytestream2_init(&gb, buf, buf_size); bytestream2_init(&gb, buf, buf_size);
if (!wc->mkv_mode) {
s->samples = bytestream2_get_le32(&gb); s->samples = bytestream2_get_le32(&gb);
if (s->samples != wc->samples) { if (s->samples != wc->samples) {
av_log(avctx, AV_LOG_ERROR, "Mismatching number of samples in " av_log(avctx, AV_LOG_ERROR, "Mismatching number of samples in "
"a sequence: %d and %d\n", wc->samples, s->samples); "a sequence: %d and %d\n", wc->samples, s->samples);
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
} }
} else {
s->samples = wc->samples;
}
s->frame_flags = bytestream2_get_le32(&gb); s->frame_flags = bytestream2_get_le32(&gb);
bpp = av_get_bytes_per_sample(avctx->sample_fmt); bpp = av_get_bytes_per_sample(avctx->sample_fmt);
orig_bpp = ((s->frame_flags & 0x03) + 1) << 3; orig_bpp = ((s->frame_flags & 0x03) + 1) << 3;
@ -811,9 +798,6 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no,
if (s->stereo) if (s->stereo)
samples_r = data[wc->ch_offset + 1]; samples_r = data[wc->ch_offset + 1];
if (wc->mkv_mode)
bytestream2_skip(&gb, 4); // skip block size;
wc->ch_offset += 1 + s->stereo; wc->ch_offset += 1 + s->stereo;
// parse metadata blocks // parse metadata blocks
@ -1154,14 +1138,8 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data,
s->ch_offset = 0; s->ch_offset = 0;
/* determine number of samples */ /* 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); s->samples = AV_RL32(buf + 20);
frame_flags = AV_RL32(buf + 24); frame_flags = AV_RL32(buf + 24);
}
if (s->samples <= 0) { if (s->samples <= 0) {
av_log(avctx, AV_LOG_ERROR, "Invalid number of samples: %d\n", av_log(avctx, AV_LOG_ERROR, "Invalid number of samples: %d\n",
s->samples); s->samples);
@ -1185,17 +1163,11 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data,
} }
while (buf_size > 0) { while (buf_size > 0) {
if (!s->mkv_mode) {
if (buf_size <= WV_HEADER_SIZE) if (buf_size <= WV_HEADER_SIZE)
break; break;
frame_size = AV_RL32(buf + 4) - 12; frame_size = AV_RL32(buf + 4) - 12;
buf += 20; buf += 20;
buf_size -= 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 (frame_size <= 0 || frame_size > buf_size) { if (frame_size <= 0 || frame_size > buf_size) {
av_log(avctx, AV_LOG_ERROR, av_log(avctx, AV_LOG_ERROR,
"Block %d has invalid size (size %d vs. %d bytes left)\n", "Block %d has invalid size (size %d vs. %d bytes left)\n",

View File

@ -1929,6 +1929,88 @@ static int matroska_parse_rm_audio(MatroskaDemuxContext *matroska,
return 0; 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, static int matroska_parse_frame(MatroskaDemuxContext *matroska,
MatroskaTrack *track, MatroskaTrack *track,
AVStream *st, AVStream *st,
@ -1947,6 +2029,18 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska,
return res; 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) if (st->codec->codec_id == AV_CODEC_ID_PRORES)
offset = 8; offset = 8;
@ -1996,6 +2090,10 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska,
} }
return 0; return 0;
fail:
if (pkt_data != data)
av_freep(&pkt_data);
return res;
} }
static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data, static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,