Index: /trunk/src/VBox/Main/src-client/DrvAudioVRDE.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/DrvAudioVRDE.cpp	(revision 88459)
+++ /trunk/src/VBox/Main/src-client/DrvAudioVRDE.cpp	(revision 88460)
@@ -44,5 +44,24 @@
 *********************************************************************************************************************************/
 /**
- * Audio VRDE driver instance data.
+ * VRDE stream.
+ */
+typedef struct VRDESTREAM
+{
+    /** The stream's acquired configuration. */
+    PDMAUDIOSTREAMCFG   Cfg;
+    union
+    {
+        struct
+        {
+            /** Circular buffer for holding the recorded audio frames from the host. */
+            PRTCIRCBUF  pCircBuf;
+        } In;
+    };
+} VRDESTREAM;
+/** Pointer to a VRDE stream. */
+typedef VRDESTREAM *PVRDESTREAM;
+
+/**
+ * VRDE (host) audio driver instance data.
  */
 typedef struct DRVAUDIOVRDE
@@ -52,6 +71,4 @@
     /** Pointer to the driver instance structure. */
     PPDMDRVINS           pDrvIns;
-    /** Pointer to host audio interface. */
-    PDMIHOSTAUDIO        IHostAudio;
     /** Pointer to the VRDP's console object. */
     ConsoleVRDPServer   *pConsoleVRDPServer;
@@ -60,28 +77,180 @@
     /** Number of connected clients to this VRDE instance. */
     uint32_t             cClients;
-} DRVAUDIOVRDE, *PDRVAUDIOVRDE;
-
-typedef struct VRDESTREAM
-{
-    /** The stream's acquired configuration. */
-    PPDMAUDIOSTREAMCFG pCfg;
-    union
-    {
-        struct
-        {
-            /** Circular buffer for holding the recorded audio frames from the host. */
-            PRTCIRCBUF  pCircBuf;
-        } In;
-    };
-} VRDESTREAM, *PVRDESTREAM;
+    /** Pointer to host audio interface. */
+    PDMIHOSTAUDIO        IHostAudio;
+} DRVAUDIOVRDE;
+/** Pointer to the instance data for an VRDE audio driver. */
+typedef DRVAUDIOVRDE *PDRVAUDIOVRDE;
 
 /* Sanity. */
 AssertCompileSize(PDMAUDIOFRAME, sizeof(int64_t) * 2 /* st_sample_t using by VRDP server */);
 
