Index: /trunk/src/VBox/Main/include/DrvAudioVideoRec.h
===================================================================
--- /trunk/src/VBox/Main/include/DrvAudioVideoRec.h	(revision 65196)
+++ /trunk/src/VBox/Main/include/DrvAudioVideoRec.h	(revision 65197)
@@ -1,9 +1,9 @@
 /* $Id$ */
 /** @file
- * VirtualBox driver interface to video recording backend.
+ * VirtualBox driver interface video recording audio backend.
  */
 
 /*
- * Copyright (C) 2014-2016 Oracle Corporation
+ * Copyright (C) 2014-2017 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
Index: /trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp	(revision 65196)
+++ /trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp	(revision 65197)
@@ -24,5 +24,4 @@
 #include "DrvAudioVideoRec.h"
 #include "ConsoleImpl.h"
-#include "ConsoleVRDPServer.h"
 
 #include "Logging.h"
@@ -30,4 +29,5 @@
 #include "../../Devices/Audio/DrvAudio.h"
 #include "../../Devices/Audio/AudioMixBuffer.h"
+#include "EbmlWriter.h"
 
 #include <iprt/mem.h>
@@ -40,8 +40,58 @@
 #include <VBox/err.h>
 
+#include <opus.h>
 
 /*********************************************************************************************************************************
 *   Structures and Typedefs                                                                                                      *
 *********************************************************************************************************************************/
+
+/**
+ * Enumeration for audio/video recording driver recording mode.
+ */
+typedef enum AVRECMODE
+{
+    /** Unknown / invalid recording mode. */
+    AVRECMODE_UNKNOWN     = 0,
+    /** Only record audio.
+     *  This mode does not need to talk to the video recording driver,
+     *  as this driver then simply creates an own WebM container. */
+    AVRECMODE_AUDIO       = 1,
+    /** Records audio and video.
+     *  Needs to work together with the video recording driver in
+     *  order to get a full-featured WebM container. */
+    AVRECMODE_AUDIO_VIDEO = 2
+} AVRECMODE;
+
+/**
+ * Structure for keeping codec specific data.
+ */
+typedef struct AVRECCODEC
+{
+    union
+    {
+        struct
+        {
+            /** Encoder we're going to use. */
+            OpusEncoder *pEnc;
+        } Opus;
+    };
+} AVRECCODEC, *PAVRECCODEC;
+
+/**
+ * Audio video recording output stream.
+ */
+typedef struct AVRECSTREAMOUT
+{
+    /** Note: Always must come first! */
+    PDMAUDIOSTREAM       Stream;
+    /** The PCM properties of this stream. */
+    PDMAUDIOPCMPROPS     Props;
+    uint64_t             old_ticks;
+    uint64_t             cSamplesSentPerSec;
+    /** Codec data.
+     *  As every stream can be different, one codec per stream is needed. */
+    AVRECCODEC           Codec;
+} AVRECSTREAMOUT, *PAVRECSTREAMOUT;
+
 /**
  * Video recording audio driver instance data.
@@ -57,15 +107,10 @@
     /** Pointer to the DrvAudio port interface that is above us. */
     PPDMIAUDIOCONNECTOR  pDrvAudio;
+    /** Recording mode. */
+    AVRECMODE            enmMode;
+    /** Pointer to WebM container to write recorded audio data to.
+     *  See the AVRECMODE enumeration for more information. */
+    WebMWriter          *pEBML;
 } DRVAUDIOVIDEOREC, *PDRVAUDIOVIDEOREC;
-
-typedef struct AVRECSTREAMOUT
-{
-    /** Note: Always must come first! */
-    PDMAUDIOSTREAM       Stream;
-    /** The PCM properties of this stream. */
-    PDMAUDIOPCMPROPS     Props;
-    uint64_t             old_ticks;
-    uint64_t             cSamplesSentPerSec;
-} AVRECSTREAMOUT, *PAVRECSTREAMOUT;
 
 /** Makes DRVAUDIOVIDEOREC out of PDMIHOSTAUDIO. */
@@ -87,6 +132,29 @@
     if (RT_SUCCESS(rc))
     {
-        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);
+
+        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. */
+        }
     }
 
