Example #1
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();
}
Example #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_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);
  }
}
Example #3
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]);
  }
}
Example #4
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);
  }
}
Example #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();
}
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;
}
Example #7
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;
}
Example #8
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);
}
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;
}
Example #10
0
std::vector<std::string> EbmlMaster::FindAllMissingElements()
{	
	assert(Context.GetSize() != 0);

	std::vector<std::string> missingElements;

	for (size_t ChildElementNo = 0; ChildElementNo < ElementList.size(); ChildElementNo++) {
   		EbmlElement *childElement = ElementList[ChildElementNo];
		if (!childElement->ValueIsSet()) {
			std::string missingValue;
			missingValue = "The Child Element \"";
			missingValue.append(EBML_NAME(childElement));
			missingValue.append("\" of EbmlMaster \"");
			missingValue.append(EBML_NAME(this));
			missingValue.append("\", does not have a value set.");
			missingElements.push_back(missingValue);
		}

		if (childElement->IsMaster()) {
			EbmlMaster *childMaster = (EbmlMaster *)childElement;

			std::vector<std::string> childMissingElements = childMaster->FindAllMissingElements();
			for (size_t s = 0; s < childMissingElements.size(); s++)
				missingElements.push_back(childMissingElements[s]);
		}
	}
	unsigned int EltIdx;
	for (EltIdx = 0; EltIdx < EBML_CTX_SIZE(Context); EltIdx++) {
		if (EBML_CTX_IDX(Context,EltIdx).IsMandatory()) {
			if (FindElt(EBML_CTX_IDX_INFO(Context,EltIdx)) == NULL) {
				std::string missingElement;
				missingElement = "Missing element \"";
                missingElement.append(EBML_INFO_NAME(EBML_CTX_IDX_INFO(Context,EltIdx)));
				missingElement.append("\" in EbmlMaster \"");
                missingElement.append(EBML_INFO_NAME(*EBML_CTX_MASTER(Context)));
				missingElement.append("\"");
				missingElements.push_back(missingElement);
			}
		}
	}

	return missingElements;
}
Example #11
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;
}
Example #12
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++;
	}

}
Example #13
0
File: test0.cpp Project: ares89/vlc
/*!
    \test Some test on the Cluster use
    \todo render the Cluster to a file
*/
int main(void)
{
	StdIOCallback Ebml_file(RW_FILENAME, ::MODE_CREATE);
	
	///// Writing test

	///////////////////////////////
	//   Unsigned integer
	///////////////////////////////
	
	EbmlUInteger testUInt(4); // supposed to hold a 4*8 bits value
	
	testUInt.SetID(SemanticList[0].Id, SemanticList[0].IdLength);
	testUInt = 52;
	testUInt.SetSizeLength(3); // size should be coded on at least 3 octets
	testUInt.Render(Ebml_file);

	///////////////////////////////
	//   Signed integer
	///////////////////////////////
	
	EbmlSInteger testSInt(4); // supposed to hold a 4*8 bits value

	testSInt.SetID(SemanticList[1].Id, SemanticList[1].IdLength);
	testSInt = -20;
	testSInt.Render(Ebml_file);

	///////////////////////////////
	//   Binary data
	///////////////////////////////
	const int BINARY_SIZE=3000;
	
	binary *bin = new binary[BINARY_SIZE];
	memset(bin, 0x61, BINARY_SIZE);
	EbmlBinary testBin;

	testBin.SetID(SemanticList[2].Id, SemanticList[2].IdLength);
	testBin.SetBuffer(bin, BINARY_SIZE);
	testBin.Render(Ebml_file);

	///////////////////////////////
	//   String data
	///////////////////////////////
	std::string aString = "Hello World !";
	EbmlString testStr(200);

	testStr.SetID(SemanticList[3].Id, SemanticList[3].IdLength);
	testStr = aString;
	testStr.Render(Ebml_file);

	///////////////////////////////
	//   Master element
	///////////////////////////////
	EbmlMaster testMaster;

	testMaster.SetID(SemanticList[6].Id, SemanticList[6].IdLength);
	testMaster.PushElement(testStr);
	testMaster.PushElement(testUInt);
	testMaster.Render(Ebml_file);

	///////////////////////////////
	//   Unicode String data
	///////////////////////////////
	UTFstring bString = L"Stève Lhomm€";
	EbmlUnicodeString testUStr(200);

	testUStr.SetID(SemanticList[4].Id, SemanticList[4].IdLength);
	testUStr = bString;
	testUStr.Render(Ebml_file);

	///////////////////////////////
	//   Float data
	///////////////////////////////
	EbmlFloat testFloat(EbmlFloat::FLOAT_32);

	testFloat.SetID(SemanticList[5].Id, SemanticList[5].IdLength);
	testFloat.SetPrecision(EbmlFloat::FLOAT_32);
	testFloat = 1.01234567890123456;
	testFloat.Render(Ebml_file);
	
	testFloat.SetPrecision(EbmlFloat::FLOAT_64);
	testFloat = -1.01234567890123456L;
	testFloat.Render(Ebml_file);
	
	Ebml_file.close();

	///// Reading test
	StdIOCallback Ebml_Wfile(RW_FILENAME, ::MODE_READ);
	
	// example 1 skip all the elements found
	EbmlStream aStream(Ebml_Wfile);
	EbmlElement * ElementLevel0;
	
	// read the data until a possible element is found (valid ID + size combination)
	ElementLevel0 = aStream.FindNextID(0xFFFFFFFFL, false);
	printf("Read EBML elements & skip data\n");
	while (ElementLevel0 != NULL)
	{
		printf("ID : ");
		for (int i=0; i<ElementLevel0->GetIDLength(); i++)
		{
			printf("[%02X]", ElementLevel0->GetID()[i]);
		}
		printf("\n");

		ElementLevel0->SkipData(Ebml_Wfile);
		if (ElementLevel0 != NULL)
			delete ElementLevel0;
	
		ElementLevel0 = aStream.FindNextID(0xFFFFFFFFL, false);
	}
	
	// example 2 evaluation of all elements found
	EbmlStream bStream(Ebml_Wfile);
	EbmlElement * EvaledElementLevel0;
//	EbmlElement * EvaledElementLevel1;
	
	// reset the stream to the beggining
	Ebml_Wfile.setFilePointer(0);
	
	// list of all IDs and their semantic type
//	std::list<struct Semantic> SemanticList;
//	SemanticList.push_back();
	
	ElementLevel0 = aStream.FindNextID(0xFFFFFFFFL, false);
	printf("Read EBML elements & evaluate data\n");
	while (ElementLevel0 != NULL)
	{
		int i;
		printf("ID : ");
		for (i=0; i<ElementLevel0->GetIDLength(); i++)
		{
			printf("[%02X]", ElementLevel0->GetID()[i]);
		}

		// check if the element is known
		for (i=0; i<countof(SemanticList); i++) {
			if (ElementLevel0->GetIDLength() != SemanticList[i].IdLength)
				continue;
			if (memcmp(SemanticList[i].Id, ElementLevel0->GetID(), SemanticList[i].IdLength) == 0)
				break;
		}
		/// \todo check if it is known in the context
		// handle the data inside the element
		if (i < countof(SemanticList)) {
			switch (SemanticList[i].Type)
			{
				case EBML_U_INTEGER:
					EvaledElementLevel0 = new EbmlUInteger(*ElementLevel0);
					break;
				case EBML_S_INTEGER:
					EvaledElementLevel0 = new EbmlSInteger(*ElementLevel0);
					break;
				case EBML_BINARY:
					EvaledElementLevel0 = new EbmlBinary(*ElementLevel0);
					break;
				case EBML_STRING:
					EvaledElementLevel0 = new EbmlString(*ElementLevel0);
					break;
				case EBML_STRING_UNICODE:
					EvaledElementLevel0 = new EbmlUnicodeString(*ElementLevel0);
					break;
				case EBML_FLOAT:
					EvaledElementLevel0 = new EbmlFloat(*ElementLevel0);
					break;
				case EBML_MASTER:
					EvaledElementLevel0 = new EbmlMaster(*ElementLevel0);
					break;
			}

			EvaledElementLevel0->ReadData(Ebml_Wfile);

			switch (SemanticList[i].Type)
			{
				case EBML_U_INTEGER:
					printf(" : %d", uint32(*(EbmlUInteger*)EvaledElementLevel0));
					break;
				case EBML_S_INTEGER:
					printf(" : %d", int32(*(EbmlSInteger*)EvaledElementLevel0));
					break;
				case EBML_BINARY:
					printf(" : binary data, size = %ld", (*(EbmlBinary*)EvaledElementLevel0).GetSize());
					printf(" [%02X]", binary(*(EbmlBinary*)EvaledElementLevel0));
					break;
				case EBML_STRING:
					printf(" : %s", std::string(*(EbmlString*)EvaledElementLevel0).data());
					break;
				case EBML_STRING_UNICODE:
					printf(" : (wide chars) %ls", UTFstring(*(EbmlUnicodeString*)EvaledElementLevel0).data());
					break;
				case EBML_FLOAT:
					printf(" : %f / %.15lf", float(*(EbmlFloat*)EvaledElementLevel0), double(*(EbmlFloat*)EvaledElementLevel0));
					break;
				case EBML_MASTER:
					printf(" : unsupported format 'Master'");
					break;
			}
			delete EvaledElementLevel0;
		} else {
			ElementLevel0->SkipData(Ebml_Wfile);
		}
		if (ElementLevel0 != NULL)
			delete ElementLevel0;

		printf("\n");
	
		ElementLevel0 = aStream.FindNextID(0xFFFFFFFFL, false);
	}
	
	Ebml_Wfile.close();

	return 0;
}
Example #14
0
void
move_children(EbmlMaster &source,
              EbmlMaster &destination) {
  for (auto child : source)
    destination.PushElement(*child);
}
Example #15
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);
  }
}
Example #16
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;
}