Пример #1
0
/** \brief Adjust all start and end timecodes by an offset

   All start and end timecodes are adjusted by an offset. This is done
   recursively.

   Its parameters don't have to be checked for validity.

   \param master A master containint the elements to adjust. This can be
     a KaxChapters, KaxEditionEntry or KaxChapterAtom object.
   \param offset The offset to add to each timecode. Can be negative. If
     the resulting timecode would be smaller than zero then it will be set
     to zero.
*/
void
adjust_chapter_timecodes(EbmlMaster &master,
                         int64_t offset) {
  size_t master_idx;
  for (master_idx = 0; master.ListSize() > master_idx; master_idx++) {
    if (!is_id(master[master_idx], KaxChapterAtom))
      continue;

    KaxChapterAtom *atom       = static_cast<KaxChapterAtom *>(master[master_idx]);
    KaxChapterTimeStart *start = FindChild<KaxChapterTimeStart>(atom);
    KaxChapterTimeEnd *end     = FindChild<KaxChapterTimeEnd>(atom);

    if (start)
      *static_cast<EbmlUInteger *>(start) = std::max(int64_t(uint64(*start)) + offset, int64_t(0));

    if (end)
      *static_cast<EbmlUInteger *>(end) = std::max(int64_t(uint64(*end)) + offset, int64_t(0));
  }

  for (master_idx = 0; master.ListSize() > master_idx; master_idx++) {
    EbmlMaster *work_master = dynamic_cast<EbmlMaster *>(master[master_idx]);
    if (work_master)
      adjust_chapter_timecodes(*work_master, offset);
  }
}
Пример #2
0
/** \brief Adjust all start and end timecodes by an offset

   All start and end timecodes are adjusted by an offset. This is done
   recursively.

   Its parameters don't have to be checked for validity.

   \param master A master containint the elements to adjust. This can be
     a KaxChapters, KaxEditionEntry or KaxChapterAtom object.
   \param offset The offset to add to each timecode. Can be negative. If
     the resulting timecode would be smaller than zero then it will be set
     to zero.
*/
void
adjust_chapter_timecodes(EbmlMaster &master,
                         int64_t offset) {
  size_t master_idx;
  for (master_idx = 0; master.ListSize() > master_idx; master_idx++) {
    if (!Is<KaxChapterAtom>(master[master_idx]))
      continue;

    KaxChapterAtom *atom       = static_cast<KaxChapterAtom *>(master[master_idx]);
    KaxChapterTimeStart *start = FindChild<KaxChapterTimeStart>(atom);
    KaxChapterTimeEnd *end     = FindChild<KaxChapterTimeEnd>(atom);

    if (start)
      start->SetValue(std::max<int64_t>(static_cast<int64_t>(start->GetValue()) + offset, 0));

    if (end)
      end->SetValue(std::max<int64_t>(static_cast<int64_t>(end->GetValue()) + offset, 0));
  }

  for (master_idx = 0; master.ListSize() > master_idx; master_idx++) {
    EbmlMaster *work_master = dynamic_cast<EbmlMaster *>(master[master_idx]);
    if (work_master)
      adjust_chapter_timecodes(*work_master, offset);
  }
}
Пример #3
0
/** \brief Move all chapter atoms to another container keeping editions intact

   This function moves all chapter atoms from \a src to \a dst.
   If there's already an edition in \a dst with the same UID as the current
   one in \a src, then all atoms will be put into that edition. Otherwise
   the complete edition will simply be moved over.

   After processing \a src will be empty.

   Its parameters don't have to be checked for validity.

   \param dst The container the atoms and editions will be put into.
   \param src The container the atoms and editions will be taken from.
*/
void
move_chapters_by_edition(KaxChapters &dst,
                         KaxChapters &src) {
  size_t src_idx;
  for (src_idx = 0; src.ListSize() > src_idx; src_idx++) {
    EbmlMaster *m = dynamic_cast<EbmlMaster *>(src[src_idx]);
    if (!m)
      continue;

    // Find an edition to which these atoms will be added.
    KaxEditionEntry *ee_dst = nullptr;
    KaxEditionUID *euid_src = FindChild<KaxEditionUID>(m);
    if (euid_src)
      ee_dst = find_edition_with_uid(dst, uint64(*euid_src));

    // No edition with the same UID found as the one we want to handle?
    // Then simply move the complete edition over.
    if (!ee_dst)
      dst.PushElement(*m);
    else {
      // Move all atoms from the old edition to the new one.
      size_t master_idx;
      for (master_idx = 0; m->ListSize() > master_idx; master_idx++)
        if (is_id((*m)[master_idx], KaxChapterAtom))
          ee_dst->PushElement(*(*m)[master_idx]);
        else
          delete (*m)[master_idx];

      m->RemoveAll();
      delete m;
    }
  }

  src.RemoveAll();
}
Пример #4
0
/** \brief Add missing mandatory elements

   The Matroska specs and \c libmatroska say that several elements are
   mandatory. This function makes sure that they all exist by adding them
   with their default values if they're missing. It works recursively. See
   <a href="http://www.matroska.org/technical/specs/chapters/index.html">
   the Matroska chapter specs</a> for a list or mandatory elements.

   The parameters are checked for validity.

   \param e An element that really is an \c EbmlMaster. \a e's children
     should be checked.
*/
void
fix_mandatory_chapter_elements(EbmlElement *e) {
  if (!e)
    return;

  if (dynamic_cast<KaxEditionEntry *>(e)) {
    KaxEditionEntry &ee = *static_cast<KaxEditionEntry *>(e);
    GetChild<KaxEditionFlagDefault>(ee);
    GetChild<KaxEditionFlagHidden>(ee);

    if (!FindChild<KaxEditionUID>(&ee))
      GetChildAs<KaxEditionUID, EbmlUInteger>(ee) = create_unique_number(UNIQUE_EDITION_IDS);

  } else if (dynamic_cast<KaxChapterAtom *>(e)) {
    KaxChapterAtom &a = *static_cast<KaxChapterAtom *>(e);

    GetChild<KaxChapterFlagHidden>(a);
    GetChild<KaxChapterFlagEnabled>(a);

    if (!FindChild<KaxChapterUID>(&a))
      GetChildAs<KaxChapterUID, EbmlUInteger>(a) = create_unique_number(UNIQUE_CHAPTER_IDS);

    if (!FindChild<KaxChapterTimeStart>(&a))
      GetChildAs<KaxChapterTimeStart, EbmlUInteger>(a) = 0;

  } else if (dynamic_cast<KaxChapterTrack *>(e)) {
    KaxChapterTrack &t = *static_cast<KaxChapterTrack *>(e);

    if (!FindChild<KaxChapterTrackNumber>(&t))
      GetChildAs<KaxChapterTrackNumber, EbmlUInteger>(t) = 0;

  } else if (dynamic_cast<KaxChapterDisplay *>(e)) {
    KaxChapterDisplay &d = *static_cast<KaxChapterDisplay *>(e);

    if (!FindChild<KaxChapterString>(&d))
      GetChildAs<KaxChapterString, EbmlUnicodeString>(d) = L"";

    if (!FindChild<KaxChapterLanguage>(&d))
      GetChildAs<KaxChapterLanguage, EbmlString>(d) = "eng";

  } else if (dynamic_cast<KaxChapterProcess *>(e)) {
    KaxChapterProcess &p = *static_cast<KaxChapterProcess *>(e);

    GetChild<KaxChapterProcessCodecID>(p);

  } else if (dynamic_cast<KaxChapterProcessCommand *>(e)) {
    KaxChapterProcessCommand &c = *static_cast<KaxChapterProcessCommand *>(e);

    GetChild<KaxChapterProcessTime>(c);
    GetChild<KaxChapterProcessData>(c);

  }

  if (dynamic_cast<EbmlMaster *>(e)) {
    EbmlMaster *m = static_cast<EbmlMaster *>(e);
    size_t i;
    for (i = 0; i < m->ListSize(); i++)
      fix_mandatory_chapter_elements((*m)[i]);
  }
}
Пример #5
0
unsigned long
kax_file_c::get_element_size(EbmlElement *e) {
  EbmlMaster *m = dynamic_cast<EbmlMaster *>(e);

  if (!m || e->IsFiniteSize())
    return e->GetSizeLength() + EBML_ID_LENGTH(static_cast<const EbmlId &>(*e)) + e->GetSize();

  unsigned long max_end_pos = e->GetElementPosition() + EBML_ID_LENGTH(static_cast<const EbmlId &>(*e));
  unsigned int idx;
  for (idx = 0; m->ListSize() > idx; ++idx)
    max_end_pos = std::max(max_end_pos, static_cast<unsigned long>((*m)[idx]->GetElementPosition() + get_element_size((*m)[idx])));

  return max_end_pos - e->GetElementPosition();
}
Пример #6
0
void
kax_analyzer_c::read_meta_seek(uint64_t pos,
                               std::map<int64_t, bool> &positions_found) {
  if (m_meta_seeks_by_position[pos])
    return;

  m_meta_seeks_by_position[pos] = true;

  m_file->setFilePointer(pos, seek_beginning);

  int upper_lvl_el = 0;
  EbmlElement *l1  = m_stream->FindNextElement(EBML_CONTEXT(m_segment), upper_lvl_el, 0xFFFFFFFFL, true, 1);

  if (!l1)
    return;

  if (!Is<KaxSeekHead>(l1)) {
    delete l1;
    return;
  }

  EbmlElement *l2    = nullptr;
  EbmlMaster *master = static_cast<EbmlMaster *>(l1);
  master->Read(*m_stream, EBML_CONTEXT(l1), upper_lvl_el, l2, true);

  unsigned int i;
  for (i = 0; master->ListSize() > i; i++) {
    if (!Is<KaxSeek>((*master)[i]))
      continue;

    KaxSeek *seek      = static_cast<KaxSeek *>((*master)[i]);
    KaxSeekID *seek_id = FindChild<KaxSeekID>(seek);
    int64_t seek_pos   = seek->Location() + m_segment->GetElementPosition() + m_segment->HeadSize();

    if ((0 == pos) || !seek_id)
      continue;

    if (positions_found[seek_pos])
      continue;

    EbmlId the_id(seek_id->GetBuffer(), seek_id->GetSize());
    m_data.push_back(kax_analyzer_data_c::create(the_id, seek_pos, -1));
    positions_found[seek_pos] = true;

    if (Is<KaxSeekHead>(the_id))
      read_meta_seek(seek_pos, positions_found);
  }

  delete l1;
}
Пример #7
0
static int
count_chapter_atoms_recursively(EbmlMaster &master,
                                int count) {
  size_t master_idx;

  for (master_idx = 0; master.ListSize() > master_idx; ++master_idx)
    if (is_id(master[master_idx], KaxChapterAtom))
      ++count;

    else if (dynamic_cast<EbmlMaster *>(master[master_idx]))
      count = count_chapter_atoms_recursively(*static_cast<EbmlMaster *>(master[master_idx]), count);

  return count;
}
Пример #8
0
EbmlElement *
empty_ebml_master(EbmlElement *e) {
  EbmlMaster *m;

  m = dynamic_cast<EbmlMaster *>(e);
  if (!m)
    return e;

  while (m->ListSize() > 0) {
    delete (*m)[0];
    m->Remove(0);
  }

  return m;
}
Пример #9
0
void
dump_ebml_elements(EbmlElement *element,
                   bool with_values,
                   unsigned int level) {
  std::string indent_str, value_str;
  size_t i;

  for (i = 1; i <= level; ++i)
    indent_str += " ";

  if (with_values) {
    if (NULL != dynamic_cast<EbmlUInteger *>(element))
      value_str = to_string(uint64(*static_cast<EbmlUInteger *>(element)));

    else if (NULL != dynamic_cast<EbmlSInteger *>(element))
      value_str = to_string(int64(*static_cast<EbmlSInteger *>(element)));

    else if (NULL != dynamic_cast<EbmlFloat *>(element))
      value_str = to_string(double(*static_cast<EbmlFloat *>(element)), 9);

    else if (NULL != dynamic_cast<EbmlUnicodeString *>(element))
      value_str = UTFstring_to_cstrutf8(UTFstring(*static_cast<EbmlUnicodeString *>(element)));

    else if (NULL != dynamic_cast<EbmlString *>(element))
      value_str = std::string(*static_cast<EbmlString *>(element));

    else if (NULL != dynamic_cast<EbmlDate *>(element))
      value_str = to_string(static_cast<EbmlDate *>(element)->GetEpochDate());

    else
      value_str = (boost::format("(type: %1%)") %
                   (  NULL != dynamic_cast<EbmlBinary *>(element) ? "binary"
                    : NULL != dynamic_cast<EbmlMaster *>(element) ? "master"
                    : NULL != dynamic_cast<EbmlVoid *>(element)   ? "void"
                    :                                               "unknown")).str();

    value_str = " " + value_str;
  }

  mxinfo(boost::format("%1%%2%%3%\n") % indent_str % EBML_NAME(element) % value_str);

  EbmlMaster *master = dynamic_cast<EbmlMaster *>(element);
  if (NULL == master)
    return;

  for (i = 0; master->ListSize() > i; ++i)
    dump_ebml_elements((*master)[i], with_values, level + 1);
}
Пример #10
0
EbmlMaster::EbmlMaster(const EbmlMaster & ElementToClone)
 :EbmlElement(ElementToClone)
 ,ElementList(ElementToClone.ListSize())
 ,Context(ElementToClone.Context)
 ,bChecksumUsed(ElementToClone.bChecksumUsed)
 ,Checksum(ElementToClone.Checksum)
{
	// add a clone of the list
	std::vector<EbmlElement *>::const_iterator Itr = ElementToClone.ElementList.begin();
	std::vector<EbmlElement *>::iterator myItr = ElementList.begin();
	while (Itr != ElementToClone.ElementList.end())
	{
		*myItr = (*Itr)->Clone();
		Itr++; myItr++;
	}

}
Пример #11
0
ebml_master_cptr
kax_analyzer_c::read_all(const EbmlCallbacks &callbacks) {
  reopen_file();

  ebml_master_cptr master;
  EbmlStream es(*m_file);
  size_t i;

  for (i = 0; m_data.size() > i; ++i) {
    kax_analyzer_data_c &data = *m_data[i].get();
    if (EBML_INFO_ID(callbacks) != data.m_id)
      continue;

    m_file->setFilePointer(data.m_pos);
    int upper_lvl_el     = 0;
    EbmlElement *element = es.FindNextElement(EBML_CLASS_CONTEXT(KaxSegment), upper_lvl_el, 0xFFFFFFFFL, true);
    if (!element)
      continue;

    if (EbmlId(*element) != EBML_INFO_ID(callbacks)) {
      delete element;
      continue;
    }

    EbmlElement *l2 = nullptr;
    element->Read(*m_stream, EBML_INFO_CONTEXT(callbacks), upper_lvl_el, l2, true);

    if (!master)
      master = ebml_master_cptr(static_cast<EbmlMaster *>(element));
    else {
      EbmlMaster *src = static_cast<EbmlMaster *>(element);
      while (src->ListSize() > 0) {
        master->PushElement(*(*src)[0]);
        src->Remove(0);
      }
      delete element;
    }
  }

  if (master && (master->ListSize() == 0))
    master.reset();

  return master;
}
Пример #12
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;

      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.
    GetChildAs<KaxChapterTimeStart, EbmlUInteger>(*atom) = start_tc;
    if (-1 != end_tc)
      GetChildAs<KaxChapterTimeEnd, EbmlUInteger>(*atom) = 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);
  }
}
Пример #13
0
/** \brief Remove all chapter atoms that are outside of a time range

   All chapter atoms that lie completely outside the timecode range
   given with <tt>[min_tc..max_tc]</tt> are deleted. This is the workhorse
   for ::select_chapters_in_timeframe

   Chapters which start before the window but end inside or after the window
   are kept as well, and their start timecode is adjusted.

   Its parameters don't have to be checked for validity.

   \param min_tc The minimum timecode to accept.
   \param max_tc The maximum timecode to accept.
   \param offset This value is subtracted from both the start and end timecode
     for each chapter after the decision whether or not to keep it has been
     made.
   \param m The master containing the elements to check.
*/
static void
remove_entries(int64_t min_tc,
               int64_t max_tc,
               int64_t offset,
               EbmlMaster &m) {
  if (0 == m.ListSize())
    return;

  struct chapter_entry_t {
    bool remove, spans, is_atom;
    int64_t start, end;

    chapter_entry_t()
      : remove(false)
      , spans(false)
      , is_atom(false)
      , start(0)
      , end(-1)
    {
    }
  } *entries                = new chapter_entry_t[m.ListSize()];
  unsigned int last_atom_at = 0;
  bool last_atom_found      = false;

  // Determine whether or not an entry has to be removed. Also retrieve
  // the start and end timecodes.
  size_t i;
  for (i = 0; m.ListSize() > i; ++i) {
    KaxChapterAtom *atom = dynamic_cast<KaxChapterAtom *>(m[i]);
    if (!atom)
      continue;

    last_atom_at       = i;
    last_atom_found    = true;
    entries[i].is_atom = true;

    KaxChapterTimeStart *cts = static_cast<KaxChapterTimeStart *>(atom->FindFirstElt(EBML_INFO(KaxChapterTimeStart), false));

    if (cts)
      entries[i].start = uint64(*cts);

    KaxChapterTimeEnd *cte = static_cast<KaxChapterTimeEnd *>(atom->FindFirstElt(EBML_INFO(KaxChapterTimeEnd), false));

    if (cte)
      entries[i].end = uint64(*cte);
  }

  // We can return if we don't have a single atom to work with.
  if (!last_atom_found)
    return;

  for (i = 0; m.ListSize() > i; ++i) {
    KaxChapterAtom *atom = dynamic_cast<KaxChapterAtom *>(m[i]);
    if (!atom)
      continue;

    // Calculate the end timestamps and determine whether or not an entry spans
    // several segments.
    if (-1 == entries[i].end) {
      if (i == last_atom_at)
        entries[i].end = 1LL << 62;

      else {
        int next_atom = i + 1;

        while (!entries[next_atom].is_atom)
          ++next_atom;

        entries[i].end = entries[next_atom].start;
      }
    }

    if (   (entries[i].start < min_tc)
        || ((max_tc >= 0) && (entries[i].start > max_tc)))
      entries[i].remove = true;

    if (entries[i].remove && (entries[i].start < min_tc) && (entries[i].end > min_tc))
      entries[i].spans = true;

    mxverb(3,
           boost::format("remove_chapters: entries[%1%]: remove %2% spans %3% start %4% end %5%\n")
           % i % entries[i].remove % entries[i].spans % entries[i].start % entries[i].end);

    // Spanning entries must be kept, and their start timecode must be
    // adjusted. Entries that are to be deleted will be deleted later and
    // have to be skipped for now.
    if (entries[i].remove && !entries[i].spans)
      continue;

    KaxChapterTimeStart *cts = static_cast<KaxChapterTimeStart *>(atom->FindFirstElt(EBML_INFO(KaxChapterTimeStart), false));
    KaxChapterTimeEnd *cte   = static_cast<KaxChapterTimeEnd *>(atom->FindFirstElt(EBML_INFO(KaxChapterTimeEnd), false));

    if (entries[i].spans)
      *static_cast<EbmlUInteger *>(cts) = min_tc;

    *static_cast<EbmlUInteger *>(cts) = uint64(*cts) - offset;

    if (cte) {
      int64_t end_tc =  uint64(*cte);

      if ((max_tc >= 0) && (end_tc > max_tc))
        end_tc = max_tc;
      end_tc -= offset;

      *static_cast<EbmlUInteger *>(cte) = end_tc;
    }

    EbmlMaster *m2 = dynamic_cast<EbmlMaster *>(m[i]);
    if (m2)
      remove_entries(min_tc, max_tc, offset, *m2);
  }

  // Now really delete those entries.
  i = m.ListSize();
  while (0 < i) {
    --i;
    if (entries[i].remove && !entries[i].spans) {
      delete m[i];
      m.Remove(i);
    }
  }

  delete []entries;
}