@@ -119,8 +187,48 @@
 static DECLCALLBACK(int) drvAudioVideoRecInit(PPDMIHOSTAUDIO pInterface)
 {
-    RT_NOREF(pInterface);
+    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+
     LogFlowFuncEnter();
 
-    return VINF_SUCCESS;
+    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
+
+    pThis->enmMode = AVRECMODE_AUDIO;
+
+    int rc;
+
+    try
+    {
+        switch (pThis->enmMode)
+        {
+            case AVRECMODE_AUDIO:
+            {
+                pThis->pEBML = new WebMWriter();
+                pThis->pEBML->create("/tmp/test.webm", WebMWriter::Mode_Audio); /** @todo FIX! */
+                break;
+            }
+
+            case AVRECMODE_AUDIO_VIDEO:
+            {
+                break;
+            }
+
+            default:
+                rc = VERR_NOT_SUPPORTED;
+                break;
+        }
+    }
+    catch (std::bad_alloc)
+    {
+        rc = VERR_NO_MEMORY;
+    }
+
+    if (RT_FAILURE(rc))
+    {
+        LogRel(("VideoRec: Audio recording driver failed to initialize, rc=%Rrc\n", rc));
+    }
+    else
+        LogRel2(("VideoRec: Audio recording driver initialized\n"));
+
+    return rc;
 }
 
@@ -179,7 +287,10 @@
 
     /*
-     * Call the VRDP server with the data.
+     * Call the encoder server with the data.
      */
     uint32_t cReadTotal = 0;
+
+    uint8_t pvDst[_4K];
+    opus_int32 cbDst = _4K;
 
     PPDMAUDIOSAMPLE pSamples;
@@ -192,4 +303,8 @@
         cReadTotal = cRead;
 
+        opus_int32 orc = opus_encode(pStreamOut->Codec.Opus.pEnc, (opus_int16 *)pSamples, cRead, pvDst, cbDst);
+        if (orc != OPUS_OK)
+            LogFunc(("Encoding (1) failed: %s\n", opus_strerror(orc)));
+
         if (rc == VINF_TRY_AGAIN)
         {
@@ -197,4 +312,6 @@
                                     &pSamples, &cRead);
 
+
+
             cReadTotal += cRead;
         }
@@ -205,5 +322,5 @@
     /*
      * Always report back all samples acquired, regardless of whether the
-     * VRDP server actually did process those.
+     * encoder actually did process those.
      */
     if (pcbWritten)
@@ -215,16 +332,15 @@
 
 
-static int avRecDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
-{
-    RT_NOREF(pInterface, pStream);
-
-    return VINF_SUCCESS;
-}
-
-
 static int avRecDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
 {
-    RT_NOREF(pInterface);
-    RT_NOREF(pStream);
+    PDRVAUDIOVIDEOREC pThis      = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
+    RT_NOREF(pThis);
+    PAVRECSTREAMOUT   pStreamOut = (PAVRECSTREAMOUT)pStream;
+
+    if (pStreamOut->Codec.Opus.pEnc)
+    {
+        opus_encoder_destroy(pStreamOut->Codec.Opus.pEnc);
+        pStreamOut->Codec.Opus.pEnc = NULL;
+    }
 
     return VINF_SUCCESS;
@@ -254,5 +370,38 @@
 static DECLCALLBACK(void) drvAudioVideoRecShutdown(PPDMIHOSTAUDIO pInterface)
 {
-    RT_NOREF(pInterface);
+    LogFlowFuncEnter();
+
+    PDRVAUDIOVIDEOREC pThis = PDMIHOSTAUDIO_2_DRVAUDIOVIDEOREC(pInterface);
+
+    int rc;
+
+    switch (pThis->enmMode)
+    {
+        case AVRECMODE_AUDIO:
+        {
+            if (pThis->pEBML)
+            {
+                rc = pThis->pEBML->writeFooter(0 /* Hash */);
+                AssertRC(rc);
+
+                pThis->pEBML->close();
+
+                delete pThis->pEBML;
+                pThis->pEBML = NULL;
+            }
+            break;
+        }
+
+        case AVRECMODE_AUDIO_VIDEO:
+        {
+            break;
+        }
+
+        default:
+            rc = VERR_NOT_SUPPORTED;
+            break;
+    }
+
+    LogFlowFuncLeaveRC(rc);
 }
 
@@ -296,11 +445,8 @@
     AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
 
-    int rc;
-    if (pStream->enmDir == PDMAUDIODIR_IN)
-        rc = avRecDestroyStreamIn(pInterface,  pStream);
-    else
-        rc = avRecDestroyStreamOut(pInterface, pStream);
-
-    return rc;
+    if (pStream->enmDir == PDMAUDIODIR_OUT)
+        return avRecDestroyStreamOut(pInterface, pStream);
+
+    return VINF_SUCCESS;
 }
 
