Index: /trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp	(revision 65352)
+++ /trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp	(revision 65353)
@@ -97,9 +97,9 @@
     /** (Audio) frame buffer. */
     PRTCIRCBUF           pCircBuf;
-    uint8_t             *pvFrameBuf;
-    size_t               cbFrameBufSize;
-    size_t               cbFrameBufUsed;
-    size_t               offFrameBufRead;
-    size_t               offFrameBufWrite;
+    /** Pointer to WebM container to write recorded audio data to.
+     *  See the AVRECMODE enumeration for more information. */
+    WebMWriter          *pWebM;
+    /** Assigned track number from WebM container. */
+    uint8_t              uTrack;
 } AVRECSTREAMOUT, *PAVRECSTREAMOUT;
 
@@ -119,9 +119,4 @@
     /** Recording mode. */
     AVRECMODE            enmMode;
-    /** Pointer to WebM container to write recorded audio data to.
-     *  See the AVRECMODE enumeration for more information. */
-    WebMWriter          *pEBML;
-    /** Track number for audio data. */
-    uint8_t              uTrack;
 } DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC;
 
@@ -144,47 +139,51 @@
 
 #ifdef VBOX_WITH_LIBOPUS
-    RTCircBufCreate(&pStreamOut->pCircBuf, _4K);
+    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
+
+    if (pThis->enmMode == AVRECMODE_AUDIO)
+    {
+        pStreamOut->pWebM = new WebMWriter();
+        rc = pStreamOut->pWebM->Create("/tmp/acap.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, /** @todo Fix path! */
+                                       WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None);
+        if (RT_SUCCESS(rc))
+            rc = pStreamOut->pWebM->AddAudioTrack(44100, 2, 16, &pStreamOut->uTrack);
+    }
+
+    if (RT_FAILURE(rc))
+        return rc;
+
+    rc = RTCircBufCreate(&pStreamOut->pCircBuf, _4K);
+    if (RT_FAILURE(rc))
+        return rc;
 
     rc = DrvAudioHlpStreamCfgToProps(pCfgReq, &pStreamOut->Props);
     if (RT_SUCCESS(rc))
     {
-        size_t cbFrameBuf = sizeof(uint8_t) * _4K; /** @todo Make this configurable */
-
-        pStreamOut->pvFrameBuf = (uint8_t *)RTMemAlloc(cbFrameBuf);
-        if (pStreamOut->pvFrameBuf)
-        {
-            pStreamOut->cbFrameBufSize   = cbFrameBuf;
-            pStreamOut->cbFrameBufUsed   = 0;
-            pStreamOut->offFrameBufRead  = 0;
-            pStreamOut->offFrameBufWrite = 0;
-
-            OpusEncoder *pEnc = NULL;
-
-            int orc;
-            pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc);
-            if (orc != OPUS_OK)
-            {
-                LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
-                return VERR_AUDIO_BACKEND_INIT_FAILED;
-            }
-
-            AssertPtr(pEnc);
-
-    #if 0
-            orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(DrvAudioHlpCalcBitrate()));
-            if (orc != OPUS_OK)
-            {
-                LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc)));
-                rc = VERR_AUDIO_BACKEND_INIT_FAILED;
-            }
-            else
-            {
-    #endif
-                pStreamOut->Codec.Opus.pEnc = pEnc;
-
-                if (pCfgAcq)
-                    pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
-    //      }
+        OpusEncoder *pEnc = NULL;
+
+        int orc;
+        pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc);
+        if (orc != OPUS_OK)
+        {
+            LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
+            return VERR_AUDIO_BACKEND_INIT_FAILED;
         }
+
+        AssertPtr(pEnc);
+
+#if 0
+        orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(DrvAudioHlpCalcBitrate()));
+        if (orc != OPUS_OK)
+        {
+            LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc)));
+            rc = VERR_AUDIO_BACKEND_INIT_FAILED;
+        }
+        else
+        {
+#endif
+        pStreamOut->Codec.Opus.pEnc = pEnc;
+
+        if (pCfgAcq)
+            pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
     }
 #else
@@ -209,5 +208,23 @@
     LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
 
-    AudioMixBufReset(&pStream->MixBuf);
+    switch (enmStreamCmd)
+    {
+        case PDMAUDIOSTREAMCMD_ENABLE:
+        case PDMAUDIOSTREAMCMD_RESUME:
+            break;
+
+        case PDMAUDIOSTREAMCMD_DISABLE:
+        {
+            AudioMixBufReset(&pStream->MixBuf);
+            break;
+        }
+
+        case PDMAUDIOSTREAMCMD_PAUSE:
+            break;
+
+        default:
+            AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
+            break;
+    }
 
     return VINF_SUCCESS;
@@ -234,11 +251,9 @@
         switch (pThis->enmMode)
         {
+            /* In audio-only mode we're creating our own WebM writer instance,
+             * as we don't have to synchronize with any external source, such as video recording data.*/
             case AVRECMODE_AUDIO:
             {
-                pThis->pEBML = new WebMWriter();
-                rc = pThis->pEBML->Create("/tmp/acap.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE, /** @todo Fix path! */
-                                          WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_None);
-                if (RT_SUCCESS(rc))
-                    rc = pThis->pEBML->AddAudioTrack(44100, 2, 16, &pThis->uTrack);
+                rc = VINF_SUCCESS;
                 break;
             }
@@ -333,10 +348,9 @@
 #ifdef VBOX_WITH_LIBOPUS
 
-    /** @todo For now we ASSUME 25 FPS. */
-    uint16_t cbFrameSize = (/*pStreamOut->Props.uHz*/ 48000 / 25 /* FPS */);
+    uint16_t cbFrameSize = 960; /** @todo 20ms worth of audio data. */
 
     PRTCIRCBUF pCircBuf = pStreamOut->pCircBuf;
 
-    while (pStreamOut->cbFrameBufUsed < cbFrameSize)
+    while (RTCircBufUsed(pCircBuf) < cbFrameSize)
     {
         void  *pvBuf;
@@ -400,13 +414,16 @@
             size_t  cbDst = sizeof(abDst);
 
-            /* Call the encoder to encode our input samples. */
-            opus_encoder_ctl(pStreamOut->Codec.Opus.pEnc, OPUS_SET_BITRATE(196000)); /** @todo */
+            /* Call the encoder to encode one frame per iteration. */
+            opus_encoder_ctl(pStreamOut->Codec.Opus.pEnc, OPUS_SET_BITRATE(196000)); /** @todo Only needed for VBR encoding. */
             opus_int32 cbWritten = opus_encode(pStreamOut->Codec.Opus.pEnc,
                                                (opus_int16 *)abSrc, cbSrc, abDst, cbDst);
             if (cbWritten > 0)
             {
+                cbDst = cbWritten;
+                Assert(cbDst <= sizeof(abDst));
+
                 /* Call the WebM writer to actually write the encoded audio data. */
                 WebMWriter::BlockData_Opus blockData = { abDst, cbDst };
-                rc = pThis->pEBML->WriteBlock(pThis->uTrack, &blockData, sizeof(blockData));
+                rc = pStreamOut->pWebM->WriteBlock(pStreamOut->uTrack, &blockData, sizeof(blockData));
                 AssertRC(rc);
             }
@@ -449,11 +466,4 @@
     }
 
-    if (pStreamOut->pvFrameBuf)
-    {
-        Assert(pStreamOut->cbFrameBufSize);
-        RTMemFree(pStreamOut->pvFrameBuf);
-        pStreamOut->pvFrameBuf = NULL;
-    }
-
 #ifdef VBOX_WITH_LIBOPUS
     if (pStreamOut->Codec.Opus.pEnc)
@@ -463,4 +473,18 @@
     }
 #endif
