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(); } }
bool extract_tracks(const std::string &file_name, std::vector<track_spec_t> &tspecs, kax_analyzer_c::parse_mode_e parse_mode) { if (tspecs.empty()) mxerror(Y("Nothing to do.\n")); // open input file mm_io_cptr in; kax_file_cptr file; try { in = mm_file_io_c::open(file_name); file = kax_file_cptr(new kax_file_c(in)); } 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 false; } int64_t file_size = in->get_size(); uint64_t tc_scale = TIMECODE_SCALE; bool segment_info_found = false, tracks_found = false; // open input file auto analyzer = open_and_analyze(file_name, parse_mode, false); if (analyzer) { auto af_master = ebml_master_cptr{ analyzer->read_all(EBML_INFO(KaxInfo)) }; auto segment_info = dynamic_cast<KaxInfo *>(af_master.get()); if (segment_info) { segment_info_found = true; handle_segment_info(segment_info, file.get(), tc_scale); } af_master = ebml_master_cptr{ analyzer->read_all(EBML_INFO(KaxTracks)) }; auto tracks = dynamic_cast<KaxTracks *>(af_master.get()); if (tracks) { tracks_found = true; find_and_verify_track_uids(*tracks, tspecs); create_extractors(*tracks, tspecs); } } try { in->setFilePointer(0); 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 false; } // 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 false; } if (Is<KaxSegment>(l0)) { show_element(l0, 0, Y("Segment")); break; } l0->SkipData(*es, EBML_CONTEXT(l0)); delete l0; } EbmlElement *l1 = nullptr; KaxChapters all_chapters; KaxTags all_tags; while ((l1 = file->read_next_level1_element())) { if (Is<KaxInfo>(l1) && !segment_info_found) { segment_info_found = true; handle_segment_info(static_cast<EbmlMaster *>(l1), file.get(), tc_scale); } else if (Is<KaxTracks>(l1) && !tracks_found) { tracks_found = true; find_and_verify_track_uids(*dynamic_cast<KaxTracks *>(l1), tspecs); create_extractors(*dynamic_cast<KaxTracks *>(l1), tspecs); } else if (Is<KaxCluster>(l1)) { show_element(l1, 1, Y("Cluster")); KaxCluster *cluster = static_cast<KaxCluster *>(l1); if (0 == verbose) mxinfo(boost::format(Y("Progress: %1%%%%2%")) % (int)(in->getFilePointer() * 100 / file_size) % "\r"); KaxClusterTimecode *ctc = FindChild<KaxClusterTimecode>(l1); if (ctc) { uint64_t cluster_tc = ctc->GetValue(); show_element(ctc, 2, boost::format(Y("Cluster timecode: %|1$.3f|s")) % ((float)cluster_tc * (float)tc_scale / 1000000000.0)); cluster->InitTimecode(cluster_tc, tc_scale); } else cluster->InitTimecode(0, tc_scale); size_t i; int64_t max_timecode = -1; for (i = 0; cluster->ListSize() > i; ++i) { int64_t max_bg_timecode = -1; EbmlElement *el = (*cluster)[i]; if (Is<KaxBlockGroup>(el)) { show_element(el, 2, Y("Block group")); max_bg_timecode = handle_blockgroup(*static_cast<KaxBlockGroup *>(el), *cluster, tc_scale); } else if (Is<KaxSimpleBlock>(el)) { show_element(el, 2, Y("SimpleBlock")); max_bg_timecode = handle_simpleblock(*static_cast<KaxSimpleBlock *>(el), *cluster); } max_timecode = std::max(max_timecode, max_bg_timecode); } if (-1 != max_timecode) file->set_last_timecode(max_timecode); } else if (Is<KaxChapters>(l1)) { KaxChapters &chapters = *static_cast<KaxChapters *>(l1); while (chapters.ListSize() > 0) { if (Is<KaxEditionEntry>(chapters[0])) { KaxEditionEntry &entry = *static_cast<KaxEditionEntry *>(chapters[0]); while (entry.ListSize() > 0) { if (Is<KaxChapterAtom>(entry[0])) all_chapters.PushElement(*entry[0]); entry.Remove(0); } } chapters.Remove(0); } } else if (Is<KaxTags>(l1)) { KaxTags &tags = *static_cast<KaxTags *>(l1); while (tags.ListSize() > 0) { all_tags.PushElement(*tags[0]); tags.Remove(0); } } delete l1; } // while (l1) delete l0; delete es; write_all_cuesheets(all_chapters, all_tags, tspecs); // Now just close the files and go to sleep. Mummy will sing you a // lullaby. Just close your eyes, listen to her sweet voice, singing, // singing, fading... fad... ing... close_extractors(); return true; } catch (...) { show_error(Y("Caught exception")); return false; } }
/*! \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(); } }
bool extract_tracks(const std::string &file_name, std::vector<track_spec_t> &tspecs) { if (tspecs.empty()) mxerror(Y("Nothing to do.\n")); // open input file mm_io_cptr in; kax_file_cptr file; try { in = mm_file_io_c::open(file_name); file = kax_file_cptr(new kax_file_c(in)); } catch (...) { show_error(boost::format(Y("The file '%1%' could not be opened for reading (%2%).\n")) % file_name % strerror(errno)); return false; } int64_t file_size = in->get_size(); try { EbmlStream *es = new EbmlStream(*in); // Find the EbmlHead element. Must be the first one. EbmlElement *l0 = es->FindNextID(EBML_INFO(EbmlHead), 0xFFFFFFFFL); if (NULL == l0) { show_error(Y("Error: No EBML head found.")); delete es; return false; } // 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 (NULL == l0) { show_error(Y("No segment/level 0 element found.")); return false; } 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; EbmlElement *l1 = NULL; uint64_t tc_scale = TIMECODE_SCALE; KaxChapters all_chapters; KaxTags all_tags; while (NULL != (l1 = file->read_next_level1_element())) { if (EbmlId(*l1) == EBML_ID(KaxInfo)) { // General info about this Matroska file show_element(l1, 1, Y("Segment information")); KaxTimecodeScale *ktc_scale = FINDFIRST(l1, KaxTimecodeScale); if (NULL != ktc_scale) { tc_scale = uint64(*ktc_scale); show_element(ktc_scale, 2, boost::format(Y("Timecode scale: %1%")) % tc_scale); } } 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; find_and_verify_track_uids(*dynamic_cast<KaxTracks *>(l1), tspecs); create_extractors(*dynamic_cast<KaxTracks *>(l1), tspecs); } else if (EbmlId(*l1) == EBML_ID(KaxCluster)) { show_element(l1, 1, Y("Cluster")); KaxCluster *cluster = static_cast<KaxCluster *>(l1); if (0 == verbose) mxinfo(boost::format(Y("Progress: %1%%%%2%")) % (int)(in->getFilePointer() * 100 / file_size) % "\r"); KaxClusterTimecode *ctc = FINDFIRST(l1, KaxClusterTimecode); if (NULL != ctc) { uint64_t cluster_tc = uint64(*ctc); show_element(ctc, 2, boost::format(Y("Cluster timecode: %|1$.3f|s")) % ((float)cluster_tc * (float)tc_scale / 1000000000.0)); cluster->InitTimecode(cluster_tc, tc_scale); } else cluster->InitTimecode(0, tc_scale); size_t i; for (i = 0; cluster->ListSize() > i; ++i) { EbmlElement *el = (*cluster)[i]; if (EbmlId(*el) == EBML_ID(KaxBlockGroup)) { show_element(el, 2, Y("Block group")); handle_blockgroup(*static_cast<KaxBlockGroup *>(el), *cluster, tc_scale); } else if (EbmlId(*el) == EBML_ID(KaxSimpleBlock)) { show_element(el, 2, Y("SimpleBlock")); handle_simpleblock(*static_cast<KaxSimpleBlock *>(el), *cluster); } } } else if (EbmlId(*l1) == EBML_ID(KaxChapters)) { KaxChapters &chapters = *static_cast<KaxChapters *>(l1); while (chapters.ListSize() > 0) { if (EbmlId(*chapters[0]) == EBML_ID(KaxEditionEntry)) { KaxEditionEntry &entry = *static_cast<KaxEditionEntry *>(chapters[0]); while (entry.ListSize() > 0) { if (EbmlId(*entry[0]) == EBML_ID(KaxChapterAtom)) all_chapters.PushElement(*entry[0]); entry.Remove(0); } } chapters.Remove(0); } } else if (EbmlId(*l1) == EBML_ID(KaxTags)) { KaxTags &tags = *static_cast<KaxTags *>(l1); while (tags.ListSize() > 0) { all_tags.PushElement(*tags[0]); tags.Remove(0); } } delete l1; } // while (l1 != NULL) delete l0; delete es; write_all_cuesheets(all_chapters, all_tags, tspecs); // Now just close the files and go to sleep. Mummy will sing you a // lullaby. Just close your eyes, listen to her sweet voice, singing, // singing, fading... fad... ing... close_extractors(); return true; } catch (...) { show_error(Y("Caught exception")); return false; } }