VirtualBox

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

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

Audio: Added PDMIHOSTAUDIO::pfnSetDevice with implementation for CoreAudio. Added CoreAudio config values OutputDeviceID and InputDeviceID for the same purpose. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.9 KB
Line 
1/* $Id: DrvAudioVRDE.cpp 89258 2021-05-25 09:58:08Z 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/* Sanity. */
88AssertCompileSize(PDMAUDIOFRAME, sizeof(int64_t) * 2 /* st_sample_t using by VRDP server */);
89
90
91
92/*********************************************************************************************************************************
93* Class AudioVRDE *
94*********************************************************************************************************************************/
95
96AudioVRDE::AudioVRDE(Console *pConsole)
97 : AudioDriver(pConsole)
98 , mpDrv(NULL)
99{
100 RTCritSectInit(&mCritSect);
101}
102
103
104AudioVRDE::~AudioVRDE(void)
105{
106 RTCritSectEnter(&mCritSect);
107 if (mpDrv)
108 {
109 mpDrv->pAudioVRDE = NULL;
110 mpDrv = NULL;
111 }
112 RTCritSectLeave(&mCritSect);
113 RTCritSectDelete(&mCritSect);
114}
115
116
117/**
118 * @copydoc AudioDriver::configureDriver
119 */
120int AudioVRDE::configureDriver(PCFGMNODE pLunCfg)
121{
122 int rc = CFGMR3InsertInteger(pLunCfg, "Object", (uintptr_t)this);
123 AssertRCReturn(rc, rc);
124 CFGMR3InsertInteger(pLunCfg, "ObjectVRDPServer", (uintptr_t)mpConsole->i_consoleVRDPServer());
125 AssertRCReturn(rc, rc);
126
127 return AudioDriver::configureDriver(pLunCfg);
128}
129
130
131void AudioVRDE::onVRDEClientConnect(uint32_t uClientID)
132{
133 RT_NOREF(uClientID);
134
135 RTCritSectEnter(&mCritSect);
136 if (mpDrv)
137 {
138 mpDrv->cClients++;
139 LogRel2(("Audio: VRDE client connected (#%u)\n", mpDrv->cClients));
140
141#if 0 /* later, maybe */
142 /*
143 * The first client triggers a device change event in both directions
144 * so that can start talking to the audio device.
145 *
146 * Note! Should be okay to stay in the critical section here, as it's only
147 * used at construction and destruction time.
148 */
149 if (mpDrv->cClients == 1)
150 {
151 VMSTATE enmState = PDMDrvHlpVMState(mpDrv->pDrvIns);
152 if (enmState <= VMSTATE_POWERING_OFF)
153 {
154 PDMIHOSTAUDIOPORT *pIHostAudioPort = mpDrv->pIHostAudioPort;
155 AssertPtr(pIHostAudioPort);
156 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_OUT, NULL /*pvUser*/);
157 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_IN, NULL /*pvUser*/);
158 }
159 }
160#endif
161 }
162 RTCritSectLeave(&mCritSect);
163}
164
165
166void AudioVRDE::onVRDEClientDisconnect(uint32_t uClientID)
167{
168 RT_NOREF(uClientID);
169
170 RTCritSectEnter(&mCritSect);
171 if (mpDrv)
172 {
173 Assert(mpDrv->cClients > 0);
174 mpDrv->cClients--;
175 LogRel2(("Audio: VRDE client disconnected (%u left)\n", mpDrv->cClients));
176#if 0 /* later maybe */
177 /*
178 * The last client leaving triggers a device change event in both
179 * directions so the audio devices can stop wasting time trying to
180 * talk to us. (There is an additional safeguard in
181 * drvAudioVrdeHA_StreamGetStatus.)
182 */
183 if (mpDrv->cClients == 0)
184 {
185 VMSTATE enmState = PDMDrvHlpVMState(mpDrv->pDrvIns);
186 if (enmState <= VMSTATE_POWERING_OFF)
187 {
188 PDMIHOSTAUDIOPORT *pIHostAudioPort = mpDrv->pIHostAudioPort;
189 AssertPtr(pIHostAudioPort);
190 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_OUT, NULL /*pvUser*/);
191 pIHostAudioPort->pfnNotifyDeviceChanged(pIHostAudioPort, PDMAUDIODIR_IN, NULL /*pvUser*/);
192 }
193 }
194#endif
195 }
196 RTCritSectLeave(&mCritSect);
197}
198
199
200int AudioVRDE::onVRDEControl(bool fEnable, uint32_t uFlags)
201{
202 RT_NOREF(fEnable, uFlags);
203 LogFlowThisFunc(("fEnable=%RTbool, uFlags=0x%x\n", fEnable, uFlags));
204
205 if (mpDrv == NULL)
206 return VERR_INVALID_STATE;
207
208 return VINF_SUCCESS; /* Never veto. */
209}
210
211
212/**
213 * Marks the beginning of sending captured audio data from a connected
214 * RDP client.
215 *
216 * @returns VBox status code.
217 * @param pvContext The context; in this case a pointer to a
218 * VRDESTREAMIN structure.
219 * @param pVRDEAudioBegin Pointer to a VRDEAUDIOINBEGIN structure.
220 */
221int AudioVRDE::onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin)
222{
223 AssertPtrReturn(pvContext, VERR_INVALID_POINTER);
224 AssertPtrReturn(pVRDEAudioBegin, VERR_INVALID_POINTER);
225 PVRDESTREAM pVRDEStrmIn = (PVRDESTREAM)pvContext;
226 AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
227
228#ifdef LOG_ENABLED
229 VRDEAUDIOFORMAT const audioFmt = pVRDEAudioBegin->fmt;
230 LogFlowFunc(("cbSample=%RU32, iSampleHz=%d, cChannels=%d, cBits=%d, fUnsigned=%RTbool\n",
231 VRDE_AUDIO_FMT_BYTES_PER_SAMPLE(audioFmt), VRDE_AUDIO_FMT_SAMPLE_FREQ(audioFmt),
232 VRDE_AUDIO_FMT_CHANNELS(audioFmt), VRDE_AUDIO_FMT_BITS_PER_SAMPLE(audioFmt), VRDE_AUDIO_FMT_SIGNED(audioFmt)));
233#endif
234
235 return VINF_SUCCESS;
236}
237
238
239int AudioVRDE::onVRDEInputData(void *pvContext, const void *pvData, uint32_t cbData)
240{
241 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pvContext;
242 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
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
307static int vrdeCreateStreamIn(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgAcq)
308{
309 /*
310 * The VRDP server does its own mixing and resampling as it may server
311 * multiple clients all with different sound formats. So, it feeds us
312 * raw mixer frames (somewhat akind to stereo signed 64-bit, see
313 * st_sample_t and PDMAUDIOFRAME).
314 */
315 pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW;
316 PDMAudioPropsInitEx(&pCfgAcq->Props, 8 /*64-bit*/, true /*fSigned*/, 2 /*stereo*/, 22050 /*Hz*/,
317 true /*fLittleEndian*/, true /*fRaw*/);
318
319 /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
320 const uint32_t cFramesVrdpServer = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 200 /*ms*/);
321
322 int rc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, PDMAudioPropsFramesToBytes(&pCfgAcq->Props, cFramesVrdpServer));
323 if (RT_SUCCESS(rc))
324 {
325 pCfgAcq->Backend.cFramesPeriod = cFramesVrdpServer;
326/** @todo r=bird: This is inconsistent with the above buffer allocation and I
327 * think also ALSA and Pulse backends way of setting cFramesBufferSize. */
328 pCfgAcq->Backend.cFramesBufferSize = cFramesVrdpServer * 2; /* Use "double buffering". */
329 pCfgAcq->Backend.cFramesPreBuffering = cFramesVrdpServer;
330 }
331
332 return rc;
333}
334
335
336static int vrdeCreateStreamOut(PPDMAUDIOSTREAMCFG pCfgAcq)
337{
338 /*
339 * The VRDP server does its own mixing and resampling because it may be
340 * sending the audio to any number of different clients all with different
341 * formats (including clients which hasn't yet connected). So, it desires
342 * the raw data from the mixer (somewhat akind to stereo signed 64-bit,
343 * see st_sample_t and PDMAUDIOFRAME).
344 */
345 pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW;
346 PDMAudioPropsInitEx(&pCfgAcq->Props, 8 /*64-bit*/, true /*fSigned*/, 2 /*stereo*/, 22050 /*Hz*/,
347 true /*fLittleEndian*/, true /*fRaw*/);
348
349 /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
350 /** @todo r=bird: So, if VRDP does 200ms chunks, why do we report 100ms
351 * buffer and 20ms period? How does these parameters at all correlate
352 * with the above comment?!? */
353 pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 20 /*ms*/);
354 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/);
355 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
356
357 return VINF_SUCCESS;
358}
359
360
361/**
362 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
363 */
364static DECLCALLBACK(int) drvAudioVrdeHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
365 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
366{
367 PDRVAUDIOVRDE pThis = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
368 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
369 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
370 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
371 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
372
373 /*
374 * Only create a stream if we have clients.
375 */
376 int rc;
377 NOREF(pThis);
378#if 0 /* later maybe */
379 if (pThis->cClients == 0)
380 {
381 LogFunc(("No clients, failing with VERR_AUDIO_STREAM_COULD_NOT_CREATE.\n"));
382 rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
383 }
384 else
385#endif
386 {
387 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
388 rc = vrdeCreateStreamIn(pStreamVRDE, pCfgAcq);
389 else
390 rc = vrdeCreateStreamOut(pCfgAcq);
391 PDMAudioStrmCfgCopy(&pStreamVRDE->Cfg, pCfgAcq);
392 }
393 return rc;
394}
395
396
397/**
398 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
399 */
400static DECLCALLBACK(int) drvAudioVrdeHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
401 bool fImmediate)
402{
403 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
404 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
405 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
406 RT_NOREF(fImmediate);
407
408 if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_OUT)
409 {
410 if (pDrv->pConsoleVRDPServer)
411 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
412
413 if (pStreamVRDE->In.pCircBuf)
414 {
415 RTCircBufDestroy(pStreamVRDE->In.pCircBuf);
416 pStreamVRDE->In.pCircBuf = NULL;
417 }
418 }
419
420 return VINF_SUCCESS;
421}
422
423
424/**
425 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
426 */
427static DECLCALLBACK(int) drvAudioVrdeHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
428{
429 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
430 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
431
432 int rc;
433 if (!pDrv->pConsoleVRDPServer)
434 {
435 LogRelMax(32, ("Audio: VRDP console not ready (enable)\n"));
436 rc = VERR_AUDIO_STREAM_NOT_READY;
437 }
438 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
439 {
440 rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE,
441 PDMAudioPropsMilliToFrames(&pStreamVRDE->Cfg.Props, 200 /*ms*/),
442 PDMAudioPropsHz(&pStreamVRDE->Cfg.Props),
443 PDMAudioPropsChannels(&pStreamVRDE->Cfg.Props),
444 PDMAudioPropsSampleBits(&pStreamVRDE->Cfg.Props));
445 if (rc == VERR_NOT_SUPPORTED)
446 {
447 LogRelMax(64, ("Audio: No VRDE client connected, so no input recording available\n"));
448 rc = VERR_AUDIO_STREAM_NOT_READY;
449 }
450 }
451 else
452 rc = VINF_SUCCESS;
453 LogFlowFunc(("returns %Rrc\n", rc));
454 return rc;
455}
456
457
458/**
459 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
460 */
461static DECLCALLBACK(int) drvAudioVrdeHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
462{
463 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
464 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
465
466 int rc;
467 if (!pDrv->pConsoleVRDPServer)
468 {
469 LogRelMax(32, ("Audio: VRDP console not ready (disable)\n"));
470 rc = VERR_AUDIO_STREAM_NOT_READY;
471 }
472 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
473 {
474 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL /* pvUserCtx */);
475 rc = VINF_SUCCESS;
476 }
477 else
478 rc = VINF_SUCCESS;
479 LogFlowFunc(("returns %Rrc\n", rc));
480 return rc;
481}
482
483
484/**
485 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
486 */
487static DECLCALLBACK(int) drvAudioVrdeHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
488{
489 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
490 RT_NOREF(pStream);
491
492 if (!pDrv->pConsoleVRDPServer)
493 {
494 LogRelMax(32, ("Audio: VRDP console not ready (pause)\n"));
495 return VERR_AUDIO_STREAM_NOT_READY;
496 }
497 LogFlowFunc(("returns VINF_SUCCESS\n"));
498 return VINF_SUCCESS;
499}
500
501
502/**
503 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
504 */
505static DECLCALLBACK(int) drvAudioVrdeHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
506{
507 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
508 RT_NOREF(pStream);
509
510 if (!pDrv->pConsoleVRDPServer)
511 {
512 LogRelMax(32, ("Audio: VRDP console not ready (resume)\n"));
513 return VERR_AUDIO_STREAM_NOT_READY;
514 }
515 LogFlowFunc(("returns VINF_SUCCESS\n"));
516 return VINF_SUCCESS;
517}
518
519
520/**
521 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
522 */
523static DECLCALLBACK(int) drvAudioVrdeHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
524{
525 RT_NOREF(pInterface, pStream);
526 LogFlowFunc(("returns VINF_SUCCESS\n"));
527 return VINF_SUCCESS;
528}
529
530
531/**
532 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
533 */
534static DECLCALLBACK(int) drvAudioVrdeHA_StreamControl(PPDMIHOSTAUDIO pInterface,
535 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
536{
537 /** @todo r=bird: I'd like to get rid of this pfnStreamControl method,
538 * replacing it with individual StreamXxxx methods. That would save us
539 * potentally huge switches and more easily see which drivers implement
540 * which operations (grep for pfnStreamXxxx). */
541 switch (enmStreamCmd)
542 {
543 case PDMAUDIOSTREAMCMD_ENABLE:
544 return drvAudioVrdeHA_StreamEnable(pInterface, pStream);
545 case PDMAUDIOSTREAMCMD_DISABLE:
546 return drvAudioVrdeHA_StreamDisable(pInterface, pStream);
547 case PDMAUDIOSTREAMCMD_PAUSE:
548 return drvAudioVrdeHA_StreamPause(pInterface, pStream);
549 case PDMAUDIOSTREAMCMD_RESUME:
550 return drvAudioVrdeHA_StreamResume(pInterface, pStream);
551 case PDMAUDIOSTREAMCMD_DRAIN:
552 return drvAudioVrdeHA_StreamDrain(pInterface, pStream);
553
554 case PDMAUDIOSTREAMCMD_END:
555 case PDMAUDIOSTREAMCMD_32BIT_HACK:
556 case PDMAUDIOSTREAMCMD_INVALID:
557 /* no default*/
558 break;
559 }
560 return VERR_NOT_SUPPORTED;
561}
562
563
564/**
565 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
566 */
567static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
568{
569 RT_NOREF(pInterface);
570 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
571
572 if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
573 {
574 /* Return frames instead of bytes here
575 * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
576 return PDMAudioPropsBytesToFrames(&pStreamVRDE->Cfg.Props, (uint32_t)RTCircBufUsed(pStreamVRDE->In.pCircBuf));
577 }
578 return 0;
579}
580
581
582/**
583 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
584 */
585static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
586{
587 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
588 RT_NOREF(pStream);
589
590 /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */
591 if (pDrv->cClients)
592 return _16K * sizeof(PDMAUDIOFRAME);
593 return 0;
594}
595
596
597/**
598 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
599 */
600static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvAudioVrdeHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
601 PPDMAUDIOBACKENDSTREAM pStream)
602{
603 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
604 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
605
606 return pDrv->cClients > 0 ? PDMHOSTAUDIOSTREAMSTATE_OKAY : PDMHOSTAUDIOSTREAMSTATE_INACTIVE;
607}
608
609
610/**
611 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
612 */
613static DECLCALLBACK(int) drvAudioVrdeHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
614 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
615{
616 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
617 AssertPtr(pDrv);
618 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
619 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
620 if (cbBuf)
621 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
622 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
623
624 if (!pDrv->pConsoleVRDPServer)
625 return VERR_NOT_AVAILABLE;
626
627 /* Prepate the format. */
628 PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->Cfg.Props;
629 VRDEAUDIOFORMAT const uVrdpFormat = VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps),
630 PDMAudioPropsChannels(pProps),
631 PDMAudioPropsSampleBits(pProps),
632 pProps->fSigned);
633 Assert(uVrdpFormat == VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps), 2, 64, true));
634
635 /* We specified PDMAUDIOSTREAMLAYOUT_RAW (== S64), so
636 convert the buffer pointe and size accordingly: */
637 PCPDMAUDIOFRAME paSampleBuf = (PCPDMAUDIOFRAME)pvBuf;
638 uint32_t const cFramesToWrite = cbBuf / sizeof(paSampleBuf[0]);
639 Assert(cFramesToWrite * sizeof(paSampleBuf[0]) == cbBuf);
640
641 /** @todo r=bird: there was some incoherent mumbling about "using the
642 * internal counter to track if we (still) can write to the VRDP
643 * server or if need to wait another round (time slot)". However it
644 * wasn't accessing any internal counter nor doing anything else
645 * sensible, so I've removed it. */
646
647 /*
648 * Call the VRDP server with the data.
649 */
650 uint32_t cFramesWritten = 0;
651 while (cFramesWritten < cFramesToWrite)
652 {
653 uint32_t const cFramesChunk = cFramesToWrite - cFramesWritten; /** @todo For now write all at once. */
654
655 /* Note: The VRDP server expects int64_t samples per channel, regardless
656 of the actual sample bits (e.g 8 or 16 bits). */
657 pDrv->pConsoleVRDPServer->SendAudioSamples(&paSampleBuf[cFramesWritten], cFramesChunk /* Frames */, uVrdpFormat);
658
659 cFramesWritten += cFramesChunk;
660 }
661
662 Log3Func(("cFramesWritten=%RU32\n", cFramesWritten));
663 *pcbWritten = cFramesWritten * sizeof(PDMAUDIOFRAME);
664 return VINF_SUCCESS;
665}
666
667
668/**
669 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
670 */
671static DECLCALLBACK(int) drvAudioVrdeHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
672 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
673{
674 RT_NOREF(pInterface);
675 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
676 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
677 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
678 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
679 AssertPtrReturn(pcbRead, VERR_INVALID_PARAMETER);
680
681 size_t cbData = 0;
682 if (RTCircBufUsed(pStreamVRDE->In.pCircBuf))
683 {
684 void *pvData = NULL;
685 RTCircBufAcquireReadBlock(pStreamVRDE->In.pCircBuf, cbBuf, &pvData, &cbData);
686
687 if (cbData)
688 memcpy(pvBuf, pvData, cbData);
689
690 RTCircBufReleaseReadBlock(pStreamVRDE->In.pCircBuf, cbData);
691 }
692
693 *pcbRead = (uint32_t)cbData;
694 return VINF_SUCCESS;
695}
696
697
698/*********************************************************************************************************************************
699* PDMIBASE *
700*********************************************************************************************************************************/
701
702/**
703 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
704 */
705static DECLCALLBACK(void *) drvAudioVrdeQueryInterface(PPDMIBASE pInterface, const char *pszIID)
706{
707 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
708 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
709
710 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
711 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
712 return NULL;
713}
714
715
716/*********************************************************************************************************************************
717* PDMDRVREG *
718*********************************************************************************************************************************/
719
720/**
721 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
722 */
723/*static*/ DECLCALLBACK(void) AudioVRDE::drvPowerOff(PPDMDRVINS pDrvIns)
724{
725 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
726 LogFlowFuncEnter();
727
728 if (pThis->pConsoleVRDPServer)
729 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL);
730}
731
732
733/**
734 * @interface_method_impl{PDMDRVREG,pfnDestruct}
735 */
736/*static*/ DECLCALLBACK(void) AudioVRDE::drvDestruct(PPDMDRVINS pDrvIns)
737{
738 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
739 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
740 LogFlowFuncEnter();
741
742 /** @todo For runtime detach maybe:
743 if (pThis->pConsoleVRDPServer)
744 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL); */
745
746 /*
747 * If the AudioVRDE object is still alive, we must clear it's reference to
748 * us since we'll be invalid when we return from this method.
749 */
750 AudioVRDE *pAudioVRDE = pThis->pAudioVRDE;
751 if (pAudioVRDE)
752 {
753 RTCritSectEnter(&pAudioVRDE->mCritSect);
754 pAudioVRDE->mpDrv = NULL;
755 pThis->pAudioVRDE = NULL;
756 RTCritSectLeave(&pAudioVRDE->mCritSect);
757 }
758}
759
760
761/**
762 * Construct a VRDE audio driver instance.
763 *
764 * @copydoc FNPDMDRVCONSTRUCT
765 */
766/* static */
767DECLCALLBACK(int) AudioVRDE::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
768{
769 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
770 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
771 RT_NOREF(fFlags);
772
773 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
774 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
775
776 LogRel(("Audio: Initializing VRDE driver\n"));
777 LogFlowFunc(("fFlags=0x%x\n", fFlags));
778
779 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
780 ("Configuration error: Not possible to attach anything to this driver!\n"),
781 VERR_PDM_DRVINS_NO_ATTACH);
782
783 /*
784 * Init the static parts.
785 */
786 pThis->pDrvIns = pDrvIns;
787 /* IBase */
788 pDrvIns->IBase.pfnQueryInterface = drvAudioVrdeQueryInterface;
789 /* IHostAudio */
790 pThis->IHostAudio.pfnGetConfig = drvAudioVrdeHA_GetConfig;
791 pThis->IHostAudio.pfnGetDevices = NULL;
792 pThis->IHostAudio.pfnSetDevice = NULL;
793 pThis->IHostAudio.pfnGetStatus = drvAudioVrdeHA_GetStatus;
794 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
795 pThis->IHostAudio.pfnStreamConfigHint = NULL;
796 pThis->IHostAudio.pfnStreamCreate = drvAudioVrdeHA_StreamCreate;
797 pThis->IHostAudio.pfnStreamInitAsync = NULL;
798 pThis->IHostAudio.pfnStreamDestroy = drvAudioVrdeHA_StreamDestroy;
799 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
800 pThis->IHostAudio.pfnStreamControl = drvAudioVrdeHA_StreamControl;
801 pThis->IHostAudio.pfnStreamGetReadable = drvAudioVrdeHA_StreamGetReadable;
802 pThis->IHostAudio.pfnStreamGetWritable = drvAudioVrdeHA_StreamGetWritable;
803 pThis->IHostAudio.pfnStreamGetPending = NULL;
804 pThis->IHostAudio.pfnStreamGetState = drvAudioVrdeHA_StreamGetState;
805 pThis->IHostAudio.pfnStreamPlay = drvAudioVrdeHA_StreamPlay;
806 pThis->IHostAudio.pfnStreamCapture = drvAudioVrdeHA_StreamCapture;
807
808 /*
809 * Resolve the interface to the driver above us.
810 */
811 pThis->pIHostAudioPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHOSTAUDIOPORT);
812 AssertPtrReturn(pThis->pIHostAudioPort, VERR_PDM_MISSING_INTERFACE_ABOVE);
813
814 /*
815 * Get the ConsoleVRDPServer object pointer.
816 */
817 void *pvUser;
818 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
819 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc), rc);
820
821 /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE. */
822 pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
823 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pConsoleVRDPServer) || !pThis->pConsoleVRDPServer,
824 ("pConsoleVRDPServer=%p\n", pThis->pConsoleVRDPServer), VERR_INVALID_POINTER);
825 pThis->cClients = 0;
826
827 /*
828 * Get the AudioVRDE object pointer.
829 */
830 pvUser = NULL;
831 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
832 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
833
834 pThis->pAudioVRDE = (AudioVRDE *)pvUser;
835 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pAudioVRDE), ("pAudioVRDE=%p\n", pThis->pAudioVRDE), VERR_INVALID_POINTER);
836 RTCritSectEnter(&pThis->pAudioVRDE->mCritSect);
837 pThis->pAudioVRDE->mpDrv = pThis;
838 RTCritSectLeave(&pThis->pAudioVRDE->mCritSect);
839
840 return VINF_SUCCESS;
841}
842
843
844/**
845 * VRDE audio driver registration record.
846 */
847const PDMDRVREG AudioVRDE::DrvReg =
848{
849 PDM_DRVREG_VERSION,
850 /* szName */
851 "AudioVRDE",
852 /* szRCMod */
853 "",
854 /* szR0Mod */
855 "",
856 /* pszDescription */
857 "Audio driver for VRDE backend",
858 /* fFlags */
859 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
860 /* fClass. */
861 PDM_DRVREG_CLASS_AUDIO,
862 /* cMaxInstances */
863 ~0U,
864 /* cbInstance */
865 sizeof(DRVAUDIOVRDE),
866 /* pfnConstruct */
867 AudioVRDE::drvConstruct,
868 /* pfnDestruct */
869 AudioVRDE::drvDestruct,
870 /* pfnRelocate */
871 NULL,
872 /* pfnIOCtl */
873 NULL,
874 /* pfnPowerOn */
875 NULL,
876 /* pfnReset */
877 NULL,
878 /* pfnSuspend */
879 NULL,
880 /* pfnResume */
881 NULL,
882 /* pfnAttach */
883 NULL,
884 /* pfnDetach */
885 NULL,
886 /* pfnPowerOff */
887 AudioVRDE::drvPowerOff,
888 /* pfnSoftReset */
889 NULL,
890 /* u32EndVersion */
891 PDM_DRVREG_VERSION
892};
893
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