VirtualBox

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

Last change on this file since 73372 was 73372, checked in by vboxsync, 7 years ago

Build fix.

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