mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2025-01-04 09:15:05 +00:00
Merged the two classes kax_quickparser_c and kax_analyzer_c.
Both do roughly the same anyway.
This commit is contained in:
parent
ed5e761cfb
commit
083c0beb70
@ -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
132
src/common/kax_analyzer.h
Normal 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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
53
src/mmg/wx_kax_analyzer.cpp
Normal file
53
src/mmg/wx_kax_analyzer.cpp
Normal 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
41
src/mmg/wx_kax_analyzer.h
Normal 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
|
Loading…
Reference in New Issue
Block a user