VirtualBox

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

Last change on this file since 67909 was 67909, checked in by vboxsync, 7 years ago

Audio/DrvHostPulseAudio.cpp: Added additional check for writable size in drvHostPulseAudioStreamPlay().

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

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