@@ -399,5 +545,5 @@
     AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
 
-    LogRel(("Audio: Initializing video recording driver\n"));
+    LogRel(("Audio: Initializing video recording audio driver\n"));
     LogFlowFunc(("fFlags=0x%x\n", fFlags));
 
Index: /trunk/src/VBox/Main/src-client/EbmlWriter.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/EbmlWriter.cpp	(revision 65196)
+++ /trunk/src/VBox/Main/src-client/EbmlWriter.cpp	(revision 65197)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2013-2016 Oracle Corporation
+ * Copyright (C) 2013-2017 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -220,5 +220,4 @@
 class WebMWriter_Impl
 {
-
     struct CueEntry
     {
@@ -228,5 +227,8 @@
     };
 
-    bool            m_bDebug;
+    /** Operation mode. */
+    WebMWriter::Mode m_enmMode;
+
+    bool            m_fDebug;
     int64_t         m_iLastPtsMs;
     int64_t         m_iInitialPtsMs;
@@ -249,10 +251,11 @@
     std::list<CueEntry> m_CueList;
 
-    Ebml m_Ebml;
+    Ebml                m_Ebml;
 
 public:
 
     WebMWriter_Impl() :
-        m_bDebug(false),
+        m_enmMode(WebMWriter::Mode_Unknown),
+        m_fDebug(false),
         m_iLastPtsMs(-1),
         m_iInitialPtsMs(-1),
@@ -269,6 +272,5 @@
         m_bClusterOpen(false) {}
 
-    void writeHeader(const vpx_codec_enc_cfg_t *a_pCfg,
-                     const struct vpx_rational *a_pFps)
+    void writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const struct vpx_rational *a_pFps)
     {
         m_Ebml.subStart(EBML)
@@ -291,24 +293,57 @@
         m_uTrackPos = RTFileTell(m_Ebml.getFile());
 
-        m_Ebml.subStart(Tracks)
-              .subStart(TrackEntry)
-              .serializeUnsignedInteger(TrackNumber, 1);
-
-        m_uTrackIdPos = RTFileTell(m_Ebml.getFile());
-
-        m_Ebml.serializeUnsignedInteger(TrackUID, 0, 4)
-              .serializeUnsignedInteger(TrackType, 1)
-              .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)
-              .subEnd(Tracks);
-    }
-
-    void writeBlock(const vpx_codec_enc_cfg_t *a_pCfg,
-                                const vpx_codec_cx_pkt_t *a_pPkt)
+        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"). */
+
+            m_Ebml.serializeUnsignedInteger(TrackUID, 1 /* UID */, 4)
+                  .serializeUnsignedInteger(TrackType, 2 /* Audio */)
+                  .serializeString(CodecID, "A_OPUS")
+                  .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)
     {
         uint16_t uBlockTimecode = 0;
@@ -316,5 +351,5 @@
         bool     bStartCluster = false;
 
-        /* Calculate the PTS of this frame in milliseconds */
+        /* Calculate the PTS of this frame in milliseconds. */
         iPtsMs = a_pPkt->data.frame.pts * 1000
                  * (uint64_t) a_pCfg->g_timebase.num / a_pCfg->g_timebase.den;
@@ -326,5 +361,5 @@
           m_iInitialPtsMs = m_iLastPtsMs;
 
-        /* Calculate the relative time of this block */
+        /* Calculate the relative time of this block. */
         if (iPtsMs - m_uClusterTimecode > 65536)
             bStartCluster = 1;
@@ -338,5 +373,5 @@
                 m_Ebml.subEnd(Cluster);
 
-            /* Open a new cluster */
+            /* Open a new cluster. */
             uBlockTimecode = 0;
             m_bClusterOpen = true;
@@ -354,5 +389,5 @@
         }
 
-        /* Write a Simple Block */
+        /* Write a "Simple Block". */
         m_Ebml.writeClassId(SimpleBlock);
         m_Ebml.writeUnsignedInteger(0x10000000u | (4 + a_pPkt->data.frame.sz), 4);
@@ -389,5 +424,5 @@
         if (!RT_SUCCESS(rc)) throw rc;
 
