VirtualBox

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

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

Audio/DrvHostPulseAudio.cpp: Build fix (allow to run on systems with PulseAudio < 0.9.11).

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