VirtualBox

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

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

Audio: Removed the pfnInit and pfnShutdown methods from PDMIHOSTAUDIO. These methods duplicates PDMDRVREG callbacks and were therefore superfluous. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.4 KB
Line 
1/* $Id: DrvAudioVRDE.cpp 88390 2021-04-07 10:35:06Z 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 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
217 */
218static DECLCALLBACK(int) drvAudioVrdeHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
219 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
220{
221 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
222 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
223 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
224 AssertReturn(uBufSize, VERR_INVALID_PARAMETER);
225 /* puRead is optional. */
226
227 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
228
229 size_t cbData = 0;
230
231 if (RTCircBufUsed(pStreamVRDE->In.pCircBuf))
232 {
233 void *pvData;
234
235 RTCircBufAcquireReadBlock(pStreamVRDE->In.pCircBuf, uBufSize, &pvData, &cbData);
236
237 if (cbData)
238 memcpy(pvBuf, pvData, cbData);
239
240 RTCircBufReleaseReadBlock(pStreamVRDE->In.pCircBuf, cbData);
241 }
242
243 if (puRead)
244 *puRead = (uint32_t)cbData;
245
246 return VINF_SUCCESS;
247}
248
249
250/**
251 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
252 */
253static DECLCALLBACK(int) drvAudioVrdeHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
254 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
255{
256 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
257 AssertPtr(pDrv);
258 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
259 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
260 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
261 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
262 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
263
264 if (!pDrv->pConsoleVRDPServer)
265 return VERR_NOT_AVAILABLE;
266
267 /* Prepate the format. */
268 PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->pCfg->Props;
269 VRDEAUDIOFORMAT const uVrdpFormat = VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps),
270 PDMAudioPropsChannels(pProps),
271 PDMAudioPropsSampleBits(pProps),
272 pProps->fSigned);
273 Assert(uVrdpFormat == VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps), 2, 64, true));
274
275 /* We specified PDMAUDIOSTREAMLAYOUT_RAW (== S64), so
276 convert the buffer pointe and size accordingly: */
277 PCPDMAUDIOFRAME paSampleBuf = (PCPDMAUDIOFRAME)pvBuf;
278 uint32_t const cFramesToWrite = cbBuf / sizeof(paSampleBuf[0]);
279 Assert(cFramesToWrite * sizeof(paSampleBuf[0]) == cbBuf);
280
281 /** @todo r=bird: there was some incoherent mumbling about "using the
282 * internal counter to track if we (still) can write to the VRDP
283 * server or if need to wait anothe round (time slot)". However it
284 * wasn't accessing any internal counter nor doing anything else
285 * sensible, so I've removed it. */
286
287 /*
288 * Call the VRDP server with the data.
289 */
290 uint32_t cFramesWritten = 0;
291 while (cFramesWritten < cFramesToWrite)
292 {
293 uint32_t const cFramesChunk = cFramesToWrite - cFramesWritten; /** @todo For now write all at once. */
294
295 /* Note: The VRDP server expects int64_t samples per channel, regardless
296 of the actual sample bits (e.g 8 or 16 bits). */
297 pDrv->pConsoleVRDPServer->SendAudioSamples(&paSampleBuf[cFramesWritten], cFramesChunk /* Frames */, uVrdpFormat);
298
299 cFramesWritten += cFramesChunk;
300 }
301
302 Log3Func(("cFramesWritten=%RU32\n", cFramesWritten));
303 if (pcbWritten)
304 *pcbWritten = cFramesWritten * sizeof(PDMAUDIOFRAME);
305 return VINF_SUCCESS;
306}
307
308
309static int vrdeDestroyStreamIn(PDRVAUDIOVRDE pDrv, PVRDESTREAM pStreamVRDE)
310{
311 if (pDrv->pConsoleVRDPServer)
312 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
313
314 if (pStreamVRDE->In.pCircBuf)
315 {
316 RTCircBufDestroy(pStreamVRDE->In.pCircBuf);
317 pStreamVRDE->In.pCircBuf = NULL;
318 }
319
320 return VINF_SUCCESS;
321}
322
323
324static int vrdeDestroyStreamOut(PDRVAUDIOVRDE pDrv, PVRDESTREAM pStreamVRDE)
325{
326 RT_NOREF(pDrv, pStreamVRDE);
327
328 return VINF_SUCCESS;
329}
330
331
332/**
333 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
334 */
335static DECLCALLBACK(int) drvAudioVrdeHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
336{
337 RT_NOREF(pInterface);
338 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
339
340 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "VRDE");
341
342 pBackendCfg->cbStreamOut = sizeof(VRDESTREAM);
343 pBackendCfg->cbStreamIn = sizeof(VRDESTREAM);
344 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
345 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
346
347 return VINF_SUCCESS;
348}
349
350
351/**
352 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
353 */
354static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVrdeHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
355{
356 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
357 AssertPtrReturn(pDrv, PDMAUDIOBACKENDSTS_ERROR);
358
359 RT_NOREF(enmDir);
360
361 return PDMAUDIOBACKENDSTS_RUNNING;
362}
363
364
365/**
366 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
367 */
368static DECLCALLBACK(int) drvAudioVrdeHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
369 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
370{
371 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
372 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
373 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
374 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
375
376 RT_NOREF(pInterface);
377
378 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
379
380 int rc;
381 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
382 rc = vrdeCreateStreamIn( pStreamVRDE, pCfgReq, pCfgAcq);
383 else
384 rc = vrdeCreateStreamOut(pStreamVRDE, pCfgReq, pCfgAcq);
385
386 if (RT_SUCCESS(rc))
387 {
388 pStreamVRDE->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
389 if (!pStreamVRDE->pCfg)
390 rc = VERR_NO_MEMORY;
391 }
392
393 return rc;
394}
395
396
397/**
398 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
399 */
400static DECLCALLBACK(int) drvAudioVrdeHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
401{
402 RT_NOREF(pInterface);
403 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
404
405 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
406 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
407
408 if (!pStreamVRDE->pCfg) /* Not (yet) configured? Skip. */
409 return VINF_SUCCESS;
410
411 int rc;
412 if (pStreamVRDE->pCfg->enmDir == PDMAUDIODIR_IN)
413 rc = vrdeDestroyStreamIn(pDrv, pStreamVRDE);
414 else
415 rc = vrdeDestroyStreamOut(pDrv, pStreamVRDE);
416
417 if (RT_SUCCESS(rc))
418 {
419 PDMAudioStrmCfgFree(pStreamVRDE->pCfg);
420 pStreamVRDE->pCfg = NULL;
421 }
422
423 return rc;
424}
425
426
427/**
428 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
429 */
430static DECLCALLBACK(int) drvAudioVrdeHA_StreamControl(PPDMIHOSTAUDIO pInterface,
431 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
432{
433 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
434 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
435
436 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
437 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
438
439 if (!pStreamVRDE->pCfg) /* Not (yet) configured? Skip. */
440 return VINF_SUCCESS;
441
442 int rc;
443 if (pStreamVRDE->pCfg->enmDir == PDMAUDIODIR_IN)
444 rc = vrdeControlStreamIn(pDrv, pStreamVRDE, enmStreamCmd);
445 else
446 rc = vrdeControlStreamOut(pDrv, pStreamVRDE, enmStreamCmd);
447
448 return rc;
449}
450
451
452/**
453 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
454 */
455static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
456{
457 RT_NOREF(pInterface);
458
459 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
460
461 if (pStreamVRDE->pCfg->enmDir == PDMAUDIODIR_IN)
462 {
463 /* Return frames instead of bytes here
464 * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
465 return (uint32_t)PDMAUDIOSTREAMCFG_B2F(pStreamVRDE->pCfg, RTCircBufUsed(pStreamVRDE->In.pCircBuf));
466 }
467
468 return 0;
469}
470
471
472/**
473 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
474 */
475static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
476{
477 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
478 RT_NOREF(pStream);
479
480 /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */
481 if (pDrv->cClients)
482 return _16K * sizeof(PDMAUDIOFRAME);
483 return 0;
484}
485
486
487/**
488 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
489 */
490static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvAudioVrdeHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
491{
492 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
493 RT_NOREF(pStream);
494
495 PDMAUDIOSTREAMSTS fStrmStatus = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED;
496
497 if (pDrv->cClients) /* If any clients are connected, flag the stream as enabled. */
498 fStrmStatus |= PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
499
500 return fStrmStatus;
501}
502
503
504/**
505 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
506 */
507static DECLCALLBACK(void *) drvAudioVrdeQueryInterface(PPDMIBASE pInterface, const char *pszIID)
508{
509 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
510 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
511
512 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
513 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
514 return NULL;
515}
516
517
518AudioVRDE::AudioVRDE(Console *pConsole)
519 : AudioDriver(pConsole)
520 , mpDrv(NULL)
521{
522}
523
524
525AudioVRDE::~AudioVRDE(void)
526{
527 if (mpDrv)
528 {
529 mpDrv->pAudioVRDE = NULL;
530 mpDrv = NULL;
531 }
532}
533
534
535/**
536 * @copydoc AudioDriver::configureDriver
537 */
538int AudioVRDE::configureDriver(PCFGMNODE pLunCfg)
539{
540 int rc = CFGMR3InsertInteger(pLunCfg, "Object", (uintptr_t)this);
541 AssertRCReturn(rc, rc);
542 CFGMR3InsertInteger(pLunCfg, "ObjectVRDPServer", (uintptr_t)mpConsole->i_consoleVRDPServer());
543 AssertRCReturn(rc, rc);
544
545 return AudioDriver::configureDriver(pLunCfg);
546}
547
548
549void AudioVRDE::onVRDEClientConnect(uint32_t uClientID)
550{
551 RT_NOREF(uClientID);
552
553 LogRel2(("Audio: VRDE client connected\n"));
554 if (mpDrv)
555 mpDrv->cClients++;
556}
557
558
559void AudioVRDE::onVRDEClientDisconnect(uint32_t uClientID)
560{
561 RT_NOREF(uClientID);
562
563 LogRel2(("Audio: VRDE client disconnected\n"));
564 Assert(mpDrv->cClients);
565 if (mpDrv)
566 mpDrv->cClients--;
567}
568
569
570int AudioVRDE::onVRDEControl(bool fEnable, uint32_t uFlags)
571{
572 RT_NOREF(fEnable, uFlags);
573 LogFlowThisFunc(("fEnable=%RTbool, uFlags=0x%x\n", fEnable, uFlags));
574
575 if (mpDrv == NULL)
576 return VERR_INVALID_STATE;
577
578 return VINF_SUCCESS; /* Never veto. */
579}
580
581
582/**
583 * Marks the beginning of sending captured audio data from a connected
584 * RDP client.
585 *
586 * @returns VBox status code.
587 * @param pvContext The context; in this case a pointer to a
588 * VRDESTREAMIN structure.
589 * @param pVRDEAudioBegin Pointer to a VRDEAUDIOINBEGIN structure.
590 */
591int AudioVRDE::onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin)
592{
593 AssertPtrReturn(pvContext, VERR_INVALID_POINTER);
594 AssertPtrReturn(pVRDEAudioBegin, VERR_INVALID_POINTER);
595
596 PVRDESTREAM pVRDEStrmIn = (PVRDESTREAM)pvContext;
597 AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
598
599 VRDEAUDIOFORMAT audioFmt = pVRDEAudioBegin->fmt;
600
601 int iSampleHz = VRDE_AUDIO_FMT_SAMPLE_FREQ(audioFmt); RT_NOREF(iSampleHz);
602 int cChannels = VRDE_AUDIO_FMT_CHANNELS(audioFmt); RT_NOREF(cChannels);
603 int cBits = VRDE_AUDIO_FMT_BITS_PER_SAMPLE(audioFmt); RT_NOREF(cBits);
604 bool fUnsigned = VRDE_AUDIO_FMT_SIGNED(audioFmt); RT_NOREF(fUnsigned);
605
606 LogFlowFunc(("cbSample=%RU32, iSampleHz=%d, cChannels=%d, cBits=%d, fUnsigned=%RTbool\n",
607 VRDE_AUDIO_FMT_BYTES_PER_SAMPLE(audioFmt), iSampleHz, cChannels, cBits, fUnsigned));
608
609 return VINF_SUCCESS;
610}
611
612
613int AudioVRDE::onVRDEInputData(void *pvContext, const void *pvData, uint32_t cbData)
614{
615 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pvContext;
616 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
617
618 void *pvBuf;
619 size_t cbBuf;
620
621 RTCircBufAcquireWriteBlock(pStreamVRDE->In.pCircBuf, cbData, &pvBuf, &cbBuf);
622
623 if (cbBuf)
624 memcpy(pvBuf, pvData, cbBuf);
625
626 RTCircBufReleaseWriteBlock(pStreamVRDE->In.pCircBuf, cbBuf);
627
628 if (cbBuf < cbData)
629 LogRel(("VRDE: Capturing audio data lost %zu bytes\n", cbData - cbBuf)); /** @todo Use an error counter. */
630
631 return VINF_SUCCESS; /** @todo r=andy How to tell the caller if we were not able to handle *all* input data? */
632}
633
634
635int AudioVRDE::onVRDEInputEnd(void *pvContext)
636{
637 RT_NOREF(pvContext);
638
639 return VINF_SUCCESS;
640}
641
642
643int AudioVRDE::onVRDEInputIntercept(bool fEnabled)
644{
645 RT_NOREF(fEnabled);
646 return VINF_SUCCESS; /* Never veto. */
647}
648
649
650/**
651 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
652 */
653/*static*/ DECLCALLBACK(void) AudioVRDE::drvPowerOff(PPDMDRVINS pDrvIns)
654{
655 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
656 LogFlowFuncEnter();
657
658 if (pThis->pConsoleVRDPServer)
659 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL);
660}
661
662
663/**
664 * @interface_method_impl{PDMDRVREG,pfnDestruct}
665 */
666/*static*/ DECLCALLBACK(void) AudioVRDE::drvDestruct(PPDMDRVINS pDrvIns)
667{
668 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
669 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
670 LogFlowFuncEnter();
671
672 /** @todo For runtime detach maybe:
673 if (pThis->pConsoleVRDPServer)
674 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL); */
675
676 /*
677 * If the AudioVRDE object is still alive, we must clear it's reference to
678 * us since we'll be invalid when we return from this method.
679 */
680 if (pThis->pAudioVRDE)
681 {
682 pThis->pAudioVRDE->mpDrv = NULL;
683 pThis->pAudioVRDE = NULL;
684 }
685}
686
687
688/**
689 * Construct a VRDE audio driver instance.
690 *
691 * @copydoc FNPDMDRVCONSTRUCT
692 */
693/* static */
694DECLCALLBACK(int) AudioVRDE::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
695{
696 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
697 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
698 RT_NOREF(fFlags);
699
700 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
701 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
702
703 LogRel(("Audio: Initializing VRDE driver\n"));
704 LogFlowFunc(("fFlags=0x%x\n", fFlags));
705
706 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
707 ("Configuration error: Not possible to attach anything to this driver!\n"),
708 VERR_PDM_DRVINS_NO_ATTACH);
709
710 /*
711 * Init the static parts.
712 */
713 pThis->pDrvIns = pDrvIns;
714 /* IBase */
715 pDrvIns->IBase.pfnQueryInterface = drvAudioVrdeQueryInterface;
716 /* IHostAudio */
717 pThis->IHostAudio.pfnGetConfig = drvAudioVrdeHA_GetConfig;
718 pThis->IHostAudio.pfnGetDevices = NULL;
719 pThis->IHostAudio.pfnGetStatus = drvAudioVrdeHA_GetStatus;
720 pThis->IHostAudio.pfnStreamCreate = drvAudioVrdeHA_StreamCreate;
721 pThis->IHostAudio.pfnStreamDestroy = drvAudioVrdeHA_StreamDestroy;
722 pThis->IHostAudio.pfnStreamControl = drvAudioVrdeHA_StreamControl;
723 pThis->IHostAudio.pfnStreamGetReadable = drvAudioVrdeHA_StreamGetReadable;
724 pThis->IHostAudio.pfnStreamGetWritable = drvAudioVrdeHA_StreamGetWritable;
725 pThis->IHostAudio.pfnStreamGetPending = NULL;
726 pThis->IHostAudio.pfnStreamGetStatus = drvAudioVrdeHA_StreamGetStatus;
727 pThis->IHostAudio.pfnStreamPlay = drvAudioVrdeHA_StreamPlay;
728 pThis->IHostAudio.pfnStreamCapture = drvAudioVrdeHA_StreamCapture;
729
730 /*
731 * Get the ConsoleVRDPServer object pointer.
732 */
733 void *pvUser;
734 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
735 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc), rc);
736
737 /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE. */
738 pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
739 pThis->cClients = 0;
740
741 /*
742 * Get the AudioVRDE object pointer.
743 */
744 pvUser = NULL;
745 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
746 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
747
748 pThis->pAudioVRDE = (AudioVRDE *)pvUser;
749 pThis->pAudioVRDE->mpDrv = pThis;
750
751 /*
752 * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
753 * Described in CFGM tree.
754 */
755 pThis->pDrvAudio = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
756 AssertMsgReturn(pThis->pDrvAudio, ("Configuration error: No upper interface specified!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
757
758 return VINF_SUCCESS;
759}
760
761
762/**
763 * VRDE audio driver registration record.
764 */
765const PDMDRVREG AudioVRDE::DrvReg =
766{
767 PDM_DRVREG_VERSION,
768 /* szName */
769 "AudioVRDE",
770 /* szRCMod */
771 "",
772 /* szR0Mod */
773 "",
774 /* pszDescription */
775 "Audio driver for VRDE backend",
776 /* fFlags */
777 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
778 /* fClass. */
779 PDM_DRVREG_CLASS_AUDIO,
780 /* cMaxInstances */
781 ~0U,
782 /* cbInstance */
783 sizeof(DRVAUDIOVRDE),
784 /* pfnConstruct */
785 AudioVRDE::drvConstruct,
786 /* pfnDestruct */
787 AudioVRDE::drvDestruct,
788 /* pfnRelocate */
789 NULL,
790 /* pfnIOCtl */
791 NULL,
792 /* pfnPowerOn */
793 NULL,
794 /* pfnReset */
795 NULL,
796 /* pfnSuspend */
797 NULL,
798 /* pfnResume */
799 NULL,
800 /* pfnAttach */
801 NULL,
802 /* pfnDetach */
803 NULL,
804 /* pfnPowerOff */
805 AudioVRDE::drvPowerOff,
806 /* pfnSoftReset */
807 NULL,
808 /* u32EndVersion */
809 PDM_DRVREG_VERSION
810};
811
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