-        m_Ebml.serializeUnsignedInteger(TrackUID, (m_bDebug ? 0xDEADBEEF : a_u64Hash), 4);
+        m_Ebml.serializeUnsignedInteger(TrackUID, (m_fDebug ? 0xDEADBEEF : a_u64Hash), 4);
 
         rc = RTFileSeek(m_Ebml.getFile(), 0, RTFILE_SEEK_END, NULL);
@@ -431,5 +466,5 @@
         char szVersion[64];
         RTStrPrintf(szVersion, sizeof(szVersion), "vpxenc%s",
-                    m_bDebug ? "" : vpx_codec_version_str());
+                    m_fDebug ? "" : vpx_codec_version_str());
 
         m_Ebml.subStart(Info)
@@ -449,6 +484,8 @@
 }
 
-int WebMWriter::create(const char *a_pszFilename)
-{
+int WebMWriter::create(const char *a_pszFilename, WebMWriter::Mode a_enmMode)
+{
+    m_Impl->m_enmMode = a_enmMode;
+
     return m_Impl->m_Ebml.create(a_pszFilename);
 }
Index: /trunk/src/VBox/Main/src-client/EbmlWriter.h
===================================================================
--- /trunk/src/VBox/Main/src-client/EbmlWriter.h	(revision 65196)
+++ /trunk/src/VBox/Main/src-client/EbmlWriter.h	(revision 65197)
@@ -5,5 +5,5 @@
 
 /*
- * Copyright (C) 2013-2016 Oracle Corporation
+ * Copyright (C) 2013-2017 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -32,20 +32,41 @@
 class WebMWriter
 {
+
 public:
+
+    /**
+     * Operation mode -- this specifies what to write.
+     */
+    enum Mode
+    {
+        /** 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
+    };
+
+public:
+
     WebMWriter();
     virtual ~WebMWriter();
 
-    /** Creates output file
+    /**
+     * Creates output file.
      *
      * @param   a_pszFilename   Name of the file to create.
+     * @param   a_enmMode       Operation mode.
      *
      * @returns VBox status code. */
-    int create(const char *a_pszFilename);
+    int create(const char *a_pszFilename, WebMWriter::Mode a_enmMode);
 
     /* Closes output file. */
     void close();
 
-    /** Writes WebM header to file.
-     *
+    /**
+     * Writes WebM header to file.
      * Should be called before any writeBlock call.
      *
@@ -57,5 +78,6 @@
     int writeHeader(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_rational *a_pFps);
 
-    /** Writes a block of compressed data
+    /**
+     * Writes a block of compressed data.
      *
      * @param a_pCfg Pointer to VPX Codec configuration structure.
@@ -66,6 +88,6 @@
     int writeBlock(const vpx_codec_enc_cfg_t *a_pCfg, const vpx_codec_cx_pkt_t *a_pPkt);
 
-    /** Writes WebM footer.
-     *
+    /**
+     * Writes WebM footer.
      * No other write functions should be called after this one.
      *
@@ -76,20 +98,22 @@
     int writeFooter(uint32_t a_u64Hash);
 
-    /** Gets current output file size.
+    /**
+     * Gets current output file size.
      *
      * @returns File size in bytes.
      */
-    uint64_t getFileSize();
+    uint64_t getFileSize(void);
 
-    /** Gets current free storage space
-     * available for the file.
+    /**
+     * Gets current free storage space available for the file.
      *
      * @returns Available storage free space.
      */
-    uint64_t getAvailableSpace();
+    uint64_t getAvailableSpace(void);
 
 private:
+
     /** WebMWriter implementation.
-     * To isolate some include files */
+     *  To isolate some include files. */
     WebMWriter_Impl *m_Impl;
 
@@ -97,3 +121,3 @@
 };
 
-#endif
+#endif /* ____EBMLWRITER */
Index: /trunk/src/VBox/Main/src-client/VideoRec.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/VideoRec.cpp	(revision 65196)
+++ /trunk/src/VBox/Main/src-client/VideoRec.cpp	(revision 65197)
@@ -647,5 +647,5 @@
      * other important file, causing unintentional data loss. */
 
-    int rc = pStream->pEBML->create(pszFile);
+    int rc = pStream->pEBML->create(pszFile, WebMWriter::Mode_AudioVideo); /** @todo Make mode configurable. */
     if (RT_FAILURE(rc))
     {