+
+    switch (pThis->enmMode)
+    {
+        case AVRECMODE_AUDIO:
+        {
+            if (pStreamOut->pWebM)
+                pStreamOut->pWebM->Close();
+            break;
+        }
+
+        case AVRECMODE_AUDIO_VIDEO:
+        default:
+            break;
+    }
 
     return VINF_SUCCESS;
@@ -494,31 +518,11 @@
     PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
 
-    int rc;
-
     switch (pThis->enmMode)
     {
         case AVRECMODE_AUDIO:
-        {
-            if (pThis->pEBML)
-            {
-                pThis->pEBML->Close();
-
-                delete pThis->pEBML;
-                pThis->pEBML = NULL;
-            }
+        case AVRECMODE_AUDIO_VIDEO:
+        default:
             break;
-        }
-
-        case AVRECMODE_AUDIO_VIDEO:
-        {
-            break;
-        }
-
-        default:
-            rc = VERR_NOT_SUPPORTED;
-            break;
-    }
-
-    LogFlowFuncLeaveRC(rc);
+    }
 }
 
Index: unk/src/VBox/Main/src-client/EbmlIDs.h
===================================================================
--- /trunk/src/VBox/Main/src-client/EbmlIDs.h	(revision 65352)
+++ 	(revision )
@@ -1,248 +1,0 @@
-/* $Id$ */
-/** @file
- * EbmlIDs.h - Matroska EBML Class IDs.
- */
-
-/*
- * Copyright (C) 2013-2016 Oracle Corporation
- *
- * This file is part of VirtualBox Open Source Edition (OSE), as
- * available from http://www.virtualbox.org. This file is free software;
- * you can redistribute it and/or modify it under the terms of the GNU
- * General Public License (GPL) as published by the Free Software
- * Foundation, in version 2 as it comes in the "COPYING" file of the
- * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
- * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
- *
- *
- * This code is based on:
- *
- * Copyright (c) 2010 The WebM project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS.  All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-
-/** Matroska EBML Class IDs supported by WebM */
-enum Mkv
-{
-    EBML = 0x1A45DFA3,
-    EBMLVersion = 0x4286,
-    EBMLReadVersion = 0x42F7,
-    EBMLMaxIDLength = 0x42F2,
-    EBMLMaxSizeLength = 0x42F3,
-    DocType = 0x4282,
-    DocTypeVersion = 0x4287,
-    DocTypeReadVersion = 0x4285,
-//  CRC_32 = 0xBF,
-    Void = 0xEC,
-    SignatureSlot = 0x1B538667,
-    SignatureAlgo = 0x7E8A,
-    SignatureHash = 0x7E9A,
-    SignaturePublicKey = 0x7EA5,
-    Signature = 0x7EB5,
-    SignatureElements = 0x7E5B,
-    SignatureElementList = 0x7E7B,
-    SignedElement = 0x6532,
-    //segment
-    Segment = 0x18538067,
-    //Meta Seek Information
-    SeekHead = 0x114D9B74,
-    Seek = 0x4DBB,
-    SeekID = 0x53AB,
-    SeekPosition = 0x53AC,
-    //Segment Information
-    Info = 0x1549A966,
-//  SegmentUID = 0x73A4,
-//  SegmentFilename = 0x7384,
-//  PrevUID = 0x3CB923,
-//  PrevFilename = 0x3C83AB,
-//  NextUID = 0x3EB923,
-//  NextFilename = 0x3E83BB,
-//  SegmentFamily = 0x4444,
-//  ChapterTranslate = 0x6924,
-//  ChapterTranslateEditionUID = 0x69FC,
-//  ChapterTranslateCodec = 0x69BF,
-//  ChapterTranslateID = 0x69A5,
-    TimecodeScale = 0x2AD7B1,
-    Segment_Duration = 0x4489,
-    DateUTC = 0x4461,
-//  Title = 0x7BA9,
-    MuxingApp = 0x4D80,
-    WritingApp = 0x5741,
-    //Cluster
-    Cluster = 0x1F43B675,
-    Timecode = 0xE7,
-//  SilentTracks = 0x5854,
-//  SilentTrackNumber = 0x58D7,
-//  Position = 0xA7,
-    PrevSize = 0xAB,
-    BlockGroup = 0xA0,
-    Block = 0xA1,
-//  BlockVirtual = 0xA2,
-//  BlockAdditions = 0x75A1,
-//  BlockMore = 0xA6,
-//  BlockAddID = 0xEE,
-//  BlockAdditional = 0xA5,
-    BlockDuration = 0x9B,
-//  ReferencePriority = 0xFA,
-    ReferenceBlock = 0xFB,
-//  ReferenceVirtual = 0xFD,
-//  CodecState = 0xA4,
-//  Slices = 0x8E,
-//  TimeSlice = 0xE8,
-    LaceNumber = 0xCC,
-//  FrameNumber = 0xCD,
-//  BlockAdditionID = 0xCB,
-//  MkvDelay = 0xCE,
-//  Cluster_Duration = 0xCF,
-    SimpleBlock = 0xA3,
-//  EncryptedBlock = 0xAF,
-    //Track
-    Tracks = 0x1654AE6B,
-    TrackEntry = 0xAE,
-    TrackNumber = 0xD7,
-    TrackUID = 0x73C5,
-    TrackType = 0x83,
-    FlagEnabled = 0xB9,
-    FlagDefault = 0x88,
-    FlagForced = 0x55AA,
-    FlagLacing = 0x9C,
-//  MinCache = 0x6DE7,
-//  MaxCache = 0x6DF8,
-    DefaultDuration = 0x23E383,
-//  TrackTimecodeScale = 0x23314F,
-//  TrackOffset = 0x537F,
-//  MaxBlockAdditionID = 0x55EE,
-    Name = 0x536E,
-    Language = 0x22B59C,
-    CodecID = 0x86,
-    CodecPrivate = 0x63A2,
-    CodecName = 0x258688,
-//  AttachmentLink = 0x7446,
-//  CodecSettings = 0x3A9697,
-//  CodecInfoURL = 0x3B4040,
-//  CodecDownloadURL = 0x26B240,
-//  CodecDecodeAll = 0xAA,
-//  TrackOverlay = 0x6FAB,
-//  TrackTranslate = 0x6624,
-//  TrackTranslateEditionUID = 0x66FC,
-//  TrackTranslateCodec = 0x66BF,
-//  TrackTranslateTrackID = 0x66A5,
-    CodecDelay = 0x56AA,
-    SeekPreRoll = 0x56BB,
-    //video
-    Video = 0xE0,
-    FlagInterlaced = 0x9A,
-//  StereoMode = 0x53B8,
-    PixelWidth = 0xB0,
-    PixelHeight = 0xBA,
-    PixelCropBottom = 0x54AA,
-    PixelCropTop = 0x54BB,
-    PixelCropLeft = 0x54CC,
-    PixelCropRight = 0x54DD,
-    DisplayWidth = 0x54B0,
-    DisplayHeight = 0x54BA,
-    DisplayUnit = 0x54B2,
-    AspectRatioType = 0x54B3,
-//  ColourSpace = 0x2EB524,
-//  GammaValue = 0x2FB523,
-    FrameRate = 0x2383E3,
-    //end video
-    //audio
-    Audio = 0xE1,
-    SamplingFrequency = 0xB5,
-    OutputSamplingFrequency = 0x78B5,
-    Channels = 0x9F,
-//  ChannelPositions = 0x7D7B,
-    BitDepth = 0x6264,
-    //end audio
-    //content encoding
-//  ContentEncodings = 0x6d80,
-//  ContentEncoding = 0x6240,
-//  ContentEncodingOrder = 0x5031,
-//  ContentEncodingScope = 0x5032,
-//  ContentEncodingType = 0x5033,
-//  ContentCompression = 0x5034,
-//  ContentCompAlgo = 0x4254,
-//  ContentCompSettings = 0x4255,
-//  ContentEncryption = 0x5035,
-//  ContentEncAlgo = 0x47e1,
-//  ContentEncKeyID = 0x47e2,
-//  ContentSignature = 0x47e3,
-//  ContentSigKeyID = 0x47e4,
-//  ContentSigAlgo = 0x47e5,
-//  ContentSigHashAlgo = 0x47e6,
-    //end content encoding
-    //Cueing Data
-    Cues = 0x1C53BB6B,
-    CuePoint = 0xBB,
-    CueTime = 0xB3,
-    CueTrackPositions = 0xB7,
-    CueTrack = 0xF7,
-    CueClusterPosition = 0xF1,
-    CueBlockNumber = 0x5378
-//  CueCodecState = 0xEA,
-//  CueReference = 0xDB,
-//  CueRefTime = 0x96,
-//  CueRefCluster = 0x97,
-//  CueRefNumber = 0x535F,
-//  CueRefCodecState = 0xEB,
-    //Attachment
-//  Attachments = 0x1941A469,
-//  AttachedFile = 0x61A7,
-//  FileDescription = 0x467E,
-//  FileName = 0x466E,
-//  FileMimeType = 0x4660,
-//  FileData = 0x465C,
-//  FileUID = 0x46AE,
-//  FileReferral = 0x4675,
-    //Chapters
-//  Chapters = 0x1043A770,
-//  EditionEntry = 0x45B9,
-//  EditionUID = 0x45BC,
-//  EditionFlagHidden = 0x45BD,
-//  EditionFlagDefault = 0x45DB,
-//  EditionFlagOrdered = 0x45DD,
-//  ChapterAtom = 0xB6,
-//  ChapterUID = 0x73C4,
-//  ChapterTimeStart = 0x91,
-//  ChapterTimeEnd = 0x92,
-//  ChapterFlagHidden = 0x98,
-//  ChapterFlagEnabled = 0x4598,
-//  ChapterSegmentUID = 0x6E67,
-//  ChapterSegmentEditionUID = 0x6EBC,
-//  ChapterPhysicalEquiv = 0x63C3,
-//  ChapterTrack = 0x8F,
-//  ChapterTrackNumber = 0x89,
-//  ChapterDisplay = 0x80,
-//  ChapString = 0x85,
-//  ChapLanguage = 0x437C,
-//  ChapCountry = 0x437E,
-//  ChapProcess = 0x6944,
-//  ChapProcessCodecID = 0x6955,
-//  ChapProcessPrivate = 0x450D,
-//  ChapProcessCommand = 0x6911,
-//  ChapProcessTime = 0x6922,
-//  ChapProcessData = 0x6933,
-    //Tagging
-//  Tags = 0x1254C367,
-//  Tag = 0x7373,
-//  Targets = 0x63C0,
-//  TargetTypeValue = 0x68CA,
-//  TargetType = 0x63CA,
-//  Tagging_TrackUID = 0x63C5,
-//  Tagging_EditionUID = 0x63C9,
-//  Tagging_ChapterUID = 0x63C4,
-//  AttachmentUID = 0x63C6,
-//  SimpleTag = 0x67C8,
-//  TagName = 0x45A3,
-//  TagLanguage = 0x447A,
-//  TagDefault = 0x4484,
-//  TagString = 0x4487,
-//  TagBinary = 0x4485,
-};
Index: /trunk/src/VBox/Main/src-client/EbmlMkvIDs.h
===================================================================
--- /trunk/src/VBox/Main/src-client/EbmlMkvIDs.h	(revision 65353)
+++ /trunk/src/VBox/Main/src-client/EbmlMkvIDs.h	(revision 65353)
@@ -0,0 +1,83 @@
+/* $Id$ */
+/** @file
+ * EbmlMkvIDs.h - Matroska EBML Class IDs.
+ */
+
+/*
+ * Copyright (C) 2017 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/** Matroska EBML Class IDs supported by WebM.
+ *
+ *  Keep the structure clean and group elements where it makes sense
+ *  for easier reading / lookup. */
+enum MkvElem
+{
+    MkvElem_EBML                    = 0x1A45DFA3,
+    MkvElem_EBMLVersion             = 0x4286,
+    MkvElem_EBMLReadVersion         = 0x42F7,
+    MkvElem_EBMLMaxIDLength         = 0x42F2,
+    MkvElem_EBMLMaxSizeLength       = 0x42F3,
+
+    MkvElem_DocType                 = 0x4282,
+    MkvElem_DocTypeVersion          = 0x4287,
+    MkvElem_DocTypeReadVersion      = 0x4285,
+
+    MkvElem_Segment                 = 0x18538067,
+    MkvElem_Segment_Duration        = 0x4489,
+
+    MkvElem_SeekHead                = 0x114D9B74,
+    MkvElem_Seek                    = 0x4DBB,
+    MkvElem_SeekID                  = 0x53AB,
+    MkvElem_SeekPosition            = 0x53AC,
+
+    MkvElem_Info                    = 0x1549A966,
+    MkvElem_TimecodeScale           = 0x2AD7B1,
+    MkvElem_MuxingApp               = 0x4D80,
+    MkvElem_WritingApp              = 0x5741,
+
+    MkvElem_Tracks                  = 0x1654AE6B,
+    MkvElem_TrackEntry              = 0xAE,
+    MkvElem_TrackNumber             = 0xD7,
+    MkvElem_TrackUID                = 0x73C5,
+    MkvElem_TrackType               = 0x83,
+
+    MkvElem_Cluster                 = 0x1F43B675,
+    MkvElem_Timecode                = 0xE7,
+
+    MkvElem_SimpleBlock             = 0xA3,
+
+    MkvElem_SeekPreRoll             = 0x56BB,
+
+    MkvElem_CodecID                 = 0x86,
+    MkvElem_CodecDelay              = 0x56AA,
+    MkvElem_CodecPrivate            = 0x63A2,
+    MkvElem_CodecName               = 0x258688,
+
+    MkvElem_Video                   = 0xE0,
+    MkvElem_PixelWidth              = 0xB0,
+    MkvElem_PixelHeight             = 0xBA,
+    MkvElem_FrameRate               = 0x2383E3,
+
+    MkvElem_Audio                   = 0xE1,
+    MkvElem_SamplingFrequency       = 0xB5,
+    MkvElem_OutputSamplingFrequency = 0x78B5,
+    MkvElem_Channels                = 0x9F,
+    MkvElem_BitDepth                = 0x6264,
+
+    MkvElem_Cues                    = 0x1C53BB6B,
+    MkvElem_CuePoint                = 0xBB,
+    MkvElem_CueTime                 = 0xB3,
+    MkvElem_CueTrackPositions       = 0xB7,
+    MkvElem_CueTrack                = 0xF7,
+    MkvElem_CueClusterPosition      = 0xF1
+};
+
Index: /trunk/src/VBox/Main/src-client/EbmlWriter.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/EbmlWriter.cpp	(revision 65352)
+++ /trunk/src/VBox/Main/src-client/EbmlWriter.cpp	(revision 65353)
@@ -32,5 +32,5 @@
 
 #include "EbmlWriter.h"
-#include "EbmlIDs.h"
+#include "EbmlMkvIDs.h"
 #include "Logging.h"
 
@@ -143,5 +143,5 @@
         RTFileSeek(m_File, m_Elements.top().offset, RTFILE_SEEK_BEGIN, NULL);
 
-        /* make sure that size will be serialized as uint64 */
+        /* Make sure that size will be serialized as uint64_t. */
         writeUnsignedInteger(uSize | UINT64_C(0x0100000000000000));
         RTFileSeek(m_File, uPos, RTFILE_SEEK_BEGIN, NULL);
@@ -292,5 +292,5 @@
 
     /**
-     * Structure for keeping a track entry.
+     * Structure for keeping a WebM track entry.
      */
     struct WebMTrack
@@ -299,5 +299,7 @@
             : enmType(a_enmType)
             , uTrack(a_uTrack)
-            , offID(a_offID)
+            , offUUID(a_offID)
+            , cTotalClusters(0)
+            , cTotalBlocks(0)
         {
             uUUID = RTRandU32();
@@ -315,5 +317,7 @@
                 /** Sample rate the codec is using. */
                 uint16_t uHzCodec;
-                /** Frame size (in bytes), based on the codec sample rate. */
+                /** Frame size (in bytes), based on the codec sample rate.
+                 *  Note: For now this does *not* change dynamically, in other words,
+                 *        we do CBR (and not VBR) here! */
                 size_t   cbFrameSize;
             } Audio;
