VirtualBox

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

Last change on this file since 78425 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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