From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Main/src-client/DrvAudioVRDE.cpp | 823 ++++++++++++++++++++++++++++++ 1 file changed, 823 insertions(+) create mode 100644 src/VBox/Main/src-client/DrvAudioVRDE.cpp (limited to 'src/VBox/Main/src-client/DrvAudioVRDE.cpp') diff --git a/src/VBox/Main/src-client/DrvAudioVRDE.cpp b/src/VBox/Main/src-client/DrvAudioVRDE.cpp new file mode 100644 index 00000000..c3a4be53 --- /dev/null +++ b/src/VBox/Main/src-client/DrvAudioVRDE.cpp @@ -0,0 +1,823 @@ +/* $Id: DrvAudioVRDE.cpp $ */ +/** @file + * VRDE audio backend for Main. + */ + +/* + * Copyright (C) 2013-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO +#include "LoggingNew.h" + +#include +#include "DrvAudioVRDE.h" +#include "ConsoleImpl.h" +#include "ConsoleVRDPServer.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * VRDE stream. + */ +typedef struct VRDESTREAM +{ + /** Common part. */ + PDMAUDIOBACKENDSTREAM Core; + /** 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 +{ + /** Pointer to audio VRDE object. */ + AudioVRDE *pAudioVRDE; + /** Pointer to the driver instance structure. */ + PPDMDRVINS pDrvIns; + /** Pointer to the VRDP's console object. */ + ConsoleVRDPServer *pConsoleVRDPServer; + /** Number of connected clients to this VRDE instance. */ + uint32_t cClients; + /** Interface to the driver above us (DrvAudio). */ + PDMIHOSTAUDIOPORT *pIHostAudioPort; + /** Pointer to host audio interface. */ + PDMIHOSTAUDIO IHostAudio; +} DRVAUDIOVRDE; +/** Pointer to the instance data for an VRDE audio driver. */ +typedef DRVAUDIOVRDE *PDRVAUDIOVRDE; + + +/********************************************************************************************************************************* +* Class AudioVRDE * +*********************************************************************************************************************************/ + +AudioVRDE::AudioVRDE(Console *pConsole) + : AudioDriver(pConsole) + , mpDrv(NULL) +{ + RTCritSectInit(&mCritSect); +} + + +AudioVRDE::~AudioVRDE(void) +{ + RTCritSectEnter(&mCritSect); + if (mpDrv) + { + mpDrv->pAudioVRDE = NULL; + mpDrv = NULL; + } + RTCritSectLeave(&mCritSect); + RTCritSectDelete(&mCritSect); +} + + +int AudioVRDE::configureDriver(PCFGMNODE pLunCfg, PCVMMR3VTABLE pVMM) +{ + return AudioDriver::configureDriver(pLunCfg, pVMM); +} + + +void AudioVRDE::onVRDEClientConnect(uint32_t uClientID) +{ + RT_NOREF(uClientID); + + RTCritSectEnter(&mCritSect); + if (mpDrv) + { + mpDrv->cClients++; + LogRel2(("Audio: VRDE client connected (#%u)\n", mpDrv->cClients)); + +#if 0 /* later, maybe */ + /* + * The first client triggers a device change event in both directions + * so that can start talking to the audio device. + * + * Note! Should be okay to stay in the critical section here, as it's only + * used at construction and destruction time. + */ + if (mpDrv->cClients == 1) + { + VMSTATE enmState = PDMDrvHlpVMState(mpDrv->pDrvIns); + if (enmState <= VMSTATE_POWERING_OFF) + { + PDMIHOSTAUDIOPORT *pIHostAudioPort = mpDrv->pIHostAudioPort; + AssertPtr(pIHostAudioPort); + pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_OUT, NULL /*pvUser*/); + pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_IN, NULL /*pvUser*/); + } + } +#endif + } + RTCritSectLeave(&mCritSect); +} + + +void AudioVRDE::onVRDEClientDisconnect(uint32_t uClientID) +{ + RT_NOREF(uClientID); + + RTCritSectEnter(&mCritSect); + if (mpDrv) + { + Assert(mpDrv->cClients > 0); + mpDrv->cClients--; + LogRel2(("Audio: VRDE client disconnected (%u left)\n", mpDrv->cClients)); +#if 0 /* later maybe */ + /* + * The last client leaving triggers a device change event in both + * directions so the audio devices can stop wasting time trying to + * talk to us. (There is an additional safeguard in + * drvAudioVrdeHA_StreamGetStatus.) + */ + if (mpDrv->cClients == 0) + { + VMSTATE enmState = PDMDrvHlpVMState(mpDrv->pDrvIns); + if (enmState <= VMSTATE_POWERING_OFF) + { + PDMIHOSTAUDIOPORT *pIHostAudioPort = mpDrv->pIHostAudioPort; + AssertPtr(pIHostAudioPort); + pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_OUT, NULL /*pvUser*/); + pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_IN, NULL /*pvUser*/); + } + } +#endif + } + RTCritSectLeave(&mCritSect); +} + + +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); + LogFlowFunc(("cbData=%#x\n", cbData)); + + 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->cbStream = sizeof(VRDESTREAM); + pBackendCfg->fFlags = 0; + 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; +} + + +/** + * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate} + */ +static DECLCALLBACK(int) drvAudioVrdeHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, + PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq) +{ + PDRVAUDIOVRDE pThis = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio); + PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream; + AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER); + AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER); + AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER); + + /* + * Only create a stream if we have clients. + */ + int vrc; + NOREF(pThis); +#if 0 /* later maybe */ + if (pThis->cClients == 0) + { + LogFunc(("No clients, failing with VERR_AUDIO_STREAM_COULD_NOT_CREATE.\n")); + vrc = VERR_AUDIO_STREAM_COULD_NOT_CREATE; + } + else +#endif + { + /* + * The VRDP server does its own mixing and resampling because it may be + * sending the audio to any number of different clients all with different + * formats (including clients which hasn't yet connected). So, it desires + * the raw data from the mixer (somewhat akind to stereo signed 64-bit, + * see st_sample_t and PDMAUDIOFRAME). + */ + PDMAudioPropsInitEx(&pCfgAcq->Props, 8 /*64-bit*/, true /*fSigned*/, 2 /*stereo*/, + 22050 /*Hz - VRDP_AUDIO_CHUNK_INTERNAL_FREQ_HZ*/, + true /*fLittleEndian*/, true /*fRaw*/); + + /* According to the VRDP docs (VRDP_AUDIO_CHUNK_TIME_MS), the VRDP server + stores audio in 200ms chunks. */ + const uint32_t cFramesVrdpServer = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 200 /*ms*/); + + if (pCfgReq->enmDir == PDMAUDIODIR_IN) + { + pCfgAcq->Backend.cFramesBufferSize = cFramesVrdpServer; + pCfgAcq->Backend.cFramesPeriod = cFramesVrdpServer / 4; /* This is utter non-sense, but whatever. */ + pCfgAcq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesPreBuffering * cFramesVrdpServer + / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1); + + vrc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, PDMAudioPropsFramesToBytes(&pCfgAcq->Props, cFramesVrdpServer)); + } + else + { + /** @todo r=bird: So, if VRDP does 200ms chunks, why do we report 100ms + * buffer and 20ms period? How does these parameters at all correlate + * with the above comment?!? */ + pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 20 /*ms*/); + pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/); + pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2; + vrc = VINF_SUCCESS; + } + + PDMAudioStrmCfgCopy(&pStreamVRDE->Cfg, pCfgAcq); + } + return vrc; +} + + +/** + * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy} + */ +static DECLCALLBACK(int) drvAudioVrdeHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, + bool fImmediate) +{ + PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio); + PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream; + AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER); + RT_NOREF(fImmediate); + + if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN) + { + LogFlowFunc(("Calling SendAudioInputEnd\n")); + if (pDrv->pConsoleVRDPServer) + pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL); + + if (pStreamVRDE->In.pCircBuf) + { + RTCircBufDestroy(pStreamVRDE->In.pCircBuf); + pStreamVRDE->In.pCircBuf = 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 vrc; + if (!pDrv->pConsoleVRDPServer) + { + LogRelMax(32, ("Audio: VRDP console not ready (enable)\n")); + vrc = VERR_AUDIO_STREAM_NOT_READY; + } + else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN) + { + vrc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE, + PDMAudioPropsMilliToFrames(&pStreamVRDE->Cfg.Props, 200 /*ms*/), + PDMAudioPropsHz(&pStreamVRDE->Cfg.Props), + PDMAudioPropsChannels(&pStreamVRDE->Cfg.Props), + PDMAudioPropsSampleBits(&pStreamVRDE->Cfg.Props)); + LogFlowFunc(("SendAudioInputBegin returns %Rrc\n", vrc)); + if (vrc == VERR_NOT_SUPPORTED) + { + LogRelMax(64, ("Audio: No VRDE client connected, so no input recording available\n")); + vrc = VERR_AUDIO_STREAM_NOT_READY; + } + } + else + vrc = VINF_SUCCESS; + LogFlowFunc(("returns %Rrc\n", vrc)); + return vrc; +} + + +/** + * @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 vrc; + if (!pDrv->pConsoleVRDPServer) + { + LogRelMax(32, ("Audio: VRDP console not ready (disable)\n")); + vrc = VERR_AUDIO_STREAM_NOT_READY; + } + else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN) + { + LogFlowFunc(("Calling SendAudioInputEnd\n")); + pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL /* pvUserCtx */); + vrc = VINF_SUCCESS; + } + else + vrc = VINF_SUCCESS; + LogFlowFunc(("returns %Rrc\n", vrc)); + return vrc; +} + + +/** + * @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; + } + 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,pfnStreamGetState} + */ +static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvAudioVrdeHA_StreamGetState(PPDMIHOSTAUDIO pInterface, + PPDMAUDIOBACKENDSTREAM pStream) +{ + PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio); + AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID); + + return pDrv->cClients > 0 ? PDMHOSTAUDIOSTREAMSTATE_OKAY : PDMHOSTAUDIOSTREAMSTATE_INACTIVE; +} + + +/** + * @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; + + /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */ + if (pDrv->cClients) + return PDMAudioPropsFramesToBytes(&pStreamVRDE->Cfg.Props, pStreamVRDE->Cfg.Backend.cFramesBufferSize); + return 0; +} + + +/** + * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay} + */ +static DECLCALLBACK(int) drvAudioVrdeHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream, + const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) +{ + PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio); + AssertPtr(pDrv); + AssertPtrReturn(pStream, VERR_INVALID_POINTER); + PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream; + if (cbBuf) + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER); + + if (!pDrv->pConsoleVRDPServer) + return VERR_NOT_AVAILABLE; + + /* Prepate the format. */ + PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->Cfg.Props; + VRDEAUDIOFORMAT const uVrdpFormat = VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps), + PDMAudioPropsChannels(pProps), + PDMAudioPropsSampleBits(pProps), + pProps->fSigned); + Assert(uVrdpFormat == VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps), 2, 64, true)); + + /** @todo r=bird: there was some incoherent mumbling about "using the + * internal counter to track if we (still) can write to the VRDP + * server or if need to wait another round (time slot)". However it + * wasn't accessing any internal counter nor doing anything else + * sensible, so I've removed it. */ + + uint32_t cFrames = PDMAudioPropsBytesToFrames(&pStream->pStream->Cfg.Props, cbBuf); + Assert(cFrames == cbBuf / (sizeof(uint64_t) * 2)); + pDrv->pConsoleVRDPServer->SendAudioSamples(pvBuf, cFrames, uVrdpFormat); + + Log3Func(("cFramesWritten=%RU32\n", cFrames)); + *pcbWritten = PDMAudioPropsFramesToBytes(&pStream->pStream->Cfg.Props, cFrames); + Assert(*pcbWritten == cbBuf); + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable} + */ +static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream) +{ + RT_NOREF(pInterface); + PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream; + + AssertReturn(pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN, 0); + uint32_t cbRet = (uint32_t)RTCircBufUsed(pStreamVRDE->In.pCircBuf); + Log4Func(("returns %#x\n", cbRet)); + return cbRet; +} + + +/** + * @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); + PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream; + AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + AssertReturn(cbBuf, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbRead, VERR_INVALID_PARAMETER); + + *pcbRead = 0; + while (cbBuf > 0 && RTCircBufUsed(pStreamVRDE->In.pCircBuf) > 0) + { + size_t cbData = 0; + void *pvData = NULL; + RTCircBufAcquireReadBlock(pStreamVRDE->In.pCircBuf, cbBuf, &pvData, &cbData); + + memcpy(pvBuf, pvData, cbData); + + RTCircBufReleaseReadBlock(pStreamVRDE->In.pCircBuf, cbData); + + *pcbRead += (uint32_t)cbData; + cbBuf -= (uint32_t)cbData; + pvData = (uint8_t *)pvData + cbData; + } + + LogFlowFunc(("returns %#x bytes\n", *pcbRead)); + return VINF_SUCCESS; +} + + +/********************************************************************************************************************************* +* PDMIBASE * +*********************************************************************************************************************************/ + +/** + * @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; +} + + +/********************************************************************************************************************************* +* PDMDRVREG * +*********************************************************************************************************************************/ + +/** + * @interface_method_impl{PDMDRVREG,pfnPowerOff} + */ +/*static*/ DECLCALLBACK(void) AudioVRDE::drvPowerOff(PPDMDRVINS pDrvIns) +{ + PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE); + LogFlowFuncEnter(); + + if (pThis->pConsoleVRDPServer) + pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL); +} + + +/** + * @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(); + + /** @todo For runtime detach maybe: + if (pThis->pConsoleVRDPServer) + pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL); */ + + /* + * 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. + */ + AudioVRDE *pAudioVRDE = pThis->pAudioVRDE; + if (pAudioVRDE) + { + RTCritSectEnter(&pAudioVRDE->mCritSect); + pAudioVRDE->mpDrv = NULL; + pThis->pAudioVRDE = NULL; + RTCritSectLeave(&pAudioVRDE->mCritSect); + } +} + + +/** + * 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; + pThis->cClients = 0; + /* IBase */ + pDrvIns->IBase.pfnQueryInterface = drvAudioVrdeQueryInterface; + /* IHostAudio */ + pThis->IHostAudio.pfnGetConfig = drvAudioVrdeHA_GetConfig; + pThis->IHostAudio.pfnGetDevices = NULL; + pThis->IHostAudio.pfnSetDevice = NULL; + pThis->IHostAudio.pfnGetStatus = drvAudioVrdeHA_GetStatus; + pThis->IHostAudio.pfnDoOnWorkerThread = NULL; + pThis->IHostAudio.pfnStreamConfigHint = NULL; + pThis->IHostAudio.pfnStreamCreate = drvAudioVrdeHA_StreamCreate; + pThis->IHostAudio.pfnStreamInitAsync = NULL; + pThis->IHostAudio.pfnStreamDestroy = drvAudioVrdeHA_StreamDestroy; + pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL; + pThis->IHostAudio.pfnStreamEnable = drvAudioVrdeHA_StreamEnable; + pThis->IHostAudio.pfnStreamDisable = drvAudioVrdeHA_StreamDisable; + pThis->IHostAudio.pfnStreamPause = drvAudioVrdeHA_StreamPause; + pThis->IHostAudio.pfnStreamResume = drvAudioVrdeHA_StreamResume; + pThis->IHostAudio.pfnStreamDrain = drvAudioVrdeHA_StreamDrain; + pThis->IHostAudio.pfnStreamGetState = drvAudioVrdeHA_StreamGetState; + pThis->IHostAudio.pfnStreamGetPending = NULL; + pThis->IHostAudio.pfnStreamGetWritable = drvAudioVrdeHA_StreamGetWritable; + pThis->IHostAudio.pfnStreamPlay = drvAudioVrdeHA_StreamPlay; + pThis->IHostAudio.pfnStreamGetReadable = drvAudioVrdeHA_StreamGetReadable; + pThis->IHostAudio.pfnStreamCapture = drvAudioVrdeHA_StreamCapture; + + /* + * Resolve the interface to the driver above us. + */ + pThis->pIHostAudioPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHOSTAUDIOPORT); + AssertPtrReturn(pThis->pIHostAudioPort, VERR_PDM_MISSING_INTERFACE_ABOVE); + + /* Get the Console object pointer. */ + com::Guid ConsoleUuid(COM_IIDOF(IConsole)); + IConsole *pIConsole = (IConsole *)PDMDrvHlpQueryGenericUserObject(pDrvIns, ConsoleUuid.raw()); + AssertLogRelReturn(pIConsole, VERR_INTERNAL_ERROR_3); + Console *pConsole = static_cast(pIConsole); + AssertLogRelReturn(pConsole, VERR_INTERNAL_ERROR_3); + + /* Get the console VRDP object pointer. */ + pThis->pConsoleVRDPServer = pConsole->i_consoleVRDPServer(); + AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pConsoleVRDPServer) || !pThis->pConsoleVRDPServer, + ("pConsoleVRDPServer=%p\n", pThis->pConsoleVRDPServer), VERR_INVALID_POINTER); + + /* Get the AudioVRDE object pointer. */ + pThis->pAudioVRDE = pConsole->i_getAudioVRDE(); + AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pAudioVRDE), ("pAudioVRDE=%p\n", pThis->pAudioVRDE), VERR_INVALID_POINTER); + RTCritSectEnter(&pThis->pAudioVRDE->mCritSect); + pThis->pAudioVRDE->mpDrv = pThis; + RTCritSectLeave(&pThis->pAudioVRDE->mCritSect); + + return VINF_SUCCESS; +} + + +/** + * 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 */ + NULL, + /* pfnDetach */ + NULL, + /* pfnPowerOff */ + AudioVRDE::drvPowerOff, + /* pfnSoftReset */ + NULL, + /* u32EndVersion */ + PDM_DRVREG_VERSION +}; + -- cgit v1.2.3