@@ -324,8 +328,72 @@
          *  Needed in case this track gets mux'ed with tracks from other files. Not really unique though. */
         uint32_t      uUUID;
-        /** Absolute offset in file of track ID.
+        /** Absolute offset in file of track UUID.
          *  Needed to write the hash sum within the footer. */
-        uint64_t      offID;
+        uint64_t      offUUID;
+        /** Total number of clusters. */
+        uint64_t      cTotalClusters;
+        /** Total number of blocks. */
+        uint64_t      cTotalBlocks;
     };
+
+    /**
+     * Structure for keeping a WebM cluster entry.
+     */
+    struct WebMCluster
+    {
+        WebMCluster(void)
+            : uID(0)
+            , offCluster(0)
+            , fOpen(false)
+            , tsStart(0)
+            , tsEnd(0) { }
+
+        uint64_t      uID;
+        /** Absolute offset (in bytes) of current cluster.
+         *  Needed for seeking info table. */
+        uint64_t      offCluster;
+        bool          fOpen;
+        uint64_t      tsStart;
+        uint64_t      tsEnd;
+    };
+
+    /**
+     * Structure for keeping a WebM segment entry.
+     *
+     * Current we're only using one segment.
+     */
+    struct WebMSegment
+    {
+        WebMSegment(void)
+            : offStart(0)
+            , offInfo(0)
+            , offSeekInfo(0)
+            , offTracks(0)
+            , offCues(0) { }
+
+        /** Absolute offset (in bytes) of CurSeg. */
+        uint64_t                        offStart;
+        /** Absolute offset (in bytes) of general info. */
+        uint64_t                        offInfo;
+        /** Absolute offset (in bytes) of seeking info. */
+        uint64_t                        offSeekInfo;
+        /** Absolute offset (in bytes) of tracks. */
+        uint64_t                        offTracks;
+        /** Absolute offset (in bytes) of cues table. */
+        uint64_t                        offCues;
+        /** List of cue points. Needed for seeking table. */
+        std::list<WebMCueEntry>         lstCues;
+
+        /** Map of tracks.
+         *  The key marks the track number (*not* the UUID!). */
+        std::map <uint8_t, WebMTrack *> mapTracks;
+
+        /** Current cluster which is being handled.
+         *
+         *  Note that we don't need (and shouldn't need, as this can be a *lot* of data!) a
+         *  list of all clusters. */
+        WebMCluster                     CurCluster;
+
+    } CurSeg;
 
 #ifdef VBOX_WITH_LIBOPUS
@@ -369,33 +437,13 @@
     int64_t                     m_tsLastPtsMs;
 
-    /** Start offset (in bytes) of current segment. */
-    uint64_t                    m_offSegCurStart;
-
-    /** Start offset (in bytes) of seeking info segment. */
-    uint64_t                    m_offSegSeekInfoStart;
-    /** Start offset (in bytes) of general info segment. */
-    uint64_t                    m_offSegInfoStart;
-
-    /** Start offset (in bytes) of tracks segment. */
-    uint64_t                    m_offSegTracksStart;
-
-    /** Absolute position of cue segment. */
-    uint64_t                    m_offSegCuesStart;
-    /** List of cue points. Needed for seeking table. */
-    std::list<WebMCueEntry>     m_lstCue;
-
-    /** Map of tracks.
-     *  The key marks the track number (*not* the UID!). */
-    std::map <uint8_t, WebMTrack *> m_mapTracks;
-    /** Whether we're currently in an opened tracks segment. */
-    bool                        m_fTracksOpen;
-
-    /** Timestamp (in ms) when the current cluster has been opened. */
-    uint32_t                    m_tsClusterOpenMs;
-    /** Whether we're currently in an opened cluster segment. */
-    bool                        m_fClusterOpen;
-    /** Absolute position (in bytes) of current cluster within file.
-     *  Needed for seeking info table. */
-    uint64_t                    m_offSegClusterStart;
+    /** Whether we're currently in the tracks section. */
+    bool                        m_fInTracksSection;
+
+    /** Size of timecodes in bytes. */
+    size_t                      m_cbTimecode;
+    /** Maximum value a timecode can have. */
+    uint32_t                    m_uTimecodeMax;
+    /** The timecode scale factor. */
+    uint64_t                    m_uTimecodeScaleNs;
 
     Ebml                        m_Ebml;
