VirtualBox

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

Last change on this file since 91184 was 91184, 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: 43.0 KB
Line 
1/* $Id: vkatCommon.cpp 91184 2021-09-09 17:09:38Z 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 uint64_t nsLastMsgCantWrite = 0; /* Timestamp (in ns) when the last message of an unwritable stream was shown. */
291
292 while (cbPlayedTotal < cbToPlayTotal)
293 {
294 uint64_t const nsNow = RTTimeNanoTS();
295
296 /* Pace ourselves a little. */
297 if (offStream >= cbPreBuffer)
298 {
299 if (!nsDonePreBuffering)
300 nsDonePreBuffering = nsNow;
301 uint64_t const cNsWritten = PDMAudioPropsBytesToNano64(pMix->pProps, offStream - cbPreBuffer);
302 uint64_t const cNsElapsed = nsNow - nsStarted;
303 if (cNsWritten > cNsElapsed + RT_NS_10MS)
304 RTThreadSleep((cNsWritten - cNsElapsed - RT_NS_10MS / 2) / RT_NS_1MS);
305 }
306
307 uint32_t cbPlayed = 0;
308 AudioTestMixStreamGetWritable(&pStream->Mix);
309 uint32_t const cbCanWrite = PDMAudioPropsMilliToBytes(pMix->pProps, pStream->Cfg.Device.cMsSchedulingHint);
310 if (cbCanWrite)
311 {
312 uint32_t const cbToGenerate = RT_MIN(RT_MIN(cbToPlayTotal - cbPlayedTotal, sizeof(abBuf)), cbCanWrite);
313 uint32_t cbToPlay;
314 rc = AudioTestToneGenerate(&TstTone, abBuf, cbToGenerate, &cbToPlay);
315 if (RT_SUCCESS(rc))
316 {
317 if (pTstEnv)
318 {
319 /* Write stuff to disk before trying to play it. Help analysis later. */
320 rc = AudioTestObjWrite(Obj, abBuf, cbToPlay);
321 }
322 if (RT_SUCCESS(rc))
323 {
324 rc = AudioTestMixStreamPlay(&pStream->Mix, abBuf, cbToPlay, &cbPlayed);
325 if (RT_SUCCESS(rc))
326 {
327 AssertBreakStmt(cbPlayed <= cbToPlay, rc = VERR_TOO_MUCH_DATA);
328
329 offStream += cbPlayed;
330
331 if (cbPlayed != cbToPlay)
332 RTTestFailed(g_hTest, "Only played %RU32/%RU32 bytes", cbPlayed, cbToPlay);
333 }
334 }
335 }
336
337 if (RT_FAILURE(rc))
338 break;
339
340 nsLastMsgCantWrite = 0;
341 }
342 else if (AudioTestMixStreamIsOkay(&pStream->Mix))
343 {
344 if (!nsLastMsgCantWrite || nsNow - nsLastMsgCantWrite > RT_NS_10SEC) /* Don't spam the output too much. */
345 {
346 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Waiting for stream to be writable again ...\n");
347 nsLastMsgCantWrite = nsNow;
348 }
349
350 RTThreadSleep(RT_MIN(RT_MAX(1, pStream->Cfg.Device.cMsSchedulingHint), 256));
351 }
352 else
353 AssertFailedBreakStmt(rc = VERR_AUDIO_STREAM_NOT_READY);
354
355 cbPlayedTotal += cbPlayed;
356 AssertBreakStmt(cbPlayedTotal <= cbToPlayTotal, VERR_BUFFER_OVERFLOW);
357
358 /* Fail-safe in case something screwed up while playing back. */
359 uint64_t const cNsElapsed = nsNow - nsStarted;
360 if (cNsElapsed > nsTimeout)
361 {
362 RTTestFailed(g_hTest, "Playback took too long (runng %RU64 vs. timeout %RU64), aborting\n", cNsElapsed, nsTimeout);
363 rc = VERR_TIMEOUT;
364 }
365
366 if (RT_FAILURE(rc))
367 break;
368 }
369
370 if (cbPlayedTotal != cbToPlayTotal)
371 RTTestFailed(g_hTest, "Playback ended unexpectedly (%RU32/%RU32 played)\n", cbPlayedTotal, cbToPlayTotal);
372
373 if (RT_SUCCESS(rc))
374 {
375 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Draining stream ...\n");
376 rc = AudioTestMixStreamDrain(&pStream->Mix, true /*fSync*/);
377 }
378 }
379 else
380 rc = VERR_AUDIO_STREAM_NOT_READY;
381
382 if (pTstEnv)
383 {
384 int rc2 = AudioTestObjClose(Obj);
385 if (RT_SUCCESS(rc))
386 rc = rc2;
387 }
388
389 if (RT_FAILURE(rc))
390 RTTestFailed(g_hTest, "Playing tone failed with %Rrc\n", rc);
391
392 return rc;
393}
394
395/**
396 * Records a test tone from a specific audio test stream.
397 *
398 * @returns VBox status code.
399 * @param pTstEnv Test environment to use for running the test.
400 * @param pStream Stream to use for recording the tone.
401 * @param pParms Tone parameters to use.
402 *
403 * @note Blocking function.
404 */
405static int audioTestRecordTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
406{
407 const char *pcszPathOut = pTstEnv->Set.szPathAbs;
408
409 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Recording test tone (tone frequency is %RU16Hz, %RU32ms)\n", (uint16_t)pParms->dbFreqHz, pParms->msDuration);
410 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Writing to '%s'\n", pcszPathOut);
411
412 /** @todo Use .WAV here? */
413 AUDIOTESTOBJ Obj;
414 int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "guest-tone-rec.pcm", &Obj);
415 AssertRCReturn(rc, rc);
416
417 PAUDIOTESTDRVMIXSTREAM pMix = &pStream->Mix;
418
419 rc = AudioTestMixStreamEnable(pMix);
420 if (RT_SUCCESS(rc))
421 {
422 uint64_t cbToRecTotal = PDMAudioPropsMilliToBytes(&pStream->Cfg.Props, pParms->msDuration);
423
424 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Recording %RU32 bytes total\n", cbToRecTotal);
425
426 AudioTestObjAddMetadataStr(Obj, "stream_to_record_bytes=%RU32\n", cbToRecTotal);
427 AudioTestObjAddMetadataStr(Obj, "stream_buffer_size_ms=%RU32\n", pTstEnv->cMsBufferSize);
428 AudioTestObjAddMetadataStr(Obj, "stream_prebuf_size_ms=%RU32\n", pTstEnv->cMsPreBuffer);
429 /* Note: This mostly is provided by backend (e.g. PulseAudio / ALSA / ++) and
430 * has nothing to do with the device emulation scheduling hint. */
431 AudioTestObjAddMetadataStr(Obj, "device_scheduling_hint_ms=%RU32\n", pTstEnv->cMsSchedulingHint);
432
433 uint8_t abSamples[16384];
434 uint32_t const cbSamplesAligned = PDMAudioPropsFloorBytesToFrame(pMix->pProps, sizeof(abSamples));
435 uint64_t cbRecTotal = 0;
436 while (!g_fTerminate && cbRecTotal < cbToRecTotal)
437 {
438 /*
439 * Anything we can read?
440 */
441 uint32_t const cbCanRead = AudioTestMixStreamGetReadable(pMix);
442 if (cbCanRead)
443 {
444 uint32_t const cbToRead = RT_MIN(cbCanRead, cbSamplesAligned);
445 uint32_t cbRecorded = 0;
446 rc = AudioTestMixStreamCapture(pMix, abSamples, cbToRead, &cbRecorded);
447 if (RT_SUCCESS(rc))
448 {
449 if (cbRecorded)
450 {
451 rc = AudioTestObjWrite(Obj, abSamples, cbRecorded);
452 if (RT_SUCCESS(rc))
453 {
454 cbRecTotal += cbRecorded;
455
456 /** @todo Clamp result? */
457 }
458 }
459 }
460 }
461 else if (AudioTestMixStreamIsOkay(pMix))
462 RTThreadSleep(RT_MIN(RT_MAX(1, pTstEnv->cMsSchedulingHint), 256));
463
464 if (RT_FAILURE(rc))
465 break;
466 }
467
468 int rc2 = AudioTestMixStreamDisable(pMix);
469 if (RT_SUCCESS(rc))
470 rc = rc2;
471 }
472
473 int rc2 = AudioTestObjClose(Obj);
474 if (RT_SUCCESS(rc))
475 rc = rc2;
476
477 if (RT_FAILURE(rc))
478 RTTestFailed(g_hTest, "Recording tone done failed with %Rrc\n", rc);
479
480 return rc;
481}
482
483
484/*********************************************************************************************************************************
485* ATS Callback Implementations *
486*********************************************************************************************************************************/
487
488/** @copydoc ATSCALLBACKS::pfnHowdy
489 *
490 * @note Runs as part of the guest ATS.
491 */
492static DECLCALLBACK(int) audioTestGstAtsHowdyCallback(void const *pvUser)
493{
494 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
495
496 AssertReturn(pCtx->cClients <= UINT8_MAX - 1, VERR_BUFFER_OVERFLOW);
497
498 pCtx->cClients++;
499
500 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "New client connected, now %RU8 total\n", pCtx->cClients);
501
502 return VINF_SUCCESS;
503}
504
505/** @copydoc ATSCALLBACKS::pfnBye
506 *
507 * @note Runs as part of the guest ATS.
508 */
509static DECLCALLBACK(int) audioTestGstAtsByeCallback(void const *pvUser)
510{
511 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
512
513 AssertReturn(pCtx->cClients, VERR_WRONG_ORDER);
514 pCtx->cClients--;
515
516 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Client wants to disconnect, %RU8 remaining\n", pCtx->cClients);
517
518 if (0 == pCtx->cClients) /* All clients disconnected? Tear things down. */
519 {
520 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Last client disconnected, terminating server ...\n");
521 ASMAtomicWriteBool(&g_fTerminate, true);
522 }
523
524 return VINF_SUCCESS;
525}
526
527/** @copydoc ATSCALLBACKS::pfnTestSetBegin
528 *
529 * @note Runs as part of the guest ATS.
530 */
531static DECLCALLBACK(int) audioTestGstAtsTestSetBeginCallback(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 beginning test set '%s' in '%s'\n", pszTag, pTstEnv->szPathTemp);
537
538 return AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, pszTag);
539}
540
541/** @copydoc ATSCALLBACKS::pfnTestSetEnd
542 *
543 * @note Runs as part of the guest ATS.
544 */
545static DECLCALLBACK(int) audioTestGstAtsTestSetEndCallback(void const *pvUser, const char *pszTag)
546{
547 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
548 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
549
550 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Got request for ending test set '%s'\n", pszTag);
551
552 /* Pack up everything to be ready for transmission. */
553 return audioTestEnvPrologue(pTstEnv, true /* fPack */, pCtx->szTestSetArchive, sizeof(pCtx->szTestSetArchive));
554}
555
556/** @copydoc ATSCALLBACKS::pfnTonePlay
557 *
558 * @note Runs as part of the guest ATS.
559 */
560static DECLCALLBACK(int) audioTestGstAtsTonePlayCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
561{
562 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
563 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
564
565 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Got request for playing test tone (%RU16Hz, %RU32ms) ...\n",
566 (uint16_t)pToneParms->dbFreqHz, pToneParms->msDuration);
567
568 const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
569
570 int rc = audioTestStreamInit(pTstEnv->pDrvStack, pTstStream, PDMAUDIODIR_OUT, &pTstEnv->Props, false /* fWithMixer */,
571 pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer, pTstEnv->cMsSchedulingHint);
572 if (RT_SUCCESS(rc))
573 {
574 AUDIOTESTPARMS TstParms;
575 RT_ZERO(TstParms);
576 TstParms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
577 TstParms.enmDir = PDMAUDIODIR_OUT;
578 TstParms.TestTone = *pToneParms;
579
580 PAUDIOTESTENTRY pTst;
581 rc = AudioTestSetTestBegin(&pTstEnv->Set, "Playing test tone", &TstParms, &pTst);
582 if (RT_SUCCESS(rc))
583 {
584 rc = audioTestPlayTone(pTstEnv, pTstStream, pToneParms);
585 if (RT_SUCCESS(rc))
586 {
587 AudioTestSetTestDone(pTst);
588 }
589 else
590 AudioTestSetTestFailed(pTst, rc, "Playing tone failed");
591 }
592
593 int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
594 if (RT_SUCCESS(rc))
595 rc = rc2;
596 }
597 else
598 RTTestFailed(g_hTest, "Error creating output stream, rc=%Rrc\n", rc);
599
600 return rc;
601}
602
603/** @copydoc ATSCALLBACKS::pfnToneRecord */
604static DECLCALLBACK(int) audioTestGstAtsToneRecordCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
605{
606 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
607 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
608
609 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Got request for recording test tone (%RU32ms) ...\n", pToneParms->msDuration);
610
611 const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
612
613 int rc = audioTestStreamInit(pTstEnv->pDrvStack, pTstStream, PDMAUDIODIR_IN, &pTstEnv->Props, false /* fWithMixer */,
614 pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer, pTstEnv->cMsSchedulingHint);
615 if (RT_SUCCESS(rc))
616 {
617 AUDIOTESTPARMS TstParms;
618 RT_ZERO(TstParms);
619 TstParms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
620 TstParms.enmDir = PDMAUDIODIR_IN;
621 TstParms.Props = pToneParms->Props;
622 TstParms.TestTone = *pToneParms;
623
624 PAUDIOTESTENTRY pTst;
625 rc = AudioTestSetTestBegin(&pTstEnv->Set, "Recording test tone from host", &TstParms, &pTst);
626 if (RT_SUCCESS(rc))
627 {
628 rc = audioTestRecordTone(pTstEnv, pTstStream, pToneParms);
629 if (RT_SUCCESS(rc))
630 {
631 AudioTestSetTestDone(pTst);
632 }
633 else
634 AudioTestSetTestFailed(pTst, rc, "Recording tone failed");
635 }
636
637 int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
638 if (RT_SUCCESS(rc))
639 rc = rc2;
640 }
641 else
642 RTTestFailed(g_hTest, "Error creating input stream, rc=%Rrc\n", rc);
643
644 return rc;
645}
646
647/** @copydoc ATSCALLBACKS::pfnTestSetSendBegin */
648static DECLCALLBACK(int) audioTestGstAtsTestSetSendBeginCallback(void const *pvUser, const char *pszTag)
649{
650 RT_NOREF(pszTag);
651
652 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
653
654 if (!RTFileExists(pCtx->szTestSetArchive)) /* Has the archive successfully been created yet? */
655 return VERR_WRONG_ORDER;
656
657 int rc = RTFileOpen(&pCtx->hTestSetArchive, pCtx->szTestSetArchive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
658 if (RT_SUCCESS(rc))
659 {
660 uint64_t uSize;
661 rc = RTFileQuerySize(pCtx->hTestSetArchive, &uSize);
662 if (RT_SUCCESS(rc))
663 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Sending test set '%s' (%zu bytes)\n", pCtx->szTestSetArchive, uSize);
664 }
665
666 return rc;
667}
668
669/** @copydoc ATSCALLBACKS::pfnTestSetSendRead */
670static DECLCALLBACK(int) audioTestGstAtsTestSetSendReadCallback(void const *pvUser,
671 const char *pszTag, void *pvBuf, size_t cbBuf, size_t *pcbRead)
672{
673 RT_NOREF(pszTag);
674
675 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
676
677 return RTFileRead(pCtx->hTestSetArchive, pvBuf, cbBuf, pcbRead);
678}
679
680/** @copydoc ATSCALLBACKS::pfnTestSetSendEnd */
681static DECLCALLBACK(int) audioTestGstAtsTestSetSendEndCallback(void const *pvUser, const char *pszTag)
682{
683 RT_NOREF(pszTag);
684
685 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
686
687 int rc = RTFileClose(pCtx->hTestSetArchive);
688 if (RT_SUCCESS(rc))
689 {
690 pCtx->hTestSetArchive = NIL_RTFILE;
691 }
692
693 return rc;
694}
695
696
697/*********************************************************************************************************************************
698* Implementation of audio test environment handling *
699*********************************************************************************************************************************/
700
701/**
702 * Connects an ATS client via TCP/IP to a peer.
703 *
704 * @returns VBox status code.
705 * @param pTstEnv Test environment to use.
706 * @param pClient Client to connect.
707 * @param pszWhat Hint of what to connect to where.
708 * @param pTcpOpts Pointer to TCP options to use.
709 */
710int audioTestEnvConnectViaTcp(PAUDIOTESTENV pTstEnv, PATSCLIENT pClient, const char *pszWhat, PAUDIOTESTENVTCPOPTS pTcpOpts)
711{
712 RT_NOREF(pTstEnv);
713
714 RTGETOPTUNION Val;
715 RT_ZERO(Val);
716
717 Val.u32 = pTcpOpts->enmConnMode;
718 int rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_CONN_MODE, &Val);
719 AssertRCReturn(rc, rc);
720
721 if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
722 || pTcpOpts->enmConnMode == ATSCONNMODE_SERVER)
723 {
724 Assert(pTcpOpts->uBindPort); /* Always set by the caller. */
725 Val.u16 = pTcpOpts->uBindPort;
726 rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_BIND_PORT, &Val);
727 AssertRCReturn(rc, rc);
728
729 if (pTcpOpts->szBindAddr[0])
730 {
731 Val.psz = pTcpOpts->szBindAddr;
732 rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_BIND_ADDRESS, &Val);
733 AssertRCReturn(rc, rc);
734 }
735 else
736 {
737 RTTestFailed(g_hTest, "No bind address specified!\n");
738 return VERR_INVALID_PARAMETER;
739 }
740
741 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connecting %s by listening as server at %s:%RU32 ...\n",
742 pszWhat, pTcpOpts->szBindAddr, pTcpOpts->uBindPort);
743 }
744
745
746 if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
747 || pTcpOpts->enmConnMode == ATSCONNMODE_CLIENT)
748 {
749 Assert(pTcpOpts->uConnectPort); /* Always set by the caller. */
750 Val.u16 = pTcpOpts->uConnectPort;
751 rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_CONNECT_PORT, &Val);
752 AssertRCReturn(rc, rc);
753
754 if (pTcpOpts->szConnectAddr[0])
755 {
756 Val.psz = pTcpOpts->szConnectAddr;
757 rc = AudioTestSvcClientHandleOption(pClient, ATSTCPOPT_CONNECT_ADDRESS, &Val);
758 AssertRCReturn(rc, rc);
759 }
760 else
761 {
762 RTTestFailed(g_hTest, "No connect address specified!\n");
763 return VERR_INVALID_PARAMETER;
764 }
765
766 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connecting %s by connecting as client to %s:%RU32 ...\n",
767 pszWhat, pTcpOpts->szConnectAddr, pTcpOpts->uConnectPort);
768 }
769
770 rc = AudioTestSvcClientConnect(pClient);
771 if (RT_FAILURE(rc))
772 {
773 RTTestFailed(g_hTest, "Connecting %s failed with %Rrc\n", pszWhat, rc);
774 return rc;
775 }
776
777 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Successfully connected %s\n", pszWhat);
778 return rc;
779}
780
781/**
782 * Configures and starts an ATS TCP/IP server.
783 *
784 * @returns VBox status code.
785 * @param pSrv ATS server instance to configure and start.
786 * @param pCallbacks ATS callback table to use.
787 * @param pszDesc Hint of server type which is being started.
788 * @param pTcpOpts TCP options to use.
789 */
790int audioTestEnvConfigureAndStartTcpServer(PATSSERVER pSrv, PCATSCALLBACKS pCallbacks, const char *pszDesc,
791 PAUDIOTESTENVTCPOPTS pTcpOpts)
792{
793 RTGETOPTUNION Val;
794 RT_ZERO(Val);
795
796 int rc = AudioTestSvcInit(pSrv, pCallbacks);
797 if (RT_FAILURE(rc))
798 return rc;
799
800 Val.u32 = pTcpOpts->enmConnMode;
801 rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_CONN_MODE, &Val);
802 AssertRCReturn(rc, rc);
803
804 if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
805 || pTcpOpts->enmConnMode == ATSCONNMODE_SERVER)
806 {
807 Assert(pTcpOpts->uBindPort); /* Always set by the caller. */
808 Val.u16 = pTcpOpts->uBindPort;
809 rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_BIND_PORT, &Val);
810 AssertRCReturn(rc, rc);
811
812 if (pTcpOpts->szBindAddr[0])
813 {
814 Val.psz = pTcpOpts->szBindAddr;
815 rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_BIND_ADDRESS, &Val);
816 AssertRCReturn(rc, rc);
817 }
818 else
819 {
820 RTTestFailed(g_hTest, "No bind address specified!\n");
821 return VERR_INVALID_PARAMETER;
822 }
823
824 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Starting server for %s at %s:%RU32 ...\n",
825 pszDesc, pTcpOpts->szBindAddr, pTcpOpts->uBindPort);
826 }
827
828
829 if ( pTcpOpts->enmConnMode == ATSCONNMODE_BOTH
830 || pTcpOpts->enmConnMode == ATSCONNMODE_CLIENT)
831 {
832 Assert(pTcpOpts->uConnectPort); /* Always set by the caller. */
833 Val.u16 = pTcpOpts->uConnectPort;
834 rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_CONNECT_PORT, &Val);
835 AssertRCReturn(rc, rc);
836
837 if (pTcpOpts->szConnectAddr[0])
838 {
839 Val.psz = pTcpOpts->szConnectAddr;
840 rc = AudioTestSvcHandleOption(pSrv, ATSTCPOPT_CONNECT_ADDRESS, &Val);
841 AssertRCReturn(rc, rc);
842 }
843 else
844 {
845 RTTestFailed(g_hTest, "No connect address specified!\n");
846 return VERR_INVALID_PARAMETER;
847 }
848
849 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Starting server for %s by connecting as client to %s:%RU32 ...\n",
850 pszDesc, pTcpOpts->szConnectAddr, pTcpOpts->uConnectPort);
851 }
852
853 if (RT_SUCCESS(rc))
854 {
855 rc = AudioTestSvcStart(pSrv);
856 if (RT_FAILURE(rc))
857 RTTestFailed(g_hTest, "Starting server for %s failed with %Rrc\n", pszDesc, rc);
858 }
859
860 return rc;
861}
862
863/**
864 * Initializes an audio test environment.
865 *
866 * @returns VBox status code.
867 * @param pTstEnv Audio test environment to initialize.
868 * @param pDrvStack Driver stack to use.
869 */
870int audioTestEnvInit(PAUDIOTESTENV pTstEnv, PAUDIOTESTDRVSTACK pDrvStack)
871{
872 int rc = VINF_SUCCESS;
873
874 pTstEnv->pDrvStack = pDrvStack;
875
876 /*
877 * Set sane defaults if not already set.
878 */
879 if (!RTStrNLen(pTstEnv->szTag, sizeof(pTstEnv->szTag)))
880 {
881 rc = AudioTestGenTag(pTstEnv->szTag, sizeof(pTstEnv->szTag));
882 AssertRCReturn(rc, rc);
883 }
884
885 if (!RTStrNLen(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp)))
886 {
887 rc = AudioTestPathGetTemp(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp));
888 AssertRCReturn(rc, rc);
889 }
890
891 if (!RTStrNLen(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut)))
892 {
893 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), pTstEnv->szPathTemp, "vkat-temp");
894 AssertRCReturn(rc, rc);
895 }
896
897 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Initializing environment for mode '%s'\n", pTstEnv->enmMode == AUDIOTESTMODE_HOST ? "host" : "guest");
898 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pTstEnv->szTag);
899 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut);
900 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp);
901
902 if (!pTstEnv->cMsBufferSize)
903 pTstEnv->cMsBufferSize = UINT32_MAX;
904 if (!pTstEnv->cMsPreBuffer)
905 pTstEnv->cMsPreBuffer = UINT32_MAX;
906 if (!pTstEnv->cMsSchedulingHint)
907 pTstEnv->cMsSchedulingHint = UINT32_MAX;
908
909 char szPathTemp[RTPATH_MAX];
910 if ( !strlen(pTstEnv->szPathTemp)
911 || !strlen(pTstEnv->szPathOut))
912 rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
913
914 if ( RT_SUCCESS(rc)
915 && !strlen(pTstEnv->szPathTemp))
916 rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp");
917
918 if (RT_SUCCESS(rc))
919 {
920 rc = RTDirCreate(pTstEnv->szPathTemp, RTFS_UNIX_IRWXU, 0 /* fFlags */);
921 if (rc == VERR_ALREADY_EXISTS)
922 rc = VINF_SUCCESS;
923 }
924
925 if ( RT_SUCCESS(rc)
926 && !strlen(pTstEnv->szPathOut))
927 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat");
928
929 if (RT_SUCCESS(rc))
930 {
931 rc = RTDirCreate(pTstEnv->szPathOut, RTFS_UNIX_IRWXU, 0 /* fFlags */);
932 if (rc == VERR_ALREADY_EXISTS)
933 rc = VINF_SUCCESS;
934 }
935
936 if (RT_FAILURE(rc))
937 return rc;
938
939 /**
940 * For NAT'ed VMs we use (default):
941 * - client mode (uConnectAddr / uConnectPort) on the guest.
942 * - server mode (uBindAddr / uBindPort) on the host.
943 */
944 if ( !pTstEnv->TcpOpts.szConnectAddr[0]
945 && !pTstEnv->TcpOpts.szBindAddr[0])
946 RTStrCopy(pTstEnv->TcpOpts.szBindAddr, sizeof(pTstEnv->TcpOpts.szBindAddr), "0.0.0.0");
947
948 /*
949 * Determine connection mode based on set variables.
950 */
951 if ( pTstEnv->TcpOpts.szBindAddr[0]
952 && pTstEnv->TcpOpts.szConnectAddr[0])
953 {
954 pTstEnv->TcpOpts.enmConnMode = ATSCONNMODE_BOTH;
955 }
956 else if (pTstEnv->TcpOpts.szBindAddr[0])
957 pTstEnv->TcpOpts.enmConnMode = ATSCONNMODE_SERVER;
958 else /* "Reversed mode", i.e. used for NATed VMs. */
959 pTstEnv->TcpOpts.enmConnMode = ATSCONNMODE_CLIENT;
960
961 /* Set a back reference to the test environment for the callback context. */
962 pTstEnv->CallbackCtx.pTstEnv = pTstEnv;
963
964 ATSCALLBACKS Callbacks;
965 RT_ZERO(Callbacks);
966 Callbacks.pvUser = &pTstEnv->CallbackCtx;
967
968 if (pTstEnv->enmMode == AUDIOTESTMODE_GUEST)
969 {
970 Callbacks.pfnHowdy = audioTestGstAtsHowdyCallback;
971 Callbacks.pfnBye = audioTestGstAtsByeCallback;
972 Callbacks.pfnTestSetBegin = audioTestGstAtsTestSetBeginCallback;
973 Callbacks.pfnTestSetEnd = audioTestGstAtsTestSetEndCallback;
974 Callbacks.pfnTonePlay = audioTestGstAtsTonePlayCallback;
975 Callbacks.pfnToneRecord = audioTestGstAtsToneRecordCallback;
976 Callbacks.pfnTestSetSendBegin = audioTestGstAtsTestSetSendBeginCallback;
977 Callbacks.pfnTestSetSendRead = audioTestGstAtsTestSetSendReadCallback;
978 Callbacks.pfnTestSetSendEnd = audioTestGstAtsTestSetSendEndCallback;
979
980 if (!pTstEnv->TcpOpts.uBindPort)
981 pTstEnv->TcpOpts.uBindPort = ATS_TCP_DEF_BIND_PORT_GUEST;
982
983 if (!pTstEnv->TcpOpts.uConnectPort)
984 pTstEnv->TcpOpts.uConnectPort = ATS_TCP_DEF_CONNECT_PORT_GUEST;
985
986 pTstEnv->pSrv = (PATSSERVER)RTMemAlloc(sizeof(ATSSERVER));
987 AssertPtrReturn(pTstEnv->pSrv, VERR_NO_MEMORY);
988
989 /*
990 * Start the ATS (Audio Test Service) on the guest side.
991 * That service then will perform playback and recording operations on the guest, triggered from the host.
992 *
993 * When running this in self-test mode, that service also can be run on the host if nothing else is specified.
994 * Note that we have to bind to "0.0.0.0" by default so that the host can connect to it.
995 */
996 rc = audioTestEnvConfigureAndStartTcpServer(pTstEnv->pSrv, &Callbacks, "guest", &pTstEnv->TcpOpts);
997 }
998 else /* Host mode */
999 {
1000 if (!pTstEnv->TcpOpts.uBindPort)
1001 pTstEnv->TcpOpts.uBindPort = ATS_TCP_DEF_BIND_PORT_HOST;
1002
1003 if (!pTstEnv->TcpOpts.uConnectPort)
1004 pTstEnv->TcpOpts.uConnectPort = ATS_TCP_DEF_CONNECT_PORT_HOST_PORT_FWD;
1005
1006 /**
1007 * Note: Don't set pTstEnv->TcpOpts.szTcpConnectAddr by default here, as this specifies what connection mode
1008 * (client / server / both) we use on the host.
1009 */
1010
1011 /* We need to start a server on the host so that VMs configured with NAT networking
1012 * can connect to it as well. */
1013 rc = AudioTestSvcClientCreate(&pTstEnv->u.Host.AtsClGuest);
1014 if (RT_SUCCESS(rc))
1015 rc = audioTestEnvConnectViaTcp(pTstEnv, &pTstEnv->u.Host.AtsClGuest,
1016 "host -> guest", &pTstEnv->TcpOpts);
1017 if (RT_SUCCESS(rc))
1018 {
1019 AUDIOTESTENVTCPOPTS ValKitTcpOpts;
1020 RT_ZERO(ValKitTcpOpts);
1021
1022 /* We only connect as client to the Validation Kit audio driver ATS. */
1023 ValKitTcpOpts.enmConnMode = ATSCONNMODE_CLIENT;
1024
1025 /* For now we ASSUME that the Validation Kit audio driver ATS runs on the same host as VKAT (this binary) runs on. */
1026 ValKitTcpOpts.uConnectPort = ATS_TCP_DEF_CONNECT_PORT_VALKIT; /** @todo Make this dynamic. */
1027 RTStrCopy(ValKitTcpOpts.szConnectAddr, sizeof(ValKitTcpOpts.szConnectAddr), ATS_TCP_DEF_CONNECT_HOST_ADDR_STR); /** @todo Ditto. */
1028
1029 rc = AudioTestSvcClientCreate(&pTstEnv->u.Host.AtsClValKit);
1030 if (RT_SUCCESS(rc))
1031 {
1032 rc = audioTestEnvConnectViaTcp(pTstEnv, &pTstEnv->u.Host.AtsClValKit,
1033 "host -> valkit", &ValKitTcpOpts);
1034 if (RT_FAILURE(rc))
1035 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Unable to connect to the Validation Kit audio driver!\n"
1036 "There could be multiple reasons:\n\n"
1037 " - Wrong host being used\n"
1038 " - VirtualBox host version is too old\n"
1039 " - Audio debug mode is not enabled\n"
1040 " - Support for Validation Kit audio driver is not included\n"
1041 " - Firewall / network configuration problem\n");
1042 }
1043 }
1044 }
1045
1046 return rc;
1047}
1048
1049/**
1050 * Destroys an audio test environment.
1051 *
1052 * @param pTstEnv Audio test environment to destroy.
1053 */
1054void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv)
1055{
1056 if (!pTstEnv)
1057 return;
1058
1059 /* When in host mode, we need to destroy our ATS clients in order to also let
1060 * the ATS server(s) know we're going to quit. */
1061 if (pTstEnv->enmMode == AUDIOTESTMODE_HOST)
1062 {
1063 AudioTestSvcClientDestroy(&pTstEnv->u.Host.AtsClValKit);
1064 AudioTestSvcClientDestroy(&pTstEnv->u.Host.AtsClGuest);
1065 }
1066
1067 if (pTstEnv->pSrv)
1068 {
1069 int rc2 = AudioTestSvcDestroy(pTstEnv->pSrv);
1070 AssertRC(rc2);
1071
1072 RTMemFree(pTstEnv->pSrv);
1073 pTstEnv->pSrv = NULL;
1074 }
1075
1076 for (unsigned i = 0; i < RT_ELEMENTS(pTstEnv->aStreams); i++)
1077 {
1078 int rc2 = audioTestStreamDestroy(pTstEnv, &pTstEnv->aStreams[i]);
1079 if (RT_FAILURE(rc2))
1080 RTTestFailed(g_hTest, "Stream destruction for stream #%u failed with %Rrc\n", i, rc2);
1081 }
1082
1083 /* Try cleaning up a bit. */
1084 RTDirRemove(pTstEnv->szPathTemp);
1085 RTDirRemove(pTstEnv->szPathOut);
1086
1087 pTstEnv->pDrvStack = NULL;
1088}
1089
1090/**
1091 * Closes, packs up and destroys a test environment.
1092 *
1093 * @returns VBox status code.
1094 * @param pTstEnv Test environment to handle.
1095 * @param fPack Whether to pack the test set up before destroying / wiping it.
1096 * @param pszPackFile Where to store the packed test set file on success. Can be NULL if \a fPack is \c false.
1097 * @param cbPackFile Size (in bytes) of \a pszPackFile. Can be 0 if \a fPack is \c false.
1098 */
1099int audioTestEnvPrologue(PAUDIOTESTENV pTstEnv, bool fPack, char *pszPackFile, size_t cbPackFile)
1100{
1101 /* Close the test set first. */
1102 AudioTestSetClose(&pTstEnv->Set);
1103
1104 int rc = VINF_SUCCESS;
1105
1106 if (fPack)
1107 {
1108 /* Before destroying the test environment, pack up the test set so
1109 * that it's ready for transmission. */
1110 rc = AudioTestSetPack(&pTstEnv->Set, pTstEnv->szPathOut, pszPackFile, cbPackFile);
1111 if (RT_SUCCESS(rc))
1112 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set packed up to '%s'\n", pszPackFile);
1113 }
1114
1115 if (!g_fDrvAudioDebug) /* Don't wipe stuff when debugging. Can be useful for introspecting data. */
1116 /* ignore rc */ AudioTestSetWipe(&pTstEnv->Set);
1117
1118 AudioTestSetDestroy(&pTstEnv->Set);
1119
1120 if (RT_FAILURE(rc))
1121 RTTestFailed(g_hTest, "Test set prologue failed with %Rrc\n", rc);
1122
1123 return rc;
1124}
1125
1126/**
1127 * Initializes an audio test parameters set.
1128 *
1129 * @param pTstParms Test parameters set to initialize.
1130 */
1131void audioTestParmsInit(PAUDIOTESTPARMS pTstParms)
1132{
1133 RT_ZERO(*pTstParms);
1134}
1135
1136/**
1137 * Destroys an audio test parameters set.
1138 *
1139 * @param pTstParms Test parameters set to destroy.
1140 */
1141void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms)
1142{
1143 if (!pTstParms)
1144 return;
1145
1146 return;
1147}
1148
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