VirtualBox

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

Last change on this file since 70772 was 70644, checked in by vboxsync, 6 years ago

Audio/Main: More code needed for attaching / detaching host backends at runtime.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use