Index: /trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp	(revision 65255)
+++ /trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp	(revision 65256)
@@ -33,5 +33,4 @@
 #include <iprt/mem.h>
 #include <iprt/cdefs.h>
-#include <iprt/circbuf.h>
 
 #include <VBox/vmm/pdmaudioifs.h>
@@ -90,7 +89,13 @@
     uint64_t             old_ticks;
     uint64_t             cSamplesSentPerSec;
-    /** Codec data.
+    /** Codec-specific data.
      *  As every stream can be different, one codec per stream is needed. */
     AVRECCODEC           Codec;
+    /** (Audio) frame buffer. */
+    uint8_t             *pvFrameBuf;
+    size_t               cbFrameBufSize;
+    size_t               cbFrameBufUsed;
+    size_t               offFrameBufRead;
+    size_t               offFrameBufWrite;
 } AVRECSTREAMOUT, *PAVRECSTREAMOUT;
 
@@ -133,28 +138,41 @@
     if (RT_SUCCESS(rc))
     {
-        OpusEncoder *pEnc = NULL;
-
-        int orc;
-        pEnc = opus_encoder_create(48000 /* Hz */, 2 /* Stereo */, OPUS_APPLICATION_AUDIO, &orc);
-        if (orc != OPUS_OK)
+        size_t cbFrameBuf = sizeof(uint8_t) * _8K; /** @todo Make this configurable */
+
+        pStreamOut->pvFrameBuf = (uint8_t *)RTMemAlloc(cbFrameBuf);
+        if (pStreamOut->pvFrameBuf)
         {
-            LogRel(("VideoRec: Audio codec failed to initialize: %s\n", opus_strerror(orc)));
-            return VERR_AUDIO_BACKEND_INIT_FAILED;
-        }
-
-        AssertPtr(pEnc);
-
-        orc = opus_encoder_ctl(pEnc, OPUS_SET_BITRATE(pCfgReq->uHz));
-        if (orc != OPUS_OK)
-        {
-            LogRel(("VideoRec: Audio codec failed to set bitrate: %s\n", opus_strerror(orc)));
-            rc = VERR_AUDIO_BACKEND_INIT_FAILED;
-        }
-        else
-        {
-            pStreamOut->Codec.Opus.pEnc = pEnc;
-
-            if (pCfgAcq)
-                pCfgAcq->cSampleBufferSize = _4K; /** @todo Make this configurable. */
+            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. */
+    //      }
         }
     }
@@ -205,7 +223,8 @@
             {
                 pThis->pEBML = new WebMWriter();
-                rc = pThis->pEBML->create("/tmp/test.webm", RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE,
-                                          WebMWriter::Mode_Audio, /** @todo Fix path! */
+                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.0, 44100, 2, 16);
                 break;
             }
@@ -257,5 +276,6 @@
  */
 static DECLCALLBACK(int) drvAudioVideoRecStreamPlay(PPDMIHOSTAUDIO pInterface,
-                                                    PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
+                                                    PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf,
+                                                    uint32_t *pcbWritten)
 {
     RT_NOREF2(pvBuf, cbBuf);
@@ -291,36 +311,52 @@
 
     /*
-     * Call the encoder server with the data.
+     * Call the encoder with the data.
      */
-    uint32_t cReadTotal = 0;
-
-    uint8_t abDst[_4K];
-    opus_int32 cbDst = (opus_int32)sizeof(abDst);
-
-    PPDMAUDIOSAMPLE pSamples;
-    uint32_t cRead;
-    int rc = AudioMixBufAcquire(&pStream->MixBuf, cSamplesToSend,
-                                &pSamples, &cRead);
-    if (   RT_SUCCESS(rc)
-        && cRead)
-    {
-        cReadTotal = cRead;
-
-        opus_int32 orc = opus_encode(pStreamOut->Codec.Opus.pEnc, (opus_int16 *)pSamples, cRead, abDst, cbDst);
-        if (orc != OPUS_OK)
-            LogFunc(("Encoding (1) failed: %s\n", opus_strerror(orc)));
-
-        if (rc == VINF_TRY_AGAIN)
+
+    /** @todo For now we ASSUME 25 FPS. */
+    uint16_t csFrameSize = /*pStreamOut->Props.uHz*/ 48000 / 25;
+    size_t   cbFrameSize = AUDIOMIXBUF_S2B(&pStream->MixBuf, csFrameSize);
+
+    uint32_t csRead = 0;
+    int rc = AudioMixBufReadCirc(&pStream->MixBuf, (uint8_t *)&pStreamOut->pvFrameBuf[pStreamOut->offFrameBufWrite],
+                                 pStreamOut->cbFrameBufSize - pStreamOut->offFrameBufWrite, &csRead);
+    if (RT_SUCCESS(rc))
+    {
+        const uint32_t cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, csRead);
+
+        pStreamOut->offFrameBufWrite =
+            (pStreamOut->offFrameBufWrite + cbRead) % pStreamOut->cbFrameBufSize;
+        Assert(pStreamOut->offFrameBufWrite <= pStreamOut->cbFrameBufSize);
+        pStreamOut->cbFrameBufUsed  += cbRead;
+        Assert(pStreamOut->cbFrameBufUsed  <= pStreamOut->cbFrameBufSize);
+
+        if (pStreamOut->cbFrameBufUsed >= cbFrameSize)
         {
-            rc = AudioMixBufAcquire(&pStream->MixBuf, cSamplesToSend - cRead,
-                                    &pSamples, &cRead);
-
-
-
-            cReadTotal += cRead;
+            /* Opus always encodes PER FRAME, that is, 2.5, 5, 10, 20, 40 or 60 ms of audio data. */
+            uint8_t abDst[_4K];
+            size_t  cbDst = sizeof(abDst);
+
+            /* Call the encoder to encode our input samples. */
+            opus_int32 cbWritten = opus_encode(pStreamOut->Codec.Opus.pEnc,
+                                               (opus_int16 *)&pStreamOut->pvFrameBuf[pStreamOut->offFrameBufRead],
+                                               csFrameSize, abDst, cbDst);
+            if (cbWritten > 0)
+            {
+                pStreamOut->offFrameBufRead = (pStreamOut->offFrameBufRead + cbWritten) % pStreamOut->cbFrameBufSize;
+                Assert(pStreamOut->cbFrameBufUsed >= cbFrameSize);
+                pStreamOut->cbFrameBufUsed -= cbFrameSize;
+
+                /* Call the WebM writer to actually write the encoded audio data. */
+                WebMWriter::BlockData_Opus blockData = { abDst, cbDst };
+                rc = pThis->pEBML->WriteBlock(WebMWriter::BlockType_Audio, &blockData, sizeof(blockData));
+                AssertRC(rc);
+            }
+            else if (cbWritten < 0)
+                AssertMsgFailed(("Encoding failed: %s\n", opus_strerror(cbWritten)));
         }
     }
 
-    AudioMixBufFinish(&pStream->MixBuf, cSamplesToSend);
+    if (csRead)
+        AudioMixBufFinish(&pStream->MixBuf, csRead);
 
     /*
@@ -329,7 +365,7 @@
      */
     if (pcbWritten)
-        *pcbWritten = cReadTotal;
-
-    LogFlowFunc(("cReadTotal=%RU32, rc=%Rrc\n", cReadTotal, rc));
+        *pcbWritten = csRead;
+
+    LogFlowFunc(("csRead=%RU32, rc=%Rrc\n", csRead, rc));
     return rc;
 }
