/** \brief Merges consecutive EbmlVoid elements into a single one

    Iterates over the level 1 elements in the m_file and merges
    consecutive EbmlVoid elements into a single one which covers
    the same space as the smaller ones combined.

    Void elements at the end of the m_file are removed as well.
 */
void
kax_analyzer_c::merge_void_elements() {
  size_t start_idx = 0;

  while (m_data.size() > start_idx) {
    // We only have to do work on EbmlVoid elements. Skip the others.
    if (!Is<EbmlVoid>(m_data[start_idx]->m_id)) {
      ++start_idx;
      continue;
    }

    // Found an EbmlVoid element. See how many consecutive EbmlVoid elements
    // there are at this position and calculate the combined size.
    size_t end_idx  = start_idx + 1;
    size_t new_size = m_data[start_idx]->m_size;
    while ((m_data.size() > end_idx) && Is<EbmlVoid>(m_data[end_idx]->m_id)) {
      new_size += m_data[end_idx]->m_size;
      ++end_idx;
    }

    // Is this only a single EbmlVoid element? If yes continue.
    if (end_idx == (start_idx + 1)) {
      start_idx += 2;
      continue;
    }

    // Write the new EbmlVoid element to the m_file.
    m_file->setFilePointer(m_data[start_idx]->m_pos);

    EbmlVoid evoid;
    evoid.SetSize(new_size);
    evoid.UpdateSize();
    evoid.SetSize(new_size - evoid.HeadSize());
    evoid.Render(*m_file);

    // Update the internal records to reflect the changes.
    m_data[start_idx]->m_size = new_size;
    m_data.erase(m_data.begin() + start_idx + 1, m_data.begin() + end_idx);

    start_idx += 2;
  }

  // See how many void elements there are at the end of the m_file.
  start_idx = m_data.size();

  while ((0 < start_idx) && Is<EbmlVoid>(m_data[start_idx - 1]->m_id))
    --start_idx;

  // If there are none then we're done.
  if (m_data.size() <= start_idx)
    return;

  // Truncate the m_file after the last non-void element and update the m_segment size.
  m_file->truncate(m_data[start_idx]->m_pos);
  adjust_segment_size();
}
Beispiel #2
0
uint64 EbmlVoid::ReplaceWith(EbmlElement & EltToReplaceWith, IOCallback & output, bool ComeBackAfterward, bool bKeepIntact)
{
	EltToReplaceWith.UpdateSize(bKeepIntact);
	if (HeadSize() + Size < EltToReplaceWith.GetSize() + EltToReplaceWith.HeadSize()) {
		// the element can't be written here !
		return 0;
	}
	if (HeadSize() + Size - EltToReplaceWith.GetSize() - EltToReplaceWith.HeadSize() == 1) {
		// there is not enough space to put a filling element
		return 0;
	}

	uint64 CurrentPosition = output.getFilePointer();

	output.setFilePointer(GetElementPosition());
	EltToReplaceWith.Render(output, bKeepIntact);

	if (HeadSize() + Size - EltToReplaceWith.GetSize() - EltToReplaceWith.HeadSize() > 1) {
	  // fill the rest with another void element
	  EbmlVoid aTmp;
	  aTmp.SetSize(HeadSize() + Size - EltToReplaceWith.GetSize() - EltToReplaceWith.HeadSize() - 1); // 1 is the length of the Void ID
	  int HeadBefore = aTmp.HeadSize();
	  aTmp.SetSize(aTmp.GetSize() - CodedSizeLength(aTmp.Size, aTmp.SizeLength, aTmp.bSizeIsFinite));
	  int HeadAfter = aTmp.HeadSize();
	  if (HeadBefore != HeadAfter) {
		  aTmp.SetSizeLength(CodedSizeLength(aTmp.Size, aTmp.SizeLength, aTmp.bSizeIsFinite) - (HeadAfter - HeadBefore));
	  }
	  aTmp.RenderHead(output, false, bKeepIntact); // the rest of the data is not rewritten
	}

	if (ComeBackAfterward) {
		output.setFilePointer(CurrentPosition);
	}

	return Size + HeadSize();
}
/** \brief Create an EbmlVoid element at a specific location

    This function fills a gap in the file with an EbmlVoid. If an
    EbmlVoid element is located directly behind the gap then this
    element is overwritten as well.

    The function calculates the size of the new void element by taking
    the next non-EbmlVoid's position and subtracting from it the end
    position of the current element indicated by the \c data_idx
    parameter.

    If the space is not big enough to contain an EbmlVoid element then
    the EBML head of the following element is moved one byte to the
    front and its size field is extended by one byte. That way the
    file stays compatible with all parsers, and only a small number of
    bytes have to be moved around.

    The \c m_data member structure is also updated to reflect the
    changes made to the file.

    The function relies on \c m_data[data_idx] to be up to date
    regarding its size. If the size of \c m_data[data_idx] is zero
    then it is assumed that the element shall be overwritten with an
    EbmlVoid element, and \c m_data[data_idx] will be removed from the
    \c m_data structure.

    \param data_idx Index into the \c m_data structure pointing to the
     current element after which the gap is located.

    \return \c true if a new void element was created and \c false if
      there was no need to create one or if there was not enough
      space.
 */
