Handling of block additions (for WAVPACK and other codecs). Patch by Steve Lhomme (see AUTHORS) with modifications by myself.

This commit is contained in:
Moritz Bunkus 2004-12-20 18:56:32 +00:00
parent 8e8d875f24
commit bb865de9a1
11 changed files with 279 additions and 35 deletions

View File

@ -124,7 +124,7 @@ wv_parse_frame(mm_io_c *mm_io,
meta.channel_count = (wphdr.flags & WV_MONO_FLAG) ? 1 : 2;
total_bytes = wphdr.ck_size + 8;
if (wphdr.flags & WV_FINAL_BLOCK) {
mxverb(2, "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_HYBRID_FLAG) ? "hybrid" : "lossless",
wphdr.ck_size + 8);

View File

@ -45,12 +45,14 @@ typedef struct {
char *out_name;
mm_io_c *out;
mm_io_c *out2;
avi_t *avi;
ogg_stream_state osstate;
rmff_track_t *rmtrack;
int64_t tid, tuid;
bool in_use, done;
int64_t max_blockadd_id;
char track_type;
int type;

View File

@ -68,6 +68,7 @@ extern "C" {
#include "chapters.h"
#include "checksums.h"
#include "common.h"
#include "commonebml.h"
#include "librmff.h"
#include "matroska.h"
#include "mkvextract.h"
@ -361,12 +362,6 @@ check_output_files() {
tracks[i].type = FILE_TYPE_TTA;
} else if (!strcmp(tracks[i].codec_id, MKV_A_WAVPACK4)) {
if (tracks[i].private_data == NULL) {
mxwarn(_("Track ID %lld is missing some critical "
"information. The track will be skipped.\n"),
tracks[i].tid);
continue;
}
tracks[i].type = FILE_TYPE_WAVPACK4;
@ -477,6 +472,27 @@ create_output_files() {
dummy_out_name.c_str(), strerror(errno));
}
} else if (tracks[i].type == FILE_TYPE_WAVPACK4) {
try {
tracks[i].out = new mm_file_io_c(tracks[i].out_name, MODE_CREATE);
if (tracks[i].max_blockadd_id != 0) {
string tmpstr = tracks[i].out_name;
size_t pos = tmpstr.rfind('.');
if ((pos != string::npos) && (pos != 0))
tmpstr = tmpstr.substr(0, pos - 1);
tmpstr += "wvc";
tracks[i].out2 = new mm_file_io_c(tmpstr, MODE_CREATE);
}
} catch (exception &ex) {
mxerror(_(" The file '%s' could not be opened for writing (%s).\n"),
tracks[i].out_name, strerror(errno));
}
mxinfo(_("Track ID %lld is being extracted to a %s file '%s'.\n"),
tracks[i].tid, typenames[tracks[i].type],
tracks[i].out_name);
} else {
try {
@ -643,6 +659,7 @@ create_output_files() {
static void
handle_data(KaxBlock *block,
KaxBlockAdditions *block_adds,
int64_t block_duration,
bool has_ref) {
kax_track_t *track;
@ -940,9 +957,27 @@ handle_data(KaxBlock *block,
break;
case FILE_TYPE_WAVPACK4:
// _todo_ support hybrid mode ?
track->out->write("wvpk", 4);
put_uint32_le(buffer, data.Size());
track->out->write(buffer, 4);
track->out->write(data.Buffer(), data.Size());
track->bytes_written += data.Size();
// support hybrid mode data
if (track->out2 && block_adds) {
KaxBlockMore *block_more = FINDFIRST(block_adds, KaxBlockMore);
if (block_more == NULL)
break;
KaxBlockAdditional *block_addition =
FINDFIRST(block_more, KaxBlockAdditional);
if (block_addition == NULL)
break;
track->out2->write("wvpk", 4);
put_uint32_le(buffer, block_addition->GetSize() + 20);
track->out2->write(buffer, 4);
track->out2->write(data.Buffer(), 20);
track->out2->write(block_addition->GetBuffer(),
block_addition->GetSize());
}
break;
case FILE_TYPE_TTA:
@ -1119,6 +1154,7 @@ close_files() {
default:
delete tracks[i].out;
delete tracks[i].out2;
}
}
}
@ -1297,7 +1333,7 @@ extract_tracks(const char *file_name) {
if (EbmlId(*l2) == KaxTrackEntry::ClassInfos.GlobalId) {
int64_t tuid, default_duration;
float a_sfreq, v_fps;
int a_channels, a_bps, v_width, v_height;
int a_channels, a_bps, v_width, v_height, max_blockadd_id;
char kax_track_type, *codec_id;
unsigned char *private_data;
int private_size;
@ -1319,6 +1355,7 @@ extract_tracks(const char *file_name) {
private_data = NULL;
private_size = 0;
default_duration = 0;
max_blockadd_id = 0;
upper_lvl_el = 0;
l3 = es->FindNextElement(l2->Generic().Context, upper_lvl_el,
@ -1340,6 +1377,13 @@ extract_tracks(const char *file_name) {
show_element(l3, 3, _("Track number: %u (%s)"), uint32(tnum),
msg);
} else if (is_id(l3, KaxMaxBlockAdditionID)) {
KaxMaxBlockAdditionID &max_badd_id =
*static_cast<KaxMaxBlockAdditionID *>(l3);
max_badd_id.ReadData(es->I_O());
max_blockadd_id = uint64(max_badd_id);
show_element(l3, 3, _("Max BlockAdd ID: %u"), max_blockadd_id);
} else if (EbmlId(*l3) == KaxTrackUID::ClassInfos.GlobalId) {
KaxTrackUID &el_tuid = *static_cast<KaxTrackUID *>(l3);
el_tuid.ReadData(es->I_O());
@ -1611,6 +1655,9 @@ extract_tracks(const char *file_name) {
create_output_files();
} else if (EbmlId(*l1) == KaxCluster::ClassInfos.GlobalId) {
KaxBlockAdditions *blockadds;
blockadds = NULL;
show_element(l1, 1, _("Cluster"));
cluster = (KaxCluster *)l1;
@ -1670,6 +1717,14 @@ extract_tracks(const char *file_name) {
((float)int64(reference)) * tc_scale / 1000000.0);
has_reference = true;
} else if (EbmlId(*l3) ==
KaxBlockAdditions::ClassInfos.GlobalId) {
blockadds = static_cast<KaxBlockAdditions *>(l3);
blockadds->ReadData(es->I_O(), SCOPE_ALL_DATA);
show_element(l3, 3, _("Block Additions"));
has_reference = true;
} else
l3->SkipData(*es, l3->Generic().Context);
@ -1696,7 +1751,7 @@ extract_tracks(const char *file_name) {
// Now write the stuff to the file. Or not. Or get something to
// drink. Or read a good book. Dunno, your choice, really.
handle_data(block, block_duration, has_reference);
handle_data(block, blockadds, block_duration, has_reference);
} else
l2->SkipData(*es, l2->Generic().Context);

View File

@ -1093,6 +1093,12 @@ def_handle(tracks) {
*static_cast<KaxTrackTimecodeScale *>(l3);
show_element(l3, 3, "Timecode scale: %f", float(ttc_scale));
} else if (is_id(l3, KaxMaxBlockAdditionID)) {
KaxMaxBlockAdditionID &max_block_add_id =
*static_cast<KaxMaxBlockAdditionID *>(l3);
show_element(l3, 3, "Max BlockAddition ID: %u",
uint32(max_block_add_id));
} else if (is_id(l3, KaxContentEncodings))
handle(content_encodings);
@ -1462,6 +1468,7 @@ def_handle2(block_group,
show_element(l3, 3, "Reference virtual: %lld",
int64(ref_virt));
#endif // MATROSKA_VERSION >= 2
} else if (is_id(l3, KaxBlockAdditions)) {
show_element(l3, 3, "Additions");
@ -1500,7 +1507,6 @@ def_handle2(block_group,
show_unknown_element(l4, 4);
} // while (l4 != NULL)
#endif // MATROSKA_VERSION >= 2
} else if (is_id(l3, KaxSlices)) {
show_element(l3, 3, "Slices");

View File

@ -1747,11 +1747,11 @@ kax_reader_c::create_packetizer(int64_t tid) {
(int64_t)t->tnum);
} else if (t->a_formattag == FOURCC('W', 'V', 'P', '4')) {
/* _todo_:
wavpack_meta_t meta;
safefree(nti->private_data);
nti->private_data = NULL;
nti->private_size = 0;*/
wavpack_meta_t meta;
nti->private_size = 0;
meta.bits_per_sample = t->a_bps;
meta.channel_count = t->a_channels;
meta.sample_rate = (uint32_t)t->a_sfreq;

View File

@ -77,8 +77,8 @@ wavpack_reader_c::wavpack_reader_c(track_info_c *nti)
if (packet_size < 0)
mxerror(FMT_FN "The correction file header was not read correctly.\n",
ti->fname.c_str());
mm_io_correc->setFilePointer(-sizeof(wavpack_header_t),
seek_current);
mm_io_correc->setFilePointer(mm_io->getFilePointer() -
sizeof(wavpack_header_t), seek_beginning);
meta.has_correction = true;
}
} catch (exception &ex) {
@ -88,7 +88,8 @@ wavpack_reader_c::wavpack_reader_c(track_info_c *nti)
}
if (verbose)
mxinfo(FMT_FN "Using the WAVPACK demultiplexer.\n", ti->fname.c_str());
mxinfo(FMT_FN "Using the WAVPACK demultiplexer%s.\n", ti->fname.c_str(),
meta.has_correction ? " with a correction file" : "");
}
wavpack_reader_c::~wavpack_reader_c() {
@ -109,7 +110,7 @@ wavpack_reader_c::create_packetizer(int64_t) {
file_status_e
wavpack_reader_c::read(generic_packetizer_c *ptzr,
bool force) {
wavpack_header_t dummy_header;
wavpack_header_t dummy_header, dummy_header_correc;
wavpack_meta_t dummy_meta;
int32_t data_size = wv_parse_frame(mm_io, dummy_header, dummy_meta);
@ -135,6 +136,44 @@ wavpack_reader_c::read(generic_packetizer_c *ptzr,
memory_c mem(chunk, data_size + sizeof(wavpack_header_t) -
WV_KEEP_HEADER_POSITION, true);
// find the if there is a correction file data corresponding
memories_c mems;
mems.push_back(&mem);
if (mm_io_correc) {
do {
data_size = wv_parse_frame(mm_io_correc, dummy_header_correc,
dummy_meta);
// no more correction to be found
if (data_size < 0) {
delete mm_io_correc;
mm_io_correc = NULL;
dummy_header_correc.block_samples = dummy_header.block_samples + 1;
break;
}
} while (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 *)safemalloc(data_size + 4);
// only keep the CRC in the header
put_uint32_le(chunk_correc, dummy_header_correc.crc);
if (mm_io_correc->read(&chunk_correc[4], data_size) < 0) {
delete mm_io_correc;
mm_io_correc = NULL;
}
else {
memory_c mem_correc(chunk_correc, data_size + 4, true);
mems.push_back(&mem_correc);
PTZR0->process(mems);
}
}
}
if (mems.size() == 1)
PTZR0->process(mem);
return FILE_STATUS_MOREDATA;

View File

@ -69,18 +69,14 @@ cluster_helper_c::~cluster_helper_c() {
void
cluster_helper_c::free_contents(ch_contents_t *clstr) {
packet_t *p;
int i;
vector<packet_t *>::iterator i;
assert(clstr != NULL);
assert(clstr->cluster != NULL);
delete clstr->cluster;
for (i = 0; i < clstr->packets.size(); i++) {
p = clstr->packets[i];
safefree(p->data);
safefree(p);
}
foreach(i, clstr->packets)
delete *i;
delete clstr;
}
@ -504,6 +500,23 @@ cluster_helper_c::render_cluster(ch_contents_t *clstr) {
(&GetChild<KaxReferencePriority>(*new_block_group)) =
pack->ref_priority;
// Handle BlockAdditions if needed
if ((new_block_group != NULL) && (pack->data_adds.size() > 0)) {
KaxBlockAdditions *additions = static_cast<KaxBlockAdditions *>
(&GetChild<KaxBlockAdditions>(*new_block_group));
for (k = 0; k < pack->data_adds.size(); k++) {
KaxBlockMore *block_more = static_cast<KaxBlockMore *>
(&GetChild<KaxBlockMore>(*additions));
*static_cast<EbmlUInteger *>(&GetChild<KaxBlockAddID>(*block_more)) =
k + 1;
KaxBlockAdditional *block_additional =
static_cast<KaxBlockAdditional *>
(&GetChild<KaxBlockAdditional>(*block_more));
block_additional->SetBuffer((binary *)pack->data_adds[k],
pack->data_adds_lengths[k]);
}
}
elements_in_cluster++;
if (new_block_group == NULL)

View File

@ -806,8 +806,7 @@ generic_packetizer_c::add_packet(memory_c &mem,
if (reader->ptzr_first_packet == NULL)
reader->ptzr_first_packet = this;
pack = (packet_t *)safemalloc(sizeof(packet_t));
memset(pack, 0, sizeof(packet_t));
pack = new packet_t;
length = mem.size;
if (compressor != NULL) {
@ -836,6 +835,73 @@ generic_packetizer_c::add_packet(memory_c &mem,
deferred_packets.push_back(pack);
}
void
generic_packetizer_c::add_packet(memories_c &mems,
int64_t timecode,
int64_t duration,
bool duration_mandatory,
int64_t bref,
int64_t fref,
int ref_priority) {
int length, add_length, i;
packet_t *pack;
if (reader->ptzr_first_packet == NULL)
reader->ptzr_first_packet = this;
pack = (packet_t *)safemalloc(sizeof(packet_t));
memset(pack, 0, sizeof(packet_t));
pack->data_adds.clear();
pack->data_adds.resize(mems.size() - 1);
length = mems[0]->size;
if (compressor != NULL) {
pack->data = compressor->compress(mems[0]->data, length);
mems[0]->release();
for (i = 1; i < mems.size(); i++) {
add_length = mems[i]->size;
pack->data_adds.push_back(compressor->compress(mems[i]->data,
add_length));
pack->data_adds_lengths.push_back(add_length);
mems[i]->release();
}
} else {
if (!mems[0]->is_free)
pack->data = (unsigned char *)safememdup(mems[0]->data, length);
else {
pack->data = mems[0]->data;
mems[0]->lock();
}
for (i = 1; i < mems.size(); i++) {
if (!mems[i]->is_free) {
add_length = mems[i]->size;
pack->data_adds.push_back((unsigned char *)safememdup(mems[i]->data,
add_length));
pack->data_adds_lengths.push_back(add_length);
} else {
pack->data_adds.push_back(mems[i]->data);
pack->data_adds_lengths.push_back(mems[i]->size);
mems[i]->lock();
}
}
}
pack->length = length;
pack->timecode = timecode;
pack->bref = bref;
pack->fref = fref;
pack->ref_priority = ref_priority;
pack->duration = duration;
pack->duration_mandatory = duration_mandatory;
pack->source = this;
enqueued_bytes += pack->length;
if (connected_to != 1)
add_packet2(pack);
else
deferred_packets.push_back(pack);
}
void
generic_packetizer_c::add_packet2(packet_t *pack) {
int64_t factory_timecode;
@ -847,8 +913,7 @@ generic_packetizer_c::add_packet2(packet_t *pack) {
pack->fref += correction_timecode_offset + append_timecode_offset;
if (pack->timecode < 0) {
safefree(pack->data);
safefree(pack);
delete pack;
return;
}

View File

@ -107,6 +107,8 @@ public:
}
};
typedef std::vector<memory_c *> memories_c;
struct audio_sync_t {
int64_t displacement;
double linear;
@ -132,7 +134,7 @@ enum default_track_priority_e {
#define FMT_FN "'%s': "
#define FMT_TID "'%s' track %lld: "
typedef struct {
struct packet_t {
KaxBlockGroup *group;
KaxBlock *block;
KaxCluster *cluster;
@ -142,7 +144,27 @@ typedef struct {
int64_t unmodified_assigned_timecode, unmodified_duration;
bool duration_mandatory, superseeded, gap_following;
generic_packetizer_c *source;
} packet_t;
vector<unsigned char *> data_adds;
vector<int> data_adds_lengths;
packet_t():
group(NULL), block(NULL), cluster(NULL), data(NULL), length(0),
ref_priority(0),
timecode(0), bref(0), fref(0), duration(0),
packet_num(0),
assigned_timecode(0), unmodified_assigned_timecode(0),
unmodified_duration(0),
duration_mandatory(false), superseeded(false), gap_following(false),
source(NULL) {}
virtual ~packet_t() {
vector<unsigned char *>::iterator i;
safefree(data);
foreach(i, data_adds)
safefree(*i);
}
};
struct cue_creation_t {
cue_strategy_e cues;
@ -433,6 +455,10 @@ public:
int64_t duration, bool duration_mandatory = false,
int64_t bref = -1, int64_t fref = -1,
int ref_priority = -1);
virtual void add_packet(memories_c &mems, int64_t timecode,
int64_t duration, bool duration_mandatory = false,
int64_t bref = -1, int64_t fref = -1,
int ref_priority = -1);
virtual void add_packet2(packet_t *pack);
virtual void process_deferred_packets();
@ -462,6 +488,12 @@ public:
int64_t timecode = -1, int64_t length = -1,
int64_t bref = -1, int64_t fref = -1) = 0;
virtual int process(memories_c &mems,
int64_t timecode = -1, int64_t length = -1,
int64_t bref = -1, int64_t fref = -1) {
return process(*mems[0], timecode, length, bref, fref);
}
virtual void dump_debug_info() = 0;
virtual void set_cue_creation(cue_strategy_e create_cue_data) {

View File

@ -47,9 +47,10 @@ wavpack_packetizer_c::set_headers() {
set_audio_bit_depth(bits_per_sample);
set_track_default_duration(samples_per_block * 1000000000 / sample_rate);
set_track_max_additionals(has_correction ? 1 : 0);
// _todo_ specify in the track header if it's using BlockAdditionals
generic_packetizer_c::set_headers();
track_entry->EnableLacing(!has_correction);
}
int
@ -78,6 +79,34 @@ wavpack_packetizer_c::process(memory_c &mem,
return FILE_STATUS_MOREDATA;
}
int
wavpack_packetizer_c::process(memories_c &mems,
int64_t,
int64_t duration,
int64_t,
int64_t) {
debug_enter("wavpack_packetizer_c::process");
memory_c & mem = *mems[0];
// memory_c & mem_correc = *mems[1];
int64_t samples = get_uint32_le(&mem.data[12]);
int64_t sample_index = get_uint32_le(&mem.data[8]);
if (duration == -1) {
add_packet(mems, irnd(sample_index * 1000000000 / sample_rate),
irnd(samples * 1000000000 / sample_rate));
} else {
mxverb(2, "wavpack_packetizer: incomplete block with duration %lld\n",
duration);
add_packet(mems, irnd((double)sample_index * 1000000000 / sample_rate),
duration);
}
samples_output = sample_index + samples;
debug_leave("wavpack_packetizer_c::process");
return FILE_STATUS_MOREDATA;
}
void
wavpack_packetizer_c::dump_debug_info() {
mxdebug("wavpack_packetizer_c: queue: %d\n", packet_queue.size());

View File

@ -37,6 +37,9 @@ public:
virtual int process(memory_c &mem, int64_t timecode = -1,
int64_t length = -1, int64_t bref = -1,
int64_t fref = -1);
virtual int process(memories_c &mem,
int64_t timecode = -1, int64_t length = -1,
int64_t bref = -1, int64_t fref = -1);
virtual void set_headers();
virtual void dump_debug_info();