@@ -341,4 +377,11 @@
     RT_NOREF(pThis);
     PAVRECSTREAMOUT   pStreamOut = (PAVRECSTREAMOUT)pStream;
+
+    if (pStreamOut->pvFrameBuf)
+    {
+        Assert(pStreamOut->cbFrameBufSize);
+        RTMemFree(pStreamOut->pvFrameBuf);
+        pStreamOut->pvFrameBuf = NULL;
+    }
 
     if (pStreamOut->Codec.Opus.pEnc)
@@ -386,8 +429,5 @@
             if (pThis->pEBML)
             {
-                rc = pThis->pEBML->writeFooter(0 /* Hash */);
-                AssertRC(rc);
-
-                pThis->pEBML->close();
+                pThis->pEBML->Close();
 
                 delete pThis->pEBML;
Index: /trunk/src/VBox/Main/src-client/EbmlWriter.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/EbmlWriter.cpp	(revision 65255)
+++ /trunk/src/VBox/Main/src-client/EbmlWriter.cpp	(revision 65256)
@@ -19,12 +19,16 @@
 #include <list>
 #include <stack>
-#include <iprt/string.h>
-#include <iprt/file.h>
 #include <iprt/asm.h>
 #include <iprt/cdefs.h>
 #include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/rand.h>
+#include <iprt/string.h>
+
 #include <VBox/log.h>
+
 #include "EbmlWriter.h"
 #include "EbmlIDs.h"
+#include "Logging.h"
 
 
@@ -48,4 +52,11 @@
 public:
 
+    Ebml(void)
+        : m_File(NIL_RTFILE) { }
+
+    virtual ~Ebml(void) { close(); }
+
+public:
+
     /** Creates EBML output file. */
     inline int create(const char *a_pszFilename, uint64_t fOpen)
@@ -55,5 +66,5 @@
 
     /** Returns file size. */
-    inline uint64_t getFileSize()
+    inline uint64_t getFileSize(void)
     {
         return RTFileTell(m_File);
@@ -61,5 +72,5 @@
 
     /** Get reference to file descriptor */
-    inline const RTFILE &getFile()
+    inline const RTFILE &getFile(void)
     {
         return m_File;
@@ -67,5 +78,5 @@
 
     /** Returns available space on storage. */
-    inline uint64_t getAvailableSpace()
+    inline uint64_t getAvailableSpace(void)
     {
         RTFOFF pcbFree;
@@ -75,7 +86,25 @@
 
     /** Closes the file. */
-    inline void close()
-    {
+    inline void close(void)
+    {
+        if (!isOpen())
+            return;
+
+        AssertMsg(m_Elements.size() == 0,
+                  ("%zu elements are not closed yet (next element to close is 0x%x)\n",
+                   m_Elements.size(), m_Elements.top().classId));
+
         RTFileClose(m_File);
+        m_File = NIL_RTFILE;
+    }
+
+    /**
+     * Returns whether the file is open or not.
+     *
+     * @returns True if open, false if not.
+     */
+    inline bool isOpen(void)
+    {
+        return RTFileIsValid(m_File);
     }
 
@@ -99,5 +128,7 @@
          * to the function. Otherwise it may mean that we have a bug in the code.
          */
-        if(m_Elements.empty() || m_Elements.top().classId != classId) throw VERR_INTERNAL_ERROR;
+        AssertMsg(!m_Elements.empty(), ("No elements to close anymore\n"));
+        AssertMsg(m_Elements.top().classId == classId,
+                  ("Ending sub element 0x%x is in wrong order (next to close is 0x%x)\n", classId, m_Elements.top().classId));
 
         uint64_t uPos = RTFileTell(m_File);
@@ -156,8 +187,7 @@
 
     /** Writes raw data to file. */
-    inline void write(const void *data, size_t size)
-    {
-        int rc = RTFileWrite(m_File, data, size, NULL);
-        if (!RT_SUCCESS(rc)) throw rc;
+    inline int write(const void *data, size_t size)
+    {
+        return RTFileWrite(m_File, data, size, NULL);
     }
 
@@ -229,9 +259,48 @@
 class WebMWriter_Impl
 {
-    struct CueEntry
-    {
+    /**
+     * Structure for keeping a cue entry.
+     */
+    struct WebMCueEntry
+    {
+        WebMCueEntry(uint32_t t, uint64_t l)
+            : time(t), loc(l) {}
+
         uint32_t    time;
         uint64_t    loc;
-        CueEntry(uint32_t t, uint64_t l) : time(t), loc(l) {}
+    };
+
+    /**
+     * Track type enumeration.
+     */
+    enum WebMTrackType
+    {
+        /** Unknown / invalid type. */
+        WebMTrackType_Invalid     = 0,
+        /** Only writes audio. */
+        WebMTrackType_Audio       = 1,
+        /** Only writes video. */
+        WebMTrackType_Video       = 2
+    };
+
+    /**
+     * Structure for keeping a track entry.
+     */
+    struct WebMTrack
+    {
+        WebMTrack(WebMTrackType a_enmType, uint64_t a_offID)
+            : enmType(a_enmType)
+            , offID(a_offID)
+        {
+            uID = RTRandU32();
+        }
+
+        /** The type of this track. */
+        WebMTrackType enmType;
+        /** The track's UUID. */
+        uint32_t      uID;
+        /** Absolute offset in file of track ID.
+         *  Needed to write the hash sum within the footer. */
+        uint64_t      offID;
     };
 
@@ -243,79 +312,127 @@
     struct OpusPrivData
     {
-        uint8_t  au8Head[8]       = { 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64 };
-        uint8_t  u8Version        = 1;
-        uint8_t  c8Channels;
-        uint16_t u16PreSkip       = 0;
-        uint32_t u32SampleRate;
-        uint16_t u16Gain          = 0;
-        uint8_t  u8Mapping_family = 0;
+        uint8_t  au8Head[8]      = { 0x4f, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64 };
+        uint8_t  u8Version       = 1;
+        uint8_t  c8Channels      = 0;
+        uint16_t u16PreSkip      = 0;
+        uint32_t u32SampleRate   = 0;
+        uint16_t u16Gain         = 0;
+        uint8_t  u8MappingFamily = 0;
     };
 # pragma pack(pop)
 #endif /* VBOX_WITH_AUDIO_VIDEOREC */
 
-    /** Operation mode. */
-    WebMWriter::Mode       m_enmMode;
     /** Audio codec to use. */
-    WebMWriter::AudioCodec m_enmAudioCodec;
+    WebMWriter::AudioCodec      m_enmAudioCodec;
     /** Video codec to use. */
-    WebMWriter::VideoCodec m_enmVideoCodec;
-
-    bool                   m_fDebug;
-
-    /** Timestamp of initial PTS (Presentation Time Stamp). */
-    int64_t                m_tsInitialPtsMs;
-    /** Timestamp of last written PTS (Presentation Time Stamp). */
-    int64_t                m_tsLastPtsMs;
-
-    vpx_rational_t         m_Framerate;
+    WebMWriter::VideoCodec      m_enmVideoCodec;
+
+    /** This PTS (Presentation Time Stamp) acts as our master clock for synchronizing streams. */
+    uint64_t                    m_uPts;
+    /** Timestamp (in ms) of initial PTS. */
+    int64_t                     m_tsInitialPtsMs;
+    /** Timestamp (in ms) of last written PTS. */
+    int64_t                     m_tsLastPtsMs;
 
     /** Start offset (in bytes) of current segment. */
-    uint64_t               m_offSegCurStart;
+    uint64_t                    m_offSegCurStart;
 
     /** Start offset (in bytes) of seeking info segment. */
-    uint64_t               m_offSegSeekInfoStart;
-    /** Offset (in bytes) for current seek info element. */
-    uint64_t               m_offSeekInfo;
+    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;
+    uint64_t                    m_offSegTracksStart;
 
     /** Absolute position of cue segment. */
-    uint64_t               m_uCuePos;
+    uint64_t                    m_offSegCuesStart;
     /** List of cue points. Needed for seeking table. */
-    std::list<CueEntry>    m_lstCue;
-
-    uint64_t               m_uTrackIdPos;
+    std::list<WebMCueEntry>     m_lstCue;
+
+    /** List of tracks. */
+    std::list<WebMTrack>        m_lstTracks;
+    bool                        m_fTracksOpen;
 
     /** Timestamp (in ms) when the current cluster has been opened. */
-    uint32_t               m_tsClusterOpenMs;
+    uint32_t                    m_tsClusterOpenMs;
     /** Whether we're currently in an opened cluster segment. */
-    bool                   m_fClusterOpen;
+    bool                        m_fClusterOpen;
     /** Absolute position (in bytes) of current cluster within file.
      *  Needed for seeking info table. */
-    uint64_t               m_offSegClusterStart;
-
-    Ebml                   m_Ebml;
+    uint64_t                    m_offSegClusterStart;
+
+    Ebml                        m_Ebml;
 
 public:
 
     WebMWriter_Impl() :
-          m_enmMode(WebMWriter::Mode_Unknown)
-        , m_fDebug(false)
+          m_uPts(0)
         , m_tsInitialPtsMs(-1)
         , m_tsLastPtsMs(-1)
-        , m_Framerate()
         , m_offSegCurStart(0)
         , m_offSegSeekInfoStart(0)
-        , m_offSeekInfo(0)
+        , m_offSegInfoStart(0)
         , m_offSegTracksStart(0)
-        , m_uCuePos(0)
-        , m_uTrackIdPos(0)
+        , m_offSegCuesStart(0)
+        , m_fTracksOpen(false)
         , m_tsClusterOpenMs(0)
         , m_fClusterOpen(false)
         , m_offSegClusterStart(0) {}
 
-    void writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const struct vpx_rational *a_pFps)
-    {
+    int AddAudioTrack(float fSamplingHz, float fOutputHz, uint8_t cChannels, uint8_t cBitDepth)
+    {
+        m_Ebml.subStart(TrackEntry);
+        m_Ebml.serializeUnsignedInteger(TrackNumber, (uint8_t)m_lstTracks.size());
+        /** @todo Implement track's "Language" property? Currently this defaults to English ("eng"). */
+
+        WebMTrack TrackAudio(WebMTrackType_Audio, RTFileTell(m_Ebml.getFile()));
+        m_lstTracks.push_back(TrackAudio);
+
+        /** @todo Resolve codec type. */
+        OpusPrivData opusPrivData;
+
+        m_Ebml.serializeUnsignedInteger(TrackUID, TrackAudio.uID, 4)
+              .serializeUnsignedInteger(TrackType, 2 /* Audio */)
+              .serializeString(CodecID, "A_OPUS")
+              .serializeData(CodecPrivate, &opusPrivData, sizeof(opusPrivData))
+              .subStart(Audio)
+              .serializeFloat(SamplingFrequency, fSamplingHz)
+              .serializeFloat(OutputSamplingFrequency, fOutputHz)
+              .serializeUnsignedInteger(Channels, cChannels)
+              .serializeUnsignedInteger(BitDepth, cBitDepth)
+              .subEnd(Audio)
+              .subEnd(TrackEntry);
+
+        return VINF_SUCCESS;
+    }
+
+    int AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS)
+    {
+        m_Ebml.subStart(TrackEntry);
+        m_Ebml.serializeUnsignedInteger(TrackNumber, (uint8_t)m_lstTracks.size());
+
+        WebMTrack TrackVideo(WebMTrackType_Video, RTFileTell(m_Ebml.getFile()));
+        m_lstTracks.push_back(TrackVideo);
+
+        /** @todo Resolve codec type. */
+
+        m_Ebml.serializeUnsignedInteger(TrackUID, TrackVideo.uID /* 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);
+
+        return VINF_SUCCESS;
+    }
+
+    int writeHeader(void)
+    {
+        LogFunc(("Header @ %RU64\n", RTFileTell(m_Ebml.getFile())));
+
         m_Ebml.subStart(EBML)
               .serializeUnsignedInteger(EBMLVersion, 1)
@@ -330,6 +447,4 @@
         m_Ebml.subStart(Segment);
 
-        m_Framerate = *a_pFps;
-
         /* Save offset of current segment. */
         m_offSegCurStart = RTFileTell(m_Ebml.getFile());
@@ -340,95 +455,77 @@
         m_offSegTracksStart = RTFileTell(m_Ebml.getFile());
 
+        /* The tracks segment starts right after this header. */
         m_Ebml.subStart(Tracks);
-
-        /* Write video? */
-        if (   m_enmMode == WebMWriter::Mode_Video
-            || m_enmMode == WebMWriter::Mode_AudioVideo)
-        {
-            /*
-             * Video track.
-             */
-            m_Ebml.subStart(TrackEntry);
-            m_Ebml.serializeUnsignedInteger(TrackNumber, 1);
-
-            m_uTrackIdPos = RTFileTell(m_Ebml.getFile());
-
-            m_Ebml.serializeUnsignedInteger(TrackUID, 0 /* UID */, 4)
-                  .serializeUnsignedInteger(TrackType, 1 /* Video */)
-                  .serializeString(CodecID, "V_VP8")
-                  .subStart(Video)
-                  .serializeUnsignedInteger(PixelWidth, a_pCfg->g_w)
-                  .serializeUnsignedInteger(PixelHeight, a_pCfg->g_h)
-                  .serializeFloat(FrameRate, (double) a_pFps->num / a_pFps->den)
-                  .subEnd(Video)
-                  .subEnd(TrackEntry);
-        }
-
-#ifdef VBOX_WITH_AUDIO_VIDEOREC
-        if (   m_enmMode == WebMWriter::Mode_Audio
-            || m_enmMode == WebMWriter::Mode_AudioVideo)
-        {
-            /*
-             * Audio track.
-             */
-            m_Ebml.subStart(TrackEntry);
-            m_Ebml.serializeUnsignedInteger(TrackNumber, 2);
-            /** @todo Implement track's "Language" property? Currently this defaults to English ("eng"). */
-
-            OpusPrivData opusPrivData;
-
-            m_Ebml.serializeUnsignedInteger(TrackUID, 1 /* UID */, 4)
-                  .serializeUnsignedInteger(TrackType, 2 /* Audio */)
-                  .serializeString(CodecID, "A_OPUS")
-                  .serializeData(CodecPrivate, &opusPrivData, sizeof(opusPrivData))
-                  .subStart(Audio)
-                  .serializeFloat(SamplingFrequency, 44100.0)
-                  .serializeFloat(OutputSamplingFrequency, 44100.0)
-                  .serializeUnsignedInteger(Channels, 2)
-                  .serializeUnsignedInteger(BitDepth, 16)
-                  .subEnd(Audio)
-                  .subEnd(TrackEntry);
-        }
-#endif
-
-        m_Ebml.subEnd(Tracks);
-    }
-
-    void writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
+        m_fTracksOpen = 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));
+
+        /* Write a "Simple Block". */
+        m_Ebml.writeClassId(SimpleBlock);
+        /* Block size. */
+        m_Ebml.writeUnsignedInteger(0x10000000u | (1      /* Track number. */
+                                    + 2      /* Timecode .*/
+                                    + 1      /* Flags. */
+                                    + cbData /* Actual frame data. */),  4);
+        /* Track number. */
+        m_Ebml.writeSize(uTrackID);
+        /* Timecode (relative to cluster opening timecode). */
+        m_Ebml.writeUnsignedInteger(uTimecode, 2);
+        /* Flags. */
+        m_Ebml.writeUnsignedInteger(fFlags, 1);
+        /* Frame data. */
+        m_Ebml.write(pvData, cbData);
+
+        return VINF_SUCCESS;
+    }
+
+    int writeBlockVP8(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
     {
         /* Calculate the PTS of this frame in milliseconds. */
-        int64_t iPtsMs = a_pPkt->data.frame.pts * 1000
-                       * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;
-
-        if (iPtsMs <= m_tsLastPtsMs)
-            iPtsMs = m_tsLastPtsMs + 1;
-
-        m_tsLastPtsMs = iPtsMs;
+        int64_t tsPtsMs = a_pPkt->data.frame.pts * 1000
+                        * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;
+
+        if (tsPtsMs <= m_tsLastPtsMs)
+            tsPtsMs = m_tsLastPtsMs + 1;
+
+        m_tsLastPtsMs = tsPtsMs;
 
         if (m_tsInitialPtsMs < 0)
-          m_tsInitialPtsMs = m_tsLastPtsMs;
+            m_tsInitialPtsMs = m_tsLastPtsMs;
 
         /* Calculate the relative time of this block. */
-        uint16_t uBlockTimecode = 0;
-        bool     fClusterStart  = false;
-
-        if (iPtsMs - m_tsClusterOpenMs > 65536)
+        uint16_t tsBlockMs     = 0;
+        bool     fClusterStart = false;
+
+        if (tsPtsMs - m_tsClusterOpenMs > 65536)
             fClusterStart = true;
         else
-            uBlockTimecode = static_cast<uint16_t>(iPtsMs - m_tsClusterOpenMs);
-
-        bool fKeyframe = RT_BOOL(a_pPkt->data.frame.flags & VPX_FRAME_IS_KEY);
+        {
+            /* Calculate the block's timecode, which is relative to the Cluster timecode. */
+            tsBlockMs = static_cast<uint16_t>(tsPtsMs - m_tsClusterOpenMs);
+        }
+
+        const bool fKeyframe = RT_BOOL(a_pPkt->data.frame.flags & VPX_FRAME_IS_KEY);
 
         if (   fClusterStart
             || fKeyframe)
         {
-            if (m_fClusterOpen)
+            if (m_fClusterOpen) /* Close current cluster first. */
                 m_Ebml.subEnd(Cluster);
 
+            tsBlockMs = 0;
+
             /* Open a new cluster. */
-            uBlockTimecode = 0;
-            m_fClusterOpen = true;
-            m_tsClusterOpenMs = (uint32_t)iPtsMs;
+            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);
@@ -437,34 +534,107 @@
             if (fKeyframe)
             {
-                CueEntry cue(m_tsClusterOpenMs, m_offSegClusterStart);
+                WebMCueEntry cue(m_tsClusterOpenMs, m_offSegClusterStart);
                 m_lstCue.push_back(cue);
             }
         }
 
-        /* Write a "Simple Block". */
-        m_Ebml.writeClassId(SimpleBlock);
-        m_Ebml.writeUnsignedInteger(0x10000000u | (4 + a_pPkt->data.frame.sz), 4);
-        m_Ebml.writeSize(1);
-        m_Ebml.writeUnsignedInteger(uBlockTimecode, 2);
-        m_Ebml.writeUnsignedInteger((fKeyframe ? 0x80 : 0) | (a_pPkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE ? 0x08 : 0), 1);
-        m_Ebml.write(a_pPkt->data.frame.buf, a_pPkt->data.frame.sz);
-    }
-
-    void writeFooter(uint32_t a_u64Hash)
-    {
+        uint8_t fFlags = 0;
+        if (fKeyframe)
+            fFlags |= 0x80; /* Key frame. */
+        if (a_pPkt->data.frame.flags & VPX_FRAME_IS_INVISIBLE)
+            fFlags |= 0x08; /* Invisible frame. */
+
+        return writeSimpleBlockInternal(0 /** @todo FIX! */, tsBlockMs, a_pPkt->data.frame.buf, a_pPkt->data.frame.sz, fFlags);
+    }
+
+#ifdef VBOX_WITH_AUDIO_VIDEOREC
+    /* Audio blocks that have same absolute timecode as video blocks SHOULD be written before the video blocks. */
+    int writeBlockOpus(const void *pvData, size_t cbData)
+    {
+static uint16_t s_uTimecode = 0;
+
+        return writeSimpleBlockInternal(1 /** @todo FIX! */, s_uTimecode++, pvData, cbData, 0 /* Flags */);
+    }
+#endif
+
+    int WriteBlock(WebMWriter::BlockType blockType, const void *pvData, size_t cbData)
+    {
+        int rc;
+
+        if (m_fTracksOpen)
+        {
+            m_Ebml.subEnd(Tracks);
+            m_fTracksOpen = false;
+        }
+
+        switch (blockType)
+        {
+#ifdef VBOX_WITH_AUDIO_VIDEOREC
+            case WebMWriter::BlockType_Audio:
+            {
+                if (m_enmAudioCodec == WebMWriter::AudioCodec_Opus)
+                {
+                    Assert(cbData == sizeof(WebMWriter::BlockData_Opus));
+                    WebMWriter::BlockData_Opus *pData = (WebMWriter::BlockData_Opus *)pvData;
+                    rc = writeBlockOpus(pData->pvData, pData->cbData);
+                }
+                else
+                    rc = VERR_NOT_SUPPORTED;
+                break;
+            }
+#endif
+            case WebMWriter::BlockType_Video:
+            {
+                if (m_enmVideoCodec == WebMWriter::VideoCodec_VP8)
+                {
+                    Assert(cbData == sizeof(WebMWriter::BlockData_VP8));
+                    WebMWriter::BlockData_VP8 *pData = (WebMWriter::BlockData_VP8 *)pvData;
+                    rc = writeBlockVP8(pData->pCfg, pData->pPkt);
+                }
+                else
+                    rc = VERR_NOT_SUPPORTED;
+                break;
+            }
+
+            default:
+                rc = VERR_NOT_SUPPORTED;
+                break;
+        }
+
+        return rc;
+    }
+
+    int writeFooter(void)
+    {
+        if (m_fTracksOpen)
+        {
+            m_Ebml.subEnd(Tracks);
+            m_fTracksOpen = false;
+        }
+
         if (m_fClusterOpen)
+        {
             m_Ebml.subEnd(Cluster);
-
-        m_uCuePos = RTFileTell(m_Ebml.getFile());
+            m_fClusterOpen = false;
+        }
+
+        /*
+         * Write Cues segment.
+         */
+        LogFunc(("Cues @ %RU64\n", RTFileTell(m_Ebml.getFile())));
+
+        m_offSegCuesStart = RTFileTell(m_Ebml.getFile());
+
         m_Ebml.subStart(Cues);
-        for (std::list<CueEntry>::iterator it = m_lstCue.begin(); it != m_lstCue.end(); ++it)
+
+        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);
+            m_Ebml.subStart(CuePoint)
+                  .serializeUnsignedInteger(CueTime, it->time)
+                  .subStart(CueTrackPositions)
+                  .serializeUnsignedInteger(CueTrack, 1)
+                  .serializeUnsignedInteger(CueClusterPosition, it->loc - m_offSegCurStart, 8)
+                  .subEnd(CueTrackPositions)
+                  .subEnd(CuePoint);
         }
 
@@ -472,15 +642,14 @@
               .subEnd(Segment);
 
+        /*
+         * Update SeekHead / Info segment.
+         */
+
         writeSeekInfo();
 
-        int rc = RTFileSeek(m_Ebml.getFile(), m_uTrackIdPos, RTFILE_SEEK_BEGIN, NULL);
-        if (!RT_SUCCESS(rc)) throw rc;
-
-        m_Ebml.serializeUnsignedInteger(TrackUID, (m_fDebug ? 0xDEADBEEF : a_u64Hash), 4);
-
-        rc = RTFileSeek(m_Ebml.getFile(), 0, RTFILE_SEEK_END, NULL);
-        if (!RT_SUCCESS(rc)) throw rc;
-    }
-
+        return RTFileSeek(m_Ebml.getFile(), 0, RTFILE_SEEK_END, NULL);
+    }
+
+    friend class Ebml;
     friend class WebMWriter;
 
@@ -489,9 +658,10 @@
     void writeSeekInfo(void)
     {
-        uint64_t uPos = RTFileTell(m_Ebml.getFile());
         if (m_offSegSeekInfoStart)
             RTFileSeek(m_Ebml.getFile(), m_offSegSeekInfoStart, RTFILE_SEEK_BEGIN, NULL);
         else
-            m_offSegSeekInfoStart = uPos;
+            m_offSegSeekInfoStart = RTFileTell(m_Ebml.getFile());
+
+        LogFunc(("SeekHead @ %RU64\n", m_offSegSeekInfoStart));
 
         m_Ebml.subStart(SeekHead)
@@ -504,20 +674,21 @@
               .subStart(Seek)
               .serializeUnsignedInteger(SeekID, Cues)
-              .serializeUnsignedInteger(SeekPosition, m_uCuePos - m_offSegCurStart, 8)
+              .serializeUnsignedInteger(SeekPosition, m_offSegCuesStart - m_offSegCurStart, 8)
               .subEnd(Seek)
 
               .subStart(Seek)
               .serializeUnsignedInteger(SeekID, Info)
-              .serializeUnsignedInteger(SeekPosition, m_offSeekInfo - m_offSegCurStart, 8)
+              .serializeUnsignedInteger(SeekPosition, m_offSegInfoStart - m_offSegCurStart, 8)
               .subEnd(Seek)
 
         .subEnd(SeekHead);
 
-        int64_t iFrameTime = (int64_t)1000 * m_Framerate.den / m_Framerate.num;
-        m_offSeekInfo = RTFileTell(m_Ebml.getFile());
+        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));
 
         char szVersion[64];
-        RTStrPrintf(szVersion, sizeof(szVersion), "vpxenc%s",
-                    m_fDebug ? "" : vpx_codec_version_str());
+        RTStrPrintf(szVersion, sizeof(szVersion), "vpxenc%s", vpx_codec_version_str()); /** @todo Make this configurable? */
 
         m_Ebml.subStart(Info)
@@ -535,27 +706,23 @@
 {
     if (m_pImpl)
+    {
+        Close();
         delete m_pImpl;
+    }
 }
 
-int WebMWriter::create(const char *a_pszFilename, uint64_t a_fOpen, WebMWriter::Mode a_enmMode,
+int WebMWriter::Create(const char *a_pszFilename, uint64_t a_fOpen,
                        WebMWriter::AudioCodec a_enmAudioCodec, WebMWriter::VideoCodec a_enmVideoCodec)
 {
-    m_pImpl->m_enmMode       = a_enmMode;
-    m_pImpl->m_enmAudioCodec = a_enmAudioCodec;
-    m_pImpl->m_enmVideoCodec = a_enmVideoCodec;
-
-    return m_pImpl->m_Ebml.create(a_pszFilename, a_fOpen);
-}
-
-void WebMWriter::close(void)
-{
-    m_pImpl->m_Ebml.close();
-}
-
-int WebMWriter::writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_rational *a_pFps)
-{
     try
     {
-        m_pImpl->writeHeader(a_pCfg, a_pFps);
+        m_pImpl->m_enmAudioCodec = a_enmAudioCodec;
+        m_pImpl->m_enmVideoCodec = a_enmVideoCodec;
+
+        LogFunc(("Creating '%s'\n", a_pszFilename));
+
+        int rc = m_pImpl->m_Ebml.create(a_pszFilename, a_fOpen);
+        if (RT_SUCCESS(rc))
+            rc = m_pImpl->writeHeader();
     }
     catch(int rc)
@@ -566,36 +733,48 @@
 }
 
-int WebMWriter::writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt)
-{
+int WebMWriter::Close(void)
+{
+    if (!m_pImpl->m_Ebml.isOpen())
+        return VINF_SUCCESS;
+
+    int rc = m_pImpl->writeFooter();
+
+    /* Close the file in any case. */
+    m_pImpl->m_Ebml.close();
+
+    return rc;
+}
+
+int WebMWriter::AddAudioTrack(float fSamplingHz, float fOutputHz, uint8_t cChannels, uint8_t cBitDepth)
+{
+    return m_pImpl->AddAudioTrack(fSamplingHz, fOutputHz, cChannels, cBitDepth);
+}
+
+int WebMWriter::AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS)
+{
+    return m_pImpl->AddVideoTrack(uWidth, uHeight, dbFPS);
+}
+
+int WebMWriter::WriteBlock(WebMWriter::BlockType blockType, const void *pvData, size_t cbData)
+{
+    int rc;
+
     try
     {
-        m_pImpl->writeBlock(a_pCfg, a_pPkt);
-    }
-    catch(int rc)
-    {
-        return rc;
-    }
-    return VINF_SUCCESS;
+        rc = m_pImpl->WriteBlock(blockType, pvData, cbData);
+    }
+    catch(int rc2)
+    {
+        rc = rc2;
+    }
+    return rc;
 }
 
