VirtualBox

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

Last change on this file since 88309 was 88300, checked in by vboxsync, 4 years ago

Audio: We don't return IPRT status codes, but VBox status codes. duh. 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 88300 2021-03-26 14:31:55Z 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 /*
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 * @returns VBox 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