VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostPulseAudio.cpp@ 85449

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

Audio/DrvHostPulseAudio: Don't access the context anymore when waiting for it to become ready failed for some reason

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