bool extract_cuesheet(kax_analyzer_c &analyzer, options_c::mode_options_c &options) { KaxChapters all_chapters; auto chapters_m = analyzer.read_all(EBML_INFO(KaxChapters)); auto tags_m = analyzer.read_all(EBML_INFO(KaxTags)); KaxChapters *chapters = dynamic_cast<KaxChapters *>(chapters_m.get()); KaxTags *all_tags = dynamic_cast<KaxTags *>( tags_m.get()); if (!chapters || !all_tags) return true; for (auto chapter_entry : *chapters) { if (!dynamic_cast<KaxEditionEntry *>(chapter_entry)) continue; auto eentry = static_cast<KaxEditionEntry *>(chapter_entry); for (auto edition_entry : *eentry) if (dynamic_cast<KaxChapterAtom *>(edition_entry)) all_chapters.PushElement(*edition_entry); } write_cuesheet(analyzer.get_file().get_file_name(), all_chapters, *all_tags, -1, *open_output_file(options.m_output_file_name)); while (all_chapters.ListSize() > 0) all_chapters.Remove(0); return true; }
static uint64_t find_timecode_scale(kax_analyzer_c &analyzer) { auto info_m = analyzer.read_all(EBML_INFO(KaxInfo)); auto info = dynamic_cast<KaxInfo *>(info_m.get()); return info ? FindChildValue<KaxTimecodeScale>(info, 1000000ull) : 1000000ull; }
bool extract_tags(kax_analyzer_c &analyzer, options_c::mode_options_c &options) { auto tags = analyzer.read_all(EBML_INFO(KaxTags)); if (!dynamic_cast<KaxTags *>(tags.get())) return true; mtx::xml::ebml_tags_converter_c::write_xml(static_cast<KaxTags &>(*tags), *open_output_file(options.m_output_file_name)); return true; }
static std::unordered_map<int64_t, std::vector<cue_point_t> > parse_cue_points(kax_analyzer_c &analyzer) { auto cues_m = analyzer.read_all(EBML_INFO(KaxCues)); auto cues = dynamic_cast<KaxCues *>(cues_m.get()); if (!cues) mxerror(Y("No cues were found.\n")); auto cue_points = std::unordered_map<int64_t, std::vector<cue_point_t> >{}; for (auto const &elt : *cues) { auto kcue_point = dynamic_cast<KaxCuePoint *>(elt); if (!kcue_point) continue; auto ktime = FindChild<KaxCueTime>(*kcue_point); if (!ktime) continue; auto p = cue_point_t{ktime->GetValue()}; auto ktrack_pos = FindChild<KaxCueTrackPositions>(*kcue_point); if (!ktrack_pos) continue; for (auto const &pos_elt : *ktrack_pos) { if (Is<KaxCueClusterPosition>(pos_elt)) p.cluster_position.reset(static_cast<KaxCueClusterPosition *>(pos_elt)->GetValue()); else if (Is<KaxCueRelativePosition>(pos_elt)) p.relative_position.reset(static_cast<KaxCueRelativePosition *>(pos_elt)->GetValue()); else if (Is<KaxCueDuration>(pos_elt)) p.duration.reset(static_cast<KaxCueDuration *>(pos_elt)->GetValue()); } for (auto const &pos_elt : *ktrack_pos) if (Is<KaxCueTrack>(pos_elt)) cue_points[ static_cast<KaxCueTrack *>(pos_elt)->GetValue() ].push_back(p); } return cue_points; }
static std::map<int64_t, int64_t> generate_track_number_map(kax_analyzer_c &analyzer) { auto track_number_map = std::map<int64_t, int64_t>{}; auto tracks_m = analyzer.read_all(EBML_INFO(KaxTracks)); auto tracks = dynamic_cast<KaxTracks *>(tracks_m.get()); if (!tracks) return track_number_map; auto tid = 0; for (auto const &elt : *tracks) { auto ktrack_entry = dynamic_cast<KaxTrackEntry *>(elt); if (!ktrack_entry) continue; auto ktrack_number = FindChild<KaxTrackNumber>(ktrack_entry); if (ktrack_number) track_number_map[tid++] = ktrack_number->GetValue(); } return track_number_map; }
bool extract_tracks(kax_analyzer_c &analyzer, options_c::mode_options_c &options) { auto &tspecs = options.m_tracks; if (tspecs.empty()) return false; // open input file auto &in = analyzer.get_file(); auto file = std::make_shared<kax_file_c>(in); int64_t file_size = in.get_size(); // open input file auto af_segment_info = ebml_master_cptr{ analyzer.read_all(EBML_INFO(KaxInfo)) }; auto segment_info = dynamic_cast<KaxInfo *>(af_segment_info.get()); auto af_tracks = ebml_master_cptr{ analyzer.read_all(EBML_INFO(KaxTracks)) }; auto tracks = dynamic_cast<KaxTracks *>(af_tracks.get()); if (!segment_info || !tracks) return false; find_and_verify_track_uids(*tracks, tspecs); create_extractors(*tracks, tspecs); create_timestamp_files(*tracks, tspecs); try { in.setFilePointer(0); auto es = std::make_shared<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.")); 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)) break; l0->SkipData(*es, EBML_CONTEXT(l0)); delete l0; } auto previous_percentage = -1; auto tc_scale = FindChildValue<KaxTimecodeScale, uint64_t>(segment_info, 1000000); file->set_timestamp_scale(tc_scale); file->set_segment_end(*l0); while (true) { auto cluster = std::unique_ptr<KaxCluster>{file->read_next_cluster()}; if (!cluster) break; auto ctc = static_cast<KaxClusterTimecode *> (cluster->FindFirstElt(EBML_INFO(KaxClusterTimecode), false)); cluster->InitTimecode(ctc ? ctc->GetValue() : 0, tc_scale); if (0 == verbose) { auto current_percentage = in.getFilePointer() * 100 / file_size; if (previous_percentage != static_cast<int>(current_percentage)) { if (mtx::cli::g_gui_mode) mxinfo(boost::format("#GUI#progress %1%%%\n") % current_percentage); else mxinfo(boost::format(Y("Progress: %1%%%%2%")) % current_percentage % "\r"); previous_percentage = current_percentage; } } size_t i; int64_t max_timestamp = -1; for (i = 0; cluster->ListSize() > i; ++i) { int64_t max_bg_timestamp = -1; EbmlElement *el = (*cluster)[i]; if (Is<KaxBlockGroup>(el)) max_bg_timestamp = handle_blockgroup(*static_cast<KaxBlockGroup *>(el), *cluster, tc_scale); else if (Is<KaxSimpleBlock>(el)) max_bg_timestamp = handle_simpleblock(*static_cast<KaxSimpleBlock *>(el), *cluster); max_timestamp = std::max(max_timestamp, max_bg_timestamp); } if (-1 != max_timestamp) file->set_last_timestamp(max_timestamp); } delete l0; auto af_chapters = ebml_element_cptr{ analyzer.read_all(EBML_INFO(KaxChapters)) }; auto chapters = dynamic_cast<KaxChapters *>(af_chapters.get()); auto af_tags = ebml_element_cptr{ analyzer.read_all(EBML_INFO(KaxTags)) }; auto tags = dynamic_cast<KaxTags *>(af_tags.get()); if (chapters && tags) write_all_cuesheets(*chapters, *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(); close_timestamp_files(); if (0 == verbose) { if (mtx::cli::g_gui_mode) mxinfo(boost::format("#GUI#progress %1%%%\n") % 100); else mxinfo(boost::format(Y("Progress: %1%%%%2%")) % 100 % "\n"); } return true; } catch (...) { show_error(Y("Caught exception")); return false; } }