bool
kax_analyzer_c::handle_void_elements(size_t data_idx) {
  // Is the element at the end of the file? If so truncate the file
  // and remove the element from the m_data structure if that was
  // requested. Then we're done.
  if (m_data.size() == (data_idx + 1)) {
    m_file->truncate(m_data[data_idx]->m_pos + m_data[data_idx]->m_size);
    adjust_segment_size();
    if (0 == m_data[data_idx]->m_size)
      m_data.erase(m_data.begin() + data_idx);
    return false;
  }

  // Are the following elements EbmlVoid elements?
  size_t end_idx = data_idx + 1;
  while ((m_data.size() > end_idx) && Is<EbmlVoid>(m_data[end_idx]->m_id))
    ++end_idx;

  if (end_idx > data_idx + 1)
    // Yes, there is at least one. Remove these elements from the list
    // in order to create a new EbmlVoid element covering their space
    // as well.
    m_data.erase(m_data.begin() + data_idx + 1, m_data.begin() + end_idx);

  // Calculate how much space we have to cover with a void
  // element. This is the difference between the next element's
  // position and the current element's end.
  int64_t void_pos = m_data[data_idx]->m_pos + m_data[data_idx]->m_size;
  int void_size    = m_data[data_idx + 1]->m_pos - void_pos;

  // If the difference is 0 then we have nothing to do.
  if (0 == void_size)
    return false;

  // See if we have enough space to fit an EbmlVoid element in. An
  // EbmlVoid element needs at least two bytes (one for the ID, one
  // for the size).
  if (1 == void_size) {
    // No. The most compatible way to deal with this situation is to
    // move the element ID of the following element one byte to the
    // front and extend the following element's size field by one
    // byte.

    ebml_element_cptr e = read_element(m_data[data_idx + 1]);

    if (!e)
      return false;

    // However, this might not work if the element's size was already
    // eight bytes long.
    if (8 == e->GetSizeLength()) {
      // In this case try doing the same with the previous
      // element. The whole element has be moved one byte to the back.
      e = read_element(m_data[data_idx]);
      if (!e)
        return false;

      std::shared_ptr<EbmlElement> af_e(e);

      // Again the test for maximum size length.
      if (8 == e->GetSizeLength())
        return false;

      // Copy the content one byte to the back.
      unsigned int id_length = EBML_ID_LENGTH(static_cast<const EbmlId &>(*e));
      uint64_t content_pos   = m_data[data_idx]->m_pos + id_length + e->GetSizeLength();
      uint64_t content_size  = m_data[data_idx + 1]->m_pos - content_pos - 1;
      memory_cptr buffer     = memory_c::alloc(content_size);

      m_file->setFilePointer(content_pos);
      if (m_file->read(buffer, content_size) != content_size)
        return false;

      m_file->setFilePointer(content_pos + 1);
      if (m_file->write(buffer) != content_size)
        return false;

      // Prepare the new codec size and write it.
      binary head[8];           // Class D + 64 bits coded size
      int coded_size = CodedSizeLength(content_size, e->GetSizeLength() + 1, true);
      CodedValueLength(content_size, coded_size, head);
      m_file->setFilePointer(m_data[data_idx]->m_pos + id_length);
      if (m_file->write(head, coded_size) != static_cast<unsigned int>(coded_size))
        return false;

      // Update internal structures.
      m_data[data_idx]->m_size += 1;

      return true;
    }

    binary head[4 + 8];         // Class D + 64 bits coded size
    unsigned int head_size = EBML_ID_LENGTH(static_cast<const EbmlId &>(*e));
    EbmlId(*e).Fill(head);

    int coded_size = CodedSizeLength(e->GetSize(), e->GetSizeLength() + 1, true);
    CodedValueLength(e->GetSize(), coded_size, &head[head_size]);
    head_size += coded_size;

    m_file->setFilePointer(m_data[data_idx + 1]->m_pos - 1);
    m_file->write(head, head_size);

    --m_data[data_idx + 1]->m_pos;
    ++m_data[data_idx + 1]->m_size;

    // Update meta seek indices for m_data[data_idx]'s new position.
    e = read_element(m_data[data_idx + 1]);

    remove_from_meta_seeks(EbmlId(*e));
    merge_void_elements();
    add_to_meta_seek(e.get());
    merge_void_elements();

    return false;
  }

  m_file->setFilePointer(void_pos);

  // Yes. Write a new EbmlVoid element and update the internal records.

  // Calculating the void element's content size. This is not straight
  // forward because there are special values for the total size for
  // which the header size must be forced to be one byte more than would
  // be strictly necessary in order to occupy the space. Here's an
  // example:

  // A two-byte header field (one for the ID, one for the content
  // size) can have a content size of at most 127 bytes, meaning a
  // total size of 129 is easy to achieve: set the content size to 127
  // bytes, libEBML will calculate the required length of the content
  // size field to be 1 and the resulting element's total size will be
  // the desired 129 bytes.

  // A content size of 128 would mean that the content size field must be
  // at least two bytes long. Taking the element's ID into account this
  // would mean a total element size of 128 + 2 + 1 = 131 bytes. So
  // getting libEBML to produce an element of a total size of 131 is
  // easy, too: set the content size to 128 bytes, and libEBML will do
  // the rest.

  // The problematic total size is a 130 bytes. There simply is no
  // content size for which adding the minimal length of the content
  // size field and 1 for the ID would result in a total size of 130
  // bytes.

  // The solution is writing the length field with more bytes than
  // necessary. In the case of a total size of 130 bytes we could use
  // the maximum one-byte content size = 127 bytes, add one byte for the
  // ID and force the content size field's length to be two instead of
  // one byte (0x407f instead of 0xff).

  // Similar corner cases exist for the transition between content
  // size field being two/three bytes, three/four bytes long etc. In
  // order to keep the code simple we always use an eight-bytes long
  // content size field if the total size is at least nine bytes and a
  // one-byte long content size field otherwise.

  EbmlVoid evoid;
  if (void_size < 9)
    evoid.SetSize(void_size - 2);

  else {
    evoid.SetSize(void_size - 9);
    evoid.SetSizeLength(8);
  }

  evoid.Render(*m_file);

  m_data.insert(m_data.begin() + data_idx + 1, kax_analyzer_data_c::create(EBML_ID(EbmlVoid), void_pos, void_size));

  // Now check if we should overwrite the current element with the
  // EbmlVoid element. That is the case if the current element's size
  // is 0. In that case simply remove the element from the m_data
  // vector.
  if (0 == m_data[data_idx]->m_size)
    m_data.erase(m_data.begin() + data_idx);

  return true;
}
Beispiel #4
0
/*!
    The first file is a "binary" file with data scaling from 0x00 to 0xFF repeatedly
    The second file is a "text" file with data scaling from 'z' to 'a'
*/
int main(int argc, char **argv)
{
    cout << "Creating \"muxed.mkv\"" << endl;

    try {
    // write the head of the file (with everything already configured)
    StdIOCallback out_file("muxed.mkv", MODE_CREATE);

    ///// Writing EBML test
    EbmlHead FileHead;

    EDocType & MyDocType = GetChild<EDocType>(FileHead);
    *static_cast<EbmlString *>(&MyDocType) = "matroska";

    EDocTypeVersion & MyDocTypeVer = GetChild<EDocTypeVersion>(FileHead);
    *(static_cast<EbmlUInteger *>(&MyDocTypeVer)) = MATROSKA_VERSION;

    EDocTypeReadVersion & MyDocTypeReadVer = GetChild<EDocTypeReadVersion>(FileHead);
    *(static_cast<EbmlUInteger *>(&MyDocTypeReadVer)) = 1;

    FileHead.Render(out_file, bWriteDefaultValues);

    KaxSegment FileSegment;

    // size is unknown and will always be, we can render it right away
    uint64 SegmentSize = FileSegment.WriteHead(out_file, 5, bWriteDefaultValues);

    KaxTracks & MyTracks = GetChild<KaxTracks>(FileSegment);

    // reserve some space for the Meta Seek writen at the end
    EbmlVoid Dummy;
    Dummy.SetSize(300); // 300 octets
    Dummy.Render(out_file, bWriteDefaultValues);

    KaxSeekHead MetaSeek;

    // fill the mandatory Info section
    KaxInfo & MyInfos = GetChild<KaxInfo>(FileSegment);
    KaxTimecodeScale & TimeScale = GetChild<KaxTimecodeScale>(MyInfos);
    *(static_cast<EbmlUInteger *>(&TimeScale)) = TIMECODE_SCALE;

    KaxDuration & SegDuration = GetChild<KaxDuration>(MyInfos);
    *(static_cast<EbmlFloat *>(&SegDuration)) = 0.0;

    *((EbmlUnicodeString *)&GetChild<KaxMuxingApp>(MyInfos))  = L"libmatroska 0.5.0";
    *((EbmlUnicodeString *)&GetChild<KaxWritingApp>(MyInfos)) = L"éàôï";
    GetChild<KaxWritingApp>(MyInfos).SetDefaultSize(25);

    filepos_t InfoSize = MyInfos.Render(out_file);
    MetaSeek.IndexThis(MyInfos, FileSegment);

    // fill track 1 params
    KaxTrackEntry & MyTrack1 = GetChild<KaxTrackEntry>(MyTracks);
    MyTrack1.SetGlobalTimecodeScale(TIMECODE_SCALE);

    KaxTrackNumber & MyTrack1Number = GetChild<KaxTrackNumber>(MyTrack1);
    *(static_cast<EbmlUInteger *>(&MyTrack1Number)) = 1;

    KaxTrackUID & MyTrack1UID = GetChild<KaxTrackUID>(MyTrack1);
    *(static_cast<EbmlUInteger *>(&MyTrack1UID)) = 7;

    *(static_cast<EbmlUInteger *>(&GetChild<KaxTrackType>(MyTrack1))) = track_audio;

    KaxCodecID & MyTrack1CodecID = GetChild<KaxCodecID>(MyTrack1);
    *static_cast<EbmlString *>(&MyTrack1CodecID) = "Dummy Audio Codec";

    MyTrack1.EnableLacing(true);

    // Test the new ContentEncoding elements
    KaxContentEncodings &cencodings = GetChild<KaxContentEncodings>(MyTrack1);
    KaxContentEncoding &cencoding = GetChild<KaxContentEncoding>(cencodings);
    *(static_cast<EbmlUInteger *>(&GetChild<KaxContentEncodingOrder>(cencoding))) = 10;
    *(static_cast<EbmlUInteger *>(&GetChild<KaxContentEncodingScope>(cencoding))) = 11;
    *(static_cast<EbmlUInteger *>(&GetChild<KaxContentEncodingType>(cencoding))) = 12;

    KaxContentCompression &ccompression = GetChild<KaxContentCompression>(cencoding);
    *(static_cast<EbmlUInteger *>(&GetChild<KaxContentCompAlgo>(ccompression))) = 13;
    GetChild<KaxContentCompSettings>(ccompression).CopyBuffer((const binary *)"hello1", 6);

    KaxContentEncryption &cencryption = GetChild<KaxContentEncryption>(cencoding);
    *(static_cast<EbmlUInteger *>(&GetChild<KaxContentEncAlgo>(cencryption))) = 14;
    GetChild<KaxContentEncKeyID>(cencryption).CopyBuffer((const binary *)"hello2", 6);
    *(static_cast<EbmlUInteger *>(&GetChild<KaxContentSigAlgo>(cencryption))) = 15;
    *(static_cast<EbmlUInteger *>(&GetChild<KaxContentSigHashAlgo>(cencryption))) = 16;
    GetChild<KaxContentSigKeyID>(cencryption).CopyBuffer((const binary *)"hello3", 6);
    GetChild<KaxContentSignature>(cencryption).CopyBuffer((const binary *)"hello4", 6);

    // audio specific params
    KaxTrackAudio & MyTrack1Audio = GetChild<KaxTrackAudio>(MyTrack1);

    KaxAudioSamplingFreq & MyTrack1Freq = GetChild<KaxAudioSamplingFreq>(MyTrack1Audio);
    *(static_cast<EbmlFloat *>(&MyTrack1Freq)) = 44100.0;
    MyTrack1Freq.ValidateSize();

#if MATROSKA_VERSION >= 2
    KaxAudioPosition & MyTrack1Pos = GetChild<KaxAudioPosition>(MyTrack1Audio);
    binary *_Pos = new binary[5];
    _Pos[0] = '0';
    _Pos[1] = '1';
    _Pos[2] = '2';
    _Pos[3] = '3';
    _Pos[4] = '\0';
    MyTrack1Pos.SetBuffer(_Pos, 5);
#endif // MATROSKA_VERSION

    KaxAudioChannels & MyTrack1Channels = GetChild<KaxAudioChannels>(MyTrack1Audio);
    *(static_cast<EbmlUInteger *>(&MyTrack1Channels)) = 2;

    // fill track 2 params
    KaxTrackEntry & MyTrack2 = GetNextChild<KaxTrackEntry>(MyTracks, MyTrack1);
    MyTrack2.SetGlobalTimecodeScale(TIMECODE_SCALE);

    KaxTrackNumber & MyTrack2Number = GetChild<KaxTrackNumber>(MyTrack2);
    *(static_cast<EbmlUInteger *>(&MyTrack2Number)) = 200;

    KaxTrackUID & MyTrack2UID = GetChild<KaxTrackUID>(MyTrack2);
    *(static_cast<EbmlUInteger *>(&MyTrack2UID)) = 13;

    *(static_cast<EbmlUInteger *>(&GetChild<KaxTrackType>(MyTrack2))) = track_video;

    KaxCodecID & MyTrack2CodecID = GetChild<KaxCodecID>(MyTrack2);
    *static_cast<EbmlString *>(&MyTrack2CodecID) = "Dummy Video Codec";

    MyTrack2.EnableLacing(false);

    // video specific params
    KaxTrackVideo & MyTrack2Video = GetChild<KaxTrackVideo>(MyTrack2);

    KaxVideoPixelHeight & MyTrack2PHeight = GetChild<KaxVideoPixelHeight>(MyTrack2Video);
    *(static_cast<EbmlUInteger *>(&MyTrack2PHeight)) = 200;

    KaxVideoPixelWidth & MyTrack2PWidth = GetChild<KaxVideoPixelWidth>(MyTrack2Video);
    *(static_cast<EbmlUInteger *>(&MyTrack2PWidth)) = 320;

    uint64 TrackSize = MyTracks.Render(out_file, bWriteDefaultValues);

    KaxTracks * pMyTracks2 = static_cast<KaxTracks *>(MyTracks.Clone());
//    KaxTracks * pMyTracks2 = new KaxTracks(MyTracks);

    MetaSeek.IndexThis(MyTracks, FileSegment);


    // "manual" filling of a cluster"
    /// \todo whenever a BlockGroup is created, we should memorize it's position
    KaxCues AllCues;
    AllCues.SetGlobalTimecodeScale(TIMECODE_SCALE);

    KaxCluster Clust1;
    Clust1.SetParent(FileSegment); // mandatory to store references in this Cluster
    Clust1.SetPreviousTimecode(0, TIMECODE_SCALE); // the first timecode here
    Clust1.EnableChecksum();

    // automatic filling of a Cluster
    // simple frame
    KaxBlockGroup *MyNewBlock, *MyLastBlockTrk1 = NULL, *MyLastBlockTrk2 = NULL, *MyNewBlock2;
    DataBuffer *data7 = new DataBuffer((binary *)"tototototo", countof("tototototo"));
    Clust1.AddFrame(MyTrack1, 250 * TIMECODE_SCALE, *data7, MyNewBlock, LACING_EBML);
    if (MyNewBlock != NULL)
      MyLastBlockTrk1 = MyNewBlock;
    DataBuffer *data0 = new DataBuffer((binary *)"TOTOTOTO", countof("TOTOTOTO"));
    Clust1.AddFrame(MyTrack1, 260 * TIMECODE_SCALE, *data0, MyNewBlock); // to test EBML lacing
    if (MyNewBlock != NULL)
      MyLastBlockTrk1 = MyNewBlock;
    DataBuffer *data6 = new DataBuffer((binary *)"tototototo", countof("tototototo"));
    Clust1.AddFrame(MyTrack1, 270 * TIMECODE_SCALE, *data6, MyNewBlock); // to test lacing
    if (MyNewBlock != NULL) {
      MyLastBlockTrk1 = MyNewBlock;
    } else {
      MyLastBlockTrk1->SetBlockDuration(50 * TIMECODE_SCALE);
    }

    DataBuffer *data5 = new DataBuffer((binary *)"tototototo", countof("tototototo"));
    Clust1.AddFrame(MyTrack2, 23 * TIMECODE_SCALE, *data5, MyNewBlock); // to test with another track

    // add the "real" block to the cue entries
        KaxBlockBlob *Blob1 = new KaxBlockBlob(BLOCK_BLOB_NO_SIMPLE);
        Blob1->SetBlockGroup(*MyLastBlockTrk1);
    AllCues.AddBlockBlob(*Blob1);

    // frame for Track 2
    DataBuffer *data8 = new DataBuffer((binary *)"tttyyy", countof("tttyyy"));
    Clust1.AddFrame(MyTrack2, 107 * TIMECODE_SCALE, *data8, MyNewBlock, *MyLastBlockTrk2);

        KaxBlockBlob *Blob2 = new KaxBlockBlob(BLOCK_BLOB_NO_SIMPLE);
        Blob2->SetBlockGroup(*MyNewBlock);
    AllCues.AddBlockBlob(*Blob2);

    // frame with a past reference
    DataBuffer *data4 = new DataBuffer((binary *)"tttyyy", countof("tttyyy"));
    Clust1.AddFrame(MyTrack1, 300 * TIMECODE_SCALE, *data4, MyNewBlock, *MyLastBlockTrk1);

    // frame with a past & future reference
    if (MyNewBlock != NULL) {
      DataBuffer *data3 = new DataBuffer((binary *)"tttyyy", countof("tttyyy"));
      if (Clust1.AddFrame(MyTrack1, 280 * TIMECODE_SCALE, *data3, MyNewBlock2, *MyLastBlockTrk1, *MyNewBlock)) {
        MyNewBlock2->SetBlockDuration(20 * TIMECODE_SCALE);
        MyLastBlockTrk1 = MyNewBlock2;
      } else {
        printf("Error adding a frame !!!");
      }
    }

        KaxBlockBlob *Blob3 = new KaxBlockBlob(BLOCK_BLOB_NO_SIMPLE);
        Blob3->SetBlockGroup(*MyLastBlockTrk1);
    AllCues.AddBlockBlob(*Blob3);
    //AllCues.UpdateSize();

    // simulate the writing of the stream :
    // - write an empty element with enough size for the cue entry
    // - write the cluster(s)
    // - seek back in the file and write the cue entry over the empty element

    uint64 ClusterSize = Clust1.Render(out_file, AllCues, bWriteDefaultValues);
    Clust1.ReleaseFrames();
    MetaSeek.IndexThis(Clust1, FileSegment);

    KaxCluster Clust2;
    Clust2.SetParent(FileSegment); // mandatory to store references in this Cluster
    Clust2.SetPreviousTimecode(300 * TIMECODE_SCALE, TIMECODE_SCALE); // the first timecode here
    Clust2.EnableChecksum();

    DataBuffer *data2 = new DataBuffer((binary *)"tttyyy", countof("tttyyy"));
    Clust2.AddFrame(MyTrack1, 350 * TIMECODE_SCALE, *data2, MyNewBlock, *MyLastBlockTrk1);

        KaxBlockBlob *Blob4 = new KaxBlockBlob(BLOCK_BLOB_NO_SIMPLE);
        Blob4->SetBlockGroup(*MyNewBlock);
    AllCues.AddBlockBlob(*Blob4);

    ClusterSize += Clust2.Render(out_file, AllCues, bWriteDefaultValues);
    Clust2.ReleaseFrames();

// older version, write at the end    AllCues.Render(out_file);
    filepos_t CueSize = AllCues.Render(out_file, bWriteDefaultValues);
    MetaSeek.IndexThis(AllCues, FileSegment);

    // Chapters
    KaxChapters Chapters;
    Chapters.EnableChecksum();
    KaxEditionEntry & aEdition = GetChild<KaxEditionEntry>(Chapters);
    KaxChapterAtom & aAtom = GetChild<KaxChapterAtom>(aEdition);
    KaxChapterUID & aUID = GetChild<KaxChapterUID>(aAtom);
    *static_cast<EbmlUInteger *>(&aUID) = 0x67890;

    KaxChapterTimeStart & aChapStart = GetChild<KaxChapterTimeStart>(aAtom);
    *static_cast<EbmlUInteger *>(&aChapStart) = 0;

    KaxChapterTimeEnd & aChapEnd = GetChild<KaxChapterTimeEnd>(aAtom);
    *static_cast<EbmlUInteger *>(&aChapEnd) = 300 * TIMECODE_SCALE;

    KaxChapterDisplay & aDisplay = GetChild<KaxChapterDisplay>(aAtom);
    KaxChapterString & aChapString = GetChild<KaxChapterString>(aDisplay);
    *static_cast<EbmlUnicodeString *>(&aChapString) = L"Le film réduit à un chapitre";

    KaxChapterLanguage & aChapLang = GetChild<KaxChapterLanguage>(aDisplay);
    *static_cast<EbmlString *>(&aChapLang) = "fra";

    KaxChapterDisplay & aDisplay2 = GetNextChild<KaxChapterDisplay>(aAtom, aDisplay);
    KaxChapterString & aChapString2 = GetChild<KaxChapterString>(aDisplay2);
    *static_cast<EbmlUnicodeString *>(&aChapString2) = L"The movie in one chapter";

    KaxChapterLanguage & aChapLang2 = GetChild<KaxChapterLanguage>(aDisplay2);
    *static_cast<EbmlString *>(&aChapLang2) = "eng";

    filepos_t ChapterSize = Chapters.Render(out_file, bWriteDefaultValues);
    MetaSeek.IndexThis(Chapters, FileSegment);

    // Write some tags
    KaxTags AllTags;
    AllTags.EnableChecksum();
    KaxTag & aTag = GetChild<KaxTag>(AllTags);
    KaxTagTargets & Targets = GetChild<KaxTagTargets>(aTag);
    KaxTagSimple & TagSimple = GetChild<KaxTagSimple>(aTag);

    KaxTagTrackUID & TrackUID = GetChild<KaxTagTrackUID>(Targets);
    *static_cast<EbmlUInteger *>(&TrackUID) = 0x12345;

    KaxTagChapterUID & ChapterUID = GetChild<KaxTagChapterUID>(Targets);
    *static_cast<EbmlUInteger *>(&ChapterUID) = 0x67890;

        KaxTagName & aTagName = GetChild<KaxTagName>(TagSimple);
        *static_cast<EbmlUnicodeString *>(&aTagName) = L"NAME";

        KaxTagString & aTagtring = GetChild<KaxTagString>(TagSimple);
        *static_cast<EbmlUnicodeString *>(&aTagtring) = L"Testé123";

    filepos_t TagsSize = AllTags.Render(out_file, bWriteDefaultValues);
    MetaSeek.IndexThis(AllTags, FileSegment);

    TrackSize += pMyTracks2->Render(out_file, bWriteDefaultValues);
    MetaSeek.IndexThis(*pMyTracks2, FileSegment);

    // \todo put it just before the Cue Entries
    filepos_t MetaSeekSize = Dummy.ReplaceWith(MetaSeek, out_file, bWriteDefaultValues);

#ifdef VOID_TEST
    MyInfos.VoidMe(out_file);
#endif // VOID_TEST

    // let's assume we know the size of the Segment element
    // the size of the FileSegment is also computed because mandatory elements we don't write ourself exist
    if (FileSegment.ForceSize(SegmentSize - FileSegment.HeadSize() + MetaSeekSize
                            + TrackSize + ClusterSize + CueSize + InfoSize + TagsSize + ChapterSize)) {
      FileSegment.OverwriteHead(out_file);
    }

#if 0
    delete[] buf_bin;
    delete[] buf_txt;
#endif // 0

#ifdef OLD
    MuxedFile.Close(1000); // 1000 ms
#endif // OLD
    out_file.close();

        delete Blob1;
        delete Blob2;
        delete Blob3;
        delete Blob4;
    }
    catch (exception & Ex)
    {
    cout << Ex.what() << endl;
    }

    return 0;
}