@@ -411,13 +459,14 @@
         , m_tsInitialPtsMs(-1)
         , m_tsLastPtsMs(-1)
-        , m_offSegCurStart(0)
-        , m_offSegSeekInfoStart(0)
-        , m_offSegInfoStart(0)
-        , m_offSegTracksStart(0)
-        , m_offSegCuesStart(0)
-        , m_fTracksOpen(false)
-        , m_tsClusterOpenMs(0)
-        , m_fClusterOpen(false)
-        , m_offSegClusterStart(0) {}
+        , m_fInTracksSection(false)
+    {
+        /* Size (in bytes) of time code to write. We use 2 bytes (16 bit) by default. */
+        m_cbTimecode   = 2;
+        m_uTimecodeMax = UINT16_MAX;
+
+        /* This is the default for WebM -- all timecodes in the segments are expressed in ms.
+         * This allows every cluster to have blocks with positive values up to 32.767 seconds. */
+        m_uTimecodeScaleNs = 1000000;
+    }
 
     virtual ~WebMWriter_Impl()
@@ -429,13 +478,13 @@
     {
 #ifdef VBOX_WITH_LIBOPUS
-        m_Ebml.subStart(TrackEntry);
-        m_Ebml.serializeUnsignedInteger(TrackNumber, (uint8_t)m_mapTracks.size());
+        m_Ebml.subStart(MkvElem_TrackEntry);
+        m_Ebml.serializeUnsignedInteger(MkvElem_TrackNumber, (uint8_t)CurSeg.mapTracks.size());
         /** @todo Implement track's "Language" property? Currently this defaults to English ("eng"). */
 
-        uint8_t uTrack = (uint8_t)m_mapTracks.size();
+        uint8_t uTrack = (uint8_t)CurSeg.mapTracks.size();
 
         WebMTrack *pTrack = new WebMTrack(WebMTrackType_Audio, uTrack, RTFileTell(m_Ebml.getFile()));
 
-        /* Clamp the codec rate value if it reaches a certain threshold. */
+        /* Clamp the codec rate (Hz) value if it reaches a certain threshold. */
         if      (uHz > 24000) pTrack->Audio.uHzCodec = 48000;
         else if (uHz > 16000) pTrack->Audio.uHzCodec = 24000;
@@ -446,24 +495,24 @@
         pTrack->Audio.uHzIn = uHz;
 
-        /** @todo 1920 bytes is 40ms worth of audio data. Make this configurable. */
-        pTrack->Audio.cbFrameSize =  1920 / (48000 / pTrack->Audio.uHzCodec);
+        /** @todo 960 bytes is 20ms worth of audio data. Make this configurable. */
+        pTrack->Audio.cbFrameSize = 960 /* Bytes */ / (48000 /* Hz */ / pTrack->Audio.uHzCodec);
 
         /** @todo Resolve codec type. */
         OpusPrivData opusPrivData(uHz, cChannels);
 
-        m_Ebml.serializeUnsignedInteger(TrackUID, pTrack->uUUID, 4)
-              .serializeUnsignedInteger(TrackType, 2 /* Audio */)
-              .serializeString(CodecID, "A_OPUS")
-              .serializeData(CodecPrivate, &opusPrivData, sizeof(opusPrivData))
-              .serializeUnsignedInteger(CodecDelay, 0)
-              .serializeUnsignedInteger(SeekPreRoll, 80000000)
-              .subStart(Audio)
-                  .serializeFloat(SamplingFrequency,  (float)pTrack->Audio.uHzCodec)
-                  .serializeUnsignedInteger(Channels, cChannels)
-                  .serializeUnsignedInteger(BitDepth, cBits)
-              .subEnd(Audio)
-              .subEnd(TrackEntry);
-
-        m_mapTracks[uTrack] = pTrack;
+        m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID, 4)
+              .serializeUnsignedInteger(MkvElem_TrackType, 2 /* Audio */)
+              .serializeString(MkvElem_CodecID, "A_OPUS")
+              .serializeData(MkvElem_CodecPrivate, &opusPrivData, sizeof(opusPrivData))
+              .serializeUnsignedInteger(MkvElem_CodecDelay,  0)
+              .serializeUnsignedInteger(MkvElem_SeekPreRoll, 80000000) /* 80.000ms */
+              .subStart(MkvElem_Audio)
+                  .serializeFloat(MkvElem_SamplingFrequency,  (float)pTrack->Audio.uHzCodec)
+                  .serializeUnsignedInteger(MkvElem_Channels, cChannels)
+                  .serializeUnsignedInteger(MkvElem_BitDepth, cBits)
+              .subEnd(MkvElem_Audio)
+              .subEnd(MkvElem_TrackEntry);
+
+        CurSeg.mapTracks[uTrack] = pTrack;
 
         if (puTrack)
