VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/audio/vkatCommon.cpp@ 91141

Last change on this file since 91141 was 91141, checked in by vboxsync, 3 years ago

Audio/Validation Kit: Some more diagnostics to find out why some testbox guests refuse to play any test tones. ​bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.3 KB
Line 
1/* $Id: vkatCommon.cpp 91141 2021-09-07 14:37:42Z vboxsync $ */
2/** @file
3 * Validation Kit Audio Test (VKAT) - Self test 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#define LOG_GROUP LOG_GROUP_AUDIO_TEST
32#include <iprt/log.h>
33
34#include <iprt/ctype.h>
35#include <iprt/dir.h>
36#include <iprt/errcore.h>
37#include <iprt/getopt.h>
38#include <iprt/message.h>
39#include <iprt/rand.h>
40#include <iprt/test.h>
41
42#include "Audio/AudioHlp.h"
43#include "Audio/AudioTest.h"
44#include "Audio/AudioTestService.h"
45#include "Audio/AudioTestServiceClient.h"
46
47#include "vkatInternal.h"
48
49
50/*********************************************************************************************************************************
51* Defined Constants And Macros *
52*********************************************************************************************************************************/
53
54
55/*********************************************************************************************************************************
56* Internal Functions *
57*********************************************************************************************************************************/
58static int audioTestStreamInit(PAUDIOTESTDRVSTACK pDrvStack, PAUDIOTESTSTREAM pStream, PDMAUDIODIR enmDir, PCPDMAUDIOPCMPROPS pProps, bool fWithMixer, uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint);
59static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream);
60
61
62/*********************************************************************************************************************************
63* Device enumeration + handling. *
64*********************************************************************************************************************************/
65
66/**
67 * Enumerates audio devices and optionally searches for a specific device.
68 *
69 * @returns VBox status code.
70 * @param pDrvStack Driver stack to use for enumeration.
71 * @param pszDev Device name to search for. Can be NULL if the default device shall be used.
72 * @param ppDev Where to return the pointer of the device enumeration of \a pTstEnv when a
73 * specific device was found.
74 */
75int audioTestDevicesEnumerateAndCheck(PAUDIOTESTDRVSTACK pDrvStack, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev)
76{
77 RTTestSubF(g_hTest, "Enumerating audio devices and checking for device '%s'", pszDev && *pszDev ? pszDev : "[Default]");
78
79 if (!pDrvStack->pIHostAudio->pfnGetDevices)
80 {
81 RTTestSkipped(g_hTest, "Backend does not support device enumeration, skipping");
82 return VINF_NOT_SUPPORTED;
83 }
84
85 Assert(pszDev == NULL || ppDev);
86
87 if (ppDev)
88 *ppDev = NULL;
89
90 int rc = pDrvStack->pIHostAudio->pfnGetDevices(pDrvStack->pIHostAudio, &pDrvStack->DevEnum);
91 if (RT_SUCCESS(rc))
92 {
93 PPDMAUDIOHOSTDEV pDev;
94 RTListForEach(&pDrvStack->DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
95 {
96 char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
97 if (pDev->pszId)
98 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s' (ID '%s'):\n", pDev->pszName, pDev->pszId);
99 else
100 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s':\n", pDev->pszName);
101 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Usage = %s\n", PDMAudioDirGetName(pDev->enmUsage));
102 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Flags = %s\n", PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags));
103 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Input channels = %RU8\n", pDev->cMaxInputChannels);
104 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Output channels = %RU8\n", pDev->cMaxOutputChannels);
105
106 if ( (pszDev && *pszDev)
107 && !RTStrCmp(pDev->pszName, pszDev))
108 {
109 *ppDev = pDev;
110 }
111 }
112 }
113 else
114 RTTestFailed(g_hTest, "Enumerating audio devices failed with %Rrc", rc);
115
116 if (RT_SUCCESS(rc))
117 {
118 if ( (pszDev && *pszDev)
119 && *ppDev == NULL)
120 {
121 RTTestFailed(g_hTest, "Audio device '%s' not found", pszDev);
122 rc = VERR_NOT_FOUND;
123 }
124 }
125
126 RTTestSubDone(g_hTest);
127 return rc;
128}
129
130static int audioTestStreamInit(PAUDIOTESTDRVSTACK pDrvStack, PAUDIOTESTSTREAM pStream,
131 PDMAUDIODIR enmDir, PCPDMAUDIOPCMPROPS pProps, bool fWithMixer,
132 uint32_t cMsBufferSize, uint32_t cMsPreBuffer, uint32_t cMsSchedulingHint)
133{
134 int rc;
135
136 if (enmDir == PDMAUDIODIR_IN)
137 rc = audioTestDriverStackStreamCreateInput(pDrvStack, pProps, cMsBufferSize,
138 cMsPreBuffer, cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
139 else if (enmDir == PDMAUDIODIR_OUT)
140 rc = audioTestDriverStackStreamCreateOutput(pDrvStack, pProps, cMsBufferSize,
141 cMsPreBuffer, cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
142 else
143 rc = VERR_NOT_SUPPORTED;
144
145 if (RT_SUCCESS(rc))
146 {
147 if (!pDrvStack->pIAudioConnector)
148 {
149 pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
150 }
151 else
152 pStream->pBackend = NULL;
153
154 /*
155 * Automatically enable the mixer if the PCM properties don't match.
156 */
157 if ( !fWithMixer
158 && !PDMAudioPropsAreEqual(pProps, &pStream->Cfg.Props))
159 {
160 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enabling stream mixer\n");
161 fWithMixer = true;
162 }
163
164 rc = AudioTestMixStreamInit(&pStream->Mix, pDrvStack, pStream->pStream,
165 fWithMixer ? pProps : NULL, 100 /* ms */); /** @todo Configure mixer buffer? */
166 }
167
168 if (RT_FAILURE(rc))
169 RTTestFailed(g_hTest, "Initializing %s stream failed with %Rrc", enmDir == PDMAUDIODIR_IN ? "input" : "output", rc);
170
171 return rc;
172}
173
174/**
175 * Destroys an audio test stream.
176 *
177 * @returns VBox status code.
178 * @param pTstEnv Test environment the stream to destroy contains.
179 * @param pStream Audio stream to destroy.
180 */
181static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream)
182{
183 int rc = VINF_SUCCESS;
184 if (pStream && pStream->pStream)
185 {
186 /** @todo Anything else to do here, e.g. test if there are left over samples or some such? */
187
188 audioTestDriverStackStreamDestroy(pTstEnv->pDrvStack, pStream->pStream);
189 pStream->pStream = NULL;
190 pStream->pBackend = NULL;
191 }
192
193 AudioTestMixStreamTerm(&pStream->Mix);
194
195 return rc;
196}
197
198
199/*********************************************************************************************************************************
200* Test Primitives *
201*********************************************************************************************************************************/
202
203#if 0 /* Unused */
204/**
205 * Returns a random scheduling hint (in ms).
206 */
207DECLINLINE(uint32_t) audioTestEnvGetRandomSchedulingHint(void)
208{
209 static const unsigned s_aSchedulingHintsMs[] =
210 {
211 10,
212 25,
213 50,
214 100,
215 200,
216 250
217 };
218
219 return s_aSchedulingHintsMs[RTRandU32Ex(0, RT_ELEMENTS(s_aSchedulingHintsMs) - 1)];
220}
221#endif
222
223/**
224 * Plays a test tone on a specific audio test stream.
225 *
226 * @returns VBox status code.
227 * @param pTstEnv Test environment to use for running the test.
228 * Optional and can be NULL (for simple playback only).
229 * @param pStream Stream to use for playing the tone.
230 * @param pParms Tone parameters to use.
231 *
232 * @note Blocking function.
233 */
234int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
235{
236 AUDIOTESTTONE TstTone;
237 AudioTestToneInit(&TstTone, &pStream->Cfg.Props, pParms->dbFreqHz);
238
239 char const *pcszPathOut = NULL;
240 if (pTstEnv)
241 pcszPathOut = pTstEnv->Set.szPathAbs;
242
243 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Playing test tone (tone frequency is %RU16Hz, %RU32ms)\n", (uint16_t)pParms->dbFreqHz, pParms->msDuration);
244 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using %RU32ms stream scheduling hint\n", pStream->Cfg.Device.cMsSchedulingHint);
245 if (pcszPathOut)
246 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Writing to '%s'\n", pcszPathOut);
247
248 int rc;
249
250 /** @todo Use .WAV here? */
251 AUDIOTESTOBJ Obj;
252 RT_ZERO(Obj); /* Shut up MSVC. */
253 if (pTstEnv)
254 {
255 rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "guest-tone-play.pcm", &Obj);
256 AssertRCReturn(rc, rc);
257 }
258
259 rc = AudioTestMixStreamEnable(&pStream->Mix);
260 if ( RT_SUCCESS(rc)
261 && AudioTestMixStreamIsOkay(&pStream->Mix))
262 {
263 uint8_t abBuf[_4K];
264
265 uint32_t cbToPlayTotal = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props, pParms->msDuration);
266 AssertStmt(cbToPlayTotal, rc = VERR_INVALID_PARAMETER);
267 uint32_t cbPlayedTotal = 0;
268
269 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Playing %RU32 bytes total\n", cbToPlayTotal);
270
271 if (pTstEnv)
272 {
273 AudioTestObjAddMetadataStr(Obj, "stream_to_play_bytes=%RU32\n", cbToPlayTotal);
274 AudioTestObjAddMetadataStr(Obj, "stream_period_size_frames=%RU32\n", pStream->Cfg.Backend.cFramesPeriod);
275 AudioTestObjAddMetadataStr(Obj, "stream_buffer_size_frames=%RU32\n", pStream->Cfg.Backend.cFramesBufferSize);
276 AudioTestObjAddMetadataStr(Obj, "stream_prebuf_size_frames=%RU32\n", pStream->Cfg.Backend.cFramesPreBuffering);
277 /* Note: This mostly is provided by backend (e.g. PulseAudio / ALSA / ++) and
278 * has nothing to do with the device emulation scheduling hint. */
279 AudioTestObjAddMetadataStr(Obj, "device_scheduling_hint_ms=%RU32\n", pStream->Cfg.Device.cMsSchedulingHint);
280 }
281
282 PAUDIOTESTDRVMIXSTREAM pMix = &pStream->Mix;
283
284 uint32_t const cbPreBuffer = PDMAudioPropsFramesToBytes(pMix->pProps, pStream->Cfg.Backend.cFramesPreBuffering);
285 uint64_t const nsStarted = RTTimeNanoTS();
286 uint64_t nsDonePreBuffering = 0;
287
288 uint64_t offStream = 0;
289 uint64_t nsTimeout = RT_MS_5MIN_64 * RT_NS_1MS;
290
291 while (cbPlayedTotal < cbToPlayTotal)
292 {
293 /* Pace ourselves a little. */
294 if (offStream >= cbPreBuffer)
295 {
296 if (!nsDonePreBuffering)
297 nsDonePreBuffering = RTTimeNanoTS();
298 uint64_t const cNsWritten = PDMAudioPropsBytesToNano64(pMix->pProps, offStream - cbPreBuffer);
299 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStarted;
300 if (cNsWritten > cNsElapsed + RT_NS_10MS)
301 RTThreadSleep((cNsWritten - cNsElapsed - RT_NS_10MS / 2) / RT_NS_1MS);
302 }
303
304 uint32_t cbPlayed = 0;
305 uint32_t const cbCanWrite = AudioTestMixStreamGetWritable(&pStream->Mix);
306 if (cbCanWrite)
307 {
308 uint32_t const cbToGenerate = RT_MIN(RT_MIN(cbToPlayTotal - cbPlayedTotal, sizeof(abBuf)), cbCanWrite);
309 uint32_t cbToPlay;
310 rc = AudioTestToneGenerate(&TstTone, abBuf, cbToGenerate, &cbToPlay);
311 if (RT_SUCCESS(rc))
312 {
313 if (pTstEnv)
314 {
315 /* Write stuff to disk before trying to play it. Help analysis later. */
316 rc = AudioTestObjWrite(Obj, abBuf, cbToPlay);
317 }
318 if (RT_SUCCESS(rc))
319 {
320 rc = AudioTestMixStreamPlay(&pStream->Mix, abBuf, cbToPlay, &cbPlayed);
321 if (RT_SUCCESS(rc))
322 {
323 AssertBreakStmt(cbPlayed <= cbToPlay, rc = VERR_TOO_MUCH_DATA);
324
325 offStream += cbPlayed;
326
327 if (cbPlayed != cbToPlay)
328 RTTestFailed(g_hTest, "Only played %RU32/%RU32 bytes", cbPlayed, cbToPlay);
329 }
330 }
331 }
332
333 if (RT_FAILURE(rc))
334 break;
335 }
336 else if (AudioTestMixStreamIsOkay(&pStream->Mix))
337 RTThreadSleep(RT_MIN(RT_MAX(1, pStream->Cfg.Device.cMsSchedulingHint), 256));
338 else
339 AssertFailedBreakStmt(rc = VERR_AUDIO_STREAM_NOT_READY);
340
341 cbPlayedTotal += cbPlayed;
342 AssertBreakStmt(cbPlayedTotal <= cbToPlayTotal, VERR_BUFFER_OVERFLOW);
343
344 /* Fail-safe in case something screwed up while playing back. */
345 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStarted;
346 if (cNsElapsed > nsTimeout)
347 {
348 RTTestFailed(g_hTest, "Playback took too long (runng %RU64 vs. timeout %RU64), aborting\n", cNsElapsed, nsTimeout);
349 rc = VERR_TIMEOUT;
350 }
351
352 if (RT_FAILURE(rc))
353 break;
354 }
355
356 if (cbPlayedTotal != cbToPlayTotal)
357 RTTestFailed(g_hTest, "Playback ended unexpectedly (%RU32/%RU32 played)\n", cbPlayedTotal, cbToPlayTotal);
358
359 if (RT_SUCCESS(rc))
360 {
361 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Draining stream ...\n");
362 rc = AudioTestMixStreamDrain(&pStream->Mix, true /*fSync*/);
363 }
364 }
365 else
366 rc = VERR_AUDIO_STREAM_NOT_READY;
367
368 if (pTstEnv)
369 {
370 int rc2 = AudioTestObjClose(Obj);
371 if (RT_SUCCESS(rc))
372 rc = rc2;
373 }
374
375 if (RT_FAILURE(rc))
376 RTTestFailed(g_hTest, "Playing tone failed with %Rrc\n", rc);
377
378 return rc;
379}
380
381/**
382 * Records a test tone from a specific audio test stream.
383 *
384 * @returns VBox status code.
385 * @param pTstEnv Test environment to use for running the test.
386 * @param pStream Stream to use for recording the tone.
387 * @param pParms Tone parameters to use.
388 *
389 * @note Blocking function.
390 */
391static int audioTestRecordTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
392{
393 const char *pcszPathOut = pTstEnv->Set.szPathAbs;
394
395 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Recording test tone (tone frequency is %RU16Hz, %RU32ms)\n", (uint16_t)pParms->dbFreqHz, pParms->msDuration);
396 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Writing to '%s'\n", pcszPathOut);
397
398 /** @todo Use .WAV here? */
399 AUDIOTESTOBJ Obj;
400 int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "guest-tone-rec.pcm", &Obj);
401 AssertRCReturn(rc, rc);
402
403 PAUDIOTESTDRVMIXSTREAM pMix = &pStream->Mix;
404
405 rc = AudioTestMixStreamEnable(pMix);
406 if (RT_SUCCESS(rc))
407 {
408 uint64_t cbToRecTotal = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props, pParms->msDuration);
409
410 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Recording %RU32 bytes total\n", cbToRecTotal);
411
412 AudioTestObjAddMetadataStr(Obj, "stream_to_record_bytes=%RU32\n", cbToRecTotal);
413 AudioTestObjAddMetadataStr(Obj, "stream_buffer_size_ms=%RU32\n", pTstEnv->cMsBufferSize);
414 AudioTestObjAddMetadataStr(Obj, "stream_prebuf_size_ms=%RU32\n", pTstEnv->cMsPreBuffer);
415 /* Note: This mostly is provided by backend (e.g. PulseAudio / ALSA / ++) and
416 * has nothing to do with the device emulation scheduling hint. */
417 AudioTestObjAddMetadataStr(Obj, "device_scheduling_hint_ms=%RU32\n", pTstEnv->cMsSchedulingHint);
418
419 uint8_t abSamples[16384];
420 uint32_t const cbSamplesAligned = PDMAudioPropsFloorBytesToFrame(pMix->pProps, sizeof(abSamples));
421 uint64_t cbRecTotal = 0;
422 while (!g_fTerminate && cbRecTotal < cbToRecTotal)
423 {
424 /*
425 * Anything we can read?
426 */
427 uint32_t const cbCanRead = AudioTestMixStreamGetReadable(pMix);
428 if (cbCanRead)
429 {
430 uint32_t const cbToRead = RT_MIN(cbCanRead, cbSamplesAligned);
431 uint32_t cbRecorded = 0;
432 rc = AudioTestMixStreamCapture(pMix, abSamples, cbToRead, &cbRecorded);
433 if (RT_SUCCESS(rc))
434 {
435 if (cbRecorded)
436 {
437 rc = AudioTestObjWrite(Obj, abSamples, cbRecorded);
438 if (RT_SUCCESS(rc))
439 {
440 cbRecTotal += cbRecorded;
441
442 /** @todo Clamp result? */
443 }
444 }
445 }
446 }
447 else if (AudioTestMixStreamIsOkay(pMix))
448 RTThreadSleep(RT_MIN(RT_MAX(1, pTstEnv->cMsSchedulingHint), 256));
449
450 if (RT_FAILURE(rc))
451 break;
452 }
453
454 int rc2 = AudioTestMixStreamDisable(pMix);
455 if (RT_SUCCESS(rc))
456 rc = rc2;
457 }
458
459 int rc2 = AudioTestObjClose(Obj);
460 if (RT_SUCCESS(rc))
461 rc = rc2;
462
463 if (RT_FAILURE(rc))
464 RTTestFailed(g_hTest, "Recording tone done failed with %Rrc\n", rc);
465
466 return rc;
467}
468
469
470/*********************************************************************************************************************************
471* ATS Callback Implementations *
472*********************************************************************************************************************************/
473
474/** @copydoc ATSCALLBACKS::pfnHowdy
475 *
476 * @note Runs as part of the guest ATS.
477 */
478static DECLCALLBACK(int) audioTestGstAtsHowdyCallback(void const *pvUser)
479{
480 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
481
482 AssertReturn(pCtx->cClients <= UINT8_MAX - 1, VERR_BUFFER_OVERFLOW);
483
484 pCtx->cClients++;
485
486 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "New client connected, now %RU8 total\n", pCtx->cClients);
487
488 return VINF_SUCCESS;
489}
490
491/** @copydoc ATSCALLBACKS::pfnBye
492 *
493 * @note Runs as part of the guest ATS.
494 */
495static DECLCALLBACK(int) audioTestGstAtsByeCallback(void const *pvUser)
496{
497 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
498
499 AssertReturn(pCtx->cClients, VERR_WRONG_ORDER);
500 pCtx->cClients--;
501
502 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Client wants to disconnect, %RU8 remaining\n", pCtx->cClients);
503
504 if (0 == pCtx->cClients) /* All clients disconnected? Tear things down. */
505 {
506 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Last client disconnected, terminating server ...\n");
507 ASMAtomicWriteBool(&g_fTerminate, true);
508 }
509
510 return VINF_SUCCESS;
511}
512
513/** @copydoc ATSCALLBACKS::pfnTestSetBegin
514 *
515 * @note Runs as part of the guest ATS.
516 */
517static DECLCALLBACK(int) audioTestGstAtsTestSetBeginCallback(void const *pvUser, const char *pszTag)
518{
519 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
520 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
521
522 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Got request for beginning test set '%s' in '%s'\n", pszTag, pTstEnv->szPathTemp);
523
524 return AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, pszTag);
525}
526
527/** @copydoc ATSCALLBACKS::pfnTestSetEnd
528 *
529 * @note Runs as part of the guest ATS.
530 */
531static DECLCALLBACK(int) audioTestGstAtsTestSetEndCallback(void const *pvUser, const char *pszTag)
532{
533 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
534 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
535
536 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Got request for ending test set '%s'\n", pszTag);
537
538 /* Pack up everything to be ready for transmission. */
539 return audioTestEnvPrologue(pTstEnv, true /* fPack */, pCtx->szTestSetArchive, sizeof(pCtx->szTestSetArchive));
540}
541
542/** @copydoc ATSCALLBACKS::pfnTonePlay
543 *
544 * @note Runs as part of the guest ATS.
545 */
546static DECLCALLBACK(int) audioTestGstAtsTonePlayCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
547{
548 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
549 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
550
551 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Got request for playing test tone (%RU16Hz, %RU32ms) ...\n",
552 (uint16_t)pToneParms->dbFreqHz, pToneParms->msDuration);
553
554 const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
555
556 int rc = audioTestStreamInit(pTstEnv->pDrvStack, pTstStream, PDMAUDIODIR_OUT, &pTstEnv->Props, false /* fWithMixer */,
557 pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer, pTstEnv->cMsSchedulingHint);
558 if (RT_SUCCESS(rc))
559 {
560 AUDIOTESTPARMS TstParms;
561 RT_ZERO(TstParms);
562 TstParms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
563 TstParms.enmDir = PDMAUDIODIR_OUT;
564 TstParms.TestTone = *pToneParms;
565
566 PAUDIOTESTENTRY pTst;
567 rc = AudioTestSetTestBegin(&pTstEnv->Set, "Playing test tone", &TstParms, &pTst);
568 if (RT_SUCCESS(rc))
569 {
570 rc = audioTestPlayTone(pTstEnv, pTstStream, pToneParms);
571 if (RT_SUCCESS(rc))
572 {
573 AudioTestSetTestDone(pTst);
574 }
575 else
576 AudioTestSetTestFailed(pTst, rc, "Playing tone failed");
577 }
578
579 int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
580 if (RT_SUCCESS(rc))
581 rc = rc2;
582 }
583 else
584 RTTestFailed(g_hTest, "Error creating output stream, rc=%Rrc\n", rc);
585
586 return rc;
587}
588
589/** @copydoc ATSCALLBACKS::pfnToneRecord */
590static DECLCALLBACK(int) audioTestGstAtsToneRecordCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
591{
592 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
593 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
594
595 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Got request for recording test tone (%RU32ms) ...\n", pToneParms->msDuration);
596
597 const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
598
599 int rc = audioTestStreamInit(pTstEnv->pDrvStack, pTstStream, PDMAUDIODIR_IN, &pTstEnv->Props, false /* fWithMixer */,
600 pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer, pTstEnv->cMsSchedulingHint);
601 if (RT_SUCCESS(rc))
602 {
603 AUDIOTESTPARMS TstParms;
604 RT_ZERO(TstParms);
605 TstParms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
606 TstParms.enmDir = PDMAUDIODIR_IN;
607 TstParms.Props = pToneParms->Props;
608 TstParms.TestTone = *pToneParms;
609
610 PAUDIOTESTENTRY pTst;
611 rc = AudioTestSetTestBegin(&pTstEnv->Set, "Recording test tone from host", &TstParms, &pTst);
612 if (RT_SUCCESS(rc))
613 {
614 rc = audioTestRecordTone(pTstEnv, pTstStream, pToneParms);
615 if (RT_SUCCESS(rc))
616 {
617 AudioTestSetTestDone(pTst);
618 }
619 else
620 AudioTestSetTestFailed(pTst, rc, "Recording tone failed");
621 }
622
623 int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
624 if (RT_SUCCESS(rc))
625 rc = rc2;
626 }
627 else
628 RTTestFailed(g_hTest, "Error creating input stream, rc=%Rrc\n", rc);
629
630 return rc;
631}
632
633/** @copydoc ATSCALLBACKS::pfnTestSetSendBegin */
634static DECLCALLBACK(int) audioTestGstAtsTestSetSendBeginCallback(void const *pvUser, const char *pszTag)
635{
636 RT_NOREF(pszTag);
637
638 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
639
640 if (!RTFileExists(pCtx->szTestSetArchive)) /* Has the archive successfully been created yet? */
641 return VERR_WRONG_ORDER;
642
643 int rc = RTFileOpen(&pCtx->hTestSetArchive, pCtx->szTestSetArchive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
644 if (RT_SUCCESS(rc))
645 {
646 uint64_t uSize;
647 rc = RTFileQuerySize(pCtx->hTestSetArchive, &uSize);
648 if (RT_SUCCESS(rc))
649 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Sending test set '%s' (%zu bytes)\n", pCtx->szTestSetArchive, uSize);
650 }
651
652 return rc;
653}
654
655/** @copydoc ATSCALLBACKS::pfnTestSetSendRead */
656static DECLCALLBACK(int) audioTestGstAtsTestSetSendReadCallback(void const *pvUser,
657 const char *pszTag, void *pvBuf, size_t cbBuf, size_t *pcbRead)
658{
659 RT_NOREF(pszTag);
660
661 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
662
663 return RTFileRead(pCtx->hTestSetArchive, pvBuf, cbBuf, pcbRead);
664}
665
666/** @copydoc ATSCALLBACKS::pfnTestSetSendEnd */
667static DECLCALLBACK(int) audioTestGstAtsTestSetSendEndCallback(void const *pvUser, const char *pszTag)
668{
669 RT_NOREF(pszTag);
670
671 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
672
673 int rc = RTFileClose(pCtx->hTestSetArchive);
674 if (RT_SUCCESS(rc))
675 {
676 pCtx->hTestSetArchive = NIL_RTFILE;
677 }
678
679 return rc;
680}
681
682
683/*********************************************************************************************************************************
684* Implementation of audio test environment handling *
685*********************************************************************************************************************************/
686
687/**
688 * Connects an ATS client via TCP/IP to a peer.
689 *
690 * @returns VBox status code.
691 * @param pTstEnv Test environment to use.
692 * @param pClient Client to connect.
693 * @param pszWhat Hint of what to connect to where.
694 * @param pTcpOpts Pointer to TCP options to use.
695 */
696int audioTestEnvConnectViaTcp(PAUDIOTESTENV pTstEnv, PATSCLIENT pClient, const char *pszWhat, PAUDIOTESTENVTCPOPTS pTcpOpts)
697{
698 RT_NOREF(pTstEnv);
699
700 RTGETOPTUNION Val;
701 RT_ZERO(Val);
702
703 Val.u32 = pTcpOpts->enmConnMode;
704 int rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_CONN_MODE, &Val);
705 AssertRCReturn(rc, rc);
706
707 if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
708 || pTcpOpts->enmConnMode == ATSCONNMODE_SERVER)
709 {
710 Assert(pTcpOpts->uBindPort); /* Always set by the caller. */
711 Val.u16 = pTcpOpts->uBindPort;
712 rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_BIND_PORT, &Val);
713 AssertRCReturn(rc, rc);
714
715 if (pTcpOpts->szBindAddr[0])
716 {
717 Val.psz = pTcpOpts->szBindAddr;
718 rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_BIND_ADDRESS, &Val);
719 AssertRCReturn(rc, rc);
720 }
721 else
722 {
723 RTTestFailed(g_hTest, "No bind address specified!\n");
724 return VERR_INVALID_PARAMETER;
725 }
726
727 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connecting %s by listening as server at %s:%RU32 ...\n",
728 pszWhat, pTcpOpts->szBindAddr, pTcpOpts->uBindPort);
729 }
730
731
732 if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
733 || pTcpOpts->enmConnMode == ATSCONNMODE_CLIENT)
734 {
735 Assert(pTcpOpts->uConnectPort); /* Always set by the caller. */
736 Val.u16 = pTcpOpts->uConnectPort;
737 rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_CONNECT_PORT, &Val);
738 AssertRCReturn(rc, rc);
739
740 if (pTcpOpts->szConnectAddr[0])
741 {
742 Val.psz = pTcpOpts->szConnectAddr;
743 rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_CONNECT_ADDRESS, &Val);
744 AssertRCReturn(rc, rc);
745 }
746 else
747 {
748 RTTestFailed(g_hTest, "No connect address specified!\n");
749 return VERR_INVALID_PARAMETER;
750 }
751
752 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connecting %s by connecting as client to %s:%RU32 ...\n",
753 pszWhat, pTcpOpts->szConnectAddr, pTcpOpts->uConnectPort);
754 }
755
756 rc = AudioTestSvcClientConnect(pClient);
757 if (RT_FAILURE(rc))
758 {
759 RTTestFailed(g_hTest, "Connecting %s failed with %Rrc\n", pszWhat, rc);
760 return rc;
761 }
762
763 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Successfully connected %s\n", pszWhat);
764 return rc;
765}
766
767/**
768 * Configures and starts an ATS TCP/IP server.
769 *
770 * @returns VBox status code.
771 * @param pSrv ATS server instance to configure and start.
772 * @param pCallbacks ATS callback table to use.
773 * @param pszDesc Hint of server type which is being started.
774 * @param pTcpOpts TCP options to use.
775 */
776int audioTestEnvConfigureAndStartTcpServer(PATSSERVER pSrv, PCATSCALLBACKS pCallbacks, const char *pszDesc,
777 PAUDIOTESTENVTCPOPTS pTcpOpts)
778{
779 RTGETOPTUNION Val;
780 RT_ZERO(Val);
781
782 int rc = AudioTestSvcInit(pSrv, pCallbacks);
783 if (RT_FAILURE(rc))
784 return rc;
785
786 Val.u32 = pTcpOpts->enmConnMode;
787 rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_CONN_MODE, &Val);
788 AssertRCReturn(rc, rc);
789
790 if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
791 || pTcpOpts->enmConnMode == ATSCONNMODE_SERVER)
792 {
793 Assert(pTcpOpts->uBindPort); /* Always set by the caller. */
794 Val.u16 = pTcpOpts->uBindPort;
795 rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_BIND_PORT, &Val);
796 AssertRCReturn(rc, rc);
797
798 if (pTcpOpts->szBindAddr[0])
799 {
800 Val.psz = pTcpOpts->szBindAddr;
801 rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_BIND_ADDRESS, &Val);
802 AssertRCReturn(rc, rc);
803 }
804 else
805 {
806 RTTestFailed(g_hTest, "No bind address specified!\n");
807 return VERR_INVALID_PARAMETER;
808 }
809
810 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Starting server for %s at %s:%RU32 ...\n",
811 pszDesc, pTcpOpts->szBindAddr, pTcpOpts->uBindPort);
812 }
813
814
815 if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
816 || pTcpOpts->enmConnMode == ATSCONNMODE_CLIENT)
817 {
818 Assert(pTcpOpts->uConnectPort); /* Always set by the caller. */
819 Val.u16 = pTcpOpts->uConnectPort;
820 rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_CONNECT_PORT, &Val);
821 AssertRCReturn(rc, rc);
822
823 if (pTcpOpts->szConnectAddr[0])
824 {
825 Val.psz = pTcpOpts->szConnectAddr;
826 rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_CONNECT_ADDRESS, &Val);
827 AssertRCReturn(rc, rc);
828 }
829 else
830 {
831 RTTestFailed(g_hTest, "No connect address specified!\n");
832 return VERR_INVALID_PARAMETER;
833 }
834
835 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Starting server for %s by connecting as client to %s:%RU32 ...\n",
836 pszDesc, pTcpOpts->szConnectAddr, pTcpOpts->uConnectPort);
837 }
838
839 if (RT_SUCCESS(rc))
840 {
841 rc = AudioTestSvcStart(pSrv);
842 if (RT_FAILURE(rc))
843 RTTestFailed(g_hTest, "Starting server for %s failed with %Rrc\n", pszDesc, rc);
844 }
845
846 return rc;
847}
848
849/**
850 * Initializes an audio test environment.
851 *
852 * @returns VBox status code.
853 * @param pTstEnv Audio test environment to initialize.
854 * @param pDrvStack Driver stack to use.
855 */
856int audioTestEnvInit(PAUDIOTESTENV pTstEnv, PAUDIOTESTDRVSTACK pDrvStack)
857{
858 int rc = VINF_SUCCESS;
859
860 pTstEnv->pDrvStack = pDrvStack;
861
862 /*
863 * Set sane defaults if not already set.
864 */
865 if (!RTStrNLen(pTstEnv->szTag, sizeof(pTstEnv->szTag)))
866 {
867 rc = AudioTestGenTag(pTstEnv->szTag, sizeof(pTstEnv->szTag));
868 AssertRCReturn(rc, rc);
869 }
870
871 if (!RTStrNLen(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp)))
872 {
873 rc = AudioTestPathGetTemp(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp));
874 AssertRCReturn(rc, rc);
875 }
876
877 if (!RTStrNLen(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut)))
878 {
879 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), pTstEnv->szPathTemp, "vkat-temp");
880 AssertRCReturn(rc, rc);
881 }
882
883 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Initializing environment for mode '%s'\n", pTstEnv->enmMode == AUDIOTESTMODE_HOST ? "host" : "guest");
884 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pTstEnv->szTag);
885 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut);
886 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp);
887
888 if (!pTstEnv->cMsBufferSize)
889 pTstEnv->cMsBufferSize = UINT32_MAX;
890 if (!pTstEnv->cMsPreBuffer)
891 pTstEnv->cMsPreBuffer = UINT32_MAX;
892 if (!pTstEnv->cMsSchedulingHint)
893 pTstEnv->cMsSchedulingHint = UINT32_MAX;
894
895 char szPathTemp[RTPATH_MAX];
896 if ( !strlen(pTstEnv->szPathTemp)
897 || !strlen(pTstEnv->szPathOut))
898 rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
899
900 if ( RT_SUCCESS(rc)
901 && !strlen(pTstEnv->szPathTemp))
902 rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp");
903
904 if (RT_SUCCESS(rc))
905 {
906 rc = RTDirCreate(pTstEnv->szPathTemp, RTFS_UNIX_IRWXU, 0 /* fFlags */);
907 if (rc == VERR_ALREADY_EXISTS)
908 rc = VINF_SUCCESS;
909 }
910
911 if ( RT_SUCCESS(rc)
912 && !strlen(pTstEnv->szPathOut))
913 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat");
914
915 if (RT_SUCCESS(rc))
916 {
917 rc = RTDirCreate(pTstEnv->szPathOut, RTFS_UNIX_IRWXU, 0 /* fFlags */);
918 if (rc == VERR_ALREADY_EXISTS)
919 rc = VINF_SUCCESS;
920 }
921
922 if (RT_FAILURE(rc))
923 return rc;
924
925 /**
926 * For NAT'ed VMs we use (default):
927 * - client mode (uConnectAddr / uConnectPort) on the guest.
928 * - server mode (uBindAddr / uBindPort) on the host.
929 */
930 if ( !pTstEnv->TcpOpts.szConnectAddr[0]
931 && !pTstEnv->TcpOpts.szBindAddr[0])
932 RTStrCopy(pTstEnv->TcpOpts.szBindAddr, sizeof(pTstEnv->TcpOpts.szBindAddr), "0.0.0.0");
933
934 /*
935 * Determine connection mode based on set variables.
936 */
937 if ( pTstEnv->TcpOpts.szBindAddr[0]
938 && pTstEnv->TcpOpts.szConnectAddr[0])
939 {
940 pTstEnv->TcpOpts.enmConnMode = ATSCONNMODE_BOTH;
941 }
942 else if (pTstEnv->TcpOpts.szBindAddr[0])
943 pTstEnv->TcpOpts.enmConnMode = ATSCONNMODE_SERVER;
944 else /* "Reversed mode", i.e. used for NATed VMs. */
945 pTstEnv->TcpOpts.enmConnMode = ATSCONNMODE_CLIENT;
946
947 /* Set a back reference to the test environment for the callback context. */
948 pTstEnv->CallbackCtx.pTstEnv = pTstEnv;
949
950 ATSCALLBACKS Callbacks;
951 RT_ZERO(Callbacks);
952 Callbacks.pvUser = &pTstEnv->CallbackCtx;
953
954 if (pTstEnv->enmMode == AUDIOTESTMODE_GUEST)
955 {
956 Callbacks.pfnHowdy = audioTestGstAtsHowdyCallback;
957 Callbacks.pfnBye = audioTestGstAtsByeCallback;
958 Callbacks.pfnTestSetBegin = audioTestGstAtsTestSetBeginCallback;
959 Callbacks.pfnTestSetEnd = audioTestGstAtsTestSetEndCallback;
960 Callbacks.pfnTonePlay = audioTestGstAtsTonePlayCallback;
961 Callbacks.pfnToneRecord = audioTestGstAtsToneRecordCallback;
962 Callbacks.pfnTestSetSendBegin = audioTestGstAtsTestSetSendBeginCallback;
963 Callbacks.pfnTestSetSendRead = audioTestGstAtsTestSetSendReadCallback;
964 Callbacks.pfnTestSetSendEnd = audioTestGstAtsTestSetSendEndCallback;
965
966 if (!pTstEnv->TcpOpts.uBindPort)
967 pTstEnv->TcpOpts.uBindPort = ATS_TCP_DEF_BIND_PORT_GUEST;
968
969 if (!pTstEnv->TcpOpts.uConnectPort)
970 pTstEnv->TcpOpts.uConnectPort = ATS_TCP_DEF_CONNECT_PORT_GUEST;
971
972 pTstEnv->pSrv = (PATSSERVER)RTMemAlloc(sizeof(ATSSERVER));
973 AssertPtrReturn(pTstEnv->pSrv, VERR_NO_MEMORY);
974
975 /*
976 * Start the ATS (Audio Test Service) on the guest side.
977 * That service then will perform playback and recording operations on the guest, triggered from the host.
978 *
979 * When running this in self-test mode, that service also can be run on the host if nothing else is specified.
980 * Note that we have to bind to "0.0.0.0" by default so that the host can connect to it.
981 */
982 rc = audioTestEnvConfigureAndStartTcpServer(pTstEnv->pSrv, &Callbacks, "guest", &pTstEnv->TcpOpts);
983 }
984 else /* Host mode */
985 {
986 if (!pTstEnv->TcpOpts.uBindPort)
987 pTstEnv->TcpOpts.uBindPort = ATS_TCP_DEF_BIND_PORT_HOST;
988
989 if (!pTstEnv->TcpOpts.uConnectPort)
990 pTstEnv->TcpOpts.uConnectPort = ATS_TCP_DEF_CONNECT_PORT_HOST_PORT_FWD;
991
992 /**
993 * Note: Don't set pTstEnv->TcpOpts.szTcpConnectAddr by default here, as this specifies what connection mode
994 * (client / server / both) we use on the host.
995 */
996
997 /* We need to start a server on the host so that VMs configured with NAT networking
998 * can connect to it as well. */
999 rc = AudioTestSvcClientCreate(&pTstEnv->u.Host.AtsClGuest);
1000 if (RT_SUCCESS(rc))
1001 rc = audioTestEnvConnectViaTcp(pTstEnv, &pTstEnv->u.Host.AtsClGuest,
1002 "host -> guest", &pTstEnv->TcpOpts);
1003 if (RT_SUCCESS(rc))
1004 {
1005 AUDIOTESTENVTCPOPTS ValKitTcpOpts;
1006 RT_ZERO(ValKitTcpOpts);
1007
1008 /* We only connect as client to the Validation Kit audio driver ATS. */
1009 ValKitTcpOpts.enmConnMode = ATSCONNMODE_CLIENT;
1010
1011 /* For now we ASSUME that the Validation Kit audio driver ATS runs on the same host as VKAT (this binary) runs on. */
1012 ValKitTcpOpts.uConnectPort = ATS_TCP_DEF_CONNECT_PORT_VALKIT; /** @todo Make this dynamic. */
1013 RTStrCopy(ValKitTcpOpts.szConnectAddr, sizeof(ValKitTcpOpts.szConnectAddr), ATS_TCP_DEF_CONNECT_HOST_ADDR_STR); /** @todo Ditto. */
1014
1015 rc = AudioTestSvcClientCreate(&pTstEnv->u.Host.AtsClValKit);
1016 if (RT_SUCCESS(rc))
1017 {
1018 rc = audioTestEnvConnectViaTcp(pTstEnv, &pTstEnv->u.Host.AtsClValKit,
1019 "host -> valkit", &ValKitTcpOpts);
1020 if (RT_FAILURE(rc))
1021 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Unable to connect to the Validation Kit audio driver!\n"
1022 "There could be multiple reasons:\n\n"
1023 " - Wrong host being used\n"
1024 " - VirtualBox host version is too old\n"
1025 " - Audio debug mode is not enabled\n"
1026 " - Support for Validation Kit audio driver is not included\n"
1027 " - Firewall / network configuration problem\n");
1028 }
1029 }
1030 }
1031
1032 return rc;
1033}
1034
1035/**
1036 * Destroys an audio test environment.
1037 *
1038 * @param pTstEnv Audio test environment to destroy.
1039 */
1040void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv)
1041{
1042 if (!pTstEnv)
1043 return;
1044
1045 /* When in host mode, we need to destroy our ATS clients in order to also let
1046 * the ATS server(s) know we're going to quit. */
1047 if (pTstEnv->enmMode == AUDIOTESTMODE_HOST)
1048 {
1049 AudioTestSvcClientDestroy(&pTstEnv->u.Host.AtsClValKit);
1050 AudioTestSvcClientDestroy(&pTstEnv->u.Host.AtsClGuest);
1051 }
1052
1053 if (pTstEnv->pSrv)
1054 {
1055 int rc2 = AudioTestSvcDestroy(pTstEnv->pSrv);
1056 AssertRC(rc2);
1057
1058 RTMemFree(pTstEnv->pSrv);
1059 pTstEnv->pSrv = NULL;
1060 }
1061
1062 for (unsigned i = 0; i < RT_ELEMENTS(pTstEnv->aStreams); i++)
1063 {
1064 int rc2 = audioTestStreamDestroy(pTstEnv, &pTstEnv->aStreams[i]);
1065 if (RT_FAILURE(rc2))
1066 RTTestFailed(g_hTest, "Stream destruction for stream #%u failed with %Rrc\n", i, rc2);
1067 }
1068
1069 /* Try cleaning up a bit. */
1070 RTDirRemove(pTstEnv->szPathTemp);
1071 RTDirRemove(pTstEnv->szPathOut);
1072
1073 pTstEnv->pDrvStack = NULL;
1074}
1075
1076/**
1077 * Closes, packs up and destroys a test environment.
1078 *
1079 * @returns VBox status code.
1080 * @param pTstEnv Test environment to handle.
1081 * @param fPack Whether to pack the test set up before destroying / wiping it.
1082 * @param pszPackFile Where to store the packed test set file on success. Can be NULL if \a fPack is \c false.
1083 * @param cbPackFile Size (in bytes) of \a pszPackFile. Can be 0 if \a fPack is \c false.
1084 */
1085int audioTestEnvPrologue(PAUDIOTESTENV pTstEnv, bool fPack, char *pszPackFile, size_t cbPackFile)
1086{
1087 /* Close the test set first. */
1088 AudioTestSetClose(&pTstEnv->Set);
1089
1090 int rc = VINF_SUCCESS;
1091
1092 if (fPack)
1093 {
1094 /* Before destroying the test environment, pack up the test set so
1095 * that it's ready for transmission. */
1096 rc = AudioTestSetPack(&pTstEnv->Set, pTstEnv->szPathOut, pszPackFile, cbPackFile);
1097 if (RT_SUCCESS(rc))
1098 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set packed up to '%s'\n", pszPackFile);
1099 }
1100
1101 if (!g_fDrvAudioDebug) /* Don't wipe stuff when debugging. Can be useful for introspecting data. */
1102 /* ignore rc */ AudioTestSetWipe(&pTstEnv->Set);
1103
1104 AudioTestSetDestroy(&pTstEnv->Set);
1105
1106 if (RT_FAILURE(rc))
1107 RTTestFailed(g_hTest, "Test set prologue failed with %Rrc\n", rc);
1108
1109 return rc;
1110}
1111
1112/**
1113 * Initializes an audio test parameters set.
1114 *
1115 * @param pTstParms Test parameters set to initialize.
1116 */
1117void audioTestParmsInit(PAUDIOTESTPARMS pTstParms)
1118{
1119 RT_ZERO(*pTstParms);
1120}
1121
1122/**
1123 * Destroys an audio test parameters set.
1124 *
1125 * @param pTstParms Test parameters set to destroy.
1126 */
1127void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms)
1128{
1129 if (!pTstParms)
1130 return;
1131
1132 return;
1133}
1134
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