VirtualBox

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

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

Audio: Removed PDMAUDIOFRAME. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.1 KB
Line 
1/* $Id: DrvAudioVRDE.cpp 89415 2021-05-31 20:31:47Z 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
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
303static int vrdeCreateStreamIn(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgAcq)
304{
305 /*
306 * The VRDP server does its own mixing and resampling as it may server
307 * multiple clients all with different sound formats. So, it feeds us
308 * raw mixer frames (somewhat akind to stereo signed 64-bit, see
309 * st_sample_t and PDMAUDIOFRAME).
310 */
311 PDMAudioPropsInitEx(&pCfgAcq->Props, 8 /*64-bit*/, true /*fSigned*/, 2 /*stereo*/, 22050 /*Hz*/,
312 true /*fLittleEndian*/, true /*fRaw*/);
313
314 /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
315 const uint32_t cFramesVrdpServer = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 200 /*ms*/);
316
317 int rc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, PDMAudioPropsFramesToBytes(&pCfgAcq->Props, cFramesVrdpServer));
318 if (RT_SUCCESS(rc))
319 {
320 pCfgAcq->Backend.cFramesPeriod = cFramesVrdpServer;
321/** @todo r=bird: This is inconsistent with the above buffer allocation and I
322 * think also ALSA and Pulse backends way of setting cFramesBufferSize. */
323 pCfgAcq->Backend.cFramesBufferSize = cFramesVrdpServer * 2; /* Use "double buffering". */
324 pCfgAcq->Backend.cFramesPreBuffering = cFramesVrdpServer;
325 }
326
327 return rc;
328}
329
330
331static int vrdeCreateStreamOut(PPDMAUDIOSTREAMCFG pCfgAcq)
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*/, 22050 /*Hz*/,
341 true /*fLittleEndian*/, true /*fRaw*/);
342
343 /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
344 /** @todo r=bird: So, if VRDP does 200ms chunks, why do we report 100ms
345 * buffer and 20ms period? How does these parameters at all correlate
346 * with the above comment?!? */
347 pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 20 /*ms*/);
348 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/);
349 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
350
351 return VINF_SUCCESS;
352}
353
354
355/**
356 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
357 */
358static DECLCALLBACK(int) drvAudioVrdeHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
359 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
360{
361 PDRVAUDIOVRDE pThis = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
362 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
363 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
364 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
365 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
366
367 /*
368 * Only create a stream if we have clients.
369 */
370 int rc;
371 NOREF(pThis);
372#if 0 /* later maybe */
373 if (pThis->cClients == 0)
374 {
375 LogFunc(("No clients, failing with VERR_AUDIO_STREAM_COULD_NOT_CREATE.\n"));
376 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
377 }
378 else
379#endif
380 {
381 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
382 rc = vrdeCreateStreamIn(pStreamVRDE, pCfgAcq);
383 else
384 rc = vrdeCreateStreamOut(pCfgAcq);
385 PDMAudioStrmCfgCopy(&pStreamVRDE->Cfg, pCfgAcq);
386 }
387 return rc;
388}
389
390
391/**
392 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
393 */
394static DECLCALLBACK(int) drvAudioVrdeHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
395 bool fImmediate)
396{
397 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
398 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
399 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
400 RT_NOREF(fImmediate);
401
402 if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_OUT)
403 {
404 if (pDrv->pConsoleVRDPServer)
405 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
406
407 if (pStreamVRDE->In.pCircBuf)
408 {
409 RTCircBufDestroy(pStreamVRDE->In.pCircBuf);
410 pStreamVRDE->In.pCircBuf = NULL;
411 }
412 }
413
414 return VINF_SUCCESS;
415}
416
417
418/**
419 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
420 */
421static DECLCALLBACK(int) drvAudioVrdeHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
422{
423 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
424 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
425
426 int rc;
427 if (!pDrv->pConsoleVRDPServer)
428 {
429 LogRelMax(32, ("Audio: VRDP console not ready (enable)\n"));
430 rc = VERR_AUDIO_STREAM_NOT_READY;
431 }
432 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
433 {
434 rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE,
435 PDMAudioPropsMilliToFrames(&pStreamVRDE->Cfg.Props, 200 /*ms*/),
436 PDMAudioPropsHz(&pStreamVRDE->Cfg.Props),
437 PDMAudioPropsChannels(&pStreamVRDE->Cfg.Props),
438 PDMAudioPropsSampleBits(&pStreamVRDE->Cfg.Props));
439 if (rc == VERR_NOT_SUPPORTED)
440 {
441 LogRelMax(64, ("Audio: No VRDE client connected, so no input recording available\n"));
442 rc = VERR_AUDIO_STREAM_NOT_READY;
443 }
444 }
445 else
446 rc = VINF_SUCCESS;
447 LogFlowFunc(("returns %Rrc\n", rc));
448 return rc;
449}
450
451
452/**
453 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
454 */
455static DECLCALLBACK(int) drvAudioVrdeHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
456{
457 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
458 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
459
460 int rc;
461 if (!pDrv->pConsoleVRDPServer)
462 {
463 LogRelMax(32, ("Audio: VRDP console not ready (disable)\n"));
464 rc = VERR_AUDIO_STREAM_NOT_READY;
465 }
466 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
467 {
468 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL /* pvUserCtx */);
469 rc = VINF_SUCCESS;
470 }
471 else
472 rc = VINF_SUCCESS;
473 LogFlowFunc(("returns %Rrc\n", rc));
474 return rc;
475}
476
477
478/**
479 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
480 */
481static DECLCALLBACK(int) drvAudioVrdeHA_StreamPause(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 (pause)\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,pfnStreamResume}
498 */
499static DECLCALLBACK(int) drvAudioVrdeHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
500{
501 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
502 RT_NOREF(pStream);
503
504 if (!pDrv->pConsoleVRDPServer)
505 {
506 LogRelMax(32, ("Audio: VRDP console not ready (resume)\n"));
507 return VERR_AUDIO_STREAM_NOT_READY;
508 }
509 LogFlowFunc(("returns VINF_SUCCESS\n"));
510 return VINF_SUCCESS;
511}
512
513
514/**
515 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
516 */
517static DECLCALLBACK(int) drvAudioVrdeHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
518{
519 RT_NOREF(pInterface, pStream);
520 LogFlowFunc(("returns VINF_SUCCESS\n"));
521 return VINF_SUCCESS;
522}
523
524
525/**
526 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
527 */
528static DECLCALLBACK(int) drvAudioVrdeHA_StreamControl(PPDMIHOSTAUDIO pInterface,
529 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
530{
531 /** @todo r=bird: I'd like to get rid of this pfnStreamControl method,
532 * replacing it with individual StreamXxxx methods. That would save us
533 * potentally huge switches and more easily see which drivers implement
534 * which operations (grep for pfnStreamXxxx). */
535 switch (enmStreamCmd)
536 {
537 case PDMAUDIOSTREAMCMD_ENABLE:
538 return drvAudioVrdeHA_StreamEnable(pInterface, pStream);
539 case PDMAUDIOSTREAMCMD_DISABLE:
540 return drvAudioVrdeHA_StreamDisable(pInterface, pStream);
541 case PDMAUDIOSTREAMCMD_PAUSE:
542 return drvAudioVrdeHA_StreamPause(pInterface, pStream);
543 case PDMAUDIOSTREAMCMD_RESUME:
544 return drvAudioVrdeHA_StreamResume(pInterface, pStream);
545 case PDMAUDIOSTREAMCMD_DRAIN:
546 return drvAudioVrdeHA_StreamDrain(pInterface, pStream);
547
548 case PDMAUDIOSTREAMCMD_END:
549 case PDMAUDIOSTREAMCMD_32BIT_HACK:
550 case PDMAUDIOSTREAMCMD_INVALID:
551 /* no default*/
552 break;
553 }
554 return VERR_NOT_SUPPORTED;
555}
556
557
558/**
559 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
560 */
561static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
562{
563 RT_NOREF(pInterface);
564 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
565
566 if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
567 {
568 /* Return frames instead of bytes here
569 * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
570 return PDMAudioPropsBytesToFrames(&pStreamVRDE->Cfg.Props, (uint32_t)RTCircBufUsed(pStreamVRDE->In.pCircBuf));
571 }
572 return 0;
573}
574
575
576/**
577 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
578 */
579static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
580{
581 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
582 RT_NOREF(pStream);
583
584 /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */
585 if (pDrv->cClients)
586 return _16K * sizeof(int64_t) * 2;
587 return 0;
588}
589
590
591/**
592 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
593 */
594static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvAudioVrdeHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
595 PPDMAUDIOBACKENDSTREAM pStream)
596{
597 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
598 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
599
600 return pDrv->cClients > 0 ? PDMHOSTAUDIOSTREAMSTATE_OKAY : PDMHOSTAUDIOSTREAMSTATE_INACTIVE;
601}
602
603
604/**
605 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
606 */
607static DECLCALLBACK(int) drvAudioVrdeHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
608 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
609{
610 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
611 AssertPtr(pDrv);
612 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
613 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
614 if (cbBuf)
615 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
616 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
617
618 if (!pDrv->pConsoleVRDPServer)
619 return VERR_NOT_AVAILABLE;
620
621 /* Prepate the format. */
622 PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->Cfg.Props;
623 VRDEAUDIOFORMAT const uVrdpFormat = VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps),
624 PDMAudioPropsChannels(pProps),
625 PDMAudioPropsSampleBits(pProps),
626 pProps->fSigned);
627 Assert(uVrdpFormat == VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps), 2, 64, true));
628
629 /** @todo r=bird: there was some incoherent mumbling about "using the
630 * internal counter to track if we (still) can write to the VRDP
631 * server or if need to wait another round (time slot)". However it
632 * wasn't accessing any internal counter nor doing anything else
633 * sensible, so I've removed it. */
634
635 uint32_t cFrames = PDMAudioPropsBytesToFrames(&pStream->pStream->Props, cbBuf);
636 Assert(cFrames == cbBuf / (sizeof(uint64_t) * 2));
637 pDrv->pConsoleVRDPServer->SendAudioSamples(pvBuf, cFrames, uVrdpFormat);
638
639 Log3Func(("cFramesWritten=%RU32\n", cFrames));
640 *pcbWritten = PDMAudioPropsFramesToBytes(&pStream->pStream->Props, cFrames);
641 Assert(*pcbWritten == cbBuf);
642 return VINF_SUCCESS;
643}
644
645
646/**
647 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
648 */
649static DECLCALLBACK(int) drvAudioVrdeHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
650 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
651{
652 RT_NOREF(pInterface);
653 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
654 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
655 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
656 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
657 AssertPtrReturn(pcbRead, VERR_INVALID_PARAMETER);
658
659 size_t cbData = 0;
660 if (RTCircBufUsed(pStreamVRDE->In.pCircBuf))
661 {
662 void *pvData = NULL;
663 RTCircBufAcquireReadBlock(pStreamVRDE->In.pCircBuf, cbBuf, &pvData, &cbData);
664
665 if (cbData)
666 memcpy(pvBuf, pvData, cbData);
667
668 RTCircBufReleaseReadBlock(pStreamVRDE->In.pCircBuf, cbData);
669 }
670
671 *pcbRead = (uint32_t)cbData;
672 return VINF_SUCCESS;
673}
674
675
676/*********************************************************************************************************************************
677* PDMIBASE *
678*********************************************************************************************************************************/
679
680/**
681 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
682 */
683static DECLCALLBACK(void *) drvAudioVrdeQueryInterface(PPDMIBASE pInterface, const char *pszIID)
684{
685 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
686 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
687
688 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
689 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
690 return NULL;
691}
692
693
694/*********************************************************************************************************************************
695* PDMDRVREG *
696*********************************************************************************************************************************/
697
698/**
699 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
700 */
701/*static*/ DECLCALLBACK(void) AudioVRDE::drvPowerOff(PPDMDRVINS pDrvIns)
702{
703 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
704 LogFlowFuncEnter();
705
706 if (pThis->pConsoleVRDPServer)
707 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL);
708}
709
710
711/**
712 * @interface_method_impl{PDMDRVREG,pfnDestruct}
713 */
714/*static*/ DECLCALLBACK(void) AudioVRDE::drvDestruct(PPDMDRVINS pDrvIns)
715{
716 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
717 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
718 LogFlowFuncEnter();
719
720 /** @todo For runtime detach maybe:
721 if (pThis->pConsoleVRDPServer)
722 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL); */
723
724 /*
725 * If the AudioVRDE object is still alive, we must clear it's reference to
726 * us since we'll be invalid when we return from this method.
727 */
728 AudioVRDE *pAudioVRDE = pThis->pAudioVRDE;
729 if (pAudioVRDE)
730 {
731 RTCritSectEnter(&pAudioVRDE->mCritSect);
732 pAudioVRDE->mpDrv = NULL;
733 pThis->pAudioVRDE = NULL;
734 RTCritSectLeave(&pAudioVRDE->mCritSect);
735 }
736}
737
738
739/**
740 * Construct a VRDE audio driver instance.
741 *
742 * @copydoc FNPDMDRVCONSTRUCT
743 */
744/* static */
745DECLCALLBACK(int) AudioVRDE::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
746{
747 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
748 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
749 RT_NOREF(fFlags);
750
751 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
752 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
753
754 LogRel(("Audio: Initializing VRDE driver\n"));
755 LogFlowFunc(("fFlags=0x%x\n", fFlags));
756
757 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
758 ("Configuration error: Not possible to attach anything to this driver!\n"),
759 VERR_PDM_DRVINS_NO_ATTACH);
760
761 /*
762 * Init the static parts.
763 */
764 pThis->pDrvIns = pDrvIns;
765 /* IBase */
766 pDrvIns->IBase.pfnQueryInterface = drvAudioVrdeQueryInterface;
767 /* IHostAudio */
768 pThis->IHostAudio.pfnGetConfig = drvAudioVrdeHA_GetConfig;
769 pThis->IHostAudio.pfnGetDevices = NULL;
770 pThis->IHostAudio.pfnSetDevice = NULL;
771 pThis->IHostAudio.pfnGetStatus = drvAudioVrdeHA_GetStatus;
772 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
773 pThis->IHostAudio.pfnStreamConfigHint = NULL;
774 pThis->IHostAudio.pfnStreamCreate = drvAudioVrdeHA_StreamCreate;
775 pThis->IHostAudio.pfnStreamInitAsync = NULL;
776 pThis->IHostAudio.pfnStreamDestroy = drvAudioVrdeHA_StreamDestroy;
777 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
778 pThis->IHostAudio.pfnStreamControl = drvAudioVrdeHA_StreamControl;
779 pThis->IHostAudio.pfnStreamGetReadable = drvAudioVrdeHA_StreamGetReadable;
780 pThis->IHostAudio.pfnStreamGetWritable = drvAudioVrdeHA_StreamGetWritable;
781 pThis->IHostAudio.pfnStreamGetPending = NULL;
782 pThis->IHostAudio.pfnStreamGetState = drvAudioVrdeHA_StreamGetState;
783 pThis->IHostAudio.pfnStreamPlay = drvAudioVrdeHA_StreamPlay;
784 pThis->IHostAudio.pfnStreamCapture = drvAudioVrdeHA_StreamCapture;
785
786 /*
787 * Resolve the interface to the driver above us.
788 */
789 pThis->pIHostAudioPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHOSTAUDIOPORT);
790 AssertPtrReturn(pThis->pIHostAudioPort, VERR_PDM_MISSING_INTERFACE_ABOVE);
791
792 /*
793 * Get the ConsoleVRDPServer object pointer.
794 */
795 void *pvUser;
796 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
797 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc), rc);
798
799 /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE. */
800 pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
801 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pConsoleVRDPServer) || !pThis->pConsoleVRDPServer,
802 ("pConsoleVRDPServer=%p\n", pThis->pConsoleVRDPServer), VERR_INVALID_POINTER);
803 pThis->cClients = 0;
804
805 /*
806 * Get the AudioVRDE object pointer.
807 */
808 pvUser = NULL;
809 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
810 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
811
812 pThis->pAudioVRDE = (AudioVRDE *)pvUser;
813 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pAudioVRDE), ("pAudioVRDE=%p\n", pThis->pAudioVRDE), VERR_INVALID_POINTER);
814 RTCritSectEnter(&pThis->pAudioVRDE->mCritSect);
815 pThis->pAudioVRDE->mpDrv = pThis;
816 RTCritSectLeave(&pThis->pAudioVRDE->mCritSect);
817
818 return VINF_SUCCESS;
819}
820
821
822/**
823 * VRDE audio driver registration record.
824 */
825const PDMDRVREG AudioVRDE::DrvReg =
826{
827 PDM_DRVREG_VERSION,
828 /* szName */
829 "AudioVRDE",
830 /* szRCMod */
831 "",
832 /* szR0Mod */
833 "",
834 /* pszDescription */
835 "Audio driver for VRDE backend",
836 /* fFlags */
837 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
838 /* fClass. */
839 PDM_DRVREG_CLASS_AUDIO,
840 /* cMaxInstances */
841 ~0U,
842 /* cbInstance */
843 sizeof(DRVAUDIOVRDE),
844 /* pfnConstruct */
845 AudioVRDE::drvConstruct,
846 /* pfnDestruct */
847 AudioVRDE::drvDestruct,
848 /* pfnRelocate */
849 NULL,
850 /* pfnIOCtl */
851 NULL,
852 /* pfnPowerOn */
853 NULL,
854 /* pfnReset */
855 NULL,
856 /* pfnSuspend */
857 NULL,
858 /* pfnResume */
859 NULL,
860 /* pfnAttach */
861 NULL,
862 /* pfnDetach */
863 NULL,
864 /* pfnPowerOff */
865 AudioVRDE::drvPowerOff,
866 /* pfnSoftReset */
867 NULL,
868 /* u32EndVersion */
869 PDM_DRVREG_VERSION
870};
871
Note: See TracBrowser for help on using the repository browser.

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