@@ -479,23 +528,23 @@
     int AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS, uint8_t *puTrack)
     {
-        m_Ebml.subStart(TrackEntry);
-        m_Ebml.serializeUnsignedInteger(TrackNumber, (uint8_t)m_mapTracks.size());
-
-        uint8_t uTrack = (uint8_t)m_mapTracks.size();
+        m_Ebml.subStart(MkvElem_TrackEntry);
+        m_Ebml.serializeUnsignedInteger(MkvElem_TrackNumber, (uint8_t)CurSeg.mapTracks.size());
+
+        uint8_t uTrack = (uint8_t)CurSeg.mapTracks.size();
 
         WebMTrack *pTrack = new WebMTrack(WebMTrackType_Video, uTrack, RTFileTell(m_Ebml.getFile()));
 
         /** @todo Resolve codec type. */
-        m_Ebml.serializeUnsignedInteger(TrackUID, pTrack->uUUID /* UID */, 4)
-              .serializeUnsignedInteger(TrackType, 1 /* Video */)
-              .serializeString(CodecID, "V_VP8")
-              .subStart(Video)
-              .serializeUnsignedInteger(PixelWidth, uWidth)
-              .serializeUnsignedInteger(PixelHeight, uHeight)
-              .serializeFloat(FrameRate, dbFPS)
-              .subEnd(Video)
-              .subEnd(TrackEntry);
-
-        m_mapTracks[uTrack] = pTrack;
+        m_Ebml.serializeUnsignedInteger(MkvElem_TrackUID, pTrack->uUUID /* UID */, 4)
+              .serializeUnsignedInteger(MkvElem_TrackType, 1 /* Video */)
+              .serializeString(MkvElem_CodecID, "V_VP8")
+              .subStart(MkvElem_Video)
+              .serializeUnsignedInteger(MkvElem_PixelWidth, uWidth)
+              .serializeUnsignedInteger(MkvElem_PixelHeight, uHeight)
+              .serializeFloat(MkvElem_FrameRate, dbFPS)
+              .subEnd(MkvElem_Video)
+              .subEnd(MkvElem_TrackEntry);
+
+        CurSeg.mapTracks[uTrack] = pTrack;
 
         if (puTrack)
@@ -509,55 +558,62 @@
         LogFunc(("Header @ %RU64\n", RTFileTell(m_Ebml.getFile())));
 
-        m_Ebml.subStart(EBML)
-              .serializeUnsignedInteger(EBMLVersion, 1)
-              .serializeUnsignedInteger(EBMLReadVersion, 1)
-              .serializeUnsignedInteger(EBMLMaxIDLength, 4)
-              .serializeUnsignedInteger(EBMLMaxSizeLength, 8)
-              .serializeString(DocType, "webm")
-              .serializeUnsignedInteger(DocTypeVersion, 2)
-              .serializeUnsignedInteger(DocTypeReadVersion, 2)
-              .subEnd(EBML);
-
-        m_Ebml.subStart(Segment);
+        m_Ebml.subStart(MkvElem_EBML)
+              .serializeUnsignedInteger(MkvElem_EBMLVersion, 1)
+              .serializeUnsignedInteger(MkvElem_EBMLReadVersion, 1)
+              .serializeUnsignedInteger(MkvElem_EBMLMaxIDLength, 4)
+              .serializeUnsignedInteger(MkvElem_EBMLMaxSizeLength, 8)
+              .serializeString(MkvElem_DocType, "webm")
+              .serializeUnsignedInteger(MkvElem_DocTypeVersion, 2)
+              .serializeUnsignedInteger(MkvElem_DocTypeReadVersion, 2)
+              .subEnd(MkvElem_EBML);
+
+        m_Ebml.subStart(MkvElem_Segment);
 
         /* Save offset of current segment. */
-        m_offSegCurStart = RTFileTell(m_Ebml.getFile());
-
-        writeSeekInfo();
+        CurSeg.offStart = RTFileTell(m_Ebml.getFile());
+
+        writeSegSeekInfo();
 
         /* Save offset of upcoming tracks segment. */
-        m_offSegTracksStart = RTFileTell(m_Ebml.getFile());
+        CurSeg.offTracks = RTFileTell(m_Ebml.getFile());
 
         /* The tracks segment starts right after this header. */
-        m_Ebml.subStart(Tracks);
-        m_fTracksOpen = true;
+        m_Ebml.subStart(MkvElem_Tracks);
+        m_fInTracksSection = true;
 
         return VINF_SUCCESS;
     }
 
