VirtualBox

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

Last change on this file since 88297 was 88269, checked in by vboxsync, 4 years ago

Audio: Made sure PDMAUDIOPCMPROPS is initialized using a helper function or the initializer macro, and that vital changes are made using setting helper functions. There are now two derived fields (frame size and shift count) that must be maintained, so this was the sanest way of doing it. Added a raw flag to PDMAUDIOPCMPROPS for VRDE/VRDP, since it wants the raw mixer content and we need a way of expressing this (PDMAUDIOSTREAMLAYOUT isn't the right place). The mixer buffers now uses PDMAUDIOPCMPROPS rather than the weird 32-bit format contraption for picking conversion functions. Simplify the drvAudioStreamPlay code by eliminating the PDMAUDIOSTREAMLAYOUT_RAW special case. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.5 KB
Line 
1/* $Id: DrvAudioVRDE.cpp 88269 2021-03-24 11:45:54Z vboxsync $ */
2/** @file
3 * VRDE audio backend for Main.
4 */
5
6/*
7 * Copyright (C) 2013-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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/RemoteDesktop/VRDE.h>
39#include <VBox/err.h>
40
41
42/*********************************************************************************************************************************
43* Structures and Typedefs *
44*********************************************************************************************************************************/
45/**
46 * Audio VRDE driver instance data.
47 */
48typedef struct DRVAUDIOVRDE
49{
50 /** Pointer to audio VRDE object. */
51 AudioVRDE *pAudioVRDE;
52 /** Pointer to the driver instance structure. */
53 PPDMDRVINS pDrvIns;
54 /** Pointer to host audio interface. */
55 PDMIHOSTAUDIO IHostAudio;
56 /** Pointer to the VRDP's console object. */
57 ConsoleVRDPServer *pConsoleVRDPServer;
58 /** Pointer to the DrvAudio port interface that is above us. */
59 PPDMIAUDIOCONNECTOR pDrvAudio;
60 /** Number of connected clients to this VRDE instance. */
61 uint32_t cClients;
62} DRVAUDIOVRDE, *PDRVAUDIOVRDE;
63
64typedef struct VRDESTREAM
65{
66 /** The stream's acquired configuration. */
67 PPDMAUDIOSTREAMCFG pCfg;
68 union
69 {
70 struct
71 {
72 /** Circular buffer for holding the recorded audio frames from the host. */
73 PRTCIRCBUF pCircBuf;
74 } In;
75 };
76} VRDESTREAM, *PVRDESTREAM;
77
78/* Sanity. */
79AssertCompileSize(PDMAUDIOFRAME, sizeof(int64_t) * 2 /* st_sample_t using by VRDP server */);
80
81static int vrdeCreateStreamIn(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
82{
83 RT_NOREF(pCfgReq);
84 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
85
86 /*
87 * The VRDP server does its own mixing and resampling as it may server
88 * multiple clients all with different sound formats. So, it feeds us
89 * raw mixer frames (somewhat akind to stereo signed 64-bit, see
90 * st_sample_t and PDMAUDIOFRAME).
91 */
92 pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW;
93 PDMAudioPropsInitEx(&pCfgAcq->Props, 8 /*64-bit*/, true /*fSigned*/, 2 /*stereo*/, 22050 /*Hz*/,
94 true /*fLittleEndian*/, true /*fRaw*/);
95
96 /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
97 const uint32_t cFramesVrdpServer = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 200 /*ms*/);
98
99 int rc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, PDMAudioPropsFramesToBytes(&pCfgAcq->Props, cFramesVrdpServer));
100 if (RT_SUCCESS(rc))
101 {
102 pCfgAcq->Backend.cFramesPeriod = cFramesVrdpServer;
103/** @todo r=bird: This is inconsistent with the above buffer allocation and I
104 * think also ALSA and Pulse backends way of setting cFramesBufferSize. */
105 pCfgAcq->Backend.cFramesBufferSize = cFramesVrdpServer * 2; /* Use "double buffering". */
106 pCfgAcq->Backend.cFramesPreBuffering = cFramesVrdpServer;
107 }
108
109 return rc;
110}
111
112
113static int vrdeCreateStreamOut(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
114{
115 RT_NOREF(pStreamVRDE, pCfgReq);
116 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
117
118 /*
119 * The VRDP server does its own mixing and resampling because it may be
120 * sending the audio to any number of different clients all with different
121 * formats (including clients which hasn't yet connected). So, it desires
122 * the raw data from the mixer (somewhat akind to stereo signed 64-bit,
123 * see st_sample_t and PDMAUDIOFRAME).
124 */
125 pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW;
126 PDMAudioPropsInitEx(&pCfgAcq->Props, 8 /*64-bit*/, true /*fSigned*/, 2 /*stereo*/, 22050 /*Hz*/,
127 true /*fLittleEndian*/, true /*fRaw*/);
128
129 /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
130 /** @todo r=bird: So, if VRDP does 200ms chunks, why do we report 100ms
131 * buffer and 20ms period? How does these parameters at all correlate
132 * with the above comment?!? */
133 pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 20 /*ms*/);
134 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/);
135 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
136
137 return VINF_SUCCESS;
138}
139
140
141static int vrdeControlStreamOut(PDRVAUDIOVRDE pDrv, PVRDESTREAM pStreamVRDE, PDMAUDIOSTREAMCMD enmStreamCmd)
142{
143 RT_NOREF(pDrv, pStreamVRDE, enmStreamCmd);
144
145 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
146
147 return VINF_SUCCESS;
148}
149
150
151static int vrdeControlStreamIn(PDRVAUDIOVRDE pDrv, PVRDESTREAM pStreamVRDE, PDMAUDIOSTREAMCMD enmStreamCmd)
152{
153 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
154
155 if (!pDrv->pConsoleVRDPServer)
156 {
157 LogRel(("Audio: VRDP console not ready yet\n"));
158 return VERR_AUDIO_STREAM_NOT_READY;
159 }
160
161 int rc;
162
163 /* Initialize only if not already done. */
164 switch (enmStreamCmd)
165 {
166 case PDMAUDIOSTREAMCMD_ENABLE:
167 {
168 rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE,
169 PDMAudioPropsMilliToFrames(&pStreamVRDE->pCfg->Props, 200 /*ms*/),
170 PDMAudioPropsHz(&pStreamVRDE->pCfg->Props),
171 PDMAudioPropsChannels(&pStreamVRDE->pCfg->Props),
172 PDMAudioPropsSampleBits(&pStreamVRDE->pCfg->Props));
173 if (rc == VERR_NOT_SUPPORTED)
174 {
175 LogRel(("Audio: No VRDE client connected, so no input recording available\n"));
176 rc = VERR_AUDIO_STREAM_NOT_READY;
177 }
178
179 break;
180 }
181
182 case PDMAUDIOSTREAMCMD_DISABLE:
183 {
184 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL /* pvUserCtx */);
185 rc = VINF_SUCCESS;
186
187 break;
188 }
189
190 case PDMAUDIOSTREAMCMD_PAUSE:
191 {
192 rc = VINF_SUCCESS;
193 break;
194 }
195
196 case PDMAUDIOSTREAMCMD_RESUME:
197 {
198 rc = VINF_SUCCESS;
199 break;
200 }
201
202 default:
203 {
204 rc = VERR_NOT_SUPPORTED;
205 break;
206 }
207 }
208
209 if (RT_FAILURE(rc))
210 LogFunc(("Failed with %Rrc\n", rc));
211
212 return rc;
213}
214
215
216/**
217 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
218 */
219static DECLCALLBACK(int) drvAudioVrdeHA_Init(PPDMIHOSTAUDIO pInterface)
220{
221 RT_NOREF(pInterface);
222 LogFlowFuncEnter();
223
224 return VINF_SUCCESS;
225}
226
227
228/**
229 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
230 */
231static DECLCALLBACK(int) drvAudioVrdeHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
232 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
233{
234 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
235 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
236 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
237 AssertReturn(uBufSize, VERR_INVALID_PARAMETER);
238 /* puRead is optional. */
239
240 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
241
242 size_t cbData = 0;
243
244 if (RTCircBufUsed(pStreamVRDE->In.pCircBuf))
245 {
246 void *pvData;
247
248 RTCircBufAcquireReadBlock(pStreamVRDE->In.pCircBuf, uBufSize, &pvData, &cbData);
249
250 if (cbData)
251 memcpy(pvBuf, pvData, cbData);
252
253 RTCircBufReleaseReadBlock(pStreamVRDE->In.pCircBuf, cbData);
254 }
255
256 if (puRead)
257 *puRead = (uint32_t)cbData;
258
259 return VINF_SUCCESS;
260}
261
262
263/**
264 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
265 */
266static DECLCALLBACK(int) drvAudioVrdeHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
267 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
268{
269 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
270 AssertPtr(pDrv);
271 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
272 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
273 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
274 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
275 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
276
277 if (!pDrv->pConsoleVRDPServer)
278 return VERR_NOT_AVAILABLE;
279
280 /* Prepate the format. */
281 PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->pCfg->Props;
282 VRDEAUDIOFORMAT const uVrdpFormat = VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps),
283 PDMAudioPropsChannels(pProps),
284 PDMAudioPropsSampleBits(pProps),
285 pProps->fSigned);
286 Assert(uVrdpFormat == VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps), 2, 64, true));
287
288 /* We specified PDMAUDIOSTREAMLAYOUT_RAW (== S64), so
289 convert the buffer pointe and size accordingly: */
290 PCPDMAUDIOFRAME paSampleBuf = (PCPDMAUDIOFRAME)pvBuf;
291 uint32_t const cFramesToWrite = cbBuf / sizeof(paSampleBuf[0]);
292 Assert(cFramesToWrite * sizeof(paSampleBuf[0]) == cbBuf);
293
294 /** @todo r=bird: there was some incoherent mumbling about "using the
295 * internal counter to track if we (still) can write to the VRDP
296 * server or if need to wait anothe round (time slot)". However it
297 * wasn't accessing any internal counter nor doing anything else
298 * sensible, so I've removed it. */
299
300 /*
301 * Call the VRDP server with the data.
302 */
303 uint32_t cFramesWritten = 0;
304 while (cFramesWritten < cFramesToWrite)
305 {
306 uint32_t const cFramesChunk = cFramesToWrite - cFramesWritten; /** @todo For now write all at once. */
307
308 /* Note: The VRDP server expects int64_t samples per channel, regardless
309 of the actual sample bits (e.g 8 or 16 bits). */
310 pDrv->pConsoleVRDPServer->SendAudioSamples(&paSampleBuf[cFramesWritten], cFramesChunk /* Frames */, uVrdpFormat);
311
312 cFramesWritten += cFramesChunk;
313 }
314
315 Log3Func(("cFramesWritten=%RU32\n", cFramesWritten));
316 if (pcbWritten)
317 *pcbWritten = cFramesWritten * sizeof(PDMAUDIOFRAME);
318 return VINF_SUCCESS;
319}
320
321
322static int vrdeDestroyStreamIn(PDRVAUDIOVRDE pDrv, PVRDESTREAM pStreamVRDE)
323{
324 if (pDrv->pConsoleVRDPServer)
325 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
326
327 if (pStreamVRDE->In.pCircBuf)
328 {
329 RTCircBufDestroy(pStreamVRDE->In.pCircBuf);
330 pStreamVRDE->In.pCircBuf = NULL;
331 }
332
333 return VINF_SUCCESS;
334}
335
336
337static int vrdeDestroyStreamOut(PDRVAUDIOVRDE pDrv, PVRDESTREAM pStreamVRDE)
338{
339 RT_NOREF(pDrv, pStreamVRDE);
340
341 return VINF_SUCCESS;
342}
343
344
345/**
346 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
347 */
348static DECLCALLBACK(int) drvAudioVrdeHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
349{
350 RT_NOREF(pInterface);
351 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
352
353 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "VRDE");
354
355 pBackendCfg->cbStreamOut = sizeof(VRDESTREAM);
356 pBackendCfg->cbStreamIn = sizeof(VRDESTREAM);
357 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
358 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
359
360 return VINF_SUCCESS;
361}
362
363
364/**
365 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
366 */
367static DECLCALLBACK(void) drvAudioVrdeHA_Shutdown(PPDMIHOSTAUDIO pInterface)
368{
369 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
370 AssertPtrReturnVoid(pDrv);
371
372 if (pDrv->pConsoleVRDPServer)
373 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
374}
375
376
377/**
378 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
379 */
380static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVrdeHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
381{
382 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
383 AssertPtrReturn(pDrv, PDMAUDIOBACKENDSTS_ERROR);
384
385 RT_NOREF(enmDir);
386
387 return PDMAUDIOBACKENDSTS_RUNNING;
388}
389
390
391/**
392 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
393 */
394static DECLCALLBACK(int) drvAudioVrdeHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
395 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
396{
397 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
398 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
399 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
400 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
401
402 RT_NOREF(pInterface);
403
404 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
405
406 int rc;
407 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
408 rc = vrdeCreateStreamIn( pStreamVRDE, pCfgReq, pCfgAcq);
409 else
410 rc = vrdeCreateStreamOut(pStreamVRDE, pCfgReq, pCfgAcq);
411
412 if (RT_SUCCESS(rc))
413 {
414 pStreamVRDE->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
415 if (!pStreamVRDE->pCfg)
416 rc = VERR_NO_MEMORY;
417 }
418
419 return rc;
420}
421
422
423/**
424 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
425 */
426static DECLCALLBACK(int) drvAudioVrdeHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
427{
428 RT_NOREF(pInterface);
429 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
430
431 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
432 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
433
434 if (!pStreamVRDE->pCfg) /* Not (yet) configured? Skip. */
435 return VINF_SUCCESS;
436
437 int rc;
438 if (pStreamVRDE->pCfg->enmDir == PDMAUDIODIR_IN)
439 rc = vrdeDestroyStreamIn(pDrv, pStreamVRDE);
440 else
441 rc = vrdeDestroyStreamOut(pDrv, pStreamVRDE);
442
443 if (RT_SUCCESS(rc))
444 {
445 PDMAudioStrmCfgFree(pStreamVRDE->pCfg);
446 pStreamVRDE->pCfg = NULL;
447 }
448
449 return rc;
450}
451
452
453/**
454 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
455 */
456static DECLCALLBACK(int) drvAudioVrdeHA_StreamControl(PPDMIHOSTAUDIO pInterface,
457 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
458{
459 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
460 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
461
462 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
463 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
464
465 if (!pStreamVRDE->pCfg) /* Not (yet) configured? Skip. */
466 return VINF_SUCCESS;
467
468 int rc;
469 if (pStreamVRDE->pCfg->enmDir == PDMAUDIODIR_IN)
470 rc = vrdeControlStreamIn(pDrv, pStreamVRDE, enmStreamCmd);
471 else
472 rc = vrdeControlStreamOut(pDrv, pStreamVRDE, enmStreamCmd);
473
474 return rc;
475}
476
477
478/**
479 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
480 */
481static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
482{
483 RT_NOREF(pInterface);
484
485 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
486
487 if (pStreamVRDE->pCfg->enmDir == PDMAUDIODIR_IN)
488 {
489 /* Return frames instead of bytes here
490 * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
491 return (uint32_t)PDMAUDIOSTREAMCFG_B2F(pStreamVRDE->pCfg, RTCircBufUsed(pStreamVRDE->In.pCircBuf));
492 }
493
494 return 0;
495}
496
497
498/**
499 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
500 */
501static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
502{
503 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
504 RT_NOREF(pStream);
505
506 /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */
507 if (pDrv->cClients)
508 return _16K * sizeof(PDMAUDIOFRAME);
509 return 0;
510}
511
512
513/**
514 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
515 */
516static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioVrdeHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
517{
518 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
519 RT_NOREF(pStream);
520
521 PDMAUDIOSTREAMSTS fStrmStatus = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
522
523 if (pDrv->cClients) /* If any clients are connected, flag the stream as enabled. */
524 fStrmStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
525
526 return fStrmStatus;
527}
528
529
530/**
531 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
532 */
533static DECLCALLBACK(int) drvAudioVrdeHA_StreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
534{
535 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
536 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
537
538 /* Nothing to do here for VRDE. */
539 return VINF_SUCCESS;
540}
541
542
543/**
544 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
545 */
546static DECLCALLBACK(void *) drvAudioVrdeQueryInterface(PPDMIBASE pInterface, const char *pszIID)
547{
548 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
549 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
550
551 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
552 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
553 return NULL;
554}
555
556
557AudioVRDE::AudioVRDE(Console *pConsole)
558 : AudioDriver(pConsole)
559 , mpDrv(NULL)
560{
561}
562
563
564AudioVRDE::~AudioVRDE(void)
565{
566 if (mpDrv)
567 {
568 mpDrv->pAudioVRDE = NULL;
569 mpDrv = NULL;
570 }
571}
572
573
574/**
575 * @copydoc AudioDriver::configureDriver
576 */
577int AudioVRDE::configureDriver(PCFGMNODE pLunCfg)
578{
579 int rc = CFGMR3InsertInteger(pLunCfg, "Object", (uintptr_t)this);
580 AssertRCReturn(rc, rc);
581 CFGMR3InsertInteger(pLunCfg, "ObjectVRDPServer", (uintptr_t)mpConsole->i_consoleVRDPServer());
582 AssertRCReturn(rc, rc);
583
584 return AudioDriver::configureDriver(pLunCfg);
585}
586
587
588void AudioVRDE::onVRDEClientConnect(uint32_t uClientID)
589{
590 RT_NOREF(uClientID);
591
592 LogRel2(("Audio: VRDE client connected\n"));
593 if (mpDrv)
594 mpDrv->cClients++;
595}
596
597
598void AudioVRDE::onVRDEClientDisconnect(uint32_t uClientID)
599{
600 RT_NOREF(uClientID);
601
602 LogRel2(("Audio: VRDE client disconnected\n"));
603 Assert(mpDrv->cClients);
604 if (mpDrv)
605 mpDrv->cClients--;
606}
607
608
609int AudioVRDE::onVRDEControl(bool fEnable, uint32_t uFlags)
610{
611 RT_NOREF(fEnable, uFlags);
612 LogFlowThisFunc(("fEnable=%RTbool, uFlags=0x%x\n", fEnable, uFlags));
613
614 if (mpDrv == NULL)
615 return VERR_INVALID_STATE;
616
617 return VINF_SUCCESS; /* Never veto. */
618}
619
620
621/**
622 * Marks the beginning of sending captured audio data from a connected
623 * RDP client.
624 *
625 * @return IPRT status code.
626 * @param pvContext The context; in this case a pointer to a
627 * VRDESTREAMIN structure.
628 * @param pVRDEAudioBegin Pointer to a VRDEAUDIOINBEGIN structure.
629 */
630int AudioVRDE::onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin)
631{
632 AssertPtrReturn(pvContext, VERR_INVALID_POINTER);
633 AssertPtrReturn(pVRDEAudioBegin, VERR_INVALID_POINTER);
634
635 PVRDESTREAM pVRDEStrmIn = (PVRDESTREAM)pvContext;
636 AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
637
638 VRDEAUDIOFORMAT audioFmt = pVRDEAudioBegin->fmt;
639
640 int iSampleHz = VRDE_AUDIO_FMT_SAMPLE_FREQ(audioFmt); RT_NOREF(iSampleHz);
641 int cChannels = VRDE_AUDIO_FMT_CHANNELS(audioFmt); RT_NOREF(cChannels);
642 int cBits = VRDE_AUDIO_FMT_BITS_PER_SAMPLE(audioFmt); RT_NOREF(cBits);
643 bool fUnsigned = VRDE_AUDIO_FMT_SIGNED(audioFmt); RT_NOREF(fUnsigned);
644
645 LogFlowFunc(("cbSample=%RU32, iSampleHz=%d, cChannels=%d, cBits=%d, fUnsigned=%RTbool\n",
646 VRDE_AUDIO_FMT_BYTES_PER_SAMPLE(audioFmt), iSampleHz, cChannels, cBits, fUnsigned));
647
648 return VINF_SUCCESS;
649}
650
651
652int AudioVRDE::onVRDEInputData(void *pvContext, const void *pvData, uint32_t cbData)
653{
654 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pvContext;
655 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
656
657 void *pvBuf;
658 size_t cbBuf;
659
660 RTCircBufAcquireWriteBlock(pStreamVRDE->In.pCircBuf, cbData, &pvBuf, &cbBuf);
661
662 if (cbBuf)
663 memcpy(pvBuf, pvData, cbBuf);
664
665 RTCircBufReleaseWriteBlock(pStreamVRDE->In.pCircBuf, cbBuf);
666
667 if (cbBuf < cbData)
668 LogRel(("VRDE: Capturing audio data lost %zu bytes\n", cbData - cbBuf)); /** @todo Use an error counter. */
669
670 return VINF_SUCCESS; /** @todo r=andy How to tell the caller if we were not able to handle *all* input data? */
671}
672
673
674int AudioVRDE::onVRDEInputEnd(void *pvContext)
675{
676 RT_NOREF(pvContext);
677
678 return VINF_SUCCESS;
679}
680
681
682int AudioVRDE::onVRDEInputIntercept(bool fEnabled)
683{
684 RT_NOREF(fEnabled);
685 return VINF_SUCCESS; /* Never veto. */
686}
687
688
689/**
690 * Construct a VRDE audio driver instance.
691 *
692 * @copydoc FNPDMDRVCONSTRUCT
693 */
694/* static */
695DECLCALLBACK(int) AudioVRDE::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
696{
697 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
698 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
699 RT_NOREF(fFlags);
700
701 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
702 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
703
704 LogRel(("Audio: Initializing VRDE driver\n"));
705 LogFlowFunc(("fFlags=0x%x\n", fFlags));
706
707 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
708 ("Configuration error: Not possible to attach anything to this driver!\n"),
709 VERR_PDM_DRVINS_NO_ATTACH);
710
711 /*
712 * Init the static parts.
713 */
714 pThis->pDrvIns = pDrvIns;
715 /* IBase */
716 pDrvIns->IBase.pfnQueryInterface = drvAudioVrdeQueryInterface;
717 /* IHostAudio */
718 pThis->IHostAudio.pfnInit = drvAudioVrdeHA_Init;
719 pThis->IHostAudio.pfnShutdown = drvAudioVrdeHA_Shutdown;
720 pThis->IHostAudio.pfnGetConfig = drvAudioVrdeHA_GetConfig;
721 pThis->IHostAudio.pfnGetDevices = NULL;
722 pThis->IHostAudio.pfnGetStatus = drvAudioVrdeHA_GetStatus;
723 pThis->IHostAudio.pfnSetCallback = NULL;
724 pThis->IHostAudio.pfnStreamCreate = drvAudioVrdeHA_StreamCreate;
725 pThis->IHostAudio.pfnStreamDestroy = drvAudioVrdeHA_StreamDestroy;
726 pThis->IHostAudio.pfnStreamControl = drvAudioVrdeHA_StreamControl;
727 pThis->IHostAudio.pfnStreamGetReadable = drvAudioVrdeHA_StreamGetReadable;
728 pThis->IHostAudio.pfnStreamGetWritable = drvAudioVrdeHA_StreamGetWritable;
729 pThis->IHostAudio.pfnStreamGetPending = NULL;
730 pThis->IHostAudio.pfnStreamGetStatus = drvAudioVrdeHA_StreamGetStatus;
731 pThis->IHostAudio.pfnStreamIterate = drvAudioVrdeHA_StreamIterate;
732 pThis->IHostAudio.pfnStreamPlay = drvAudioVrdeHA_StreamPlay;
733 pThis->IHostAudio.pfnStreamCapture = drvAudioVrdeHA_StreamCapture;
734
735 /*
736 * Get the ConsoleVRDPServer object pointer.
737 */
738 void *pvUser;
739 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
740 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc), rc);
741
742 /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE. */
743 pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
744 pThis->cClients = 0;
745
746 /*
747 * Get the AudioVRDE object pointer.
748 */
749 pvUser = NULL;
750 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
751 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
752
753 pThis->pAudioVRDE = (AudioVRDE *)pvUser;
754 pThis->pAudioVRDE->mpDrv = pThis;
755
756 /*
757 * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
758 * Described in CFGM tree.
759 */
760 pThis->pDrvAudio = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
761 AssertMsgReturn(pThis->pDrvAudio, ("Configuration error: No upper interface specified!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
762
763 return VINF_SUCCESS;
764}
765
766
767/**
768 * @interface_method_impl{PDMDRVREG,pfnDestruct}
769 */
770/* static */
771DECLCALLBACK(void) AudioVRDE::drvDestruct(PPDMDRVINS pDrvIns)
772{
773 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
774 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
775 LogFlowFuncEnter();
776
777 /*
778 * If the AudioVRDE object is still alive, we must clear it's reference to
779 * us since we'll be invalid when we return from this method.
780 */
781 if (pThis->pAudioVRDE)
782 {
783 pThis->pAudioVRDE->mpDrv = NULL;
784 pThis->pAudioVRDE = NULL;
785 }
786}
787
788/**
789 * @interface_method_impl{PDMDRVREG,pfnAttach}
790 */
791/* static */
792DECLCALLBACK(int) AudioVRDE::drvAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
793{
794 RT_NOREF(pDrvIns, fFlags);
795
796 LogFlowFuncEnter();
797
798 return VINF_SUCCESS;
799}
800
801/**
802 * @interface_method_impl{PDMDRVREG,pfnDetach}
803 */
804/* static */
805DECLCALLBACK(void) AudioVRDE::drvDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
806{
807 RT_NOREF(pDrvIns, fFlags);
808
809 LogFlowFuncEnter();
810}
811
812
813/**
814 * VRDE audio driver registration record.
815 */
816const PDMDRVREG AudioVRDE::DrvReg =
817{
818 PDM_DRVREG_VERSION,
819 /* szName */
820 "AudioVRDE",
821 /* szRCMod */
822 "",
823 /* szR0Mod */
824 "",
825 /* pszDescription */
826 "Audio driver for VRDE backend",
827 /* fFlags */
828 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
829 /* fClass. */
830 PDM_DRVREG_CLASS_AUDIO,
831 /* cMaxInstances */
832 ~0U,
833 /* cbInstance */
834 sizeof(DRVAUDIOVRDE),
835 /* pfnConstruct */
836 AudioVRDE::drvConstruct,
837 /* pfnDestruct */
838 AudioVRDE::drvDestruct,
839 /* pfnRelocate */
840 NULL,
841 /* pfnIOCtl */
842 NULL,
843 /* pfnPowerOn */
844 NULL,
845 /* pfnReset */
846 NULL,
847 /* pfnSuspend */
848 NULL,
849 /* pfnResume */
850 NULL,
851 /* pfnAttach */
852 AudioVRDE::drvAttach,
853 /* pfnDetach */
854 AudioVRDE::drvDetach,
855 /* pfnPowerOff */
856 NULL,
857 /* pfnSoftReset */
858 NULL,
859 /* u32EndVersion */
860 PDM_DRVREG_VERSION
861};
862
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette