VirtualBox

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

Last change on this file since 82423 was 82255, checked in by vboxsync, 5 years ago

vmm/pdmaudioifs.h: More of the same. bugref:9218

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

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