-int WebMWriter::writeFooter(uint32_t a_u64Hash)
-{
-    try
-    {
-        m_pImpl->writeFooter(a_u64Hash);
-    }
-    catch(int rc)
-    {
-        return rc;
-    }
-    return VINF_SUCCESS;
-}
-
-uint64_t WebMWriter::getFileSize()
+uint64_t WebMWriter::GetFileSize(void)
 {
     return m_pImpl->m_Ebml.getFileSize();
 }
 
-uint64_t WebMWriter::getAvailableSpace()
+uint64_t WebMWriter::GetAvailableSpace(void)
 {
     return m_pImpl->m_Ebml.getAvailableSpace();
Index: /trunk/src/VBox/Main/src-client/EbmlWriter.h
===================================================================
--- /trunk/src/VBox/Main/src-client/EbmlWriter.h	(revision 65255)
+++ /trunk/src/VBox/Main/src-client/EbmlWriter.h	(revision 65256)
@@ -59,23 +59,33 @@
     };
 
-    struct BlockData
+    /**
+     * Block data for VP8-encoded video data.
+     */
+    struct BlockData_VP8
     {
-        void  *pvData;
-        size_t cbData;
+        const vpx_codec_enc_cfg_t *pCfg;
+        const vpx_codec_cx_pkt_t  *pPkt;
     };
 
     /**
-     * Operation mode -- this specifies what to write.
+     * Block data for Opus-encoded audio data.
      */
