Merged the two classes kax_quickparser_c and kax_analyzer_c.

Both do roughly the same anyway.
This commit is contained in:
Moritz Bunkus 2009-05-09 22:08:18 +02:00
parent ed5e761cfb
commit 083c0beb70
17 changed files with 597 additions and 655 deletions

View File

@ -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 <moritz@bunkus.org>.
*/
// 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 <limits>
#endif
#include <iostream>
#include "common/os.h"
#include <wx/progdlg.h>
#include <algorithm>
#include <ebml/EbmlHead.h>
#include <ebml/EbmlStream.h>
#include <ebml/EbmlVoid.h>
#include <matroska/KaxCluster.h>
#include <matroska/KaxSeekHead.h>
#include <matroska/KaxSegment.h>
#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<KaxSegment *>(l0);
int64_t file_size = file->get_size();
int upper_lvl_el = 0;
bool cont = true;
m_segment = counted_ptr<KaxSegment>(static_cast<KaxSegment *>(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<KaxSegment> new_segment = counted_ptr<KaxSegment>(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<KaxSeekHead *>(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<EbmlMaster *>(element);
else {
EbmlMaster *src = static_cast<EbmlMaster *>(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<EbmlMaster *>(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<KaxSeek *>((*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<kax_analyzer_data_cptr>::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;
}

132
src/common/kax_analyzer.h Normal file
View File

@ -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 <moritz@bunkus.org>.
*/
#ifndef __MTX_COMMON_KAX_ANALYZER_H
#define __MTX_COMMON_KAX_ANALYZER_H
#include "os.h"
#include <map>
#include <matroska/KaxSegment.h>
#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_c> 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<kax_analyzer_data_cptr> m_data;
private:
std::string m_file_name;
mm_file_io_c *m_file;
bool m_close_file;
counted_ptr<KaxSegment> m_segment;
std::map<int64_t, bool> 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_c> kax_analyzer_cptr;
#endif // __MTX_COMMON_KAX_ANALYZER_H

View File

@ -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 <moritz@bunkus.org>.
*/
#include <ebml/EbmlHead.h>
#include <ebml/EbmlStream.h>
#include <matroska/KaxCluster.h>
#include <matroska/KaxSegment.h>
#include <matroska/KaxSeekHead.h>
#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<EbmlMaster *>(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<KaxSeek *>((*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<segment_child_t>::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<segment_child_t>::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<EbmlMaster *>(e);
else {
EbmlMaster *src = static_cast<EbmlMaster *>(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;
}

View File

@ -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 <moritz@bunkus.org>.
*/
#ifndef __MTX_COMMON_QUICKPARSER_H
#define __MTX_COMMON_QUICKPARSER_H
#include "os.h"
#include <vector>
#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<segment_child_t> m_children;
std::vector<segment_child_t>::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

View File

@ -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<track_spec_t> &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<KaxAttachments *>(qp->read_all(KaxAttachments::ClassInfos));
if (attachments != NULL) {
KaxAttachments *attachments = dynamic_cast<KaxAttachments *>(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)

View File

@ -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<KaxChapters *>(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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"\n"
"<!-- <!DOCTYPE Tags SYSTEM \"matroskatags.dtd\"> -->\n"
"\n"
"<Chapters>\n");
write_chapters_xml(chapters, &out);
out.puts("</Chapters>\n");
KaxChapters *chapters = dynamic_cast<KaxChapters *>(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("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"\n"
"<!-- <!DOCTYPE Tags SYSTEM \"matroskatags.dtd\"> -->\n"
"\n"
"<Chapters>\n");
write_chapters_xml(chapters, &out);
out.puts("</Chapters>\n");
delete chapters;
} else {
int dummy = 1;
write_chapters_simple(dummy, chapters, &out);
}
delete in;
delete qp;
delete chapters;
}

View File

@ -13,13 +13,6 @@
#include "common/os.h"
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
extern "C" {
#include <avilib.h>
}
#include <ebml/EbmlHead.h>
#include <ebml/EbmlSubHead.h>
@ -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<KaxChapters *>(qp->read_all(KaxChapters::ClassInfos));
KaxTags *all_tags = dynamic_cast<KaxTags *>(qp->read_all(KaxTags::ClassInfos));
KaxChapters *chapters = dynamic_cast<KaxChapters *>(analyzer->read_all(KaxChapters::ClassInfos));
KaxTags *all_tags = dynamic_cast<KaxTags *>(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;
}

View File

@ -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<KaxTags *>(m);
assert(NULL != tags);
@ -69,7 +68,4 @@ extract_tags(const char *file_name,
delete tags;
}
delete in;
delete qp;
}

View File

@ -16,6 +16,8 @@
#include "common/os.h"
#include <wx/checkbox.h>
#include "mmg/header_editor_frame.h"
class he_value_page_c: public he_page_base_c {

View File

@ -19,6 +19,7 @@
#include <wx/button.h>
#include <wx/dnd.h>
#include <wx/filedlg.h>
#include <wx/msgdlg.h>
#include <wx/statline.h>
#include <wx/textctrl.h>
@ -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;

View File

@ -26,7 +26,7 @@
#include <wx/treectrl.h>
#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();

View File

@ -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 <moritz@bunkus.org>.
*/
#ifndef __KAX_ANALYZER_H
#define __KAX_ANALYZER_H
#include <string>
#include <vector>
#include <ebml/EbmlElement.h>
#include <matroska/KaxSegment.h>
#include <wx/wx.h>
#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<analyzer_data_c *> 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

View File

@ -14,7 +14,10 @@
#ifndef __MMG_DIALOG_H
#define __MMG_DIALOG_H
#include "common/os.h"
#include <wx/html/helpctrl.h>
#include <wx/log.h>
#include "mmg/mmg.h"

View File

@ -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;

View File

@ -20,7 +20,7 @@
#include <matroska/KaxChapters.h>
#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);

View File

@ -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 <moritz@bunkus.org>.
*/
#include "common/os.h"
#include <wx/progdlg.h>
#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;
}

41
src/mmg/wx_kax_analyzer.h Normal file
View File

@ -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 <moritz@bunkus.org>.
*/
#ifndef __WX_KAX_ANALYZER_H
#define __WX_KAX_ANALYZER_H
#include "common/os.h"
#include <wx/progdlg.h>
#include <wx/window.h>
#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