VirtualBox

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

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

DrvHostPulseAudio: LogRel unsupported audio formats.

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