VirtualBox

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

Last change on this file since 67972 was 67951, checked in by vboxsync, 8 years ago

Audio/DrvHostPulseAudio: Added underflow / write callbacks from 5.1, more work on bringing latency down.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette