VirtualBox

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

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

Audio: Added geberuc asynchronous init to DrvAudio for use in WAS (and maybe others). bugref:9890

  • Added optional asynchronous init via a worker thread pool in DrvAudio (pfnStreamInitAsync).
  • Added interface for the backend to use the thread pool from the backend (pfnDoOnWorkerThread).
  • s/PDMIAUDIONOTIFYFROMHOST/PDMIHOSTAUDIOPORT/g
  • New BACKEND_READY state flag (a bit confusing wrt to INITIALIZED, but whatever).
  • Don't RESUME streams which aren't actually paused (on VM resume).
  • Restore the backend state correctly when the per-direction enable flag is changed in DrvAudio. Would enable the streams regardless of actual state.
  • Move more PDMAUDIOSTREAM members from the public structure and into the DRVAUDIOSTREAM.
  • ++
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.3 KB
Line 
1/* $Id: DrvAudioVRDE.cpp 88819 2021-05-03 10:26:28Z 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 /** Pointer to the DrvAudio port interface that is above us. */
78 PPDMIAUDIOCONNECTOR pDrvAudio;
79 /** Number of connected clients to this VRDE instance. */
80 uint32_t cClients;
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}
101
102
103AudioVRDE::~AudioVRDE(void)
104{
105 if (mpDrv)
106 {
107 mpDrv->pAudioVRDE = NULL;
108 mpDrv = NULL;
109 }
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 LogRel2(("Audio: VRDE client connected\n"));
132 if (mpDrv)
133 mpDrv->cClients++;
134}
135
136
137void AudioVRDE::onVRDEClientDisconnect(uint32_t uClientID)
138{
139 RT_NOREF(uClientID);
140
141 LogRel2(("Audio: VRDE client disconnected\n"));
142 Assert(mpDrv->cClients);
143 if (mpDrv)
144 mpDrv->cClients--;
145}
146
147
148int AudioVRDE::onVRDEControl(bool fEnable, uint32_t uFlags)
149{
150 RT_NOREF(fEnable, uFlags);
151 LogFlowThisFunc(("fEnable=%RTbool, uFlags=0x%x\n", fEnable, uFlags));
152
153 if (mpDrv == NULL)
154 return VERR_INVALID_STATE;
155
156 return VINF_SUCCESS; /* Never veto. */
157}
158
159
160/**
161 * Marks the beginning of sending captured audio data from a connected
162 * RDP client.
163 *
164 * @returns VBox status code.
165 * @param pvContext The context; in this case a pointer to a
166 * VRDESTREAMIN structure.
167 * @param pVRDEAudioBegin Pointer to a VRDEAUDIOINBEGIN structure.
168 */
169int AudioVRDE::onVRDEInputBegin(void *pvContext, PVRDEAUDIOINBEGIN pVRDEAudioBegin)
170{
171 AssertPtrReturn(pvContext, VERR_INVALID_POINTER);
172 AssertPtrReturn(pVRDEAudioBegin, VERR_INVALID_POINTER);
173 PVRDESTREAM pVRDEStrmIn = (PVRDESTREAM)pvContext;
174 AssertPtrReturn(pVRDEStrmIn, VERR_INVALID_POINTER);
175
176#ifdef LOG_ENABLED
177 VRDEAUDIOFORMAT const audioFmt = pVRDEAudioBegin->fmt;
178 LogFlowFunc(("cbSample=%RU32, iSampleHz=%d, cChannels=%d, cBits=%d, fUnsigned=%RTbool\n",
179 VRDE_AUDIO_FMT_BYTES_PER_SAMPLE(audioFmt), VRDE_AUDIO_FMT_SAMPLE_FREQ(audioFmt),
180 VRDE_AUDIO_FMT_CHANNELS(audioFmt), VRDE_AUDIO_FMT_BITS_PER_SAMPLE(audioFmt), VRDE_AUDIO_FMT_SIGNED(audioFmt)));
181#endif
182
183 return VINF_SUCCESS;
184}
185
186
187int AudioVRDE::onVRDEInputData(void *pvContext, const void *pvData, uint32_t cbData)
188{
189 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pvContext;
190 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
191
192 void *pvBuf = NULL;
193 size_t cbBuf = 0;
194 RTCircBufAcquireWriteBlock(pStreamVRDE->In.pCircBuf, cbData, &pvBuf, &cbBuf);
195
196 if (cbBuf)
197 memcpy(pvBuf, pvData, cbBuf);
198
199 RTCircBufReleaseWriteBlock(pStreamVRDE->In.pCircBuf, cbBuf);
200
201 if (cbBuf < cbData)
202 LogRelMax(999, ("VRDE: Capturing audio data lost %zu bytes\n", cbData - cbBuf)); /** @todo Use an error counter. */
203
204 return VINF_SUCCESS; /** @todo r=andy How to tell the caller if we were not able to handle *all* input data? */
205}
206
207
208int AudioVRDE::onVRDEInputEnd(void *pvContext)
209{
210 RT_NOREF(pvContext);
211 return VINF_SUCCESS;
212}
213
214
215int AudioVRDE::onVRDEInputIntercept(bool fEnabled)
216{
217 RT_NOREF(fEnabled);
218 return VINF_SUCCESS; /* Never veto. */
219}
220
221
222
223/*********************************************************************************************************************************
224* PDMIHOSTAUDIO *
225*********************************************************************************************************************************/
226
227/**
228 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
229 */
230static DECLCALLBACK(int) drvAudioVrdeHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
231{
232 RT_NOREF(pInterface);
233 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
234
235 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "VRDE");
236 pBackendCfg->cbStream = sizeof(VRDESTREAM);
237 pBackendCfg->fFlags = 0;
238 pBackendCfg->cMaxStreamsIn = UINT32_MAX;
239 pBackendCfg->cMaxStreamsOut = UINT32_MAX;
240
241 return VINF_SUCCESS;
242}
243
244
245/**
246 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
247 */
248static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvAudioVrdeHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
249{
250 RT_NOREF(pInterface, enmDir);
251 return PDMAUDIOBACKENDSTS_RUNNING;
252}
253
254
255static int vrdeCreateStreamIn(PVRDESTREAM pStreamVRDE, PPDMAUDIOSTREAMCFG pCfgAcq)
256{
257 /*
258 * The VRDP server does its own mixing and resampling as it may server
259 * multiple clients all with different sound formats. So, it feeds us
260 * raw mixer frames (somewhat akind to stereo signed 64-bit, see
261 * st_sample_t and PDMAUDIOFRAME).
262 */
263 pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW;
264 PDMAudioPropsInitEx(&pCfgAcq->Props, 8 /*64-bit*/, true /*fSigned*/, 2 /*stereo*/, 22050 /*Hz*/,
265 true /*fLittleEndian*/, true /*fRaw*/);
266
267 /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
268 const uint32_t cFramesVrdpServer = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 200 /*ms*/);
269
270 int rc = RTCircBufCreate(&pStreamVRDE->In.pCircBuf, PDMAudioPropsFramesToBytes(&pCfgAcq->Props, cFramesVrdpServer));
271 if (RT_SUCCESS(rc))
272 {
273 pCfgAcq->Backend.cFramesPeriod = cFramesVrdpServer;
274/** @todo r=bird: This is inconsistent with the above buffer allocation and I
275 * think also ALSA and Pulse backends way of setting cFramesBufferSize. */
276 pCfgAcq->Backend.cFramesBufferSize = cFramesVrdpServer * 2; /* Use "double buffering". */
277 pCfgAcq->Backend.cFramesPreBuffering = cFramesVrdpServer;
278 }
279
280 return rc;
281}
282
283
284static int vrdeCreateStreamOut(PPDMAUDIOSTREAMCFG pCfgAcq)
285{
286 /*
287 * The VRDP server does its own mixing and resampling because it may be
288 * sending the audio to any number of different clients all with different
289 * formats (including clients which hasn't yet connected). So, it desires
290 * the raw data from the mixer (somewhat akind to stereo signed 64-bit,
291 * see st_sample_t and PDMAUDIOFRAME).
292 */
293 pCfgAcq->enmLayout = PDMAUDIOSTREAMLAYOUT_RAW;
294 PDMAudioPropsInitEx(&pCfgAcq->Props, 8 /*64-bit*/, true /*fSigned*/, 2 /*stereo*/, 22050 /*Hz*/,
295 true /*fLittleEndian*/, true /*fRaw*/);
296
297 /* According to the VRDP docs, the VRDP server stores audio in 200ms chunks. */
298 /** @todo r=bird: So, if VRDP does 200ms chunks, why do we report 100ms
299 * buffer and 20ms period? How does these parameters at all correlate
300 * with the above comment?!? */
301 pCfgAcq->Backend.cFramesPeriod = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 20 /*ms*/);
302 pCfgAcq->Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(&pCfgAcq->Props, 100 /*ms*/);
303 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod * 2;
304
305 return VINF_SUCCESS;
306}
307
308
309/**
310 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
311 */
312static DECLCALLBACK(int) drvAudioVrdeHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
313 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
314{
315 RT_NOREF(pInterface);
316 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
317 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
318 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
319 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
320
321 int rc;
322 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
323 rc = vrdeCreateStreamIn(pStreamVRDE, pCfgAcq);
324 else
325 rc = vrdeCreateStreamOut(pCfgAcq);
326 PDMAudioStrmCfgCopy(&pStreamVRDE->Cfg, pCfgAcq);
327 return rc;
328}
329
330
331/**
332 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
333 */
334static DECLCALLBACK(int) drvAudioVrdeHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
335{
336 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
337 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
338 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
339
340 if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_OUT)
341 {
342 if (pDrv->pConsoleVRDPServer)
343 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL);
344
345 if (pStreamVRDE->In.pCircBuf)
346 {
347 RTCircBufDestroy(pStreamVRDE->In.pCircBuf);
348 pStreamVRDE->In.pCircBuf = NULL;
349 }
350 }
351 pDrv->pConsoleVRDPServer = NULL;
352
353 return VINF_SUCCESS;
354}
355
356
357/**
358 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
359 */
360static DECLCALLBACK(int) drvAudioVrdeHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
361{
362 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
363 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
364
365 int rc;
366 if (!pDrv->pConsoleVRDPServer)
367 {
368 LogRelMax(32, ("Audio: VRDP console not ready (enable)\n"));
369 rc = VERR_AUDIO_STREAM_NOT_READY;
370 }
371 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
372 {
373 rc = pDrv->pConsoleVRDPServer->SendAudioInputBegin(NULL, pStreamVRDE,
374 PDMAudioPropsMilliToFrames(&pStreamVRDE->Cfg.Props, 200 /*ms*/),
375 PDMAudioPropsHz(&pStreamVRDE->Cfg.Props),
376 PDMAudioPropsChannels(&pStreamVRDE->Cfg.Props),
377 PDMAudioPropsSampleBits(&pStreamVRDE->Cfg.Props));
378 if (rc == VERR_NOT_SUPPORTED)
379 {
380 LogRelMax(64, ("Audio: No VRDE client connected, so no input recording available\n"));
381 rc = VERR_AUDIO_STREAM_NOT_READY;
382 }
383 }
384 else
385 rc = VINF_SUCCESS;
386 LogFlowFunc(("returns %Rrc\n", rc));
387 return rc;
388}
389
390
391/**
392 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
393 */
394static DECLCALLBACK(int) drvAudioVrdeHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
395{
396 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
397 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
398
399 int rc;
400 if (!pDrv->pConsoleVRDPServer)
401 {
402 LogRelMax(32, ("Audio: VRDP console not ready (disable)\n"));
403 rc = VERR_AUDIO_STREAM_NOT_READY;
404 }
405 else if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
406 {
407 pDrv->pConsoleVRDPServer->SendAudioInputEnd(NULL /* pvUserCtx */);
408 rc = VINF_SUCCESS;
409 }
410 else
411 rc = VINF_SUCCESS;
412 LogFlowFunc(("returns %Rrc\n", rc));
413 return rc;
414}
415
416
417/**
418 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
419 */
420static DECLCALLBACK(int) drvAudioVrdeHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
421{
422 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
423 RT_NOREF(pStream);
424
425 if (!pDrv->pConsoleVRDPServer)
426 {
427 LogRelMax(32, ("Audio: VRDP console not ready (pause)\n"));
428 return VERR_AUDIO_STREAM_NOT_READY;
429 }
430 LogFlowFunc(("returns VINF_SUCCESS\n"));
431 return VINF_SUCCESS;
432}
433
434
435/**
436 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
437 */
438static DECLCALLBACK(int) drvAudioVrdeHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
439{
440 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
441 RT_NOREF(pStream);
442
443 if (!pDrv->pConsoleVRDPServer)
444 {
445 LogRelMax(32, ("Audio: VRDP console not ready (resume)\n"));
446 return VERR_AUDIO_STREAM_NOT_READY;
447 }
448 LogFlowFunc(("returns VINF_SUCCESS\n"));
449 return VINF_SUCCESS;
450}
451
452
453/**
454 * @ interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
455 */
456static DECLCALLBACK(int) drvAudioVrdeHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
457{
458 RT_NOREF(pInterface, pStream);
459 LogFlowFunc(("returns VINF_SUCCESS\n"));
460 return VINF_SUCCESS;
461}
462
463
464/**
465 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
466 */
467static DECLCALLBACK(int) drvAudioVrdeHA_StreamControl(PPDMIHOSTAUDIO pInterface,
468 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
469{
470 /** @todo r=bird: I'd like to get rid of this pfnStreamControl method,
471 * replacing it with individual StreamXxxx methods. That would save us
472 * potentally huge switches and more easily see which drivers implement
473 * which operations (grep for pfnStreamXxxx). */
474 switch (enmStreamCmd)
475 {
476 case PDMAUDIOSTREAMCMD_ENABLE:
477 return drvAudioVrdeHA_StreamEnable(pInterface, pStream);
478 case PDMAUDIOSTREAMCMD_DISABLE:
479 return drvAudioVrdeHA_StreamDisable(pInterface, pStream);
480 case PDMAUDIOSTREAMCMD_PAUSE:
481 return drvAudioVrdeHA_StreamPause(pInterface, pStream);
482 case PDMAUDIOSTREAMCMD_RESUME:
483 return drvAudioVrdeHA_StreamResume(pInterface, pStream);
484 case PDMAUDIOSTREAMCMD_DRAIN:
485 return drvAudioVrdeHA_StreamDrain(pInterface, pStream);
486
487 case PDMAUDIOSTREAMCMD_END:
488 case PDMAUDIOSTREAMCMD_32BIT_HACK:
489 case PDMAUDIOSTREAMCMD_INVALID:
490 /* no default*/
491 break;
492 }
493 return VERR_NOT_SUPPORTED;
494}
495
496
497/**
498 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
499 */
500static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
501{
502 RT_NOREF(pInterface);
503 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
504
505 if (pStreamVRDE->Cfg.enmDir == PDMAUDIODIR_IN)
506 {
507 /* Return frames instead of bytes here
508 * (since we specified PDMAUDIOSTREAMLAYOUT_RAW as the audio data layout). */
509 return PDMAudioPropsBytesToFrames(&pStreamVRDE->Cfg.Props, (uint32_t)RTCircBufUsed(pStreamVRDE->In.pCircBuf));
510 }
511 return 0;
512}
513
514
515/**
516 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
517 */
518static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
519{
520 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
521 RT_NOREF(pStream);
522
523 /** @todo Find some sane value here. We probably need a VRDE API VRDE to specify this. */
524 if (pDrv->cClients)
525 return _16K * sizeof(PDMAUDIOFRAME);
526 return 0;
527}
528
529
530/**
531 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
532 */
533static DECLCALLBACK(uint32_t) drvAudioVrdeHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
534{
535 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
536 RT_NOREF(pStream);
537
538 uint32_t fStrmStatus = PDMAUDIOSTREAM_STS_INITIALIZED;
539 if (pDrv->cClients) /* If any clients are connected, flag the stream as enabled. */
540 fStrmStatus |= PDMAUDIOSTREAM_STS_ENABLED;
541
542 return fStrmStatus;
543}
544
545
546/**
547 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
548 */
549static DECLCALLBACK(int) drvAudioVrdeHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
550 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
551{
552 PDRVAUDIOVRDE pDrv = RT_FROM_MEMBER(pInterface, DRVAUDIOVRDE, IHostAudio);
553 AssertPtr(pDrv);
554 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
555 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
556 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
557 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
558 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
559
560 if (!pDrv->pConsoleVRDPServer)
561 return VERR_NOT_AVAILABLE;
562
563 /* Prepate the format. */
564 PPDMAUDIOPCMPROPS pProps = &pStreamVRDE->Cfg.Props;
565 VRDEAUDIOFORMAT const uVrdpFormat = VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps),
566 PDMAudioPropsChannels(pProps),
567 PDMAudioPropsSampleBits(pProps),
568 pProps->fSigned);
569 Assert(uVrdpFormat == VRDE_AUDIO_FMT_MAKE(PDMAudioPropsHz(pProps), 2, 64, true));
570
571 /* We specified PDMAUDIOSTREAMLAYOUT_RAW (== S64), so
572 convert the buffer pointe and size accordingly: */
573 PCPDMAUDIOFRAME paSampleBuf = (PCPDMAUDIOFRAME)pvBuf;
574 uint32_t const cFramesToWrite = cbBuf / sizeof(paSampleBuf[0]);
575 Assert(cFramesToWrite * sizeof(paSampleBuf[0]) == cbBuf);
576
577 /** @todo r=bird: there was some incoherent mumbling about "using the
578 * internal counter to track if we (still) can write to the VRDP
579 * server or if need to wait anothe round (time slot)". However it
580 * wasn't accessing any internal counter nor doing anything else
581 * sensible, so I've removed it. */
582
583 /*
584 * Call the VRDP server with the data.
585 */
586 uint32_t cFramesWritten = 0;
587 while (cFramesWritten < cFramesToWrite)
588 {
589 uint32_t const cFramesChunk = cFramesToWrite - cFramesWritten; /** @todo For now write all at once. */
590
591 /* Note: The VRDP server expects int64_t samples per channel, regardless
592 of the actual sample bits (e.g 8 or 16 bits). */
593 pDrv->pConsoleVRDPServer->SendAudioSamples(&paSampleBuf[cFramesWritten], cFramesChunk /* Frames */, uVrdpFormat);
594
595 cFramesWritten += cFramesChunk;
596 }
597
598 Log3Func(("cFramesWritten=%RU32\n", cFramesWritten));
599 if (pcbWritten)
600 *pcbWritten = cFramesWritten * sizeof(PDMAUDIOFRAME);
601 return VINF_SUCCESS;
602}
603
604
605/**
606 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
607 */
608static DECLCALLBACK(int) drvAudioVrdeHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
609 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
610{
611 RT_NOREF(pInterface);
612 PVRDESTREAM pStreamVRDE = (PVRDESTREAM)pStream;
613 AssertPtrReturn(pStreamVRDE, VERR_INVALID_POINTER);
614 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
615 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
616 AssertPtrReturn(pcbRead, VERR_INVALID_PARAMETER);
617
618 size_t cbData = 0;
619 if (RTCircBufUsed(pStreamVRDE->In.pCircBuf))
620 {
621 void *pvData = NULL;
622 RTCircBufAcquireReadBlock(pStreamVRDE->In.pCircBuf, cbBuf, &pvData, &cbData);
623
624 if (cbData)
625 memcpy(pvBuf, pvData, cbData);
626
627 RTCircBufReleaseReadBlock(pStreamVRDE->In.pCircBuf, cbData);
628 }
629
630 *pcbRead = (uint32_t)cbData;
631 return VINF_SUCCESS;
632}
633
634
635/*********************************************************************************************************************************
636* PDMIBASE *
637*********************************************************************************************************************************/
638
639/**
640 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
641 */
642static DECLCALLBACK(void *) drvAudioVrdeQueryInterface(PPDMIBASE pInterface, const char *pszIID)
643{
644 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
645 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
646
647 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
648 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
649 return NULL;
650}
651
652
653/*********************************************************************************************************************************
654* PDMDRVREG *
655*********************************************************************************************************************************/
656
657/**
658 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
659 */
660/*static*/ DECLCALLBACK(void) AudioVRDE::drvPowerOff(PPDMDRVINS pDrvIns)
661{
662 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
663 LogFlowFuncEnter();
664
665 if (pThis->pConsoleVRDPServer)
666 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL);
667}
668
669
670/**
671 * @interface_method_impl{PDMDRVREG,pfnDestruct}
672 */
673/*static*/ DECLCALLBACK(void) AudioVRDE::drvDestruct(PPDMDRVINS pDrvIns)
674{
675 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
676 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
677 LogFlowFuncEnter();
678
679 /** @todo For runtime detach maybe:
680 if (pThis->pConsoleVRDPServer)
681 pThis->pConsoleVRDPServer->SendAudioInputEnd(NULL); */
682
683 /*
684 * If the AudioVRDE object is still alive, we must clear it's reference to
685 * us since we'll be invalid when we return from this method.
686 */
687 if (pThis->pAudioVRDE)
688 {
689 pThis->pAudioVRDE->mpDrv = NULL;
690 pThis->pAudioVRDE = NULL;
691 }
692}
693
694
695/**
696 * Construct a VRDE audio driver instance.
697 *
698 * @copydoc FNPDMDRVCONSTRUCT
699 */
700/* static */
701DECLCALLBACK(int) AudioVRDE::drvConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
702{
703 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
704 PDRVAUDIOVRDE pThis = PDMINS_2_DATA(pDrvIns, PDRVAUDIOVRDE);
705 RT_NOREF(fFlags);
706
707 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
708 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
709
710 LogRel(("Audio: Initializing VRDE driver\n"));
711 LogFlowFunc(("fFlags=0x%x\n", fFlags));
712
713 AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
714 ("Configuration error: Not possible to attach anything to this driver!\n"),
715 VERR_PDM_DRVINS_NO_ATTACH);
716
717 /*
718 * Init the static parts.
719 */
720 pThis->pDrvIns = pDrvIns;
721 /* IBase */
722 pDrvIns->IBase.pfnQueryInterface = drvAudioVrdeQueryInterface;
723 /* IHostAudio */
724 pThis->IHostAudio.pfnGetConfig = drvAudioVrdeHA_GetConfig;
725 pThis->IHostAudio.pfnGetDevices = 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.pfnStreamControl = drvAudioVrdeHA_StreamControl;
734 pThis->IHostAudio.pfnStreamGetReadable = drvAudioVrdeHA_StreamGetReadable;
735 pThis->IHostAudio.pfnStreamGetWritable = drvAudioVrdeHA_StreamGetWritable;
736 pThis->IHostAudio.pfnStreamGetPending = NULL;
737 pThis->IHostAudio.pfnStreamGetStatus = drvAudioVrdeHA_StreamGetStatus;
738 pThis->IHostAudio.pfnStreamPlay = drvAudioVrdeHA_StreamPlay;
739 pThis->IHostAudio.pfnStreamCapture = drvAudioVrdeHA_StreamCapture;
740
741 /*
742 * Get the ConsoleVRDPServer object pointer.
743 */
744 void *pvUser;
745 int rc = CFGMR3QueryPtr(pCfg, "ObjectVRDPServer", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
746 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"ObjectVRDPServer\" value, rc=%Rrc\n", rc), rc);
747
748 /* CFGM tree saves the pointer to ConsoleVRDPServer in the Object node of AudioVRDE. */
749 pThis->pConsoleVRDPServer = (ConsoleVRDPServer *)pvUser;
750 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pConsoleVRDPServer) || !pThis->pConsoleVRDPServer,
751 ("pConsoleVRDPServer=%p\n", pThis->pConsoleVRDPServer), VERR_INVALID_POINTER);
752 pThis->cClients = 0;
753
754 /*
755 * Get the AudioVRDE object pointer.
756 */
757 pvUser = NULL;
758 rc = CFGMR3QueryPtr(pCfg, "Object", &pvUser); /** @todo r=andy Get rid of this hack and use IHostAudio::SetCallback. */
759 AssertMsgRCReturn(rc, ("Confguration error: No/bad \"Object\" value, rc=%Rrc\n", rc), rc);
760
761 pThis->pAudioVRDE = (AudioVRDE *)pvUser;
762 AssertLogRelMsgReturn(RT_VALID_PTR(pThis->pAudioVRDE), ("pAudioVRDE=%p\n", pThis->pAudioVRDE), VERR_INVALID_POINTER);
763 pThis->pAudioVRDE->mpDrv = pThis;
764
765 /*
766 * Get the interface for the above driver (DrvAudio) to make mixer/conversion calls.
767 * Described in CFGM tree.
768 */
769 pThis->pDrvAudio = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMIAUDIOCONNECTOR);
770 AssertMsgReturn(pThis->pDrvAudio, ("Configuration error: No upper interface specified!\n"), VERR_PDM_MISSING_INTERFACE_ABOVE);
771
772 return VINF_SUCCESS;
773}
774
775
776/**
777 * VRDE audio driver registration record.
778 */
779const PDMDRVREG AudioVRDE::DrvReg =
780{
781 PDM_DRVREG_VERSION,
782 /* szName */
783 "AudioVRDE",
784 /* szRCMod */
785 "",
786 /* szR0Mod */
787 "",
788 /* pszDescription */
789 "Audio driver for VRDE backend",
790 /* fFlags */
791 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
792 /* fClass. */
793 PDM_DRVREG_CLASS_AUDIO,
794 /* cMaxInstances */
795 ~0U,
796 /* cbInstance */
797 sizeof(DRVAUDIOVRDE),
798 /* pfnConstruct */
799 AudioVRDE::drvConstruct,
800 /* pfnDestruct */
801 AudioVRDE::drvDestruct,
802 /* pfnRelocate */
803 NULL,
804 /* pfnIOCtl */
805 NULL,
806 /* pfnPowerOn */
807 NULL,
808 /* pfnReset */
809 NULL,
810 /* pfnSuspend */
811 NULL,
812 /* pfnResume */
813 NULL,
814 /* pfnAttach */
815 NULL,
816 /* pfnDetach */
817 NULL,
818 /* pfnPowerOff */
819 AudioVRDE::drvPowerOff,
820 /* pfnSoftReset */
821 NULL,
822 /* u32EndVersion */
823 PDM_DRVREG_VERSION
824};
825
Note: See TracBrowser for help on using the repository browser.

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