-    enum Mode
+    struct BlockData_Opus
     {
-        /** Unknown / invalid mode. */
-        Mode_Unknown     = 0,
-        /** Only writes audio. */
-        Mode_Audio       = 1,
-        /** Only writes video. */
-        Mode_Video       = 2,
-        /** Writes audio and video. */
-        Mode_AudioVideo  = 3
+        /** Pointer to encoded Opus audio data. */
+        const void *pvData;
+        /** Size (in bytes) of encoded Opus audio data. */
+        size_t      cbData;
+    };
+
+    /**
+     * Block type to write.
+     */
+    enum BlockType
+    {
+        BlockType_Invalid = 0,
+        BlockType_Audio   = 1,
+        BlockType_Video   = 2,
+        BlockType_Raw     = 3
     };
 
@@ -90,45 +100,28 @@
      * @param   a_pszFilename   Name of the file to create.
      * @param   a_fOpen         File open mode of type RTFILE_O_.
-     * @param   a_enmMode       Operation mode.
      * @param   a_enmAudioCodec Audio codec to use.
      * @param   a_enmVideoCodec Video codec to use.
      *
      * @returns VBox status code. */
-    int create(const char *a_pszFilename, uint64_t a_fOpen, WebMWriter::Mode a_enmMode,
+    int Create(const char *a_pszFilename, uint64_t a_fOpen,
                WebMWriter::AudioCodec a_enmAudioCodec, WebMWriter::VideoCodec a_enmVideoCodec);
 
     /** Closes output file. */
-    void close();
+    int Close(void);
 
-    /**
-     * Writes WebM header to file.
-     * Should be called before any writeBlock call.
-     *
-     * @param a_pCfg Pointer to VPX Codec configuration structure.
-     * @param a_pFps Framerate information (frames per second).
-     *
-     * @returns VBox status code.
-     */
-    int writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_rational *a_pFps);
+    int AddAudioTrack(float fSamplingHz, float fOutputHz, uint8_t cChannels, uint8_t cBitDepth);
+
+    int AddVideoTrack(uint16_t uWidth, uint16_t uHeight, double dbFPS);
 
     /**
      * Writes a block of compressed data.
      *
-     * @param a_pCfg Pointer to VPX Codec configuration structure.
-     * @param a_pPkt VPX data packet.
+     * @param blockType         Block type to write.
+     * @param pvData            Pointer to block data to write.
+     * @param cbData            Size (in bytes) of block data to write.
      *
      * @returns VBox status code.
      */
