Correction for WAVPACK demuxing. Patch by Steve Lhomme (see AUTHORS).

This commit is contained in:
Moritz Bunkus 2004-12-27 16:57:52 +00:00
parent b4a12d0ff8
commit 7e5a49887e
3 changed files with 147 additions and 69 deletions

View File

@ -93,9 +93,14 @@ read_next_header(mm_io_c *mm_io,
int32_t int32_t
wv_parse_frame(mm_io_c *mm_io, wv_parse_frame(mm_io_c *mm_io,
wavpack_header_t &wphdr, wavpack_header_t &wphdr,
wavpack_meta_t &meta) { wavpack_meta_t &meta,
bool read_blocked_frames,
bool keep_initial_position) {
uint32_t bcount; uint32_t bcount;
uint64_t first_data_pos = mm_io->getFilePointer();
bool can_leave = !read_blocked_frames;
do {
// read next WavPack header // read next WavPack header
bcount = read_next_header(mm_io, &wphdr); bcount = read_next_header(mm_io, &wphdr);
@ -105,6 +110,8 @@ wv_parse_frame(mm_io_c *mm_io,
// if there's audio samples in there... // if there's audio samples in there...
if (wphdr.block_samples) { if (wphdr.block_samples) {
meta.channel_count += (wphdr.flags & WV_MONO_FLAG) ? 1 : 2;
if (wphdr.flags & WV_INITIAL_BLOCK) {
meta.sample_rate = (wphdr.flags & WV_SRATE_MASK) >> WV_SRATE_LSB; meta.sample_rate = (wphdr.flags & WV_SRATE_MASK) >> WV_SRATE_LSB;
if (meta.sample_rate == 15) if (meta.sample_rate == 15)
@ -119,19 +126,19 @@ wv_parse_frame(mm_io_c *mm_io,
meta.samples_per_block = wphdr.block_samples; meta.samples_per_block = wphdr.block_samples;
if (wphdr.flags & WV_INITIAL_BLOCK) { first_data_pos = mm_io->getFilePointer();
meta.channel_count = (wphdr.flags & WV_MONO_FLAG) ? 1 : 2; meta.channel_count = (wphdr.flags & WV_MONO_FLAG) ? 1 : 2;
if (wphdr.flags & WV_FINAL_BLOCK) { if (wphdr.flags & WV_FINAL_BLOCK) {
can_leave = true;
mxverb(3, "wavpack_reader: %s block: %s, %d bytes\n", mxverb(3, "wavpack_reader: %s block: %s, %d bytes\n",
(wphdr.flags & WV_MONO_FLAG) ? "mono" : "stereo", (wphdr.flags & WV_MONO_FLAG) ? "mono" : "stereo",
(wphdr.flags & WV_HYBRID_FLAG) ? "hybrid" : "lossless", (wphdr.flags & WV_HYBRID_FLAG) ? "hybrid" : "lossless",
wphdr.ck_size + 8); wphdr.ck_size + 8);
} }
} else { } else {
meta.channel_count += (wphdr.flags & WV_MONO_FLAG) ? 1 : 2;
if (wphdr.flags & WV_FINAL_BLOCK) { if (wphdr.flags & WV_FINAL_BLOCK) {
mxverb(2, "wavpack_reader: %d chans: %s, %d bytes\n", can_leave = true;
mxverb(2, "wavpack_reader: %d chans, mode: %s, %d bytes\n",
meta.channel_count, meta.channel_count,
(wphdr.flags & WV_HYBRID_FLAG) ? "hybrid" : "lossless", (wphdr.flags & WV_HYBRID_FLAG) ? "hybrid" : "lossless",
wphdr.ck_size + 8); wphdr.ck_size + 8);
@ -139,6 +146,13 @@ wv_parse_frame(mm_io_c *mm_io,
} }
} else } else
mxwarn("wavpack_reader: non-audio block found\n"); mxwarn("wavpack_reader: non-audio block found\n");
if (!can_leave) {
mm_io->skip(wphdr.ck_size - sizeof(wavpack_header_t) + 8);
}
} while (!can_leave);
if (keep_initial_position)
mm_io->setFilePointer(first_data_pos);
return wphdr.ck_size - sizeof(wavpack_header_t) + 8; return wphdr.ck_size - sizeof(wavpack_header_t) + 8;
} }

View File

@ -83,6 +83,8 @@ typedef struct {
// encountered // encountered
int32_t MTX_DLL_API wv_parse_frame(mm_io_c *mm_io, wavpack_header_t &header, int32_t MTX_DLL_API wv_parse_frame(mm_io_c *mm_io, wavpack_header_t &header,
wavpack_meta_t &meta); wavpack_meta_t &meta,
bool read_blocked_frames,
bool keep_initial_position);
#endif // __WAVPACK_COMMON_H #endif // __WAVPACK_COMMON_H

View File

@ -56,7 +56,7 @@ wavpack_reader_c::wavpack_reader_c(track_info_c *nti)
mm_io = new mm_file_io_c(ti->fname); mm_io = new mm_file_io_c(ti->fname);
size = mm_io->get_size(); size = mm_io->get_size();
packet_size = wv_parse_frame(mm_io, header, meta); packet_size = wv_parse_frame(mm_io, header, meta, true, true);
if (packet_size < 0) if (packet_size < 0)
mxerror(FMT_FN "The file header was not read correctly.\n", mxerror(FMT_FN "The file header was not read correctly.\n",
ti->fname.c_str()); ti->fname.c_str());
@ -73,11 +73,12 @@ wavpack_reader_c::wavpack_reader_c(track_info_c *nti)
try { try {
if (header.flags & WV_HYBRID_FLAG) { if (header.flags & WV_HYBRID_FLAG) {
mm_io_correc = new mm_file_io_c(ti->fname + "c"); mm_io_correc = new mm_file_io_c(ti->fname + "c");
packet_size = wv_parse_frame(mm_io_correc, header_correc, meta_correc); packet_size = wv_parse_frame(mm_io_correc, header_correc, meta_correc,
true, true);
if (packet_size < 0) if (packet_size < 0)
mxerror(FMT_FN "The correction file header was not read correctly.\n", mxerror(FMT_FN "The correction file header was not read correctly.\n",
ti->fname.c_str()); ti->fname.c_str());
mm_io_correc->setFilePointer(mm_io->getFilePointer() - mm_io_correc->setFilePointer(mm_io_correc->getFilePointer() -
sizeof(wavpack_header_t), seek_beginning); sizeof(wavpack_header_t), seek_beginning);
meta.has_correction = true; meta.has_correction = true;
} }
@ -112,13 +113,30 @@ wavpack_reader_c::read(generic_packetizer_c *ptzr,
bool force) { bool force) {
wavpack_header_t dummy_header, dummy_header_correc; wavpack_header_t dummy_header, dummy_header_correc;
wavpack_meta_t dummy_meta; wavpack_meta_t dummy_meta;
int32_t data_size = wv_parse_frame(mm_io, dummy_header, dummy_meta); uint64_t initial_position = mm_io->getFilePointer();
uint8_t *chunk, *databuffer;
// determine the final data size
int32_t data_size = 0, block_size;
int extra_frames_number = -1;
dummy_meta.channel_count = 0;
while (dummy_meta.channel_count < meta.channel_count) {
extra_frames_number++;
block_size = wv_parse_frame(mm_io, dummy_header, dummy_meta, false, false);
if (block_size == -1)
return FILE_STATUS_DONE;
data_size += block_size;
mm_io->skip(block_size);
}
if (data_size < 0) if (data_size < 0)
return FILE_STATUS_DONE; return FILE_STATUS_DONE;
uint8_t *chunk = (uint8_t *)safemalloc(data_size + sizeof(wavpack_header_t) - data_size += sizeof(wavpack_header_t) - WV_KEEP_HEADER_POSITION;
WV_KEEP_HEADER_POSITION); if (extra_frames_number)
data_size += (extra_frames_number * 12) + 4;
chunk = (uint8_t *)safemalloc(data_size);
// keep the header minus the ID & size (all found in Matroska) // keep the header minus the ID & size (all found in Matroska)
put_uint16_le(chunk, dummy_header.version); put_uint16_le(chunk, dummy_header.version);
@ -127,15 +145,28 @@ wavpack_reader_c::read(generic_packetizer_c *ptzr,
put_uint32_le(&chunk[4], dummy_header.total_samples); put_uint32_le(&chunk[4], dummy_header.total_samples);
put_uint32_le(&chunk[8], dummy_header.block_index); put_uint32_le(&chunk[8], dummy_header.block_index);
put_uint32_le(&chunk[12], dummy_header.block_samples); put_uint32_le(&chunk[12], dummy_header.block_samples);
put_uint32_le(&chunk[16], dummy_header.flags);
put_uint32_le(&chunk[20], dummy_header.crc);
if (mm_io->read(&chunk[sizeof(wavpack_header_t) - WV_KEEP_HEADER_POSITION], mm_io->setFilePointer(initial_position);
data_size) < 0)
dummy_meta.channel_count = 0;
databuffer = &chunk[16];
while (dummy_meta.channel_count < meta.channel_count) {
block_size = wv_parse_frame(mm_io, dummy_header, dummy_meta, false, false);
put_uint32_le(databuffer, dummy_header.flags);
databuffer += 4;
put_uint32_le(databuffer, dummy_header.crc);
databuffer += 4;
if (meta.channel_count > 2) {
// not stored for the last block
put_uint32_le(databuffer, block_size);
databuffer += 4;
}
if (mm_io->read(databuffer, block_size) < 0)
return FILE_STATUS_DONE; return FILE_STATUS_DONE;
databuffer += block_size;
}
memory_c mem(chunk, data_size + sizeof(wavpack_header_t) - memory_c mem(chunk, data_size, true);
WV_KEEP_HEADER_POSITION, true);
// find the if there is a correction file data corresponding // find the if there is a correction file data corresponding
memories_c mems; memories_c mems;
@ -143,8 +174,21 @@ wavpack_reader_c::read(generic_packetizer_c *ptzr,
if (mm_io_correc) { if (mm_io_correc) {
do { do {
data_size = wv_parse_frame(mm_io_correc, dummy_header_correc, initial_position = mm_io_correc->getFilePointer();
dummy_meta); // determine the final data size
data_size = 0;
extra_frames_number = 0;
dummy_meta.channel_count = 0;
while (dummy_meta.channel_count < meta_correc.channel_count) {
extra_frames_number++;
block_size = wv_parse_frame(mm_io_correc, dummy_header_correc,
dummy_meta, false, false);
if (block_size == -1)
return FILE_STATUS_DONE;
data_size += block_size;
mm_io_correc->skip(block_size);
}
// no more correction to be found // no more correction to be found
if (data_size < 0) { if (data_size < 0) {
delete mm_io_correc; delete mm_io_correc;
@ -155,23 +199,41 @@ wavpack_reader_c::read(generic_packetizer_c *ptzr,
} while (dummy_header_correc.block_samples < dummy_header.block_samples); } while (dummy_header_correc.block_samples < dummy_header.block_samples);
if (dummy_header_correc.block_samples == dummy_header.block_samples) { if (dummy_header_correc.block_samples == dummy_header.block_samples) {
uint8_t *chunk_correc;
uint8_t *chunk_correc = (uint8_t *)safemalloc(data_size + 4); mm_io_correc->setFilePointer(initial_position);
if (meta.channel_count > 2)
data_size += extra_frames_number * 8;
else
data_size += 4;
chunk_correc = (uint8_t *)safemalloc(data_size);
// only keep the CRC in the header // only keep the CRC in the header
put_uint32_le(chunk_correc, dummy_header_correc.crc); dummy_meta.channel_count = 0;
databuffer = chunk_correc;
if (mm_io_correc->read(&chunk_correc[4], data_size) < 0) { while (dummy_meta.channel_count < meta_correc.channel_count) {
block_size = wv_parse_frame(mm_io_correc, dummy_header_correc,
dummy_meta, false, false);
put_uint32_le(databuffer, dummy_header_correc.crc);
databuffer += 4;
if (meta_correc.channel_count > 2) {
// not stored for the last block
put_uint32_le(databuffer, block_size);
databuffer += 4;
}
if (mm_io_correc->read(databuffer, block_size) < 0) {
delete mm_io_correc; delete mm_io_correc;
mm_io_correc = NULL; mm_io_correc = NULL;
} }
else { databuffer += block_size;
memory_c mem_correc(chunk_correc, data_size + 4, true); }
memory_c mem_correc(chunk_correc, data_size, true);
mems.push_back(&mem_correc); mems.push_back(&mem_correc);
PTZR0->process(mems); PTZR0->process(mems);
} }
} }
}
if (mems.size() == 1) if (mems.size() == 1)
PTZR0->process(mem); PTZR0->process(mem);