VirtualBox

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

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

DrvAudioVRDE: Some fixes and cleanups. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.9 KB
Line 
1/* $Id: DrvAudioVRDE.cpp 89543 2021-06-07 09:57:06Z 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 * VRDE stream.
47 */
48typedef struct VRDESTREAM
49{
50 /** Common part. */
51 PDMAUDIOBACKENDSTREAM Core;
52 /** The stream's acquired configuration. */
53 PDMAUDIOSTREAMCFG Cfg;
54 union
55 {
56 struct
57 {
58 /** Circular buffer for holding the recorded audio frames from the host. */
59 PRTCIRCBUF pCircBuf;
60 } In;
61 };
62} VRDESTREAM;
63/** Pointer to a VRDE stream. */
64typedef VRDESTREAM *PVRDESTREAM;
65
66/**
67 * VRDE (host) audio driver instance data.
68 */
69typedef struct DRVAUDIOVRDE
70{
71 /** Pointer to audio VRDE object. */
72 AudioVRDE *pAudioVRDE;
73 /** Pointer to the driver instance structure. */
74 PPDMDRVINS pDrvIns;
75 /** Pointer to the VRDP's console object. */
76 ConsoleVRDPServer *pConsoleVRDPServer;
77 /** Number of connected clients to this VRDE instance. */
78 uint32_t cClients;
79 /** Interface to the driver above us (DrvAudio). */
80 PDMIHOSTAUDIOPORT *pIHostAudioPort;
81 /** Pointer to host audio interface. */
82 PDMIHOSTAUDIO IHostAudio;
83} DRVAUDIOVRDE;
84/** Pointer to the instance data for an VRDE audio driver. */
85typedef DRVAUDIOVRDE *PDRVAUDIOVRDE;
86
87
88/*********************************************************************************************************************************
89* Class AudioVRDE *
90*********************************************************************************************************************************/
91
92AudioVRDE::AudioVRDE(Console *pConsole)
93 : AudioDriver(pConsole)
94 , mpDrv(NULL)
95{
96 RTCritSectInit(&mCritSect);
97}
98
99
100AudioVRDE::~AudioVRDE(void)
101{
102 RTCritSectEnter(&mCritSect);
103 if (mpDrv)
104 {
105 mpDrv->pAudioVRDE = NULL;
106 mpDrv = NULL;
107 }
108 RTCritSectLeave(&mCritSect);
109 RTCritSectDelete(&mCritSect);
110}
111
112
113/**
114 * @copydoc AudioDriver::configureDriver
115 */
116int AudioVRDE::configureDriver(PCFGMNODE pLunCfg)
117{
118 int rc = CFGMR3InsertInteger(pLunCfg, "Object", (uintptr_t)this);
119 AssertRCReturn(rc, rc);
120 CFGMR3InsertInteger(pLunCfg, "ObjectVRDPServer", (uintptr_t)mpConsole->i_consoleVRDPServer());
121 AssertRCReturn(rc, rc);
122
123 return AudioDriver::configureDriver(pLunCfg);
124}
125
126
127void AudioVRDE::onVRDEClientConnect(uint32_t uClientID)
128{
129 RT_NOREF(uClientID);
130
131 RTCritSectEnter(&mCritSect);
132 if (mpDrv)
133 {
134 mpDrv->cClients++;
135 LogRel2(("Audio: VRDE client connected (#%u)\n", mpDrv->cClients));
136
137#if 0 /* later, maybe */
138 /*
139 * The first client triggers a device change event in both directions
140 * so that can start talking to the audio device.
141 *
142 * Note! Should be okay to stay in the critical section here, as it's only
143 * used at construction and destruction time.
144 */
145 if (mpDrv->cClients == 1)
146 {
147 VMSTATE enmState = PDMDrvHlpVMState(mpDrv->pDrvIns);
148 if (enmState <= VMSTATE_POWERING_OFF)
149 {
150 PDMIHOSTAUDIOPORT *pIHostAudioPort = mpDrv->pIHostAudioPort;
151 AssertPtr(pIHostAudioPort);
152 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_OUT, NULL /*pvUser*/);
153 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_IN, NULL /*pvUser*/);
154 }
155 }
156#endif
157 }
158 RTCritSectLeave(&mCritSect);
159}
160
161
162void AudioVRDE::onVRDEClientDisconnect(uint32_t uClientID)
163{
164 RT_NOREF(uClientID);
165
166 RTCritSectEnter(&mCritSect);
167 if (mpDrv)
168 {
169 Assert(mpDrv->cClients > 0);
170 mpDrv->cClients--;
171 LogRel2(("Audio: VRDE client disconnected (%u left)\n", mpDrv->cClients));
172#if 0 /* later maybe */
173 /*
174 * The last client leaving triggers a device change event in both
175 * directions so the audio devices can stop wasting time trying to
176 * talk to us. (There is an additional safeguard in
177 * drvAudioVrdeHA_StreamGetStatus.)
178 */
179 if (mpDrv->cClients == 0)
180 {
181 VMSTATE enmState = PDMDrvHlpVMState(mpDrv->pDrvIns);
182 if (enmState <= VMSTATE_POWERING_OFF)
183 {
184 PDMIHOSTAUDIOPORT *pIHostAudioPort = mpDrv->pIHostAudioPort;
185 AssertPtr(pIHostAudioPort);
186 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_OUT, NULL /*pvUser*/);
187 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_IN, NULL /*pvUser*/);
188 }
189 }
190#endif
191 }
192 RTCritSectLeave(&mCritSect);
193}
194
195
196int AudioVRDE::onVRDEControl(bool fEnable, uint32_t uFlags)
197{
198 RT_NOREF(fEnable, uFlags);
199 LogFlowThisFunc(("fEnable=%RTbool, uFlags=0x%x\n", fEnable, uFlags));
200
201 if (mpDrv == NULL)
202 return VERR_INVALID_STATE;
203
204 return VINF_SUCCESS; /* Never veto. */
205}
206
207
208/**
209 * Marks the beginning of sending captured audio data from a connected
210 * RDP client.
211 *
212 * @returns VBox status code.
213 * @param pvContext The context; in this case a pointer to a
214 * VRDESTREAMIN structure.
215 * @param pVRDEAudioBegin Pointer to a VRDEAUDIOINBEGIN structure.
216 */
217int AudioVRDE::onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin)
218{
219 AssertPtrReturn(pvContext, VERR_INVALID_POINTER);
220 AssertPtrReturn(pVRDEAudioBegin, VERR_INVALID_POINTER);
221 PVRDESTREAM pVRDEStrmIn = (PVRDESTREAM)pvContext;
222 AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
223
224#ifdef LOG_ENABLED
225 VRDEAUDIOFORMAT const audioFmt = pVRDEAudioBegin->fmt;
226 LogFlowFunc(("cbSample=%RU32, iSampleHz=%d, cChannels=%d, cBits=%d, fUnsigned=%RTbool\n",
227 VRDE_AUDIO_FMT_BYTES_PER_SAMPLE(audioFmt), VRDE_AUDIO_FMT_SAMPLE_FREQ(audioFmt),
228 VRDE_AUDIO_FMT_CHANNELS(audioFmt), VRDE_AUDIO_FMT_BITS_PER_SAMPLE(audioFmt), VRDE_AUDIO_FMT_SIGNED(audioFmt)));
229#endif
230
231 return VINF_SUCCESS;
232}
233
234
235int AudioVRDE::onVRDEInputData(void *pvContext, const void *pvData, uint32_t cbData)
236{
237 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pvContext;
238 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
239 LogFlowFunc(("cbData=%#x\n", cbData));
240
241 void *pvBuf = NULL;
242 size_t cbBuf = 0;
243 RTCircBufAcquireWriteBlock(pStreamVRDE->In.pCircBuf, cbData, &pvBuf, &cbBuf);
244
245 if (cbBuf)
246 memcpy(pvBuf, pvData, cbBuf);
247
248 RTCircBufReleaseWriteBlock(pStreamVRDE->In.pCircBuf, cbBuf);
249
250 if (cbBuf < cbData)
251 LogRelMax(999, ("VRDE: Capturing audio data lost %zu bytes\n", cbData - cbBuf)); /** @todo Use an error counter. */
252
253 return VINF_SUCCESS; /** @todo r=andy How to tell the caller if we were not able to handle *all* input data? */
254}
255
256
257int AudioVRDE::onVRDEInputEnd(void *pvContext)
258{
259 RT_NOREF(pvContext);
260 return VINF_SUCCESS;
261}
262
263
264int AudioVRDE::onVRDEInputIntercept(bool fEnabled)
265{
266 RT_NOREF(fEnabled);
267 return VINF_SUCCESS; /* Never veto. */
268}
269
270
271
272/*********************************************************************************************************************************
273* PDMIHOSTAUDIO *
274*********************************************************************************************************************************/
275
276/**
277 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
278 */
279static DECLCALLBACK(int) drvAudioVrdeHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
280{
281 RT_NOREF(pInterface);
282 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
283
284 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "VRDE");
285 pBackendCfg->cbStream = sizeof(VRDESTREAM);
286 pBackendCfg->fFlags = 0;
287 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
288 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
289
290 return VINF_SUCCESS;
291}
292
293
294/**
295 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
296 */
297static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVrdeHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
298{
299 RT_NOREF(pInterface, enmDir);
300 return PDMAUDIOBACKENDSTS_RUNNING;
301}
302
303
304/**
305 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
306 */
307static DECLCALLBACK(int) drvAudioVrdeHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
308 PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
309{
310 PDRVAUDIOVRDE pThis = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
311 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
312 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
313 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
314 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
315
316 /*
317 * Only create a stream if we have clients.
318 */
319 int rc;
320 NOREF(pThis);
321#if 0 /* later maybe */
322 if (pThis->cClients == 0)
323 {
324 LogFunc(("No clients, failing with VERR_AUDIO_STREAM_COULD_NOT_CREATE.\n"));
325 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
326 }
327 else
328#endif
329 {
330 /*
331 * The VRDP server does its own mixing and resampling because it may be
332 * sending the audio to any number of different clients all with different
333 * formats (including clients which hasn't yet connected). So, it desires
334 * the raw data from the mixer (somewhat akind to stereo signed 64-bit,
335 * see st_sample_t and PDMAUDIOFRAME).
336 */
337 PDMAudioPropsInitEx(&pCfgAcq->Props, 8 /*64-bit*/, true /*fSigned*/, 2 /*stereo*/,
338 22050 /*Hz - VRDP_AUDIO_CHUNK_INTERNAL_FREQ_HZ*/,
339 true /*fLittleEndian*/, true /*fRaw*/);
340
341 /* According to the VRDP docs (VRDP_AUDIO_CHUNK_TIME_MS), the VRDP server
342 stores audio in 200ms chunks. */
343 const uint32_t cFramesVrdpServer = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 200 /*ms*/);
344
345 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
346 {
347 pCfgAcq->Backend.cFramesBufferSize = cFramesVrdpServer;
348 pCfgAcq->Backend.cFramesPeriod = cFramesVrdpServer / 4; /* This is utter non-sense, but whatever. */
349 pCfgAcq->Backend.cFramesPreBuffering = pCfgReq->Backend.cFramesPreBuffering * cFramesVrdpServer
350 / RT_MAX(pCfgReq->Backend.cFramesBufferSize, 1);
351
352 rc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, PDMAudioPropsFramesToBytes(&pCfgAcq->Props, cFramesVrdpServer));
353 }
354 else
355 {
356 /** @todo r=bird: So, if VRDP does 200ms chunks, why do we report 100ms
357 * buffer and 20ms period? How does these parameters at all correlate
358 * with the above comment?!? */
359 pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 20 /*ms*/);
360 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/);
361 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
362 rc = VINF_SUCCESS;
363 }
364
365 PDMAudioStrmCfgCopy(&pStreamVRDE->Cfg, pCfgAcq);
366 }
367 return rc;
368}
369
370
371/**
372 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
373 */
374static DECLCALLBACK(int) drvAudioVrdeHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
375 bool fImmediate)
376{
377 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
378 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
379 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
380 RT_NOREF(fImmediate);
381
382 if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
383 {
384 LogFlowFunc(("Calling SendAudioInputEnd\n"));
385 if (pDrv->pConsoleVRDPServer)
386 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
387
388 if (pStreamVRDE->In.pCircBuf)
389 {
390 RTCircBufDestroy(pStreamVRDE->In.pCircBuf);
391 pStreamVRDE->In.pCircBuf = NULL;
392 }
393 }
394
395 return VINF_SUCCESS;
396}
397
398
399/**
400 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
401 */
402static DECLCALLBACK(int) drvAudioVrdeHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
403{
404 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
405 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
406
407 int rc;
408 if (!pDrv->pConsoleVRDPServer)
409 {
410 LogRelMax(32, ("Audio: VRDP console not ready (enable)\n"));
411 rc = VERR_AUDIO_STREAM_NOT_READY;
412 }
413 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
414 {
415 rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE,
416 PDMAudioPropsMilliToFrames(&pStreamVRDE->Cfg.Props, 200 /*ms*/),
417 PDMAudioPropsHz(&pStreamVRDE->Cfg.Props),
418 PDMAudioPropsChannels(&pStreamVRDE->Cfg.Props),
419 PDMAudioPropsSampleBits(&pStreamVRDE->Cfg.Props));
420 LogFlowFunc(("SendAudioInputBegin returns %Rrc\n", rc));
421 if (rc == VERR_NOT_SUPPORTED)
422 {
423 LogRelMax(64, ("Audio: No VRDE client connected, so no input recording available\n"));
424 rc = VERR_AUDIO_STREAM_NOT_READY;
425 }
426 }
427 else
428 rc = VINF_SUCCESS;
429 LogFlowFunc(("returns %Rrc\n", rc));
430 return rc;
431}
432
433
434/**
435 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
436 */
437static DECLCALLBACK(int) drvAudioVrdeHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
438{
439 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
440 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
441
442 int rc;
443 if (!pDrv->pConsoleVRDPServer)
444 {
445 LogRelMax(32, ("Audio: VRDP console not ready (disable)\n"));
446 rc = VERR_AUDIO_STREAM_NOT_READY;
447 }
448 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
449 {
450 LogFlowFunc(("Calling SendAudioInputEnd\n"));
451 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL /* pvUserCtx */);
452 rc = VINF_SUCCESS;
453 }
454 else
455 rc = VINF_SUCCESS;
456 LogFlowFunc(("returns %Rrc\n", rc));
457 return rc;
458}
459
460
461/**
462 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
463 */
464static DECLCALLBACK(int) drvAudioVrdeHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
465{
466 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
467 RT_NOREF(pStream);
468
469 if (!pDrv->pConsoleVRDPServer)
470 {
471 LogRelMax(32, ("Audio: VRDP console not ready (pause)\n"));
472 return VERR_AUDIO_STREAM_NOT_READY;
473 }
474 LogFlowFunc(("returns VINF_SUCCESS\n"));
475 return VINF_SUCCESS;
476}
477
478
479/**
480 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
481 */
482static DECLCALLBACK(int) drvAudioVrdeHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
483{
484 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
485 RT_NOREF(pStream);
486
487 if (!pDrv->pConsoleVRDPServer)
488 {
489 LogRelMax(32, ("Audio: VRDP console not ready (resume)\n"));
490 return VERR_AUDIO_STREAM_NOT_READY;
491 }
492 LogFlowFunc(("returns VINF_SUCCESS\n"));
493 return VINF_SUCCESS;
494}
495
496
497/**
498 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
499 */
500static DECLCALLBACK(int) drvAudioVrdeHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
501{
502 RT_NOREF(pInterface, pStream);
503 LogFlowFunc(("returns VINF_SUCCESS\n"));
504 return VINF_SUCCESS;
505}
506
507
508/**
509 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
510 */
511static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvAudioVrdeHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
512 PPDMAUDIOBACKENDSTREAM pStream)
513{
514 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
515 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
516
517 return pDrv->cClients > 0 ? PDMHOSTAUDIOSTREAMSTATE_OKAY : PDMHOSTAUDIOSTREAMSTATE_INACTIVE;
518}
519
520
521/**
522 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
523 */
524static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
525{
526 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
527 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
528
529 /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */
530 if (pDrv->cClients)
531 return PDMAudioPropsFramesToBytes(&pStreamVRDE->Cfg.Props, pStreamVRDE->Cfg.Backend.cFramesBufferSize);
532 return 0;
533}
534
535
536/**
537 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
538 */
539static DECLCALLBACK(int) drvAudioVrdeHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
540 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
541{
542 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
543 AssertPtr(pDrv);
544 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
545 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
546 if (cbBuf)
547 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
548 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
549
550 if (!pDrv->pConsoleVRDPServer)
551 return VERR_NOT_AVAILABLE;
552
553 /* Prepate the format. */
554 PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->Cfg.Props;
555 VRDEAUDIOFORMAT const uVrdpFormat = VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps),
556 PDMAudioPropsChannels(pProps),
557 PDMAudioPropsSampleBits(pProps),
558 pProps->fSigned);
559 Assert(uVrdpFormat == VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps), 2, 64, true));
560
561 /** @todo r=bird: there was some incoherent mumbling about "using the
562 * internal counter to track if we (still) can write to the VRDP
563 * server or if need to wait another round (time slot)". However it
564 * wasn't accessing any internal counter nor doing anything else
565 * sensible, so I've removed it. */
566
567 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pStream->pStream->Cfg.Props, cbBuf);
568 Assert(cFrames == cbBuf / (sizeof(uint64_t) * 2));
569 pDrv->pConsoleVRDPServer->SendAudioSamples(pvBuf, cFrames, uVrdpFormat);
570
571 Log3Func(("cFramesWritten=%RU32\n", cFrames));
572 *pcbWritten = PDMAudioPropsFramesToBytes(&pStream->pStream->Cfg.Props, cFrames);
573 Assert(*pcbWritten == cbBuf);
574 return VINF_SUCCESS;
575}
576
577
578/**
579 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
580 */
581static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
582{
583 RT_NOREF(pInterface);
584 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
585
586 AssertReturn(pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN, 0);
587 uint32_t cbRet = (uint32_t)RTCircBufUsed(pStreamVRDE->In.pCircBuf);
588 Log4Func(("returns %#x\n", cbRet));
589 return cbRet;
590}
591
592
593/**
594 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
595 */
596static DECLCALLBACK(int) drvAudioVrdeHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
597 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
598{
599 RT_NOREF(pInterface);
600 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
601 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
602 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
603 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
604 AssertPtrReturn(pcbRead, VERR_INVALID_PARAMETER);
605
606 *pcbRead = 0;
607 while (cbBuf > 0 && RTCircBufUsed(pStreamVRDE->In.pCircBuf) > 0)
608 {
609 size_t cbData = 0;
610 void *pvData = NULL;
611 RTCircBufAcquireReadBlock(pStreamVRDE->In.pCircBuf, cbBuf, &pvData, &cbData);
612
613 memcpy(pvBuf, pvData, cbData);
614
615 RTCircBufReleaseReadBlock(pStreamVRDE->In.pCircBuf, cbData);
616
617 *pcbRead += (uint32_t)cbData;
618 cbBuf -= (uint32_t)cbData;
619 pvData = (uint8_t *)pvData + cbData;
620 }
621
622 LogFlowFunc(("returns %#x bytes\n", *pcbRead));
623 return VINF_SUCCESS;
624}
625
626
627/*********************************************************************************************************************************
628* PDMIBASE *
629*********************************************************************************************************************************/
630
631/**
632 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
633 */
634static DECLCALLBACK(void *) drvAudioVrdeQueryInterface(PPDMIBASE pInterface, const char *pszIID)
635{
636 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
637 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
638
639 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
640 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
641 return NULL;
642}
643
644
645/*********************************************************************************************************************************
646* PDMDRVREG *
647*********************************************************************************************************************************/
648
649/**
650 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
651 */
652/*static*/ DECLCALLBACK(void) AudioVRDE::drvPowerOff(PPDMDRVINS pDrvIns)
653{
654 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
655 LogFlowFuncEnter();
656
657 if (pThis->pConsoleVRDPServer)
658 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL);
659}
660
661
662/**
663 * @interface_method_impl{PDMDRVREG,pfnDestruct}
664 */
665/*static*/ DECLCALLBACK(void) AudioVRDE::drvDestruct(PPDMDRVINS pDrvIns)
666{
667 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
668 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
669 LogFlowFuncEnter();
670
671 /** @todo For runtime detach maybe:
672 if (pThis->pConsoleVRDPServer)
673 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL); */
674
675 /*
676 * If the AudioVRDE object is still alive, we must clear it's reference to
677 * us since we'll be invalid when we return from this method.
678 */
679 AudioVRDE *pAudioVRDE = pThis->pAudioVRDE;
680 if (pAudioVRDE)
681 {
682 RTCritSectEnter(&pAudioVRDE->mCritSect);
683 pAudioVRDE->mpDrv = NULL;
684 pThis->pAudioVRDE = NULL;
685 RTCritSectLeave(&pAudioVRDE->mCritSect);
686 }
687}
688
689
690/**
691 * Construct a VRDE audio driver instance.
692 *
693 * @copydoc FNPDMDRVCONSTRUCT
694 */
695/* static */
696DECLCALLBACK(int) AudioVRDE::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
697{
698 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
699 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
700 RT_NOREF(fFlags);
701
702 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
703 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
704
705 LogRel(("Audio: Initializing VRDE driver\n"));
706 LogFlowFunc(("fFlags=0x%x\n", fFlags));
707
708 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
709 ("Configuration error: Not possible to attach anything to this driver!\n"),
710 VERR_PDM_DRVINS_NO_ATTACH);
711
712 /*
713 * Init the static parts.
714 */
715 pThis->pDrvIns = pDrvIns;
716 /* IBase */
717 pDrvIns->IBase.pfnQueryInterface = drvAudioVrdeQueryInterface;
718 /* IHostAudio */
719 pThis->IHostAudio.pfnGetConfig = drvAudioVrdeHA_GetConfig;
720 pThis->IHostAudio.pfnGetDevices = NULL;
721 pThis->IHostAudio.pfnSetDevice = NULL;
722 pThis->IHostAudio.pfnGetStatus = drvAudioVrdeHA_GetStatus;
723 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
724 pThis->IHostAudio.pfnStreamConfigHint = NULL;
725 pThis->IHostAudio.pfnStreamCreate = drvAudioVrdeHA_StreamCreate;
726 pThis->IHostAudio.pfnStreamInitAsync = NULL;
727 pThis->IHostAudio.pfnStreamDestroy = drvAudioVrdeHA_StreamDestroy;
728 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
729 pThis->IHostAudio.pfnStreamEnable = drvAudioVrdeHA_StreamEnable;
730 pThis->IHostAudio.pfnStreamDisable = drvAudioVrdeHA_StreamDisable;
731 pThis->IHostAudio.pfnStreamPause = drvAudioVrdeHA_StreamPause;
732 pThis->IHostAudio.pfnStreamResume = drvAudioVrdeHA_StreamResume;
733 pThis->IHostAudio.pfnStreamDrain = drvAudioVrdeHA_StreamDrain;
734 pThis->IHostAudio.pfnStreamGetState = drvAudioVrdeHA_StreamGetState;
735 pThis->IHostAudio.pfnStreamGetPending = NULL;
736 pThis->IHostAudio.pfnStreamGetWritable = drvAudioVrdeHA_StreamGetWritable;
737 pThis->IHostAudio.pfnStreamPlay = drvAudioVrdeHA_StreamPlay;
738 pThis->IHostAudio.pfnStreamGetReadable = drvAudioVrdeHA_StreamGetReadable;
739 pThis->IHostAudio.pfnStreamCapture = drvAudioVrdeHA_StreamCapture;
740
741 /*
742 * Resolve the interface to the driver above us.
743 */
744 pThis->pIHostAudioPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHOSTAUDIOPORT);
745 AssertPtrReturn(pThis->pIHostAudioPort, VERR_PDM_MISSING_INTERFACE_ABOVE);
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 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pConsoleVRDPServer) || !pThis->pConsoleVRDPServer,
757 ("pConsoleVRDPServer=%p\n", pThis->pConsoleVRDPServer), VERR_INVALID_POINTER);
758 pThis->cClients = 0;
759
760 /*
761 * Get the AudioVRDE object pointer.
762 */
763 pvUser = NULL;
764 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
765 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
766
767 pThis->pAudioVRDE = (AudioVRDE *)pvUser;
768 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pAudioVRDE), ("pAudioVRDE=%p\n", pThis->pAudioVRDE), VERR_INVALID_POINTER);
769 RTCritSectEnter(&pThis->pAudioVRDE->mCritSect);
770 pThis->pAudioVRDE->mpDrv = pThis;
771 RTCritSectLeave(&pThis->pAudioVRDE->mCritSect);
772
773 return VINF_SUCCESS;
774}
775
776
777/**
778 * VRDE audio driver registration record.
779 */
780const PDMDRVREG AudioVRDE::DrvReg =
781{
782 PDM_DRVREG_VERSION,
783 /* szName */
784 "AudioVRDE",
785 /* szRCMod */
786 "",
787 /* szR0Mod */
788 "",
789 /* pszDescription */
790 "Audio driver for VRDE backend",
791 /* fFlags */
792 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
793 /* fClass. */
794 PDM_DRVREG_CLASS_AUDIO,
795 /* cMaxInstances */
796 ~0U,
797 /* cbInstance */
798 sizeof(DRVAUDIOVRDE),
799 /* pfnConstruct */
800 AudioVRDE::drvConstruct,
801 /* pfnDestruct */
802 AudioVRDE::drvDestruct,
803 /* pfnRelocate */
804 NULL,
805 /* pfnIOCtl */
806 NULL,
807 /* pfnPowerOn */
808 NULL,
809 /* pfnReset */
810 NULL,
811 /* pfnSuspend */
812 NULL,
813 /* pfnResume */
814 NULL,
815 /* pfnAttach */
816 NULL,
817 /* pfnDetach */
818 NULL,
819 /* pfnPowerOff */
820 AudioVRDE::drvPowerOff,
821 /* pfnSoftReset */
822 NULL,
823 /* u32EndVersion */
824 PDM_DRVREG_VERSION
825};
826
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