VirtualBox

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

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

DrvAudioVRDE: Cleanup. [build fix] bugref:9890

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

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