VirtualBox

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

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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