VirtualBox

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

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

Audio/ValKit: More work on ATS client destruction handling. bugref:10008

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