Ejemplo n.º 1
0
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();
    }
}
Ejemplo n.º 2
0
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;
  }
}
Ejemplo n.º 3
0
/*!
	\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();
	}
}
Ejemplo n.º 4
0
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;
  }
}