VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioPulseAudio.cpp@ 88386

Last change on this file since 88386 was 88386, checked in by vboxsync, 4 years ago

DrvHostAudioPulseAudio: Fail if pa_context_connect() fails. Baked drvHostPulseAudioHA_Init into the constructor and drvHostPulseAudioHA_Shutdown into the power off callback and destructor callbacks. (untested) bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.2 KB
Line 
1/* $Id: DrvHostAudioPulseAudio.cpp 88386 2021-04-07 09:55:22Z vboxsync $ */
2/** @file
3 * Host audio driver - Pulse Audio.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
23#include <VBox/log.h>
24#include <VBox/vmm/pdmaudioifs.h>
25#include <VBox/vmm/pdmaudioinline.h>
26
27#include <stdio.h>
28
29#include <iprt/alloc.h>
30#include <iprt/mem.h>
31#include <iprt/uuid.h>
32#include <iprt/semaphore.h>
33
34#include "DrvHostAudioPulseAudioStubsMangling.h"
35#include "DrvHostAudioPulseAudioStubs.h"
36
37#include <pulse/pulseaudio.h>
38
39#include "VBoxDD.h"
40
41
42/*********************************************************************************************************************************
43* Defines *
44*********************************************************************************************************************************/
45#define VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS 32 /** @todo Make this configurable thru driver options. */
46
47#ifndef PA_STREAM_NOFLAGS
48# define PA_STREAM_NOFLAGS (pa_context_flags_t)0x0000U /* since 0.9.19 */
49#endif
50
51#ifndef PA_CONTEXT_NOFLAGS
52# define PA_CONTEXT_NOFLAGS (pa_context_flags_t)0x0000U /* since 0.9.19 */
53#endif
54
55/** No flags specified. */
56#define PULSEAUDIOENUMCBFLAGS_NONE 0
57/** (Release) log found devices. */
58#define PULSEAUDIOENUMCBFLAGS_LOG RT_BIT(0)
59
60/** Makes DRVHOSTPULSEAUDIO out of PDMIHOSTAUDIO. */
61#define PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface) \
62 ( (PDRVHOSTPULSEAUDIO)((uintptr_t)pInterface - RT_UOFFSETOF(DRVHOSTPULSEAUDIO, IHostAudio)) )
63
64
65/*********************************************************************************************************************************
66* Structures *
67*********************************************************************************************************************************/
68/**
69 * Callback context for the server init context state changed callback.
70 */
71typedef struct PULSEAUDIOSTATECHGCTX
72{
73 /** The event semaphore. */
74 RTSEMEVENT hEvtInit;
75 /** The returned context state. */
76 volatile pa_context_state_t enmCtxState;
77} PULSEAUDIOSTATECHGCTX;
78/** Pointer to a server init context state changed callback context. */
79typedef PULSEAUDIOSTATECHGCTX *PPULSEAUDIOSTATECHGCTX;
80
81/**
82 * Host Pulse audio driver instance data.
83 * @implements PDMIAUDIOCONNECTOR
84 */
85typedef struct DRVHOSTPULSEAUDIO
86{
87 /** Pointer to the driver instance structure. */
88 PPDMDRVINS pDrvIns;
89 /** Pointer to PulseAudio's threaded main loop. */
90 pa_threaded_mainloop *pMainLoop;
91 /**
92 * Pointer to our PulseAudio context.
93 * Note: We use a pMainLoop in a separate thread (pContext).
94 * So either use callback functions or protect these functions
95 * by pa_threaded_mainloop_lock() / pa_threaded_mainloop_unlock().
96 */
97 pa_context *pContext;
98 /** Shutdown indicator. */
99 volatile bool fAbortLoop;
100 /** Enumeration operation successful? */
101 volatile bool fEnumOpSuccess;
102 /** Pointer to host audio interface. */
103 PDMIHOSTAUDIO IHostAudio;
104 /** Error count for not flooding the release log.
105 * Specify UINT32_MAX for unlimited logging. */
106 uint32_t cLogErrors;
107 /** The stream (base) name; needed for distinguishing
108 * streams in the PulseAudio mixer controls if multiple
109 * VMs are running at the same time. */
110 char szStreamName[64];
111
112 /** Don't want to put this on the stack... */
113 PULSEAUDIOSTATECHGCTX InitStateChgCtx;
114} DRVHOSTPULSEAUDIO, *PDRVHOSTPULSEAUDIO;
115
116typedef struct PULSEAUDIOSTREAM
117{
118 /** The stream's acquired configuration. */
119 PPDMAUDIOSTREAMCFG pCfg;
120 /** Pointer to driver instance. */
121 PDRVHOSTPULSEAUDIO pDrv;
122 /** Pointer to opaque PulseAudio stream. */
123 pa_stream *pStream;
124 /** Pulse sample format and attribute specification. */
125 pa_sample_spec SampleSpec;
126 /** Pulse playback and buffer metrics. */
127 pa_buffer_attr BufAttr;
128 int fOpSuccess;
129 /** Pointer to Pulse sample peeking buffer. */
130 const uint8_t *pu8PeekBuf;
131 /** Current size (in bytes) of peeking data in
132 * buffer. */
133 size_t cbPeekBuf;
134 /** Our offset (in bytes) in peeking buffer. */
135 size_t offPeekBuf;
136 pa_operation *pDrainOp;
137 /** Number of occurred audio data underflows. */
138 uint32_t cUnderflows;
139 /** Current latency (in us). */
140 uint64_t curLatencyUs;
141#ifdef LOG_ENABLED
142 /** Start time stamp (in us) of stream playback / recording. */
143 pa_usec_t tsStartUs;
144 /** Time stamp (in us) when last read from / written to the stream. */
145 pa_usec_t tsLastReadWrittenUs;
146#endif
147} PULSEAUDIOSTREAM, *PPULSEAUDIOSTREAM;
148
149/**
150 * Callback context for server enumeration callbacks.
151 */
152typedef struct PULSEAUDIOENUMCBCTX
153{
154 /** Pointer to host backend driver. */
155 PDRVHOSTPULSEAUDIO pDrv;
156 /** Enumeration flags. */
157 uint32_t fFlags;
158 /** Number of found input devices. */
159 uint8_t cDevIn;
160 /** Number of found output devices. */
161 uint8_t cDevOut;
162 /** Name of default sink being used. Must be free'd using RTStrFree(). */
163 char *pszDefaultSink;
164 /** Name of default source being used. Must be free'd using RTStrFree(). */
165 char *pszDefaultSource;
166} PULSEAUDIOENUMCBCTX, *PPULSEAUDIOENUMCBCTX;
167
168
169/*
170 * To allow running on systems with PulseAudio < 0.9.11.
171 */
172#if !defined(PA_CONTEXT_IS_GOOD) && PA_API_VERSION < 12 /* 12 = 0.9.11 where PA_STREAM_IS_GOOD was added */
173DECLINLINE(bool) PA_CONTEXT_IS_GOOD(pa_context_state_t enmState)
174{
175 return enmState == PA_CONTEXT_CONNECTING
176 || enmState == PA_CONTEXT_AUTHORIZING
177 || enmState == PA_CONTEXT_SETTING_NAME
178 || enmState == PA_CONTEXT_READY;
179}
180#endif
181
182#if !defined(PA_STREAM_IS_GOOD) && PA_API_VERSION < 12 /* 12 = 0.9.11 where PA_STREAM_IS_GOOD was added */
183DECLINLINE(bool) PA_STREAM_IS_GOOD(pa_stream_state_t enmState)
184{
185 return enmState == PA_STREAM_CREATING
186 || enmState == PA_STREAM_READY;
187}
188#endif
189
190
191/*********************************************************************************************************************************
192* Prototypes *
193*********************************************************************************************************************************/
194
195static int paEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum);
196static int paError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg);
197#ifdef DEBUG
198static void paStreamCbUnderflow(pa_stream *pStream, void *pvContext);
199static void paStreamCbReqWrite(pa_stream *pStream, size_t cbLen, void *pvContext);
200#endif
201static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvContext);
202
203
204/**
205 * Signal the main loop to abort. Just signalling isn't sufficient as the
206 * mainloop might not have been entered yet.
207 */
208static void paSignalWaiter(PDRVHOSTPULSEAUDIO pThis)
209{
210 if (!pThis)
211 return;
212
213 pThis->fAbortLoop = true;
214 pa_threaded_mainloop_signal(pThis->pMainLoop, 0);
215}
216
217
218static pa_sample_format_t paAudioPropsToPulse(PPDMAUDIOPCMPROPS pProps)
219{
220 switch (PDMAudioPropsSampleSize(pProps))
221 {
222 case 1:
223 if (!pProps->fSigned)
224 return PA_SAMPLE_U8;
225 break;
226
227 case 2:
228 if (pProps->fSigned)
229 return PDMAudioPropsIsLittleEndian(pProps) ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
230 break;
231
232#ifdef PA_SAMPLE_S32LE
233 case 4:
234 if (pProps->fSigned)
235 return PDMAudioPropsIsLittleEndian(pProps) ? PA_SAMPLE_S32LE : PA_SAMPLE_S32BE;
236 break;
237#endif
238 }
239
240 AssertMsgFailed(("%RU8%s not supported\n", PDMAudioPropsSampleSize(pProps), pProps->fSigned ? "S" : "U"));
241 return PA_SAMPLE_INVALID;
242}
243
244
245static int paPulseToAudioProps(PPDMAUDIOPCMPROPS pProps, pa_sample_format_t pulsefmt, uint8_t cChannels, uint32_t uHz)
246{
247 AssertReturn(cChannels > 0, VERR_INVALID_PARAMETER);
248 AssertReturn(cChannels < 16, VERR_INVALID_PARAMETER);
249
250 switch (pulsefmt)
251 {
252 case PA_SAMPLE_U8:
253 PDMAudioPropsInit(pProps, 1 /*8-bit*/, false /*signed*/, cChannels, uHz);
254 break;
255
256 case PA_SAMPLE_S16LE:
257 PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/);
258 break;
259
260 case PA_SAMPLE_S16BE:
261 PDMAudioPropsInitEx(pProps, 2 /*16-bit*/, true /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/);
262 break;
263
264#ifdef PA_SAMPLE_S32LE
265 case PA_SAMPLE_S32LE:
266 PDMAudioPropsInitEx(pProps, 4 /*32-bit*/, true /*signed*/, cChannels, uHz, true /*fLittleEndian*/, false /*fRaw*/);
267 break;
268#endif
269
270#ifdef PA_SAMPLE_S32BE
271 case PA_SAMPLE_S32BE:
272 PDMAudioPropsInitEx(pProps, 4 /*32-bit*/, true /*signed*/, cChannels, uHz, false /*fLittleEndian*/, false /*fRaw*/);
273 break;
274#endif
275
276 default:
277 AssertLogRelMsgFailed(("PulseAudio: Format (%d) not supported\n", pulsefmt));
278 return VERR_NOT_SUPPORTED;
279 }
280
281 return VINF_SUCCESS;
282}
283
284
285/**
286 * Synchronously wait until an operation completed.
287 */
288static int paWaitForEx(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP, RTMSINTERVAL cMsTimeout)
289{
290 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
291 AssertPtrReturn(pOP, VERR_INVALID_POINTER);
292
293 int rc = VINF_SUCCESS;
294
295 uint64_t u64StartMs = RTTimeMilliTS();
296 while (pa_operation_get_state(pOP) == PA_OPERATION_RUNNING)
297 {
298 if (!pThis->fAbortLoop)
299 {
300 AssertPtr(pThis->pMainLoop);
301 pa_threaded_mainloop_wait(pThis->pMainLoop);
302 if ( !pThis->pContext
303 || pa_context_get_state(pThis->pContext) != PA_CONTEXT_READY)
304 {
305 LogRel(("PulseAudio: pa_context_get_state context not ready\n"));
306 break;
307 }
308 }
309 pThis->fAbortLoop = false;
310
311 uint64_t u64ElapsedMs = RTTimeMilliTS() - u64StartMs;
312 if (u64ElapsedMs >= cMsTimeout)
313 {
314 rc = VERR_TIMEOUT;
315 break;
316 }
317 }
318
319 pa_operation_unref(pOP);
320
321 return rc;
322}
323
324
325static int paWaitFor(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP)
326{
327 return paWaitForEx(pThis, pOP, 10 * 1000 /* 10s timeout */);
328}
329
330
331/**
332 * Context status changed, init variant signalling our own event semaphore
333 * so we can do a timed wait.
334 */
335static void paContextCbStateChangedInit(pa_context *pCtx, void *pvUser)
336{
337 AssertPtrReturnVoid(pCtx);
338
339 PPULSEAUDIOSTATECHGCTX pStateChgCtx = (PPULSEAUDIOSTATECHGCTX)pvUser;
340 pa_context_state_t enmCtxState = pa_context_get_state(pCtx);
341 switch (enmCtxState)
342 {
343 case PA_CONTEXT_READY:
344 case PA_CONTEXT_TERMINATED:
345 case PA_CONTEXT_FAILED:
346 pStateChgCtx->enmCtxState = enmCtxState;
347 RTSemEventSignal(pStateChgCtx->hEvtInit);
348 break;
349
350 default:
351 break;
352 }
353}
354
355
356/**
357 * Context status changed.
358 */
359static void paContextCbStateChanged(pa_context *pCtx, void *pvUser)
360{
361 AssertPtrReturnVoid(pCtx);
362
363 PDRVHOSTPULSEAUDIO pThis = (PDRVHOSTPULSEAUDIO)pvUser;
364 AssertPtrReturnVoid(pThis);
365
366 switch (pa_context_get_state(pCtx))
367 {
368 case PA_CONTEXT_READY:
369 case PA_CONTEXT_TERMINATED:
370 case PA_CONTEXT_FAILED:
371 paSignalWaiter(pThis);
372 break;
373
374 default:
375 break;
376 }
377}
378
379
380/**
381 * Callback called when our pa_stream_drain operation was completed.
382 */
383static void paStreamCbDrain(pa_stream *pStream, int fSuccess, void *pvUser)
384{
385 AssertPtrReturnVoid(pStream);
386
387 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pvUser;
388 AssertPtrReturnVoid(pStreamPA);
389
390 pStreamPA->fOpSuccess = fSuccess;
391 if (fSuccess)
392 {
393 pa_operation_unref(pa_stream_cork(pStream, 1,
394 paStreamCbSuccess, pvUser));
395 }
396 else
397 paError(pStreamPA->pDrv, "Failed to drain stream");
398
399 if (pStreamPA->pDrainOp)
400 {
401 pa_operation_unref(pStreamPA->pDrainOp);
402 pStreamPA->pDrainOp = NULL;
403 }
404}
405
406
407/**
408 * Stream status changed.
409 */
410static void paStreamCbStateChanged(pa_stream *pStream, void *pvUser)
411{
412 AssertPtrReturnVoid(pStream);
413
414 PDRVHOSTPULSEAUDIO pThis = (PDRVHOSTPULSEAUDIO)pvUser;
415 AssertPtrReturnVoid(pThis);
416
417 switch (pa_stream_get_state(pStream))
418 {
419 case PA_STREAM_READY:
420 case PA_STREAM_FAILED:
421 case PA_STREAM_TERMINATED:
422 paSignalWaiter(pThis);
423 break;
424
425 default:
426 break;
427 }
428}
429
430#ifdef DEBUG
431
432static void paStreamCbReqWrite(pa_stream *pStream, size_t cbLen, void *pvContext)
433{
434 RT_NOREF(cbLen, pvContext);
435
436 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
437 AssertPtrReturnVoid(pStrm);
438
439 pa_usec_t usec = 0;
440 int neg = 0;
441 pa_stream_get_latency(pStream, &usec, &neg);
442
443 Log2Func(("Requested %zu bytes -- Current latency is %RU64ms\n", cbLen, usec / 1000));
444}
445
446
447static void paStreamCbUnderflow(pa_stream *pStream, void *pvContext)
448{
449 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
450 AssertPtrReturnVoid(pStrm);
451
452 pStrm->cUnderflows++;
453
454 LogRel2(("PulseAudio: Warning: Hit underflow #%RU32\n", pStrm->cUnderflows));
455
456 if ( pStrm->cUnderflows >= 6 /** @todo Make this check configurable. */
457 && pStrm->curLatencyUs < 2000000 /* 2s */)
458 {
459 pStrm->curLatencyUs = (pStrm->curLatencyUs * 3) / 2;
460
461 LogRel2(("PulseAudio: Output latency increased to %RU64ms\n", pStrm->curLatencyUs / 1000 /* ms */));
462
463 pStrm->BufAttr.maxlength = pa_usec_to_bytes(pStrm->curLatencyUs, &pStrm->SampleSpec);
464 pStrm->BufAttr.tlength = pa_usec_to_bytes(pStrm->curLatencyUs, &pStrm->SampleSpec);
465
466 pa_stream_set_buffer_attr(pStream, &pStrm->BufAttr, NULL, NULL);
467
468 pStrm->cUnderflows = 0;
469 }
470
471 pa_usec_t curLatencyUs = 0;
472 pa_stream_get_latency(pStream, &curLatencyUs, NULL /* Neg */);
473
474 LogRel2(("PulseAudio: Latency now is %RU64ms\n", curLatencyUs / 1000 /* ms */));
475
476# ifdef LOG_ENABLED
477 const pa_timing_info *pTInfo = pa_stream_get_timing_info(pStream);
478 const pa_sample_spec *pSpec = pa_stream_get_sample_spec(pStream);
479
480 pa_usec_t curPosWritesUs = pa_bytes_to_usec(pTInfo->write_index, pSpec);
481 pa_usec_t curPosReadsUs = pa_bytes_to_usec(pTInfo->read_index, pSpec);
482 pa_usec_t curTsUs = pa_rtclock_now() - pStrm->tsStartUs;
483
484 Log2Func(("curPosWrite=%RU64ms, curPosRead=%RU64ms, curTs=%RU64ms, curLatency=%RU64ms (%RU32Hz, %RU8 channels)\n",
485 curPosWritesUs / RT_US_1MS_64, curPosReadsUs / RT_US_1MS_64,
486 curTsUs / RT_US_1MS_64, curLatencyUs / RT_US_1MS_64, pSpec->rate, pSpec->channels));
487# endif
488}
489
490
491static void paStreamCbOverflow(pa_stream *pStream, void *pvContext)
492{
493 RT_NOREF(pStream, pvContext);
494
495 Log2Func(("Warning: Hit overflow\n"));
496}
497
498#endif /* DEBUG */
499
500static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvUser)
501{
502 AssertPtrReturnVoid(pStream);
503
504 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvUser;
505 AssertPtrReturnVoid(pStrm);
506
507 pStrm->fOpSuccess = fSuccess;
508
509 if (fSuccess)
510 paSignalWaiter(pStrm->pDrv);
511 else
512 paError(pStrm->pDrv, "Failed to finish stream operation");
513}
514
515
516static int paStreamOpen(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, bool fIn, const char *pszName)
517{
518 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
519 AssertPtrReturn(pStreamPA, VERR_INVALID_POINTER);
520 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
521
522 int rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
523 pa_stream *pStream = NULL;
524
525 pa_threaded_mainloop_lock(pThis->pMainLoop);
526
527 do /* goto avoidance non-loop */
528 {
529 pa_sample_spec *pSampleSpec = &pStreamPA->SampleSpec;
530
531 LogFunc(("Opening '%s', rate=%dHz, channels=%d, format=%s\n",
532 pszName, pSampleSpec->rate, pSampleSpec->channels,
533 pa_sample_format_to_string(pSampleSpec->format)));
534
535 if (!pa_sample_spec_valid(pSampleSpec))
536 {
537 LogRel(("PulseAudio: Unsupported sample specification for stream '%s'\n", pszName));
538 break;
539 }
540
541 pa_buffer_attr *pBufAttr = &pStreamPA->BufAttr;
542
543 /** @todo r=andy Use pa_stream_new_with_proplist instead. */
544 if (!(pStream = pa_stream_new(pThis->pContext, pszName, pSampleSpec, NULL /* pa_channel_map */)))
545 {
546 LogRel(("PulseAudio: Could not create stream '%s'\n", pszName));
547 rc = VERR_NO_MEMORY;
548 break;
549 }
550
551#ifdef DEBUG
552 pa_stream_set_write_callback (pStream, paStreamCbReqWrite, pStreamPA);
553 pa_stream_set_underflow_callback (pStream, paStreamCbUnderflow, pStreamPA);
554 if (!fIn) /* Only for output streams. */
555 pa_stream_set_overflow_callback(pStream, paStreamCbOverflow, pStreamPA);
556#endif
557 pa_stream_set_state_callback (pStream, paStreamCbStateChanged, pThis);
558
559 uint32_t flags = PA_STREAM_NOFLAGS;
560#if PA_API_VERSION >= 12
561 /* XXX */
562 flags |= PA_STREAM_ADJUST_LATENCY;
563#endif
564 /* For using pa_stream_get_latency() and pa_stream_get_time(). */
565 flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
566
567 /* No input/output right away after the stream was started. */
568 flags |= PA_STREAM_START_CORKED;
569
570 if (fIn)
571 {
572 LogFunc(("Input stream attributes: maxlength=%d fragsize=%d\n",
573 pBufAttr->maxlength, pBufAttr->fragsize));
574
575 if (pa_stream_connect_record(pStream, /*dev=*/NULL, pBufAttr, (pa_stream_flags_t)flags) < 0)
576 {
577 LogRel(("PulseAudio: Could not connect input stream '%s': %s\n",
578 pszName, pa_strerror(pa_context_errno(pThis->pContext))));
579 break;
580 }
581 }
582 else
583 {
584 LogFunc(("Output buffer attributes: maxlength=%d tlength=%d prebuf=%d minreq=%d\n",
585 pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
586
587 if (pa_stream_connect_playback(pStream, /*dev=*/NULL, pBufAttr, (pa_stream_flags_t)flags,
588 /*cvolume=*/NULL, /*sync_stream=*/NULL) < 0)
589 {
590 LogRel(("PulseAudio: Could not connect playback stream '%s': %s\n",
591 pszName, pa_strerror(pa_context_errno(pThis->pContext))));
592 break;
593 }
594 }
595
596 /* Wait until the stream is ready. */
597 pa_stream_state_t enmStreamState;
598 for (;;)
599 {
600 enmStreamState = pa_stream_get_state(pStream);
601 if ( enmStreamState == PA_STREAM_READY
602 || !PA_STREAM_IS_GOOD(enmStreamState))
603 break;
604
605 if (!pThis->fAbortLoop)
606 pa_threaded_mainloop_wait(pThis->pMainLoop);
607 pThis->fAbortLoop = false;
608 }
609 if (!PA_STREAM_IS_GOOD(enmStreamState))
610 {
611 LogRel(("PulseAudio: Failed to initialize stream '%s' (state %ld)\n", pszName, enmStreamState));
612 break;
613 }
614
615#ifdef LOG_ENABLED
616 pStreamPA->tsStartUs = pa_rtclock_now();
617#endif
618 const pa_buffer_attr *pBufAttrObtained = pa_stream_get_buffer_attr(pStream);
619 AssertPtrBreak(pBufAttrObtained);
620 memcpy(pBufAttr, pBufAttrObtained, sizeof(pa_buffer_attr));
621
622 LogFunc(("Obtained %s buffer attributes: tLength=%RU32, maxLength=%RU32, minReq=%RU32, fragSize=%RU32, preBuf=%RU32\n",
623 fIn ? "capture" : "playback",
624 pBufAttr->tlength, pBufAttr->maxlength, pBufAttr->minreq, pBufAttr->fragsize, pBufAttr->prebuf));
625
626 pStreamPA->pStream = pStream;
627
628 pa_threaded_mainloop_unlock(pThis->pMainLoop);
629 LogFlowFuncLeaveRC(VINF_SUCCESS);
630 return VINF_SUCCESS;
631
632 } while (0);
633
634 /* We failed. */
635 if (pStream)
636 pa_stream_disconnect(pStream);
637
638 pa_threaded_mainloop_unlock(pThis->pMainLoop);
639
640 if (pStream)
641 pa_stream_unref(pStream);
642 LogFlowFuncLeaveRC(rc);
643 return rc;
644}
645
646
647static int paCreateStreamOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA,
648 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
649{
650 pStreamPA->pDrainOp = NULL;
651
652 pStreamPA->SampleSpec.format = paAudioPropsToPulse(&pCfgReq->Props);
653 pStreamPA->SampleSpec.rate = PDMAudioPropsHz(&pCfgReq->Props);
654 pStreamPA->SampleSpec.channels = PDMAudioPropsChannels(&pCfgReq->Props);
655
656 pStreamPA->curLatencyUs = PDMAudioPropsFramesToMilli(&pCfgReq->Props, pCfgReq->Backend.cFramesBufferSize) * RT_US_1MS;
657
658 const uint32_t cbLatency = pa_usec_to_bytes(pStreamPA->curLatencyUs, &pStreamPA->SampleSpec);
659
660 LogRel2(("PulseAudio: Initial output latency is %RU64ms (%RU32 bytes)\n", pStreamPA->curLatencyUs / RT_US_1MS, cbLatency));
661
662 pStreamPA->BufAttr.tlength = cbLatency;
663 pStreamPA->BufAttr.maxlength = -1; /* Let the PulseAudio server choose the biggest size it can handle. */
664 pStreamPA->BufAttr.prebuf = cbLatency;
665 pStreamPA->BufAttr.minreq = PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesPeriod);
666
667 LogFunc(("Requested: BufAttr tlength=%RU32, maxLength=%RU32, minReq=%RU32\n",
668 pStreamPA->BufAttr.tlength, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq));
669
670 Assert(pCfgReq->enmDir == PDMAUDIODIR_OUT);
671
672 char szName[256];
673 RTStrPrintf(szName, sizeof(szName), "VirtualBox %s [%s]", PDMAudioPlaybackDstGetName(pCfgReq->u.enmDst), pThis->szStreamName);
674
675 /* Note that the struct BufAttr is updated to the obtained values after this call! */
676 int rc = paStreamOpen(pThis, pStreamPA, false /* fIn */, szName);
677 if (RT_FAILURE(rc))
678 return rc;
679
680 rc = paPulseToAudioProps(&pCfgAcq->Props, pStreamPA->SampleSpec.format,
681 pStreamPA->SampleSpec.channels, pStreamPA->SampleSpec.rate);
682 if (RT_FAILURE(rc))
683 {
684 LogRel(("PulseAudio: Cannot find audio output format %ld\n", pStreamPA->SampleSpec.format));
685 return rc;
686 }
687
688 LogFunc(("Acquired: BufAttr tlength=%RU32, maxLength=%RU32, minReq=%RU32\n",
689 pStreamPA->BufAttr.tlength, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq));
690
691 pCfgAcq->Backend.cFramesPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.minreq);
692 pCfgAcq->Backend.cFramesBufferSize = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.tlength);
693 pCfgAcq->Backend.cFramesPreBuffering = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.prebuf);
694
695 pStreamPA->pDrv = pThis;
696
697 return rc;
698}
699
700
701static int paCreateStreamIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA,
702 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
703{
704 pStreamPA->SampleSpec.format = paAudioPropsToPulse(&pCfgReq->Props);
705 pStreamPA->SampleSpec.rate = PDMAudioPropsHz(&pCfgReq->Props);
706 pStreamPA->SampleSpec.channels = PDMAudioPropsChannels(&pCfgReq->Props);
707
708 pStreamPA->BufAttr.fragsize = PDMAudioPropsFramesToBytes(&pCfgReq->Props, pCfgReq->Backend.cFramesPeriod);
709 pStreamPA->BufAttr.maxlength = -1; /* Let the PulseAudio server choose the biggest size it can handle. */
710
711 Assert(pCfgReq->enmDir == PDMAUDIODIR_IN);
712
713 char szName[256];
714 RTStrPrintf(szName, sizeof(szName), "VirtualBox %s [%s]", PDMAudioRecSrcGetName(pCfgReq->u.enmSrc), pThis->szStreamName);
715
716 /* Note: Other members of BufAttr are ignored for record streams. */
717 int rc = paStreamOpen(pThis, pStreamPA, true /* fIn */, szName);
718 if (RT_FAILURE(rc))
719 return rc;
720
721 rc = paPulseToAudioProps(&pCfgAcq->Props, pStreamPA->SampleSpec.format,
722 pStreamPA->SampleSpec.channels, pStreamPA->SampleSpec.rate);
723 if (RT_FAILURE(rc))
724 {
725 LogRel(("PulseAudio: Cannot find audio capture format %ld\n", pStreamPA->SampleSpec.format));
726 return rc;
727 }
728
729 pStreamPA->pDrv = pThis;
730 pStreamPA->pu8PeekBuf = NULL;
731
732 pCfgAcq->Backend.cFramesPeriod = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.fragsize);
733 pCfgAcq->Backend.cFramesBufferSize = pStreamPA->BufAttr.maxlength != UINT32_MAX /* paranoia */
734 ? PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamPA->BufAttr.maxlength)
735 : pCfgAcq->Backend.cFramesPeriod * 2 /* whatever */;
736 pCfgAcq->Backend.cFramesPreBuffering = pCfgAcq->Backend.cFramesPeriod;
737
738 LogFlowFuncLeaveRC(rc);
739 return rc;
740}
741
742
743/**
744 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
745 */
746static DECLCALLBACK(int) drvHostPulseAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
747 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
748{
749 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
750 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
751 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
752 AssertReturn(uBufSize, VERR_INVALID_PARAMETER);
753 /* pcbRead is optional. */
754
755 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
756 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream;
757
758 /* We should only call pa_stream_readable_size() once and trust the first value. */
759 pa_threaded_mainloop_lock(pThis->pMainLoop);
760 size_t cbAvail = pa_stream_readable_size(pStreamPA->pStream);
761 pa_threaded_mainloop_unlock(pThis->pMainLoop);
762
763 if (cbAvail == (size_t)-1)
764 return paError(pStreamPA->pDrv, "Failed to determine input data size");
765
766 /* If the buffer was not dropped last call, add what remains. */
767 if (pStreamPA->pu8PeekBuf)
768 {
769 Assert(pStreamPA->cbPeekBuf >= pStreamPA->offPeekBuf);
770 cbAvail += (pStreamPA->cbPeekBuf - pStreamPA->offPeekBuf);
771 }
772
773 Log3Func(("cbAvail=%zu\n", cbAvail));
774
775 if (!cbAvail) /* No data? Bail out. */
776 {
777 if (puRead)
778 *puRead = 0;
779 return VINF_SUCCESS;
780 }
781
782 int rc = VINF_SUCCESS;
783
784 size_t cbToRead = RT_MIN(cbAvail, uBufSize);
785
786 Log3Func(("cbToRead=%zu, cbAvail=%zu, offPeekBuf=%zu, cbPeekBuf=%zu\n",
787 cbToRead, cbAvail, pStreamPA->offPeekBuf, pStreamPA->cbPeekBuf));
788
789 uint32_t cbReadTotal = 0;
790
791 while (cbToRead)
792 {
793 /* If there is no data, do another peek. */
794 if (!pStreamPA->pu8PeekBuf)
795 {
796 pa_threaded_mainloop_lock(pThis->pMainLoop);
797 pa_stream_peek(pStreamPA->pStream,
798 (const void**)&pStreamPA->pu8PeekBuf, &pStreamPA->cbPeekBuf);
799 pa_threaded_mainloop_unlock(pThis->pMainLoop);
800
801 pStreamPA->offPeekBuf = 0;
802
803 /* No data anymore?
804 * Note: If there's a data hole (cbPeekBuf then contains the length of the hole)
805 * we need to drop the stream lateron. */
806 if ( !pStreamPA->pu8PeekBuf
807 && !pStreamPA->cbPeekBuf)
808 {
809 break;
810 }
811 }
812
813 Assert(pStreamPA->cbPeekBuf >= pStreamPA->offPeekBuf);
814 size_t cbToWrite = RT_MIN(pStreamPA->cbPeekBuf - pStreamPA->offPeekBuf, cbToRead);
815
816 Log3Func(("cbToRead=%zu, cbToWrite=%zu, offPeekBuf=%zu, cbPeekBuf=%zu, pu8PeekBuf=%p\n",
817 cbToRead, cbToWrite,
818 pStreamPA->offPeekBuf, pStreamPA->cbPeekBuf, pStreamPA->pu8PeekBuf));
819
820 if ( cbToWrite
821 /* Only copy data if it's not a data hole (see above). */
822 && pStreamPA->pu8PeekBuf
823 && pStreamPA->cbPeekBuf)
824 {
825 memcpy((uint8_t *)pvBuf + cbReadTotal, pStreamPA->pu8PeekBuf + pStreamPA->offPeekBuf, cbToWrite);
826
827 Assert(cbToRead >= cbToWrite);
828 cbToRead -= cbToWrite;
829 cbReadTotal += cbToWrite;
830
831 pStreamPA->offPeekBuf += cbToWrite;
832 Assert(pStreamPA->offPeekBuf <= pStreamPA->cbPeekBuf);
833 }
834
835 if (/* Nothing to write anymore? Drop the buffer. */
836 !cbToWrite
837 /* Was there a hole in the peeking buffer? Drop it. */
838 || !pStreamPA->pu8PeekBuf
839 /* If the buffer is done, drop it. */
840 || pStreamPA->offPeekBuf == pStreamPA->cbPeekBuf)
841 {
842 pa_threaded_mainloop_lock(pThis->pMainLoop);
843 pa_stream_drop(pStreamPA->pStream);
844 pa_threaded_mainloop_unlock(pThis->pMainLoop);
845
846 pStreamPA->pu8PeekBuf = NULL;
847 }
848 }
849
850 if (RT_SUCCESS(rc))
851 {
852 if (puRead)
853 *puRead = cbReadTotal;
854 }
855
856 return rc;
857}
858
859
860/**
861 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
862 */
863static DECLCALLBACK(int) drvHostPulseAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
864 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
865{
866 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
867 AssertPtr(pThis);
868 PPULSEAUDIOSTREAM pPAStream = (PPULSEAUDIOSTREAM)pStream;
869 AssertPtrReturn(pPAStream, VERR_INVALID_POINTER);
870 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
871 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
872 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
873
874 pa_threaded_mainloop_lock(pThis->pMainLoop);
875
876#ifdef LOG_ENABLED
877 const pa_usec_t tsNowUs = pa_rtclock_now();
878 const pa_usec_t tsDeltaPlayedUs = tsNowUs - pPAStream->tsLastReadWrittenUs;
879 pPAStream->tsLastReadWrittenUs = tsNowUs;
880 Log3Func(("tsDeltaPlayedMs=%RU64\n", tsDeltaPlayedUs / RT_US_1MS));
881#endif
882
883 int rc;
884 size_t const cbWriteable = pa_stream_writable_size(pPAStream->pStream);
885 if (cbWriteable != (size_t)-1)
886 {
887 size_t cbLeft = RT_MIN(cbWriteable, cbBuf);
888 Assert(cbLeft > 0 /* At this point we better have *something* to write (DrvAudio checked before calling). */);
889 if (pa_stream_write(pPAStream->pStream, pvBuf, cbLeft, NULL /*pfnFree*/, 0 /*offset*/, PA_SEEK_RELATIVE) >= 0)
890 {
891 *pcbWritten = (uint32_t)cbLeft;
892 rc = VINF_SUCCESS;
893 }
894 else
895 rc = paError(pPAStream->pDrv, "Failed to write to output stream");
896 }
897 else
898 rc = paError(pPAStream->pDrv, "Failed to determine output data size");
899
900 pa_threaded_mainloop_unlock(pThis->pMainLoop);
901 return rc;
902}
903
904
905/** @todo Implement va handling. */
906static int paError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg)
907{
908 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
909 AssertPtrReturn(szMsg, VERR_INVALID_POINTER);
910
911 if (pThis->cLogErrors++ < VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS)
912 {
913 int rc2 = pa_context_errno(pThis->pContext);
914 LogRel2(("PulseAudio: %s: %s\n", szMsg, pa_strerror(rc2)));
915 }
916
917 /** @todo Implement some PulseAudio -> IPRT mapping here. */
918 return VERR_GENERAL_FAILURE;
919}
920
921
922static void paEnumSinkCb(pa_context *pCtx, const pa_sink_info *pInfo, int eol, void *pvUserData)
923{
924 if (eol > 0)
925 return;
926
927 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
928 AssertPtrReturnVoid(pCbCtx);
929 PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv;
930 AssertPtrReturnVoid(pThis);
931 if (eol < 0)
932 {
933 pThis->fEnumOpSuccess = false;
934 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
935 return;
936 }
937
938 AssertPtrReturnVoid(pCtx);
939 AssertPtrReturnVoid(pInfo);
940
941 LogRel2(("PulseAudio: Using output sink '%s'\n", pInfo->name));
942
943 /** @todo Store sinks + channel mapping in callback context as soon as we have surround support. */
944 pCbCtx->cDevOut++;
945
946 pThis->fEnumOpSuccess = true;
947 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
948}
949
950
951static void paEnumSourceCb(pa_context *pCtx, const pa_source_info *pInfo, int eol, void *pvUserData)
952{
953 if (eol > 0)
954 return;
955
956 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
957 AssertPtrReturnVoid(pCbCtx);
958 PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv;
959 AssertPtrReturnVoid(pThis);
960 if (eol < 0)
961 {
962 pThis->fEnumOpSuccess = false;
963 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
964 return;
965 }
966
967 AssertPtrReturnVoid(pCtx);
968 AssertPtrReturnVoid(pInfo);
969
970 LogRel2(("PulseAudio: Using input source '%s'\n", pInfo->name));
971
972 /** @todo Store sources + channel mapping in callback context as soon as we have surround support. */
973 pCbCtx->cDevIn++;
974
975 pThis->fEnumOpSuccess = true;
976 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
977}
978
979
980static void paEnumServerCb(pa_context *pCtx, const pa_server_info *pInfo, void *pvUserData)
981{
982 AssertPtrReturnVoid(pCtx);
983 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
984 AssertPtrReturnVoid(pCbCtx);
985 PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv;
986 AssertPtrReturnVoid(pThis);
987
988 if (!pInfo)
989 {
990 pThis->fEnumOpSuccess = false;
991 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
992 return;
993 }
994
995 if (pInfo->default_sink_name)
996 {
997 Assert(RTStrIsValidEncoding(pInfo->default_sink_name));
998 pCbCtx->pszDefaultSink = RTStrDup(pInfo->default_sink_name);
999 }
1000
1001 if (pInfo->default_sink_name)
1002 {
1003 Assert(RTStrIsValidEncoding(pInfo->default_source_name));
1004 pCbCtx->pszDefaultSource = RTStrDup(pInfo->default_source_name);
1005 }
1006
1007 pThis->fEnumOpSuccess = true;
1008 pa_threaded_mainloop_signal(pThis->pMainLoop, 0);
1009}
1010
1011
1012static int paEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
1013{
1014 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1015 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1016
1017 PDMAUDIOBACKENDCFG Cfg;
1018 RT_ZERO(Cfg);
1019
1020 RTStrPrintf2(Cfg.szName, sizeof(Cfg.szName), "PulseAudio");
1021
1022 Cfg.cbStreamOut = sizeof(PULSEAUDIOSTREAM);
1023 Cfg.cbStreamIn = sizeof(PULSEAUDIOSTREAM);
1024 Cfg.cMaxStreamsOut = UINT32_MAX;
1025 Cfg.cMaxStreamsIn = UINT32_MAX;
1026
1027 PULSEAUDIOENUMCBCTX CbCtx;
1028 RT_ZERO(CbCtx);
1029
1030 CbCtx.pDrv = pThis;
1031 CbCtx.fFlags = fEnum;
1032
1033 bool fLog = (fEnum & PULSEAUDIOENUMCBFLAGS_LOG);
1034
1035 pa_threaded_mainloop_lock(pThis->pMainLoop);
1036
1037 pThis->fEnumOpSuccess = false;
1038
1039 LogRel(("PulseAudio: Retrieving server information ...\n"));
1040
1041 /* Check if server information is available and bail out early if it isn't. */
1042 pa_operation *paOpServerInfo = pa_context_get_server_info(pThis->pContext, paEnumServerCb, &CbCtx);
1043 if (!paOpServerInfo)
1044 {
1045 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1046
1047 LogRel(("PulseAudio: Server information not available, skipping enumeration\n"));
1048 return VINF_SUCCESS;
1049 }
1050
1051 int rc = paWaitFor(pThis, paOpServerInfo);
1052 if (RT_SUCCESS(rc) && !pThis->fEnumOpSuccess)
1053 rc = VERR_AUDIO_BACKEND_INIT_FAILED; /* error code does not matter */
1054 if (RT_SUCCESS(rc))
1055 {
1056 if (CbCtx.pszDefaultSink)
1057 {
1058 if (fLog)
1059 LogRel2(("PulseAudio: Default output sink is '%s'\n", CbCtx.pszDefaultSink));
1060
1061 pThis->fEnumOpSuccess = false;
1062 rc = paWaitFor(pThis, pa_context_get_sink_info_by_name(pThis->pContext, CbCtx.pszDefaultSink,
1063 paEnumSinkCb, &CbCtx));
1064 if (RT_SUCCESS(rc) && !pThis->fEnumOpSuccess)
1065 rc = VERR_AUDIO_BACKEND_INIT_FAILED; /* error code does not matter */
1066 if ( RT_FAILURE(rc)
1067 && fLog)
1068 {
1069 LogRel(("PulseAudio: Error enumerating properties for default output sink '%s'\n", CbCtx.pszDefaultSink));
1070 }
1071 }
1072 else if (fLog)
1073 LogRel2(("PulseAudio: No default output sink found\n"));
1074
1075 if (RT_SUCCESS(rc))
1076 {
1077 if (CbCtx.pszDefaultSource)
1078 {
1079 if (fLog)
1080 LogRel2(("PulseAudio: Default input source is '%s'\n", CbCtx.pszDefaultSource));
1081
1082 pThis->fEnumOpSuccess = false;
1083 rc = paWaitFor(pThis, pa_context_get_source_info_by_name(pThis->pContext, CbCtx.pszDefaultSource,
1084 paEnumSourceCb, &CbCtx));
1085 if ( (RT_FAILURE(rc) || !pThis->fEnumOpSuccess)
1086 && fLog)
1087 {
1088 LogRel(("PulseAudio: Error enumerating properties for default input source '%s'\n", CbCtx.pszDefaultSource));
1089 }
1090 }
1091 else if (fLog)
1092 LogRel2(("PulseAudio: No default input source found\n"));
1093 }
1094
1095 if (RT_SUCCESS(rc))
1096 {
1097 if (fLog)
1098 {
1099 LogRel2(("PulseAudio: Found %RU8 host playback device(s)\n", CbCtx.cDevOut));
1100 LogRel2(("PulseAudio: Found %RU8 host capturing device(s)\n", CbCtx.cDevIn));
1101 }
1102
1103 if (pCfg)
1104 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
1105 }
1106
1107 if (CbCtx.pszDefaultSink)
1108 {
1109 RTStrFree(CbCtx.pszDefaultSink);
1110 CbCtx.pszDefaultSink = NULL;
1111 }
1112
1113 if (CbCtx.pszDefaultSource)
1114 {
1115 RTStrFree(CbCtx.pszDefaultSource);
1116 CbCtx.pszDefaultSource = NULL;
1117 }
1118 }
1119 else if (fLog)
1120 LogRel(("PulseAudio: Error enumerating PulseAudio server properties\n"));
1121
1122 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1123
1124 LogFlowFuncLeaveRC(rc);
1125 return rc;
1126}
1127
1128
1129static int paDestroyStreamIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA)
1130{
1131 LogFlowFuncEnter();
1132
1133 if (pStreamPA->pStream)
1134 {
1135 pa_threaded_mainloop_lock(pThis->pMainLoop);
1136
1137 pa_stream_disconnect(pStreamPA->pStream);
1138 pa_stream_unref(pStreamPA->pStream);
1139
1140 pStreamPA->pStream = NULL;
1141
1142 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1143 }
1144
1145 return VINF_SUCCESS;
1146}
1147
1148
1149static int paDestroyStreamOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA)
1150{
1151 if (pStreamPA->pStream)
1152 {
1153 pa_threaded_mainloop_lock(pThis->pMainLoop);
1154
1155 /* Make sure to cancel a pending draining operation, if any. */
1156 if (pStreamPA->pDrainOp)
1157 {
1158 pa_operation_cancel(pStreamPA->pDrainOp);
1159 pStreamPA->pDrainOp = NULL;
1160 }
1161
1162 pa_stream_disconnect(pStreamPA->pStream);
1163 pa_stream_unref(pStreamPA->pStream);
1164
1165 pStreamPA->pStream = NULL;
1166
1167 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1168 }
1169
1170 return VINF_SUCCESS;
1171}
1172
1173
1174static int paControlStreamOut(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd)
1175{
1176 int rc = VINF_SUCCESS;
1177
1178 switch (enmStreamCmd)
1179 {
1180 case PDMAUDIOSTREAMCMD_ENABLE:
1181 case PDMAUDIOSTREAMCMD_RESUME:
1182 {
1183 pa_threaded_mainloop_lock(pThis->pMainLoop);
1184
1185 if ( pStreamPA->pDrainOp
1186 && pa_operation_get_state(pStreamPA->pDrainOp) != PA_OPERATION_DONE)
1187 {
1188 pa_operation_cancel(pStreamPA->pDrainOp);
1189 pa_operation_unref(pStreamPA->pDrainOp);
1190
1191 pStreamPA->pDrainOp = NULL;
1192 }
1193 else
1194 {
1195 /* Uncork (resume) stream. */
1196 rc = paWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Uncork */, paStreamCbSuccess, pStreamPA));
1197 }
1198
1199 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1200 break;
1201 }
1202
1203 case PDMAUDIOSTREAMCMD_DISABLE:
1204 case PDMAUDIOSTREAMCMD_PAUSE:
1205 {
1206 /* Pause audio output (the Pause bit of the AC97 x_CR register is set).
1207 * Note that we must return immediately from here! */
1208 pa_threaded_mainloop_lock(pThis->pMainLoop);
1209 if (!pStreamPA->pDrainOp)
1210 {
1211 rc = paWaitFor(pThis, pa_stream_trigger(pStreamPA->pStream, paStreamCbSuccess, pStreamPA));
1212 if (RT_SUCCESS(rc))
1213 pStreamPA->pDrainOp = pa_stream_drain(pStreamPA->pStream, paStreamCbDrain, pStreamPA);
1214 }
1215 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1216 break;
1217 }
1218
1219 default:
1220 rc = VERR_NOT_SUPPORTED;
1221 break;
1222 }
1223
1224 LogFlowFuncLeaveRC(rc);
1225 return rc;
1226}
1227
1228
1229static int paControlStreamIn(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA, PDMAUDIOSTREAMCMD enmStreamCmd)
1230{
1231 int rc = VINF_SUCCESS;
1232
1233 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1234
1235 switch (enmStreamCmd)
1236 {
1237 case PDMAUDIOSTREAMCMD_ENABLE:
1238 case PDMAUDIOSTREAMCMD_RESUME:
1239 {
1240 pa_threaded_mainloop_lock(pThis->pMainLoop);
1241 rc = paWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 0 /* Play / resume */, paStreamCbSuccess, pStreamPA));
1242 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1243 break;
1244 }
1245
1246 case PDMAUDIOSTREAMCMD_DISABLE:
1247 case PDMAUDIOSTREAMCMD_PAUSE:
1248 {
1249 pa_threaded_mainloop_lock(pThis->pMainLoop);
1250 if (pStreamPA->pu8PeekBuf) /* Do we need to drop the peek buffer?*/
1251 {
1252 pa_stream_drop(pStreamPA->pStream);
1253 pStreamPA->pu8PeekBuf = NULL;
1254 }
1255
1256 rc = paWaitFor(pThis, pa_stream_cork(pStreamPA->pStream, 1 /* Stop / pause */, paStreamCbSuccess, pStreamPA));
1257 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1258 break;
1259 }
1260
1261 default:
1262 rc = VERR_NOT_SUPPORTED;
1263 break;
1264 }
1265
1266 return rc;
1267}
1268
1269
1270/**
1271 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
1272 */
1273static DECLCALLBACK(int) drvHostPulseAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
1274{
1275 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1276 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
1277
1278 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1279
1280 return paEnumerate(pThis, pBackendCfg, PULSEAUDIOENUMCBFLAGS_LOG /* fEnum */);
1281}
1282
1283
1284/**
1285 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
1286 */
1287static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostPulseAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1288{
1289 RT_NOREF(enmDir);
1290 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
1291
1292 return PDMAUDIOBACKENDSTS_RUNNING;
1293}
1294
1295
1296/**
1297 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
1298 */
1299static DECLCALLBACK(int) drvHostPulseAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1300 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1301{
1302 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1303 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1304 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
1305 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
1306
1307 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1308 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream;
1309
1310 int rc;
1311 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
1312 rc = paCreateStreamIn (pThis, pStreamPA, pCfgReq, pCfgAcq);
1313 else if (pCfgReq->enmDir == PDMAUDIODIR_OUT)
1314 rc = paCreateStreamOut(pThis, pStreamPA, pCfgReq, pCfgAcq);
1315 else
1316 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
1317
1318 if (RT_SUCCESS(rc))
1319 {
1320 pStreamPA->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
1321 if (!pStreamPA->pCfg)
1322 rc = VERR_NO_MEMORY;
1323 }
1324
1325 return rc;
1326}
1327
1328
1329/**
1330 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
1331 */
1332static DECLCALLBACK(int) drvHostPulseAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1333{
1334 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1335 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1336
1337 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1338 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream;
1339
1340 if (!pStreamPA->pCfg) /* Not (yet) configured? Skip. */
1341 return VINF_SUCCESS;
1342
1343 int rc;
1344 if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN)
1345 rc = paDestroyStreamIn (pThis, pStreamPA);
1346 else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT)
1347 rc = paDestroyStreamOut(pThis, pStreamPA);
1348 else
1349 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1350
1351 if (RT_SUCCESS(rc))
1352 {
1353 PDMAudioStrmCfgFree(pStreamPA->pCfg);
1354 pStreamPA->pCfg = NULL;
1355 }
1356
1357 return rc;
1358}
1359
1360
1361/**
1362 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
1363 */
1364static DECLCALLBACK(int) drvHostPulseAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
1365 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1366{
1367 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1368 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1369
1370 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1371 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream;
1372
1373 if (!pStreamPA->pCfg) /* Not (yet) configured? Skip. */
1374 return VINF_SUCCESS;
1375
1376 int rc;
1377 if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN)
1378 rc = paControlStreamIn (pThis, pStreamPA, enmStreamCmd);
1379 else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT)
1380 rc = paControlStreamOut(pThis, pStreamPA, enmStreamCmd);
1381 else
1382 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1383
1384 return rc;
1385}
1386
1387
1388static uint32_t paStreamGetAvail(PDRVHOSTPULSEAUDIO pThis, PPULSEAUDIOSTREAM pStreamPA)
1389{
1390 pa_threaded_mainloop_lock(pThis->pMainLoop);
1391
1392 uint32_t cbAvail = 0;
1393
1394 if (PA_STREAM_IS_GOOD(pa_stream_get_state(pStreamPA->pStream)))
1395 {
1396 if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_IN)
1397 {
1398 cbAvail = (uint32_t)pa_stream_readable_size(pStreamPA->pStream);
1399 Log3Func(("cbReadable=%RU32\n", cbAvail));
1400 }
1401 else if (pStreamPA->pCfg->enmDir == PDMAUDIODIR_OUT)
1402 {
1403 size_t cbWritable = pa_stream_writable_size(pStreamPA->pStream);
1404
1405 Log3Func(("cbWritable=%zu, maxLength=%RU32, minReq=%RU32\n",
1406 cbWritable, pStreamPA->BufAttr.maxlength, pStreamPA->BufAttr.minreq));
1407
1408 /* Don't report more writable than the PA server can handle. */
1409 if (cbWritable > pStreamPA->BufAttr.maxlength)
1410 cbWritable = pStreamPA->BufAttr.maxlength;
1411
1412 cbAvail = (uint32_t)cbWritable;
1413 }
1414 else
1415 AssertFailed();
1416 }
1417
1418 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1419
1420 return cbAvail;
1421}
1422
1423
1424/**
1425 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
1426 */
1427static DECLCALLBACK(uint32_t) drvHostPulseAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1428{
1429 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1430 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream;
1431
1432 return paStreamGetAvail(pThis, pStreamPA);
1433}
1434
1435
1436/**
1437 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
1438 */
1439static DECLCALLBACK(uint32_t) drvHostPulseAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1440{
1441 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1442 PPULSEAUDIOSTREAM pStreamPA = (PPULSEAUDIOSTREAM)pStream;
1443
1444 return paStreamGetAvail(pThis, pStreamPA);
1445}
1446
1447
1448/**
1449 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
1450 */
1451static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostPulseAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1452{
1453 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1454 RT_NOREF(pStream);
1455
1456 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1457
1458 PDMAUDIOSTREAMSTS fStrmSts = PDMAUDIOSTREAMSTS_FLAGS_NONE;
1459
1460 /* Check PulseAudio's general status. */
1461 if ( pThis->pContext
1462 && PA_CONTEXT_IS_GOOD(pa_context_get_state(pThis->pContext)))
1463 fStrmSts = PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
1464
1465 return fStrmSts;
1466}
1467
1468
1469/**
1470 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1471 */
1472static DECLCALLBACK(void *) drvHostPulseAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1473{
1474 AssertPtrReturn(pInterface, NULL);
1475 AssertPtrReturn(pszIID, NULL);
1476
1477 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1478 PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
1479 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1480 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1481
1482 return NULL;
1483}
1484
1485
1486/**
1487 * @interface_method_impl{PDMDRVREG,pfnPowerOff}
1488 */
1489static DECLCALLBACK(void) drvHostPulseAudioPowerOff(PPDMDRVINS pDrvIns)
1490{
1491 PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
1492 LogFlowFuncEnter();
1493
1494 if (pThis->pMainLoop)
1495 pa_threaded_mainloop_stop(pThis->pMainLoop);
1496
1497 if (pThis->pContext)
1498 {
1499 pa_context_disconnect(pThis->pContext);
1500 pa_context_unref(pThis->pContext);
1501 pThis->pContext = NULL;
1502 }
1503
1504 if (pThis->pMainLoop)
1505 {
1506 pa_threaded_mainloop_free(pThis->pMainLoop);
1507 pThis->pMainLoop = NULL;
1508 }
1509
1510 LogFlowFuncLeave();
1511}
1512
1513
1514/**
1515 * Destructs a PulseAudio Audio driver instance.
1516 *
1517 * @copydoc FNPDMDRVDESTRUCT
1518 */
1519static DECLCALLBACK(void) drvHostPulseAudioDestruct(PPDMDRVINS pDrvIns)
1520{
1521 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1522 LogFlowFuncEnter();
1523 drvHostPulseAudioPowerOff(pDrvIns);
1524 LogFlowFuncLeave();
1525}
1526
1527
1528/**
1529 * Constructs a PulseAudio Audio driver instance.
1530 *
1531 * @copydoc FNPDMDRVCONSTRUCT
1532 */
1533static DECLCALLBACK(int) drvHostPulseAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1534{
1535 RT_NOREF(pCfg, fFlags);
1536 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1537 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1538
1539 PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
1540 LogRel(("Audio: Initializing PulseAudio driver\n"));
1541
1542 /*
1543 * Initialize instance data.
1544 */
1545 pThis->pDrvIns = pDrvIns;
1546 /* IBase */
1547 pDrvIns->IBase.pfnQueryInterface = drvHostPulseAudioQueryInterface;
1548 /* IHostAudio */
1549 pThis->IHostAudio.pfnInit = NULL;
1550 pThis->IHostAudio.pfnShutdown = NULL;
1551 pThis->IHostAudio.pfnGetConfig = drvHostPulseAudioHA_GetConfig;
1552 pThis->IHostAudio.pfnGetStatus = drvHostPulseAudioHA_GetStatus;
1553 pThis->IHostAudio.pfnStreamCreate = drvHostPulseAudioHA_StreamCreate;
1554 pThis->IHostAudio.pfnStreamDestroy = drvHostPulseAudioHA_StreamDestroy;
1555 pThis->IHostAudio.pfnStreamControl = drvHostPulseAudioHA_StreamControl;
1556 pThis->IHostAudio.pfnStreamGetReadable = drvHostPulseAudioHA_StreamGetReadable;
1557 pThis->IHostAudio.pfnStreamGetWritable = drvHostPulseAudioHA_StreamGetWritable;
1558 pThis->IHostAudio.pfnStreamGetStatus = drvHostPulseAudioHA_StreamGetStatus;
1559 pThis->IHostAudio.pfnStreamPlay = drvHostPulseAudioHA_StreamPlay;
1560 pThis->IHostAudio.pfnStreamCapture = drvHostPulseAudioHA_StreamCapture;
1561 pThis->IHostAudio.pfnGetDevices = NULL;
1562 pThis->IHostAudio.pfnStreamGetPending = NULL;
1563
1564 /*
1565 * Read configuration.
1566 */
1567 int rc2 = CFGMR3QueryString(pCfg, "StreamName", pThis->szStreamName, sizeof(pThis->szStreamName));
1568 AssertMsgRCReturn(rc2, ("Confguration error: No/bad \"StreamName\" value, rc=%Rrc\n", rc2), rc2);
1569
1570 /*
1571 * Load the pulse audio library.
1572 */
1573 int rc = audioLoadPulseLib();
1574 if (RT_SUCCESS(rc))
1575 LogRel(("PulseAudio: Using version %s\n", pa_get_library_version()));
1576 else
1577 {
1578 LogRel(("PulseAudio: Failed to load the PulseAudio shared library! Error %Rrc\n", rc));
1579 return rc;
1580 }
1581
1582 /*
1583 * Set up the basic pulse audio bits (remember the destructore is always called).
1584 */
1585 //pThis->fAbortLoop = false;
1586 pThis->pMainLoop = pa_threaded_mainloop_new();
1587 if (!pThis->pMainLoop)
1588 {
1589 LogRel(("PulseAudio: Failed to allocate main loop: %s\n", pa_strerror(pa_context_errno(pThis->pContext))));
1590 return VERR_NO_MEMORY;
1591 }
1592
1593 pThis->pContext = pa_context_new(pa_threaded_mainloop_get_api(pThis->pMainLoop), "VirtualBox");
1594 if (!pThis->pContext)
1595 {
1596 LogRel(("PulseAudio: Failed to allocate context: %s\n", pa_strerror(pa_context_errno(pThis->pContext))));
1597 return VERR_NO_MEMORY;
1598 }
1599
1600 if (pa_threaded_mainloop_start(pThis->pMainLoop) < 0)
1601 {
1602 LogRel(("PulseAudio: Failed to start threaded mainloop: %s\n", pa_strerror(pa_context_errno(pThis->pContext))));
1603 return VERR_AUDIO_BACKEND_INIT_FAILED;
1604 }
1605
1606 /*
1607 * Connect to the pulse audio server.
1608 *
1609 * We install an init state callback so we can do a timed wait in case
1610 * connecting to the pulseaudio server should take too long.
1611 */
1612 pThis->InitStateChgCtx.hEvtInit = NIL_RTSEMEVENT;
1613 pThis->InitStateChgCtx.enmCtxState = PA_CONTEXT_UNCONNECTED;
1614 rc = RTSemEventCreate(&pThis->InitStateChgCtx.hEvtInit);
1615 AssertLogRelRCReturn(rc, rc);
1616
1617 pa_threaded_mainloop_lock(pThis->pMainLoop);
1618 pa_context_set_state_callback(pThis->pContext, paContextCbStateChangedInit, &pThis->InitStateChgCtx);
1619 if (!pa_context_connect(pThis->pContext, NULL /* pszServer */, PA_CONTEXT_NOFLAGS, NULL))
1620 {
1621 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1622
1623 rc = RTSemEventWait(pThis->InitStateChgCtx.hEvtInit, RT_MS_10SEC); /* 10 seconds should be plenty. */
1624 if (RT_SUCCESS(rc))
1625 {
1626 if (pThis->InitStateChgCtx.enmCtxState == PA_CONTEXT_READY)
1627 {
1628 /* Install the main state changed callback to know if something happens to our acquired context. */
1629 pa_threaded_mainloop_lock(pThis->pMainLoop);
1630 pa_context_set_state_callback(pThis->pContext, paContextCbStateChanged, pThis /* pvUserData */);
1631 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1632 }
1633 else
1634 {
1635 LogRel(("PulseAudio: Failed to initialize context (state %d, rc=%Rrc)\n", pThis->InitStateChgCtx.enmCtxState, rc));
1636 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
1637 }
1638 }
1639 else
1640 {
1641 LogRel(("PulseAudio: Waiting for context to become ready failed: %Rrc\n", rc));
1642 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
1643 }
1644 }
1645 else
1646 {
1647 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1648 LogRel(("PulseAudio: Failed to connect to server: %s\n", pa_strerror(pa_context_errno(pThis->pContext))));
1649 rc = VERR_AUDIO_BACKEND_INIT_FAILED; /* bird: This used to be VINF_SUCCESS. */
1650 }
1651
1652 RTSemEventDestroy(pThis->InitStateChgCtx.hEvtInit);
1653 pThis->InitStateChgCtx.hEvtInit = NIL_RTSEMEVENT;
1654
1655 return rc;
1656}
1657
1658
1659/**
1660 * Pulse audio driver registration record.
1661 */
1662const PDMDRVREG g_DrvHostPulseAudio =
1663{
1664 /* u32Version */
1665 PDM_DRVREG_VERSION,
1666 /* szName */
1667 "PulseAudio",
1668 /* szRCMod */
1669 "",
1670 /* szR0Mod */
1671 "",
1672 /* pszDescription */
1673 "Pulse Audio host driver",
1674 /* fFlags */
1675 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1676 /* fClass. */
1677 PDM_DRVREG_CLASS_AUDIO,
1678 /* cMaxInstances */
1679 ~0U,
1680 /* cbInstance */
1681 sizeof(DRVHOSTPULSEAUDIO),
1682 /* pfnConstruct */
1683 drvHostPulseAudioConstruct,
1684 /* pfnDestruct */
1685 drvHostPulseAudioDestruct,
1686 /* pfnRelocate */
1687 NULL,
1688 /* pfnIOCtl */
1689 NULL,
1690 /* pfnPowerOn */
1691 NULL,
1692 /* pfnReset */
1693 NULL,
1694 /* pfnSuspend */
1695 NULL,
1696 /* pfnResume */
1697 NULL,
1698 /* pfnAttach */
1699 NULL,
1700 /* pfnDetach */
1701 NULL,
1702 /* pfnPowerOff */
1703 drvHostPulseAudioPowerOff,
1704 /* pfnSoftReset */
1705 NULL,
1706 /* u32EndVersion */
1707 PDM_DRVREG_VERSION
1708};
1709
1710#if 0 /* unused */
1711static struct audio_option pulse_options[] =
1712{
1713 {"DAC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_out,
1714 "DAC period size in milliseconds", NULL, 0},
1715 {"ADC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_in,
1716 "ADC period size in milliseconds", NULL, 0}
1717};
1718#endif
1719
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