VirtualBox

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

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

DrvAudioVRDE.cpp: Return available samples in drvAudioVRDEStreamGetReadable().

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