VirtualBox

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

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

Audio: Worked over draining, starting with the internal DMA buffer (instead of just the pre-buffer and backend buffer) and using the async I/O thread to keep calling PDMIAUDIOCONNECTOR::pfnStreamIterate and PDMIHOSTAUDIO::pfnStreamPlay (NULL buffer) every so often till the draining is done. Also put a rough deadline on the draining. The PDMAUDIOSTREAMCMD_DISABLE is now defined to stop playback/capturing immediately, even when already draining (if possible). This gets rid of the timers in DrvAudio and windows backends. DrvAudio no longer issue an DISABLE command at the end of the drain, it assumes the backend does that internally itself. After issuing PDMAUDIOSTREAMCMD_DRAIN the client (be it mixer or drvaudio) will not provide any more data for the buffers via pfnStreamPlay. Only tested windows, needs to re-test all platforms. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.8 KB
Line 
1/* $Id: DrvAudioVRDE.cpp 88991 2021-05-12 00:46:35Z 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{
402 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
403 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
404 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
405
406 if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_OUT)
407 {
408 if (pDrv->pConsoleVRDPServer)
409 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
410
411 if (pStreamVRDE->In.pCircBuf)
412 {
413 RTCircBufDestroy(pStreamVRDE->In.pCircBuf);
414 pStreamVRDE->In.pCircBuf = NULL;
415 }
416 }
417
418 return VINF_SUCCESS;
419}
420
421
422/**
423 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
424 */
425static DECLCALLBACK(int) drvAudioVrdeHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
426{
427 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
428 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
429
430 int rc;
431 if (!pDrv->pConsoleVRDPServer)
432 {
433 LogRelMax(32, ("Audio: VRDP console not ready (enable)\n"));
434 rc = VERR_AUDIO_STREAM_NOT_READY;
435 }
436 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
437 {
438 rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE,
439 PDMAudioPropsMilliToFrames(&pStreamVRDE->Cfg.Props, 200 /*ms*/),
440 PDMAudioPropsHz(&pStreamVRDE->Cfg.Props),
441 PDMAudioPropsChannels(&pStreamVRDE->Cfg.Props),
442 PDMAudioPropsSampleBits(&pStreamVRDE->Cfg.Props));
443 if (rc == VERR_NOT_SUPPORTED)
444 {
445 LogRelMax(64, ("Audio: No VRDE client connected, so no input recording available\n"));
446 rc = VERR_AUDIO_STREAM_NOT_READY;
447 }
448 }
449 else
450 rc = VINF_SUCCESS;
451 LogFlowFunc(("returns %Rrc\n", rc));
452 return rc;
453}
454
455
456/**
457 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
458 */
459static DECLCALLBACK(int) drvAudioVrdeHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
460{
461 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
462 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
463
464 int rc;
465 if (!pDrv->pConsoleVRDPServer)
466 {
467 LogRelMax(32, ("Audio: VRDP console not ready (disable)\n"));
468 rc = VERR_AUDIO_STREAM_NOT_READY;
469 }
470 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
471 {
472 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL /* pvUserCtx */);
473 rc = VINF_SUCCESS;
474 }
475 else
476 rc = VINF_SUCCESS;
477 LogFlowFunc(("returns %Rrc\n", rc));
478 return rc;
479}
480
481
482/**
483 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
484 */
485static DECLCALLBACK(int) drvAudioVrdeHA_StreamPause(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 (pause)\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,pfnStreamResume}
502 */
503static DECLCALLBACK(int) drvAudioVrdeHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
504{
505 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
506 RT_NOREF(pStream);
507
508 if (!pDrv->pConsoleVRDPServer)
509 {
510 LogRelMax(32, ("Audio: VRDP console not ready (resume)\n"));
511 return VERR_AUDIO_STREAM_NOT_READY;
512 }
513 LogFlowFunc(("returns VINF_SUCCESS\n"));
514 return VINF_SUCCESS;
515}
516
517
518/**
519 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
520 */
521static DECLCALLBACK(int) drvAudioVrdeHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
522{
523 RT_NOREF(pInterface, pStream);
524 LogFlowFunc(("returns VINF_SUCCESS\n"));
525 return VINF_SUCCESS;
526}
527
528
529/**
530 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
531 */
532static DECLCALLBACK(int) drvAudioVrdeHA_StreamControl(PPDMIHOSTAUDIO pInterface,
533 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
534{
535 /** @todo r=bird: I'd like to get rid of this pfnStreamControl method,
536 * replacing it with individual StreamXxxx methods. That would save us
537 * potentally huge switches and more easily see which drivers implement
538 * which operations (grep for pfnStreamXxxx). */
539 switch (enmStreamCmd)
540 {
541 case PDMAUDIOSTREAMCMD_ENABLE:
542 return drvAudioVrdeHA_StreamEnable(pInterface, pStream);
543 case PDMAUDIOSTREAMCMD_DISABLE:
544 return drvAudioVrdeHA_StreamDisable(pInterface, pStream);
545 case PDMAUDIOSTREAMCMD_PAUSE:
546 return drvAudioVrdeHA_StreamPause(pInterface, pStream);
547 case PDMAUDIOSTREAMCMD_RESUME:
548 return drvAudioVrdeHA_StreamResume(pInterface, pStream);
549 case PDMAUDIOSTREAMCMD_DRAIN:
550 return drvAudioVrdeHA_StreamDrain(pInterface, pStream);
551
552 case PDMAUDIOSTREAMCMD_END:
553 case PDMAUDIOSTREAMCMD_32BIT_HACK:
554 case PDMAUDIOSTREAMCMD_INVALID:
555 /* no default*/
556 break;
557 }
558 return VERR_NOT_SUPPORTED;
559}
560
561
562/**
563 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
564 */
565static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
566{
567 RT_NOREF(pInterface);
568 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
569
570 if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
571 {
572 /* Return frames instead of bytes here
573 * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
574 return PDMAudioPropsBytesToFrames(&pStreamVRDE->Cfg.Props, (uint32_t)RTCircBufUsed(pStreamVRDE->In.pCircBuf));
575 }
576 return 0;
577}
578
579
580/**
581 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
582 */
583static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
584{
585 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
586 RT_NOREF(pStream);
587
588 /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */
589 if (pDrv->cClients)
590 return _16K * sizeof(PDMAUDIOFRAME);
591 return 0;
592}
593
594
595/**
596 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
597 */
598static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvAudioVrdeHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
599 PPDMAUDIOBACKENDSTREAM pStream)
600{
601 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
602 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
603
604 return pDrv->cClients > 0 ? PDMHOSTAUDIOSTREAMSTATE_OKAY : PDMHOSTAUDIOSTREAMSTATE_INACTIVE;
605}
606
607
608/**
609 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
610 */
611static DECLCALLBACK(int) drvAudioVrdeHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
612 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
613{
614 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
615 AssertPtr(pDrv);
616 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
617 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
618 if (cbBuf)
619 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
620 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
621
622 if (!pDrv->pConsoleVRDPServer)
623 return VERR_NOT_AVAILABLE;
624
625 /* Prepate the format. */
626 PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->Cfg.Props;
627 VRDEAUDIOFORMAT const uVrdpFormat = VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps),
628 PDMAudioPropsChannels(pProps),
629 PDMAudioPropsSampleBits(pProps),
630 pProps->fSigned);
631 Assert(uVrdpFormat == VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps), 2, 64, true));
632
633 /* We specified PDMAUDIOSTREAMLAYOUT_RAW (== S64), so
634 convert the buffer pointe and size accordingly: */
635 PCPDMAUDIOFRAME paSampleBuf = (PCPDMAUDIOFRAME)pvBuf;
636 uint32_t const cFramesToWrite = cbBuf / sizeof(paSampleBuf[0]);
637 Assert(cFramesToWrite * sizeof(paSampleBuf[0]) == cbBuf);
638
639 /** @todo r=bird: there was some incoherent mumbling about "using the
640 * internal counter to track if we (still) can write to the VRDP
641 * server or if need to wait another round (time slot)". However it
642 * wasn't accessing any internal counter nor doing anything else
643 * sensible, so I've removed it. */
644
645 /*
646 * Call the VRDP server with the data.
647 */
648 uint32_t cFramesWritten = 0;
649 while (cFramesWritten < cFramesToWrite)
650 {
651 uint32_t const cFramesChunk = cFramesToWrite - cFramesWritten; /** @todo For now write all at once. */
652
653 /* Note: The VRDP server expects int64_t samples per channel, regardless
654 of the actual sample bits (e.g 8 or 16 bits). */
655 pDrv->pConsoleVRDPServer->SendAudioSamples(&paSampleBuf[cFramesWritten], cFramesChunk /* Frames */, uVrdpFormat);
656
657 cFramesWritten += cFramesChunk;
658 }
659
660 Log3Func(("cFramesWritten=%RU32\n", cFramesWritten));
661 *pcbWritten = cFramesWritten * sizeof(PDMAUDIOFRAME);
662 return VINF_SUCCESS;
663}
664
665
666/**
667 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
668 */
669static DECLCALLBACK(int) drvAudioVrdeHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
670 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
671{
672 RT_NOREF(pInterface);
673 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
674 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
675 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
676 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
677 AssertPtrReturn(pcbRead, VERR_INVALID_PARAMETER);
678
679 size_t cbData = 0;
680 if (RTCircBufUsed(pStreamVRDE->In.pCircBuf))
681 {
682 void *pvData = NULL;
683 RTCircBufAcquireReadBlock(pStreamVRDE->In.pCircBuf, cbBuf, &pvData, &cbData);
684
685 if (cbData)
686 memcpy(pvBuf, pvData, cbData);
687
688 RTCircBufReleaseReadBlock(pStreamVRDE->In.pCircBuf, cbData);
689 }
690
691 *pcbRead = (uint32_t)cbData;
692 return VINF_SUCCESS;
693}
694
695
696/*********************************************************************************************************************************
697* PDMIBASE *
698*********************************************************************************************************************************/
699
700/**
701 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
702 */
703static DECLCALLBACK(void *) drvAudioVrdeQueryInterface(PPDMIBASE pInterface, const char *pszIID)
704{
705 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
706 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
707
708 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
709 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
710 return NULL;
711}
712
713
714/*********************************************************************************************************************************
715* PDMDRVREG *
716*********************************************************************************************************************************/
717
718/**
719 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
720 */
721/*static*/ DECLCALLBACK(void) AudioVRDE::drvPowerOff(PPDMDRVINS pDrvIns)
722{
723 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
724 LogFlowFuncEnter();
725
726 if (pThis->pConsoleVRDPServer)
727 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL);
728}
729
730
731/**
732 * @interface_method_impl{PDMDRVREG,pfnDestruct}
733 */
734/*static*/ DECLCALLBACK(void) AudioVRDE::drvDestruct(PPDMDRVINS pDrvIns)
735{
736 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
737 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
738 LogFlowFuncEnter();
739
740 /** @todo For runtime detach maybe:
741 if (pThis->pConsoleVRDPServer)
742 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL); */
743
744 /*
745 * If the AudioVRDE object is still alive, we must clear it's reference to
746 * us since we'll be invalid when we return from this method.
747 */
748 AudioVRDE *pAudioVRDE = pThis->pAudioVRDE;
749 if (pAudioVRDE)
750 {
751 RTCritSectEnter(&pAudioVRDE->mCritSect);
752 pAudioVRDE->mpDrv = NULL;
753 pThis->pAudioVRDE = NULL;
754 RTCritSectLeave(&pAudioVRDE->mCritSect);
755 }
756}
757
758
759/**
760 * Construct a VRDE audio driver instance.
761 *
762 * @copydoc FNPDMDRVCONSTRUCT
763 */
764/* static */
765DECLCALLBACK(int) AudioVRDE::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
766{
767 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
768 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
769 RT_NOREF(fFlags);
770
771 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
772 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
773
774 LogRel(("Audio: Initializing VRDE driver\n"));
775 LogFlowFunc(("fFlags=0x%x\n", fFlags));
776
777 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
778 ("Configuration error: Not possible to attach anything to this driver!\n"),
779 VERR_PDM_DRVINS_NO_ATTACH);
780
781 /*
782 * Init the static parts.
783 */
784 pThis->pDrvIns = pDrvIns;
785 /* IBase */
786 pDrvIns->IBase.pfnQueryInterface = drvAudioVrdeQueryInterface;
787 /* IHostAudio */
788 pThis->IHostAudio.pfnGetConfig = drvAudioVrdeHA_GetConfig;
789 pThis->IHostAudio.pfnGetDevices = NULL;
790 pThis->IHostAudio.pfnGetStatus = drvAudioVrdeHA_GetStatus;
791 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
792 pThis->IHostAudio.pfnStreamConfigHint = NULL;
793 pThis->IHostAudio.pfnStreamCreate = drvAudioVrdeHA_StreamCreate;
794 pThis->IHostAudio.pfnStreamInitAsync = NULL;
795 pThis->IHostAudio.pfnStreamDestroy = drvAudioVrdeHA_StreamDestroy;
796 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
797 pThis->IHostAudio.pfnStreamControl = drvAudioVrdeHA_StreamControl;
798 pThis->IHostAudio.pfnStreamGetReadable = drvAudioVrdeHA_StreamGetReadable;
799 pThis->IHostAudio.pfnStreamGetWritable = drvAudioVrdeHA_StreamGetWritable;
800 pThis->IHostAudio.pfnStreamGetPending = NULL;
801 pThis->IHostAudio.pfnStreamGetState = drvAudioVrdeHA_StreamGetState;
802 pThis->IHostAudio.pfnStreamPlay = drvAudioVrdeHA_StreamPlay;
803 pThis->IHostAudio.pfnStreamCapture = drvAudioVrdeHA_StreamCapture;
804
805 /*
806 * Resolve the interface to the driver above us.
807 */
808 pThis->pIHostAudioPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIHOSTAUDIOPORT);
809 AssertPtrReturn(pThis->pIHostAudioPort, VERR_PDM_MISSING_INTERFACE_ABOVE);
810
811 /*
812 * Get the ConsoleVRDPServer object pointer.
813 */
814 void *pvUser;
815 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
816 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc), rc);
817
818 /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE. */
819 pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
820 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pConsoleVRDPServer) || !pThis->pConsoleVRDPServer,
821 ("pConsoleVRDPServer=%p\n", pThis->pConsoleVRDPServer), VERR_INVALID_POINTER);
822 pThis->cClients = 0;
823
824 /*
825 * Get the AudioVRDE object pointer.
826 */
827 pvUser = NULL;
828 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
829 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
830
831 pThis->pAudioVRDE = (AudioVRDE *)pvUser;
832 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pAudioVRDE), ("pAudioVRDE=%p\n", pThis->pAudioVRDE), VERR_INVALID_POINTER);
833 RTCritSectEnter(&pThis->pAudioVRDE->mCritSect);
834 pThis->pAudioVRDE->mpDrv = pThis;
835 RTCritSectLeave(&pThis->pAudioVRDE->mCritSect);
836
837 return VINF_SUCCESS;
838}
839
840
841/**
842 * VRDE audio driver registration record.
843 */
844const PDMDRVREG AudioVRDE::DrvReg =
845{
846 PDM_DRVREG_VERSION,
847 /* szName */
848 "AudioVRDE",
849 /* szRCMod */
850 "",
851 /* szR0Mod */
852 "",
853 /* pszDescription */
854 "Audio driver for VRDE backend",
855 /* fFlags */
856 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
857 /* fClass. */
858 PDM_DRVREG_CLASS_AUDIO,
859 /* cMaxInstances */
860 ~0U,
861 /* cbInstance */
862 sizeof(DRVAUDIOVRDE),
863 /* pfnConstruct */
864 AudioVRDE::drvConstruct,
865 /* pfnDestruct */
866 AudioVRDE::drvDestruct,
867 /* pfnRelocate */
868 NULL,
869 /* pfnIOCtl */
870 NULL,
871 /* pfnPowerOn */
872 NULL,
873 /* pfnReset */
874 NULL,
875 /* pfnSuspend */
876 NULL,
877 /* pfnResume */
878 NULL,
879 /* pfnAttach */
880 NULL,
881 /* pfnDetach */
882 NULL,
883 /* pfnPowerOff */
884 AudioVRDE::drvPowerOff,
885 /* pfnSoftReset */
886 NULL,
887 /* u32EndVersion */
888 PDM_DRVREG_VERSION
889};
890
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