VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/pulseaudio.c@ 26522

Last change on this file since 26522 was 25768, checked in by vboxsync, 15 years ago

Devices/pulseaudio: protect against killing the pulseaudio daemon during record

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.7 KB
Line 
1/** @file
2 *
3 * VBox PulseAudio backend
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DEV_AUDIO
27#include <VBox/log.h>
28#include <iprt/mem.h>
29
30#include <pulse/pulseaudio.h>
31#include "pulse_stubs.h"
32
33#include "../../vl_vbox.h"
34#include "audio.h"
35#define AUDIO_CAP "pulse"
36#include "audio_int.h"
37#include <stdio.h>
38
39#define MAX_LOG_REL_ERRORS 32
40
41/*
42 * We use a g_pMainLoop in a separate thread g_pContext. We have to call functions for
43 * manipulating objects either from callback functions or we have to protect
44 * these functions by pa_threaded_mainloop_lock() / pa_threaded_mainloop_unlock().
45 */
46static struct pa_threaded_mainloop *g_pMainLoop;
47static struct pa_context *g_pContext;
48
49typedef struct PulseVoice
50{
51 /** not accessed from within this context */
52 union
53 {
54 HWVoiceOut In;
55 HWVoiceIn Out;
56 } hw;
57 /** DAC buffer */
58 void *pPCMBuf;
59 /** Pulse stream */
60 pa_stream *pStream;
61 /** Pulse sample format and attribute specification */
62 pa_sample_spec SampleSpec;
63 /** Pulse playback and buffer metrics */
64 pa_buffer_attr BufAttr;
65 int fOpSuccess;
66 /** number of logged errors */
67 unsigned cErrors;
68 /** Pulse record peek buffer */
69 const uint8_t *pu8PeekBuf;
70 size_t cbPeekBuf;
71 size_t offPeekBuf;
72} PulseVoice;
73
74/* The desired buffer length in milliseconds. Will be the target total stream
75 * latency on newer version of pulse. Apparent latency can be less (or more.)
76 */
77static struct
78{
79 int buffer_msecs_out;
80 int buffer_msecs_in;
81} conf
82=
83{
84 INIT_FIELD (.buffer_msecs_out = ) 100,
85 INIT_FIELD (.buffer_msecs_in = ) 100,
86};
87
88static pa_sample_format_t aud_to_pulsefmt (audfmt_e fmt)
89{
90 switch (fmt)
91 {
92 case AUD_FMT_U8:
93 return PA_SAMPLE_U8;
94
95 case AUD_FMT_S16:
96 return PA_SAMPLE_S16LE;
97
98#ifdef PA_SAMPLE_S32LE
99 case AUD_FMT_S32:
100 return PA_SAMPLE_S32LE;
101#endif
102
103 default:
104 dolog ("Bad audio format %d\n", fmt);
105 return PA_SAMPLE_U8;
106 }
107}
108
109
110static int pulse_to_audfmt (pa_sample_format_t pulsefmt, audfmt_e *fmt, int *endianess)
111{
112 switch (pulsefmt)
113 {
114 case PA_SAMPLE_U8:
115 *endianess = 0;
116 *fmt = AUD_FMT_U8;
117 break;
118
119 case PA_SAMPLE_S16LE:
120 *fmt = AUD_FMT_S16;
121 *endianess = 0;
122 break;
123
124 case PA_SAMPLE_S16BE:
125 *fmt = AUD_FMT_S16;
126 *endianess = 1;
127 break;
128
129#ifdef PA_SAMPLE_S32LE
130 case PA_SAMPLE_S32LE:
131 *fmt = AUD_FMT_S32;
132 *endianess = 0;
133 break;
134#endif
135
136#ifdef PA_SAMPLE_S32BE
137 case PA_SAMPLE_S32BE:
138 *fmt = AUD_FMT_S32;
139 *endianess = 1;
140 break;
141#endif
142
143 default:
144 return -1;
145 }
146 return 0;
147}
148
149static void context_state_callback(pa_context *c, void *userdata)
150{
151 PulseVoice *pPulse = (PulseVoice *)userdata;
152 switch (pa_context_get_state(c))
153 {
154 case PA_CONTEXT_READY:
155 case PA_CONTEXT_TERMINATED:
156 pa_threaded_mainloop_signal(g_pMainLoop, 0);
157 break;
158
159 case PA_CONTEXT_FAILED:
160 LogRel(("Pulse: Audio input/output stopped!\n"));
161 if (pPulse)
162 pPulse->cErrors = MAX_LOG_REL_ERRORS;
163 pa_threaded_mainloop_signal(g_pMainLoop, 0);
164 break;
165
166 default:
167 break;
168 }
169}
170
171static void stream_state_callback(pa_stream *s, void *userdata)
172{
173 switch (pa_stream_get_state(s))
174 {
175 case PA_STREAM_READY:
176 case PA_STREAM_FAILED:
177 case PA_STREAM_TERMINATED:
178 pa_threaded_mainloop_signal(g_pMainLoop, 0);
179 break;
180
181 default:
182 break;
183 }
184}
185
186static int pulse_open (int fIn, pa_stream **ppStream, pa_sample_spec *pSampleSpec,
187 pa_buffer_attr *pBufAttr)
188{
189 const pa_buffer_attr *pBufAttrObtained;
190 pa_stream *pStream = NULL;
191 char achPCMName[64];
192 pa_stream_flags_t flags = 0;
193 const char *stream_name = audio_get_stream_name();
194
195 RTStrPrintf(achPCMName, sizeof(achPCMName), "%.32s%s%s%s",
196 stream_name ? stream_name : "",
197 stream_name ? " (" : "",
198 fIn ? "pcm_in" : "pcm_out",
199 stream_name ? ")" : "");
200
201 LogRel(("Pulse: open %s rate=%dHz channels=%d format=%s\n",
202 fIn ? "PCM_IN" : "PCM_OUT", pSampleSpec->rate, pSampleSpec->channels,
203 pa_sample_format_to_string(pSampleSpec->format)));
204
205 if (!pa_sample_spec_valid(pSampleSpec))
206 {
207 LogRel(("Pulse: Unsupported sample specification\n"));
208 goto fail;
209 }
210
211 pa_threaded_mainloop_lock(g_pMainLoop);
212
213 if (!(pStream = pa_stream_new(g_pContext, achPCMName, pSampleSpec, /*channel_map=*/NULL)))
214 {
215 LogRel(("Pulse: Cannot create stream %s\n", achPCMName));
216 goto unlock_and_fail;
217 }
218
219 pa_stream_set_state_callback(pStream, stream_state_callback, NULL);
220
221#if PA_API_VERSION >= 12
222 /* XXX */
223 flags |= PA_STREAM_ADJUST_LATENCY;
224#endif
225
226#if 0
227 /* not applicable as we don't use pa_stream_get_latency() and pa_stream_get_time() */
228 flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
229#endif
230
231 if (fIn)
232 {
233 LogRel(("Pulse: Requested record buffer attributes: maxlength=%d fragsize=%d\n",
234 pBufAttr->maxlength, pBufAttr->fragsize));
235
236 if (pa_stream_connect_record(pStream, /*dev=*/NULL, pBufAttr, flags) < 0)
237 {
238 LogRel(("Pulse: Cannot connect record stream: %s\n",
239 pa_strerror(pa_context_errno(g_pContext))));
240 goto disconnect_unlock_and_fail;
241 }
242 }
243 else
244 {
245 LogRel(("Pulse: Requested playback buffer attributes: maxlength=%d tlength=%d prebuf=%d minreq=%d\n",
246 pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
247
248 flags |= PA_STREAM_START_CORKED;
249
250 if (pa_stream_connect_playback(pStream, /*dev=*/NULL, pBufAttr, flags,
251 /*cvolume=*/NULL, /*sync_stream=*/NULL) < 0)
252 {
253 LogRel(("Pulse: Cannot connect playback stream: %s\n",
254 pa_strerror(pa_context_errno(g_pContext))));
255 goto disconnect_unlock_and_fail;
256 }
257 }
258
259 /* Wait until the stream is ready */
260 for (;;)
261 {
262 pa_stream_state_t sstate;
263 pa_threaded_mainloop_wait(g_pMainLoop);
264
265 sstate = pa_stream_get_state(pStream);
266 if (sstate == PA_STREAM_READY)
267 break;
268 else if (sstate == PA_STREAM_FAILED || sstate == PA_STREAM_TERMINATED)
269 {
270 LogRel(("Pulse: Failed to initialize stream (state %d)\n", sstate));
271 goto disconnect_unlock_and_fail;
272 }
273 }
274
275 pBufAttrObtained = pa_stream_get_buffer_attr(pStream);
276 memcpy(pBufAttr, pBufAttrObtained, sizeof(pa_buffer_attr));
277
278 if (fIn)
279 {
280 LogRel(("Pulse: Obtained record buffer attributes: maxlength=%d fragsize=%d\n",
281 pBufAttr->maxlength, pBufAttr->fragsize));
282 }
283 else
284 {
285 LogRel(("Pulse: Obtained playback buffer attributes: maxlength=%d tlength=%d prebuf=%d minreq=%d\n",
286 pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
287 }
288
289 pa_threaded_mainloop_unlock(g_pMainLoop);
290 *ppStream = pStream;
291 return 0;
292
293disconnect_unlock_and_fail:
294 pa_stream_disconnect(pStream);
295
296unlock_and_fail:
297 pa_threaded_mainloop_unlock(g_pMainLoop);
298
299fail:
300 if (pStream)
301 pa_stream_unref(pStream);
302
303 *ppStream = NULL;
304 return -1;
305}
306
307static int pulse_init_out (HWVoiceOut *hw, audsettings_t *as)
308{
309 PulseVoice *pPulse = (PulseVoice *) hw;
310 audsettings_t obt_as;
311 int cbBuf;
312
313 pPulse->SampleSpec.format = aud_to_pulsefmt (as->fmt);
314 pPulse->SampleSpec.rate = as->freq;
315 pPulse->SampleSpec.channels = as->nchannels;
316
317 /* Note that setting maxlength to -1 does not work on PulseAudio servers
318 * older than 0.9.10. So use the suggested value of 3/2 of tlength */
319 pPulse->BufAttr.tlength = (pa_bytes_per_second(&pPulse->SampleSpec)
320 * conf.buffer_msecs_out) / 1000;
321 pPulse->BufAttr.maxlength = (pPulse->BufAttr.tlength * 3) / 2;
322 pPulse->BufAttr.prebuf = -1; /* Same as tlength */
323 pPulse->BufAttr.minreq = -1; /* Pulse should set something sensible for minreq on it's own */
324
325 /* Notice that the struct BufAttr is updated to the obtained values after this call */
326 if (pulse_open (0, &pPulse->pStream, &pPulse->SampleSpec, &pPulse->BufAttr))
327 return -1;
328
329 if (pulse_to_audfmt (pPulse->SampleSpec.format, &obt_as.fmt, &obt_as.endianness))
330 {
331 LogRel(("Pulse: Cannot find audio format %d\n", pPulse->SampleSpec.format));
332 return -1;
333 }
334
335 obt_as.freq = pPulse->SampleSpec.rate;
336 obt_as.nchannels = pPulse->SampleSpec.channels;
337
338 audio_pcm_init_info (&hw->info, &obt_as);
339 cbBuf = audio_MIN(pPulse->BufAttr.tlength * 2, pPulse->BufAttr.maxlength);
340
341 pPulse->pPCMBuf = RTMemAllocZ(cbBuf);
342 if (!pPulse->pPCMBuf)
343 {
344 LogRel(("Pulse: Could not allocate DAC buffer of %d bytes\n", cbBuf));
345 return -1;
346 }
347
348 /* Convert from bytes to frames (aka samples) */
349 hw->samples = cbBuf >> hw->info.shift;
350
351 return 0;
352}
353
354static void pulse_fini_out (HWVoiceOut *hw)
355{
356 PulseVoice *pPulse = (PulseVoice *)hw;
357
358 if (pPulse->pStream)
359 {
360 pa_threaded_mainloop_lock(g_pMainLoop);
361 pa_stream_disconnect(pPulse->pStream);
362 pa_stream_unref(pPulse->pStream);
363 pa_threaded_mainloop_unlock(g_pMainLoop);
364 pPulse->pStream = NULL;
365 }
366
367 if (pPulse->pPCMBuf)
368 {
369 RTMemFree (pPulse->pPCMBuf);
370 pPulse->pPCMBuf = NULL;
371 }
372}
373
374static int pulse_run_out (HWVoiceOut *hw)
375{
376 PulseVoice *pPulse = (PulseVoice *) hw;
377 int cFramesLive;
378 int cFramesWritten = 0;
379 int csSamples;
380 int cFramesToWrite;
381 int cFramesAvail;
382 size_t cbAvail;
383 size_t cbToWrite;
384 uint8_t *pu8Dst;
385 st_sample_t *psSrc;
386
387 cFramesLive = audio_pcm_hw_get_live_out (hw);
388 if (!cFramesLive)
389 return 0;
390
391 pa_threaded_mainloop_lock(g_pMainLoop);
392
393 cbAvail = pa_stream_writable_size (pPulse->pStream);
394 if (cbAvail == (size_t)-1)
395 {
396 if (pPulse->cErrors < MAX_LOG_REL_ERRORS)
397 {
398 int rc = pa_context_errno(g_pContext);
399 pPulse->cErrors++;
400 LogRel(("Pulse: Failed to determine the writable size: %s\n",
401 pa_strerror(rc)));
402 }
403 goto unlock_and_exit;
404 }
405
406 cFramesAvail = cbAvail >> hw->info.shift; /* bytes => samples */
407 cFramesWritten = audio_MIN (cFramesLive, cFramesAvail);
408 csSamples = cFramesWritten;
409
410 while (csSamples)
411 {
412 /* split request at the end of our samples buffer */
413 cFramesToWrite = audio_MIN (csSamples, hw->samples - hw->rpos);
414 cbToWrite = cFramesToWrite << hw->info.shift;
415 psSrc = hw->mix_buf + hw->rpos;
416 pu8Dst = advance (pPulse->pPCMBuf, hw->rpos << hw->info.shift);
417
418 hw->clip (pu8Dst, psSrc, cFramesToWrite);
419
420 if (pa_stream_write (pPulse->pStream, pu8Dst, cbToWrite,
421 /*cleanup_callback=*/NULL, 0, PA_SEEK_RELATIVE) < 0)
422 {
423 LogRel(("Pulse: Failed to write %d samples: %s\n",
424 cFramesToWrite, pa_strerror(pa_context_errno(g_pContext))));
425 break;
426 }
427 hw->rpos = (hw->rpos + cFramesToWrite) % hw->samples;
428 csSamples -= cFramesToWrite;
429 }
430
431unlock_and_exit:
432 pa_threaded_mainloop_unlock(g_pMainLoop);
433
434 return cFramesWritten;
435}
436
437static int pulse_write (SWVoiceOut *sw, void *buf, int len)
438{
439 return audio_pcm_sw_write (sw, buf, len);
440}
441
442static void stream_success_callback(pa_stream *pStream, int success, void *userdata)
443{
444 PulseVoice *pPulse = (PulseVoice *) userdata;
445 pPulse->fOpSuccess = success;
446 if (!success)
447 {
448 if (pPulse->cErrors < MAX_LOG_REL_ERRORS)
449 {
450 int rc = pa_context_errno(g_pContext);
451 pPulse->cErrors++;
452 LogRel(("Pulse: Failed stream operation: %s\n", pa_strerror(rc)));
453 }
454 }
455 pa_threaded_mainloop_signal(g_pMainLoop, 0);
456}
457
458static int pulse_wait_for_operation (pa_operation *op)
459{
460 if (op)
461 {
462 while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
463 pa_threaded_mainloop_wait(g_pMainLoop);
464 pa_operation_unref(op);
465 }
466
467 return 1;
468}
469
470static int pulse_ctl_out (HWVoiceOut *hw, int cmd, ...)
471{
472 PulseVoice *pPulse = (PulseVoice *) hw;
473
474 switch (cmd)
475 {
476 case VOICE_ENABLE:
477 pa_threaded_mainloop_lock(g_pMainLoop);
478 pulse_wait_for_operation(pa_stream_cork(pPulse->pStream, 0, stream_success_callback, pPulse));
479 pa_threaded_mainloop_unlock(g_pMainLoop);
480 break;
481
482 case VOICE_DISABLE:
483 pa_threaded_mainloop_lock(g_pMainLoop);
484 pulse_wait_for_operation(pa_stream_trigger(pPulse->pStream, stream_success_callback, pPulse));
485 pulse_wait_for_operation(pa_stream_drain(pPulse->pStream, stream_success_callback, pPulse));
486 pulse_wait_for_operation(pa_stream_cork(pPulse->pStream, 1, stream_success_callback, pPulse));
487 pa_threaded_mainloop_unlock(g_pMainLoop);
488 break;
489
490 default:
491 return -1;
492 }
493 return 0;
494}
495
496static int pulse_init_in (HWVoiceIn *hw, audsettings_t *as)
497{
498 PulseVoice *pPulse = (PulseVoice *) hw;
499 audsettings_t obt_as;
500
501 pPulse->SampleSpec.format = aud_to_pulsefmt (as->fmt);
502 pPulse->SampleSpec.rate = as->freq;
503 pPulse->SampleSpec.channels = as->nchannels;
504
505 /* XXX check these values */
506 pPulse->BufAttr.fragsize = (pa_bytes_per_second(&pPulse->SampleSpec)
507 * conf.buffer_msecs_in) / 1000;
508 pPulse->BufAttr.maxlength = (pPulse->BufAttr.fragsize * 3) / 2;
509 /* Other memebers of pa_buffer_attr are ignored for record streams */
510
511 if (pulse_open (1, &pPulse->pStream, &pPulse->SampleSpec, &pPulse->BufAttr))
512 return -1;
513
514 if (pulse_to_audfmt (pPulse->SampleSpec.format, &obt_as.fmt, &obt_as.endianness))
515 {
516 LogRel(("Pulse: Cannot find audio format %d\n", pPulse->SampleSpec.format));
517 return -1;
518 }
519
520 obt_as.freq = pPulse->SampleSpec.rate;
521 obt_as.nchannels = pPulse->SampleSpec.channels;
522 audio_pcm_init_info (&hw->info, &obt_as);
523 hw->samples = audio_MIN(pPulse->BufAttr.fragsize * 10, pPulse->BufAttr.maxlength)
524 >> hw->info.shift;
525 pPulse->pu8PeekBuf = NULL;
526
527 return 0;
528}
529
530static void pulse_fini_in (HWVoiceIn *hw)
531{
532 PulseVoice *pPulse = (PulseVoice *)hw;
533
534 if (pPulse->pStream)
535 {
536 pa_threaded_mainloop_lock(g_pMainLoop);
537 pa_stream_disconnect(pPulse->pStream);
538 pa_stream_unref(pPulse->pStream);
539 pa_threaded_mainloop_unlock(g_pMainLoop);
540 pPulse->pStream = NULL;
541 }
542}
543
544static int pulse_run_in (HWVoiceIn *hw)
545{
546 PulseVoice *pPulse = (PulseVoice *) hw;
547 const int hwshift = hw->info.shift;
548 int cFramesRead = 0; /* total frames which have been read this call */
549 int cFramesAvail; /* total frames available from pulse at start of call */
550 int cFramesToRead; /* the largest amount we want/can get this call */
551 int cFramesToPeek; /* the largest amount we want/can get this peek */
552
553 /* We should only call pa_stream_readable_size() once and trust the first value */
554 pa_threaded_mainloop_lock(g_pMainLoop);
555 cFramesAvail = pa_stream_readable_size(pPulse->pStream) >> hwshift;
556 pa_threaded_mainloop_unlock(g_pMainLoop);
557
558 if (cFramesAvail == -1)
559 {
560 if (pPulse->cErrors < MAX_LOG_REL_ERRORS)
561 {
562 int rc = pa_context_errno(g_pContext);
563 pPulse->cErrors++;
564 LogRel(("Pulse: Failed to determine the readable size: %s\n",
565 pa_strerror(rc)));
566 }
567 return 0;
568 }
569
570 /* If the buffer was not dropped last call, add what remains */
571 if (pPulse->pu8PeekBuf)
572 cFramesAvail += (pPulse->cbPeekBuf - pPulse->offPeekBuf) >> hwshift;
573
574 cFramesToRead = audio_MIN(cFramesAvail, hw->samples - audio_pcm_hw_get_live_in(hw));
575 for (; cFramesToRead; cFramesToRead -= cFramesToPeek)
576 {
577 /* If there is no data, do another peek */
578 if (!pPulse->pu8PeekBuf)
579 {
580 pa_threaded_mainloop_lock(g_pMainLoop);
581 pa_stream_peek(pPulse->pStream, (const void**)&pPulse->pu8PeekBuf, &pPulse->cbPeekBuf);
582 pa_threaded_mainloop_unlock(g_pMainLoop);
583 pPulse->offPeekBuf = 0;
584 if ( !pPulse->pu8PeekBuf
585 || !pPulse->cbPeekBuf)
586 break;
587 }
588
589 cFramesToPeek = audio_MIN((signed)( pPulse->cbPeekBuf
590 - pPulse->offPeekBuf) >> hwshift,
591 cFramesToRead);
592
593 /* Check for wrapping around the buffer end */
594 if (hw->wpos + cFramesToPeek > hw->samples)
595 {
596 int cFramesDelta = hw->samples - hw->wpos;
597
598 hw->conv(hw->conv_buf + hw->wpos,
599 pPulse->pu8PeekBuf + pPulse->offPeekBuf,
600 cFramesDelta,
601 &nominal_volume);
602
603 hw->conv(hw->conv_buf,
604 pPulse->pu8PeekBuf + pPulse->offPeekBuf + (cFramesDelta << hwshift),
605 cFramesToPeek - cFramesDelta,
606 &nominal_volume);
607 }
608 else
609 {
610 hw->conv(hw->conv_buf + hw->wpos,
611 pPulse->pu8PeekBuf + pPulse->offPeekBuf,
612 cFramesToPeek,
613 &nominal_volume);
614 }
615
616 cFramesRead += cFramesToPeek;
617 hw->wpos = (hw->wpos + cFramesToPeek) % hw->samples;
618 pPulse->offPeekBuf += cFramesToPeek << hwshift;
619
620 /* If the buffer is done, drop it */
621 if (pPulse->offPeekBuf == pPulse->cbPeekBuf)
622 {
623 pa_threaded_mainloop_lock(g_pMainLoop);
624 pa_stream_drop(pPulse->pStream);
625 pa_threaded_mainloop_unlock(g_pMainLoop);
626 pPulse->pu8PeekBuf = NULL;
627 }
628 }
629
630 return cFramesRead;
631}
632
633static int pulse_read (SWVoiceIn *sw, void *buf, int size)
634{
635 return audio_pcm_sw_read (sw, buf, size);
636}
637
638static int pulse_ctl_in (HWVoiceIn *hw, int cmd, ...)
639{
640 PulseVoice *pPulse = (PulseVoice *)hw;
641
642 switch (cmd)
643 {
644 case VOICE_ENABLE:
645 pa_threaded_mainloop_lock(g_pMainLoop);
646 pulse_wait_for_operation(pa_stream_cork(pPulse->pStream, 0, stream_success_callback, pPulse));
647 pa_threaded_mainloop_unlock(g_pMainLoop);
648 break;
649
650 case VOICE_DISABLE:
651 pa_threaded_mainloop_lock(g_pMainLoop);
652 if (pPulse->pu8PeekBuf)
653 {
654 pa_stream_drop(pPulse->pStream);
655 pPulse->pu8PeekBuf = NULL;
656 }
657 pulse_wait_for_operation(pa_stream_cork(pPulse->pStream, 1, stream_success_callback, pPulse));
658 pa_threaded_mainloop_unlock(g_pMainLoop);
659 break;
660
661 default:
662 return -1;
663 }
664 return 0;
665}
666
667static void *pulse_audio_init (void)
668{
669 int rc;
670
671 rc = audioLoadPulseLib();
672 if (RT_FAILURE(rc))
673 {
674 LogRel(("Pulse: Failed to load the PulseAudio shared library! Error %Rrc\n", rc));
675 return NULL;
676 }
677
678 if (!(g_pMainLoop = pa_threaded_mainloop_new()))
679 {
680 LogRel(("Pulse: Failed to allocate main loop: %s\n",
681 pa_strerror(pa_context_errno(g_pContext))));
682 goto fail;
683 }
684
685 if (!(g_pContext = pa_context_new(pa_threaded_mainloop_get_api(g_pMainLoop), "VBox")))
686 {
687 LogRel(("Pulse: Failed to allocate context: %s\n",
688 pa_strerror(pa_context_errno(g_pContext))));
689 goto fail;
690 }
691
692 if (pa_threaded_mainloop_start(g_pMainLoop) < 0)
693 {
694 LogRel(("Pulse: Failed to start threaded mainloop: %s\n",
695 pa_strerror(pa_context_errno(g_pContext))));
696 goto fail;
697 }
698
699 pa_context_set_state_callback(g_pContext, context_state_callback, NULL);
700 pa_threaded_mainloop_lock(g_pMainLoop);
701
702 if (pa_context_connect(g_pContext, /*server=*/NULL, 0, NULL) < 0)
703 {
704 LogRel(("Pulse: Failed to connect to server: %s\n",
705 pa_strerror(pa_context_errno(g_pContext))));
706 goto unlock_and_fail;
707 }
708
709 /* Wait until the g_pContext is ready */
710 for (;;)
711 {
712 pa_context_state_t cstate;
713 pa_threaded_mainloop_wait(g_pMainLoop);
714 cstate = pa_context_get_state(g_pContext);
715 if (cstate == PA_CONTEXT_READY)
716 break;
717 else if (cstate == PA_CONTEXT_TERMINATED || cstate == PA_CONTEXT_FAILED)
718 {
719 LogRel(("Pulse: Failed to initialize context (state %d)\n", cstate));
720 goto unlock_and_fail;
721 }
722 }
723 pa_threaded_mainloop_unlock(g_pMainLoop);
724
725 return &conf;
726
727unlock_and_fail:
728 if (g_pMainLoop)
729 pa_threaded_mainloop_unlock(g_pMainLoop);
730
731fail:
732 if (g_pMainLoop)
733 pa_threaded_mainloop_stop(g_pMainLoop);
734
735 if (g_pContext)
736 {
737 pa_context_disconnect(g_pContext);
738 pa_context_unref(g_pContext);
739 g_pContext = NULL;
740 }
741
742 if (g_pMainLoop)
743 {
744 pa_threaded_mainloop_free(g_pMainLoop);
745 g_pMainLoop = NULL;
746 }
747
748 return NULL;
749}
750
751static void pulse_audio_fini (void *opaque)
752{
753 if (g_pMainLoop)
754 pa_threaded_mainloop_stop(g_pMainLoop);
755
756 if (g_pContext)
757 {
758 pa_context_disconnect(g_pContext);
759 pa_context_unref(g_pContext);
760 g_pContext = NULL;
761 }
762
763 if (g_pMainLoop)
764 {
765 pa_threaded_mainloop_free(g_pMainLoop);
766 g_pMainLoop = NULL;
767 }
768
769 (void) opaque;
770}
771
772static struct audio_option pulse_options[] =
773{
774 {"DAC_MS", AUD_OPT_INT, &conf.buffer_msecs_out,
775 "DAC period size in milliseconds", NULL, 0},
776 {"ADC_MS", AUD_OPT_INT, &conf.buffer_msecs_in,
777 "ADC period size in milliseconds", NULL, 0},
778 {NULL, 0, NULL, NULL, NULL, 0}
779};
780
781static struct audio_pcm_ops pulse_pcm_ops =
782{
783 pulse_init_out,
784 pulse_fini_out,
785 pulse_run_out,
786 pulse_write,
787 pulse_ctl_out,
788
789 pulse_init_in,
790 pulse_fini_in,
791 pulse_run_in,
792 pulse_read,
793 pulse_ctl_in
794};
795
796struct audio_driver pulse_audio_driver =
797{
798 INIT_FIELD (name = ) "pulse",
799 INIT_FIELD (descr = ) "PulseAudio http://www.pulseaudio.org",
800 INIT_FIELD (options = ) pulse_options,
801 INIT_FIELD (init = ) pulse_audio_init,
802 INIT_FIELD (fini = ) pulse_audio_fini,
803 INIT_FIELD (pcm_ops = ) &pulse_pcm_ops,
804 INIT_FIELD (can_be_default = ) 1,
805 INIT_FIELD (max_voices_out = ) INT_MAX,
806 INIT_FIELD (max_voices_in = ) INT_MAX,
807 INIT_FIELD (voice_size_out = ) sizeof (PulseVoice),
808 INIT_FIELD (voice_size_in = ) sizeof (PulseVoice)
809};
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