-static int vrdeCreateStreamIn(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
-{
-    RT_NOREF(pCfgReq);
-    AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
-
+
+
+/*********************************************************************************************************************************
+*   Class AudioVRDE                                                                                                              *
+*********************************************************************************************************************************/
+
+AudioVRDE::AudioVRDE(Console *pConsole)
+    : AudioDriver(pConsole)
+    , mpDrv(NULL)
+{
+}
+
+
+AudioVRDE::~AudioVRDE(void)
+{
+    if (mpDrv)
+    {
+        mpDrv->pAudioVRDE = NULL;
+        mpDrv = NULL;
+    }
+}
+
+
+/**
+ * @copydoc AudioDriver::configureDriver
+ */
+int AudioVRDE::configureDriver(PCFGMNODE pLunCfg)
+{
+    int rc = CFGMR3InsertInteger(pLunCfg, "Object", (uintptr_t)this);
+    AssertRCReturn(rc, rc);
+    CFGMR3InsertInteger(pLunCfg, "ObjectVRDPServer", (uintptr_t)mpConsole->i_consoleVRDPServer());
+    AssertRCReturn(rc, rc);
+
+    return AudioDriver::configureDriver(pLunCfg);
+}
+
+
+void AudioVRDE::onVRDEClientConnect(uint32_t uClientID)
+{
+    RT_NOREF(uClientID);
+
+    LogRel2(("Audio: VRDE client connected\n"));
+    if (mpDrv)
+        mpDrv->cClients++;
+}
+
+
+void AudioVRDE::onVRDEClientDisconnect(uint32_t uClientID)
+{
+    RT_NOREF(uClientID);
+
+    LogRel2(("Audio: VRDE client disconnected\n"));
+    Assert(mpDrv->cClients);
+    if (mpDrv)
+        mpDrv->cClients--;
+}
+
+
+int AudioVRDE::onVRDEControl(bool fEnable, uint32_t uFlags)
+{
+    RT_NOREF(fEnable, uFlags);
+    LogFlowThisFunc(("fEnable=%RTbool, uFlags=0x%x\n", fEnable, uFlags));
+
+    if (mpDrv == NULL)
+        return VERR_INVALID_STATE;
+
+    return VINF_SUCCESS; /* Never veto. */
+}
+
+
+/**
+ * Marks the beginning of sending captured audio data from a connected
+ * RDP client.
+ *
+ * @returns VBox status code.
+ * @param   pvContext               The context; in this case a pointer to a
+ *                                  VRDESTREAMIN structure.
+ * @param   pVRDEAudioBegin         Pointer to a VRDEAUDIOINBEGIN structure.
+ */
+int AudioVRDE::onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin)
+{
+    AssertPtrReturn(pvContext, VERR_INVALID_POINTER);
+    AssertPtrReturn(pVRDEAudioBegin, VERR_INVALID_POINTER);
+    PVRDESTREAM pVRDEStrmIn = (PVRDESTREAM)pvContext;
+    AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
+
+#ifdef LOG_ENABLED
+    VRDEAUDIOFORMAT const audioFmt = pVRDEAudioBegin->fmt;
+    LogFlowFunc(("cbSample=%RU32, iSampleHz=%d, cChannels=%d, cBits=%d, fUnsigned=%RTbool\n",
+                 VRDE_AUDIO_FMT_BYTES_PER_SAMPLE(audioFmt), VRDE_AUDIO_FMT_SAMPLE_FREQ(audioFmt),
+                 VRDE_AUDIO_FMT_CHANNELS(audioFmt), VRDE_AUDIO_FMT_BITS_PER_SAMPLE(audioFmt), VRDE_AUDIO_FMT_SIGNED(audioFmt)));
+#endif
+
+    return VINF_SUCCESS;
+}
+
+
+int AudioVRDE::onVRDEInputData(void *pvContext, const void *pvData, uint32_t cbData)
+{
+    PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pvContext;
+    AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
+
+    void  *pvBuf = NULL;
+    size_t cbBuf = 0;
+    RTCircBufAcquireWriteBlock(pStreamVRDE->In.pCircBuf, cbData, &pvBuf, &cbBuf);
+
+    if (cbBuf)
+        memcpy(pvBuf, pvData, cbBuf);
+
+    RTCircBufReleaseWriteBlock(pStreamVRDE->In.pCircBuf, cbBuf);
+
+    if (cbBuf < cbData)
+        LogRelMax(999, ("VRDE: Capturing audio data lost %zu bytes\n", cbData - cbBuf)); /** @todo Use an error counter. */
+
+    return VINF_SUCCESS; /** @todo r=andy How to tell the caller if we were not able to handle *all* input data? */
+}
+
+
+int AudioVRDE::onVRDEInputEnd(void *pvContext)
+{
+    RT_NOREF(pvContext);
+    return VINF_SUCCESS;
+}
+
+
+int AudioVRDE::onVRDEInputIntercept(bool fEnabled)
+{
+    RT_NOREF(fEnabled);
+    return VINF_SUCCESS; /* Never veto. */
+}
+
+
+
+/*********************************************************************************************************************************
+*   PDMIHOSTAUDIO                                                                                                                *
+*********************************************************************************************************************************/
+
+/**
+ * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
+{
+    RT_NOREF(pInterface);
+    AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
+
+    RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "VRDE");
+    pBackendCfg->cbStreamOut    = sizeof(VRDESTREAM);
+    pBackendCfg->cbStreamIn     = sizeof(VRDESTREAM);
+    pBackendCfg->cMaxStreamsIn  = UINT32_MAX;
+    pBackendCfg->cMaxStreamsOut = UINT32_MAX;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
+ */
+static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVrdeHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
+{
+    RT_NOREF(pInterface, enmDir);
+    return PDMAUDIOBACKENDSTS_RUNNING;
+}
+
+
+static int vrdeCreateStreamIn(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgAcq)
+{
     /*
      * The VRDP server does its own mixing and resampling as it may server
@@ -111,9 +280,6 @@
 
 
-static int vrdeCreateStreamOut(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
-{
-    RT_NOREF(pStreamVRDE, pCfgReq);
-    AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
-
+static int vrdeCreateStreamOut(PPDMAUDIOSTREAMCFG pCfgAcq)
+{
     /*
      * The VRDP server does its own mixing and resampling because it may be
@@ -139,110 +305,239 @@
 
 
-static int vrdeControlStreamOut(PDRVAUDIOVRDE pDrv, PVRDESTREAM pStreamVRDE, PDMAUDIOSTREAMCMD enmStreamCmd)
-{
-    RT_NOREF(pDrv, pStreamVRDE, enmStreamCmd);
-
-    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
-
-    return VINF_SUCCESS;
-}
-
-
-static int vrdeControlStreamIn(PDRVAUDIOVRDE pDrv, PVRDESTREAM pStreamVRDE, PDMAUDIOSTREAMCMD enmStreamCmd)
-{
-    LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
-
+/**
+ * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
+                                                     PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
+{
+    RT_NOREF(pInterface);
+    PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
+    AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
+    AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
+
+    int rc;
+    if (pCfgReq->enmDir == PDMAUDIODIR_IN)
+        rc = vrdeCreateStreamIn(pStreamVRDE, pCfgAcq);
+    else
+        rc = vrdeCreateStreamOut(pCfgAcq);
+    PDMAudioStrmCfgCopy(&pStreamVRDE->Cfg, pCfgAcq);
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
+{
+    PDRVAUDIOVRDE pDrv        = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    PVRDESTREAM   pStreamVRDE = (PVRDESTREAM)pStream;
+    AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
+
+    if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_OUT)
+    {
+        if (pDrv->pConsoleVRDPServer)
+            pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
+
+        if (pStreamVRDE->In.pCircBuf)
+        {
+            RTCircBufDestroy(pStreamVRDE->In.pCircBuf);
+            pStreamVRDE->In.pCircBuf = NULL;
+        }
+    }
+    pDrv->pConsoleVRDPServer = NULL;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
+{
+    PDRVAUDIOVRDE pDrv        = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    PVRDESTREAM   pStreamVRDE = (PVRDESTREAM)pStream;
+
+    int rc;
     if (!pDrv->pConsoleVRDPServer)
     {
-        LogRel(("Audio: VRDP console not ready yet\n"));
+        LogRelMax(32, ("Audio: VRDP console not ready (enable)\n"));
+        rc = VERR_AUDIO_STREAM_NOT_READY;
+    }
+    else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
+    {
+        rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE,
+                                                           PDMAudioPropsMilliToFrames(&pStreamVRDE->Cfg.Props, 200 /*ms*/),
+                                                           PDMAudioPropsHz(&pStreamVRDE->Cfg.Props),
+                                                           PDMAudioPropsChannels(&pStreamVRDE->Cfg.Props),
+                                                           PDMAudioPropsSampleBits(&pStreamVRDE->Cfg.Props));
+        if (rc == VERR_NOT_SUPPORTED)
+        {
+            LogRelMax(64, ("Audio: No VRDE client connected, so no input recording available\n"));
+            rc = VERR_AUDIO_STREAM_NOT_READY;
+        }
+    }
+    else
+        rc = VINF_SUCCESS;
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+/**
+ * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
+{
+    PDRVAUDIOVRDE pDrv        = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    PVRDESTREAM   pStreamVRDE = (PVRDESTREAM)pStream;
+
+    int rc;
+    if (!pDrv->pConsoleVRDPServer)
+    {
+        LogRelMax(32, ("Audio: VRDP console not ready (disable)\n"));
+        rc = VERR_AUDIO_STREAM_NOT_READY;
+    }
+    else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
+    {
+        pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL /* pvUserCtx */);
+        rc = VINF_SUCCESS;
+    }
+    else
+        rc = VINF_SUCCESS;
+    LogFlowFunc(("returns %Rrc\n", rc));
+    return rc;
+}
+
+
+/**
+ * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    RT_NOREF(pStream);
+
+    if (!pDrv->pConsoleVRDPServer)
+    {
+        LogRelMax(32, ("Audio: VRDP console not ready (pause)\n"));
         return VERR_AUDIO_STREAM_NOT_READY;
     }
-
-    int rc;
-
-    /* Initialize only if not already done. */
+    LogFlowFunc(("returns VINF_SUCCESS\n"));
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    RT_NOREF(pStream);
+
+    if (!pDrv->pConsoleVRDPServer)
+    {
+        LogRelMax(32, ("Audio: VRDP console not ready (resume)\n"));
+        return VERR_AUDIO_STREAM_NOT_READY;
+    }
+    LogFlowFunc(("returns VINF_SUCCESS\n"));
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
+{
+    RT_NOREF(pInterface, pStream);
+    LogFlowFunc(("returns VINF_SUCCESS\n"));
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_StreamControl(PPDMIHOSTAUDIO pInterface,
+                                                      PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
+{
+    /** @todo r=bird: I'd like to get rid of this pfnStreamControl method,
+     *        replacing it with individual StreamXxxx methods.  That would save us
+     *        potentally huge switches and more easily see which drivers implement
+     *        which operations (grep for pfnStreamXxxx). */
     switch (enmStreamCmd)
     {
         case PDMAUDIOSTREAMCMD_ENABLE:
-        {
-            rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE,
-                                                               PDMAudioPropsMilliToFrames(&pStreamVRDE->pCfg->Props, 200 /*ms*/),
-                                                               PDMAudioPropsHz(&pStreamVRDE->pCfg->Props),
-                                                               PDMAudioPropsChannels(&pStreamVRDE->pCfg->Props),
-                                                               PDMAudioPropsSampleBits(&pStreamVRDE->pCfg->Props));
-            if (rc == VERR_NOT_SUPPORTED)
-            {
-                LogRel(("Audio: No VRDE client connected, so no input recording available\n"));
-                rc = VERR_AUDIO_STREAM_NOT_READY;
-            }
-
+            return drvAudioVrdeHA_StreamEnable(pInterface, pStream);
+        case PDMAUDIOSTREAMCMD_DISABLE:
+            return drvAudioVrdeHA_StreamDisable(pInterface, pStream);
+        case PDMAUDIOSTREAMCMD_PAUSE:
+            return drvAudioVrdeHA_StreamPause(pInterface, pStream);
+        case PDMAUDIOSTREAMCMD_RESUME:
+            return drvAudioVrdeHA_StreamResume(pInterface, pStream);
+        case PDMAUDIOSTREAMCMD_DRAIN:
+            return drvAudioVrdeHA_StreamDrain(pInterface, pStream);
+
+        case PDMAUDIOSTREAMCMD_END:
+        case PDMAUDIOSTREAMCMD_32BIT_HACK:
+        case PDMAUDIOSTREAMCMD_INVALID:
+            /* no default*/
             break;
-        }
-
-        case PDMAUDIOSTREAMCMD_DISABLE:
-        {
-            pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL /* pvUserCtx */);
-            rc = VINF_SUCCESS;
-
-            break;
-        }
-
-        case PDMAUDIOSTREAMCMD_PAUSE:
-        {
-            rc = VINF_SUCCESS;
-            break;
-        }
-
-        case PDMAUDIOSTREAMCMD_RESUME:
-        {
-            rc = VINF_SUCCESS;
-            break;
-        }
-
-        default:
-        {
-            rc = VERR_NOT_SUPPORTED;
-            break;
-        }
-    }
-
-    if (RT_FAILURE(rc))
-        LogFunc(("Failed with %Rrc\n", rc));
-
-    return rc;
-}
-
-/**
- * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
- */
-static DECLCALLBACK(int) drvAudioVrdeHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
-                                                      void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
-{
-    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
-    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
-    AssertPtrReturn(pvBuf,      VERR_INVALID_POINTER);
-    AssertReturn(uBufSize,         VERR_INVALID_PARAMETER);
-    /* puRead is optional. */
-
+    }
+    return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
+ */
+static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
+{
+    RT_NOREF(pInterface);
     PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
 
-    size_t cbData = 0;
-
-    if (RTCircBufUsed(pStreamVRDE->In.pCircBuf))
-    {
-        void *pvData;
-
-        RTCircBufAcquireReadBlock(pStreamVRDE->In.pCircBuf, uBufSize, &pvData, &cbData);
-
-        if (cbData)
-            memcpy(pvBuf, pvData, cbData);
-
-        RTCircBufReleaseReadBlock(pStreamVRDE->In.pCircBuf, cbData);
-    }
-
-    if (puRead)
-        *puRead = (uint32_t)cbData;
-
-    return VINF_SUCCESS;
+    if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
+    {
+        /* Return frames instead of bytes here
+         * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
+        return (uint32_t)PDMAudioPropsBytesToFrames(&pStreamVRDE->Cfg.Props, RTCircBufUsed(pStreamVRDE->In.pCircBuf));
+    }
+    return 0;
+}
+
+
+/**
+ * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
+ */
+static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    RT_NOREF(pStream);
+
+    /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */
+    if (pDrv->cClients)
+        return _16K * sizeof(PDMAUDIOFRAME);
+    return 0;
+}
+
+
+/**
+ * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
+ */
+static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioVrdeHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
+{
+    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+    RT_NOREF(pStream);
+
+    PDMAUDIOSTREAMSTS fStrmStatus = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
+
+    if (pDrv->cClients) /* If any clients are connected, flag the stream as enabled. */
+       fStrmStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
+
+    return fStrmStatus;
 }
 
@@ -266,5 +561,5 @@
 
     /* Prepate the format. */
-    PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->pCfg->Props;
+    PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->Cfg.Props;
     VRDEAUDIOFORMAT const uVrdpFormat = VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps),
                                                             PDMAudioPropsChannels(pProps),
