From 083c0beb7057866495f495ab9f69e2afb586c5a7 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Sat, 9 May 2009 22:08:18 +0200 Subject: [PATCH] Merged the two classes kax_quickparser_c and kax_analyzer_c. Both do roughly the same anyway. --- src/{mmg => common}/kax_analyzer.cpp | 505 ++++++++++++++++----------- src/common/kax_analyzer.h | 132 +++++++ src/common/quickparser.cpp | 209 ----------- src/common/quickparser.h | 64 ---- src/extract/attachments.cpp | 17 +- src/extract/chapters.cpp | 50 ++- src/extract/cuesheets.cpp | 23 +- src/extract/tags.cpp | 14 +- src/mmg/he_value_page.h | 2 + src/mmg/header_editor_frame.cpp | 19 +- src/mmg/header_editor_frame.h | 8 +- src/mmg/kax_analyzer.h | 102 ------ src/mmg/mmg_dialog.h | 3 + src/mmg/tab_chapters.cpp | 6 +- src/mmg/tab_chapters.h | 4 +- src/mmg/wx_kax_analyzer.cpp | 53 +++ src/mmg/wx_kax_analyzer.h | 41 +++ 17 files changed, 597 insertions(+), 655 deletions(-) rename src/{mmg => common}/kax_analyzer.cpp (50%) create mode 100644 src/common/kax_analyzer.h delete mode 100644 src/common/quickparser.cpp delete mode 100644 src/common/quickparser.h delete mode 100644 src/mmg/kax_analyzer.h create mode 100644 src/mmg/wx_kax_analyzer.cpp create mode 100644 src/mmg/wx_kax_analyzer.h diff --git a/src/mmg/kax_analyzer.cpp b/src/common/kax_analyzer.cpp similarity index 50% rename from src/mmg/kax_analyzer.cpp rename to src/common/kax_analyzer.cpp index f0bb4fba9..9a3437eb1 100644 --- a/src/mmg/kax_analyzer.cpp +++ b/src/common/kax_analyzer.cpp @@ -1,182 +1,179 @@ /* - mkvmerge GUI -- utility for splicing together matroska files + mkvmerge -- utility for splicing together matroska files from component media subtypes Distributed under the GPL see the file COPYING for details or visit http://www.gnu.org/copyleft/gpl.html - Matroska file analyzer + Matroska file analyzer and updater Written by Moritz Bunkus . */ -// The Debian g++ 3.3.1 has problems in its standard C++ headers with min -// being defined differently. So just include these files now when min -// has not been defined yet. -#if __GNUC__ != 2 -#include -#endif -#include - #include "common/os.h" -#include +#include #include #include #include #include #include +#include +#include "common/common.h" #include "common/ebml.h" -#include "mmg/kax_analyzer.h" -#include "mmg/mmg.h" +#include "common/error.h" +#include "common/kax_analyzer.h" -using namespace std; using namespace libebml; +using namespace libmatroska; -#define in_parent(p) (file->getFilePointer() < (p->GetElementPosition() + p->ElementSize())) +#define in_parent(p) (m_file->getFilePointer() < (p->GetElementPosition() + p->ElementSize())) -kax_analyzer_c::kax_analyzer_c(wxWindow *parent, - string name) - : m_parent(parent) - , file_name(name) - , segment(NULL) +bool +operator <(const kax_analyzer_data_cptr &d1, + const kax_analyzer_data_cptr &d2) { + return d1->m_pos < d2->m_pos; +} + +kax_analyzer_c::kax_analyzer_c(std::string file_name) + : m_file_name(file_name) + , m_file(NULL) + , m_close_file(true) + , m_stream(NULL) { try { - file = new mm_file_io_c(file_name.c_str(), MODE_WRITE); + m_file = new mm_file_io_c(m_file_name, MODE_WRITE); } catch (...) { - throw error_c(boost::format(Y("Could not open the file '%1%'.")) % file_name); + throw error_c(boost::format(Y("Could not open the file '%1%'.")) % m_file_name); } } kax_analyzer_c::~kax_analyzer_c() { - uint32_t i; - - delete file; - for (i = 0; i < data.size(); i++) - delete data[i]; - if (segment != NULL) - delete segment; + if (m_close_file) + delete m_file; } void kax_analyzer_c::debug_dump_elements() { int i; - for (i = 0; i < data.size(); i++) { - const EbmlCallbacks *callbacks = find_ebml_callbacks(KaxSegment::ClassInfos, data[i]->id); - std::string name; + for (i = 0; i < m_data.size(); i++) { + const EbmlCallbacks *callbacks = find_ebml_callbacks(KaxSegment::ClassInfos, m_data[i]->m_id); - if ((NULL == callbacks) && (EbmlVoid::ClassInfos.GlobalId == data[i]->id)) + if ((NULL == callbacks) && (EbmlVoid::ClassInfos.GlobalId == m_data[i]->m_id)) callbacks = &EbmlVoid::ClassInfos; + std::string name; if (NULL != callbacks) name = callbacks->DebugName; else { - std::string format = (boost::format("0x%%|0%1%x|") % (data[i]->id.Length * 2)).str(); - name = (boost::format(format) % data[i]->id.Value).str(); + std::string format = (boost::format("0x%%|0%1%x|") % (m_data[i]->m_id.Length * 2)).str(); + name = (boost::format(format) % m_data[i]->m_id.Value).str(); } - mxinfo(boost::format("%1%: %2% size %3% at %4%\n") % i % name % data[i]->size % data[i]->pos); + mxinfo(boost::format("%1%: %2% size %3% at %4%\n") % i % name % m_data[i]->m_size % m_data[i]->m_pos); } } bool -kax_analyzer_c::probe(string file_name) { +kax_analyzer_c::probe(std::string file_name) { try { unsigned char data[4]; mm_file_io_c in(file_name.c_str()); if (in.read(data, 4) != 4) return false; - if ((data[0] != 0x1A) || (data[1] != 0x45) || - (data[2] != 0xDF) || (data[3] != 0xA3)) - return false; - return true; + + return ((0x1a == data[0]) && (0x45 == data[1]) && (0xdf == data[2]) && (0xa3 == data[3])); } catch (...) { return false; } } bool -kax_analyzer_c::process() { - segment = NULL; +kax_analyzer_c::process(bool parse_fully) { + int64_t file_size = m_file->get_size(); + show_progress_start(file_size); - file->setFilePointer(0); - EbmlStream es(*file); + m_segment.clear(); + m_data.clear(); - unsigned int i; - for (i = 0; i < data.size(); i++) - delete data[i]; - data.clear(); + m_file->setFilePointer(0); + m_stream = new EbmlStream(*m_file); // Find the EbmlHead element. Must be the first one. - EbmlElement *l0 = es.FindNextID(EbmlHead::ClassInfos, 0xFFFFFFFFL); - if (l0 == NULL) + EbmlElement *l0 = m_stream->FindNextID(EbmlHead::ClassInfos, 0xFFFFFFFFL); + if (NULL == l0) throw error_c(Y("Not a valid Matroska file (no EBML head found)")); // Don't verify its data for now. - l0->SkipData(es, l0->Generic().Context); + l0->SkipData(*m_stream, l0->Generic().Context); delete l0; while (1) { // Next element must be a segment - l0 = es.FindNextID(KaxSegment::ClassInfos, 0xFFFFFFFFFFFFFFFFLL); - if (l0 == NULL) + l0 = m_stream->FindNextID(KaxSegment::ClassInfos, 0xFFFFFFFFFFFFFFFFLL); + if (NULL == l0) throw error_c(Y("Not a valid Matroska file (no segment/level 0 element found)")); + if (EbmlId(*l0) == KaxSegment::ClassInfos.GlobalId) break; - l0->SkipData(es, l0->Generic().Context); + + l0->SkipData(*m_stream, l0->Generic().Context); delete l0; } - wxProgressDialog progdlg(Z("Analysis is running"), Z("The Matroska file is analyzed."), 100, m_parent, wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_REMAINING_TIME); - - segment = static_cast(l0); - int64_t file_size = file->get_size(); - int upper_lvl_el = 0; - bool cont = true; + m_segment = counted_ptr(static_cast(l0)); + int upper_lvl_el = 0; + bool aborted = false; + bool cluster_found = false; // We've got our segment, so let's find all level 1 elements. - EbmlElement *l1 = es.FindNextElement(segment->Generic().Context, upper_lvl_el, 0xFFFFFFFFFFFFFFFFLL, true, 1); - while ((l1 != NULL) && (upper_lvl_el <= 0)) { - data.push_back(new analyzer_data_c(l1->Generic().GlobalId, l1->GetElementPosition(), l1->ElementSize(true))); - while (app->Pending()) - app->Dispatch(); + EbmlElement *l1 = m_stream->FindNextElement(m_segment->Generic().Context, upper_lvl_el, 0xFFFFFFFFFFFFFFFFLL, true, 1); + while ((NULL != l1) && (0 >= upper_lvl_el)) { + m_data.push_back(kax_analyzer_data_c::create(l1->Generic().GlobalId, l1->GetElementPosition(), l1->ElementSize(true))); - l1->SkipData(es, l1->Generic().Context); + cluster_found |= is_id(l1, KaxCluster); + + l1->SkipData(*m_stream, l1->Generic().Context); delete l1; l1 = NULL; - if (!in_parent(segment) || !(cont = progdlg.Update((int)(file->getFilePointer() * 100 / file_size)))) + aborted = !show_progress_running((int)(m_file->getFilePointer() * 100 / file_size)); + + if (!in_parent(m_segment) || aborted || (cluster_found && !parse_fully)) break; - l1 = es.FindNextElement(segment->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true); + l1 = m_stream->FindNextElement(m_segment->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true); } // while (l1 != NULL) - if (l1 != NULL) + if (NULL != l1) delete l1; - if (cont) + if (!aborted && !parse_fully) + read_all_meta_seeks(); + + show_progress_done(); + + if (!aborted) return true; - delete segment; - segment = NULL; - for (i = 0; i < data.size(); i++) - delete data[i]; - data.clear(); + m_segment.clear(); + m_data.clear(); + return false; } EbmlElement * -kax_analyzer_c::read_element(analyzer_data_c *element_data) { - EbmlStream es(*file); - file->setFilePointer(element_data->pos); +kax_analyzer_c::read_element(kax_analyzer_data_c *element_data) { + EbmlStream es(*m_file); + m_file->setFilePointer(element_data->m_pos); int upper_lvl_el = 0; - EbmlElement *e = es.FindNextElement(segment->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); - const EbmlCallbacks *callbacks = find_ebml_callbacks(KaxSegment::ClassInfos, element_data->id); + EbmlElement *e = es.FindNextElement(m_segment->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); + const EbmlCallbacks *callbacks = find_ebml_callbacks(KaxSegment::ClassInfos, element_data->m_id); if ((NULL == e) || (NULL == callbacks) || (EbmlId(*e) != callbacks->GlobalId)) { delete e; @@ -184,7 +181,7 @@ kax_analyzer_c::read_element(analyzer_data_c *element_data) { } upper_lvl_el = 0; - e->Read(es, callbacks->Context, upper_lvl_el, e, true); + e->Read(*m_stream, callbacks->Context, upper_lvl_el, e, true); return e; } @@ -208,43 +205,41 @@ kax_analyzer_c::update_element(EbmlElement *e, return uer_success; } -/** \brief Sets the segment size to the length of the file +/** \brief Sets the m_segment size to the length of the m_file */ void kax_analyzer_c::adjust_segment_size() { - KaxSegment *new_segment = new KaxSegment; - file->setFilePointer(segment->GetElementPosition()); - new_segment->WriteHead(*file, segment->HeadSize() - 4); + counted_ptr new_segment = counted_ptr(new KaxSegment); + m_file->setFilePointer(m_segment->GetElementPosition()); + new_segment->WriteHead(*m_file, m_segment->HeadSize() - 4); - file->setFilePointer(0, seek_end); - if (!new_segment->ForceSize(file->getFilePointer() - segment->HeadSize() - segment->GetElementPosition())) { - segment->OverwriteHead(*file); - delete new_segment; + m_file->setFilePointer(0, seek_end); + if (!new_segment->ForceSize(m_file->getFilePointer() - m_segment->HeadSize() - m_segment->GetElementPosition())) { + m_segment->OverwriteHead(*m_file); throw uer_error_segment_size_for_element; } - new_segment->OverwriteHead(*file); - delete segment; - segment = new_segment; + new_segment->OverwriteHead(*m_file); + m_segment = new_segment; } /** \brief Create an EbmlVoid element at a specific location This function creates an EbmlVoid element at the position \c - file_pos with the size \c void_size. If \c void_size is not big + m_file_pos with the size \c void_size. If \c void_size is not big enough to contain an EbmlVoid element then the space is overwritten with zero bytes. The \c data member structure is also updated to reflect the - changes made to the file. If \c add_new_data_element is \c true + changes made to the m_file. If \c add_new_data_element is \c true and a void element was actually written then a new element will be added to \c data at position \c data_idx. If \c add_new_data_element is \c false and a void element could not be written then the element at \c data_idx is removed from \c - data. Otherwise the element at position \c data_idx is updated. + m_data. Otherwise the element at position \c data_idx is updated. - \param file_pos The position in the file for the new void element. + \param m_file_pos The position in the m_file for the new void element. \param void_size The size of the new void element. \param data_idx Index into the \c data structure where a new data element will be added or which data element to update. @@ -268,7 +263,7 @@ kax_analyzer_c::create_void_element(int64_t file_pos, return false; // See if enough space was freed to fit an EbmlVoid element in. - file->setFilePointer(file_pos); + m_file->setFilePointer(file_pos); if (2 > void_size) { // No. The most compatible way to deal with this situation is to @@ -277,9 +272,9 @@ kax_analyzer_c::create_void_element(int64_t file_pos, // byte. if (!add_new_data_element) - data.erase(data.begin() + data_idx, data.begin() + data_idx + 1); + m_data.erase(m_data.begin() + data_idx, m_data.begin() + data_idx + 1); - EbmlElement *e = read_element(data[data_idx]); + EbmlElement *e = read_element(m_data[data_idx]); if (NULL == e) return false; @@ -294,14 +289,14 @@ kax_analyzer_c::create_void_element(int64_t file_pos, delete e; - file->setFilePointer(data[data_idx]->pos - 1); - file->write(head, head_size); + m_file->setFilePointer(m_data[data_idx]->m_pos - 1); + m_file->write(head, head_size); - --data[data_idx]->pos; - ++data[data_idx]->size; + --m_data[data_idx]->m_pos; + ++m_data[data_idx]->m_size; - // Update meta seek indices for data[data_idx]'s new position. - e = read_element(data[data_idx]); + // Update meta seek indices for m_data[data_idx]'s new position. + e = read_element(m_data[data_idx]); remove_from_meta_seeks(e->Generic().GlobalId); merge_void_elements(); @@ -318,15 +313,15 @@ kax_analyzer_c::create_void_element(int64_t file_pos, evoid.SetSize(void_size); evoid.UpdateSize(); evoid.SetSize(void_size - evoid.HeadSize()); - evoid.Render(*file); + evoid.Render(*m_file); if (add_new_data_element) - data.insert(data.begin() + data_idx, new analyzer_data_c(EbmlVoid::ClassInfos.GlobalId, evoid.GetElementPosition(), void_size)); + m_data.insert(m_data.begin() + data_idx, kax_analyzer_data_c::create(EbmlVoid::ClassInfos.GlobalId, evoid.GetElementPosition(), void_size)); else { - data[data_idx]->id = EbmlVoid::ClassInfos.GlobalId; - data[data_idx]->pos = evoid.GetElementPosition(); - data[data_idx]->size = void_size; + m_data[data_idx]->m_id = EbmlVoid::ClassInfos.GlobalId; + m_data[data_idx]->m_pos = evoid.GetElementPosition(); + m_data[data_idx]->m_size = void_size; } return true; @@ -334,7 +329,7 @@ kax_analyzer_c::create_void_element(int64_t file_pos, /** \brief Removes all seek entries for a specific element - Iterates over the level 1 elements in the file and reads each seek + Iterates over the level 1 elements in the m_file and reads each seek head it finds. All entries for the given \c id are removed from the seek head. If the seek head has been changed then it is rewritten to its original position. The space freed up is filled @@ -346,12 +341,12 @@ void kax_analyzer_c::remove_from_meta_seeks(EbmlId id) { int data_idx; - for (data_idx = 0; data.size() > data_idx; ++data_idx) { + for (data_idx = 0; m_data.size() > data_idx; ++data_idx) { // We only have to do work on SeekHead elements. Skip the others. - if (data[data_idx]->id != KaxSeekHead::ClassInfos.GlobalId) + if (m_data[data_idx]->m_id != KaxSeekHead::ClassInfos.GlobalId) continue; - // Read the element from the file. Remember its size so that a new + // Read the element from the m_file. Remember its size so that a new // EbmlVoid element can be constructed afterwards. EbmlElement *element = read_element(data_idx); KaxSeekHead *seek_head = dynamic_cast(element); @@ -382,7 +377,7 @@ kax_analyzer_c::remove_from_meta_seeks(EbmlId id) { modified = true; } - // Only rewrite the element to the file if it has been modified. + // Only rewrite the element to the m_file if it has been modified. if (!modified) { delete element; continue; @@ -396,13 +391,13 @@ kax_analyzer_c::remove_from_meta_seeks(EbmlId id) { throw uer_error_unknown; // Overwrite the element itself and update its internal record. - file->setFilePointer(data[data_idx]->pos); - seek_head->Render(*file, true); + m_file->setFilePointer(m_data[data_idx]->m_pos); + seek_head->Render(*m_file, true); - data[data_idx]->size = new_size; + m_data[data_idx]->m_size = new_size; // Create a void element to cover the freed space. - if (create_void_element(data[data_idx]->pos + new_size, old_size - new_size, data_idx + 1, true)) + if (create_void_element(m_data[data_idx]->m_pos + new_size, old_size - new_size, data_idx + 1, true)) ++data_idx; delete element; @@ -411,7 +406,7 @@ kax_analyzer_c::remove_from_meta_seeks(EbmlId id) { /** \brief Overwrites all instances of a specific level 1 element - Iterates over the level 1 elements in the file and overwrites + Iterates over the level 1 elements in the m_file and overwrites each instance of a specific level 1 element given by \c id. It is replaced with a new EbmlVoid element. @@ -421,31 +416,31 @@ void kax_analyzer_c::overwrite_all_instances(EbmlId id) { int data_idx; - for (data_idx = 0; data.size() > data_idx; ++data_idx) { + for (data_idx = 0; m_data.size() > data_idx; ++data_idx) { // We only have to do work on specific elements. Skip the others. - if (data[data_idx]->id != id) + if (m_data[data_idx]->m_id != id) continue; // Overwrite with a void element. - create_void_element(data[data_idx]->pos, data[data_idx]->size, data_idx, false); + create_void_element(m_data[data_idx]->m_pos, m_data[data_idx]->m_size, data_idx, false); } } /** \brief Merges consecutive EbmlVoid elements into a single one - Iterates over the level 1 elements in the file and merges + Iterates over the level 1 elements in the m_file and merges consecutive EbmlVoid elements into a single one which covers the same space as the smaller ones combined. - Void elements at the end of the file are removed as well. + Void elements at the end of the m_file are removed as well. */ void kax_analyzer_c::merge_void_elements() { int start_idx = 0; - while (data.size() > start_idx) { + while (m_data.size() > start_idx) { // We only have to do work on EbmlVoid elements. Skip the others. - if (data[start_idx]->id != EbmlVoid::ClassInfos.GlobalId) { + if (m_data[start_idx]->m_id != EbmlVoid::ClassInfos.GlobalId) { ++start_idx; continue; } @@ -453,9 +448,9 @@ kax_analyzer_c::merge_void_elements() { // Found an EbmlVoid element. See how many consecutive EbmlVoid elements // there are at this position and calculate the combined size. int end_idx = start_idx + 1; - int new_size = data[start_idx]->size; - while ((data.size() > end_idx) && (data[end_idx]->id == EbmlVoid::ClassInfos.GlobalId)) { - new_size += data[end_idx]->size; + int new_size = m_data[start_idx]->m_size; + while ((m_data.size() > end_idx) && (m_data[end_idx]->m_id == EbmlVoid::ClassInfos.GlobalId)) { + new_size += m_data[end_idx]->m_size; ++end_idx; } @@ -465,42 +460,42 @@ kax_analyzer_c::merge_void_elements() { continue; } - // Write the new EbmlVoid element to the file. - file->setFilePointer(data[start_idx]->pos); + // Write the new EbmlVoid element to the m_file. + m_file->setFilePointer(m_data[start_idx]->m_pos); EbmlVoid evoid; evoid.SetSize(new_size); evoid.UpdateSize(); evoid.SetSize(new_size - evoid.HeadSize()); - evoid.Render(*file); + evoid.Render(*m_file); // Update the internal records to reflect the changes. - data[start_idx]->size = new_size; - data.erase(data.begin() + start_idx + 1, data.begin() + end_idx); + m_data[start_idx]->m_size = new_size; + m_data.erase(m_data.begin() + start_idx + 1, m_data.begin() + end_idx); start_idx += 2; } - // See how many void elements there are at the end of the file. - start_idx = data.size() - 1; + // See how many void elements there are at the end of the m_file. + start_idx = m_data.size() - 1; - while ((0 <= start_idx) && (EbmlVoid::ClassInfos.GlobalId == data[start_idx]->id)) + while ((0 <= start_idx) && (EbmlVoid::ClassInfos.GlobalId == m_data[start_idx]->m_id)) --start_idx; ++start_idx; // If there are none then we're done. - if (data.size() <= start_idx) + if (m_data.size() <= start_idx) return; - // Truncate the file after the last non-void element and update the segment size. - file->truncate(data[start_idx]->pos); + // Truncate the m_file after the last non-void element and update the m_segment size. + m_file->truncate(m_data[start_idx]->m_pos); adjust_segment_size(); } -/** \brief Finds a suitable spot for an element and writes it to the file +/** \brief Finds a suitable spot for an element and writes it to the m_file First, a suitable spot for the element is determined by looking at - EbmlVoid elements. If none is found in the middle of the file then + EbmlVoid elements. If none is found in the middle of the m_file then the element will be appended at the end. Second, the element is written at the location determined in the @@ -512,7 +507,7 @@ kax_analyzer_c::merge_void_elements() { \param e Pointer to the element to write. \param write_defaults Boolean that decides whether or not elements - which contain their default value are written to the file. + which contain their default value are written to the m_file. */ void kax_analyzer_c::write_element(EbmlElement *e, @@ -521,36 +516,36 @@ kax_analyzer_c::write_element(EbmlElement *e, int element_size = e->ElementSize(write_defaults); int data_idx; - for (data_idx = 0; data.size() > data_idx; ++data_idx) { + for (data_idx = 0; m_data.size() > data_idx; ++data_idx) { // We're only interested in EbmlVoid elements. Skip the others. - if (data[data_idx]->id != EbmlVoid::ClassInfos.GlobalId) + if (m_data[data_idx]->m_id != EbmlVoid::ClassInfos.GlobalId) continue; // Skip the element if it doesn't provide enough space. - if (data[data_idx]->size < element_size) + if (m_data[data_idx]->m_size < element_size) continue; // We've found our element. Overwrite it. - file->setFilePointer(data[data_idx]->pos); - e->Render(*file, write_defaults); + m_file->setFilePointer(m_data[data_idx]->m_pos); + e->Render(*m_file, write_defaults); // Update the internal records. - data[data_idx]->id = e->Generic().GlobalId; + m_data[data_idx]->m_id = e->Generic().GlobalId; // Create a new void element after the element we've just written. - create_void_element(data[data_idx]->pos + element_size, data[data_idx]->size - element_size, data_idx + 1, true); + create_void_element(m_data[data_idx]->m_pos + element_size, m_data[data_idx]->m_size - element_size, data_idx + 1, true); // We're done. return; } - // We haven't found a suitable place. So store the element at the end of the file + // We haven't found a suitable place. So store the element at the end of the m_file // and update the internal records. - file->setFilePointer(0, seek_end); - e->Render(*file, write_defaults); - data.push_back(new analyzer_data_c(e->Generic().GlobalId, file->getFilePointer() - e->ElementSize(write_defaults), e->ElementSize(write_defaults))); + m_file->setFilePointer(0, seek_end); + e->Render(*m_file, write_defaults); + m_data.push_back(kax_analyzer_data_c::create(e->Generic().GlobalId, m_file->getFilePointer() - e->ElementSize(write_defaults), e->ElementSize(write_defaults))); - // Adjust the segment's size. + // Adjust the m_segment's size. adjust_segment_size(); } @@ -558,7 +553,7 @@ kax_analyzer_c::write_element(EbmlElement *e, This function iterates over all meta seek elements and looks for one that has enough space (via following EbmlVoid elements or - because it is located at the end of the file) for indexing + because it is located at the end of the m_file) for indexing the element \c e. If no such element is found then a new meta seek element is @@ -570,18 +565,18 @@ void kax_analyzer_c::add_to_meta_seek(EbmlElement *e) { int data_idx, first_seek_head_idx = -1; - for (data_idx = 0; data.size() > data_idx; ++data_idx) { + for (data_idx = 0; m_data.size() > data_idx; ++data_idx) { // We only have to do work on SeekHead elements. Skip the others. - if (data[data_idx]->id != KaxSeekHead::ClassInfos.GlobalId) + if (m_data[data_idx]->m_id != KaxSeekHead::ClassInfos.GlobalId) continue; // Calculate how much free space there is behind the seek head. // merge_void_elemens() guarantees that there is no EbmlVoid element - // at the end of the file and that all consecutive EbmlVoid elements + // at the end of the m_file and that all consecutive EbmlVoid elements // have been merged into a single element. - int available_space = data[data_idx]->size; - if (((data_idx + 1) < data.size()) && (data[data_idx + 1]->id == EbmlVoid::ClassInfos.GlobalId)) - available_space += data[data_idx + 1]->size; + int available_space = m_data[data_idx]->m_size; + if (((data_idx + 1) < m_data.size()) && (m_data[data_idx + 1]->m_id == EbmlVoid::ClassInfos.GlobalId)) + available_space += m_data[data_idx + 1]->m_size; // Read the seek head, index the element and see how much space it needs. EbmlElement *element = read_element(data_idx); @@ -592,33 +587,33 @@ kax_analyzer_c::add_to_meta_seek(EbmlElement *e) { if (-1 == first_seek_head_idx) first_seek_head_idx = data_idx; - seek_head->IndexThis(*e, *segment); + seek_head->IndexThis(*e, *m_segment.get()); seek_head->UpdateSize(true); - // We can use this seek head if it is at the end of the file, or if there + // We can use this seek head if it is at the end of the m_file, or if there // is enough space behind it in form of void elements. - if ((data.size() != (data_idx + 1)) && (seek_head->ElementSize(true) > available_space)) { + if ((m_data.size() != (data_idx + 1)) && (seek_head->ElementSize(true) > available_space)) { delete seek_head; continue; } // Write the seek head. - file->setFilePointer(data[data_idx]->pos); - seek_head->Render(*file, true); + m_file->setFilePointer(m_data[data_idx]->m_pos); + seek_head->Render(*m_file, true); - // If this seek head is located at the end of the file then we have - // to adjust the segment size. - if (data.size() == (data_idx + 1)) + // If this seek head is located at the end of the m_file then we have + // to adjust the m_segment size. + if (m_data.size() == (data_idx + 1)) adjust_segment_size(); else // Otherwise adjust the following EbmlVoid element: decrease its size. - create_void_element(data[data_idx]->pos + seek_head->ElementSize(true), - data[data_idx]->size + data[data_idx + 1]->size - seek_head->ElementSize(true), + create_void_element(m_data[data_idx]->m_pos + seek_head->ElementSize(true), + m_data[data_idx]->m_size + m_data[data_idx + 1]->m_size - seek_head->ElementSize(true), data_idx + 1, false); // Update the internal record. - data[data_idx]->size = seek_head->ElementSize(true); + m_data[data_idx]->m_size = seek_head->ElementSize(true); delete seek_head; @@ -629,7 +624,7 @@ kax_analyzer_c::add_to_meta_seek(EbmlElement *e) { // No suitable meta seek head found -- we have to write a new one. // If we have found a prior seek head then we copy that one to the end - // of the file including the newly indexed element and write a one-element + // of the m_file including the newly indexed element and write a one-element // seek head at the first meta seek's position pointing to the one at the // end. if (-1 != first_seek_head_idx) { @@ -640,33 +635,33 @@ kax_analyzer_c::add_to_meta_seek(EbmlElement *e) { throw uer_error_unknown; // ...index our element... - seek_head->IndexThis(*e, *segment); + seek_head->IndexThis(*e, *m_segment.get()); seek_head->UpdateSize(true); - // ...write the seek head at the end of the file... - file->setFilePointer(0, seek_end); - seek_head->Render(*file, true); + // ...write the seek head at the end of the m_file... + m_file->setFilePointer(0, seek_end); + seek_head->Render(*m_file, true); // ...and update the internal records. - data.push_back(new analyzer_data_c(KaxSeekHead::ClassInfos.GlobalId, seek_head->GetElementPosition(), seek_head->ElementSize(true))); + m_data.push_back(kax_analyzer_data_c::create(KaxSeekHead::ClassInfos.GlobalId, seek_head->GetElementPosition(), seek_head->ElementSize(true))); - // Update the segment size. + // Update the m_segment size. adjust_segment_size(); - // Create a new seek head and write it to the file. + // Create a new seek head and write it to the m_file. KaxSeekHead *forward_seek_head = new KaxSeekHead; - forward_seek_head->IndexThis(*seek_head, *segment); + forward_seek_head->IndexThis(*seek_head, *m_segment.get()); forward_seek_head->UpdateSize(true); - file->setFilePointer(data[first_seek_head_idx]->pos); - forward_seek_head->Render(*file, true); + m_file->setFilePointer(m_data[first_seek_head_idx]->m_pos); + forward_seek_head->Render(*m_file, true); // Update the internal record to reflect that there's a new seek head. - int void_size = data[first_seek_head_idx]->size - forward_seek_head->ElementSize(true); - data[first_seek_head_idx]->size = forward_seek_head->ElementSize(true); + int void_size = m_data[first_seek_head_idx]->m_size - forward_seek_head->ElementSize(true); + m_data[first_seek_head_idx]->m_size = forward_seek_head->ElementSize(true); // Create a void element behind the small new first seek head. - create_void_element(data[first_seek_head_idx]->pos + data[first_seek_head_idx]->size, void_size, first_seek_head_idx + 1, true); + create_void_element(m_data[first_seek_head_idx]->m_pos + m_data[first_seek_head_idx]->m_size, void_size, first_seek_head_idx + 1, true); // We're done. delete forward_seek_head; @@ -677,29 +672,29 @@ kax_analyzer_c::add_to_meta_seek(EbmlElement *e) { // We don't have a seek head to copy. Create one before the first chapter if possible. KaxSeekHead *new_seek_head = new KaxSeekHead; - new_seek_head->IndexThis(*e, *segment); + new_seek_head->IndexThis(*e, *m_segment.get()); new_seek_head->UpdateSize(true); - for (data_idx = 0; data.size() > data_idx; ++data_idx) { + for (data_idx = 0; m_data.size() > data_idx; ++data_idx) { // We can only overwrite void elements. Skip the others. - if (data[data_idx]->id != EbmlVoid::ClassInfos.GlobalId) + if (m_data[data_idx]->m_id != EbmlVoid::ClassInfos.GlobalId) continue; // Skip the element if it doesn't offer enough space for the seek head. - if (data[data_idx]->size < new_seek_head->ElementSize(true)) + if (m_data[data_idx]->m_size < new_seek_head->ElementSize(true)) continue; // We've found a suitable spot. Write the seek head. - file->setFilePointer(data[data_idx]->pos); - new_seek_head->Render(*file, true); + m_file->setFilePointer(m_data[data_idx]->m_pos); + new_seek_head->Render(*m_file, true); // Write a void element after the newly written seek head in order to // cover the space previously occupied by the old void element. - create_void_element(data[data_idx]->pos + new_seek_head->ElementSize(true), data[data_idx]->size - new_seek_head->ElementSize(true), data_idx + 1, true); + create_void_element(m_data[data_idx]->m_pos + new_seek_head->ElementSize(true), m_data[data_idx]->m_size - new_seek_head->ElementSize(true), data_idx + 1, true); // Adjust the internal records for the new seek head. - data[data_idx]->size = new_seek_head->ElementSize(true); - data[data_idx]->id = KaxSeekHead::ClassInfos.GlobalId; + m_data[data_idx]->m_size = new_seek_head->ElementSize(true); + m_data[data_idx]->m_id = KaxSeekHead::ClassInfos.GlobalId; // We're done. delete new_seek_head; @@ -712,3 +707,117 @@ kax_analyzer_c::add_to_meta_seek(EbmlElement *e) { // We cannot write a seek head before the first cluster. This is not supported at the moment. throw uer_error_not_indexable; } + +EbmlMaster * +kax_analyzer_c::read_all(const EbmlCallbacks &callbacks) { + EbmlMaster *master = NULL; + EbmlStream es(*m_file); + int i; + + for (i = 0; m_data.size() > i; ++i) { + kax_analyzer_data_c &data = *m_data[i].get(); + if (callbacks.GlobalId != data.m_id) + continue; + + m_file->setFilePointer(data.m_pos); + int upper_lvl_el = 0; + EbmlElement *element = es.FindNextElement(KaxSegment::ClassInfos.Context, upper_lvl_el, 0xFFFFFFFFL, true); + if (NULL == element) + continue; + + if (element->Generic().GlobalId != callbacks.GlobalId) { + delete element; + continue; + } + + EbmlElement *l2 = NULL; + element->Read(*m_stream, callbacks.Context, upper_lvl_el, l2, true); + + if (NULL == master) + master = static_cast(element); + else { + EbmlMaster *src = static_cast(element); + while (src->ListSize() > 0) { + master->PushElement(*(*src)[0]); + src->Remove(0); + } + delete element; + } + } + + if ((NULL != master) && (master->ListSize() == 0)) { + delete master; + return NULL; + } + + return master; +} + +void +kax_analyzer_c::read_all_meta_seeks() { + m_meta_seeks_by_position.clear(); + + unsigned int i, num_entries = m_data.size(); + + for (i = 0; i < num_entries; i++) + if (KaxSeekHead::ClassInfos.GlobalId == m_data[i]->m_id) + read_meta_seek(m_data[i]->m_pos); + + sort(m_data.begin(), m_data.end()); +} + +void +kax_analyzer_c::read_meta_seek(int64_t pos) { + if (map_has_key(m_meta_seeks_by_position, pos)) + return; + + m_meta_seeks_by_position[pos] = true; + + m_file->setFilePointer(pos, seek_beginning); + + int upper_lvl_el = 0; + EbmlElement *l1 = m_stream->FindNextElement(m_segment->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); + + if (NULL == l1) + return; + + if (!is_id(l1, KaxSeekHead)) { + delete l1; + return; + } + + EbmlElement *l2 = NULL; + EbmlMaster *master = static_cast(l1); + master->Read(*m_stream, l1->Generic().Context, upper_lvl_el, l2, true); + + unsigned int i; + for (i = 0; master->ListSize() > i; i++) { + if (!is_id((*master)[i], KaxSeek)) + continue; + + KaxSeek *seek = static_cast((*master)[i]); + KaxSeekID *seek_id = FINDFIRST(seek, KaxSeekID); + int64_t seek_pos = seek->Location() + m_segment->GetElementPosition() + m_segment->HeadSize(); + + if ((0 == pos) || (NULL == seek_id)) + continue; + + bool found = false; + std::vector::iterator it; + mxforeach(it, m_data) + if ((*it)->m_pos == seek_pos) { + found = true; + break; + } + + if (!found) { + EbmlId the_id(seek_id->GetBuffer(), seek_id->GetSize()); + m_data.push_back(kax_analyzer_data_c::create(the_id, seek_pos, -1)); + + if (KaxSeekHead::ClassInfos.GlobalId == the_id) + read_meta_seek(seek_pos); + } + } + + delete l1; +} diff --git a/src/common/kax_analyzer.h b/src/common/kax_analyzer.h new file mode 100644 index 000000000..eb89167c1 --- /dev/null +++ b/src/common/kax_analyzer.h @@ -0,0 +1,132 @@ +/* + mkvmerge -- utility for splicing together matroska files + from component media subtypes + + Distributed under the GPL + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html + + quick Matroska file parsing + + Written by Moritz Bunkus . +*/ + +#ifndef __MTX_COMMON_KAX_ANALYZER_H +#define __MTX_COMMON_KAX_ANALYZER_H + +#include "os.h" + +#include + +#include + +#include "common/matroska.h" +#include "common/common.h" +#include "common/mm_io.h" + +using namespace libebml; +using namespace libmatroska; + +class kax_analyzer_data_c; +typedef counted_ptr kax_analyzer_data_cptr; + +class kax_analyzer_data_c { +public: + EbmlId m_id; + int64_t m_pos, m_size; + +public: + static kax_analyzer_data_cptr create(const EbmlId id, int64_t pos, int64_t size) { + return kax_analyzer_data_cptr(new kax_analyzer_data_c(id, pos, size)); + } + +public: + kax_analyzer_data_c(const EbmlId id, int64_t pos, int64_t size) + : m_id(id) + , m_pos(pos) + , m_size(size) + { + } +}; + +bool operator <(const kax_analyzer_data_cptr &d1, const kax_analyzer_data_cptr &d2); + +class kax_analyzer_c { +public: + enum update_element_result_e { + uer_success, + uer_error_segment_size_for_element, + uer_error_segment_size_for_meta_seek, + uer_error_meta_seek, + uer_error_not_indexable, + uer_error_unknown, + }; + +public: + std::vector m_data; + +private: + std::string m_file_name; + mm_file_io_c *m_file; + bool m_close_file; + counted_ptr m_segment; + std::map m_meta_seeks_by_position; + EbmlStream *m_stream; + +public: // Static functions + static bool probe(std::string file_name); + +public: + kax_analyzer_c(std::string file_name); + kax_analyzer_c(mm_file_io_c *file); + virtual ~kax_analyzer_c(); + + virtual update_element_result_e update_element(EbmlElement *e, bool write_defaults = false); + virtual EbmlMaster *read_all(const EbmlCallbacks &callbacks); + + virtual EbmlElement *read_element(kax_analyzer_data_c *element_data); + virtual EbmlElement *read_element(kax_analyzer_data_cptr element_data) { + return read_element(element_data.get()); + } + virtual EbmlElement *read_element(unsigned int pos) { + return read_element(m_data[pos]); + } + + virtual int find(const EbmlId &id) { + unsigned int i; + + for (i = 0; m_data.size() > i; i++) + if (id == m_data[i]->m_id) + return i; + + return -1; + } + + virtual bool process(bool parse_fully = true); + + virtual void show_progress_start(int64_t size) { + } + virtual bool show_progress_running(int percentage) { + return true; + } + virtual void show_progress_done() { + } + +protected: + virtual void remove_from_meta_seeks(EbmlId id); + virtual void overwrite_all_instances(EbmlId id); + virtual void merge_void_elements(); + virtual void write_element(EbmlElement *e, bool write_defaults); + virtual void add_to_meta_seek(EbmlElement *e); + + virtual void adjust_segment_size(); + virtual bool create_void_element(int64_t file_pos, int void_size, int data_idx, bool add_new_data_element); + + virtual void debug_dump_elements(); + + virtual void read_all_meta_seeks(); + virtual void read_meta_seek(int64_t pos); +}; +typedef counted_ptr kax_analyzer_cptr; + +#endif // __MTX_COMMON_KAX_ANALYZER_H diff --git a/src/common/quickparser.cpp b/src/common/quickparser.cpp deleted file mode 100644 index 231da6813..000000000 --- a/src/common/quickparser.cpp +++ /dev/null @@ -1,209 +0,0 @@ -/* - mkvmerge -- utility for splicing together matroska files - from component media subtypes - - Distributed under the GPL - see the file COPYING for details - or visit http://www.gnu.org/copyleft/gpl.html - - quick Matroska file parsing - - Written by Moritz Bunkus . -*/ - -#include -#include - -#include -#include -#include - -#include "common/common.h" -#include "common/ebml.h" -#include "common/error.h" -#include "common/quickparser.h" - -using namespace libebml; -using namespace libmatroska; - -kax_quickparser_c::kax_quickparser_c(mm_io_c &in, - bool parse_fully) - : m_in(in) -{ - in.setFilePointer(0, seek_beginning); - EbmlStream es(in); - - // Find the EbmlHead element. Must be the first one. - EbmlElement *l0 = es.FindNextID(EbmlHead::ClassInfos, 0xFFFFFFFFL); - if (NULL == l0) - throw error_c(Y("Error: No EBML head found.")); - - // Don't verify its data for now. - l0->SkipData(es, l0->Generic().Context); - delete l0; - - while (1) { - // Next element must be a segment - l0 = es.FindNextID(KaxSegment::ClassInfos, 0xFFFFFFFFFFFFFFFFLL); - if (NULL == l0) - throw error_c(Y("No segment/level 0 element found.")); - - if (EbmlId(*l0) == KaxSegment::ClassInfos.GlobalId) - break; - - l0->SkipData(es, l0->Generic().Context); - delete l0; - } - - int upper_lvl_el = 0; - EbmlElement *l1 = es.FindNextElement(l0->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); - while ((NULL != l1) && (0 >= upper_lvl_el)) { - if (is_id(l1, KaxCluster) && !parse_fully) - break; - - m_children.push_back(segment_child_t(l1->GetElementPosition(), l1->ElementSize(), l1->Generic().GlobalId)); - - if ((l0->GetElementPosition() + l0->ElementSize()) < l1->GetElementPosition()) { - delete l1; - break; - } - - if (0 > upper_lvl_el) { - upper_lvl_el++; - if (0 > upper_lvl_el) - break; - - } - - l1->SkipData(es, l1->Generic().Context); - delete l1; - l1 = es.FindNextElement(l0->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true); - - } // while (l1 != NULL) - - int child_idx; - for (child_idx = 0; m_children.size() > child_idx; child_idx++) { - segment_child_t *child = &m_children[child_idx]; - if (child->m_id != KaxSeekHead::ClassInfos.GlobalId) - continue; - - in.setFilePointer(child->m_pos, seek_beginning); - upper_lvl_el = 0; - EbmlElement *l1 = es.FindNextElement(l0->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1); - if (NULL == l1) - continue; - - if (!is_id(l1, KaxSeekHead)) { - delete l1; - continue; - } - - EbmlMaster *m = static_cast(l1); - EbmlElement *l2 = NULL; - m->Read(es, l1->Generic().Context, upper_lvl_el, l2, true); - - int master_idx; - for (master_idx = 0; m->ListSize() > master_idx; master_idx++) { - KaxSeek *seek = dynamic_cast((*m)[master_idx]); - if (NULL == seek) - continue; - - KaxSeekID *seek_id = FINDFIRST(seek, KaxSeekID); - int64_t pos = seek->Location() + l0->GetElementPosition() + l0->HeadSize(); - - if ((0 == pos) || (NULL == seek_id)) - continue; - - bool found = false; - std::vector::const_iterator it; - mxforeach(it, m_children) - if ((*it).m_pos == pos) { - found = true; - break; - } - - if (!found) - m_children.push_back(segment_child_t(pos, -1, EbmlId(seek_id->GetBuffer(), seek_id->GetSize()))); - } - delete l1; - } - - delete l0; - - m_current_child = m_children.begin(); -} - -int -kax_quickparser_c::num_elements(const EbmlId &id) - const { - std::vector::const_iterator it; - int num = 0; - - mxforeach(it, m_children) - if ((*it).m_id == id) - num++; - - return num; -} - -segment_child_t * -kax_quickparser_c::get_next(const EbmlId &id) { - while (m_current_child != m_children.end()) - if ((*m_current_child).m_id == id) { - segment_child_t *ptr = &(*m_current_child); - m_current_child++; - return ptr; - } else - m_current_child++; - - return NULL; -} - -void -kax_quickparser_c::reset() { - m_current_child = m_children.begin(); -} - -EbmlMaster * -kax_quickparser_c::read_all(const EbmlCallbacks &callbacks) { - segment_child_t *child; - EbmlMaster *m = NULL; - EbmlStream es(m_in); - - reset(); - - for (child = get_next(callbacks.GlobalId); child != NULL; child = get_next(callbacks.GlobalId)) { - m_in.setFilePointer(child->m_pos); - int upper_lvl_el = 0; - EbmlElement *e = es.FindNextElement(KaxSegment::ClassInfos.Context, upper_lvl_el, 0xFFFFFFFFL, true); - if (NULL == e) - continue; - - if (!(e->Generic().GlobalId == callbacks.GlobalId)) { - delete e; - continue; - } - - EbmlElement *l2 = NULL; - e->Read(es, callbacks.Context, upper_lvl_el, l2, true); - if (NULL == m) - m = static_cast(e); - else { - EbmlMaster *src = static_cast(e); - while (src->ListSize() > 0) { - m->PushElement(*(*src)[0]); - src->Remove(0); - } - delete e; - } - } - - reset(); - - if ((NULL != m) && (m->ListSize() == 0)) { - delete m; - return NULL; - } - - return m; -} diff --git a/src/common/quickparser.h b/src/common/quickparser.h deleted file mode 100644 index f09b3f398..000000000 --- a/src/common/quickparser.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - mkvmerge -- utility for splicing together matroska files - from component media subtypes - - Distributed under the GPL - see the file COPYING for details - or visit http://www.gnu.org/copyleft/gpl.html - - quick Matroska file parsing - - Written by Moritz Bunkus . -*/ - -#ifndef __MTX_COMMON_QUICKPARSER_H -#define __MTX_COMMON_QUICKPARSER_H - -#include "os.h" - -#include - -#include "common/common.h" -#include "common/mm_io.h" - -using namespace libebml; - -struct segment_child_t { - int64_t m_pos; - int64_t m_size; - EbmlId m_id; - - segment_child_t() - : m_pos(0) - , m_size(0) - , m_id((uint32_t)0, 0) - { - } - - segment_child_t(int64_t pos, - int64_t size, - const EbmlId &id) - : m_pos(pos) - , m_size(size) - , m_id(id) - { - } -}; - -class kax_quickparser_c { -private: - std::vector m_children; - std::vector::iterator m_current_child; - mm_io_c &m_in; - -public: - kax_quickparser_c(mm_io_c &in, bool parse_fully = false); - virtual ~kax_quickparser_c() {}; - - virtual int num_elements(const EbmlId &id) const; - virtual segment_child_t *get_next(const EbmlId &id); - virtual void reset(); - virtual EbmlMaster *read_all(const EbmlCallbacks &callbacks); -}; - -#endif diff --git a/src/extract/attachments.cpp b/src/extract/attachments.cpp index 9ce94cb4f..39d005395 100644 --- a/src/extract/attachments.cpp +++ b/src/extract/attachments.cpp @@ -33,8 +33,7 @@ extern "C" { #include "common/common.h" #include "common/ebml.h" -#include "common/mm_io.h" -#include "common/quickparser.h" +#include "common/kax_analyzer.h" #include "extract/mkvextract.h" using namespace libmatroska; @@ -112,27 +111,23 @@ void extract_attachments(const char *file_name, vector &tracks, bool parse_fully) { - mm_io_c *in; - kax_quickparser_c *qp; + kax_analyzer_cptr analyzer; // open input file try { - in = new mm_file_io_c(file_name); - qp = new kax_quickparser_c(*in, parse_fully); + analyzer = kax_analyzer_cptr(new kax_analyzer_c(file_name)); + analyzer->process(parse_fully); } catch (...) { show_error(boost::format(Y("The file '%1%' could not be opened for reading (%2%).")) % file_name % strerror(errno)); return; } - KaxAttachments *attachments = dynamic_cast(qp->read_all(KaxAttachments::ClassInfos)); - if (attachments != NULL) { + KaxAttachments *attachments = dynamic_cast(analyzer->read_all(KaxAttachments::ClassInfos)); + if (NULL != attachments) { handle_attachments(attachments, tracks); delete attachments; } - delete in; - delete qp; - int i; for (i = 0; i < tracks.size(); i++) if (!tracks[i].done) diff --git a/src/extract/chapters.cpp b/src/extract/chapters.cpp index 1bb9523f8..79776ccfb 100644 --- a/src/extract/chapters.cpp +++ b/src/extract/chapters.cpp @@ -32,52 +32,48 @@ extern "C" { #include "common/common.h" #include "common/ebml.h" #include "common/mm_io.h" -#include "common/quickparser.h" +#include "common/kax_analyzer.h" #include "extract/mkvextract.h" using namespace libmatroska; -using namespace std; void extract_chapters(const char *file_name, bool chapter_format_simple, bool parse_fully) { - mm_io_c *in; - kax_quickparser_c *qp; + kax_analyzer_cptr analyzer; // open input file try { - in = new mm_file_io_c(file_name); - qp = new kax_quickparser_c(*in, parse_fully); + analyzer = kax_analyzer_cptr(new kax_analyzer_c(file_name)); + analyzer->process(parse_fully); } catch (...) { show_error(boost::format(Y("The file '%1%' could not be opened for reading (%2%).")) % file_name % strerror(errno)); return; } - EbmlMaster *m = qp->read_all(KaxChapters::ClassInfos); - if (NULL != m) { - KaxChapters *chapters = dynamic_cast(m); - assert(NULL != chapters); + EbmlMaster *master = analyzer->read_all(KaxChapters::ClassInfos); + if (NULL == master) + return; - mm_stdio_c out; - if (!chapter_format_simple) { - out.write_bom("UTF-8"); - out.puts("\n" - "\n" - "\n" - "\n" - "\n"); - write_chapters_xml(chapters, &out); - out.puts("\n"); + KaxChapters *chapters = dynamic_cast(master); + assert(NULL != chapters); - } else { - int dummy = 1; - write_chapters_simple(dummy, chapters, &out); - } + mm_stdio_c out; + if (!chapter_format_simple) { + out.write_bom("UTF-8"); + out.puts("\n" + "\n" + "\n" + "\n" + "\n"); + write_chapters_xml(chapters, &out); + out.puts("\n"); - delete chapters; + } else { + int dummy = 1; + write_chapters_simple(dummy, chapters, &out); } - delete in; - delete qp; + delete chapters; } diff --git a/src/extract/cuesheets.cpp b/src/extract/cuesheets.cpp index 26b707e8f..48a35a130 100644 --- a/src/extract/cuesheets.cpp +++ b/src/extract/cuesheets.cpp @@ -13,13 +13,6 @@ #include "common/os.h" #include -#include -#include -#include - -extern "C" { -#include -} #include #include @@ -37,10 +30,10 @@ extern "C" { #include "common/chapters.h" #include "common/common.h" #include "common/ebml.h" +#include "common/kax_analyzer.h" #include "common/math.h" #include "common/matroska.h" #include "common/mm_io.h" -#include "common/quickparser.h" #include "common/string_formatting.h" #include "common/tag_common.h" #include "extract/mkvextract.h" @@ -206,21 +199,20 @@ write_cuesheet(const char *file_name, void extract_cuesheet(const char *file_name, bool parse_fully) { - mm_io_c *in; - kax_quickparser_c *qp; + kax_analyzer_cptr analyzer; // open input file try { - in = new mm_file_io_c(file_name); - qp = new kax_quickparser_c(*in, parse_fully); + analyzer = kax_analyzer_cptr(new kax_analyzer_c(file_name)); + analyzer->process(parse_fully); } catch (...) { show_error(boost::format(Y("The file '%1%' could not be opened for reading (%2%).")) % file_name % strerror(errno)); return; } KaxChapters all_chapters; - KaxChapters *chapters = dynamic_cast(qp->read_all(KaxChapters::ClassInfos)); - KaxTags *all_tags = dynamic_cast(qp->read_all(KaxTags::ClassInfos)); + KaxChapters *chapters = dynamic_cast(analyzer->read_all(KaxChapters::ClassInfos)); + KaxTags *all_tags = dynamic_cast(analyzer->read_all(KaxTags::ClassInfos)); if ((NULL != chapters) && (NULL != all_tags)) { int i; @@ -244,7 +236,4 @@ extract_cuesheet(const char *file_name, delete all_tags; delete chapters; - - delete in; - delete qp; } diff --git a/src/extract/tags.cpp b/src/extract/tags.cpp index 5f245b893..f2ebde9d9 100644 --- a/src/extract/tags.cpp +++ b/src/extract/tags.cpp @@ -31,8 +31,8 @@ extern "C" { #include "common/common.h" #include "common/ebml.h" +#include "common/kax_analyzer.h" #include "common/mm_io.h" -#include "common/quickparser.h" #include "common/tagwriter.h" #include "extract/mkvextract.h" @@ -42,19 +42,18 @@ using namespace std; void extract_tags(const char *file_name, bool parse_fully) { - mm_io_c *in; - kax_quickparser_c *qp; + kax_analyzer_cptr analyzer; // open input file try { - in = new mm_file_io_c(file_name); - qp = new kax_quickparser_c(*in, parse_fully); + analyzer = kax_analyzer_cptr(new kax_analyzer_c(file_name)); + analyzer->process(parse_fully); } catch (...) { show_error(boost::format(Y("The file '%1%' could not be opened for reading (%2%).")) % file_name % strerror(errno)); return; } - EbmlMaster *m = qp->read_all(KaxTags::ClassInfos); + EbmlMaster *m = analyzer->read_all(KaxTags::ClassInfos); if (NULL != m) { KaxTags *tags = dynamic_cast(m); assert(NULL != tags); @@ -69,7 +68,4 @@ extract_tags(const char *file_name, delete tags; } - - delete in; - delete qp; } diff --git a/src/mmg/he_value_page.h b/src/mmg/he_value_page.h index b6f857dd3..e668f0ab7 100644 --- a/src/mmg/he_value_page.h +++ b/src/mmg/he_value_page.h @@ -16,6 +16,8 @@ #include "common/os.h" +#include + #include "mmg/header_editor_frame.h" class he_value_page_c: public he_page_base_c { diff --git a/src/mmg/header_editor_frame.cpp b/src/mmg/header_editor_frame.cpp index 889755ef1..f09acda74 100644 --- a/src/mmg/header_editor_frame.cpp +++ b/src/mmg/header_editor_frame.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -217,7 +218,7 @@ header_editor_frame_c::open_file(wxFileName file_name) { m_e_tracks = NULL; delete m_analyzer; - m_analyzer = new kax_analyzer_c(this, wxMB(file_name.GetFullPath())); + m_analyzer = new wx_kax_analyzer_c(this, wxMB(file_name.GetFullPath())); if (!m_analyzer->process()) { delete m_analyzer; m_analyzer = NULL; @@ -245,17 +246,17 @@ header_editor_frame_c::open_file(wxFileName file_name) { m_pages.clear(); m_top_level_pages.clear(); - for (i = 0; m_analyzer->data.size() > i; ++i) { - analyzer_data_c *data = m_analyzer->data[i]; - if (data->id == KaxInfo::ClassInfos.GlobalId) { + for (i = 0; m_analyzer->m_data.size() > i; ++i) { + kax_analyzer_data_c *data = m_analyzer->m_data[i].get(); + if (data->m_id == KaxInfo::ClassInfos.GlobalId) { handle_segment_info(data); break; } } - for (i = 0; m_analyzer->data.size() > i; ++i) { - analyzer_data_c *data = m_analyzer->data[i]; - if (data->id == KaxTracks::ClassInfos.GlobalId) { + for (i = 0; m_analyzer->m_data.size() > i; ++i) { + kax_analyzer_data_c *data = m_analyzer->m_data[i].get(); + if (data->m_id == KaxTracks::ClassInfos.GlobalId) { handle_tracks(data); break; } @@ -272,7 +273,7 @@ header_editor_frame_c::open_file(wxFileName file_name) { } void -header_editor_frame_c::handle_segment_info(analyzer_data_c *data) { +header_editor_frame_c::handle_segment_info(kax_analyzer_data_c *data) { EbmlElement *e = m_analyzer->read_element(data); if (NULL == e) return; @@ -311,7 +312,7 @@ header_editor_frame_c::handle_segment_info(analyzer_data_c *data) { } void -header_editor_frame_c::handle_tracks(analyzer_data_c *data) { +header_editor_frame_c::handle_tracks(kax_analyzer_data_c *data) { EbmlElement *e = m_analyzer->read_element(data); if (NULL == e) return; diff --git a/src/mmg/header_editor_frame.h b/src/mmg/header_editor_frame.h index 694367837..62ac756b7 100644 --- a/src/mmg/header_editor_frame.h +++ b/src/mmg/header_editor_frame.h @@ -26,7 +26,7 @@ #include #include "mmg/he_page_base.h" -#include "mmg/kax_analyzer.h" +#include "mmg/wx_kax_analyzer.h" #define ID_M_HE_FILE_OPEN 20000 #define ID_M_HE_FILE_SAVE 20001 @@ -69,7 +69,7 @@ public: wxTreeCtrl *m_tc_tree; wxTreeItemId m_root_id; - kax_analyzer_c *m_analyzer; + wx_kax_analyzer_c *m_analyzer; EbmlElement *m_e_segment_info, *m_e_tracks; @@ -116,8 +116,8 @@ protected: void clear_pages(); - void handle_segment_info(analyzer_data_c *data); - void handle_tracks(analyzer_data_c *data); + void handle_segment_info(kax_analyzer_data_c *data); + void handle_tracks(kax_analyzer_data_c *data); bool have_been_modified(); void do_modifications(); diff --git a/src/mmg/kax_analyzer.h b/src/mmg/kax_analyzer.h deleted file mode 100644 index 7209e0b8d..000000000 --- a/src/mmg/kax_analyzer.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - mkvmerge GUI -- utility for splicing together matroska files - from component media subtypes - - Distributed under the GPL - see the file COPYING for details - or visit http://www.gnu.org/copyleft/gpl.html - - Matroska file analyzer - - Written by Moritz Bunkus . -*/ - -#ifndef __KAX_ANALYZER_H -#define __KAX_ANALYZER_H - -#include -#include - -#include -#include - -#include - -#include "common/common.h" -#include "common/error.h" -#include "common/mm_io.h" - -using namespace std; -using namespace libmatroska; - -#define ID_B_ANALYZER_ABORT 11000 - -class analyzer_data_c { -public: - EbmlId id; - int64_t pos, size; - bool delete_this; - -public: - analyzer_data_c(const EbmlId nid, int64_t npos, int64_t nsize): - id(nid), pos(npos), size(nsize), delete_this(false) { - }; -}; - -class kax_analyzer_c { -public: - enum update_element_result_e { - uer_success, - uer_error_segment_size_for_element, - uer_error_segment_size_for_meta_seek, - uer_error_meta_seek, - uer_error_not_indexable, - uer_error_unknown, - }; - -public: - wxWindow *m_parent; - - vector data; - string file_name; - mm_file_io_c *file; - KaxSegment *segment; - -public: // Static functions - static bool probe(string file_name); - -public: - kax_analyzer_c(wxWindow *parent, string name); - virtual ~kax_analyzer_c(); - - virtual update_element_result_e update_element(EbmlElement *e, bool write_defaults = false); - virtual EbmlElement *read_element(analyzer_data_c *element_data); - virtual EbmlElement *read_element(unsigned int pos) { - return read_element(data[pos]); - } - virtual int find(const EbmlId &id) { - uint32_t i; - - for (i = 0; i < data.size(); i++) - if (id == data[i]->id) - return i; - - return -1; - }; - - virtual bool process(); - -protected: - virtual void remove_from_meta_seeks(EbmlId id); - virtual void overwrite_all_instances(EbmlId id); - virtual void merge_void_elements(); - virtual void write_element(EbmlElement *e, bool write_defaults); - virtual void add_to_meta_seek(EbmlElement *e); - - virtual void adjust_segment_size(); - virtual bool create_void_element(int64_t file_pos, int void_size, int data_idx, bool add_new_data_element); - - virtual void debug_dump_elements(); -}; - -#endif // __KAX_ANALYZER_H diff --git a/src/mmg/mmg_dialog.h b/src/mmg/mmg_dialog.h index b1938e339..f65029ca9 100644 --- a/src/mmg/mmg_dialog.h +++ b/src/mmg/mmg_dialog.h @@ -14,7 +14,10 @@ #ifndef __MMG_DIALOG_H #define __MMG_DIALOG_H +#include "common/os.h" + #include +#include #include "mmg/mmg.h" diff --git a/src/mmg/tab_chapters.cpp b/src/mmg/tab_chapters.cpp index 7daee75ff..9cb211cf6 100644 --- a/src/mmg/tab_chapters.cpp +++ b/src/mmg/tab_chapters.cpp @@ -34,10 +34,10 @@ #include "common/string_parsing.h" #include "common/unique_numbers.h" #include "common/wxcommon.h" -#include "mmg/kax_analyzer.h" #include "mmg/mmg_dialog.h" #include "mmg/mmg.h" #include "mmg/tab_chapters.h" +#include "mmg/wx_kax_analyzer.h" using namespace std; using namespace libebml; @@ -562,7 +562,7 @@ tab_chapters::load(wxString name) { if (kax_analyzer_c::probe(wxMB(name))) { if (analyzer != NULL) delete analyzer; - analyzer = new kax_analyzer_c(this, wxMB(name)); + analyzer = new wx_kax_analyzer_c(this, wxMB(name)); file_name = name; if (!analyzer->process()) { delete analyzer; @@ -667,7 +667,7 @@ tab_chapters::on_save_chapters_to_kax_file(wxCommandEvent &evt) { if (analyzer != NULL) delete analyzer; - analyzer = new kax_analyzer_c(this, wxMB(file_name)); + analyzer = new wx_kax_analyzer_c(this, wxMB(file_name)); if (!analyzer->process()) { delete analyzer; analyzer = NULL; diff --git a/src/mmg/tab_chapters.h b/src/mmg/tab_chapters.h index 200dbd599..8a660efc2 100644 --- a/src/mmg/tab_chapters.h +++ b/src/mmg/tab_chapters.h @@ -20,7 +20,7 @@ #include -#include "mmg/kax_analyzer.h" +#include "mmg/wx_kax_analyzer.h" #include "common/wxcommon.h" #define ID_TRC_CHAPTERS 16000 @@ -71,7 +71,7 @@ public: KaxChapters *chapters; - kax_analyzer_c *analyzer; + wx_kax_analyzer_c *analyzer; public: tab_chapters(wxWindow *parent, wxMenu *nm_chapters); diff --git a/src/mmg/wx_kax_analyzer.cpp b/src/mmg/wx_kax_analyzer.cpp new file mode 100644 index 000000000..8a5ae38af --- /dev/null +++ b/src/mmg/wx_kax_analyzer.cpp @@ -0,0 +1,53 @@ +/* + mkvmerge GUI -- utility for splicing together matroska files + from component media subtypes + + Distributed under the GPL + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html + + Matroska file analyzer (wxWidgets interface) + + Written by Moritz Bunkus . +*/ + +#include "common/os.h" + +#include + +#include "mmg/mmg.h" +#include "mmg/wx_kax_analyzer.h" + +wx_kax_analyzer_c::wx_kax_analyzer_c(wxWindow *parent, + std::string file_name) + : kax_analyzer_c(file_name) + , m_parent(parent) +{ +} + +wx_kax_analyzer_c::~wx_kax_analyzer_c() { +} + +void +wx_kax_analyzer_c::show_progress_start(int64_t file_size) { + m_prog_dlg = new wxProgressDialog(Z("Analysis is running"), Z("The Matroska file is analyzed."), 100, m_parent, + wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_REMAINING_TIME); + + while (app->Pending()) + app->Dispatch(); +} + +bool +wx_kax_analyzer_c::show_progress_running(int percentage) { + bool aborted = !m_prog_dlg->Update(percentage); + while (app->Pending()) + app->Dispatch(); + + return !aborted; +} + +void +wx_kax_analyzer_c::show_progress_done() { + delete m_prog_dlg; + m_prog_dlg = NULL; +} diff --git a/src/mmg/wx_kax_analyzer.h b/src/mmg/wx_kax_analyzer.h new file mode 100644 index 000000000..4001476b0 --- /dev/null +++ b/src/mmg/wx_kax_analyzer.h @@ -0,0 +1,41 @@ +/* + mkvmerge GUI -- utility for splicing together matroska files + from component media subtypes + + Distributed under the GPL + see the file COPYING for details + or visit http://www.gnu.org/copyleft/gpl.html + + Matroska file analyzer (wxWidgets interface) + + Written by Moritz Bunkus . +*/ + +#ifndef __WX_KAX_ANALYZER_H +#define __WX_KAX_ANALYZER_H + +#include "common/os.h" + +#include +#include + +#include "common/common.h" +#include "common/kax_analyzer.h" + +#define ID_B_ANALYZER_ABORT 11000 + +class wx_kax_analyzer_c: public kax_analyzer_c { +private: + wxWindow *m_parent; + wxProgressDialog *m_prog_dlg; + +public: + wx_kax_analyzer_c(wxWindow *parent, std::string file_name); + virtual ~wx_kax_analyzer_c(); + + virtual void show_progress_start(int64_t size); + virtual bool show_progress_running(int percentage); + virtual void show_progress_done(); +}; + +#endif // __WX_KAX_ANALYZER_H