VirtualBox

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

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

Audio/ValKit: Implemented support for downloading (guest) test sets. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.1 KB
Line 
1/* $Id: vkatCommon.cpp 89614 2021-06-11 06:34:13Z 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* Prototypes *
50*********************************************************************************************************************************/
51
52static int audioTestCreateStreamDefaultIn(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps);
53static int audioTestCreateStreamDefaultOut(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps);
54static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream);
55static int audioTestDevicesEnumerateAndCheck(PAUDIOTESTENV pTstEnv, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev);
56
57
58/*********************************************************************************************************************************
59* Defined Constants And Macros *
60*********************************************************************************************************************************/
61/**
62 * Structure for keeping a user context for the test service callbacks.
63 */
64typedef struct ATSCALLBACKCTX
65{
66 /** The test environment bound to this context. */
67 PAUDIOTESTENV pTstEnv;
68 /** Absolute path to the packed up test set archive.
69 * Keep it simple for now and only support one (open) archive at a time. */
70 char szTestSetArchive[RTPATH_MAX];
71 /** File handle to the (opened) test set archive for reading. */
72 RTFILE hTestSetArchive;
73} ATSCALLBACKCTX;
74typedef ATSCALLBACKCTX *PATSCALLBACKCTX;
75
76
77/*********************************************************************************************************************************
78* Device enumeration + handling. *
79*********************************************************************************************************************************/
80
81/**
82 * Enumerates audio devices and optionally searches for a specific device.
83 *
84 * @returns VBox status code.
85 * @param pTstEnv Test env to use for enumeration.
86 * @param pszDev Device name to search for. Can be NULL if the default device shall be used.
87 * @param ppDev Where to return the pointer of the device enumeration of \a pTstEnv when a
88 * specific device was found.
89 */
90static int audioTestDevicesEnumerateAndCheck(PAUDIOTESTENV pTstEnv, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev)
91{
92#ifdef DEBUG_andy
93 return VINF_SUCCESS;
94#endif
95
96 RTTestSubF(g_hTest, "Enumerating audio devices and checking for device '%s'", pszDev ? pszDev : "<Default>");
97
98 if (!pTstEnv->DrvStack.pIHostAudio->pfnGetDevices)
99 {
100 RTTestSkipped(g_hTest, "Backend does not support device enumeration, skipping");
101 return VINF_NOT_SUPPORTED;
102 }
103
104 Assert(pszDev == NULL || ppDev);
105
106 if (ppDev)
107 *ppDev = NULL;
108
109 int rc = pTstEnv->DrvStack.pIHostAudio->pfnGetDevices(pTstEnv->DrvStack.pIHostAudio, &pTstEnv->DevEnum);
110 if (RT_SUCCESS(rc))
111 {
112 PPDMAUDIOHOSTDEV pDev;
113 RTListForEach(&pTstEnv->DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
114 {
115 char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
116 if (pDev->pszId)
117 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s' (ID '%s'):\n", pDev->pszName, pDev->pszId);
118 else
119 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s':\n", pDev->pszName);
120 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Usage = %s\n", PDMAudioDirGetName(pDev->enmUsage));
121 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Flags = %s\n", PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags));
122 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Input channels = %RU8\n", pDev->cMaxInputChannels);
123 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Output channels = %RU8\n", pDev->cMaxOutputChannels);
124
125 if ( pszDev
126 && !RTStrCmp(pDev->pszName, pszDev))
127 {
128 *ppDev = pDev;
129 }
130 }
131 }
132 else
133 RTTestFailed(g_hTest, "Enumerating audio devices failed with %Rrc", rc);
134
135 RTTestSubDone(g_hTest);
136
137 if ( pszDev
138 && *ppDev == NULL)
139 {
140 RTTestFailed(g_hTest, "Audio device '%s' not found", pszDev);
141 return VERR_NOT_FOUND;
142 }
143
144 return VINF_SUCCESS;
145}
146
147/**
148 * Opens an audio device.
149 *
150 * @returns VBox status code.
151 * @param pDev Audio device to open.
152 */
153int audioTestDeviceOpen(PPDMAUDIOHOSTDEV pDev)
154{
155 int rc = VINF_SUCCESS;
156
157 RTTestSubF(g_hTest, "Opening audio device '%s' ...", pDev->pszName);
158
159 /** @todo Detect + open device here. */
160
161 RTTestSubDone(g_hTest);
162
163 return rc;
164}
165
166/**
167 * Closes an audio device.
168 *
169 * @returns VBox status code.
170 * @param pDev Audio device to close.
171 */
172int audioTestDeviceClose(PPDMAUDIOHOSTDEV pDev)
173{
174 int rc = VINF_SUCCESS;
175
176 RTTestSubF(g_hTest, "Closing audio device '%s' ...", pDev->pszName);
177
178 /** @todo Close device here. */
179
180 RTTestSubDone(g_hTest);
181
182 return rc;
183}
184
185/**
186 * Destroys an audio test stream.
187 *
188 * @returns VBox status code.
189 * @param pTstEnv Test environment the stream to destroy contains.
190 * @param pStream Audio stream to destroy.
191 */
192static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream)
193{
194 int rc = VINF_SUCCESS;
195 if (pStream && pStream->pStream)
196 {
197 /** @todo Anything else to do here, e.g. test if there are left over samples or some such? */
198
199 audioTestDriverStackStreamDestroy(&pTstEnv->DrvStack, pStream->pStream);
200 pStream->pStream = NULL;
201 pStream->pBackend = NULL;
202 }
203
204 return rc;
205}
206
207/**
208 * Creates an audio default input (recording) test stream.
209 * Convenience function.
210 *
211 * @returns VBox status code.
212 * @param pTstEnv Test environment to use for creating the stream.
213 * @param pStream Audio stream to create.
214 * @param pProps PCM properties to use for creation.
215 */
216static int audioTestCreateStreamDefaultIn(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)
217{
218 pStream->pBackend = NULL;
219 int rc = audioTestDriverStackStreamCreateInput(&pTstEnv->DrvStack, pProps, pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer,
220 pTstEnv->cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
221 if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)
222 pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
223 return rc;
224}
225
226/**
227 * Creates an audio default output (playback) test stream.
228 * Convenience function.
229 *
230 * @returns VBox status code.
231 * @param pTstEnv Test environment to use for creating the stream.
232 * @param pStream Audio stream to create.
233 * @param pProps PCM properties to use for creation.
234 */
235static int audioTestCreateStreamDefaultOut(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)
236{
237 pStream->pBackend = NULL;
238 int rc = audioTestDriverStackStreamCreateOutput(&pTstEnv->DrvStack, pProps, pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer,
239 pTstEnv->cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
240 if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)
241 pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
242 return rc;
243}
244
245
246/*********************************************************************************************************************************
247* Test Primitives *
248*********************************************************************************************************************************/
249
250/**
251 * Plays a test tone on a specific audio test stream.
252 *
253 * @returns VBox status code.
254 * @param pTstEnv Test environment to use for running the test.
255 * @param pStream Stream to use for playing the tone.
256 * @param pParms Tone parameters to use.
257 *
258 * @note Blocking function.
259 */
260static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
261{
262 AUDIOTESTTONE TstTone;
263 AudioTestToneInit(&TstTone, &pParms->Props, pParms->dbFreqHz);
264
265 const char *pcszPathOut = pTstEnv->Set.szPathAbs;
266
267 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Playing test tone (tone frequency is %RU16Hz, %RU32ms)\n", (uint16_t)pParms->dbFreqHz, pParms->msDuration);
268 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Writing to '%s'\n", pcszPathOut);
269
270 /** @todo Use .WAV here? */
271 PAUDIOTESTOBJ pObj;
272 int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "guest-tone-play.pcm", &pObj);
273 AssertRCReturn(rc, rc);
274
275 if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))
276 {
277 uint32_t cbBuf;
278 uint8_t abBuf[_4K];
279
280 const uint32_t cbPerSched = PDMAudioPropsMilliToBytes(&pParms->Props, pTstEnv->cMsSchedulingHint);
281 AssertStmt(cbPerSched, rc = VERR_INVALID_PARAMETER);
282 uint32_t cbToWrite = PDMAudioPropsMilliToBytes(&pParms->Props, pParms->msDuration);
283 AssertStmt(cbToWrite, rc = VERR_INVALID_PARAMETER);
284
285 if (RT_SUCCESS(rc))
286 {
287 AudioTestSetObjAddMetadataStr(pObj, "buffer_size_ms=%RU32\n", pTstEnv->cMsBufferSize);
288 AudioTestSetObjAddMetadataStr(pObj, "prebuf_size_ms=%RU32\n", pTstEnv->cMsPreBuffer);
289 AudioTestSetObjAddMetadataStr(pObj, "scheduling_hint_ms=%RU32\n", pTstEnv->cMsSchedulingHint);
290
291 while (cbToWrite)
292 {
293 uint32_t cbWritten = 0;
294 uint32_t cbToGenerate = RT_MIN(cbToWrite, RT_MIN(cbPerSched, sizeof(abBuf)));
295 Assert(cbToGenerate);
296
297 rc = AudioTestToneGenerate(&TstTone, abBuf, cbToGenerate, &cbBuf);
298 if (RT_SUCCESS(rc))
299 {
300 /* Write stuff to disk before trying to play it. Help analysis later. */
301 rc = AudioTestSetObjWrite(pObj, abBuf, cbBuf);
302 if (RT_SUCCESS(rc))
303 rc = audioTestDriverStackStreamPlay(&pTstEnv->DrvStack, pStream->pStream,
304 abBuf, cbBuf, &cbWritten);
305 }
306
307 if (RT_FAILURE(rc))
308 break;
309
310 RTThreadSleep(pTstEnv->cMsSchedulingHint);
311
312 Assert(cbToWrite >= cbWritten);
313 cbToWrite -= cbWritten;
314 }
315 }
316 }
317 else
318 rc = VERR_AUDIO_STREAM_NOT_READY;
319
320 int rc2 = AudioTestSetObjClose(pObj);
321 if (RT_SUCCESS(rc))
322 rc = rc2;
323
324 if (RT_FAILURE(rc))
325 RTTestFailed(g_hTest, "Playing tone done failed with %Rrc\n", rc);
326
327 return rc;
328}
329
330/**
331 * Records a test tone from a specific audio test stream.
332 *
333 * @returns VBox status code.
334 * @param pTstEnv Test environment to use for running the test.
335 * @param pStream Stream to use for recording the tone.
336 * @param pParms Tone parameters to use.
337 *
338 * @note Blocking function.
339 */
340static int audioTestRecordTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
341{
342 const char *pcszPathOut = pTstEnv->Set.szPathAbs;
343
344 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Recording test tone (for %RU32ms)\n", pParms->msDuration);
345 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Writing to '%s'\n", pcszPathOut);
346
347 /** @todo Use .WAV here? */
348 PAUDIOTESTOBJ pObj;
349 int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "guest-tone-rec.pcm", &pObj);
350 AssertRCReturn(rc, rc);
351
352 if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))
353 {
354 const uint32_t cbPerSched = PDMAudioPropsMilliToBytes(&pParms->Props, pTstEnv->cMsSchedulingHint);
355 AssertStmt(cbPerSched, rc = VERR_INVALID_PARAMETER);
356 uint32_t cbToRead = PDMAudioPropsMilliToBytes(&pParms->Props, pParms->msDuration);
357 AssertStmt(cbToRead, rc = VERR_INVALID_PARAMETER);
358
359 if (RT_SUCCESS(rc))
360 {
361 AudioTestSetObjAddMetadataStr(pObj, "buffer_size_ms=%RU32\n", pTstEnv->cMsBufferSize);
362 AudioTestSetObjAddMetadataStr(pObj, "prebuf_size_ms=%RU32\n", pTstEnv->cMsPreBuffer);
363 AudioTestSetObjAddMetadataStr(pObj, "scheduling_hint_ms=%RU32\n", pTstEnv->cMsSchedulingHint);
364
365 uint8_t abBuf[_4K];
366
367 while (cbToRead)
368 {
369 const uint32_t cbChunk = RT_MIN(cbToRead, RT_MIN(cbPerSched, sizeof(abBuf)));
370
371 uint32_t cbRead = 0;
372 rc = audioTestDriverStackStreamCapture(&pTstEnv->DrvStack, pStream->pStream, (void *)abBuf, cbChunk, &cbRead);
373 if (RT_SUCCESS(rc))
374 rc = AudioTestSetObjWrite(pObj, abBuf, cbRead);
375
376 if (RT_FAILURE(rc))
377 break;
378
379 RTThreadSleep(pTstEnv->cMsSchedulingHint);
380
381 Assert(cbToRead >= cbRead);
382 cbToRead -= cbRead;
383 }
384 }
385 }
386 else
387 rc = VERR_AUDIO_STREAM_NOT_READY;
388
389 int rc2 = AudioTestSetObjClose(pObj);
390 if (RT_SUCCESS(rc))
391 rc = rc2;
392
393 if (RT_FAILURE(rc))
394 RTTestFailed(g_hTest, "Recording tone done failed with %Rrc\n", rc);
395
396 return rc;
397}
398
399
400/*********************************************************************************************************************************
401* ATS Callback Implementations *
402*********************************************************************************************************************************/
403
404/** @copydoc ATSCALLBACKS::pfnTestSetBegin
405 *
406 * @note Runs as part of the guest ATS.
407 */
408static DECLCALLBACK(int) audioTestGstAtsTestSetBeginCallback(void const *pvUser, const char *pszTag)
409{
410 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
411 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
412
413 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Beginning test set '%s' in '%s'\n", pszTag, pTstEnv->szPathTemp);
414
415 return AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, pszTag);
416}
417
418/** @copydoc ATSCALLBACKS::pfnTestSetEnd
419 *
420 * @note Runs as part of the guest ATS.
421 */
422static DECLCALLBACK(int) audioTestGstAtsTestSetEndCallback(void const *pvUser, const char *pszTag)
423{
424 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
425 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
426
427 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Ending test set '%s'\n", pszTag);
428
429 /* Pack up everything to be ready for transmission. */
430 return audioTestEnvPrologue(pTstEnv, true /* fPack */, pCtx->szTestSetArchive, sizeof(pCtx->szTestSetArchive));
431}
432
433/** @copydoc ATSCALLBACKS::pfnTonePlay
434 *
435 * @note Runs as part of the guest ATS.
436 */
437static DECLCALLBACK(int) audioTestGstAtsTonePlayCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
438{
439 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
440 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
441
442 AUDIOTESTTONE TstTone;
443 AudioTestToneInitRandom(&TstTone, &pToneParms->Props);
444
445 const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
446
447 int rc = audioTestCreateStreamDefaultOut(pTstEnv, pTstStream, &pToneParms->Props);
448 if (RT_SUCCESS(rc))
449 {
450 AUDIOTESTPARMS TstParms;
451 RT_ZERO(TstParms);
452 TstParms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
453 TstParms.enmDir = PDMAUDIODIR_OUT;
454 TstParms.TestTone = *pToneParms;
455
456 PAUDIOTESTENTRY pTst;
457 rc = AudioTestSetTestBegin(&pTstEnv->Set, "Playing test tone", &TstParms, &pTst);
458 if (RT_SUCCESS(rc))
459 {
460 rc = audioTestPlayTone(pTstEnv, pTstStream, pToneParms);
461 if (RT_SUCCESS(rc))
462 {
463 AudioTestSetTestDone(pTst);
464 }
465 else
466 AudioTestSetTestFailed(pTst, rc, "Playing tone failed");
467 }
468
469 int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
470 if (RT_SUCCESS(rc))
471 rc = rc2;
472 }
473 else
474 RTTestFailed(g_hTest, "Error creating output stream, rc=%Rrc\n", rc);
475
476 return rc;
477}
478
479/** @copydoc ATSCALLBACKS::pfnToneRecord */
480static DECLCALLBACK(int) audioTestGstAtsToneRecordCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
481{
482 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
483 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
484
485 const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
486
487 int rc = audioTestCreateStreamDefaultIn(pTstEnv, pTstStream, &pToneParms->Props);
488 if (RT_SUCCESS(rc))
489 {
490 AUDIOTESTPARMS TstParms;
491 RT_ZERO(TstParms);
492 TstParms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
493 TstParms.enmDir = PDMAUDIODIR_IN;
494 TstParms.Props = pToneParms->Props;
495 TstParms.TestTone = *pToneParms;
496
497 PAUDIOTESTENTRY pTst;
498 rc = AudioTestSetTestBegin(&pTstEnv->Set, "Recording test tone from host", &TstParms, &pTst);
499 if (RT_SUCCESS(rc))
500 {
501 rc = audioTestRecordTone(pTstEnv, pTstStream, pToneParms);
502 if (RT_SUCCESS(rc))
503 {
504 AudioTestSetTestDone(pTst);
505 }
506 else
507 AudioTestSetTestFailed(pTst, rc, "Recording tone failed");
508 }
509
510 int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
511 if (RT_SUCCESS(rc))
512 rc = rc2;
513 }
514 else
515 RTTestFailed(g_hTest, "Error creating input stream, rc=%Rrc\n", rc);
516
517 return rc;
518}
519
520/** @copydoc ATSCALLBACKS::pfnTestSetSendBegin */
521static DECLCALLBACK(int) audioTestGstAtsTestSetSendBeginCallback(void const *pvUser, const char *pszTag)
522{
523 RT_NOREF(pszTag);
524
525 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
526
527 if (!RTFileExists(pCtx->szTestSetArchive)) /* Has the archive successfully been created yet? */
528 return VERR_WRONG_ORDER;
529
530 int rc = RTFileOpen(&pCtx->hTestSetArchive, pCtx->szTestSetArchive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
531 if (RT_SUCCESS(rc))
532 {
533 uint64_t uSize;
534 rc = RTFileQuerySize(pCtx->hTestSetArchive, &uSize);
535 if (RT_SUCCESS(rc))
536 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Sending test set '%s' (%zu bytes)\n", pCtx->szTestSetArchive, uSize);
537 }
538
539 return rc;
540}
541
542/** @copydoc ATSCALLBACKS::pfnTestSetSendRead */
543static DECLCALLBACK(int) audioTestGstAtsTestSetSendReadCallback(void const *pvUser,
544 const char *pszTag, void *pvBuf, size_t cbBuf, size_t *pcbRead)
545{
546 RT_NOREF(pszTag);
547
548 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
549
550 return RTFileRead(pCtx->hTestSetArchive, pvBuf, cbBuf, pcbRead);
551}
552
553/** @copydoc ATSCALLBACKS::pfnTestSetSendEnd */
554static DECLCALLBACK(int) audioTestGstAtsTestSetSendEndCallback(void const *pvUser, const char *pszTag)
555{
556 RT_NOREF(pszTag);
557
558 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
559
560 int rc = RTFileClose(pCtx->hTestSetArchive);
561 if (RT_SUCCESS(rc))
562 {
563 pCtx->hTestSetArchive = NIL_RTFILE;
564 }
565
566 return rc;
567}
568
569
570/*********************************************************************************************************************************
571* Implementation of audio test environment handling *
572*********************************************************************************************************************************/
573
574int audioTestEnvConnectToHostAts(PAUDIOTESTENV pTstEnv,
575 const char *pszHostTcpAddr, uint32_t uHostTcpPort)
576{
577 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connecting to host ATS at %s:%RU32 ...\n",
578 (pszHostTcpAddr && *pszHostTcpAddr) ? pszHostTcpAddr : ATS_TCP_HOST_DEFAULT_ADDR_STR,
579 uHostTcpPort ? uHostTcpPort : ATS_TCP_HOST_DEFAULT_PORT);
580
581 int rc = AudioTestSvcClientConnect(&pTstEnv->u.Host.AtsClValKit, pszHostTcpAddr, uHostTcpPort);
582 if (RT_FAILURE(rc))
583 {
584 RTTestFailed(g_hTest, "Connecting to host ATS failed with %Rrc\n", rc);
585 return rc;
586 }
587
588 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connected to host ATS\n");
589 return rc;
590}
591
592int audioTestEnvConnectToGuestAts(PAUDIOTESTENV pTstEnv,
593 const char *pszGuestTcpAddr, uint32_t uGuestTcpPort)
594{
595 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Connecting to guest ATS at %s:%RU32 ...\n",
596 (pszGuestTcpAddr && *pszGuestTcpAddr) ? pszGuestTcpAddr : "127.0.0.1",
597 uGuestTcpPort ? uGuestTcpPort : ATS_TCP_GUEST_DEFAULT_PORT);
598
599 int rc = AudioTestSvcClientConnect(&pTstEnv->u.Host.AtsClGuest, pszGuestTcpAddr, uGuestTcpPort);
600 if (RT_FAILURE(rc))
601 {
602 RTTestFailed(g_hTest, "Connecting to guest ATS failed with %Rrc\n", rc);
603 return rc;
604 }
605
606 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connected to guest ATS\n");
607 return rc;
608}
609
610/**
611 * Initializes an audio test environment.
612 *
613 * @param pTstEnv Audio test environment to initialize.
614 * @param pDrvReg Audio driver to use.
615 * @param fWithDrvAudio Whether to include DrvAudio in the stack or not.
616 * @param pszHostTcpAddr Host ATS TCP/IP address to connect to.
617 * If NULL, ATS_TCP_HOST_DEFAULT_ADDR_STR will be used.
618 * @param uHostTcpPort Host ATS TCP/IP port to connect to.
619 * If 0, ATS_TCP_HOST_DEFAULT_PORT will be used.
620 * @param pszGuestTcpAddr Guest ATS TCP/IP address to connect to.
621 * If NULL, localhost (127.0.0.1) will be used.
622 * @param uGuestTcpPort Guest ATS TCP/IP port to connect to.
623 * If 0, ATS_TCP_GUEST_DEFAULT_PORT will be used.
624 */
625int audioTestEnvInit(PAUDIOTESTENV pTstEnv,
626 PCPDMDRVREG pDrvReg, bool fWithDrvAudio,
627 const char *pszHostTcpAddr, uint32_t uHostTcpPort,
628 const char *pszGuestTcpAddr, uint32_t uGuestTcpPort)
629{
630 int rc = VINF_SUCCESS;
631
632 /*
633 * Set sane defaults if not already set.
634 */
635 if (!RTStrNLen(pTstEnv->szTag, sizeof(pTstEnv->szTag)))
636 {
637 rc = AudioTestGenTag(pTstEnv->szTag, sizeof(pTstEnv->szTag));
638 AssertRCReturn(rc, rc);
639 }
640
641 if (!RTStrNLen(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp)))
642 {
643 rc = AudioTestPathGetTemp(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp));
644 AssertRCReturn(rc, rc);
645 }
646
647 if (!RTStrNLen(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut)))
648 {
649 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), pTstEnv->szPathTemp, "vkat-temp");
650 AssertRCReturn(rc, rc);
651 }
652
653 if (pDrvReg == NULL)
654 {
655 if (pTstEnv->fSelftest)
656 pDrvReg = &g_DrvHostValidationKitAudio;
657 else /* Go with the platform's default backend if nothing else is set. */
658 pDrvReg = g_aBackends[0].pDrvReg;
659 }
660
661 if (!uHostTcpPort)
662 uHostTcpPort = ATS_TCP_HOST_DEFAULT_PORT;
663
664 if (!uGuestTcpPort)
665 uGuestTcpPort = ATS_TCP_GUEST_DEFAULT_PORT;
666
667 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test mode is '%s'\n", pTstEnv->enmMode == AUDIOTESTMODE_HOST ? "host" : "guest");
668 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pTstEnv->szTag);
669 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut);
670 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp);
671
672 PDMAudioHostEnumInit(&pTstEnv->DevEnum);
673
674 pTstEnv->cMsBufferSize = 300; /* ms */ /** @todo Randomize this also? */
675 pTstEnv->cMsPreBuffer = 150; /* ms */ /** @todo Ditto. */
676 pTstEnv->cMsSchedulingHint = RTRandU32Ex(10, 80); /* Choose a random scheduling (in ms). */
677
678 /* Only the guest mode needs initializing the driver stack. */
679 const bool fUseDriverStack = pTstEnv->enmMode == AUDIOTESTMODE_GUEST;
680 if (fUseDriverStack)
681 {
682 rc = audioTestDriverStackInitEx(&pTstEnv->DrvStack, pDrvReg,
683 true /* fEnabledIn */, true /* fEnabledOut */, fWithDrvAudio);
684 if (RT_FAILURE(rc))
685 return rc;
686
687 PPDMAUDIOHOSTDEV pDev;
688 rc = audioTestDevicesEnumerateAndCheck(pTstEnv, NULL /* pszDevice */, &pDev); /** @todo Implement device checking. */
689 if (RT_FAILURE(rc))
690 return rc;
691 }
692
693 char szPathTemp[RTPATH_MAX];
694 if ( !strlen(pTstEnv->szPathTemp)
695 || !strlen(pTstEnv->szPathOut))
696 rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
697
698 if ( RT_SUCCESS(rc)
699 && !strlen(pTstEnv->szPathTemp))
700 rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp");
701
702 if ( RT_SUCCESS(rc)
703 && !strlen(pTstEnv->szPathOut))
704 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat");
705
706 if (RT_FAILURE(rc))
707 return rc;
708
709 /** @todo Implement NAT mode like we do for TxS later? */
710 if (pTstEnv->enmMode == AUDIOTESTMODE_GUEST)
711 {
712 ATSCALLBACKCTX Ctx;
713 Ctx.pTstEnv = pTstEnv;
714
715 ATSCALLBACKS Callbacks;
716 Callbacks.pfnTestSetBegin = audioTestGstAtsTestSetBeginCallback;
717 Callbacks.pfnTestSetEnd = audioTestGstAtsTestSetEndCallback;
718 Callbacks.pfnTonePlay = audioTestGstAtsTonePlayCallback;
719 Callbacks.pfnToneRecord = audioTestGstAtsToneRecordCallback;
720 Callbacks.pfnTestSetSendBegin = audioTestGstAtsTestSetSendBeginCallback;
721 Callbacks.pfnTestSetSendRead = audioTestGstAtsTestSetSendReadCallback;
722 Callbacks.pfnTestSetSendEnd = audioTestGstAtsTestSetSendEndCallback;
723 Callbacks.pvUser = &Ctx;
724
725 /*
726 * Start the ATS (Audio Test Service) on the guest side.
727 * That service then will perform playback and recording operations on the guest, triggered from the host.
728 *
729 * When running this in self-test mode, that service also will be run on the host.
730 */
731 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Starting guest ATS at %s:%RU32...\n",
732 (pszGuestTcpAddr && *pszGuestTcpAddr) ? pszGuestTcpAddr : "127.0.0.1",
733 uGuestTcpPort ? uGuestTcpPort : ATS_TCP_GUEST_DEFAULT_PORT);
734 rc = AudioTestSvcInit(&pTstEnv->u.Guest.Srv, pszGuestTcpAddr, uGuestTcpPort, &Callbacks);
735 if (RT_SUCCESS(rc))
736 rc = AudioTestSvcStart(&pTstEnv->u.Guest.Srv);
737
738 if (RT_FAILURE(rc))
739 RTTestFailed(g_hTest, "Starting ATS failed with %Rrc\n", rc);
740 }
741 else /* Host mode */
742 {
743 rc = audioTestEnvConnectToHostAts(pTstEnv, pszHostTcpAddr, uHostTcpPort);
744 if (RT_SUCCESS(rc))
745 rc = audioTestEnvConnectToGuestAts(pTstEnv, pszGuestTcpAddr, uGuestTcpPort);
746 }
747
748 if ( RT_FAILURE(rc)
749 && fUseDriverStack)
750 audioTestDriverStackDelete(&pTstEnv->DrvStack);
751
752 return rc;
753}
754
755/**
756 * Destroys an audio test environment.
757 *
758 * @param pTstEnv Audio test environment to destroy.
759 */
760void audioTestEnvDestroy(PAUDIOTESTENV pTstEnv)
761{
762 if (!pTstEnv)
763 return;
764
765 PDMAudioHostEnumDelete(&pTstEnv->DevEnum);
766
767 for (unsigned i = 0; i < RT_ELEMENTS(pTstEnv->aStreams); i++)
768 {
769 int rc2 = audioTestStreamDestroy(pTstEnv, &pTstEnv->aStreams[i]);
770 if (RT_FAILURE(rc2))
771 RTTestFailed(g_hTest, "Stream destruction for stream #%u failed with %Rrc\n", i, rc2);
772 }
773
774 /* Try cleaning up a bit. */
775 RTDirRemove(pTstEnv->szPathTemp);
776 RTDirRemove(pTstEnv->szPathOut);
777
778 audioTestDriverStackDelete(&pTstEnv->DrvStack);
779}
780
781/**
782 * Closes, packs up and destroys a test environment.
783 *
784 * @returns VBox status code.
785 * @param pTstEnv Test environment to handle.
786 * @param fPack Whether to pack the test set up before destroying / wiping it.
787 * @param pszPackFile Where to store the packed test set file on success. Can be NULL if \a fPack is \c false.
788 * @param cbPackFile Size (in bytes) of \a pszPackFile. Can be 0 if \a fPack is \c false.
789 */
790int audioTestEnvPrologue(PAUDIOTESTENV pTstEnv, bool fPack, char *pszPackFile, size_t cbPackFile)
791{
792 /* Close the test set first. */
793 AudioTestSetClose(&pTstEnv->Set);
794
795 int rc = VINF_SUCCESS;
796
797 if (fPack)
798 {
799 /* Before destroying the test environment, pack up the test set so
800 * that it's ready for transmission. */
801 rc = AudioTestSetPack(&pTstEnv->Set, pTstEnv->szPathOut, pszPackFile, cbPackFile);
802 if (RT_SUCCESS(rc))
803 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test set packed up to '%s'\n", pszPackFile);
804 }
805
806 /* ignore rc */ AudioTestSetWipe(&pTstEnv->Set);
807
808 AudioTestSetDestroy(&pTstEnv->Set);
809
810 if (RT_FAILURE(rc))
811 RTTestFailed(g_hTest, "Test set prologue failed with %Rrc\n", rc);
812
813 return rc;
814}
815
816/**
817 * Initializes an audio test parameters set.
818 *
819 * @param pTstParms Test parameters set to initialize.
820 */
821void audioTestParmsInit(PAUDIOTESTPARMS pTstParms)
822{
823 RT_ZERO(*pTstParms);
824}
825
826/**
827 * Destroys an audio test parameters set.
828 *
829 * @param pTstParms Test parameters set to destroy.
830 */
831void audioTestParmsDestroy(PAUDIOTESTPARMS pTstParms)
832{
833 if (!pTstParms)
834 return;
835
836 return;
837}
838
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