VirtualBox

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

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

Audio/Validation Kit: Reverted r146802, didn't work. ​bugref:10008

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