void
ebml_chapters_converter_c::fix_atom(KaxChapterAtom &atom)
  const {
  for (auto element : atom)
    if (dynamic_cast<KaxChapterAtom *>(element))
      fix_atom(*static_cast<KaxChapterAtom *>(element));

  if (!FindChild<KaxChapterTimeStart>(atom))
    throw conversion_x{Y("<ChapterAtom> is missing the <ChapterTimeStart> child.")};

  if (!FindChild<KaxChapterUID>(atom)) {
    KaxChapterUID *cuid                = new KaxChapterUID;
    *static_cast<EbmlUInteger *>(cuid) = create_unique_number(UNIQUE_CHAPTER_IDS);
    atom.PushElement(*cuid);
  }

  KaxChapterTrack *ctrack = FindChild<KaxChapterTrack>(atom);
  if (ctrack && !FindChild<KaxChapterTrackNumber>(ctrack))
    throw conversion_x{Y("<ChapterTrack> is missing the <ChapterTrackNumber> child.")};

  KaxChapterDisplay *cdisplay = FindChild<KaxChapterDisplay>(atom);
  if (cdisplay)
    fix_display(*cdisplay);
}
Beispiel #2
0
/** \brief Merge all chapter atoms sharing the same UID

   If two or more chapters with the same UID are encountered on the same
   level then those are merged into a single chapter. The start timecode
   is the minimum start timecode of all the chapters, and the end timecode
   is the maximum end timecode of all the chapters.

   The parameters do not have to be checked for validity.

   \param master The master containing the elements to check.
*/
void
merge_chapter_entries(EbmlMaster &master) {
  size_t master_idx;

  // Iterate over all children of the atomaster.
  for (master_idx = 0; master.ListSize() > master_idx; ++master_idx) {
    // Not every child is a chapter atomaster. Skip those.
    KaxChapterAtom *atom = dynamic_cast<KaxChapterAtom *>(master[master_idx]);
    if (!atom)
      continue;

    int64_t uid = get_chapter_uid(*atom);
    if (-1 == uid)
      continue;

    // First get the start and end time, if present.
    int64_t start_tc = get_chapter_start(*atom, 0);
    int64_t end_tc   = get_chapter_end(*atom);

    mxverb(3, boost::format("chapters: merge_entries: looking for %1% with %2%, %3%\n") % uid % start_tc % end_tc);

    // Now iterate over all remaining atoms and find those with the same
    // UID.
    size_t merge_idx = master_idx + 1;
    while (true) {
      KaxChapterAtom *merge_this = nullptr;
      for (; master.ListSize() > merge_idx; ++merge_idx) {
        KaxChapterAtom *cmp_atom = dynamic_cast<KaxChapterAtom *>(master[merge_idx]);
        if (!cmp_atom)
          continue;

        if (get_chapter_uid(*cmp_atom) == uid) {
          merge_this = cmp_atom;
          break;
        }
      }

      // If we haven't found an atom with the same UID then we're done here.
      if (!merge_this)
        break;

      // Do the merger! First get the start and end timecodes if present.
      int64_t merge_start_tc = get_chapter_start(*merge_this, 0);
      int64_t merge_end_tc   = get_chapter_end(*merge_this);

      // Then compare them to the ones we have for the soon-to-be merged
      // chapter and assign accordingly.
      if (merge_start_tc < start_tc)
        start_tc = merge_start_tc;

      if ((-1 == end_tc) || (merge_end_tc > end_tc))
        end_tc = merge_end_tc;

      // Move all chapter atoms from the merged entry into the target
      // entry so that they will be merged recursively as well.
      auto merge_child_idx = 0u;
      auto num_children    = merge_this->ListSize();

      while (merge_child_idx < num_children) {
        if (Is<KaxChapterAtom>((*merge_this)[merge_child_idx])) {
          atom->PushElement(*(*merge_this)[merge_child_idx]);
          merge_this->Remove(merge_child_idx);
          --num_children;

        } else
          ++merge_child_idx;
      }

      mxverb(3, boost::format("chapters: merge_entries:   found one at %1% with %2%, %3%; merged to %4%, %5%\n") % merge_idx % merge_start_tc % merge_end_tc % start_tc % end_tc);

      // Finally remove the entry itself.
      delete master[merge_idx];
      master.Remove(merge_idx);
    }

    // Assign the start and end timecode to the chapter. Only assign an
    // end timecode if one was present in at least one of the merged
    // chapter atoms.
    GetChild<KaxChapterTimeStart>(*atom).SetValue(start_tc);
    if (-1 != end_tc)
      GetChild<KaxChapterTimeEnd>(*atom).SetValue(end_tc);
  }

  // Recusively merge atoms.
  for (master_idx = 0; master.ListSize() > master_idx; ++master_idx) {
    EbmlMaster *merge_master = dynamic_cast<EbmlMaster *>(master[master_idx]);
    if (merge_master)
      merge_chapter_entries(*merge_master);
  }
}