/** \brief Move all chapter atoms to another container keeping editions intact This function moves all chapter atoms from \a src to \a dst. If there's already an edition in \a dst with the same UID as the current one in \a src, then all atoms will be put into that edition. Otherwise the complete edition will simply be moved over. After processing \a src will be empty. Its parameters don't have to be checked for validity. \param dst The container the atoms and editions will be put into. \param src The container the atoms and editions will be taken from. */ void move_chapters_by_edition(KaxChapters &dst, KaxChapters &src) { size_t src_idx; for (src_idx = 0; src.ListSize() > src_idx; src_idx++) { EbmlMaster *m = dynamic_cast<EbmlMaster *>(src[src_idx]); if (!m) continue; // Find an edition to which these atoms will be added. KaxEditionEntry *ee_dst = nullptr; KaxEditionUID *euid_src = FindChild<KaxEditionUID>(m); if (euid_src) ee_dst = find_edition_with_uid(dst, uint64(*euid_src)); // No edition with the same UID found as the one we want to handle? // Then simply move the complete edition over. if (!ee_dst) dst.PushElement(*m); else { // Move all atoms from the old edition to the new one. size_t master_idx; for (master_idx = 0; m->ListSize() > master_idx; master_idx++) if (is_id((*m)[master_idx], KaxChapterAtom)) ee_dst->PushElement(*(*m)[master_idx]); else delete (*m)[master_idx]; m->RemoveAll(); delete m; } } src.RemoveAll(); }
/** \brief Adjust all start and end timecodes by an offset All start and end timecodes are adjusted by an offset. This is done recursively. Its parameters don't have to be checked for validity. \param master A master containint the elements to adjust. This can be a KaxChapters, KaxEditionEntry or KaxChapterAtom object. \param offset The offset to add to each timecode. Can be negative. If the resulting timecode would be smaller than zero then it will be set to zero. */ void adjust_chapter_timecodes(EbmlMaster &master, int64_t offset) { size_t master_idx; for (master_idx = 0; master.ListSize() > master_idx; master_idx++) { if (!is_id(master[master_idx], KaxChapterAtom)) continue; KaxChapterAtom *atom = static_cast<KaxChapterAtom *>(master[master_idx]); KaxChapterTimeStart *start = FindChild<KaxChapterTimeStart>(atom); KaxChapterTimeEnd *end = FindChild<KaxChapterTimeEnd>(atom); if (start) *static_cast<EbmlUInteger *>(start) = std::max(int64_t(uint64(*start)) + offset, int64_t(0)); if (end) *static_cast<EbmlUInteger *>(end) = std::max(int64_t(uint64(*end)) + offset, int64_t(0)); } for (master_idx = 0; master.ListSize() > master_idx; master_idx++) { EbmlMaster *work_master = dynamic_cast<EbmlMaster *>(master[master_idx]); if (work_master) adjust_chapter_timecodes(*work_master, offset); } }
/** \brief Add missing mandatory elements The Matroska specs and \c libmatroska say that several elements are mandatory. This function makes sure that they all exist by adding them with their default values if they're missing. It works recursively. See <a href="http://www.matroska.org/technical/specs/chapters/index.html"> the Matroska chapter specs</a> for a list or mandatory elements. The parameters are checked for validity. \param e An element that really is an \c EbmlMaster. \a e's children should be checked. */ void fix_mandatory_chapter_elements(EbmlElement *e) { if (!e) return; if (dynamic_cast<KaxEditionEntry *>(e)) { KaxEditionEntry &ee = *static_cast<KaxEditionEntry *>(e); GetChild<KaxEditionFlagDefault>(ee); GetChild<KaxEditionFlagHidden>(ee); if (!FindChild<KaxEditionUID>(&ee)) GetChildAs<KaxEditionUID, EbmlUInteger>(ee) = create_unique_number(UNIQUE_EDITION_IDS); } else if (dynamic_cast<KaxChapterAtom *>(e)) { KaxChapterAtom &a = *static_cast<KaxChapterAtom *>(e); GetChild<KaxChapterFlagHidden>(a); GetChild<KaxChapterFlagEnabled>(a); if (!FindChild<KaxChapterUID>(&a)) GetChildAs<KaxChapterUID, EbmlUInteger>(a) = create_unique_number(UNIQUE_CHAPTER_IDS); if (!FindChild<KaxChapterTimeStart>(&a)) GetChildAs<KaxChapterTimeStart, EbmlUInteger>(a) = 0; } else if (dynamic_cast<KaxChapterTrack *>(e)) { KaxChapterTrack &t = *static_cast<KaxChapterTrack *>(e); if (!FindChild<KaxChapterTrackNumber>(&t)) GetChildAs<KaxChapterTrackNumber, EbmlUInteger>(t) = 0; } else if (dynamic_cast<KaxChapterDisplay *>(e)) { KaxChapterDisplay &d = *static_cast<KaxChapterDisplay *>(e); if (!FindChild<KaxChapterString>(&d)) GetChildAs<KaxChapterString, EbmlUnicodeString>(d) = L""; if (!FindChild<KaxChapterLanguage>(&d)) GetChildAs<KaxChapterLanguage, EbmlString>(d) = "eng"; } else if (dynamic_cast<KaxChapterProcess *>(e)) { KaxChapterProcess &p = *static_cast<KaxChapterProcess *>(e); GetChild<KaxChapterProcessCodecID>(p); } else if (dynamic_cast<KaxChapterProcessCommand *>(e)) { KaxChapterProcessCommand &c = *static_cast<KaxChapterProcessCommand *>(e); GetChild<KaxChapterProcessTime>(c); GetChild<KaxChapterProcessData>(c); } if (dynamic_cast<EbmlMaster *>(e)) { EbmlMaster *m = static_cast<EbmlMaster *>(e); size_t i; for (i = 0; i < m->ListSize(); i++) fix_mandatory_chapter_elements((*m)[i]); } }
/** \brief Adjust all start and end timecodes by an offset All start and end timecodes are adjusted by an offset. This is done recursively. Its parameters don't have to be checked for validity. \param master A master containint the elements to adjust. This can be a KaxChapters, KaxEditionEntry or KaxChapterAtom object. \param offset The offset to add to each timecode. Can be negative. If the resulting timecode would be smaller than zero then it will be set to zero. */ void adjust_chapter_timecodes(EbmlMaster &master, int64_t offset) { size_t master_idx; for (master_idx = 0; master.ListSize() > master_idx; master_idx++) { if (!Is<KaxChapterAtom>(master[master_idx])) continue; KaxChapterAtom *atom = static_cast<KaxChapterAtom *>(master[master_idx]); KaxChapterTimeStart *start = FindChild<KaxChapterTimeStart>(atom); KaxChapterTimeEnd *end = FindChild<KaxChapterTimeEnd>(atom); if (start) start->SetValue(std::max<int64_t>(static_cast<int64_t>(start->GetValue()) + offset, 0)); if (end) end->SetValue(std::max<int64_t>(static_cast<int64_t>(end->GetValue()) + offset, 0)); } for (master_idx = 0; master.ListSize() > master_idx; master_idx++) { EbmlMaster *work_master = dynamic_cast<EbmlMaster *>(master[master_idx]); if (work_master) adjust_chapter_timecodes(*work_master, offset); } }
unsigned long kax_file_c::get_element_size(EbmlElement *e) { EbmlMaster *m = dynamic_cast<EbmlMaster *>(e); if (!m || e->IsFiniteSize()) return e->GetSizeLength() + EBML_ID_LENGTH(static_cast<const EbmlId &>(*e)) + e->GetSize(); unsigned long max_end_pos = e->GetElementPosition() + EBML_ID_LENGTH(static_cast<const EbmlId &>(*e)); unsigned int idx; for (idx = 0; m->ListSize() > idx; ++idx) max_end_pos = std::max(max_end_pos, static_cast<unsigned long>((*m)[idx]->GetElementPosition() + get_element_size((*m)[idx]))); return max_end_pos - e->GetElementPosition(); }
void kax_analyzer_c::read_meta_seek(uint64_t pos, std::map<int64_t, bool> &positions_found) { if (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(EBML_CONTEXT(m_segment), upper_lvl_el, 0xFFFFFFFFL, true, 1); if (!l1) return; if (!Is<KaxSeekHead>(l1)) { delete l1; return; } EbmlElement *l2 = nullptr; EbmlMaster *master = static_cast<EbmlMaster *>(l1); master->Read(*m_stream, EBML_CONTEXT(l1), upper_lvl_el, l2, true); unsigned int i; for (i = 0; master->ListSize() > i; i++) { if (!Is<KaxSeek>((*master)[i])) continue; KaxSeek *seek = static_cast<KaxSeek *>((*master)[i]); KaxSeekID *seek_id = FindChild<KaxSeekID>(seek); int64_t seek_pos = seek->Location() + m_segment->GetElementPosition() + m_segment->HeadSize(); if ((0 == pos) || !seek_id) continue; if (positions_found[seek_pos]) continue; EbmlId the_id(seek_id->GetBuffer(), seek_id->GetSize()); m_data.push_back(kax_analyzer_data_c::create(the_id, seek_pos, -1)); positions_found[seek_pos] = true; if (Is<KaxSeekHead>(the_id)) read_meta_seek(seek_pos, positions_found); } delete l1; }
EbmlElement * empty_ebml_master(EbmlElement *e) { EbmlMaster *m; m = dynamic_cast<EbmlMaster *>(e); if (!m) return e; while (m->ListSize() > 0) { delete (*m)[0]; m->Remove(0); } return m; }
void dump_ebml_elements(EbmlElement *element, bool with_values, unsigned int level) { std::string indent_str, value_str; size_t i; for (i = 1; i <= level; ++i) indent_str += " "; if (with_values) { if (NULL != dynamic_cast<EbmlUInteger *>(element)) value_str = to_string(uint64(*static_cast<EbmlUInteger *>(element))); else if (NULL != dynamic_cast<EbmlSInteger *>(element)) value_str = to_string(int64(*static_cast<EbmlSInteger *>(element))); else if (NULL != dynamic_cast<EbmlFloat *>(element)) value_str = to_string(double(*static_cast<EbmlFloat *>(element)), 9); else if (NULL != dynamic_cast<EbmlUnicodeString *>(element)) value_str = UTFstring_to_cstrutf8(UTFstring(*static_cast<EbmlUnicodeString *>(element))); else if (NULL != dynamic_cast<EbmlString *>(element)) value_str = std::string(*static_cast<EbmlString *>(element)); else if (NULL != dynamic_cast<EbmlDate *>(element)) value_str = to_string(static_cast<EbmlDate *>(element)->GetEpochDate()); else value_str = (boost::format("(type: %1%)") % ( NULL != dynamic_cast<EbmlBinary *>(element) ? "binary" : NULL != dynamic_cast<EbmlMaster *>(element) ? "master" : NULL != dynamic_cast<EbmlVoid *>(element) ? "void" : "unknown")).str(); value_str = " " + value_str; } mxinfo(boost::format("%1%%2%%3%\n") % indent_str % EBML_NAME(element) % value_str); EbmlMaster *master = dynamic_cast<EbmlMaster *>(element); if (NULL == master) return; for (i = 0; master->ListSize() > i; ++i) dump_ebml_elements((*master)[i], with_values, level + 1); }
ebml_master_cptr kax_analyzer_c::read_all(const EbmlCallbacks &callbacks) { reopen_file(); ebml_master_cptr master; EbmlStream es(*m_file); size_t i; for (i = 0; m_data.size() > i; ++i) { kax_analyzer_data_c &data = *m_data[i].get(); if (EBML_INFO_ID(callbacks) != data.m_id) continue; m_file->setFilePointer(data.m_pos); int upper_lvl_el = 0; EbmlElement *element = es.FindNextElement(EBML_CLASS_CONTEXT(KaxSegment), upper_lvl_el, 0xFFFFFFFFL, true); if (!element) continue; if (EbmlId(*element) != EBML_INFO_ID(callbacks)) { delete element; continue; } EbmlElement *l2 = nullptr; element->Read(*m_stream, EBML_INFO_CONTEXT(callbacks), upper_lvl_el, l2, true); if (!master) master = ebml_master_cptr(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 (master && (master->ListSize() == 0)) master.reset(); return master; }
std::vector<std::string> EbmlMaster::FindAllMissingElements() { assert(Context.GetSize() != 0); std::vector<std::string> missingElements; for (size_t ChildElementNo = 0; ChildElementNo < ElementList.size(); ChildElementNo++) { EbmlElement *childElement = ElementList[ChildElementNo]; if (!childElement->ValueIsSet()) { std::string missingValue; missingValue = "The Child Element \""; missingValue.append(EBML_NAME(childElement)); missingValue.append("\" of EbmlMaster \""); missingValue.append(EBML_NAME(this)); missingValue.append("\", does not have a value set."); missingElements.push_back(missingValue); } if (childElement->IsMaster()) { EbmlMaster *childMaster = (EbmlMaster *)childElement; std::vector<std::string> childMissingElements = childMaster->FindAllMissingElements(); for (size_t s = 0; s < childMissingElements.size(); s++) missingElements.push_back(childMissingElements[s]); } } unsigned int EltIdx; for (EltIdx = 0; EltIdx < EBML_CTX_SIZE(Context); EltIdx++) { if (EBML_CTX_IDX(Context,EltIdx).IsMandatory()) { if (FindElt(EBML_CTX_IDX_INFO(Context,EltIdx)) == NULL) { std::string missingElement; missingElement = "Missing element \""; missingElement.append(EBML_INFO_NAME(EBML_CTX_IDX_INFO(Context,EltIdx))); missingElement.append("\" in EbmlMaster \""); missingElement.append(EBML_INFO_NAME(*EBML_CTX_MASTER(Context))); missingElement.append("\""); missingElements.push_back(missingElement); } } } return missingElements; }
static int count_chapter_atoms_recursively(EbmlMaster &master, int count) { size_t master_idx; for (master_idx = 0; master.ListSize() > master_idx; ++master_idx) if (is_id(master[master_idx], KaxChapterAtom)) ++count; else if (dynamic_cast<EbmlMaster *>(master[master_idx])) count = count_chapter_atoms_recursively(*static_cast<EbmlMaster *>(master[master_idx]), count); return count; }
EbmlMaster::EbmlMaster(const EbmlMaster & ElementToClone) :EbmlElement(ElementToClone) ,ElementList(ElementToClone.ListSize()) ,Context(ElementToClone.Context) ,bChecksumUsed(ElementToClone.bChecksumUsed) ,Checksum(ElementToClone.Checksum) { // add a clone of the list std::vector<EbmlElement *>::const_iterator Itr = ElementToClone.ElementList.begin(); std::vector<EbmlElement *>::iterator myItr = ElementList.begin(); while (Itr != ElementToClone.ElementList.end()) { *myItr = (*Itr)->Clone(); Itr++; myItr++; } }
/*! \test Some test on the Cluster use \todo render the Cluster to a file */ int main(void) { StdIOCallback Ebml_file(RW_FILENAME, ::MODE_CREATE); ///// Writing test /////////////////////////////// // Unsigned integer /////////////////////////////// EbmlUInteger testUInt(4); // supposed to hold a 4*8 bits value testUInt.SetID(SemanticList[0].Id, SemanticList[0].IdLength); testUInt = 52; testUInt.SetSizeLength(3); // size should be coded on at least 3 octets testUInt.Render(Ebml_file); /////////////////////////////// // Signed integer /////////////////////////////// EbmlSInteger testSInt(4); // supposed to hold a 4*8 bits value testSInt.SetID(SemanticList[1].Id, SemanticList[1].IdLength); testSInt = -20; testSInt.Render(Ebml_file); /////////////////////////////// // Binary data /////////////////////////////// const int BINARY_SIZE=3000; binary *bin = new binary[BINARY_SIZE]; memset(bin, 0x61, BINARY_SIZE); EbmlBinary testBin; testBin.SetID(SemanticList[2].Id, SemanticList[2].IdLength); testBin.SetBuffer(bin, BINARY_SIZE); testBin.Render(Ebml_file); /////////////////////////////// // String data /////////////////////////////// std::string aString = "Hello World !"; EbmlString testStr(200); testStr.SetID(SemanticList[3].Id, SemanticList[3].IdLength); testStr = aString; testStr.Render(Ebml_file); /////////////////////////////// // Master element /////////////////////////////// EbmlMaster testMaster; testMaster.SetID(SemanticList[6].Id, SemanticList[6].IdLength); testMaster.PushElement(testStr); testMaster.PushElement(testUInt); testMaster.Render(Ebml_file); /////////////////////////////// // Unicode String data /////////////////////////////// UTFstring bString = L"Stève Lhomm€"; EbmlUnicodeString testUStr(200); testUStr.SetID(SemanticList[4].Id, SemanticList[4].IdLength); testUStr = bString; testUStr.Render(Ebml_file); /////////////////////////////// // Float data /////////////////////////////// EbmlFloat testFloat(EbmlFloat::FLOAT_32); testFloat.SetID(SemanticList[5].Id, SemanticList[5].IdLength); testFloat.SetPrecision(EbmlFloat::FLOAT_32); testFloat = 1.01234567890123456; testFloat.Render(Ebml_file); testFloat.SetPrecision(EbmlFloat::FLOAT_64); testFloat = -1.01234567890123456L; testFloat.Render(Ebml_file); Ebml_file.close(); ///// Reading test StdIOCallback Ebml_Wfile(RW_FILENAME, ::MODE_READ); // example 1 skip all the elements found EbmlStream aStream(Ebml_Wfile); EbmlElement * ElementLevel0; // read the data until a possible element is found (valid ID + size combination) ElementLevel0 = aStream.FindNextID(0xFFFFFFFFL, false); printf("Read EBML elements & skip data\n"); while (ElementLevel0 != NULL) { printf("ID : "); for (int i=0; i<ElementLevel0->GetIDLength(); i++) { printf("[%02X]", ElementLevel0->GetID()[i]); } printf("\n"); ElementLevel0->SkipData(Ebml_Wfile); if (ElementLevel0 != NULL) delete ElementLevel0; ElementLevel0 = aStream.FindNextID(0xFFFFFFFFL, false); } // example 2 evaluation of all elements found EbmlStream bStream(Ebml_Wfile); EbmlElement * EvaledElementLevel0; // EbmlElement * EvaledElementLevel1; // reset the stream to the beggining Ebml_Wfile.setFilePointer(0); // list of all IDs and their semantic type // std::list<struct Semantic> SemanticList; // SemanticList.push_back(); ElementLevel0 = aStream.FindNextID(0xFFFFFFFFL, false); printf("Read EBML elements & evaluate data\n"); while (ElementLevel0 != NULL) { int i; printf("ID : "); for (i=0; i<ElementLevel0->GetIDLength(); i++) { printf("[%02X]", ElementLevel0->GetID()[i]); } // check if the element is known for (i=0; i<countof(SemanticList); i++) { if (ElementLevel0->GetIDLength() != SemanticList[i].IdLength) continue; if (memcmp(SemanticList[i].Id, ElementLevel0->GetID(), SemanticList[i].IdLength) == 0) break; } /// \todo check if it is known in the context // handle the data inside the element if (i < countof(SemanticList)) { switch (SemanticList[i].Type) { case EBML_U_INTEGER: EvaledElementLevel0 = new EbmlUInteger(*ElementLevel0); break; case EBML_S_INTEGER: EvaledElementLevel0 = new EbmlSInteger(*ElementLevel0); break; case EBML_BINARY: EvaledElementLevel0 = new EbmlBinary(*ElementLevel0); break; case EBML_STRING: EvaledElementLevel0 = new EbmlString(*ElementLevel0); break; case EBML_STRING_UNICODE: EvaledElementLevel0 = new EbmlUnicodeString(*ElementLevel0); break; case EBML_FLOAT: EvaledElementLevel0 = new EbmlFloat(*ElementLevel0); break; case EBML_MASTER: EvaledElementLevel0 = new EbmlMaster(*ElementLevel0); break; } EvaledElementLevel0->ReadData(Ebml_Wfile); switch (SemanticList[i].Type) { case EBML_U_INTEGER: printf(" : %d", uint32(*(EbmlUInteger*)EvaledElementLevel0)); break; case EBML_S_INTEGER: printf(" : %d", int32(*(EbmlSInteger*)EvaledElementLevel0)); break; case EBML_BINARY: printf(" : binary data, size = %ld", (*(EbmlBinary*)EvaledElementLevel0).GetSize()); printf(" [%02X]", binary(*(EbmlBinary*)EvaledElementLevel0)); break; case EBML_STRING: printf(" : %s", std::string(*(EbmlString*)EvaledElementLevel0).data()); break; case EBML_STRING_UNICODE: printf(" : (wide chars) %ls", UTFstring(*(EbmlUnicodeString*)EvaledElementLevel0).data()); break; case EBML_FLOAT: printf(" : %f / %.15lf", float(*(EbmlFloat*)EvaledElementLevel0), double(*(EbmlFloat*)EvaledElementLevel0)); break; case EBML_MASTER: printf(" : unsupported format 'Master'"); break; } delete EvaledElementLevel0; } else { ElementLevel0->SkipData(Ebml_Wfile); } if (ElementLevel0 != NULL) delete ElementLevel0; printf("\n"); ElementLevel0 = aStream.FindNextID(0xFFFFFFFFL, false); } Ebml_Wfile.close(); return 0; }
void move_children(EbmlMaster &source, EbmlMaster &destination) { for (auto child : source) destination.PushElement(*child); }
/** \brief Merge all chapter atoms sharing the same UID If two or more chapters with the same UID are encountered on the same level then those are merged into a single chapter. The start timecode is the minimum start timecode of all the chapters, and the end timecode is the maximum end timecode of all the chapters. The parameters do not have to be checked for validity. \param master The master containing the elements to check. */ void merge_chapter_entries(EbmlMaster &master) { size_t master_idx; // Iterate over all children of the atomaster. for (master_idx = 0; master.ListSize() > master_idx; ++master_idx) { // Not every child is a chapter atomaster. Skip those. KaxChapterAtom *atom = dynamic_cast<KaxChapterAtom *>(master[master_idx]); if (!atom) continue; int64_t uid = get_chapter_uid(*atom); if (-1 == uid) continue; // First get the start and end time, if present. int64_t start_tc = get_chapter_start(*atom, 0); int64_t end_tc = get_chapter_end(*atom); mxverb(3, boost::format("chapters: merge_entries: looking for %1% with %2%, %3%\n") % uid % start_tc % end_tc); // Now iterate over all remaining atoms and find those with the same // UID. size_t merge_idx = master_idx + 1; while (true) { KaxChapterAtom *merge_this = nullptr; for (; master.ListSize() > merge_idx; ++merge_idx) { KaxChapterAtom *cmp_atom = dynamic_cast<KaxChapterAtom *>(master[merge_idx]); if (!cmp_atom) continue; if (get_chapter_uid(*cmp_atom) == uid) { merge_this = cmp_atom; break; } } // If we haven't found an atom with the same UID then we're done here. if (!merge_this) break; // Do the merger! First get the start and end timecodes if present. int64_t merge_start_tc = get_chapter_start(*merge_this, 0); int64_t merge_end_tc = get_chapter_end(*merge_this); // Then compare them to the ones we have for the soon-to-be merged // chapter and assign accordingly. if (merge_start_tc < start_tc) start_tc = merge_start_tc; if ((-1 == end_tc) || (merge_end_tc > end_tc)) end_tc = merge_end_tc; mxverb(3, boost::format("chapters: merge_entries: found one at %1% with %2%, %3%; merged to %4%, %5%\n") % merge_idx % merge_start_tc % merge_end_tc % start_tc % end_tc); // Finally remove the entry itself. delete master[merge_idx]; master.Remove(merge_idx); } // Assign the start and end timecode to the chapter. Only assign an // end timecode if one was present in at least one of the merged // chapter atoms. GetChildAs<KaxChapterTimeStart, EbmlUInteger>(*atom) = start_tc; if (-1 != end_tc) GetChildAs<KaxChapterTimeEnd, EbmlUInteger>(*atom) = end_tc; } // Recusively merge atoms. for (master_idx = 0; master.ListSize() > master_idx; ++master_idx) { EbmlMaster *merge_master = dynamic_cast<EbmlMaster *>(master[master_idx]); if (merge_master) merge_chapter_entries(*merge_master); } }
/** \brief Remove all chapter atoms that are outside of a time range All chapter atoms that lie completely outside the timecode range given with <tt>[min_tc..max_tc]</tt> are deleted. This is the workhorse for ::select_chapters_in_timeframe Chapters which start before the window but end inside or after the window are kept as well, and their start timecode is adjusted. Its parameters don't have to be checked for validity. \param min_tc The minimum timecode to accept. \param max_tc The maximum timecode to accept. \param offset This value is subtracted from both the start and end timecode for each chapter after the decision whether or not to keep it has been made. \param m The master containing the elements to check. */ static void remove_entries(int64_t min_tc, int64_t max_tc, int64_t offset, EbmlMaster &m) { if (0 == m.ListSize()) return; struct chapter_entry_t { bool remove, spans, is_atom; int64_t start, end; chapter_entry_t() : remove(false) , spans(false) , is_atom(false) , start(0) , end(-1) { } } *entries = new chapter_entry_t[m.ListSize()]; unsigned int last_atom_at = 0; bool last_atom_found = false; // Determine whether or not an entry has to be removed. Also retrieve // the start and end timecodes. size_t i; for (i = 0; m.ListSize() > i; ++i) { KaxChapterAtom *atom = dynamic_cast<KaxChapterAtom *>(m[i]); if (!atom) continue; last_atom_at = i; last_atom_found = true; entries[i].is_atom = true; KaxChapterTimeStart *cts = static_cast<KaxChapterTimeStart *>(atom->FindFirstElt(EBML_INFO(KaxChapterTimeStart), false)); if (cts) entries[i].start = uint64(*cts); KaxChapterTimeEnd *cte = static_cast<KaxChapterTimeEnd *>(atom->FindFirstElt(EBML_INFO(KaxChapterTimeEnd), false)); if (cte) entries[i].end = uint64(*cte); } // We can return if we don't have a single atom to work with. if (!last_atom_found) return; for (i = 0; m.ListSize() > i; ++i) { KaxChapterAtom *atom = dynamic_cast<KaxChapterAtom *>(m[i]); if (!atom) continue; // Calculate the end timestamps and determine whether or not an entry spans // several segments. if (-1 == entries[i].end) { if (i == last_atom_at) entries[i].end = 1LL << 62; else { int next_atom = i + 1; while (!entries[next_atom].is_atom) ++next_atom; entries[i].end = entries[next_atom].start; } } if ( (entries[i].start < min_tc) || ((max_tc >= 0) && (entries[i].start > max_tc))) entries[i].remove = true; if (entries[i].remove && (entries[i].start < min_tc) && (entries[i].end > min_tc)) entries[i].spans = true; mxverb(3, boost::format("remove_chapters: entries[%1%]: remove %2% spans %3% start %4% end %5%\n") % i % entries[i].remove % entries[i].spans % entries[i].start % entries[i].end); // Spanning entries must be kept, and their start timecode must be // adjusted. Entries that are to be deleted will be deleted later and // have to be skipped for now. if (entries[i].remove && !entries[i].spans) continue; KaxChapterTimeStart *cts = static_cast<KaxChapterTimeStart *>(atom->FindFirstElt(EBML_INFO(KaxChapterTimeStart), false)); KaxChapterTimeEnd *cte = static_cast<KaxChapterTimeEnd *>(atom->FindFirstElt(EBML_INFO(KaxChapterTimeEnd), false)); if (entries[i].spans) *static_cast<EbmlUInteger *>(cts) = min_tc; *static_cast<EbmlUInteger *>(cts) = uint64(*cts) - offset; if (cte) { int64_t end_tc = uint64(*cte); if ((max_tc >= 0) && (end_tc > max_tc)) end_tc = max_tc; end_tc -= offset; *static_cast<EbmlUInteger *>(cte) = end_tc; } EbmlMaster *m2 = dynamic_cast<EbmlMaster *>(m[i]); if (m2) remove_entries(min_tc, max_tc, offset, *m2); } // Now really delete those entries. i = m.ListSize(); while (0 < i) { --i; if (entries[i].remove && !entries[i].spans) { delete m[i]; m.Remove(i); } } delete []entries; }