void MatroskaImport::ImportCluster(KaxCluster &cluster, bool addToTrack) { KaxSegment & segment = *static_cast<KaxSegment *>(el_l0); KaxClusterTimecode & clusterTime = GetChild<KaxClusterTimecode>(cluster); CXXAutoreleasePool pool; cluster.SetParent(segment); cluster.InitTimecode(uint64(clusterTime), timecodeScale); for (int i = 0; i < cluster.ListSize(); i++) { const EbmlId & elementID = EbmlId(*cluster[i]); KaxInternalBlock *block = NULL; uint32_t duration = 0; // set to track's default duration in AddBlock if 0 short flags = 0; if (elementID == KaxBlockGroup::ClassInfos.GlobalId) { KaxBlockGroup & blockGroup = *static_cast<KaxBlockGroup *>(cluster[i]); KaxBlockDuration & blkDuration = GetChild<KaxBlockDuration>(blockGroup); block = &GetChild<KaxBlock>(blockGroup); if (blkDuration.ValueIsSet()) duration = uint32(blkDuration); flags = blockGroup.ReferenceCount() > 0 ? mediaSampleNotSync : 0; } else if (elementID == KaxSimpleBlock::ClassInfos.GlobalId) { KaxSimpleBlock & simpleBlock = *static_cast<KaxSimpleBlock *>(cluster[i]); block = &simpleBlock; if (!simpleBlock.IsKeyframe()) flags |= mediaSampleNotSync; if (simpleBlock.IsDiscardable() && IsFrameDroppingEnabled()) flags |= mediaSampleDroppable; } if (block) { block->SetParent(cluster); for (int i = 0; i < tracks.size(); i++) { if (tracks[i].number == block->TrackNum()) { tracks[i].AddBlock(*block, duration, flags); break; } } } } if (addToTrack) { for (int i = 0; i < tracks.size(); i++) tracks[i].AddSamplesToTrack(); loadState = kMovieLoadStatePlayable; } }
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; } }
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; } }