VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/DrvAudioVRDE.cpp@ 94521

Last change on this file since 94521 was 94327, checked in by vboxsync, 2 years ago

Main/DrvAudioVRDE: Drop passing pointers through CFGM in favor of using VMM2USERMETHODS::pfnQueryGenericObject, bugref:10053

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.5 KB
Line 
1/* $Id: DrvAudioVRDE.cpp 94327 2022-03-22 15:56:55Z vboxsync $ */
2/** @file
3 * VRDE audio backend for Main.
4 */
5
6/*
7 * Copyright (C) 2013-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
23#include "LoggingNew.h"
24
25#include <VBox/log.h>
26#include "DrvAudioVRDE.h"
27#include "ConsoleImpl.h"
28#include "ConsoleVRDPServer.h"
29
30#include <iprt/mem.h>
31#include <iprt/cdefs.h>
32#include <iprt/circbuf.h>
33
34#include <VBox/vmm/cfgm.h>
35#include <VBox/vmm/pdmdrv.h>
36#include <VBox/vmm/pdmaudioifs.h>
37#include <VBox/vmm/pdmaudioinline.h>
38#include <VBox/vmm/vmmr3vtable.h>
39#include <VBox/RemoteDesktop/VRDE.h>
40#include <VBox/err.h>
41
42
43/*********************************************************************************************************************************
44* Structures and Typedefs *
45*********************************************************************************************************************************/
46/**
47 * VRDE stream.
48 */
49typedef struct VRDESTREAM
50{
51 /** Common part. */
52 PDMAUDIOBACKENDSTREAM Core;
53 /** The stream's acquired configuration. */
54 PDMAUDIOSTREAMCFG Cfg;
55 union
56 {
57 struct
58 {
59 /** Circular buffer for holding the recorded audio frames from the host. */
60 PRTCIRCBUF pCircBuf;
61 } In;
62 };
63} VRDESTREAM;
64/** Pointer to a VRDE stream. */
65typedef VRDESTREAM *PVRDESTREAM;
66
67/**
68 * VRDE (host) audio driver instance data.
69 */
70typedef struct DRVAUDIOVRDE
71{
72 /** Pointer to audio VRDE object. */
73 AudioVRDE *pAudioVRDE;
74 /** Pointer to the driver instance structure. */
75 PPDMDRVINS pDrvIns;
76 /** Pointer to the VRDP's console object. */
77 ConsoleVRDPServer *pConsoleVRDPServer;
78 /** Number of connected clients to this VRDE instance. */
79 uint32_t cClients;
80 /** Interface to the driver above us (DrvAudio). */
81 PDMIHOSTAUDIOPORT *pIHostAudioPort;
82 /** Pointer to host audio interface. */
83 PDMIHOSTAUDIO IHostAudio;
84} DRVAUDIOVRDE;
85/** Pointer to the instance data for an VRDE audio driver. */
86typedef DRVAUDIOVRDE *PDRVAUDIOVRDE;
87
88
89/*********************************************************************************************************************************
90* Class AudioVRDE *
91*********************************************************************************************************************************/
92
93AudioVRDE::AudioVRDE(Console *pConsole)
94 : AudioDriver(pConsole)
95 , mpDrv(NULL)
96{
97 RTCritSectInit(&mCritSect);
98}
99
100
101AudioVRDE::~AudioVRDE(void)
102{
103 RTCritSectEnter(&mCritSect);
104 if (mpDrv)
105 {
106 mpDrv->pAudioVRDE = NULL;
107 mpDrv = NULL;
108 }
109 RTCritSectLeave(&mCritSect);
110 RTCritSectDelete(&mCritSect);
111}
112
113
114int AudioVRDE::configureDriver(PCFGMNODE pLunCfg, PCVMMR3VTABLE pVMM)
115{
116 return AudioDriver::configureDriver(pLunCfg, pVMM);
117}
118
119
120void AudioVRDE::onVRDEClientConnect(uint32_t uClientID)
121{
122 RT_NOREF(uClientID);
123
124 RTCritSectEnter(&mCritSect);
125 if (mpDrv)
126 {
127 mpDrv->cClients++;
128 LogRel2(("Audio: VRDE client connected (#%u)\n", mpDrv->cClients));
129
130#if 0 /* later, maybe */
131 /*
132 * The first client triggers a device change event in both directions
133 * so that can start talking to the audio device.
134 *
135 * Note! Should be okay to stay in the critical section here, as it's only
136 * used at construction and destruction time.
137 */
138 if (mpDrv->cClients == 1)
139 {
140 VMSTATE enmState = PDMDrvHlpVMState(mpDrv->pDrvIns);
141 if (enmState <= VMSTATE_POWERING_OFF)
142 {
143 PDMIHOSTAUDIOPORT *pIHostAudioPort = mpDrv->pIHostAudioPort;
144 AssertPtr(pIHostAudioPort);
145 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_OUT, NULL /*pvUser*/);
146 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_IN, NULL /*pvUser*/);
147 }
148 }
149#endif
150 }
151 RTCritSectLeave(&mCritSect);
152}
153
154
155void AudioVRDE::onVRDEClientDisconnect(uint32_t uClientID)
156{
157 RT_NOREF(uClientID);
158
159 RTCritSectEnter(&mCritSect);
160 if (mpDrv)
161 {
162 Assert(mpDrv->cClients > 0);
163 mpDrv->cClients--;
164 LogRel2(("Audio: VRDE client disconnected (%u left)\n", mpDrv->cClients));
165#if 0 /* later maybe */
166 /*
167 * The last client leaving triggers a device change event in both
168 * directions so the audio devices can stop wasting time trying to
169 * talk to us. (There is an additional safeguard in
170 * drvAudioVrdeHA_StreamGetStatus.)
171 */
172 if (mpDrv->cClients == 0)
173 {
174 VMSTATE enmState = PDMDrvHlpVMState(mpDrv->pDrvIns);
175 if (enmState <= VMSTATE_POWERING_OFF)
176 {
177 PDMIHOSTAUDIOPORT *pIHostAudioPort = mpDrv->pIHostAudioPort;
178 AssertPtr(pIHostAudioPort);
179 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_OUT, NULL /*pvUser*/);
180 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_IN, NULL /*pvUser*/);
181 }
182 }
183#endif
184 }
185 RTCritSectLeave(&mCritSect);
186}
187
188
189int AudioVRDE::onVRDEControl(bool fEnable, uint32_t uFlags)
190{
191 RT_NOREF(fEnable, uFlags);
192 LogFlowThisFunc(("fEnable=%RTbool, uFlags=0x%x\n", fEnable, uFlags));
193
194 if (mpDrv == NULL)
195 return VERR_INVALID_STATE;
196
197 return VINF_SUCCESS; /* Never veto. */
198}
199
200
201/**
202 * Marks the beginning of sending captured audio data from a connected
203 * RDP client.
204 *
205 * @returns VBox status code.
206 * @param pvContext The context; in this case a pointer to a
207 * VRDESTREAMIN structure.
208 * @param pVRDEAudioBegin Pointer to a VRDEAUDIOINBEGIN structure.
209 */
210int AudioVRDE::onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin)
211{
212 AssertPtrReturn(pvContext, VERR_INVALID_POINTER);
213 AssertPtrReturn(pVRDEAudioBegin, VERR_INVALID_POINTER);
214 PVRDESTREAM pVRDEStrmIn = (PVRDESTREAM)pvContext;
215 AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
216
217#ifdef LOG_ENABLED
218 VRDEAUDIOFORMAT const audioFmt = pVRDEAudioBegin->fmt;
219 LogFlowFunc(("cbSample=%RU32, iSampleHz=%d, cChannels=%d, cBits=%d, fUnsigned=%RTbool\n",
220 VRDE_AUDIO_FMT_BYTES_PER_SAMPLE(audioFmt), VRDE_AUDIO_FMT_SAMPLE_FREQ(audioFmt),
221 VRDE_AUDIO_FMT_CHANNELS(audioFmt), VRDE_AUDIO_FMT_BITS_PER_SAMPLE(audioFmt), VRDE_AUDIO_FMT_SIGNED(audioFmt)));
222#endif
223
224 return VINF_SUCCESS;
225}
226
227
228int AudioVRDE::onVRDEInputData(void *pvContext, const void *pvData, uint32_t cbData)
229{
230 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pvContext;
231 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
232 LogFlowFunc(("cbData=%#x\n", cbData));
233
234 void *pvBuf = NULL;
235 size_t cbBuf = 0;
236 RTCircBufAcquireWriteBlock(pStreamVRDE->In.pCircBuf, cbData, &pvBuf, &cbBuf);
237
238 if (cbBuf)
239 memcpy(pvBuf, pvData, cbBuf);
240
241 RTCircBufReleaseWriteBlock(pStreamVRDE->In.pCircBuf, cbBuf);
242
243 if (cbBuf < cbData)
244 LogRelMax(999, ("VRDE: Capturing audio data lost %zu bytes\n", cbData - cbBuf)); /** @todo Use an error counter. */
245
246 return VINF_SUCCESS; /** @todo r=andy How to tell the caller if we were not able to handle *all* input data? */
247}
248
249
250int AudioVRDE::onVRDEInputEnd(void *pvContext)
251{
252 RT_NOREF(pvContext);
253 return VINF_SUCCESS;
254}
255
256
257int AudioVRDE::onVRDEInputIntercept(bool fEnabled)
258{
259 RT_NOREF(fEnabled);
260 return VINF_SUCCESS; /* Never veto. */
261}
262
263
264
265/*********************************************************************************************************************************
266* PDMIHOSTAUDIO *
267*********************************************************************************************************************************/
268
269/**
270 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
271 */
272static DECLCALLBACK(int) drvAudioVrdeHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
273{
274 RT_NOREF(pInterface);
275 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
276
277 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "VRDE");
278 pBackendCfg->cbStream = sizeof(VRDESTREAM);
279 pBackendCfg->fFlags = 0;
280 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
281 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
282
283 return VINF_SUCCESS;
284}
285
286
287/**
288 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
289 */
290static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVrdeHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
291{
292 RT_NOREF(pInterface, enmDir);
293 return PDMAUDIOBACKENDSTS_RUNNING;
294}
295
296
297/**
298 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
299 */
300static DECLCALLBACK(int) drvAudioVrdeHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
301 PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
302{
303 PDRVAUDIOVRDE pThis = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
304 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
305 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
306 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
307 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
308
309 /*
310 * Only create a stream if we have clients.
311 */
312 int rc;
313 NOREF(pThis);
314#if 0 /* later maybe */
315 if (pThis->cClients == 0)
316 {
317 LogFunc(("No clients, failing with VERR_AUDIO_STREAM_COULD_NOT_CREATE.\n"));
318 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
319 }
320 else
321#endif
322 {
323 /*
324 * The VRDP server does its own mixing and resampling because it may be
325 * sending the audio to any number of different clients all with different
326 * formats (including clients which hasn't yet connected). So, it desires
327 * the raw data from the mixer (somewhat akind to stereo signed 64-bit,
328 * see st_sample_t and PDMAUDIOFRAME).
329 */
330 PDMAudioPropsInitEx(&pCfgAcq->Props, 8 /*64-bit*/, true /*fSigned*/, 2 /*stereo*/,
331 22050 /*Hz - VRDP_AUDIO_CHUNK_INTERNAL_FREQ_HZ*/,
332 true /*fLittleEndian*/, true /*fRaw*/);
333
334 /* According to the VRDP docs (VRDP_AUDIO_CHUNK_TIME_MS), the VRDP server
335 stores audio in 200ms chunks. */
336 const uint32_t cFramesVrdpServer = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 200 /*ms*/);
337
338 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
339 {
340 pCfgAcq->Backend.cFramesBufferSize = cFramesVrdpServer;
341 pCfgAcq->Backend.cFramesPeriod = cFramesVrdpServer / 4; /* This is utter non-sense, but whatever. */
342 pCfgAcq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesPreBuffering * cFramesVrdpServer
343 / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1);
344
345 rc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, PDMAudioPropsFramesToBytes(&pCfgAcq->Props, cFramesVrdpServer));
346 }
347 else
348 {
349 /** @todo r=bird: So, if VRDP does 200ms chunks, why do we report 100ms
350 * buffer and 20ms period? How does these parameters at all correlate
351 * with the above comment?!? */
352 pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 20 /*ms*/);
353 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/);
354 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
355 rc = VINF_SUCCESS;
356 }
357
358 PDMAudioStrmCfgCopy(&pStreamVRDE->Cfg, pCfgAcq);
359 }
360 return rc;
361}
362
363
364/**
365 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
366 */
367static DECLCALLBACK(int) drvAudioVrdeHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
368 bool fImmediate)
369{
370 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
371 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
372 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
373 RT_NOREF(fImmediate);
374
375 if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
376 {
377 LogFlowFunc(("Calling SendAudioInputEnd\n"));
378 if (pDrv->pConsoleVRDPServer)
379 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
380
381 if (pStreamVRDE->In.pCircBuf)
382 {
383 RTCircBufDestroy(pStreamVRDE->In.pCircBuf);
384 pStreamVRDE->In.pCircBuf = NULL;
385 }
386 }
387
388 return VINF_SUCCESS;
389}
390
391
392/**
393 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
394 */
395static DECLCALLBACK(int) drvAudioVrdeHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
396{
397 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
398 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
399
400 int rc;
401 if (!pDrv->pConsoleVRDPServer)
402 {
403 LogRelMax(32, ("Audio: VRDP console not ready (enable)\n"));
404 rc = VERR_AUDIO_STREAM_NOT_READY;
405 }
406 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
407 {
408 rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE,
409 PDMAudioPropsMilliToFrames(&pStreamVRDE->Cfg.Props, 200 /*ms*/),
410 PDMAudioPropsHz(&pStreamVRDE->Cfg.Props),
411 PDMAudioPropsChannels(&pStreamVRDE->Cfg.Props),
412 PDMAudioPropsSampleBits(&pStreamVRDE->Cfg.Props));
413 LogFlowFunc(("SendAudioInputBegin returns %Rrc\n", rc));
414 if (rc == VERR_NOT_SUPPORTED)
415 {
416 LogRelMax(64, ("Audio: No VRDE client connected, so no input recording available\n"));
417 rc = VERR_AUDIO_STREAM_NOT_READY;
418 }
419 }
420 else
421 rc = VINF_SUCCESS;
422 LogFlowFunc(("returns %Rrc\n", rc));
423 return rc;
424}
425
426
427/**
428 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
429 */
430static DECLCALLBACK(int) drvAudioVrdeHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
431{
432 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
433 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
434
435 int rc;
436 if (!pDrv->pConsoleVRDPServer)
437 {
438 LogRelMax(32, ("Audio: VRDP console not ready (disable)\n"));
439 rc = VERR_AUDIO_STREAM_NOT_READY;
440 }
441 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
442 {
443 LogFlowFunc(("Calling SendAudioInputEnd\n"));
444 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL /* pvUserCtx */);
445 rc = VINF_SUCCESS;
446 }
447 else
448 rc = VINF_SUCCESS;
449 LogFlowFunc(("returns %Rrc\n", rc));
450 return rc;
451}
452
453
454/**
455 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
456 */
457static DECLCALLBACK(int) drvAudioVrdeHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
458{
459 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
460 RT_NOREF(pStream);
461
462 if (!pDrv->pConsoleVRDPServer)
463 {
464 LogRelMax(32, ("Audio: VRDP console not ready (pause)\n"));
465 return VERR_AUDIO_STREAM_NOT_READY;
466 }
467 LogFlowFunc(("returns VINF_SUCCESS\n"));
468 return VINF_SUCCESS;
469}
470
471
472/**
473 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
474 */
475static DECLCALLBACK(int) drvAudioVrdeHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
476{
477 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
478 RT_NOREF(pStream);
479
480 if (!pDrv->pConsoleVRDPServer)
481 {
482 LogRelMax(32, ("Audio: VRDP console not ready (resume)\n"));
483 return VERR_AUDIO_STREAM_NOT_READY;
484 }
485 LogFlowFunc(("returns VINF_SUCCESS\n"));
486 return VINF_SUCCESS;
487}
488
489
490/**
491 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
492 */
493static DECLCALLBACK(int) drvAudioVrdeHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
494{
495 RT_NOREF(pInterface, pStream);
496 LogFlowFunc(("returns VINF_SUCCESS\n"));
497 return VINF_SUCCESS;
498}
499
500
501/**
502 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
503 */
504static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvAudioVrdeHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
505 PPDMAUDIOBACKENDSTREAM pStream)
506{
507 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
508 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
509
510 return pDrv->cClients > 0 ? PDMHOSTAUDIOSTREAMSTATE_OKAY : PDMHOSTAUDIOSTREAMSTATE_INACTIVE;
511}
512
513
514/**
515 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
516 */
517static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
518{
519 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
520 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
521
522 /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */
523 if (pDrv->cClients)
524 return PDMAudioPropsFramesToBytes(&pStreamVRDE->Cfg.Props, pStreamVRDE->Cfg.Backend.cFramesBufferSize);
525 return 0;
526}
527
528
529/**
530 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
531 */
532static DECLCALLBACK(int) drvAudioVrdeHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
533 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
534{
535 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
536 AssertPtr(pDrv);
537 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
538 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
539 if (cbBuf)
540 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
541 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
542
543 if (!pDrv->pConsoleVRDPServer)
544 return VERR_NOT_AVAILABLE;
545
546 /* Prepate the format. */
547 PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->Cfg.Props;
548 VRDEAUDIOFORMAT const uVrdpFormat = VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps),
549 PDMAudioPropsChannels(pProps),
550 PDMAudioPropsSampleBits(pProps),
551 pProps->fSigned);
552 Assert(uVrdpFormat == VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps), 2, 64, true));
553
554 /** @todo r=bird: there was some incoherent mumbling about "using the
555 * internal counter to track if we (still) can write to the VRDP
556 * server or if need to wait another round (time slot)". However it
557 * wasn't accessing any internal counter nor doing anything else
558 * sensible, so I've removed it. */
559
560 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pStream->pStream->Cfg.Props, cbBuf);
561 Assert(cFrames == cbBuf / (sizeof(uint64_t) * 2));
562 pDrv->pConsoleVRDPServer->SendAudioSamples(pvBuf, cFrames, uVrdpFormat);
563
564 Log3Func(("cFramesWritten=%RU32\n", cFrames));
565 *pcbWritten = PDMAudioPropsFramesToBytes(&pStream->pStream->Cfg.Props, cFrames);
566 Assert(*pcbWritten == cbBuf);
567 return VINF_SUCCESS;
568}
569
570
571/**
572 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
573 */
574static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
575{
576 RT_NOREF(pInterface);
577 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
578
579 AssertReturn(pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN, 0);
580 uint32_t cbRet = (uint32_t)RTCircBufUsed(pStreamVRDE->In.pCircBuf);
581 Log4Func(("returns %#x\n", cbRet));
582 return cbRet;
583}
584
585
586/**
587 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
588 */
589static DECLCALLBACK(int) drvAudioVrdeHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
590 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
591{
592 RT_NOREF(pInterface);
593 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
594 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
595 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
596 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
597 AssertPtrReturn(pcbRead, VERR_INVALID_PARAMETER);
598
599 *pcbRead = 0;
600 while (cbBuf > 0 && RTCircBufUsed(pStreamVRDE->In.pCircBuf) > 0)
601 {
602 size_t cbData = 0;
603 void *pvData = NULL;
604 RTCircBufAcquireReadBlock(pStreamVRDE->In.pCircBuf, cbBuf, &pvData, &cbData);
605
606 memcpy(pvBuf, pvData, cbData);
607
608 RTCircBufReleaseReadBlock(pStreamVRDE->In.pCircBuf, cbData);
609
610 *pcbRead += (uint32_t)cbData;
611 cbBuf -= (uint32_t)cbData;
612 pvData = (uint8_t *)pvData + cbData;
613 }
614
615 LogFlowFunc(("returns %#x bytes\n", *pcbRead));
616 return VINF_SUCCESS;
617}
618
619
620/*********************************************************************************************************************************
621* PDMIBASE *
622*********************************************************************************************************************************/
623
624/**
625 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
626 */
627static DECLCALLBACK(void *) drvAudioVrdeQueryInterface(PPDMIBASE pInterface, const char *pszIID)
628{
629 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
630 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
631
632 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
633 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
634 return NULL;
635}
636
637
638/*********************************************************************************************************************************
639* PDMDRVREG *
640*********************************************************************************************************************************/
641
642/**
643 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
644 */
645/*static*/ DECLCALLBACK(void) AudioVRDE::drvPowerOff(PPDMDRVINS pDrvIns)
646{
647 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
648 LogFlowFuncEnter();
649
650 if (pThis->pConsoleVRDPServer)
651 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL);
652}
653
654
655/**
656 * @interface_method_impl{PDMDRVREG,pfnDestruct}
657 */
658/*static*/ DECLCALLBACK(void) AudioVRDE::drvDestruct(PPDMDRVINS pDrvIns)
659{
660 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
661 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
662 LogFlowFuncEnter();
663
664 /** @todo For runtime detach maybe:
665 if (pThis->pConsoleVRDPServer)
666 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL); */
667
668 /*
669 * If the AudioVRDE object is still alive, we must clear it's reference to
670 * us since we'll be invalid when we return from this method.
671 */
672 AudioVRDE *pAudioVRDE = pThis->pAudioVRDE;
673 if (pAudioVRDE)
674 {
675 RTCritSectEnter(&pAudioVRDE->mCritSect);
676 pAudioVRDE->mpDrv = NULL;
677 pThis->pAudioVRDE = NULL;
678 RTCritSectLeave(&pAudioVRDE->mCritSect);
679 }
680}
681
682
683/**
684 * Construct a VRDE audio driver instance.
685 *
686 * @copydoc FNPDMDRVCONSTRUCT
687 */
688/* static */
689DECLCALLBACK(int) AudioVRDE::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
690{
691 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
692 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
693 RT_NOREF(fFlags);
694
695 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
696 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
697
698 LogRel(("Audio: Initializing VRDE driver\n"));
699 LogFlowFunc(("fFlags=0x%x\n", fFlags));
700
701 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
702 ("Configuration error: Not possible to attach anything to this driver!\n"),
703 VERR_PDM_DRVINS_NO_ATTACH);
704
705 /*
706 * Init the static parts.
707 */
708 pThis->pDrvIns = pDrvIns;
709 pThis->cClients = 0;
710 /* IBase */
711 pDrvIns->IBase.pfnQueryInterface = drvAudioVrdeQueryInterface;
712 /* IHostAudio */
713 pThis->IHostAudio.pfnGetConfig = drvAudioVrdeHA_GetConfig;
714 pThis->IHostAudio.pfnGetDevices = NULL;
715 pThis->IHostAudio.pfnSetDevice = NULL;
716 pThis->IHostAudio.pfnGetStatus = drvAudioVrdeHA_GetStatus;
717 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
718 pThis->IHostAudio.pfnStreamConfigHint = NULL;
719 pThis->IHostAudio.pfnStreamCreate = drvAudioVrdeHA_StreamCreate;
720 pThis->IHostAudio.pfnStreamInitAsync = NULL;
721 pThis->IHostAudio.pfnStreamDestroy = drvAudioVrdeHA_StreamDestroy;
722 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
723 pThis->IHostAudio.pfnStreamEnable = drvAudioVrdeHA_StreamEnable;
724 pThis->IHostAudio.pfnStreamDisable = drvAudioVrdeHA_StreamDisable;
725 pThis->IHostAudio.pfnStreamPause = drvAudioVrdeHA_StreamPause;
726 pThis->IHostAudio.pfnStreamResume = drvAudioVrdeHA_StreamResume;
727 pThis->IHostAudio.pfnStreamDrain = drvAudioVrdeHA_StreamDrain;
728 pThis->IHostAudio.pfnStreamGetState = drvAudioVrdeHA_StreamGetState;
729 pThis->IHostAudio.pfnStreamGetPending = NULL;
730 pThis->IHostAudio.pfnStreamGetWritable = drvAudioVrdeHA_StreamGetWritable;
731 pThis->IHostAudio.pfnStreamPlay = drvAudioVrdeHA_StreamPlay;
732 pThis->IHostAudio.pfnStreamGetReadable = drvAudioVrdeHA_StreamGetReadable;
733 pThis->IHostAudio.pfnStreamCapture = drvAudioVrdeHA_StreamCapture;
734
735 /*
736 * Resolve the interface to the driver above us.
737 */
738 pThis->pIHostAudioPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHOSTAUDIOPORT);
739 AssertPtrReturn(pThis->pIHostAudioPort, VERR_PDM_MISSING_INTERFACE_ABOVE);
740
741 /* Get the Console object pointer. */
742 com::Guid ConsoleUuid(COM_IIDOF(IConsole));
743 IConsole *pIConsole = (IConsole *)PDMDrvHlpQueryGenericUserObject(pDrvIns, ConsoleUuid.raw());
744 AssertLogRelReturn(pIConsole, VERR_INTERNAL_ERROR_3);
745 Console *pConsole = static_cast<Console *>(pIConsole);
746 AssertLogRelReturn(pConsole, VERR_INTERNAL_ERROR_3);
747
748 /* Get the console VRDP object pointer. */
749 pThis->pConsoleVRDPServer = pConsole->i_consoleVRDPServer();
750 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pConsoleVRDPServer) || !pThis->pConsoleVRDPServer,
751 ("pConsoleVRDPServer=%p\n", pThis->pConsoleVRDPServer), VERR_INVALID_POINTER);
752
753 /* Get the AudioVRDE object pointer. */
754 pThis->pAudioVRDE = pConsole->i_getAudioVRDE();
755 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pAudioVRDE), ("pAudioVRDE=%p\n", pThis->pAudioVRDE), VERR_INVALID_POINTER);
756 RTCritSectEnter(&pThis->pAudioVRDE->mCritSect);
757 pThis->pAudioVRDE->mpDrv = pThis;
758 RTCritSectLeave(&pThis->pAudioVRDE->mCritSect);
759
760 return VINF_SUCCESS;
761}
762
763
764/**
765 * VRDE audio driver registration record.
766 */
767const PDMDRVREG AudioVRDE::DrvReg =
768{
769 PDM_DRVREG_VERSION,
770 /* szName */
771 "AudioVRDE",
772 /* szRCMod */
773 "",
774 /* szR0Mod */
775 "",
776 /* pszDescription */
777 "Audio driver for VRDE backend",
778 /* fFlags */
779 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
780 /* fClass. */
781 PDM_DRVREG_CLASS_AUDIO,
782 /* cMaxInstances */
783 ~0U,
784 /* cbInstance */
785 sizeof(DRVAUDIOVRDE),
786 /* pfnConstruct */
787 AudioVRDE::drvConstruct,
788 /* pfnDestruct */
789 AudioVRDE::drvDestruct,
790 /* pfnRelocate */
791 NULL,
792 /* pfnIOCtl */
793 NULL,
794 /* pfnPowerOn */
795 NULL,
796 /* pfnReset */
797 NULL,
798 /* pfnSuspend */
799 NULL,
800 /* pfnResume */
801 NULL,
802 /* pfnAttach */
803 NULL,
804 /* pfnDetach */
805 NULL,
806 /* pfnPowerOff */
807 AudioVRDE::drvPowerOff,
808 /* pfnSoftReset */
809 NULL,
810 /* u32EndVersion */
811 PDM_DRVREG_VERSION
812};
813
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use