-    int writeSimpleBlockInternal(uint8_t uTrackID, uint16_t uTimecode, const void *pvData, size_t cbData, uint8_t fFlags)
-    {
-        LogFunc(("SimpleBlock @ %RU64 (T%RU8, TS=%RU16, %zu bytes)\n", RTFileTell(m_Ebml.getFile()), uTrackID, uTimecode, cbData));
+    int writeSimpleBlockInternal(WebMTrack *a_pTrack, uint64_t a_uTimecode,
+                                 const void *a_pvData, size_t a_cbData, uint8_t a_fFlags)
+    {
+        LogFunc(("SimpleBlock @ %RU64 (T%RU8, TS=%RU64, %zu bytes)\n",
+                 RTFileTell(m_Ebml.getFile()), a_pTrack->uTrack, a_uTimecode, a_cbData));
+
+        /** @todo Mask out non-valid timecode bits, e.g. the upper 48 bits for a (default) 16-bit timecode. */
+        Assert(a_uTimecode <= m_uTimecodeMax);
 
         /* Write a "Simple Block". */
-        m_Ebml.writeClassId(SimpleBlock);
+        m_Ebml.writeClassId(MkvElem_SimpleBlock);
         /* Block size. */
-        m_Ebml.writeUnsignedInteger(0x10000000u | (1      /* Track number. */
-                                    + 2      /* Timecode .*/
-                                    + 1      /* Flags. */
-                                    + cbData /* Actual frame data. */),  4);
+        m_Ebml.writeUnsignedInteger(0x10000000u | (  1            /* Track number size. */
+                                                   + m_cbTimecode /* Timecode size .*/
+                                                   + 1            /* Flags size. */
+                                                   + a_cbData     /* Actual frame data size. */),  4);
         /* Track number. */
-        m_Ebml.writeSize(uTrackID);
+        m_Ebml.writeSize(a_pTrack->uTrack);
         /* Timecode (relative to cluster opening timecode). */
-        m_Ebml.writeUnsignedInteger(uTimecode, 2);
+        m_Ebml.writeUnsignedInteger(a_uTimecode, m_cbTimecode);
         /* Flags. */
-        m_Ebml.writeUnsignedInteger(fFlags, 1);
+        m_Ebml.writeUnsignedInteger(a_fFlags, 1);
         /* Frame data. */
-        m_Ebml.write(pvData, cbData);
+        m_Ebml.write(a_pvData, a_cbData);
+
+        a_pTrack->cTotalBlocks++;
 
         return VINF_SUCCESS;
     }
 
-    int writeBlockVP8(const WebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
+    int writeBlockVP8(WebMTrack *a_pTrack, const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
     {
         RT_NOREF(a_pTrack);
@@ -579,10 +635,11 @@
         bool     fClusterStart = false;
 
-        if (tsPtsMs - m_tsClusterOpenMs > 65536)
+        /* Did we reach the maximum our timecode can hold? Use a new cluster then. */
+        if (tsPtsMs - CurSeg.CurCluster.tsStart > m_uTimecodeMax)
             fClusterStart = true;
         else
         {
-            /* Calculate the block's timecode, which is relative to the Cluster timecode. */
-            tsBlockMs = static_cast<uint16_t>(tsPtsMs - m_tsClusterOpenMs);
+            /* Calculate the block's timecode, which is relative to the current cluster's starting timecode. */
+            tsBlockMs = static_cast<uint16_t>(tsPtsMs - CurSeg.CurCluster.tsStart);
         }
 
@@ -592,24 +649,31 @@
             || fKeyframe)
         {
-            if (m_fClusterOpen) /* Close current cluster first. */
-                m_Ebml.subEnd(Cluster);
+            WebMCluster &Cluster = CurSeg.CurCluster;
+
+            a_pTrack->cTotalClusters++;
+
+            if (Cluster.fOpen) /* Close current cluster first. */
+            {
+                m_Ebml.subEnd(MkvElem_Cluster);
+                Cluster.fOpen = false;
+            }
 
             tsBlockMs = 0;
 
             /* Open a new cluster. */
-            m_fClusterOpen       = true;
-            m_tsClusterOpenMs    = (uint32_t)tsPtsMs;
-            m_offSegClusterStart = RTFileTell(m_Ebml.getFile());
-
-            LogFunc(("Cluster @ %RU64\n", m_offSegClusterStart));
-
-            m_Ebml.subStart(Cluster)
-                  .serializeUnsignedInteger(Timecode, m_tsClusterOpenMs);
+            Cluster.fOpen      = true;
+            Cluster.tsStart    = tsPtsMs;
+            Cluster.offCluster = RTFileTell(m_Ebml.getFile());
+
+            LogFunc(("Cluster @ %RU64\n", Cluster.offCluster));
+
+            m_Ebml.subStart(MkvElem_Cluster)
+                  .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tsStart);
 
             /* Save a cue point if this is a keyframe. */
             if (fKeyframe)
             {
-                WebMCueEntry cue(m_tsClusterOpenMs, m_offSegClusterStart);
-                m_lstCue.push_back(cue);
+                WebMCueEntry cue(Cluster.tsStart, Cluster.offCluster);
+                CurSeg.lstCues.push_back(cue);
             }
         }
@@ -621,30 +685,72 @@
             fFlags |= 0x08; /* Invisible frame. */
 
-        return writeSimpleBlockInternal(a_pTrack->uTrack, tsBlockMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags);
+        return writeSimpleBlockInternal(a_pTrack, tsBlockMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags);
     }
 
 #ifdef VBOX_WITH_LIBOPUS
     /* Audio blocks that have same absolute timecode as video blocks SHOULD be written before the video blocks. */
-    int writeBlockOpus(const WebMTrack *a_pTrack, const void *pvData, size_t cbData)
+    int writeBlockOpus(WebMTrack *a_pTrack, const void *pvData, size_t cbData)
     {
         AssertPtrReturn(a_pTrack, VERR_INVALID_POINTER);
-        AssertReturn(a_pTrack->Audio.cbFrameSize == cbData, VERR_INVALID_PARAMETER);
-
-static uint16_t s_uTimecode = 0;
-
-        if (!m_fClusterOpen)
-        {
-            m_Ebml.subStart(Cluster)
-                  .serializeUnsignedInteger(Timecode, 0);
-            m_fClusterOpen = true;
+        AssertPtrReturn(pvData, VERR_INVALID_POINTER);
+        AssertReturn(cbData, VERR_INVALID_PARAMETER);
+
+        //static uint64_t s_uTimecode = 0;
+        /* For now this is constant while encoding (CBR), but might change to VBR later. */
+        //s_uTimecode += a_pTrack->Audio.cbFrameSize * 48000 / a_pTrack->Audio.uHzCodec;
+
+        //static uint64_t s_uPts = 0;
+
+        int64_t tsPtsMs = 1000000000 * a_pTrack->cTotalBlocks / 48000;
+
+        m_tsLastPtsMs = tsPtsMs;
+
+        if (m_tsInitialPtsMs < 0)
+            m_tsInitialPtsMs = m_tsLastPtsMs;
+
+        WebMCluster &Cluster = CurSeg.CurCluster;
+
+        /* Calculate the relative time of this block. */
+        uint16_t tsBlockMs     = 0;
+        bool     fClusterStart = false;
+
+        if (a_pTrack->cTotalBlocks == 0)
+            fClusterStart = true;
+
+        /* Did we reach the maximum our timecode can hold? Use a new cluster then. */
+        if (tsPtsMs - Cluster.tsStart > m_uTimecodeMax)
+            fClusterStart = true;
+        else
+        {
+            /* Calculate the block's timecode, which is relative to the Cluster timecode. */
+            tsBlockMs = static_cast<uint16_t>(tsPtsMs - Cluster.tsStart);
         }
 
-        uint64_t ts = (s_uTimecode * 1000 * 1000 * 1000) / 48000;
-
-        LogFunc(("ts=%RU64 (timecode %RU16)\n", ts, s_uTimecode));
-
-        s_uTimecode += a_pTrack->Audio.cbFrameSize;
-
-        return writeSimpleBlockInternal(a_pTrack->uTrack, ts, pvData, cbData, 0 /* Flags */);
+        if (fClusterStart)
+        {
+            a_pTrack->cTotalClusters++;
+
+            if (Cluster.fOpen) /* Close current cluster first. */
+            {
+                m_Ebml.subEnd(MkvElem_Cluster);
+                Cluster.fOpen = false;
+            }
+
+            tsBlockMs = 0;
+
+            Cluster.fOpen      = true;
+            Cluster.tsStart    = tsPtsMs;
+            Cluster.offCluster = RTFileTell(m_Ebml.getFile());
+
+            LogFunc(("Cluster @ %RU64\n", Cluster.offCluster));
+
+            m_Ebml.subStart(MkvElem_Cluster)
+                  .serializeUnsignedInteger(MkvElem_Timecode, Cluster.tsStart);
+        }
+
+        LogFunc(("Cluster %RU64 - Block %RU64: -> %RU64ms\n",
+                 a_pTrack->cTotalClusters, a_pTrack->cTotalBlocks, a_pTrack->cTotalBlocks * 20));
+
+        return writeSimpleBlockInternal(a_pTrack, tsBlockMs, pvData, cbData, 0 /* Flags */);
     }
 #endif
@@ -654,17 +760,17 @@
         RT_NOREF(cbData); /* Only needed for assertions for now. */
 
-        WebMTracks::const_iterator itTrack = m_mapTracks.find(uTrack);
-        if (itTrack == m_mapTracks.end())
+        WebMTracks::iterator itTrack = CurSeg.mapTracks.find(uTrack);
+        if (itTrack == CurSeg.mapTracks.end())
             return VERR_NOT_FOUND;
 
-        const WebMTrack *pTrack = itTrack->second;
+        WebMTrack *pTrack = itTrack->second;
         AssertPtr(pTrack);
 
         int rc;
 
-        if (m_fTracksOpen)
-        {
-            m_Ebml.subEnd(Tracks);
-            m_fTracksOpen = false;
+        if (m_fInTracksSection)
+        {
+            m_Ebml.subEnd(MkvElem_Tracks);
+            m_fInTracksSection = false;
         }
 
@@ -710,44 +816,47 @@
     int writeFooter(void)
     {
-        if (m_fTracksOpen)
-        {
-            m_Ebml.subEnd(Tracks);
-            m_fTracksOpen = false;
+        if (m_fInTracksSection)
+        {
+            m_Ebml.subEnd(MkvElem_Tracks);
+            m_fInTracksSection = false;
         }
 
-        if (m_fClusterOpen)
-        {
-            m_Ebml.subEnd(Cluster);
-            m_fClusterOpen = false;
+        if (CurSeg.CurCluster.fOpen)
+        {
+            m_Ebml.subEnd(MkvElem_Cluster);
+            CurSeg.CurCluster.fOpen = false;
         }
 
         /*
-         * Write Cues segment.
+         * Write Cues element.
          */
         LogFunc(("Cues @ %RU64\n", RTFileTell(m_Ebml.getFile())));
 
-        m_offSegCuesStart = RTFileTell(m_Ebml.getFile());
-
-        m_Ebml.subStart(Cues);
-
-        for (std::list<WebMCueEntry>::iterator it = m_lstCue.begin(); it != m_lstCue.end(); ++it)
-        {
-            m_Ebml.subStart(CuePoint)
-                  .serializeUnsignedInteger(CueTime, it->time)
-                  .subStart(CueTrackPositions)
-                  .serializeUnsignedInteger(CueTrack, 1)
-                  .serializeUnsignedInteger(CueClusterPosition, it->loc - m_offSegCurStart, 8)
-                  .subEnd(CueTrackPositions)
-                  .subEnd(CuePoint);
+        CurSeg.offCues = RTFileTell(m_Ebml.getFile());
+
+        m_Ebml.subStart(MkvElem_Cues);
+
+        std::list<WebMCueEntry>::iterator itCuePoint = CurSeg.lstCues.begin();
+        while (itCuePoint != CurSeg.lstCues.end())
+        {
+            m_Ebml.subStart(MkvElem_CuePoint)
+                  .serializeUnsignedInteger(MkvElem_CueTime, itCuePoint->time)
+                  .subStart(MkvElem_CueTrackPositions)
+                  .serializeUnsignedInteger(MkvElem_CueTrack, 1)
+                  .serializeUnsignedInteger(MkvElem_CueClusterPosition, itCuePoint->loc - CurSeg.offStart, 8)
+                  .subEnd(MkvElem_CueTrackPositions)
+                  .subEnd(MkvElem_CuePoint);
+
+            itCuePoint++;
         }
 
-        m_Ebml.subEnd(Cues)
-              .subEnd(Segment);
+        m_Ebml.subEnd(MkvElem_Cues)
+              .subEnd(MkvElem_Segment);
 
         /*
-         * Update SeekHead / Info segment.
+         * Re-Update SeekHead / Info elements.
          */
 
-        writeSeekInfo();
+        writeSegSeekInfo();
 
         return RTFileSeek(m_Ebml.getFile(), 0, RTFILE_SEEK_END, NULL);
@@ -756,12 +865,12 @@
     int close(void)
     {
-        WebMTracks::iterator itTrack = m_mapTracks.begin();
-        for (; itTrack != m_mapTracks.end(); ++itTrack)
+        WebMTracks::iterator itTrack = CurSeg.mapTracks.begin();
+        for (; itTrack != CurSeg.mapTracks.end(); ++itTrack)
         {
             delete itTrack->second;
-            m_mapTracks.erase(itTrack);
+            CurSeg.mapTracks.erase(itTrack);
         }
 
-        Assert(m_mapTracks.size() == 0);
+        Assert(CurSeg.mapTracks.size() == 0);
 
         m_Ebml.close();
@@ -775,36 +884,41 @@
 private:
 
-    void writeSeekInfo(void)
-    {
-        if (m_offSegSeekInfoStart)
-            RTFileSeek(m_Ebml.getFile(), m_offSegSeekInfoStart, RTFILE_SEEK_BEGIN, NULL);
+    /**
+     * Writes the segment's seek information and cue points.
+     *
+     * @returns IPRT status code.
+     */
+    void writeSegSeekInfo(void)
+    {
+        if (CurSeg.offSeekInfo)
+            RTFileSeek(m_Ebml.getFile(), CurSeg.offSeekInfo, RTFILE_SEEK_BEGIN, NULL);
         else
-            m_offSegSeekInfoStart = RTFileTell(m_Ebml.getFile());
-
-        LogFunc(("SeekHead @ %RU64\n", m_offSegSeekInfoStart));
-
-        m_Ebml.subStart(SeekHead)
-
-              .subStart(Seek)
-              .serializeUnsignedInteger(SeekID, Tracks)
-              .serializeUnsignedInteger(SeekPosition, m_offSegTracksStart - m_offSegCurStart, 8)
-              .subEnd(Seek)
-
-              .subStart(Seek)
-              .serializeUnsignedInteger(SeekID, Cues)
-              .serializeUnsignedInteger(SeekPosition, m_offSegCuesStart - m_offSegCurStart, 8)
-              .subEnd(Seek)
-
-              .subStart(Seek)
-              .serializeUnsignedInteger(SeekID, Info)
-              .serializeUnsignedInteger(SeekPosition, m_offSegInfoStart - m_offSegCurStart, 8)
-              .subEnd(Seek)
-
-        .subEnd(SeekHead);
+            CurSeg.offSeekInfo = RTFileTell(m_Ebml.getFile());
+
+        LogFunc(("SeekHead @ %RU64\n", CurSeg.offSeekInfo));
+
+        m_Ebml.subStart(MkvElem_SeekHead)
+
+              .subStart(MkvElem_Seek)
+              .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Tracks)
+              .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offTracks - CurSeg.offStart, 8)
+              .subEnd(MkvElem_Seek)
+
+              .subStart(MkvElem_Seek)
+              .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Cues)
+              .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offCues - CurSeg.offStart, 8)
+              .subEnd(MkvElem_Seek)
+
+              .subStart(MkvElem_Seek)
+              .serializeUnsignedInteger(MkvElem_SeekID, MkvElem_Info)
+              .serializeUnsignedInteger(MkvElem_SeekPosition, CurSeg.offInfo - CurSeg.offStart, 8)
+              .subEnd(MkvElem_Seek)
+
+        .subEnd(MkvElem_SeekHead);
 
         int64_t iFrameTime = (int64_t)1000 * 1 / 25; //m_Framerate.den / m_Framerate.num; /** @todo Fix this! */
-        m_offSegInfoStart = RTFileTell(m_Ebml.getFile());
-
-        LogFunc(("Info @ %RU64\n", m_offSegInfoStart));
+        CurSeg.offInfo = RTFileTell(m_Ebml.getFile());
+
+        LogFunc(("Info @ %RU64\n", CurSeg.offInfo));
 
         char szMux[64];
@@ -814,10 +928,10 @@
         RTStrPrintf(szApp, sizeof(szApp), VBOX_PRODUCT " %sr%u", VBOX_VERSION_STRING, RTBldCfgRevision());
 
-        m_Ebml.subStart(Info)
-              .serializeUnsignedInteger(TimecodeScale, 1000000)
-              .serializeFloat(Segment_Duration, m_tsLastPtsMs + iFrameTime - m_tsInitialPtsMs)
-              .serializeString(MuxingApp, szMux)
-              .serializeString(WritingApp, szApp)
-              .subEnd(Info);
+        m_Ebml.subStart(MkvElem_Info)
+              .serializeUnsignedInteger(MkvElem_TimecodeScale, m_uTimecodeScaleNs)
+              .serializeFloat(MkvElem_Segment_Duration, m_tsLastPtsMs + iFrameTime - m_tsInitialPtsMs)
+              .serializeString(MkvElem_MuxingApp, szMux)
+              .serializeString(MkvElem_WritingApp, szApp)
+              .subEnd(MkvElem_Info);
     }
 };
