Commit 5074f454 authored by Anton Khirnov's avatar Anton Khirnov

wavpack demuxer: export full wavpack blocks.

Currently the demuxer shaves the blocks and exports only the
information that is useful to the decoder.

Exporting the blocks just as they are stored is simpler to understand
and will make remuxing wavpack easier.
parent 72dee5f4
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
* WavPack lossless audio decoder * WavPack lossless audio decoder
*/ */
#define WV_HEADER_SIZE 32
#define WV_MONO 0x00000004 #define WV_MONO 0x00000004
#define WV_JOINT_STEREO 0x00000010 #define WV_JOINT_STEREO 0x00000010
#define WV_FALSE_STEREO 0x40000000 #define WV_FALSE_STEREO 0x40000000
...@@ -1145,7 +1147,7 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data, ...@@ -1145,7 +1147,7 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data,
AVFrame *frame = data; AVFrame *frame = data;
int frame_size, ret, frame_flags; int frame_size, ret, frame_flags;
if (avpkt->size < 12 + s->multichannel * 4) if (avpkt->size <= WV_HEADER_SIZE)
return AVERROR_INVALIDDATA; return AVERROR_INVALIDDATA;
s->block = 0; s->block = 0;
...@@ -1157,13 +1159,8 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data, ...@@ -1157,13 +1159,8 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data,
buf += 4; buf += 4;
frame_flags = AV_RL32(buf); frame_flags = AV_RL32(buf);
} else { } else {
if (s->multichannel) { s->samples = AV_RL32(buf + 20);
s->samples = AV_RL32(buf + 4); frame_flags = AV_RL32(buf + 24);
frame_flags = AV_RL32(buf + 8);
} else {
s->samples = AV_RL32(buf);
frame_flags = AV_RL32(buf + 4);
}
} }
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",
...@@ -1188,18 +1185,16 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data, ...@@ -1188,18 +1185,16 @@ static int wavpack_decode_frame(AVCodecContext *avctx, void *data,
} }
while (buf_size > 0) { while (buf_size > 0) {
if (!s->multichannel) { if (!s->mkv_mode) {
frame_size = buf_size; if (buf_size <= WV_HEADER_SIZE)
break;
frame_size = AV_RL32(buf + 4) - 12;
buf += 20;
buf_size -= 20;
} else { } else {
if (!s->mkv_mode) { if (buf_size < 12) // MKV files can have zero flags after last block
frame_size = AV_RL32(buf) - 12; break;
buf += 4; frame_size = AV_RL32(buf + 8) + 12;
buf_size -= 4;
} 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,
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
// specs say that maximum block size is 1Mb // specs say that maximum block size is 1Mb
#define WV_BLOCK_LIMIT 1047576 #define WV_BLOCK_LIMIT 1047576
#define WV_EXTRA_SIZE 12 #define WV_HEADER_SIZE 32
#define WV_START_BLOCK 0x0800 #define WV_START_BLOCK 0x0800
#define WV_END_BLOCK 0x1000 #define WV_END_BLOCK 0x1000
...@@ -56,13 +56,13 @@ static const int wv_rates[16] = { ...@@ -56,13 +56,13 @@ static const int wv_rates[16] = {
}; };
typedef struct { typedef struct {
uint8_t block_header[WV_HEADER_SIZE];
uint32_t blksize, flags; uint32_t blksize, flags;
int rate, chan, bpp; int rate, chan, bpp;
uint32_t chmask; uint32_t chmask;
uint32_t samples, soff; uint32_t samples, soff;
int multichannel; int multichannel;
int block_parsed; int block_parsed;
uint8_t extra[WV_EXTRA_SIZE];
int64_t pos; int64_t pos;
int64_t apetag_start; int64_t apetag_start;
...@@ -80,12 +80,11 @@ static int wv_probe(AVProbeData *p) ...@@ -80,12 +80,11 @@ static int wv_probe(AVProbeData *p)
return 0; return 0;
} }
static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb, static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb)
int append)
{ {
WVContext *wc = ctx->priv_data; WVContext *wc = ctx->priv_data;
uint32_t tag, ver; uint32_t ver;
int size; int size, ret;
int rate, bpp, chan; int rate, bpp, chan;
uint32_t chmask; uint32_t chmask;
...@@ -95,33 +94,30 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb, ...@@ -95,33 +94,30 @@ static int wv_read_block_header(AVFormatContext *ctx, AVIOContext *pb,
if (wc->apetag_start && wc->pos >= wc->apetag_start) if (wc->apetag_start && wc->pos >= wc->apetag_start)
return AVERROR_EOF; return AVERROR_EOF;
if (!append) { ret = avio_read(pb, wc->block_header, WV_HEADER_SIZE);
tag = avio_rl32(pb); if (ret != WV_HEADER_SIZE)
if (tag != MKTAG('w', 'v', 'p', 'k')) return (ret < 0) ? ret : AVERROR_EOF;
return AVERROR_INVALIDDATA;
size = avio_rl32(pb); if (AV_RL32(wc->block_header) != MKTAG('w', 'v', 'p', 'k'))
if (size < 24 || size > WV_BLOCK_LIMIT) { return AVERROR_INVALIDDATA;
av_log(ctx, AV_LOG_ERROR, "Incorrect block size %i\n", size);
return AVERROR_INVALIDDATA; size = AV_RL32(wc->block_header + 4);
} if (size < 24 || size > WV_BLOCK_LIMIT) {
wc->blksize = size; av_log(ctx, AV_LOG_ERROR, "Incorrect block size %i\n", size);
ver = avio_rl16(pb); return AVERROR_INVALIDDATA;
if (ver < 0x402 || ver > 0x410) { }
av_log(ctx, AV_LOG_ERROR, "Unsupported version %03X\n", ver); wc->blksize = size;
return AVERROR_PATCHWELCOME; ver = AV_RL32(wc->block_header + 8);
} if (ver < 0x402 || ver > 0x410) {
avio_r8(pb); // track no av_log(ctx, AV_LOG_ERROR, "Unsupported version %03X\n", ver);
avio_r8(pb); // track sub index return AVERROR_PATCHWELCOME;
wc->samples = avio_rl32(pb); // total samples in file
wc->soff = avio_rl32(pb); // offset in samples of current block
avio_read(pb, wc->extra, WV_EXTRA_SIZE);
} else {
size = wc->blksize;
} }
wc->flags = AV_RL32(wc->extra + 4); wc->samples = AV_RL32(wc->block_header + 12); // total samples in file
wc->soff = AV_RL32(wc->block_header + 16); // offset in samples of current block
wc->flags = AV_RL32(wc->block_header + 24);
/* Blocks with zero samples don't contain actual audio information /* Blocks with zero samples don't contain actual audio information
* and should be ignored */ * and should be ignored */
if (!AV_RN32(wc->extra)) if (!AV_RN32(wc->block_header + 20))
return 0; return 0;
// parse flags // parse flags
bpp = ((wc->flags & 3) + 1) << 3; bpp = ((wc->flags & 3) + 1) << 3;
...@@ -235,9 +231,9 @@ static int wv_read_header(AVFormatContext *s) ...@@ -235,9 +231,9 @@ static int wv_read_header(AVFormatContext *s)
wc->block_parsed = 0; wc->block_parsed = 0;
for (;;) { for (;;) {
if ((ret = wv_read_block_header(s, pb, 0)) < 0) if ((ret = wv_read_block_header(s, pb)) < 0)
return ret; return ret;
if (!AV_RN32(wc->extra)) if (!AV_RL32(wc->block_header + 20))
avio_skip(pb, wc->blksize - 24); avio_skip(pb, wc->blksize - 24);
else else
break; break;
...@@ -272,75 +268,49 @@ static int wv_read_packet(AVFormatContext *s, AVPacket *pkt) ...@@ -272,75 +268,49 @@ static int wv_read_packet(AVFormatContext *s, AVPacket *pkt)
{ {
WVContext *wc = s->priv_data; WVContext *wc = s->priv_data;
int ret; int ret;
int size, ver, off; int off;
int64_t pos; int64_t pos;
uint32_t block_samples; uint32_t block_samples;
if (s->pb->eof_reached) if (s->pb->eof_reached)
return AVERROR_EOF; return AVERROR_EOF;
if (wc->block_parsed) { if (wc->block_parsed) {
if ((ret = wv_read_block_header(s, s->pb, 0)) < 0) if ((ret = wv_read_block_header(s, s->pb)) < 0)
return ret; return ret;
} }
pos = wc->pos; pos = wc->pos;
off = wc->multichannel ? 4 : 0; if (av_new_packet(pkt, wc->blksize + WV_HEADER_SIZE) < 0)
if (av_new_packet(pkt, wc->blksize + WV_EXTRA_SIZE + off) < 0)
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
if (wc->multichannel) memcpy(pkt->data, wc->block_header, WV_HEADER_SIZE);
AV_WL32(pkt->data, wc->blksize + WV_EXTRA_SIZE + 12); ret = avio_read(s->pb, pkt->data + WV_HEADER_SIZE, wc->blksize);
memcpy(pkt->data + off, wc->extra, WV_EXTRA_SIZE);
ret = avio_read(s->pb, pkt->data + WV_EXTRA_SIZE + off, wc->blksize);
if (ret != wc->blksize) { if (ret != wc->blksize) {
av_free_packet(pkt); av_free_packet(pkt);
return AVERROR(EIO); return AVERROR(EIO);
} }
while (!(wc->flags & WV_END_BLOCK)) { while (!(wc->flags & WV_END_BLOCK)) {
if (avio_rl32(s->pb) != MKTAG('w', 'v', 'p', 'k')) { if ((ret = wv_read_block_header(s, s->pb)) < 0) {
av_free_packet(pkt);
return AVERROR_INVALIDDATA;
}
if ((ret = av_append_packet(s->pb, pkt, 4)) < 0) {
av_free_packet(pkt); av_free_packet(pkt);
return ret; return ret;
} }
size = AV_RL32(pkt->data + pkt->size - 4);
if (size < 24 || size > WV_BLOCK_LIMIT) {
av_free_packet(pkt);
av_log(s, AV_LOG_ERROR, "Incorrect block size %d\n", size);
return AVERROR_INVALIDDATA;
}
wc->blksize = size;
ver = avio_rl16(s->pb);
if (ver < 0x402 || ver > 0x410) {
av_free_packet(pkt);
av_log(s, AV_LOG_ERROR, "Unsupported version %03X\n", ver);
return AVERROR_PATCHWELCOME;
}
avio_r8(s->pb); // track no
avio_r8(s->pb); // track sub index
wc->samples = avio_rl32(s->pb); // total samples in file
wc->soff = avio_rl32(s->pb); // offset in samples of current block
if ((ret = av_append_packet(s->pb, pkt, WV_EXTRA_SIZE)) < 0) {
av_free_packet(pkt);
return ret;
}
memcpy(wc->extra, pkt->data + pkt->size - WV_EXTRA_SIZE, WV_EXTRA_SIZE);
if ((ret = wv_read_block_header(s, s->pb, 1)) < 0) { off = pkt->size;
if ((ret = av_grow_packet(pkt, WV_HEADER_SIZE + wc->blksize)) < 0) {
av_free_packet(pkt); av_free_packet(pkt);
return ret; return ret;
} }
ret = av_append_packet(s->pb, pkt, wc->blksize); memcpy(pkt->data + off, wc->block_header, WV_HEADER_SIZE);
if (ret < 0) {
ret = avio_read(s->pb, pkt->data + off + WV_HEADER_SIZE, wc->blksize);
if (ret != wc->blksize) {
av_free_packet(pkt); av_free_packet(pkt);
return ret; return (ret < 0) ? ret : AVERROR_EOF;
} }
} }
pkt->stream_index = 0; pkt->stream_index = 0;
wc->block_parsed = 1; wc->block_parsed = 1;
pkt->pts = wc->soff; pkt->pts = wc->soff;
block_samples = AV_RN32(wc->extra); block_samples = AV_RL32(wc->block_header + 20);
if (block_samples > INT32_MAX) if (block_samples > INT32_MAX)
av_log(s, AV_LOG_WARNING, av_log(s, AV_LOG_WARNING,
"Too many samples in block: %"PRIu32"\n", block_samples); "Too many samples in block: %"PRIu32"\n", block_samples);
......
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