VirtualBox

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

Last change on this file since 65660 was 65660, checked in by vboxsync, 8 years ago

Audio: Added PDMAUDIOSTREAMLAYOUT_RAW to pass the raw audio data to the VRDP backend.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette