예제 #1
0
int
cluster_helper_c::render() {
  std::vector<render_groups_cptr> render_groups;
  KaxCues cues;
  cues.SetGlobalTimecodeScale(g_timecode_scale);

  bool use_simpleblock    = !hack_engaged(ENGAGE_NO_SIMPLE_BLOCKS);

  LacingType lacing_type  = hack_engaged(ENGAGE_LACING_XIPH) ? LACING_XIPH : hack_engaged(ENGAGE_LACING_EBML) ? LACING_EBML : LACING_AUTO;

  int64_t min_cl_timecode = std::numeric_limits<int64_t>::max();
  int64_t max_cl_timecode = 0;

  int elements_in_cluster = 0;
  bool added_to_cues      = false;

  // Splitpoint stuff
  if ((-1 == m->header_overhead) && splitting())
    m->header_overhead = m->out->getFilePointer() + g_tags_size;

  // Make sure that we don't have negative/wrapped around timecodes in the output file.
  // Can happend when we're splitting; so adjust timecode_offset accordingly.
  m->timecode_offset       = boost::accumulate(m->packets, m->timecode_offset, [](int64_t a, const packet_cptr &p) { return std::min(a, p->assigned_timecode); });
  int64_t timecode_offset = m->timecode_offset + get_discarded_duration();

  for (auto &pack : m->packets) {
    generic_packetizer_c *source = pack->source;
    bool has_codec_state         = !!pack->codec_state;

    if (g_video_packetizer == source)
      m->max_video_timecode_rendered = std::max(pack->assigned_timecode + pack->get_duration(), m->max_video_timecode_rendered);

    if (discarding()) {
      if (-1 == m->first_discarded_timecode)
        m->first_discarded_timecode = pack->assigned_timecode;
      m->last_discarded_timecode_and_duration = std::max(m->last_discarded_timecode_and_duration, pack->assigned_timecode + pack->get_duration());
      continue;
    }

    if (source->contains_gap())
      m->cluster->SetSilentTrackUsed();

    render_groups_c *render_group = nullptr;
    for (auto &rg : render_groups)
      if (rg->m_source == source) {
        render_group = rg.get();
        break;
      }

    if (!render_group) {
      render_groups.push_back(render_groups_cptr(new render_groups_c(source)));
      render_group = render_groups.back().get();
    }

    min_cl_timecode                        = std::min(pack->assigned_timecode, min_cl_timecode);
    max_cl_timecode                        = std::max(pack->assigned_timecode, max_cl_timecode);

    DataBuffer *data_buffer                = new DataBuffer((binary *)pack->data->get_buffer(), pack->data->get_size());

    KaxTrackEntry &track_entry             = static_cast<KaxTrackEntry &>(*source->get_track_entry());

    kax_block_blob_c *previous_block_group = !render_group->m_groups.empty() ? render_group->m_groups.back().get() : nullptr;
    kax_block_blob_c *new_block_group      = previous_block_group;

    if (!pack->is_key_frame() || has_codec_state || pack->has_discard_padding())
      render_group->m_more_data = false;

    if (!render_group->m_more_data) {
      set_duration(render_group);
      render_group->m_durations.clear();
      render_group->m_duration_mandatory = false;

      BlockBlobType this_block_blob_type
        = !use_simpleblock                         ? BLOCK_BLOB_NO_SIMPLE
        : must_duration_be_set(render_group, pack) ? BLOCK_BLOB_NO_SIMPLE
        : !pack->data_adds.empty()                 ? BLOCK_BLOB_NO_SIMPLE
        : has_codec_state                          ? BLOCK_BLOB_NO_SIMPLE
        : pack->has_discard_padding()              ? BLOCK_BLOB_NO_SIMPLE
        :                                            BLOCK_BLOB_ALWAYS_SIMPLE;

      render_group->m_groups.push_back(kax_block_blob_cptr(new kax_block_blob_c(this_block_blob_type)));
      new_block_group = render_group->m_groups.back().get();
      m->cluster->AddBlockBlob(new_block_group);
      new_block_group->SetParent(*m->cluster);

      added_to_cues = false;
    }

    // Now put the packet into the cluster.
    render_group->m_more_data = new_block_group->add_frame_auto(track_entry, pack->assigned_timecode - timecode_offset, *data_buffer, lacing_type,
                                                                pack->has_bref() ? pack->bref - timecode_offset : -1,
                                                                pack->has_fref() ? pack->fref - timecode_offset : -1);

    if (has_codec_state) {
      KaxBlockGroup &bgroup = (KaxBlockGroup &)*new_block_group;
      KaxCodecState *cstate = new KaxCodecState;
      bgroup.PushElement(*cstate);
      cstate->CopyBuffer(pack->codec_state->get_buffer(), pack->codec_state->get_size());
    }

    if (-1 == m->first_timecode_in_file)
      m->first_timecode_in_file = pack->assigned_timecode;
    if (-1 == m->first_timecode_in_part)
      m->first_timecode_in_part = pack->assigned_timecode;

    m->min_timecode_in_file      = std::min(timecode_c::ns(pack->assigned_timecode),        m->min_timecode_in_file.value_or_max());
    m->max_timecode_in_file      = std::max(pack->assigned_timecode,                        m->max_timecode_in_file);
    m->max_timecode_and_duration = std::max(pack->assigned_timecode + pack->get_duration(), m->max_timecode_and_duration);

    if (!pack->is_key_frame() || !track_entry.LacingEnabled())
      render_group->m_more_data = false;

    render_group->m_durations.push_back(pack->get_unmodified_duration());
    render_group->m_duration_mandatory |= pack->duration_mandatory;

    cues_c::get().set_duration_for_id_timecode(source->get_track_num(), pack->assigned_timecode - timecode_offset, pack->get_duration());

    if (new_block_group) {
      // Set the reference priority if it was wanted.
      if ((0 < pack->ref_priority) && new_block_group->replace_simple_by_group())
        GetChild<KaxReferencePriority>(*new_block_group).SetValue(pack->ref_priority);

      // Handle BlockAdditions if needed
      if (!pack->data_adds.empty() && new_block_group->ReplaceSimpleByGroup()) {
        KaxBlockAdditions &additions = AddEmptyChild<KaxBlockAdditions>(*new_block_group);

        size_t data_add_idx;
        for (data_add_idx = 0; pack->data_adds.size() > data_add_idx; ++data_add_idx) {
          auto &block_more = AddEmptyChild<KaxBlockMore>(additions);
          GetChild<KaxBlockAddID     >(block_more).SetValue(data_add_idx + 1);
          GetChild<KaxBlockAdditional>(block_more).CopyBuffer((binary *)pack->data_adds[data_add_idx]->get_buffer(), pack->data_adds[data_add_idx]->get_size());
        }
      }

      if (pack->has_discard_padding())
        GetChild<KaxDiscardPadding>(*new_block_group).SetValue(pack->discard_padding.to_ns());
    }

    elements_in_cluster++;

    if (!new_block_group)
      new_block_group = previous_block_group;

    else if (g_write_cues && (!added_to_cues || has_codec_state)) {
      added_to_cues = add_to_cues_maybe(pack);
      if (added_to_cues)
        cues.AddBlockBlob(*new_block_group);
    }

    pack->group = new_block_group;

    m->track_statistics[ source->get_uid() ].process(*pack);
  }

  if (!discarding()) {
    if (0 < elements_in_cluster) {
      for (auto &rg : render_groups)
        set_duration(rg.get());

      m->cluster->SetPreviousTimecode(min_cl_timecode - timecode_offset - 1, (int64_t)g_timecode_scale);
      m->cluster->set_min_timecode(min_cl_timecode - timecode_offset);
      m->cluster->set_max_timecode(max_cl_timecode - timecode_offset);

      m->cluster->Render(*m->out, cues);
      m->bytes_in_file += m->cluster->ElementSize();

      if (g_kax_sh_cues)
        g_kax_sh_cues->IndexThis(*m->cluster, *g_kax_segment);

      m->previous_cluster_tc = m->cluster->GlobalTimecode();

      cues_c::get().postprocess_cues(cues, *m->cluster);

    } else
      m->previous_cluster_tc = -1;
  }

  m->min_timecode_in_cluster = -1;
  m->max_timecode_in_cluster = -1;

  m->cluster->delete_non_blocks();

  return 1;
}
예제 #2
0
/*!
  \todo only put the Blocks written in the cue entries
*/
filepos_t KaxCluster::Render(IOCallback & output, KaxCues & CueToUpdate, bool bSaveDefault)
{
  filepos_t Result = 0;
  size_t Index;
  EBML_MASTER_ITERATOR TrkItr, Itr;

  // update the Timecode of the Cluster before writing
  KaxClusterTimecode * Timecode = static_cast<KaxClusterTimecode *>(this->FindElt(EBML_INFO(KaxClusterTimecode)));
  *static_cast<EbmlUInteger *>(Timecode) = GlobalTimecode() / GlobalTimecodeScale();

  if (Blobs.size() == 0) {
    // old-school direct KaxBlockGroup

    // SilentTracks handling
    // check the parent cluster for existing tracks and see if they are contained in this cluster or not
    if (bSilentTracksUsed) {
      KaxTracks & MyTracks = *static_cast<KaxTracks *>(ParentSegment->FindElt(EBML_INFO(KaxTracks)));
      for (TrkItr = MyTracks.begin(); TrkItr != MyTracks.end(); ++TrkItr) {
        if (EbmlId(*(*TrkItr)) == EBML_ID(KaxTrackEntry)) {
          KaxTrackEntry & entry = *static_cast<KaxTrackEntry *>(*TrkItr);
          uint32 tracknum = entry.TrackNumber();
          for (Itr = begin(); Itr != end(); ++Itr) {
            if (EbmlId(*(*Itr)) == EBML_ID(KaxBlockGroup)) {
              KaxBlockGroup & group = *static_cast<KaxBlockGroup *>(*Itr);
              if (group.TrackNumber() == tracknum)
                break; // this track is used
            }
          }
          // the track wasn't found in this cluster
          if (Itr == end()) {
            KaxClusterSilentTracks * SilentTracks = static_cast<KaxClusterSilentTracks *>(this->FindFirstElt(EBML_INFO(KaxClusterSilentTracks)));
            assert(SilentTracks != NULL); // the flag bSilentTracksUsed should be set when creating the Cluster
            KaxClusterSilentTrackNumber * trackelt = static_cast<KaxClusterSilentTrackNumber *>(SilentTracks->AddNewElt(EBML_INFO(KaxClusterSilentTrackNumber)));
            *static_cast<EbmlUInteger *>(trackelt) = tracknum;
          }
        }
      }
    }

    Result = EbmlMaster::Render(output, bSaveDefault);
    // For all Blocks add their position on the CueEntry

    for (Itr = begin(); Itr != end(); ++Itr) {
      if (EbmlId(*(*Itr)) == EBML_ID(KaxBlockGroup)) {
        CueToUpdate.PositionSet(*static_cast<const KaxBlockGroup *>(*Itr));
      }
    }
  } else {
    // new school, using KaxBlockBlob
    for (Index = 0; Index<Blobs.size(); Index++) {
#if MATROSKA_VERSION >= 2
      if (Blobs[Index]->IsSimpleBlock())
        PushElement( (KaxSimpleBlock&) *Blobs[Index] );
      else
#endif
        PushElement( (KaxBlockGroup&) *Blobs[Index] );
    }

    // SilentTracks handling
    // check the parent cluster for existing tracks and see if they are contained in this cluster or not
    if (bSilentTracksUsed) {
      KaxTracks & MyTracks = *static_cast<KaxTracks *>(ParentSegment->FindElt(EBML_INFO(KaxTracks)));
      for (TrkItr = MyTracks.begin(); TrkItr != MyTracks.end(); ++TrkItr) {
        if (EbmlId(*(*TrkItr)) == EBML_ID(KaxTrackEntry)) {
          KaxTrackEntry & entry = *static_cast<KaxTrackEntry *>(*TrkItr);
          uint32 tracknum = entry.TrackNumber();
          for (Index = 0; Index<Blobs.size(); Index++) {
            if (((KaxInternalBlock&)*Blobs[Index]).TrackNum() == tracknum)
              break; // this track is used
          }
          // the track wasn't found in this cluster
          if (Index == ListSize()) {
            KaxClusterSilentTracks * SilentTracks = static_cast<KaxClusterSilentTracks *>(this->FindFirstElt(EBML_INFO(KaxClusterSilentTracks)));
            assert(SilentTracks != NULL); // the flag bSilentTracksUsed should be set when creating the Cluster
            KaxClusterSilentTrackNumber * trackelt = static_cast<KaxClusterSilentTrackNumber *>(SilentTracks->AddNewElt(EBML_INFO(KaxClusterSilentTrackNumber)));
            *static_cast<EbmlUInteger *>(trackelt) = tracknum;
          }
        }
      }
    }

    Result = EbmlMaster::Render(output, bSaveDefault);

    // For all Blocks add their position on the CueEntry
    for (Index = 0; Index<Blobs.size(); Index++) {
      CueToUpdate.PositionSet(*Blobs[Index]);
    }

    Blobs.clear();
  }

  return Result;
}
예제 #3
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;
}