-    int writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt);
-
-    /**
-     * Writes WebM footer.
-     * No other write functions should be called after this one.
-     *
-     * @param a_u64Hash Hash value for the data written.
-     *
-     * @returns VBox status code.
-     */
-    int writeFooter(uint32_t a_u64Hash);
+    int WriteBlock(WebMWriter::BlockType blockType, const void *pvData, size_t cbData);
 
     /**
@@ -137,5 +130,5 @@
      * @returns File size in bytes.
      */
-    uint64_t getFileSize(void);
+    uint64_t GetFileSize(void);
 
     /**
@@ -144,5 +137,5 @@
      * @returns Available storage free space.
      */
-    uint64_t getAvailableSpace(void);
+    uint64_t GetAvailableSpace(void);
 
 private:
Index: /trunk/src/VBox/Main/src-client/VideoRec.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/VideoRec.cpp	(revision 65255)
+++ /trunk/src/VBox/Main/src-client/VideoRec.cpp	(revision 65256)
@@ -578,8 +578,5 @@
         {
             AssertPtr(pStream->pEBML);
-            int rc = pStream->pEBML->writeFooter(0);
-            AssertRC(rc);
-
-            pStream->pEBML->close();
+            pStream->pEBML->Close();
 
             vpx_img_free(&pStream->Codec.VPX.RawImage);
@@ -659,16 +656,13 @@
     size_t pos = 0;
 
-    /* By default we enable both, video and audio recording (if available). */
+    /* By default we enable everything (if available). */
+    bool fHasVideoTrack = true;
 #ifdef VBOX_WITH_AUDIO_VIDEOREC
-    WebMWriter::Mode enmMode = WebMWriter::Mode_AudioVideo;
-#else
-    WebMWriter::Mode enmMode = WebMWriter::Mode_Video;
+    bool fHasAudioTrack = true;
 #endif
 
-    do {
-
-        com::Utf8Str key, value;
-        pos = options.parseKeyValue(key, value, pos);
-
+    com::Utf8Str key, value;
+    while ((pos = options.parseKeyValue(key, value, pos)) != com::Utf8Str::npos)
+    {
         if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0)
         {
@@ -687,24 +681,24 @@
             else
             {
-                LogRel(("VideoRec: Settings quality deadline to '%s'\n", value.c_str()));
+                LogRel(("VideoRec: Setting quality deadline to '%s'\n", value.c_str()));
                 pStream->uEncoderDeadline = value.toUInt32();
             }
         }
-        if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
+        else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0)
         {
 #ifdef VBOX_WITH_AUDIO_VIDEOREC
             if (value.compare("false", Utf8Str::CaseInsensitive) == 0) /* Disable audio. */
             {
-                enmMode = WebMWriter::Mode_Audio;
+                fHasVideoTrack = false;
                 LogRel(("VideoRec: Only audio will be recorded\n"));
             }
 #endif
         }
-        if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
+        else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0)
         {
 #ifdef VBOX_WITH_AUDIO_VIDEOREC
             if (value.compare("false", Utf8Str::CaseInsensitive)) /* Disable audio. */
             {
-                enmMode = WebMWriter::Mode_Video;
+                fHasAudioTrack = false;
                 LogRel(("VideoRec: Only video will be recorded\n"));
             }
@@ -714,5 +708,5 @@
             LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str()));
 
