VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/audio/vkatDriverStack.cpp@ 89409

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

Audio/ValKit: Moved the VKAT driver stack code to an own file; main file got too big already. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.0 KB
Line 
1/* $Id: vkatDriverStack.cpp 89401 2021-05-31 13:17:26Z vboxsync $ */
2/** @file
3 * Validation Kit Audio Test (VKAT) - Driver stack code.
4 */
5
6/*
7 * Copyright (C) 2021 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/buildconfig.h>
32#include <iprt/ctype.h>
33#include <iprt/dir.h>
34#include <iprt/errcore.h>
35#include <iprt/initterm.h>
36#include <iprt/getopt.h>
37#include <iprt/message.h>
38#include <iprt/path.h>
39#include <iprt/process.h>
40#include <iprt/rand.h>
41#include <iprt/stream.h>
42#include <iprt/string.h>
43#include <iprt/uuid.h>
44#include <iprt/test.h>
45
46#include <package-generated.h>
47#include "product-generated.h"
48
49#include <VBox/version.h>
50#include <VBox/log.h>
51
52#ifdef RT_OS_WINDOWS
53# include <iprt/win/windows.h> /* for CoInitializeEx */
54#endif
55
56/**
57 * Internal driver instance data
58 * @note This must be put here as it's needed before pdmdrv.h is included.
59 */
60typedef struct PDMDRVINSINT
61{
62 /** The stack the drive belongs to. */
63 struct AUDIOTESTDRVSTACK *pStack;
64} PDMDRVINSINT;
65#define PDMDRVINSINT_DECLARED
66
67#include "vkatInternal.h"
68
69
70
71/*********************************************************************************************************************************
72* Fake PDM Driver Handling. *
73*********************************************************************************************************************************/
74
75/** @name Driver Fakes/Stubs
76 *
77 * @note The VMM functions defined here will turn into driver helpers before
78 * long, as the drivers aren't supposed to import directly from the VMM in
79 * the future.
80 *
81 * @{ */
82
83VMMR3DECL(PCFGMNODE) CFGMR3GetChild(PCFGMNODE pNode, const char *pszPath)
84{
85 RT_NOREF(pNode, pszPath);
86 return NULL;
87}
88
89VMMR3DECL(int) CFGMR3QueryString(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString)
90{
91 if (pNode != NULL)
92 {
93 PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
94 if (g_uVerbosity > 2)
95 RTPrintf("debug: CFGMR3QueryString([%s], %s, %p, %#x)\n", pDrvReg->szName, pszName, pszString, cchString);
96
97 if ( ( strcmp(pDrvReg->szName, "PulseAudio") == 0
98 || strcmp(pDrvReg->szName, "HostAudioWas") == 0)
99 && strcmp(pszName, "VmName") == 0)
100 return RTStrCopy(pszString, cchString, "vkat");
101
102 if ( strcmp(pDrvReg->szName, "HostAudioWas") == 0
103 && strcmp(pszName, "VmUuid") == 0)
104 return RTStrCopy(pszString, cchString, "794c9192-d045-4f28-91ed-46253ac9998e");
105 }
106 else if (g_uVerbosity > 2)
107 RTPrintf("debug: CFGMR3QueryString(%p, %s, %p, %#x)\n", pNode, pszName, pszString, cchString);
108
109 return VERR_CFGM_VALUE_NOT_FOUND;
110}
111
112VMMR3DECL(int) CFGMR3QueryStringAlloc(PCFGMNODE pNode, const char *pszName, char **ppszString)
113{
114 char szStr[128];
115 int rc = CFGMR3QueryString(pNode, pszName, szStr, sizeof(szStr));
116 if (RT_SUCCESS(rc))
117 *ppszString = RTStrDup(szStr);
118
119 return rc;
120}
121
122VMMR3DECL(void) MMR3HeapFree(void *pv)
123{
124 /* counterpart to CFGMR3QueryStringAlloc */
125 RTStrFree((char *)pv);
126}
127
128VMMR3DECL(int) CFGMR3QueryStringDef(PCFGMNODE pNode, const char *pszName, char *pszString, size_t cchString, const char *pszDef)
129{
130 PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
131 if (RT_VALID_PTR(pDrvReg))
132 {
133 const char *pszRet = pszDef;
134 if ( g_pszDrvAudioDebug
135 && strcmp(pDrvReg->szName, "AUDIO") == 0
136 && strcmp(pszName, "DebugPathOut") == 0)
137 pszRet = g_pszDrvAudioDebug;
138
139 int rc = RTStrCopy(pszString, cchString, pszRet);
140
141 if (g_uVerbosity > 2)
142 RTPrintf("debug: CFGMR3QueryStringDef([%s], %s, %p, %#x, %s) -> '%s' + %Rrc\n",
143 pDrvReg->szName, pszName, pszString, cchString, pszDef, pszRet, rc);
144 return rc;
145 }
146
147 if (g_uVerbosity > 2)
148 RTPrintf("debug: CFGMR3QueryStringDef(%p, %s, %p, %#x, %s)\n", pNode, pszName, pszString, cchString, pszDef);
149 return RTStrCopy(pszString, cchString, pszDef);
150}
151
152VMMR3DECL(int) CFGMR3QueryBoolDef(PCFGMNODE pNode, const char *pszName, bool *pf, bool fDef)
153{
154 PCPDMDRVREG pDrvReg = (PCPDMDRVREG)pNode;
155 if (RT_VALID_PTR(pDrvReg))
156 {
157 *pf = fDef;
158 if ( strcmp(pDrvReg->szName, "AUDIO") == 0
159 && strcmp(pszName, "DebugEnabled") == 0)
160 *pf = g_fDrvAudioDebug;
161
162 if (g_uVerbosity > 2)
163 RTPrintf("debug: CFGMR3QueryBoolDef([%s], %s, %p, %RTbool) -> %RTbool\n", pDrvReg->szName, pszName, pf, fDef, *pf);
164 return VINF_SUCCESS;
165 }
166 *pf = fDef;
167 return VINF_SUCCESS;
168}
169
170VMMR3DECL(int) CFGMR3QueryU8(PCFGMNODE pNode, const char *pszName, uint8_t *pu8)
171{
172 RT_NOREF(pNode, pszName, pu8);
173 return VERR_CFGM_VALUE_NOT_FOUND;
174}
175
176VMMR3DECL(int) CFGMR3QueryU32(PCFGMNODE pNode, const char *pszName, uint32_t *pu32)
177{
178 RT_NOREF(pNode, pszName, pu32);
179 return VERR_CFGM_VALUE_NOT_FOUND;
180}
181
182VMMR3DECL(int) CFGMR3ValidateConfig(PCFGMNODE pNode, const char *pszNode,
183 const char *pszValidValues, const char *pszValidNodes,
184 const char *pszWho, uint32_t uInstance)
185{
186 RT_NOREF(pNode, pszNode, pszValidValues, pszValidNodes, pszWho, uInstance);
187 return VINF_SUCCESS;
188}
189
190/** @} */
191
192/** @name Driver Helper Fakes
193 * @{ */
194
195static DECLCALLBACK(int) audioTestDrvHlp_Attach(PPDMDRVINS pDrvIns, uint32_t fFlags, PPDMIBASE *ppBaseInterface)
196{
197 /* DrvAudio must be allowed to attach the backend driver (paranoid
198 backend drivers may call us to check that nothing is attached). */
199 if (strcmp(pDrvIns->pReg->szName, "AUDIO") == 0)
200 {
201 PAUDIOTESTDRVSTACK pDrvStack = pDrvIns->Internal.s.pStack;
202 AssertReturn(pDrvStack->pDrvBackendIns == NULL, VERR_PDM_DRIVER_ALREADY_ATTACHED);
203
204 if (g_uVerbosity > 1)
205 RTMsgInfo("Attaching backend '%s' to DrvAudio...\n", pDrvStack->pDrvReg->szName);
206 int rc = audioTestDrvConstruct(pDrvStack, pDrvStack->pDrvReg, pDrvIns, &pDrvStack->pDrvBackendIns);
207 if (RT_SUCCESS(rc))
208 {
209 if (ppBaseInterface)
210 *ppBaseInterface = &pDrvStack->pDrvBackendIns->IBase;
211 }
212 else
213 RTMsgError("Failed to attach backend: %Rrc", rc);
214 return rc;
215 }
216 RT_NOREF(fFlags);
217 return VERR_PDM_NO_ATTACHED_DRIVER;
218}
219
220static DECLCALLBACK(void) audioTestDrvHlp_STAMRegisterF(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType,
221 STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszDesc,
222 const char *pszName, ...)
223{
224 RT_NOREF(pDrvIns, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName);
225}
226
227static DECLCALLBACK(void) audioTestDrvHlp_STAMRegisterV(PPDMDRVINS pDrvIns, void *pvSample, STAMTYPE enmType,
228 STAMVISIBILITY enmVisibility, STAMUNIT enmUnit, const char *pszDesc,
229 const char *pszName, va_list args)
230{
231 RT_NOREF(pDrvIns, pvSample, enmType, enmVisibility, enmUnit, pszDesc, pszName, args);
232}
233
234static DECLCALLBACK(int) audioTestDrvHlp_STAMDeregister(PPDMDRVINS pDrvIns, void *pvSample)
235{
236 RT_NOREF(pDrvIns, pvSample);
237 return VINF_SUCCESS;
238}
239
240static DECLCALLBACK(int) audioTestDrvHlp_STAMDeregisterByPrefix(PPDMDRVINS pDrvIns, const char *pszPrefix)
241{
242 RT_NOREF(pDrvIns, pszPrefix);
243 return VINF_SUCCESS;
244}
245
246/**
247 * Get the driver helpers.
248 */
249static const PDMDRVHLPR3 *audioTestFakeGetDrvHlp(void)
250{
251 /*
252 * Note! No initializer for s_DrvHlp (also why it's not a file global).
253 * We do not want to have to update this code every time PDMDRVHLPR3
254 * grows new entries or are otherwise modified. Only when the
255 * entries used by the audio driver changes do we want to change
256 * our code.
257 */
258 static PDMDRVHLPR3 s_DrvHlp;
259 if (s_DrvHlp.u32Version != PDM_DRVHLPR3_VERSION)
260 {
261 s_DrvHlp.u32Version = PDM_DRVHLPR3_VERSION;
262 s_DrvHlp.u32TheEnd = PDM_DRVHLPR3_VERSION;
263 s_DrvHlp.pfnAttach = audioTestDrvHlp_Attach;
264 s_DrvHlp.pfnSTAMRegisterF = audioTestDrvHlp_STAMRegisterF;
265 s_DrvHlp.pfnSTAMRegisterV = audioTestDrvHlp_STAMRegisterV;
266 s_DrvHlp.pfnSTAMDeregister = audioTestDrvHlp_STAMDeregister;
267 s_DrvHlp.pfnSTAMDeregisterByPrefix = audioTestDrvHlp_STAMDeregisterByPrefix;
268 }
269 return &s_DrvHlp;
270}
271
272/** @} */
273
274
275/**
276 * Implementation of PDMIBASE::pfnQueryInterface for a fake device above
277 * DrvAudio.
278 */
279static DECLCALLBACK(void *) audioTestFakeDeviceIBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
280{
281 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, pInterface);
282 RTMsgWarning("audioTestFakeDeviceIBaseQueryInterface: Unknown interface: %s\n", pszIID);
283 return NULL;
284}
285
286/** IBase interface for a fake device above DrvAudio. */
287static PDMIBASE g_AudioTestFakeDeviceIBase = { audioTestFakeDeviceIBaseQueryInterface };
288
289
290static DECLCALLBACK(int) audioTestIHostAudioPort_DoOnWorkerThread(PPDMIHOSTAUDIOPORT pInterface, PPDMAUDIOBACKENDSTREAM pStream,
291 uintptr_t uUser, void *pvUser)
292{
293 RT_NOREF(pInterface, pStream, uUser, pvUser);
294 RTMsgWarning("audioTestIHostAudioPort_DoOnWorkerThread was called\n");
295 return VERR_NOT_IMPLEMENTED;
296}
297
298DECLCALLBACK(void) audioTestIHostAudioPort_NotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface, PDMAUDIODIR enmDir, void *pvUser)
299{
300 RT_NOREF(pInterface, enmDir, pvUser);
301 RTMsgWarning("audioTestIHostAudioPort_NotifyDeviceChanged was called\n");
302}
303
304static DECLCALLBACK(void) audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch(PPDMIHOSTAUDIOPORT pInterface,
305 PPDMAUDIOBACKENDSTREAM pStream)
306{
307 RT_NOREF(pInterface, pStream);
308 RTMsgWarning("audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch was called\n");
309}
310
311static DECLCALLBACK(void) audioTestIHostAudioPort_StreamNotifyDeviceChanged(PPDMIHOSTAUDIOPORT pInterface,
312 PPDMAUDIOBACKENDSTREAM pStream, bool fReInit)
313{
314 RT_NOREF(pInterface, pStream, fReInit);
315 RTMsgWarning("audioTestIHostAudioPort_StreamNotifyDeviceChanged was called\n");
316}
317
318static DECLCALLBACK(void) audioTestIHostAudioPort_NotifyDevicesChanged(PPDMIHOSTAUDIOPORT pInterface)
319{
320 RT_NOREF(pInterface);
321 RTMsgWarning("audioTestIHostAudioPort_NotifyDevicesChanged was called\n");
322}
323
324static PDMIHOSTAUDIOPORT g_AudioTestIHostAudioPort =
325{
326 audioTestIHostAudioPort_DoOnWorkerThread,
327 audioTestIHostAudioPort_NotifyDeviceChanged,
328 audioTestIHostAudioPort_StreamNotifyPreparingDeviceSwitch,
329 audioTestIHostAudioPort_StreamNotifyDeviceChanged,
330 audioTestIHostAudioPort_NotifyDevicesChanged,
331};
332
333/**
334 * Implementation of PDMIBASE::pfnQueryInterface for a fake DrvAudio above a
335 * backend.
336 */
337static DECLCALLBACK(void *) audioTestFakeDrvAudioIBaseQueryInterface(PPDMIBASE pInterface, const char *pszIID)
338{
339 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, pInterface);
340 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIOPORT, &g_AudioTestIHostAudioPort);
341 RTMsgWarning("audioTestFakeDrvAudioIBaseQueryInterface: Unknown interface: %s\n", pszIID);
342 return NULL;
343}
344
345/** IBase interface for a fake DrvAudio above a lonesome backend. */
346static PDMIBASE g_AudioTestFakeDrvAudioIBase = { audioTestFakeDrvAudioIBaseQueryInterface };
347
348
349
350/**
351 * Constructs a PDM audio driver instance.
352 *
353 * @returns VBox status code.
354 * @param pDrvStack The stack this is associated with.
355 * @param pDrvReg PDM driver registration record to use for construction.
356 * @param pParentDrvIns The parent driver (if any).
357 * @param ppDrvIns Where to return the driver instance structure.
358 */
359int audioTestDrvConstruct(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, PPDMDRVINS pParentDrvIns,
360 PPPDMDRVINS ppDrvIns)
361{
362 /* The destruct function must have valid data to work with. */
363 *ppDrvIns = NULL;
364
365 /*
366 * Check registration structure validation (doesn't need to be too
367 * thorough, PDM check it in detail on every VM startup).
368 */
369 AssertPtrReturn(pDrvReg, VERR_INVALID_POINTER);
370 RTMsgInfo("Initializing backend '%s' ...\n", pDrvReg->szName);
371 AssertPtrReturn(pDrvReg->pfnConstruct, VERR_INVALID_PARAMETER);
372
373 /*
374 * Create the instance data structure.
375 */
376 PPDMDRVINS pDrvIns = (PPDMDRVINS)RTMemAllocZVar(RT_UOFFSETOF_DYN(PDMDRVINS, achInstanceData[pDrvReg->cbInstance]));
377 RTTEST_CHECK_RET(g_hTest, pDrvIns, VERR_NO_MEMORY);
378
379 pDrvIns->u32Version = PDM_DRVINS_VERSION;
380 pDrvIns->iInstance = 0;
381 pDrvIns->pHlpR3 = audioTestFakeGetDrvHlp();
382 pDrvIns->pvInstanceDataR3 = &pDrvIns->achInstanceData[0];
383 pDrvIns->pReg = pDrvReg;
384 pDrvIns->pCfg = (PCFGMNODE)pDrvReg;
385 pDrvIns->Internal.s.pStack = pDrvStack;
386 pDrvIns->pUpBase = NULL;
387 pDrvIns->pDownBase = NULL;
388 if (pParentDrvIns)
389 {
390 Assert(pParentDrvIns->pDownBase == NULL);
391 pParentDrvIns->pDownBase = &pDrvIns->IBase;
392 pDrvIns->pUpBase = &pParentDrvIns->IBase;
393 }
394 else if (strcmp(pDrvReg->szName, "AUDIO") == 0)
395 pDrvIns->pUpBase = &g_AudioTestFakeDeviceIBase;
396 else
397 pDrvIns->pUpBase = &g_AudioTestFakeDrvAudioIBase;
398
399 /*
400 * Invoke the constructor.
401 */
402 int rc = pDrvReg->pfnConstruct(pDrvIns, pDrvIns->pCfg, 0 /*fFlags*/);
403 if (RT_SUCCESS(rc))
404 {
405 *ppDrvIns = pDrvIns;
406 return VINF_SUCCESS;
407 }
408
409 RTTestFailed(g_hTest, "Failed to construct audio driver '%s': %Rrc", pDrvReg->szName, rc);
410 if (pDrvReg->pfnDestruct)
411 pDrvReg->pfnDestruct(pDrvIns);
412 RTMemFree(pDrvIns);
413 return rc;
414}
415
416/**
417 * Destructs a PDM audio driver instance.
418 *
419 * @param pDrvIns Driver instance to destruct.
420 */
421static void audioTestDrvDestruct(PPDMDRVINS pDrvIns)
422{
423 if (pDrvIns)
424 {
425 Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION);
426
427 if (pDrvIns->pReg->pfnDestruct)
428 pDrvIns->pReg->pfnDestruct(pDrvIns);
429
430 pDrvIns->u32Version = 0;
431 pDrvIns->pReg = NULL;
432 RTMemFree(pDrvIns);
433 }
434}
435
436/**
437 * Sends the PDM driver a power off notification.
438 *
439 * @param pDrvIns Driver instance to notify.
440 */
441static void audioTestDrvNotifyPowerOff(PPDMDRVINS pDrvIns)
442{
443 if (pDrvIns)
444 {
445 Assert(pDrvIns->u32Version == PDM_DRVINS_VERSION);
446 if (pDrvIns->pReg->pfnPowerOff)
447 pDrvIns->pReg->pfnPowerOff(pDrvIns);
448 }
449}
450
451/**
452 * Deletes a driver stack.
453 *
454 * This will power off and destroy the drivers.
455 */
456void audioTestDriverStackDelete(PAUDIOTESTDRVSTACK pDrvStack)
457{
458 /*
459 * Do power off notifications (top to bottom).
460 */
461 audioTestDrvNotifyPowerOff(pDrvStack->pDrvAudioIns);
462 audioTestDrvNotifyPowerOff(pDrvStack->pDrvBackendIns);
463
464 /*
465 * Drivers are destroyed from bottom to top (closest to the device).
466 */
467 audioTestDrvDestruct(pDrvStack->pDrvBackendIns);
468 pDrvStack->pDrvBackendIns = NULL;
469 pDrvStack->pIHostAudio = NULL;
470
471 audioTestDrvDestruct(pDrvStack->pDrvAudioIns);
472 pDrvStack->pDrvAudioIns = NULL;
473 pDrvStack->pIAudioConnector = NULL;
474}
475
476/**
477 * Initializes a driver stack.
478 *
479 * @returns VBox status code.
480 * @param pDrvStack The driver stack to initialize.
481 * @param pDrvReg The backend driver to use.
482 * @param fWithDrvAudio Whether to include DrvAudio in the stack or not.
483 */
484int audioTestDriverStackInit(PAUDIOTESTDRVSTACK pDrvStack, PCPDMDRVREG pDrvReg, bool fWithDrvAudio)
485{
486 RT_ZERO(*pDrvStack);
487 pDrvStack->pDrvReg = pDrvReg;
488
489 int rc;
490 if (!fWithDrvAudio)
491 rc = audioTestDrvConstruct(pDrvStack, pDrvReg, NULL /*pParentDrvIns*/, &pDrvStack->pDrvBackendIns);
492 else
493 {
494 rc = audioTestDrvConstruct(pDrvStack, &g_DrvAUDIO, NULL /*pParentDrvIns*/, &pDrvStack->pDrvAudioIns);
495 if (RT_SUCCESS(rc))
496 {
497 Assert(pDrvStack->pDrvAudioIns);
498 PPDMIBASE const pIBase = &pDrvStack->pDrvAudioIns->IBase;
499 pDrvStack->pIAudioConnector = (PPDMIAUDIOCONNECTOR)pIBase->pfnQueryInterface(pIBase, PDMIAUDIOCONNECTOR_IID);
500 if (pDrvStack->pIAudioConnector)
501 {
502 /* Both input and output is disabled by default. Fix that: */
503 rc = pDrvStack->pIAudioConnector->pfnEnable(pDrvStack->pIAudioConnector, PDMAUDIODIR_OUT, true);
504 if (RT_SUCCESS(rc))
505 rc = pDrvStack->pIAudioConnector->pfnEnable(pDrvStack->pIAudioConnector, PDMAUDIODIR_IN, true);
506 if (RT_FAILURE(rc))
507 {
508 RTTestFailed(g_hTest, "Failed to enabled input and output: %Rrc", rc);
509 audioTestDriverStackDelete(pDrvStack);
510 }
511 }
512 else
513 {
514 RTTestFailed(g_hTest, "Failed to query PDMIAUDIOCONNECTOR");
515 audioTestDriverStackDelete(pDrvStack);
516 rc = VERR_PDM_MISSING_INTERFACE;
517 }
518 }
519 }
520
521 /*
522 * Get the IHostAudio interface and check that the host driver is working.
523 */
524 if (RT_SUCCESS(rc))
525 {
526 PPDMIBASE const pIBase = &pDrvStack->pDrvBackendIns->IBase;
527 pDrvStack->pIHostAudio = (PPDMIHOSTAUDIO)pIBase->pfnQueryInterface(pIBase, PDMIHOSTAUDIO_IID);
528 if (pDrvStack->pIHostAudio)
529 {
530 PDMAUDIOBACKENDSTS enmStatus = pDrvStack->pIHostAudio->pfnGetStatus(pDrvStack->pIHostAudio, PDMAUDIODIR_OUT);
531 if (enmStatus == PDMAUDIOBACKENDSTS_RUNNING)
532 return VINF_SUCCESS;
533
534 RTTestFailed(g_hTest, "Expected backend status RUNNING, got %d instead", enmStatus);
535 }
536 else
537 RTTestFailed(g_hTest, "Failed to query PDMIHOSTAUDIO for '%s'", pDrvReg->szName);
538 audioTestDriverStackDelete(pDrvStack);
539 }
540
541 return rc;
542}
543
544/**
545 * Wrapper around PDMIHOSTAUDIO::pfnSetDevice.
546 */
547int audioTestDriverStackSetDevice(PAUDIOTESTDRVSTACK pDrvStack, PDMAUDIODIR enmDir, const char *pszDevId)
548{
549 int rc;
550 if ( pDrvStack->pIHostAudio
551 && pDrvStack->pIHostAudio->pfnSetDevice)
552 rc = pDrvStack->pIHostAudio->pfnSetDevice(pDrvStack->pIHostAudio, enmDir, pszDevId);
553 else if (!pszDevId || *pszDevId)
554 rc = VINF_SUCCESS;
555 else
556 rc = VERR_INVALID_FUNCTION;
557 return rc;
558}
559
560/**
561 * Common stream creation code.
562 *
563 * @returns VBox status code.
564 * @param pDrvStack The audio driver stack to create it via.
565 * @param pCfgReq The requested config.
566 * @param ppStream Where to return the stream pointer on success.
567 * @param pCfgAcq Where to return the actual (well, not
568 * necessarily when using DrvAudio, but probably
569 * the same) stream config on success (not used as
570 * input).
571 */
572static int audioTestDriverStackStreamCreate(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAMCFG pCfgReq,
573 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
574{
575 char szTmp[PDMAUDIOSTRMCFGTOSTRING_MAX + 16];
576 int rc;
577 *ppStream = NULL;
578
579 if (pDrvStack->pIAudioConnector)
580 {
581 /*
582 * DrvAudio does most of the work here.
583 */
584 PDMAUDIOSTREAMCFG CfgGst = *pCfgReq;
585 rc = pDrvStack->pIAudioConnector->pfnStreamCreate(pDrvStack->pIAudioConnector, PDMAUDIOSTREAM_CREATE_F_NO_MIXBUF,
586 pCfgReq, &CfgGst, ppStream);
587 if (RT_SUCCESS(rc))
588 {
589 *pCfgAcq = *pCfgReq; /** @todo PDMIAUDIOCONNECTOR::pfnStreamCreate only does one utterly pointless change to the two configs (enmLayout) from what I can tell... */
590 pCfgAcq->Props = (*ppStream)->Props;
591 RTMsgInfo("Created backend stream: %s\n", PDMAudioStrmCfgToString(pCfgReq, szTmp, sizeof(szTmp)));
592 return rc;
593 }
594 RTTestFailed(g_hTest, "pfnStreamCreate failed: %Rrc", rc);
595 }
596 else
597 {
598 /*
599 * Get the config so we can see how big the PDMAUDIOBACKENDSTREAM
600 * structure actually is for this backend.
601 */
602 PDMAUDIOBACKENDCFG BackendCfg;
603 rc = pDrvStack->pIHostAudio->pfnGetConfig(pDrvStack->pIHostAudio, &BackendCfg);
604 if (RT_SUCCESS(rc))
605 {
606 if (BackendCfg.cbStream >= sizeof(PDMAUDIOBACKENDSTREAM))
607 {
608 /*
609 * Allocate and initialize the stream.
610 */
611 uint32_t const cbStream = sizeof(AUDIOTESTDRVSTACKSTREAM) - sizeof(PDMAUDIOBACKENDSTREAM) + BackendCfg.cbStream;
612 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)RTMemAllocZVar(cbStream);
613 if (pStreamAt)
614 {
615 pStreamAt->Core.uMagic = PDMAUDIOSTREAM_MAGIC;
616 pStreamAt->Core.enmDir = PDMAUDIODIR_OUT;
617 pStreamAt->Core.cbBackend = cbStream;
618 pStreamAt->Core.Props = pCfgReq->Props;
619 RTStrPrintf(pStreamAt->Core.szName, sizeof(pStreamAt->Core.szName), pCfgReq->szName);
620
621 pStreamAt->Backend.uMagic = PDMAUDIOBACKENDSTREAM_MAGIC;
622 pStreamAt->Backend.pStream = &pStreamAt->Core;
623
624 /*
625 * Call the backend to create the stream.
626 */
627 pStreamAt->Cfg = *pCfgReq;
628
629 rc = pDrvStack->pIHostAudio->pfnStreamCreate(pDrvStack->pIHostAudio, &pStreamAt->Backend,
630 pCfgReq, &pStreamAt->Cfg);
631 if (RT_SUCCESS(rc))
632 {
633 pStreamAt->Core.Props = pStreamAt->Cfg.Props;
634 if (g_uVerbosity > 1)
635 RTMsgInfo("Created backend stream: %s\n",
636 PDMAudioStrmCfgToString(&pStreamAt->Cfg, szTmp, sizeof(szTmp)));
637
638 /* Return if stream is ready: */
639 if (rc == VINF_SUCCESS)
640 {
641 *ppStream = &pStreamAt->Core;
642 *pCfgAcq = pStreamAt->Cfg;
643 return VINF_SUCCESS;
644 }
645 if (rc == VINF_AUDIO_STREAM_ASYNC_INIT_NEEDED)
646 {
647 /*
648 * Do async init right here and now.
649 */
650 rc = pDrvStack->pIHostAudio->pfnStreamInitAsync(pDrvStack->pIHostAudio, &pStreamAt->Backend,
651 false /*fDestroyed*/);
652 if (RT_SUCCESS(rc))
653 {
654 *ppStream = &pStreamAt->Core;
655 *pCfgAcq = pStreamAt->Cfg;
656 return VINF_SUCCESS;
657 }
658
659 RTTestFailed(g_hTest, "pfnStreamInitAsync failed: %Rrc\n", rc);
660 }
661 else
662 {
663 RTTestFailed(g_hTest, "pfnStreamCreate returned unexpected info status: %Rrc", rc);
664 rc = VERR_IPE_UNEXPECTED_INFO_STATUS;
665 }
666 pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/);
667 }
668 else
669 RTTestFailed(g_hTest, "pfnStreamCreate failed: %Rrc\n", rc);
670 }
671 else
672 {
673 RTTestFailed(g_hTest, "Out of memory!\n");
674 rc = VERR_NO_MEMORY;
675 }
676 }
677 else
678 {
679 RTTestFailed(g_hTest, "cbStream=%#x is too small, min %#zx!\n", BackendCfg.cbStream, sizeof(PDMAUDIOBACKENDSTREAM));
680 rc = VERR_OUT_OF_RANGE;
681 }
682 }
683 else
684 RTTestFailed(g_hTest, "pfnGetConfig failed: %Rrc\n", rc);
685 }
686 return rc;
687}
688
689/**
690 * Creates an output stream.
691 *
692 * @returns VBox status code.
693 * @param pDrvStack The audio driver stack to create it via.
694 * @param pProps The audio properties to use.
695 * @param cMsBufferSize The buffer size in milliseconds.
696 * @param cMsPreBuffer The pre-buffering amount in milliseconds.
697 * @param cMsSchedulingHint The scheduling hint in milliseconds.
698 * @param ppStream Where to return the stream pointer on success.
699 * @param pCfgAcq Where to return the actual (well, not
700 * necessarily when using DrvAudio, but probably
701 * the same) stream config on success (not used as
702 * input).
703 */
704int audioTestDriverStackStreamCreateOutput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
705 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
706 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
707{
708 /*
709 * Calculate the stream config.
710 */
711 PDMAUDIOSTREAMCFG CfgReq;
712 int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps);
713 AssertRC(rc);
714 CfgReq.enmDir = PDMAUDIODIR_OUT;
715 CfgReq.enmPath = PDMAUDIOPATH_OUT_FRONT;
716 CfgReq.Device.cMsSchedulingHint = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0
717 ? 10 : cMsSchedulingHint;
718 if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0))
719 CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */
720 else
721 CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps,
722 cMsBufferSize == UINT32_MAX || cMsBufferSize == 0
723 ? 300 : cMsBufferSize);
724 if (cMsPreBuffer == UINT32_MAX)
725 CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudo picks the default */
726 : CfgReq.Backend.cFramesBufferSize * 2 / 3;
727 else
728 CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer);
729 if ( CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16
730 && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ )
731 {
732 RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!",
733 CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize);
734 CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16
735 ? CfgReq.Backend.cFramesBufferSize - 16 : 0;
736 }
737
738 static uint32_t s_idxStream = 0;
739 uint32_t const idxStream = s_idxStream++;
740 RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "out-%u", idxStream);
741
742 /*
743 * Call common code to do the actual work.
744 */
745 return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq);
746}
747
748/**
749 * Creates an input stream.
750 *
751 * @returns VBox status code.
752 * @param pDrvStack The audio driver stack to create it via.
753 * @param pProps The audio properties to use.
754 * @param cMsBufferSize The buffer size in milliseconds.
755 * @param cMsPreBuffer The pre-buffering amount in milliseconds.
756 * @param cMsSchedulingHint The scheduling hint in milliseconds.
757 * @param ppStream Where to return the stream pointer on success.
758 * @param pCfgAcq Where to return the actual (well, not
759 * necessarily when using DrvAudio, but probably
760 * the same) stream config on success (not used as
761 * input).
762 */
763int audioTestDriverStackStreamCreateInput(PAUDIOTESTDRVSTACK pDrvStack, PCPDMAUDIOPCMPROPS pProps,
764 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint,
765 PPDMAUDIOSTREAM *ppStream, PPDMAUDIOSTREAMCFG pCfgAcq)
766{
767 /*
768 * Calculate the stream config.
769 */
770 PDMAUDIOSTREAMCFG CfgReq;
771 int rc = PDMAudioStrmCfgInitWithProps(&CfgReq, pProps);
772 AssertRC(rc);
773 CfgReq.enmDir = PDMAUDIODIR_IN;
774 CfgReq.enmPath = PDMAUDIOPATH_IN_LINE;
775 CfgReq.Device.cMsSchedulingHint = cMsSchedulingHint == UINT32_MAX || cMsSchedulingHint == 0
776 ? 10 : cMsSchedulingHint;
777 if (pDrvStack->pIAudioConnector && (cMsBufferSize == UINT32_MAX || cMsBufferSize == 0))
778 CfgReq.Backend.cFramesBufferSize = 0; /* DrvAudio picks the default */
779 else
780 CfgReq.Backend.cFramesBufferSize = PDMAudioPropsMilliToFrames(pProps,
781 cMsBufferSize == UINT32_MAX || cMsBufferSize == 0
782 ? 300 : cMsBufferSize);
783 if (cMsPreBuffer == UINT32_MAX)
784 CfgReq.Backend.cFramesPreBuffering = pDrvStack->pIAudioConnector ? UINT32_MAX /*DrvAudio picks the default */
785 : CfgReq.Backend.cFramesBufferSize / 2;
786 else
787 CfgReq.Backend.cFramesPreBuffering = PDMAudioPropsMilliToFrames(pProps, cMsPreBuffer);
788 if ( CfgReq.Backend.cFramesPreBuffering >= CfgReq.Backend.cFramesBufferSize + 16 /** @todo way to little */
789 && !pDrvStack->pIAudioConnector /*DrvAudio deals with it*/ )
790 {
791 RTMsgWarning("Cannot pre-buffer %#x frames with only %#x frames of buffer!",
792 CfgReq.Backend.cFramesPreBuffering, CfgReq.Backend.cFramesBufferSize);
793 CfgReq.Backend.cFramesPreBuffering = CfgReq.Backend.cFramesBufferSize > 16
794 ? CfgReq.Backend.cFramesBufferSize - 16 : 0;
795 }
796
797 static uint32_t s_idxStream = 0;
798 uint32_t const idxStream = s_idxStream++;
799 RTStrPrintf(CfgReq.szName, sizeof(CfgReq.szName), "in-%u", idxStream);
800
801 /*
802 * Call common code to do the actual work.
803 */
804 return audioTestDriverStackStreamCreate(pDrvStack, &CfgReq, ppStream, pCfgAcq);
805}
806
807/**
808 * Destroys a stream.
809 */
810void audioTestDriverStackStreamDestroy(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
811{
812 if (pStream)
813 {
814 if (pDrvStack->pIAudioConnector)
815 {
816 int rc = pDrvStack->pIAudioConnector->pfnStreamDestroy(pDrvStack->pIAudioConnector, pStream, true /*fImmediate*/);
817 if (RT_FAILURE(rc))
818 RTTestFailed(g_hTest, "pfnStreamDestroy failed: %Rrc", rc);
819 }
820 else
821 {
822 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
823 int rc = pDrvStack->pIHostAudio->pfnStreamDestroy(pDrvStack->pIHostAudio, &pStreamAt->Backend, true /*fImmediate*/);
824 if (RT_SUCCESS(rc))
825 {
826 pStreamAt->Core.uMagic = ~PDMAUDIOSTREAM_MAGIC;
827 pStreamAt->Backend.uMagic = ~PDMAUDIOBACKENDSTREAM_MAGIC;
828 RTMemFree(pStreamAt);
829 }
830 else
831 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamDestroy failed: %Rrc", rc);
832 }
833 }
834}
835
836/**
837 * Enables a stream.
838 */
839int audioTestDriverStackStreamEnable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
840{
841 int rc;
842 if (pDrvStack->pIAudioConnector)
843 {
844 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_ENABLE);
845 if (RT_FAILURE(rc))
846 RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc);
847 }
848 else
849 {
850 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
851 rc = pDrvStack->pIHostAudio->pfnStreamControl(pDrvStack->pIHostAudio, &pStreamAt->Backend, PDMAUDIOSTREAMCMD_ENABLE);
852 if (RT_FAILURE(rc))
853 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamControl/ENABLE failed: %Rrc", rc);
854 }
855 return rc;
856}
857
858/**
859 * Drains an output stream.
860 */
861int audioTestDriverStackStreamDrain(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream, bool fSync)
862{
863 int rc;
864 if (pDrvStack->pIAudioConnector)
865 {
866 /*
867 * Issue the drain request.
868 */
869 rc = pDrvStack->pIAudioConnector->pfnStreamControl(pDrvStack->pIAudioConnector, pStream, PDMAUDIOSTREAMCMD_DRAIN);
870 if (RT_SUCCESS(rc) && fSync)
871 {
872 /*
873 * This is a synchronous drain, so wait for the driver to change state to inactive.
874 */
875 PDMAUDIOSTREAMSTATE enmState;
876 while ( (enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream))
877 >= PDMAUDIOSTREAMSTATE_ENABLED)
878 {
879 RTThreadSleep(2);
880 rc = pDrvStack->pIAudioConnector->pfnStreamIterate(pDrvStack->pIAudioConnector, pStream);
881 if (RT_FAILURE(rc))
882 {
883 RTTestFailed(g_hTest, "pfnStreamIterate/DRAIN failed: %Rrc", rc);
884 break;
885 }
886 }
887 if (enmState != PDMAUDIOSTREAMSTATE_INACTIVE)
888 {
889 RTTestFailed(g_hTest, "Stream state not INACTIVE after draining: %s", PDMAudioStreamStateGetName(enmState));
890 rc = VERR_AUDIO_STREAM_NOT_READY;
891 }
892 }
893 else if (RT_FAILURE(rc))
894 RTTestFailed(g_hTest, "pfnStreamControl/ENABLE failed: %Rrc", rc);
895 }
896 else
897 {
898 /*
899 * Issue the drain request.
900 */
901 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
902 rc = pDrvStack->pIHostAudio->pfnStreamControl(pDrvStack->pIHostAudio, &pStreamAt->Backend, PDMAUDIOSTREAMCMD_DRAIN);
903 if (RT_SUCCESS(rc) && fSync)
904 {
905 /*
906 * This is a synchronous drain, so wait for the driver to change state to inactive.
907 */
908 PDMHOSTAUDIOSTREAMSTATE enmHostState;
909 while ( (enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio, &pStreamAt->Backend))
910 == PDMHOSTAUDIOSTREAMSTATE_DRAINING)
911 {
912 RTThreadSleep(2);
913 uint32_t cbWritten = UINT32_MAX;
914 rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend,
915 NULL /*pvBuf*/, 0 /*cbBuf*/, &cbWritten);
916 if (RT_FAILURE(rc))
917 {
918 RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN failed: %Rrc", rc);
919 break;
920 }
921 if (cbWritten != 0)
922 {
923 RTTestFailed(g_hTest, "pfnStreamPlay/DRAIN did not set cbWritten to zero: %#x", cbWritten);
924 rc = VERR_MISSING;
925 break;
926 }
927 }
928 if (enmHostState != PDMHOSTAUDIOSTREAMSTATE_OKAY)
929 {
930 RTTestFailed(g_hTest, "Stream state not OKAY after draining: %s", PDMHostAudioStreamStateGetName(enmHostState));
931 rc = VERR_AUDIO_STREAM_NOT_READY;
932 }
933 }
934 else if (RT_FAILURE(rc))
935 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamControl/ENABLE failed: %Rrc", rc);
936 }
937 return rc;
938}
939
940/**
941 * Checks if the stream is okay.
942 * @returns true if okay, false if not.
943 */
944bool audioTestDriverStackStreamIsOkay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
945{
946 /*
947 * Get the stream status and check if it means is okay or not.
948 */
949 bool fRc = false;
950 if (pDrvStack->pIAudioConnector)
951 {
952 PDMAUDIOSTREAMSTATE enmState = pDrvStack->pIAudioConnector->pfnStreamGetState(pDrvStack->pIAudioConnector, pStream);
953 switch (enmState)
954 {
955 case PDMAUDIOSTREAMSTATE_NOT_WORKING:
956 case PDMAUDIOSTREAMSTATE_NEED_REINIT:
957 break;
958 case PDMAUDIOSTREAMSTATE_INACTIVE:
959 case PDMAUDIOSTREAMSTATE_ENABLED:
960 case PDMAUDIOSTREAMSTATE_ENABLED_READABLE:
961 case PDMAUDIOSTREAMSTATE_ENABLED_WRITABLE:
962 fRc = true;
963 break;
964 /* no default */
965 case PDMAUDIOSTREAMSTATE_INVALID:
966 case PDMAUDIOSTREAMSTATE_END:
967 case PDMAUDIOSTREAMSTATE_32BIT_HACK:
968 break;
969 }
970 }
971 else
972 {
973 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
974 PDMHOSTAUDIOSTREAMSTATE enmHostState = pDrvStack->pIHostAudio->pfnStreamGetState(pDrvStack->pIHostAudio,
975 &pStreamAt->Backend);
976 switch (enmHostState)
977 {
978 case PDMHOSTAUDIOSTREAMSTATE_INITIALIZING:
979 case PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING:
980 break;
981 case PDMHOSTAUDIOSTREAMSTATE_OKAY:
982 case PDMHOSTAUDIOSTREAMSTATE_DRAINING:
983 case PDMHOSTAUDIOSTREAMSTATE_INACTIVE:
984 fRc = true;
985 break;
986 /* no default */
987 case PDMHOSTAUDIOSTREAMSTATE_INVALID:
988 case PDMHOSTAUDIOSTREAMSTATE_END:
989 case PDMHOSTAUDIOSTREAMSTATE_32BIT_HACK:
990 break;
991 }
992 }
993 return fRc;
994}
995
996/**
997 * Gets the number of bytes it's currently possible to write to the stream.
998 */
999uint32_t audioTestDriverStackStreamGetWritable(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream)
1000{
1001 uint32_t cbWritable;
1002 if (pDrvStack->pIAudioConnector)
1003 cbWritable = pDrvStack->pIAudioConnector->pfnStreamGetWritable(pDrvStack->pIAudioConnector, pStream);
1004 else
1005 {
1006 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1007 cbWritable = pDrvStack->pIHostAudio->pfnStreamGetWritable(pDrvStack->pIHostAudio, &pStreamAt->Backend);
1008 }
1009 return cbWritable;
1010}
1011
1012/**
1013 * Tries to play the @a cbBuf bytes of samples in @a pvBuf.
1014 */
1015int audioTestDriverStackStreamPlay(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
1016 void const *pvBuf, uint32_t cbBuf, uint32_t *pcbPlayed)
1017{
1018 int rc;
1019 if (pDrvStack->pIAudioConnector)
1020 {
1021 rc = pDrvStack->pIAudioConnector->pfnStreamPlay(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbPlayed);
1022 if (RT_FAILURE(rc))
1023 RTTestFailed(g_hTest, "pfnStreamPlay(,,,%#x) failed: %Rrc", cbBuf, rc);
1024 }
1025 else
1026 {
1027 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1028 rc = pDrvStack->pIHostAudio->pfnStreamPlay(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbPlayed);
1029 if (RT_FAILURE(rc))
1030 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamPlay(,,,%#x) failed: %Rrc", cbBuf, rc);
1031 }
1032 return rc;
1033}
1034
1035/**
1036 * Tries to capture @a cbBuf bytes of samples in @a pvBuf.
1037 */
1038int audioTestDriverStackStreamCapture(PAUDIOTESTDRVSTACK pDrvStack, PPDMAUDIOSTREAM pStream,
1039 void *pvBuf, uint32_t cbBuf, uint32_t *pcbCaptured)
1040{
1041 int rc;
1042 if (pDrvStack->pIAudioConnector)
1043 {
1044 rc = pDrvStack->pIAudioConnector->pfnStreamCapture(pDrvStack->pIAudioConnector, pStream, pvBuf, cbBuf, pcbCaptured);
1045 if (RT_FAILURE(rc))
1046 RTTestFailed(g_hTest, "pfnStreamCapture(,,,%#x) failed: %Rrc", cbBuf, rc);
1047 }
1048 else
1049 {
1050 PAUDIOTESTDRVSTACKSTREAM pStreamAt = (PAUDIOTESTDRVSTACKSTREAM)pStream;
1051 rc = pDrvStack->pIHostAudio->pfnStreamCapture(pDrvStack->pIHostAudio, &pStreamAt->Backend, pvBuf, cbBuf, pcbCaptured);
1052 if (RT_FAILURE(rc))
1053 RTTestFailed(g_hTest, "PDMIHOSTAUDIO::pfnStreamCapture(,,,%#x) failed: %Rrc", cbBuf, rc);
1054 }
1055 return rc;
1056}
1057
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