VirtualBox

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

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

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use