@@ -307,198 +602,37 @@
 
 
-static int vrdeDestroyStreamIn(PDRVAUDIOVRDE pDrv, PVRDESTREAM pStreamVRDE)
-{
-    if (pDrv->pConsoleVRDPServer)
-        pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
-
-    if (pStreamVRDE->In.pCircBuf)
-    {
-        RTCircBufDestroy(pStreamVRDE->In.pCircBuf);
-        pStreamVRDE->In.pCircBuf = NULL;
-    }
-
-    return VINF_SUCCESS;
-}
-
-
-static int vrdeDestroyStreamOut(PDRVAUDIOVRDE pDrv, PVRDESTREAM pStreamVRDE)
-{
-    RT_NOREF(pDrv, pStreamVRDE);
-
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
- */
-static DECLCALLBACK(int) drvAudioVrdeHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
+/**
+ * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
+                                                      void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
 {
     RT_NOREF(pInterface);
-    AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
-
-    RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "VRDE");
-
-    pBackendCfg->cbStreamOut    = sizeof(VRDESTREAM);
-    pBackendCfg->cbStreamIn     = sizeof(VRDESTREAM);
-    pBackendCfg->cMaxStreamsIn  = UINT32_MAX;
-    pBackendCfg->cMaxStreamsOut = UINT32_MAX;
-
-    return VINF_SUCCESS;
-}
-
-
-/**
- * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
- */
-static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVrdeHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
-{
-    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
-    AssertPtrReturn(pDrv, PDMAUDIOBACKENDSTS_ERROR);
-
-    RT_NOREF(enmDir);
-
-    return PDMAUDIOBACKENDSTS_RUNNING;
-}
-
-
-/**
- * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
- */
-static DECLCALLBACK(int) drvAudioVrdeHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
-                                                     PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
-{
-    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
-    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
-    AssertPtrReturn(pCfgReq,    VERR_INVALID_POINTER);
-    AssertPtrReturn(pCfgAcq,    VERR_INVALID_POINTER);
-
-    RT_NOREF(pInterface);
-
     PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
-
-    int rc;
-    if (pCfgReq->enmDir == PDMAUDIODIR_IN)
-        rc = vrdeCreateStreamIn( pStreamVRDE, pCfgReq, pCfgAcq);
-    else
-        rc = vrdeCreateStreamOut(pStreamVRDE, pCfgReq, pCfgAcq);
-
-    if (RT_SUCCESS(rc))
-    {
-        pStreamVRDE->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
-        if (!pStreamVRDE->pCfg)
-            rc = VERR_NO_MEMORY;
-    }
-
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
- */
-static DECLCALLBACK(int) drvAudioVrdeHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
-{
-    RT_NOREF(pInterface);
-    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
-
-    PDRVAUDIOVRDE pDrv        = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
-    PVRDESTREAM   pStreamVRDE = (PVRDESTREAM)pStream;
-
-    if (!pStreamVRDE->pCfg) /* Not (yet) configured? Skip. */
-        return VINF_SUCCESS;
-
-    int rc;
-    if (pStreamVRDE->pCfg->enmDir == PDMAUDIODIR_IN)
-        rc = vrdeDestroyStreamIn(pDrv, pStreamVRDE);
-    else
-        rc = vrdeDestroyStreamOut(pDrv, pStreamVRDE);
-
-    if (RT_SUCCESS(rc))
-    {
-        PDMAudioStrmCfgFree(pStreamVRDE->pCfg);
-        pStreamVRDE->pCfg = NULL;
-    }
-
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
- */
-static DECLCALLBACK(int) drvAudioVrdeHA_StreamControl(PPDMIHOSTAUDIO pInterface,
-                                                      PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
-{
-    AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
-    AssertPtrReturn(pStream,    VERR_INVALID_POINTER);
-
-    PDRVAUDIOVRDE pDrv        = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
-    PVRDESTREAM   pStreamVRDE = (PVRDESTREAM)pStream;
-
-    if (!pStreamVRDE->pCfg) /* Not (yet) configured? Skip. */
-        return VINF_SUCCESS;
-
-    int rc;
-    if (pStreamVRDE->pCfg->enmDir == PDMAUDIODIR_IN)
-        rc = vrdeControlStreamIn(pDrv, pStreamVRDE, enmStreamCmd);
-    else
-        rc = vrdeControlStreamOut(pDrv, pStreamVRDE, enmStreamCmd);
-
-    return rc;
-}
-
-
-/**
- * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
- */
-static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
-{
-    RT_NOREF(pInterface);
-
-    PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
-
-    if (pStreamVRDE->pCfg->enmDir == PDMAUDIODIR_IN)
-    {
-        /* Return frames instead of bytes here
-         * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
-        return (uint32_t)PDMAUDIOSTREAMCFG_B2F(pStreamVRDE->pCfg, RTCircBufUsed(pStreamVRDE->In.pCircBuf));
-    }
-
-    return 0;
-}
-
-
-/**
- * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
- */
-static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
-{
-    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
-    RT_NOREF(pStream);
-
-    /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */
-    if (pDrv->cClients)
-        return _16K * sizeof(PDMAUDIOFRAME);
-    return 0;
-}
-
-
-/**
- * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
- */
-static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioVrdeHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
-{
-    PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
-    RT_NOREF(pStream);
-
-    PDMAUDIOSTREAMSTS fStrmStatus = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
-
-    if (pDrv->cClients) /* If any clients are connected, flag the stream as enabled. */
-       fStrmStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
-
-    return fStrmStatus;
-}
-
+    AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
+    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pcbRead, VERR_INVALID_PARAMETER);
+
+    size_t cbData = 0;
+    if (RTCircBufUsed(pStreamVRDE->In.pCircBuf))
+    {
+        void *pvData = NULL;
+        RTCircBufAcquireReadBlock(pStreamVRDE->In.pCircBuf, cbBuf, &pvData, &cbData);
+
+        if (cbData)
+            memcpy(pvBuf, pvData, cbData);
+
+        RTCircBufReleaseReadBlock(pStreamVRDE->In.pCircBuf, cbData);
+    }
+
+    *pcbRead = (uint32_t)cbData;
+    return VINF_SUCCESS;
+}
+
+
+/*********************************************************************************************************************************
+*   PDMIBASE                                                                                                                     *
+*********************************************************************************************************************************/
 
 /**
@@ -516,135 +650,7 @@
 
 
-AudioVRDE::AudioVRDE(Console *pConsole)
-    : AudioDriver(pConsole)
-    , mpDrv(NULL)
-{
-}
-
-
-AudioVRDE::~AudioVRDE(void)
-{
-    if (mpDrv)
-    {
-        mpDrv->pAudioVRDE = NULL;
-        mpDrv = NULL;
-    }
-}
-
-
-/**
- * @copydoc AudioDriver::configureDriver
- */
-int AudioVRDE::configureDriver(PCFGMNODE pLunCfg)
-{
-    int rc = CFGMR3InsertInteger(pLunCfg, "Object", (uintptr_t)this);
-    AssertRCReturn(rc, rc);
-    CFGMR3InsertInteger(pLunCfg, "ObjectVRDPServer", (uintptr_t)mpConsole->i_consoleVRDPServer());
-    AssertRCReturn(rc, rc);
-
-    return AudioDriver::configureDriver(pLunCfg);
-}
-
-
-void AudioVRDE::onVRDEClientConnect(uint32_t uClientID)
-{
-    RT_NOREF(uClientID);
-
-    LogRel2(("Audio: VRDE client connected\n"));
-    if (mpDrv)
-        mpDrv->cClients++;
-}
-
-
-void AudioVRDE::onVRDEClientDisconnect(uint32_t uClientID)
-{
-    RT_NOREF(uClientID);
-
-    LogRel2(("Audio: VRDE client disconnected\n"));
-    Assert(mpDrv->cClients);
-    if (mpDrv)
-        mpDrv->cClients--;
-}
-
-
-int AudioVRDE::onVRDEControl(bool fEnable, uint32_t uFlags)
-{
-    RT_NOREF(fEnable, uFlags);
-    LogFlowThisFunc(("fEnable=%RTbool, uFlags=0x%x\n", fEnable, uFlags));
-
-    if (mpDrv == NULL)
-        return VERR_INVALID_STATE;
-
-    return VINF_SUCCESS; /* Never veto. */
-}
-
-
-/**
- * Marks the beginning of sending captured audio data from a connected
- * RDP client.
- *
- * @returns VBox status code.
- * @param   pvContext               The context; in this case a pointer to a
- *                                  VRDESTREAMIN structure.
- * @param   pVRDEAudioBegin         Pointer to a VRDEAUDIOINBEGIN structure.
- */
-int AudioVRDE::onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin)
-{
-    AssertPtrReturn(pvContext, VERR_INVALID_POINTER);
-    AssertPtrReturn(pVRDEAudioBegin, VERR_INVALID_POINTER);
-
-    PVRDESTREAM pVRDEStrmIn = (PVRDESTREAM)pvContext;
-    AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
-
-    VRDEAUDIOFORMAT audioFmt = pVRDEAudioBegin->fmt;
-
-    int iSampleHz  = VRDE_AUDIO_FMT_SAMPLE_FREQ(audioFmt);     RT_NOREF(iSampleHz);
-    int cChannels  = VRDE_AUDIO_FMT_CHANNELS(audioFmt);        RT_NOREF(cChannels);
-    int cBits      = VRDE_AUDIO_FMT_BITS_PER_SAMPLE(audioFmt); RT_NOREF(cBits);
-    bool fUnsigned = VRDE_AUDIO_FMT_SIGNED(audioFmt);          RT_NOREF(fUnsigned);
-
-    LogFlowFunc(("cbSample=%RU32, iSampleHz=%d, cChannels=%d, cBits=%d, fUnsigned=%RTbool\n",
-                 VRDE_AUDIO_FMT_BYTES_PER_SAMPLE(audioFmt), iSampleHz, cChannels, cBits, fUnsigned));
-
-    return VINF_SUCCESS;
-}
-
-
-int AudioVRDE::onVRDEInputData(void *pvContext, const void *pvData, uint32_t cbData)
-{
-    PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pvContext;
-    AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
-
-    void  *pvBuf;
-    size_t cbBuf;
-
-    RTCircBufAcquireWriteBlock(pStreamVRDE->In.pCircBuf, cbData, &pvBuf, &cbBuf);
-
-    if (cbBuf)
-        memcpy(pvBuf, pvData, cbBuf);
-
-    RTCircBufReleaseWriteBlock(pStreamVRDE->In.pCircBuf, cbBuf);
-
-    if (cbBuf < cbData)
-        LogRel(("VRDE: Capturing audio data lost %zu bytes\n", cbData - cbBuf)); /** @todo Use an error counter. */
-
-    return VINF_SUCCESS; /** @todo r=andy How to tell the caller if we were not able to handle *all* input data? */
-}
-
-
-int AudioVRDE::onVRDEInputEnd(void *pvContext)
-{
-    RT_NOREF(pvContext);
-
-    return VINF_SUCCESS;
-}
-
-
-int AudioVRDE::onVRDEInputIntercept(bool fEnabled)
-{
-    RT_NOREF(fEnabled);
-    return VINF_SUCCESS; /* Never veto. */
-}
-
+/*********************************************************************************************************************************
+*   PDMDRVREG                                                                                                                    *
+*********************************************************************************************************************************/
 
 /**
@@ -737,4 +743,6 @@
     /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE. */
     pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
+    AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pConsoleVRDPServer) || !pThis->pConsoleVRDPServer,
+                          ("pConsoleVRDPServer=%p\n", pThis->pConsoleVRDPServer), VERR_INVALID_POINTER);
     pThis->cClients = 0;
 
@@ -747,4 +755,5 @@
 
     pThis->pAudioVRDE = (AudioVRDE *)pvUser;
+    AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pAudioVRDE), ("pAudioVRDE=%p\n", pThis->pAudioVRDE), VERR_INVALID_POINTER);
     pThis->pAudioVRDE->mpDrv = pThis;
 
