VirtualBox

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

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

DrvAudio,++: Started going over and tidying up the StreamPlay functionality. First major change is to treat the RAW layout just like non-RAW ones and count all in bytes. bugref:9890

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