VirtualBox

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

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

Audio/ValKit: Implemented support for downloading (host) 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 89685 2021-06-14 15:41:09Z 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} ATSCALLBACKCTX;
64typedef ATSCALLBACKCTX *PATSCALLBACKCTX;
65
66
67/*********************************************************************************************************************************
68* Internal Functions *
69*********************************************************************************************************************************/
70static int audioTestCreateStreamDefaultIn(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps);
71static int audioTestCreateStreamDefaultOut(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps);
72static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream);
73static int audioTestDevicesEnumerateAndCheck(PAUDIOTESTENV pTstEnv, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev);
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 pTstEnv Test env 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 */
89static int audioTestDevicesEnumerateAndCheck(PAUDIOTESTENV pTstEnv, const char *pszDev, PPDMAUDIOHOSTDEV *ppDev)
90{
91#ifdef DEBUG_andy
92 return VINF_SUCCESS;
93#endif
94
95 RTTestSubF(g_hTest, "Enumerating audio devices and checking for device '%s'", pszDev ? pszDev : "<Default>");
96
97 if (!pTstEnv->DrvStack.pIHostAudio->pfnGetDevices)
98 {
99 RTTestSkipped(g_hTest, "Backend does not support device enumeration, skipping");
100 return VINF_NOT_SUPPORTED;
101 }
102
103 Assert(pszDev == NULL || ppDev);
104
105 if (ppDev)
106 *ppDev = NULL;
107
108 int rc = pTstEnv->DrvStack.pIHostAudio->pfnGetDevices(pTstEnv->DrvStack.pIHostAudio, &pTstEnv->DevEnum);
109 if (RT_SUCCESS(rc))
110 {
111 PPDMAUDIOHOSTDEV pDev;
112 RTListForEach(&pTstEnv->DevEnum.LstDevices, pDev, PDMAUDIOHOSTDEV, ListEntry)
113 {
114 char szFlags[PDMAUDIOHOSTDEV_MAX_FLAGS_STRING_LEN];
115 if (pDev->pszId)
116 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s' (ID '%s'):\n", pDev->pszName, pDev->pszId);
117 else
118 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Device '%s':\n", pDev->pszName);
119 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Usage = %s\n", PDMAudioDirGetName(pDev->enmUsage));
120 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Flags = %s\n", PDMAudioHostDevFlagsToString(szFlags, pDev->fFlags));
121 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Input channels = %RU8\n", pDev->cMaxInputChannels);
122 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Enum: Output channels = %RU8\n", pDev->cMaxOutputChannels);
123
124 if ( pszDev
125 && !RTStrCmp(pDev->pszName, pszDev))
126 {
127 *ppDev = pDev;
128 }
129 }
130 }
131 else
132 RTTestFailed(g_hTest, "Enumerating audio devices failed with %Rrc", rc);
133
134 RTTestSubDone(g_hTest);
135
136 if ( pszDev
137 && *ppDev == NULL)
138 {
139 RTTestFailed(g_hTest, "Audio device '%s' not found", pszDev);
140 return VERR_NOT_FOUND;
141 }
142
143 return VINF_SUCCESS;
144}
145
146/**
147 * Opens an audio device.
148 *
149 * @returns VBox status code.
150 * @param pDev Audio device to open.
151 */
152int audioTestDeviceOpen(PPDMAUDIOHOSTDEV pDev)
153{
154 int rc = VINF_SUCCESS;
155
156 RTTestSubF(g_hTest, "Opening audio device '%s' ...", pDev->pszName);
157
158 /** @todo Detect + open device here. */
159
160 RTTestSubDone(g_hTest);
161
162 return rc;
163}
164
165/**
166 * Closes an audio device.
167 *
168 * @returns VBox status code.
169 * @param pDev Audio device to close.
170 */
171int audioTestDeviceClose(PPDMAUDIOHOSTDEV pDev)
172{
173 int rc = VINF_SUCCESS;
174
175 RTTestSubF(g_hTest, "Closing audio device '%s' ...", pDev->pszName);
176
177 /** @todo Close device here. */
178
179 RTTestSubDone(g_hTest);
180
181 return rc;
182}
183
184/**
185 * Destroys an audio test stream.
186 *
187 * @returns VBox status code.
188 * @param pTstEnv Test environment the stream to destroy contains.
189 * @param pStream Audio stream to destroy.
190 */
191static int audioTestStreamDestroy(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream)
192{
193 int rc = VINF_SUCCESS;
194 if (pStream && pStream->pStream)
195 {
196 /** @todo Anything else to do here, e.g. test if there are left over samples or some such? */
197
198 audioTestDriverStackStreamDestroy(&pTstEnv->DrvStack, pStream->pStream);
199 pStream->pStream = NULL;
200 pStream->pBackend = NULL;
201 }
202
203 return rc;
204}
205
206/**
207 * Creates an audio default input (recording) test stream.
208 * Convenience function.
209 *
210 * @returns VBox status code.
211 * @param pTstEnv Test environment to use for creating the stream.
212 * @param pStream Audio stream to create.
213 * @param pProps PCM properties to use for creation.
214 */
215static int audioTestCreateStreamDefaultIn(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)
216{
217 pStream->pBackend = NULL;
218 int rc = audioTestDriverStackStreamCreateInput(&pTstEnv->DrvStack, pProps, pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer,
219 pTstEnv->cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
220 if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)
221 pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
222 return rc;
223}
224
225/**
226 * Creates an audio default output (playback) test stream.
227 * Convenience function.
228 *
229 * @returns VBox status code.
230 * @param pTstEnv Test environment to use for creating the stream.
231 * @param pStream Audio stream to create.
232 * @param pProps PCM properties to use for creation.
233 */
234static int audioTestCreateStreamDefaultOut(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PPDMAUDIOPCMPROPS pProps)
235{
236 pStream->pBackend = NULL;
237 int rc = audioTestDriverStackStreamCreateOutput(&pTstEnv->DrvStack, pProps, pTstEnv->cMsBufferSize, pTstEnv->cMsPreBuffer,
238 pTstEnv->cMsSchedulingHint, &pStream->pStream, &pStream->Cfg);
239 if (RT_SUCCESS(rc) && !pTstEnv->DrvStack.pIAudioConnector)
240 pStream->pBackend = &((PAUDIOTESTDRVSTACKSTREAM)pStream->pStream)->Backend;
241 return rc;
242}
243
244
245/*********************************************************************************************************************************
246* Test Primitives *
247*********************************************************************************************************************************/
248
249/**
250 * Plays a test tone on a specific audio test stream.
251 *
252 * @returns VBox status code.
253 * @param pTstEnv Test environment to use for running the test.
254 * @param pStream Stream to use for playing the tone.
255 * @param pParms Tone parameters to use.
256 *
257 * @note Blocking function.
258 */
259static int audioTestPlayTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
260{
261 AUDIOTESTTONE TstTone;
262 AudioTestToneInit(&TstTone, &pParms->Props, pParms->dbFreqHz);
263
264 const char *pcszPathOut = pTstEnv->Set.szPathAbs;
265
266 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Playing test tone (tone frequency is %RU16Hz, %RU32ms)\n", (uint16_t)pParms->dbFreqHz, pParms->msDuration);
267 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Writing to '%s'\n", pcszPathOut);
268
269 /** @todo Use .WAV here? */
270 PAUDIOTESTOBJ pObj;
271 int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "guest-tone-play.pcm", &pObj);
272 AssertRCReturn(rc, rc);
273
274 if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))
275 {
276 uint32_t cbBuf;
277 uint8_t abBuf[_4K];
278
279 const uint32_t cbPerSched = PDMAudioPropsMilliToBytes(&pParms->Props, pTstEnv->cMsSchedulingHint);
280 AssertStmt(cbPerSched, rc = VERR_INVALID_PARAMETER);
281 uint32_t cbToWrite = PDMAudioPropsMilliToBytes(&pParms->Props, pParms->msDuration);
282 AssertStmt(cbToWrite, rc = VERR_INVALID_PARAMETER);
283
284 if (RT_SUCCESS(rc))
285 {
286 AudioTestSetObjAddMetadataStr(pObj, "buffer_size_ms=%RU32\n", pTstEnv->cMsBufferSize);
287 AudioTestSetObjAddMetadataStr(pObj, "prebuf_size_ms=%RU32\n", pTstEnv->cMsPreBuffer);
288 AudioTestSetObjAddMetadataStr(pObj, "scheduling_hint_ms=%RU32\n", pTstEnv->cMsSchedulingHint);
289
290 while (cbToWrite)
291 {
292 uint32_t cbWritten = 0;
293 uint32_t cbToGenerate = RT_MIN(cbToWrite, RT_MIN(cbPerSched, sizeof(abBuf)));
294 Assert(cbToGenerate);
295
296 rc = AudioTestToneGenerate(&TstTone, abBuf, cbToGenerate, &cbBuf);
297 if (RT_SUCCESS(rc))
298 {
299 /* Write stuff to disk before trying to play it. Help analysis later. */
300 rc = AudioTestSetObjWrite(pObj, abBuf, cbBuf);
301 if (RT_SUCCESS(rc))
302 rc = audioTestDriverStackStreamPlay(&pTstEnv->DrvStack, pStream->pStream,
303 abBuf, cbBuf, &cbWritten);
304 }
305
306 if (RT_FAILURE(rc))
307 break;
308
309 RTThreadSleep(pTstEnv->cMsSchedulingHint);
310
311 Assert(cbToWrite >= cbWritten);
312 cbToWrite -= cbWritten;
313 }
314 }
315 }
316 else
317 rc = VERR_AUDIO_STREAM_NOT_READY;
318
319 int rc2 = AudioTestSetObjClose(pObj);
320 if (RT_SUCCESS(rc))
321 rc = rc2;
322
323 if (RT_FAILURE(rc))
324 RTTestFailed(g_hTest, "Playing tone done failed with %Rrc\n", rc);
325
326 return rc;
327}
328
329/**
330 * Records a test tone from a specific audio test stream.
331 *
332 * @returns VBox status code.
333 * @param pTstEnv Test environment to use for running the test.
334 * @param pStream Stream to use for recording the tone.
335 * @param pParms Tone parameters to use.
336 *
337 * @note Blocking function.
338 */
339static int audioTestRecordTone(PAUDIOTESTENV pTstEnv, PAUDIOTESTSTREAM pStream, PAUDIOTESTTONEPARMS pParms)
340{
341 const char *pcszPathOut = pTstEnv->Set.szPathAbs;
342
343 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Recording test tone (for %RU32ms)\n", pParms->msDuration);
344 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Writing to '%s'\n", pcszPathOut);
345
346 /** @todo Use .WAV here? */
347 PAUDIOTESTOBJ pObj;
348 int rc = AudioTestSetObjCreateAndRegister(&pTstEnv->Set, "guest-tone-rec.pcm", &pObj);
349 AssertRCReturn(rc, rc);
350
351 if (audioTestDriverStackStreamIsOkay(&pTstEnv->DrvStack, pStream->pStream))
352 {
353 const uint32_t cbPerSched = PDMAudioPropsMilliToBytes(&pParms->Props, pTstEnv->cMsSchedulingHint);
354 AssertStmt(cbPerSched, rc = VERR_INVALID_PARAMETER);
355 uint32_t cbToRead = PDMAudioPropsMilliToBytes(&pParms->Props, pParms->msDuration);
356 AssertStmt(cbToRead, rc = VERR_INVALID_PARAMETER);
357
358 if (RT_SUCCESS(rc))
359 {
360 AudioTestSetObjAddMetadataStr(pObj, "buffer_size_ms=%RU32\n", pTstEnv->cMsBufferSize);
361 AudioTestSetObjAddMetadataStr(pObj, "prebuf_size_ms=%RU32\n", pTstEnv->cMsPreBuffer);
362 AudioTestSetObjAddMetadataStr(pObj, "scheduling_hint_ms=%RU32\n", pTstEnv->cMsSchedulingHint);
363
364 uint8_t abBuf[_4K];
365
366 while (cbToRead)
367 {
368 const uint32_t cbChunk = RT_MIN(cbToRead, RT_MIN(cbPerSched, sizeof(abBuf)));
369
370 uint32_t cbRead = 0;
371 rc = audioTestDriverStackStreamCapture(&pTstEnv->DrvStack, pStream->pStream, (void *)abBuf, cbChunk, &cbRead);
372 if (RT_SUCCESS(rc))
373 rc = AudioTestSetObjWrite(pObj, abBuf, cbRead);
374
375 if (RT_FAILURE(rc))
376 break;
377
378 RTThreadSleep(pTstEnv->cMsSchedulingHint);
379
380 Assert(cbToRead >= cbRead);
381 cbToRead -= cbRead;
382 }
383 }
384 }
385 else
386 rc = VERR_AUDIO_STREAM_NOT_READY;
387
388 int rc2 = AudioTestSetObjClose(pObj);
389 if (RT_SUCCESS(rc))
390 rc = rc2;
391
392 if (RT_FAILURE(rc))
393 RTTestFailed(g_hTest, "Recording tone done failed with %Rrc\n", rc);
394
395 return rc;
396}
397
398
399/*********************************************************************************************************************************
400* ATS Callback Implementations *
401*********************************************************************************************************************************/
402
403/** @copydoc ATSCALLBACKS::pfnTestSetBegin
404 *
405 * @note Runs as part of the guest ATS.
406 */
407static DECLCALLBACK(int) audioTestGstAtsTestSetBeginCallback(void const *pvUser, const char *pszTag)
408{
409 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
410 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
411
412 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Beginning test set '%s' in '%s'\n", pszTag, pTstEnv->szPathTemp);
413
414 return AudioTestSetCreate(&pTstEnv->Set, pTstEnv->szPathTemp, pszTag);
415}
416
417/** @copydoc ATSCALLBACKS::pfnTestSetEnd
418 *
419 * @note Runs as part of the guest ATS.
420 */
421static DECLCALLBACK(int) audioTestGstAtsTestSetEndCallback(void const *pvUser, const char *pszTag)
422{
423 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
424 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
425
426 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Ending test set '%s'\n", pszTag);
427
428 /* Pack up everything to be ready for transmission. */
429 return audioTestEnvPrologue(pTstEnv, true /* fPack */, pCtx->szTestSetArchive, sizeof(pCtx->szTestSetArchive));
430}
431
432/** @copydoc ATSCALLBACKS::pfnTonePlay
433 *
434 * @note Runs as part of the guest ATS.
435 */
436static DECLCALLBACK(int) audioTestGstAtsTonePlayCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
437{
438 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
439 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
440
441 AUDIOTESTTONE TstTone;
442 AudioTestToneInitRandom(&TstTone, &pToneParms->Props);
443
444 const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
445
446 int rc = audioTestCreateStreamDefaultOut(pTstEnv, pTstStream, &pToneParms->Props);
447 if (RT_SUCCESS(rc))
448 {
449 AUDIOTESTPARMS TstParms;
450 RT_ZERO(TstParms);
451 TstParms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
452 TstParms.enmDir = PDMAUDIODIR_OUT;
453 TstParms.TestTone = *pToneParms;
454
455 PAUDIOTESTENTRY pTst;
456 rc = AudioTestSetTestBegin(&pTstEnv->Set, "Playing test tone", &TstParms, &pTst);
457 if (RT_SUCCESS(rc))
458 {
459 rc = audioTestPlayTone(pTstEnv, pTstStream, pToneParms);
460 if (RT_SUCCESS(rc))
461 {
462 AudioTestSetTestDone(pTst);
463 }
464 else
465 AudioTestSetTestFailed(pTst, rc, "Playing tone failed");
466 }
467
468 int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
469 if (RT_SUCCESS(rc))
470 rc = rc2;
471 }
472 else
473 RTTestFailed(g_hTest, "Error creating output stream, rc=%Rrc\n", rc);
474
475 return rc;
476}
477
478/** @copydoc ATSCALLBACKS::pfnToneRecord */
479static DECLCALLBACK(int) audioTestGstAtsToneRecordCallback(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
480{
481 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
482 PAUDIOTESTENV pTstEnv = pCtx->pTstEnv;
483
484 const PAUDIOTESTSTREAM pTstStream = &pTstEnv->aStreams[0]; /** @todo Make this dynamic. */
485
486 int rc = audioTestCreateStreamDefaultIn(pTstEnv, pTstStream, &pToneParms->Props);
487 if (RT_SUCCESS(rc))
488 {
489 AUDIOTESTPARMS TstParms;
490 RT_ZERO(TstParms);
491 TstParms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
492 TstParms.enmDir = PDMAUDIODIR_IN;
493 TstParms.Props = pToneParms->Props;
494 TstParms.TestTone = *pToneParms;
495
496 PAUDIOTESTENTRY pTst;
497 rc = AudioTestSetTestBegin(&pTstEnv->Set, "Recording test tone from host", &TstParms, &pTst);
498 if (RT_SUCCESS(rc))
499 {
500 rc = audioTestRecordTone(pTstEnv, pTstStream, pToneParms);
501 if (RT_SUCCESS(rc))
502 {
503 AudioTestSetTestDone(pTst);
504 }
505 else
506 AudioTestSetTestFailed(pTst, rc, "Recording tone failed");
507 }
508
509 int rc2 = audioTestStreamDestroy(pTstEnv, pTstStream);
510 if (RT_SUCCESS(rc))
511 rc = rc2;
512 }
513 else
514 RTTestFailed(g_hTest, "Error creating input stream, rc=%Rrc\n", rc);
515
516 return rc;
517}
518
519/** @copydoc ATSCALLBACKS::pfnTestSetSendBegin */
520static DECLCALLBACK(int) audioTestGstAtsTestSetSendBeginCallback(void const *pvUser, const char *pszTag)
521{
522 RT_NOREF(pszTag);
523
524 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
525
526 if (!RTFileExists(pCtx->szTestSetArchive)) /* Has the archive successfully been created yet? */
527 return VERR_WRONG_ORDER;
528
529 int rc = RTFileOpen(&pCtx->hTestSetArchive, pCtx->szTestSetArchive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
530 if (RT_SUCCESS(rc))
531 {
532 uint64_t uSize;
533 rc = RTFileQuerySize(pCtx->hTestSetArchive, &uSize);
534 if (RT_SUCCESS(rc))
535 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Sending test set '%s' (%zu bytes)\n", pCtx->szTestSetArchive, uSize);
536 }
537
538 return rc;
539}
540
541/** @copydoc ATSCALLBACKS::pfnTestSetSendRead */
542static DECLCALLBACK(int) audioTestGstAtsTestSetSendReadCallback(void const *pvUser,
543 const char *pszTag, void *pvBuf, size_t cbBuf, size_t *pcbRead)
544{
545 RT_NOREF(pszTag);
546
547 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
548
549 return RTFileRead(pCtx->hTestSetArchive, pvBuf, cbBuf, pcbRead);
550}
551
552/** @copydoc ATSCALLBACKS::pfnTestSetSendEnd */
553static DECLCALLBACK(int) audioTestGstAtsTestSetSendEndCallback(void const *pvUser, const char *pszTag)
554{
555 RT_NOREF(pszTag);
556
557 PATSCALLBACKCTX pCtx = (PATSCALLBACKCTX)pvUser;
558
559 int rc = RTFileClose(pCtx->hTestSetArchive);
560 if (RT_SUCCESS(rc))
561 {
562 pCtx->hTestSetArchive = NIL_RTFILE;
563 }
564
565 return rc;
566}
567
568
569/*********************************************************************************************************************************
570* Implementation of audio test environment handling *
571*********************************************************************************************************************************/
572
573int audioTestEnvConnectToHostAts(PAUDIOTESTENV pTstEnv,
574 const char *pszHostTcpAddr, uint32_t uHostTcpPort)
575{
576 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connecting to host ATS at %s:%RU32 ...\n",
577 (pszHostTcpAddr && *pszHostTcpAddr) ? pszHostTcpAddr : ATS_TCP_HOST_DEFAULT_ADDR_STR,
578 uHostTcpPort ? uHostTcpPort : ATS_TCP_HOST_DEFAULT_PORT);
579
580 int rc = AudioTestSvcClientConnect(&pTstEnv->u.Host.AtsClValKit, pszHostTcpAddr, uHostTcpPort);
581 if (RT_FAILURE(rc))
582 {
583 RTTestFailed(g_hTest, "Connecting to host ATS failed with %Rrc\n", rc);
584 return rc;
585 }
586
587 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connected to host ATS\n");
588 return rc;
589}
590
591int audioTestEnvConnectToGuestAts(PAUDIOTESTENV pTstEnv,
592 const char *pszGuestTcpAddr, uint32_t uGuestTcpPort)
593{
594 RTTestPrintf(g_hTest, RTTESTLVL_DEBUG, "Connecting to guest ATS at %s:%RU32 ...\n",
595 (pszGuestTcpAddr && *pszGuestTcpAddr) ? pszGuestTcpAddr : "127.0.0.1",
596 uGuestTcpPort ? uGuestTcpPort : ATS_TCP_GUEST_DEFAULT_PORT);
597
598 int rc = AudioTestSvcClientConnect(&pTstEnv->u.Host.AtsClGuest, pszGuestTcpAddr, uGuestTcpPort);
599 if (RT_FAILURE(rc))
600 {
601 RTTestFailed(g_hTest, "Connecting to guest ATS failed with %Rrc\n", rc);
602 return rc;
603 }
604
605 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Connected to guest ATS\n");
606 return rc;
607}
608
609/**
610 * Initializes an audio test environment.
611 *
612 * @param pTstEnv Audio test environment to initialize.
613 * @param pDrvReg Audio driver to use.
614 * @param fWithDrvAudio Whether to include DrvAudio in the stack or not.
615 * @param pszHostTcpAddr Host ATS TCP/IP address to connect to.
616 * If NULL, ATS_TCP_HOST_DEFAULT_ADDR_STR will be used.
617 * @param uHostTcpPort Host ATS TCP/IP port to connect to.
618 * If 0, ATS_TCP_HOST_DEFAULT_PORT will be used.
619 * @param pszGuestTcpAddr Guest ATS TCP/IP address to connect to.
620 * If NULL, localhost (127.0.0.1) will be used.
621 * @param uGuestTcpPort Guest ATS TCP/IP port to connect to.
622 * If 0, ATS_TCP_GUEST_DEFAULT_PORT will be used.
623 */
624int audioTestEnvInit(PAUDIOTESTENV pTstEnv,
625 PCPDMDRVREG pDrvReg, bool fWithDrvAudio,
626 const char *pszHostTcpAddr, uint32_t uHostTcpPort,
627 const char *pszGuestTcpAddr, uint32_t uGuestTcpPort)
628{
629 int rc = VINF_SUCCESS;
630
631 /*
632 * Set sane defaults if not already set.
633 */
634 if (!RTStrNLen(pTstEnv->szTag, sizeof(pTstEnv->szTag)))
635 {
636 rc = AudioTestGenTag(pTstEnv->szTag, sizeof(pTstEnv->szTag));
637 AssertRCReturn(rc, rc);
638 }
639
640 if (!RTStrNLen(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp)))
641 {
642 rc = AudioTestPathGetTemp(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp));
643 AssertRCReturn(rc, rc);
644 }
645
646 if (!RTStrNLen(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut)))
647 {
648 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), pTstEnv->szPathTemp, "vkat-temp");
649 AssertRCReturn(rc, rc);
650 }
651
652 if (pDrvReg == NULL)
653 {
654 if (pTstEnv->fSelftest)
655 pDrvReg = &g_DrvHostValidationKitAudio;
656 else /* Go with the platform's default backend if nothing else is set. */
657 pDrvReg = AudioTestGetDefaultBackend();
658 }
659
660 if (!uHostTcpPort)
661 uHostTcpPort = ATS_TCP_HOST_DEFAULT_PORT;
662
663 if (!uGuestTcpPort)
664 uGuestTcpPort = ATS_TCP_GUEST_DEFAULT_PORT;
665
666 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Test mode is '%s'\n", pTstEnv->enmMode == AUDIOTESTMODE_HOST ? "host" : "guest");
667 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Using tag '%s'\n", pTstEnv->szTag);
668 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Output directory is '%s'\n", pTstEnv->szPathOut);
669 RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "Temp directory is '%s'\n", pTstEnv->szPathTemp);
670
671 PDMAudioHostEnumInit(&pTstEnv->DevEnum);
672
673 pTstEnv->cMsBufferSize = 300; /* ms */ /** @todo Randomize this also? */
674 pTstEnv->cMsPreBuffer = 150; /* ms */ /** @todo Ditto. */
675 pTstEnv->cMsSchedulingHint = RTRandU32Ex(10, 80); /* Choose a random scheduling (in ms). */
676
677 /* Only the guest mode needs initializing the driver stack. */
678 const bool fUseDriverStack = pTstEnv->enmMode == AUDIOTESTMODE_GUEST;
679 if (fUseDriverStack)
680 {
681 rc = audioTestDriverStackInitEx(&pTstEnv->DrvStack, pDrvReg,
682 true /* fEnabledIn */, true /* fEnabledOut */, fWithDrvAudio);
683 if (RT_FAILURE(rc))
684 return rc;
685
686 PPDMAUDIOHOSTDEV pDev;
687 rc = audioTestDevicesEnumerateAndCheck(pTstEnv, NULL /* pszDevice */, &pDev); /** @todo Implement device checking. */
688 if (RT_FAILURE(rc))
689 return rc;
690 }
691
692 char szPathTemp[RTPATH_MAX];
693 if ( !strlen(pTstEnv->szPathTemp)
694 || !strlen(pTstEnv->szPathOut))
695 rc = RTPathTemp(szPathTemp, sizeof(szPathTemp));
696
697 if ( RT_SUCCESS(rc)
698 && !strlen(pTstEnv->szPathTemp))
699 rc = RTPathJoin(pTstEnv->szPathTemp, sizeof(pTstEnv->szPathTemp), szPathTemp, "vkat-temp");
700
701 if ( RT_SUCCESS(rc)
702 && !strlen(pTstEnv->szPathOut))
703 rc = RTPathJoin(pTstEnv->szPathOut, sizeof(pTstEnv->szPathOut), szPathTemp, "vkat");
704
705 if (RT_FAILURE(rc))
706 return rc;
707
708 /** @todo Implement NAT mode like we do for TxS later? */
709 if (pTstEnv->enmMode == AUDIOTESTMODE_GUEST)
710 {
711 ATSCALLBACKCTX Ctx;
712 Ctx.pTstEnv = pTstEnv;
713
714 ATSCALLBACKS Callbacks;
715 RT_ZERO(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