VirtualBox

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

Last change on this file since 94321 was 93444, checked in by vboxsync, 2 years ago

VMM,Main,HostServices: Use a function table for accessing the VBoxVMM.dll/so/dylib functionality, and load it dynamically when the Console object is initialized. Also converted a few drivers in Main to use device helpers to get config values and such. bugref:10074

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

© 2023 Oracle
ContactPrivacy policyTerms of Use