summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-client/DrvAudioVRDE.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-client/DrvAudioVRDE.cpp')
-rw-r--r--src/VBox/Main/src-client/DrvAudioVRDE.cpp892
1 files changed, 892 insertions, 0 deletions
diff --git a/src/VBox/Main/src-client/DrvAudioVRDE.cpp b/src/VBox/Main/src-client/DrvAudioVRDE.cpp
new file mode 100644
index 00000000..b0f6e3cc
--- /dev/null
+++ b/src/VBox/Main/src-client/DrvAudioVRDE.cpp
@@ -0,0 +1,892 @@
+/* $Id: DrvAudioVRDE.cpp $ */
+/** @file
+ * VRDE audio backend for Main.
+ */
+
+/*
+ * Copyright (C) 2013-2020 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
+#include "LoggingNew.h"
+
+#include <VBox/log.h>
+#include "DrvAudioVRDE.h"
+#include "ConsoleImpl.h"
+#include "ConsoleVRDPServer.h"
+
+#include <iprt/mem.h>
+#include <iprt/cdefs.h>
+#include <iprt/circbuf.h>
+
+#include <VBox/vmm/cfgm.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmaudioifs.h>
+#include <VBox/vmm/pdmaudioinline.h>
+#include <VBox/RemoteDesktop/VRDE.h>
+#include <VBox/err.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Audio VRDE driver instance data.
+ */
+typedef struct DRVAUDIOVRDE
+{
+ /** Pointer to audio VRDE object. */
+ AudioVRDE *pAudioVRDE;
+ /** Pointer to the driver instance structure. */
+ PPDMDRVINS pDrvIns;
+ /** Pointer to host audio interface. */
+ PDMIHOSTAUDIO IHostAudio;
+ /** Pointer to the VRDP's console object. */
+ ConsoleVRDPServer *pConsoleVRDPServer;
+ /** Pointer to the DrvAudio port interface that is above us. */
+ PPDMIAUDIOCONNECTOR pDrvAudio;
+ /** 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;
+
+/* 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);
+
+ pCfgAcq->Props.uHz = 22050; /* The VRDP server's internal frequency. */
+ pCfgAcq->Props.cChannels = 2;
+ pCfgAcq->Props.cbSample = 2; /* 16 bit. */
+ pCfgAcq->Props.fSigned = true;
+ pCfgAcq->Props.fSwapEndian = false;
+ pCfgAcq->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgAcq->Props.cbSample, pCfgAcq->Props.cChannels);
+
+ /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
+ const uint32_t cFramesVrdpServer = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 200 /*ms*/);
+
+ int rc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, PDMAudioPropsFramesToBytes(&pCfgAcq->Props, cFramesVrdpServer));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Because of historical reasons the VRDP server operates on st_sample_t structures internally,
+ * which is 2 * int64_t for left/right (stereo) channels.
+ *
+ * As the audio connector also uses this format, set the layout to "raw" and just let pass through
+ * the data without any layout modification needed.
+ */
+ pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW;
+ pCfgAcq->Backend.cFramesPeriod = cFramesVrdpServer;
+ pCfgAcq->Backend.cFramesBufferSize = pCfgAcq->Backend.cFramesPeriod * 2; /* Use "double buffering". */
+ pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod;
+ }
+
+ return rc;
+}
+
+
+static int vrdeCreateStreamOut(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
+{
+ RT_NOREF(pStreamVRDE, pCfgReq);
+
+ if (pCfgAcq)
+ {
+ /*
+ * Because of historical reasons the VRDP server operates on st_sample_t structures internally,
+ * which is 2 * int64_t for left/right (stereo) channels.
+ *
+ * As the audio connector also uses this format, set the layout to "raw" and just let pass through
+ * the data without any layout modification needed.
+ */
+ pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW;
+
+ pCfgAcq->Props.uHz = 22050; /* The VRDP server's internal frequency. */
+ pCfgAcq->Props.cChannels = 2;
+ pCfgAcq->Props.cbSample = 2; /* 16 bit. */
+ pCfgAcq->Props.fSigned = true;
+ pCfgAcq->Props.cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pCfgAcq->Props.cbSample, pCfgAcq->Props.cChannels);
+
+ /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
+ pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 20 /*ms*/);
+ pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/);
+ pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+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));
+
+ if (!pDrv->pConsoleVRDPServer)
+ {
+ LogRel(("Audio: VRDP console not ready yet\n"));
+ return VERR_AUDIO_STREAM_NOT_READY;
+ }
+
+ int rc;
+
+ /* Initialize only if not already done. */
+ switch (enmStreamCmd)
+ {
+ case PDMAUDIOSTREAMCMD_ENABLE:
+ {
+ rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE,
+ PDMAudioPropsMilliToFrames(&pStreamVRDE->pCfg->Props, 200 /*ms*/),
+ pStreamVRDE->pCfg->Props.uHz, pStreamVRDE->pCfg->Props.cChannels,
+ pStreamVRDE->pCfg->Props.cbSample * 8 /* Bit */);
+ if (rc == VERR_NOT_SUPPORTED)
+ {
+ LogRel(("Audio: No VRDE client connected, so no input recording available\n"));
+ rc = VERR_AUDIO_STREAM_NOT_READY;
+ }
+
+ 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,pfnInit}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_Init(PPDMIHOSTAUDIO pInterface)
+{
+ RT_NOREF(pInterface);
+ LogFlowFuncEnter();
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @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. */
+
+ 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;
+}
+
+
+/**
+ * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
+ const void *pvBuf, uint32_t uBufSize, uint32_t *puWritten)
+{
+ AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+ AssertPtrReturn(pStream, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(uBufSize, VERR_INVALID_PARAMETER);
+ /* puWritten is optional. */
+
+ PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+ PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
+
+ if (!pDrv->pConsoleVRDPServer)
+ return VERR_NOT_AVAILABLE;
+
+ /* Note: We get the number of *frames* in uBufSize
+ * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout) on stream creation. */
+ uint32_t cFramesLive = uBufSize;
+
+ PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->pCfg->Props;
+
+ VRDEAUDIOFORMAT format = VRDE_AUDIO_FMT_MAKE(pProps->uHz,
+ pProps->cChannels,
+ pProps->cbSample * 8 /* Bit */,
+ pProps->fSigned);
+
+ /* Use the internal counter to track if we (still) can write to the VRDP server
+ * or if we need to wait another round (time slot). */
+ uint32_t cFramesToWrite = cFramesLive;
+
+ Log3Func(("cFramesLive=%RU32, cFramesToWrite=%RU32\n", cFramesLive, cFramesToWrite));
+
+ /* Don't play more than available. */
+ if (cFramesToWrite > cFramesLive)
+ cFramesToWrite = cFramesLive;
+
+ int rc = VINF_SUCCESS;
+
+ PPDMAUDIOFRAME paSampleBuf = (PPDMAUDIOFRAME)pvBuf;
+ AssertPtr(paSampleBuf);
+
+ /*
+ * Call the VRDP server with the data.
+ */
+ uint32_t cfWritten = 0;
+ while (cFramesToWrite)
+ {
+ uint32_t cfChunk = cFramesToWrite; /** @todo For now write all at once. */
+
+ if (!cfChunk) /* Nothing to send. Bail out. */
+ break;
+
+ /* Note: The VRDP server expects int64_t samples per channel, regardless of the actual
+ * sample bits (e.g 8 or 16 bits). */
+ pDrv->pConsoleVRDPServer->SendAudioSamples(paSampleBuf + cfWritten, cfChunk /* Frames */, format);
+
+ cfWritten += cfChunk;
+ Assert(cfWritten <= cFramesLive);
+
+ Assert(cFramesToWrite >= cfChunk);
+ cFramesToWrite -= cfChunk;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Return frames instead of bytes here
+ * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
+ if (puWritten)
+ *puWritten = cfWritten;
+ }
+
+ return rc;
+}
+
+
+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)
+{
+ 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,pfnShutdown}
+ */
+static DECLCALLBACK(void) drvAudioVrdeHA_Shutdown(PPDMIHOSTAUDIO pInterface)
+{
+ PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
+ AssertPtrReturnVoid(pDrv);
+
+ if (pDrv->pConsoleVRDPServer)
+ pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
+}
+
+
+/**
+ * @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);
+ PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
+
+ PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->pCfg->Props;
+
+ RT_NOREF(pDrv, pProps);
+
+ /* Return frames instead of bytes here
+ * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
+ if (pDrv->cClients)
+ return _16K; /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */
+
+ 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;
+}
+
+
+/**
+ * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
+ */
+static DECLCALLBACK(int) drvAudioVrdeHA_StreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
+{
+ AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
+ AssertPtrReturn(pStream, VERR_INVALID_POINTER);
+
+ /* Nothing to do here for VRDE. */
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvAudioVrdeQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
+ return NULL;
+}
+
+
+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.
+ *
+ * @return IPRT 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. */
+}
+
+
+/**
+ * Construct a VRDE audio driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+/* static */
+DECLCALLBACK(int) AudioVRDE::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
+ RT_NOREF(fFlags);
+
+ AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
+ AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
+
+ LogRel(("Audio: Initializing VRDE driver\n"));
+ LogFlowFunc(("fFlags=0x%x\n", fFlags));
+
+ AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
+ ("Configuration error: Not possible to attach anything to this driver!\n"),
+ VERR_PDM_DRVINS_NO_ATTACH);
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvIns = pDrvIns;
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvAudioVrdeQueryInterface;
+ /* IHostAudio */
+ pThis->IHostAudio.pfnInit = drvAudioVrdeHA_Init;
+ pThis->IHostAudio.pfnShutdown = drvAudioVrdeHA_Shutdown;
+ pThis->IHostAudio.pfnGetConfig = drvAudioVrdeHA_GetConfig;
+ pThis->IHostAudio.pfnGetDevices = NULL;
+ pThis->IHostAudio.pfnGetStatus = drvAudioVrdeHA_GetStatus;
+ pThis->IHostAudio.pfnSetCallback = NULL;
+ pThis->IHostAudio.pfnStreamCreate = drvAudioVrdeHA_StreamCreate;
+ pThis->IHostAudio.pfnStreamDestroy = drvAudioVrdeHA_StreamDestroy;
+ pThis->IHostAudio.pfnStreamControl = drvAudioVrdeHA_StreamControl;
+ pThis->IHostAudio.pfnStreamGetReadable = drvAudioVrdeHA_StreamGetReadable;
+ pThis->IHostAudio.pfnStreamGetWritable = drvAudioVrdeHA_StreamGetWritable;
+ pThis->IHostAudio.pfnStreamGetPending = NULL;
+ pThis->IHostAudio.pfnStreamGetStatus = drvAudioVrdeHA_StreamGetStatus;
+ pThis->IHostAudio.pfnStreamIterate = drvAudioVrdeHA_StreamIterate;
+ pThis->IHostAudio.pfnStreamPlay = drvAudioVrdeHA_StreamPlay;
+ pThis->IHostAudio.pfnStreamCapture = drvAudioVrdeHA_StreamCapture;
+
+ /*
+ * Get the ConsoleVRDPServer object pointer.
+ */
+ void *pvUser;
+ int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
+ AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc), rc);
+
+ /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE. */
+ pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
+ pThis->cClients = 0;
+
+ /*
+ * Get the AudioVRDE object pointer.
+ */
+ pvUser = NULL;
+ rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
+ AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
+
+ pThis->pAudioVRDE = (AudioVRDE *)pvUser;
+ pThis->pAudioVRDE->mpDrv = pThis;
+
+ /*
+ * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
+ * Described in CFGM tree.
+ */
+ pThis->pDrvAudio = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
+ AssertMsgReturn(pThis->pDrvAudio, ("Configuration error: No upper interface specified!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnDestruct}
+ */
+/* static */
+DECLCALLBACK(void) AudioVRDE::drvDestruct(PPDMDRVINS pDrvIns)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+ PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
+ LogFlowFuncEnter();
+
+ /*
+ * If the AudioVRDE object is still alive, we must clear it's reference to
+ * us since we'll be invalid when we return from this method.
+ */
+ if (pThis->pAudioVRDE)
+ {
+ pThis->pAudioVRDE->mpDrv = NULL;
+ pThis->pAudioVRDE = NULL;
+ }
+}
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnAttach}
+ */
+/* static */
+DECLCALLBACK(int) AudioVRDE::drvAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
+{
+ RT_NOREF(pDrvIns, fFlags);
+
+ LogFlowFuncEnter();
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnDetach}
+ */
+/* static */
+DECLCALLBACK(void) AudioVRDE::drvDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
+{
+ RT_NOREF(pDrvIns, fFlags);
+
+ LogFlowFuncEnter();
+}
+
+
+/**
+ * VRDE audio driver registration record.
+ */
+const PDMDRVREG AudioVRDE::DrvReg =
+{
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "AudioVRDE",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "Audio driver for VRDE backend",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_AUDIO,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVAUDIOVRDE),
+ /* pfnConstruct */
+ AudioVRDE::drvConstruct,
+ /* pfnDestruct */
+ AudioVRDE::drvDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ AudioVRDE::drvAttach,
+ /* pfnDetach */
+ AudioVRDE::drvDetach,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+