diff --git a/src/common/wavpack_common.cpp b/src/common/wavpack_common.cpp index d2ce2b1b4..ffe2a6b1e 100644 --- a/src/common/wavpack_common.cpp +++ b/src/common/wavpack_common.cpp @@ -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); diff --git a/src/extract/mkvextract.h b/src/extract/mkvextract.h index 77e7643eb..5aa4fe521 100644 --- a/src/extract/mkvextract.h +++ b/src/extract/mkvextract.h @@ -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; diff --git a/src/extract/tracks.cpp b/src/extract/tracks.cpp index f84e8b3b5..cae0dd70b 100644 --- a/src/extract/tracks.cpp +++ b/src/extract/tracks.cpp @@ -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(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(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(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); diff --git a/src/info/mkvinfo.cpp b/src/info/mkvinfo.cpp index aec206a68..e01dddb79 100644 --- a/src/info/mkvinfo.cpp +++ b/src/info/mkvinfo.cpp @@ -1093,6 +1093,12 @@ def_handle(tracks) { *static_cast(l3); show_element(l3, 3, "Timecode scale: %f", float(ttc_scale)); + } else if (is_id(l3, KaxMaxBlockAdditionID)) { + KaxMaxBlockAdditionID &max_block_add_id = + *static_cast(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"); diff --git a/src/input/r_matroska.cpp b/src/input/r_matroska.cpp index 8720e49fd..12b761cc3 100644 --- a/src/input/r_matroska.cpp +++ b/src/input/r_matroska.cpp @@ -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_: - safefree(nti->private_data); - nti->private_data = NULL; - nti->private_size = 0;*/ wavpack_meta_t meta; + + safefree(nti->private_data); + nti->private_data = NULL; + 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; diff --git a/src/input/r_wavpack.cpp b/src/input/r_wavpack.cpp index b31a3b9a5..5efa34590 100644 --- a/src/input/r_wavpack.cpp +++ b/src/input/r_wavpack.cpp @@ -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,7 +136,45 @@ wavpack_reader_c::read(generic_packetizer_c *ptzr, memory_c mem(chunk, data_size + sizeof(wavpack_header_t) - WV_KEEP_HEADER_POSITION, true); - PTZR0->process(mem); + + // 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; } diff --git a/src/merge/cluster_helper.cpp b/src/merge/cluster_helper.cpp index d02f28247..1197b2171 100644 --- a/src/merge/cluster_helper.cpp +++ b/src/merge/cluster_helper.cpp @@ -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::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(*new_block_group)) = pack->ref_priority; + // Handle BlockAdditions if needed + if ((new_block_group != NULL) && (pack->data_adds.size() > 0)) { + KaxBlockAdditions *additions = static_cast + (&GetChild(*new_block_group)); + for (k = 0; k < pack->data_adds.size(); k++) { + KaxBlockMore *block_more = static_cast + (&GetChild(*additions)); + *static_cast(&GetChild(*block_more)) = + k + 1; + KaxBlockAdditional *block_additional = + static_cast + (&GetChild(*block_more)); + block_additional->SetBuffer((binary *)pack->data_adds[k], + pack->data_adds_lengths[k]); + } + } + elements_in_cluster++; if (new_block_group == NULL) diff --git a/src/merge/pr_generic.cpp b/src/merge/pr_generic.cpp index d98d83ec6..81068ba4c 100644 --- a/src/merge/pr_generic.cpp +++ b/src/merge/pr_generic.cpp @@ -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; } diff --git a/src/merge/pr_generic.h b/src/merge/pr_generic.h index e027b95e7..713c7cc4f 100644 --- a/src/merge/pr_generic.h +++ b/src/merge/pr_generic.h @@ -107,6 +107,8 @@ public: } }; +typedef std::vector 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 data_adds; + vector 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::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) { diff --git a/src/output/p_wavpack.cpp b/src/output/p_wavpack.cpp index bf26b4b0f..5828c7c80 100644 --- a/src/output/p_wavpack.cpp +++ b/src/output/p_wavpack.cpp @@ -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()); diff --git a/src/output/p_wavpack.h b/src/output/p_wavpack.h index b09d0dd8a..9be0512f8 100644 --- a/src/output/p_wavpack.h +++ b/src/output/p_wavpack.h @@ -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();