VirtualBox

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

Last change on this file since 88211 was 88028, checked in by vboxsync, 4 years ago

Audio: Moving some of the DrvAudio.h stuff into PDM - VBox/vmm/pdmaudioinline.h. bugref:9890

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