-    } while(pos != com::Utf8Str::npos);
+    } /* while */
 
     uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
@@ -723,6 +717,5 @@
 #endif
 
-    int rc = pStream->pEBML->create(pszFile, fOpen, enmMode,
-                                    WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_VP8);
+    int rc = pStream->pEBML->Create(pszFile, fOpen, WebMWriter::AudioCodec_Opus, WebMWriter::VideoCodec_VP8);
     if (RT_FAILURE(rc))
     {
@@ -745,7 +738,15 @@
     pStream->uDelay = 1000 / uFps;
 
-    struct vpx_rational arg_framerate = { (int)uFps, 1 };
-    rc = pStream->pEBML->writeHeader(&pStream->Codec.VPX.Config, &arg_framerate);
-    AssertRCReturn(rc, rc);
+    if (fHasVideoTrack)
+    {
+        rc = pStream->pEBML->AddVideoTrack(pStream->Codec.VPX.Config.g_w, /* Width */
+                                           pStream->Codec.VPX.Config.g_h, /* Height */
+                                           uFps);
+        if (RT_FAILURE(rc))
+        {
+            LogRel(("VideoRec: Failed to add video track to output file '%s' (%Rrc)\n", pszFile, rc));
+            return rc;
+        }
+    }
 
     /* Initialize codec */
@@ -762,8 +763,10 @@
         return VERR_NO_MEMORY;
     }
+
     pStream->pu8YuvBuf = pStream->Codec.VPX.RawImage.planes[0];
 
     pCtx->fEnabled = true;
     pStream->fEnabled = true;
+
     return VINF_SUCCESS;
 }
@@ -832,10 +835,10 @@
     if (pCtx->uMaxFileSize > 0)
     {
-        uint64_t sizeInMB = pStream->pEBML->getFileSize() / (1024 * 1024);
+        uint64_t sizeInMB = pStream->pEBML->GetFileSize() / (1024 * 1024);
         if(sizeInMB >= pCtx->uMaxFileSize)
             return true;
     }
     /* Check for available free disk space */
-    if (pStream->pEBML->getAvailableSpace() < 0x100000)
+    if (pStream->pEBML->GetAvailableSpace() < 0x100000)
     {
         LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n"));
@@ -873,14 +876,19 @@
     for (;;)
     {
-        const vpx_codec_cx_pkt_t *pkt = vpx_codec_get_cx_data(&pStream->Codec.VPX.CodecCtx, &iter);
-        if (!pkt)
-            break;
-        switch (pkt->kind)
+        const vpx_codec_cx_pkt_t *pPacket = vpx_codec_get_cx_data(&pStream->Codec.VPX.CodecCtx, &iter);
+        if (!pPacket)
+            break;
+
+        switch (pPacket->kind)
         {
             case VPX_CODEC_CX_FRAME_PKT:
-                rc = pStream->pEBML->writeBlock(&pStream->Codec.VPX.Config, pkt);
+            {
+                WebMWriter::BlockData_VP8 blockData { &pStream->Codec.VPX.Config, pPacket };
+                rc = pStream->pEBML->WriteBlock(WebMWriter::BlockType_Video, &blockData, sizeof(blockData));
                 break;
+            }
+
             default:
-                LogFlow(("Unexpected CODEC Packet.\n"));
+                LogFlow(("Unexpected CODEC packet kind %ld\n", pPacket->kind));
                 break;
         }
