void extract_timecodes(const std::string &file_name, std::vector<track_spec_t> &tspecs, int version) { if (tspecs.empty()) mxerror(Y("Nothing to do.\n")); // open input file mm_io_c *in; try { in = new mm_file_io_c(file_name, MODE_READ); } catch (mtx::mm_io::exception &ex) { show_error(boost::format(Y("The file '%1%' could not be opened for reading: %2%.\n")) % file_name % ex); return; } try { int64_t file_size = in->get_size(); EbmlStream *es = new EbmlStream(*in); // Find the EbmlHead element. Must be the first one. EbmlElement *l0 = es->FindNextID(EBML_INFO(EbmlHead), 0xFFFFFFFFL); if (!l0) { show_error(Y("Error: No EBML head found.")); delete es; return; } // Don't verify its data for now. l0->SkipData(*es, EBML_CONTEXT(l0)); delete l0; while (1) { // Next element must be a segment l0 = es->FindNextID(EBML_INFO(KaxSegment), 0xFFFFFFFFFFFFFFFFLL); if (!l0) { show_error(Y("No segment/level 0 element found.")); return; } if (EbmlId(*l0) == EBML_ID(KaxSegment)) { show_element(l0, 0, Y("Segment")); break; } l0->SkipData(*es, EBML_CONTEXT(l0)); delete l0; } bool tracks_found = false; int upper_lvl_el = 0; uint64_t tc_scale = TIMECODE_SCALE; // We've got our segment, so let's find the tracks EbmlElement *l1 = es->FindNextElement(EBML_CONTEXT(l0), upper_lvl_el, 0xFFFFFFFFL, true, 1); EbmlElement *l2 = nullptr; EbmlElement *l3 = nullptr; while (l1 && (0 >= upper_lvl_el)) { if (EbmlId(*l1) == EBML_ID(KaxInfo)) { // General info about this Matroska file show_element(l1, 1, Y("Segment information")); upper_lvl_el = 0; l2 = es->FindNextElement(EBML_CONTEXT(l1), upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l2 && (0 >= upper_lvl_el)) { if (EbmlId(*l2) == EBML_ID(KaxTimecodeScale)) { KaxTimecodeScale &ktc_scale = *static_cast<KaxTimecodeScale *>(l2); ktc_scale.ReadData(es->I_O()); tc_scale = ktc_scale.GetValue(); show_element(l2, 2, boost::format(Y("Timecode scale: %1%")) % tc_scale); } else l2->SkipData(*es, EBML_CONTEXT(l2)); if (!in_parent(l1)) { delete l2; break; } if (0 > upper_lvl_el) { upper_lvl_el++; if (0 > upper_lvl_el) break; } l2->SkipData(*es, EBML_CONTEXT(l2)); delete l2; l2 = es->FindNextElement(EBML_CONTEXT(l1), upper_lvl_el, 0xFFFFFFFFL, true); } } else if ((EbmlId(*l1) == EBML_ID(KaxTracks)) && !tracks_found) { // Yep, we've found our KaxTracks element. Now find all tracks // contained in this segment. show_element(l1, 1, Y("Segment tracks")); tracks_found = true; l1->Read(*es, EBML_CLASS_CONTEXT(KaxTracks), upper_lvl_el, l2, true); find_and_verify_track_uids(*dynamic_cast<KaxTracks *>(l1), tspecs); create_timecode_files(*dynamic_cast<KaxTracks *>(l1), tspecs, version); } else if (EbmlId(*l1) == EBML_ID(KaxCluster)) { show_element(l1, 1, Y("Cluster")); KaxCluster *cluster = (KaxCluster *)l1; uint64_t cluster_tc = 0; if (0 == verbose) mxinfo(boost::format(Y("Progress: %1%%%%2%")) % (int)(in->getFilePointer() * 100 / file_size) % "\r"); upper_lvl_el = 0; l2 = es->FindNextElement(EBML_CONTEXT(l1), upper_lvl_el, 0xFFFFFFFFL, true, 1); while (l2 && (0 >= upper_lvl_el)) { if (EbmlId(*l2) == EBML_ID(KaxClusterTimecode)) { KaxClusterTimecode &ctc = *static_cast<KaxClusterTimecode *>(l2); ctc.ReadData(es->I_O()); cluster_tc = ctc.GetValue(); show_element(l2, 2, boost::format(Y("Cluster timecode: %|1$.3f|s")) % ((float)cluster_tc * (float)tc_scale / 1000000000.0)); cluster->InitTimecode(cluster_tc, tc_scale); } else if (EbmlId(*l2) == EBML_ID(KaxBlockGroup)) { show_element(l2, 2, Y("Block group")); l2->Read(*es, EBML_CLASS_CONTEXT(KaxBlockGroup), upper_lvl_el, l3, true); handle_blockgroup(*static_cast<KaxBlockGroup *>(l2), *cluster, tc_scale); } else if (EbmlId(*l2) == EBML_ID(KaxSimpleBlock)) { show_element(l2, 2, Y("Simple block")); l2->Read(*es, EBML_CLASS_CONTEXT(KaxSimpleBlock), upper_lvl_el, l3, true); handle_simpleblock(*static_cast<KaxSimpleBlock *>(l2), *cluster); } else l2->SkipData(*es, EBML_CONTEXT(l2)); if (!in_parent(l1)) { delete l2; break; } if (0 < upper_lvl_el) { upper_lvl_el--; if (0 < upper_lvl_el) break; delete l2; l2 = l3; continue; } else if (0 > upper_lvl_el) { upper_lvl_el++; if (0 > upper_lvl_el) break; } l2->SkipData(*es, EBML_CONTEXT(l2)); delete l2; l2 = es->FindNextElement(EBML_CONTEXT(l1), upper_lvl_el, 0xFFFFFFFFL, true); } // while (l2) } else l1->SkipData(*es, EBML_CONTEXT(l1)); if (!in_parent(l0)) { delete l1; break; } if (0 < upper_lvl_el) { upper_lvl_el--; if (0 < upper_lvl_el) break; delete l1; l1 = l2; continue; } else if (0 > upper_lvl_el) { upper_lvl_el++; if (0 > upper_lvl_el) break; } l1->SkipData(*es, EBML_CONTEXT(l1)); delete l1; l1 = es->FindNextElement(EBML_CONTEXT(l0), upper_lvl_el, 0xFFFFFFFFL, true); } // while (l1) delete l0; delete es; delete in; close_timecode_files(); if (0 == verbose) mxinfo(Y("Progress: 100%\n")); } catch (...) { show_error(Y("Caught exception")); delete in; close_timecode_files(); } }
/*! \brief Method to help reading a Master element and all subsequent children quickly \todo add an option to discard even unknown elements \todo handle when a mandatory element is not found */ void EbmlMaster::Read(EbmlStream & inDataStream, const EbmlSemanticContext & sContext, int & UpperEltFound, EbmlElement * & FoundElt, bool AllowDummyElt, ScopeMode ReadFully) { if (ReadFully != SCOPE_NO_DATA) { EbmlElement * ElementLevelA; // remove all existing elements, including the mandatory ones... size_t Index; for (Index=0; Index<ElementList.size(); Index++) { if (!(*ElementList[Index]).IsLocked()) { delete ElementList[Index]; } } ElementList.clear(); uint64 MaxSizeToRead; if (IsFiniteSize()) MaxSizeToRead = GetSize(); else MaxSizeToRead = 0x7FFFFFFF; // read blocks and discard the ones we don't care about if (MaxSizeToRead > 0) { inDataStream.I_O().setFilePointer(GetSizePosition() + GetSizeLength(), seek_beginning); ElementLevelA = inDataStream.FindNextElement(sContext, UpperEltFound, MaxSizeToRead, AllowDummyElt); while (ElementLevelA != NULL && UpperEltFound <= 0 && MaxSizeToRead > 0) { if (IsFiniteSize()) MaxSizeToRead = GetEndPosition() - ElementLevelA->GetEndPosition(); // even if it's the default value if (!AllowDummyElt && ElementLevelA->IsDummy()) { ElementLevelA->SkipData(inDataStream, sContext); delete ElementLevelA; // forget this unknown element } else { // more logical to do it afterward ElementList.push_back(ElementLevelA); ElementLevelA->Read(inDataStream, EBML_CONTEXT(ElementLevelA), UpperEltFound, FoundElt, AllowDummyElt, ReadFully); // just in case ElementLevelA->SkipData(inDataStream, EBML_CONTEXT(ElementLevelA)); } if (UpperEltFound > 0) { UpperEltFound--; if (UpperEltFound > 0 || MaxSizeToRead <= 0) goto processCrc; ElementLevelA = FoundElt; continue; } if (UpperEltFound < 0) { UpperEltFound++; if (UpperEltFound < 0) goto processCrc; } if (MaxSizeToRead <= 0) goto processCrc;// this level is finished ElementLevelA = inDataStream.FindNextElement(sContext, UpperEltFound, MaxSizeToRead, AllowDummyElt); } if (UpperEltFound > 0) { FoundElt = ElementLevelA; } } processCrc: EBML_MASTER_ITERATOR Itr, CrcItr; for (Itr = ElementList.begin(); Itr != ElementList.end();) { if ((EbmlId)(*(*Itr)) == EBML_ID(EbmlCrc32)) { bChecksumUsed = true; // remove the element Checksum = *(static_cast<EbmlCrc32*>(*Itr)); CrcItr = Itr; break; } ++Itr; } if (bChecksumUsed) { delete *CrcItr; Remove(CrcItr); } SetValueIsSet(); } }