1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
17 #include "audio-pulse.h"
23 typedef pa_stream
* (dyn_pa_stream_new
) (pa_context
*c
, const char *name
, const pa_sample_spec
*ss
, const pa_channel_map
*map
);
24 typedef void (dyn_pa_stream_set_state_callback
) (pa_stream
*s
, pa_stream_notify_cb_t cb
, void *userdata
);
25 typedef void (dyn_pa_stream_set_write_callback
) (pa_stream
*p
, pa_stream_request_cb_t cb
, void *userdata
);
26 typedef void (dyn_pa_stream_set_underflow_callback
) (pa_stream
*p
, pa_stream_notify_cb_t cb
, void *userdata
);
27 typedef int (dyn_pa_stream_connect_playback
) (pa_stream
*s
, const char *dev
, const pa_buffer_attr
*attr
, pa_stream_flags_t flags
, pa_cvolume
*volume
, pa_stream
*sync_stream
);
28 typedef int (dyn_pa_stream_disconnect
) (pa_stream
*s
);
29 typedef void (dyn_pa_stream_unref
) (pa_stream
*s
);
30 typedef pa_stream_state_t (dyn_pa_stream_get_state
) (pa_stream
*p
);
31 typedef int (dyn_pa_stream_write
) (pa_stream
*p
, const void *data
, size_t bytes
, pa_free_cb_t free_cb
, int64_t offset
, pa_seek_mode_t seek
);
32 typedef size_t (dyn_pa_stream_writable_size
) (pa_stream
*p
);
33 typedef pa_operation
* (dyn_pa_stream_cork
) (pa_stream
*s
, int b
, pa_stream_success_cb_t cb
, void *userdata
);
34 typedef pa_operation
* (dyn_pa_stream_trigger
) (pa_stream
*s
, pa_stream_success_cb_t cb
, void *userdata
);
35 typedef pa_operation
* (dyn_pa_stream_flush
) (pa_stream
*s
, pa_stream_success_cb_t cb
, void *userdata
);
36 typedef int (dyn_pa_stream_get_latency
) (pa_stream
*s
, pa_usec_t
*r_usec
, int *negative
);
38 typedef pa_context
* (dyn_pa_context_new
) (pa_mainloop_api
*mainloop
, const char *name
);
39 typedef int (dyn_pa_context_errno
) (pa_context
*c
);
40 typedef pa_context_state_t (dyn_pa_context_get_state
) (pa_context
*c
);
41 typedef void (dyn_pa_context_set_state_callback
) (pa_context
*c
, pa_context_notify_cb_t cb
, void *userdata
);
42 typedef int (dyn_pa_context_connect
) (pa_context
*c
, const char *server
, pa_context_flags_t flags
, const pa_spawn_api
*api
);
43 typedef void (dyn_pa_context_disconnect
) (pa_context
*c
);
44 typedef void (dyn_pa_context_unref
) (pa_context
*c
);
46 typedef pa_threaded_mainloop
* (dyn_pa_threaded_mainloop_new
) (void);
47 typedef int (dyn_pa_threaded_mainloop_start
) (pa_threaded_mainloop
*m
);
48 typedef pa_mainloop_api
* (dyn_pa_threaded_mainloop_get_api
) (pa_threaded_mainloop
*m
);
49 typedef void (dyn_pa_threaded_mainloop_wait
) (pa_threaded_mainloop
*m
);
50 typedef int (dyn_pa_threaded_mainloop_in_thread
) (pa_threaded_mainloop
*m
);
51 typedef void (dyn_pa_threaded_mainloop_lock
) (pa_threaded_mainloop
*m
);
52 typedef void (dyn_pa_threaded_mainloop_unlock
) (pa_threaded_mainloop
*m
);
53 typedef void (dyn_pa_threaded_mainloop_signal
) (pa_threaded_mainloop
*m
, int wait_for_accept
);
54 typedef void (dyn_pa_threaded_mainloop_stop
) (pa_threaded_mainloop
*m
);
55 typedef void (dyn_pa_threaded_mainloop_free
) (pa_threaded_mainloop
* m
);
57 typedef pa_channel_map
* (dyn_pa_channel_map_init_mono
) (pa_channel_map
*m
);
58 typedef pa_channel_map
* (dyn_pa_channel_map_init_stereo
) (pa_channel_map
*m
);
59 typedef pa_channel_map
* (dyn_pa_channel_map_init_auto
) (pa_channel_map
*m
, unsigned channels
, pa_channel_map_def_t def
);
61 typedef const char* (dyn_pa_strerror
) (int error
);
63 typedef pa_operation_state_t (dyn_pa_operation_get_state
) (pa_operation
*o
);
64 typedef void (dyn_pa_operation_unref
) (pa_operation
*o
);
66 typedef const char * (dyn_pa_get_library_version
) ();
68 dyn_pa_stream_new
* d_pa_stream_new
= NULL
;
69 dyn_pa_stream_set_state_callback
* d_pa_stream_set_state_callback
= NULL
;
70 dyn_pa_stream_set_write_callback
* d_pa_stream_set_write_callback
= NULL
;
71 dyn_pa_stream_set_underflow_callback
* d_pa_stream_set_underflow_callback
= NULL
;
72 dyn_pa_stream_connect_playback
* d_pa_stream_connect_playback
= NULL
;
73 dyn_pa_stream_disconnect
* d_pa_stream_disconnect
= NULL
;
74 dyn_pa_stream_unref
* d_pa_stream_unref
= NULL
;
75 dyn_pa_stream_get_state
* d_pa_stream_get_state
= NULL
;
76 dyn_pa_stream_write
* d_pa_stream_write
= NULL
;
77 dyn_pa_stream_writable_size
* d_pa_stream_writable_size
= NULL
;
78 dyn_pa_stream_cork
* d_pa_stream_cork
= NULL
;
79 dyn_pa_stream_trigger
* d_pa_stream_trigger
= NULL
;
80 dyn_pa_stream_flush
* d_pa_stream_flush
= NULL
;
81 dyn_pa_stream_get_latency
* d_pa_stream_get_latency
= NULL
;
82 dyn_pa_context_new
* d_pa_context_new
= NULL
;
83 dyn_pa_context_errno
* d_pa_context_errno
= NULL
;
84 dyn_pa_context_get_state
* d_pa_context_get_state
= NULL
;
85 dyn_pa_context_set_state_callback
* d_pa_context_set_state_callback
= NULL
;
86 dyn_pa_context_connect
* d_pa_context_connect
= NULL
;
87 dyn_pa_context_disconnect
* d_pa_context_disconnect
= NULL
;
88 dyn_pa_context_unref
* d_pa_context_unref
= NULL
;
89 dyn_pa_threaded_mainloop_new
* d_pa_threaded_mainloop_new
= NULL
;
90 dyn_pa_threaded_mainloop_start
* d_pa_threaded_mainloop_start
= NULL
;
91 dyn_pa_threaded_mainloop_get_api
* d_pa_threaded_mainloop_get_api
= NULL
;
92 dyn_pa_threaded_mainloop_wait
* d_pa_threaded_mainloop_wait
= NULL
;
93 dyn_pa_threaded_mainloop_in_thread
* d_pa_threaded_mainloop_in_thread
= NULL
;
94 dyn_pa_threaded_mainloop_lock
* d_pa_threaded_mainloop_lock
= NULL
;
95 dyn_pa_threaded_mainloop_unlock
* d_pa_threaded_mainloop_unlock
= NULL
;
96 dyn_pa_threaded_mainloop_signal
* d_pa_threaded_mainloop_signal
= NULL
;
97 dyn_pa_threaded_mainloop_stop
* d_pa_threaded_mainloop_stop
= NULL
;
98 dyn_pa_threaded_mainloop_free
* d_pa_threaded_mainloop_free
= NULL
;
99 dyn_pa_channel_map_init_mono
* d_pa_channel_map_init_mono
= NULL
;
100 dyn_pa_channel_map_init_stereo
* d_pa_channel_map_init_stereo
= NULL
;
101 dyn_pa_channel_map_init_auto
* d_pa_channel_map_init_auto
= NULL
;
102 dyn_pa_strerror
* d_pa_strerror
= NULL
;
103 dyn_pa_operation_get_state
* d_pa_operation_get_state
= NULL
;
104 dyn_pa_operation_unref
* d_pa_operation_unref
= NULL
;
105 dyn_pa_get_library_version
* d_pa_get_library_version
= NULL
;
107 #define pa_stream_new d_pa_stream_new
108 #define pa_stream_set_state_callback d_pa_stream_set_state_callback
109 #define pa_stream_set_write_callback d_pa_stream_set_write_callback
110 #define pa_stream_set_underflow_callback d_pa_stream_set_underflow_callback
111 #define pa_stream_connect_playback d_pa_stream_connect_playback
112 #define pa_stream_disconnect d_pa_stream_disconnect
113 #define pa_stream_unref d_pa_stream_unref
114 #define pa_stream_get_state d_pa_stream_get_state
115 #define pa_stream_write d_pa_stream_write
116 #define pa_stream_writable_size d_pa_stream_writable_size
117 #define pa_stream_cork d_pa_stream_cork
118 #define pa_stream_trigger d_pa_stream_trigger
119 #define pa_stream_flush d_pa_stream_flush
120 #define pa_stream_get_latency d_pa_stream_get_latency
121 #define pa_context_new d_pa_context_new
122 #define pa_context_errno d_pa_context_errno
123 #define pa_context_get_state d_pa_context_get_state
124 #define pa_context_set_state_callback d_pa_context_set_state_callback
125 #define pa_context_connect d_pa_context_connect
126 #define pa_context_disconnect d_pa_context_disconnect
127 #define pa_context_unref d_pa_context_unref
128 #define pa_threaded_mainloop_new d_pa_threaded_mainloop_new
129 #define pa_threaded_mainloop_start d_pa_threaded_mainloop_start
130 #define pa_threaded_mainloop_get_api d_pa_threaded_mainloop_get_api
131 #define pa_threaded_mainloop_wait d_pa_threaded_mainloop_wait
132 #define pa_threaded_mainloop_in_thread d_pa_threaded_mainloop_in_thread
133 #define pa_threaded_mainloop_lock d_pa_threaded_mainloop_lock
134 #define pa_threaded_mainloop_unlock d_pa_threaded_mainloop_unlock
135 #define pa_threaded_mainloop_signal d_pa_threaded_mainloop_signal
136 #define pa_threaded_mainloop_stop d_pa_threaded_mainloop_stop
137 #define pa_threaded_mainloop_free d_pa_threaded_mainloop_free
138 #define pa_channel_map_init_mono d_pa_channel_map_init_mono
139 #define pa_channel_map_init_stereo d_pa_channel_map_init_stereo
140 #define pa_channel_map_init_auto d_pa_channel_map_init_auto
141 #define pa_strerror d_pa_strerror
142 #define pa_operation_get_state d_pa_operation_get_state
143 #define pa_operation_unref d_pa_operation_unref
149 PulseSource::PulseSource (PulsePlayer
*player
, MediaPlayer
*mplayer
, AudioStream
*stream
) : AudioSource (player
, mplayer
, stream
)
151 LOG_PULSE ("PulseSource::PulseSource ()\n");
153 this->player
= player
;
158 play_pending
= false;
162 PulseSource::~PulseSource ()
164 LOG_PULSE ("PulseSource::~PulseSource ()\n");
169 PulseSource::InitializeInternal ()
171 LOG_PULSE ("PulseSource::InitializeInternal (), initialized: %i\n", initialized
);
176 if (player
->GetPAState () != PA_CONTEXT_READY
)
181 if (!InitializePA ()) {
182 SetState (AudioError
);
190 PulseSource::InitializePA ()
193 pa_sample_spec format
;
194 pa_channel_map channel_map
;
197 LOG_AUDIO ("PulseSource::InitializePA ()\n");
201 switch (GetInputBytesPerSample ()) {
203 format
.format
= PA_SAMPLE_S16NE
;
204 SetOutputBytesPerSample (2);
207 format
.format
= PA_SAMPLE_S32NE
;
208 SetOutputBytesPerSample (4);
211 LOG_AUDIO ("PulseSource::InitializePA (): Invalid bytes per sample: %i (expected 1, 2 or 3)\n", GetInputBytesPerSample ());
216 format
.rate
= GetSampleRate ();
217 format
.channels
= GetChannels ();
219 if (format
.channels
== 1) {
220 pa_channel_map_init_mono (&channel_map
);
221 } else if (format
.channels
== 2) {
222 pa_channel_map_init_stereo (&channel_map
);
223 } else if (format
.channels
== 6 || format
.channels
== 8) {
224 channel_map
.channels
= format
.channels
;
225 for (unsigned int c
= 0; c
< PA_CHANNELS_MAX
; c
++)
226 channel_map
.map
[c
] = PA_CHANNEL_POSITION_INVALID
;
228 // this map needs testing with a 5.1/7.1 system.
229 channel_map
.map
[0] = PA_CHANNEL_POSITION_FRONT_LEFT
;
230 channel_map
.map
[1] = PA_CHANNEL_POSITION_FRONT_RIGHT
;
231 channel_map
.map
[2] = PA_CHANNEL_POSITION_FRONT_CENTER
;
232 channel_map
.map
[3] = PA_CHANNEL_POSITION_LFE
;
233 channel_map
.map
[4] = PA_CHANNEL_POSITION_REAR_LEFT
;
234 channel_map
.map
[5] = PA_CHANNEL_POSITION_REAR_RIGHT
;
235 if (format
.channels
== 8) {
236 channel_map
.map
[6] = PA_CHANNEL_POSITION_SIDE_LEFT
;
237 channel_map
.map
[7] = PA_CHANNEL_POSITION_SIDE_RIGHT
;
240 if (pa_channel_map_init_auto (&channel_map
, format
.channels
, PA_CHANNEL_MAP_DEFAULT
) == NULL
) {
241 LOG_AUDIO ("PulseSource::InitializePA (): Invalid number of channels: %i\n", format
.channels
);
246 pulse_stream
= pa_stream_new (player
->GetPAContext (), "Audio stream", &format
, &channel_map
);
247 if (pulse_stream
== NULL
) {
248 LOG_AUDIO ("PulseSource::InitializePA (): Stream creation failed: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
252 pa_stream_set_state_callback (pulse_stream
, OnStateChanged
, this);
253 pa_stream_set_write_callback (pulse_stream
, OnWrite
, this);
254 pa_stream_set_underflow_callback (pulse_stream
, OnUnderflow
, this);
256 err
= pa_stream_connect_playback (pulse_stream
, NULL
, NULL
, (pa_stream_flags_t
) (PA_STREAM_INTERPOLATE_TIMING
| PA_STREAM_AUTO_TIMING_UPDATE
| PA_STREAM_START_CORKED
), NULL
, NULL
);
258 LOG_AUDIO ("PulseSource::InitializePA (): failed to connect stream: %s.\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
265 player
->UnlockLoop ();
271 PulseSource::CloseInternal ()
273 LOG_PULSE ("PulseSource::CloseInternal ()\n");
283 pa_stream_set_state_callback (pulse_stream
, NULL
, NULL
);
284 pa_stream_set_write_callback (pulse_stream
, NULL
, NULL
);
285 pa_stream_set_underflow_callback (pulse_stream
, NULL
, NULL
);
286 pa_stream_disconnect (pulse_stream
);
287 pa_stream_unref (pulse_stream
);
290 player
->UnlockLoop ();
294 PulseSource::OnStateChanged (pa_stream
*pulse_stream
, void *userdata
)
296 ((PulseSource
*) userdata
)->OnStateChanged (pulse_stream
);
300 get_pa_stream_state_name (pa_stream_state_t state
)
303 case PA_STREAM_CREATING
: return "PA_STREAM_CREATING";
304 case PA_STREAM_TERMINATED
: return "PA_STREAM_TERMINATED";
305 case PA_STREAM_READY
: return "PA_STREAM_READY";
306 case PA_STREAM_FAILED
: return "PA_STREAM_FAILED";
307 default: return "<UNKNOWN>";
312 PulseSource::GetPAState (pa_stream
*pulse_stream
)
314 pa_stream_state_t result
;
318 if (pulse_stream
== NULL
)
319 pulse_stream
= this->pulse_stream
;
320 if (pulse_stream
!= NULL
) {
321 result
= pa_stream_get_state (pulse_stream
);
323 result
= PA_STREAM_FAILED
;
325 player
->UnlockLoop ();
331 PulseSource::OnUnderflow (pa_stream
*pulse_stream
, void *userdata
)
333 ((PulseSource
*) userdata
)->OnUnderflow ();
337 PulseSource::OnUnderflow ()
339 AudioSource::Underflowed ();
343 PulseSource::StateChanged (AudioState old_state
)
345 if (is_ready
&& GetState () == AudioPlaying
)
350 PulseSource::OnStateChanged (pa_stream
*pulse_stream
)
352 pa_stream_state_t state
;
354 if (pulse_stream
!= this->pulse_stream
&& this->pulse_stream
!= NULL
) {
355 LOG_AUDIO ("PulseSource::OnStateChanged (%p): Invalid stream.\n", pulse_stream
);
359 state
= GetPAState (pulse_stream
);
361 SetCurrentDeployment (false);
363 LOG_PULSE ("PulseSource::OnStateChanged (): %s (%i)\n", get_pa_stream_state_name (state
), state
);
366 case PA_STREAM_READY
:
369 case PA_STREAM_CREATING
:
370 case PA_STREAM_TERMINATED
:
373 case PA_STREAM_FAILED
:
376 LOG_AUDIO ("PulseSource::OnStateChanged (): Stream error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
377 SetState (AudioError
);
383 PulseSource::OnWrite (pa_stream
*s
, size_t length
, void *userdata
)
385 ((PulseSource
*) userdata
)->OnWrite (length
);
389 PulseSource::OnWrite (size_t length
)
395 LOG_PULSE ("PulseSource::OnWrite (%lld)\n", (gint64
) length
);
397 if (pulse_stream
== NULL
) {
398 // We've been destroyed
405 buffer
= g_malloc (length
);
407 frames
= Write (buffer
, length
/ GetOutputBytesPerFrame ());
409 LOG_PULSE ("PulseSource::OnWrite (%lld): Wrote %" G_GUINT64_FORMAT
" frames\n", (gint64
) length
, (gint64
) frames
);
412 // There is no need to lock here, if in a callback, the caller will have locked
413 // if called from WriteAvailable, that method has locked
414 err
= pa_stream_write (pulse_stream
, buffer
, frames
* GetOutputBytesPerFrame (), (pa_free_cb_t
) g_free
, 0, PA_SEEK_RELATIVE
);
416 LOG_AUDIO ("PulseSource::OnWrite (): Write error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
417 } else if (play_pending
) {
426 PulseSource::WriteAvailable ()
430 LOG_PULSE ("PulseSource::WriteAvailable ()\n");
433 if (pulse_stream
!= NULL
&& is_ready
) {
434 available
= pa_stream_writable_size (pulse_stream
);
435 if (available
!= (size_t) -1) {
438 LOG_AUDIO ("PulseSource::WriteAvailable (): Write error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
441 player
->UnlockLoop ();
445 PulseSource::PACork (bool cork
)
447 LOG_PULSE ("PulseSource::PACork (%i)\n", cork
);
448 pa_operation_unref (pa_stream_cork (pulse_stream
, cork
, NULL
, this));
452 PulseSource::PATrigger ()
454 LOG_PULSE ("PulseSource::PATrigger (), triggered: %i\n", triggered
);
455 pa_operation_unref (pa_stream_trigger (pulse_stream
, NULL
, this));
460 PulseSource::PAFlush ()
462 pa_operation_unref (pa_stream_flush (pulse_stream
, NULL
, this));
466 PulseSource::Played ()
468 LOG_PULSE ("PulseSource::Played ()\n");
473 if (pulse_stream
&& is_ready
) {
474 // Uncork the stream (if it was corked)
476 // And start playing it.
478 play_pending
= false;
482 player
->UnlockLoop ();
486 PulseSource::Paused ()
489 play_pending
= false;
490 if (pulse_stream
&& is_ready
)
492 player
->UnlockLoop ();
496 PulseSource::Stopped ()
498 LOG_PULSE ("PulseSource::Stopped ()\n");
501 play_pending
= false;
502 if (pulse_stream
&& is_ready
) {
503 // Pause the stream and wait for the pause to complete
505 // Drop all the samples we've sent
508 player
->UnlockLoop ();
512 PulseSource::GetDelayInternal ()
515 pa_usec_t latency
= 0;
520 if (pulse_stream
&& is_ready
) {
521 err
= pa_stream_get_latency (pulse_stream
, &latency
, &negative
);
523 LOG_AUDIO ("PulseSource::GetDelay (): Error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
524 result
= G_MAXUINT64
;
526 result
= MilliSeconds_ToPts (latency
/ 1000);
529 result
= G_MAXUINT64
;
531 player
->UnlockLoop ();
534 LOG_PULSE ("PulseSource::GetDelay (), result: %" G_GUINT64_FORMAT
" ms, latency: %" G_GUINT64_FORMAT
", err: %i, negative: %i, is_ready: %i, pulse_stream: %p\n",
535 MilliSeconds_FromPts (result
), latency
, err
, negative
, is_ready
, pulse_stream
);
544 PulsePlayer::PulsePlayer ()
548 connected
= ConnectionUnknown
;
549 pthread_mutex_init (&mutex
, NULL
);
550 pthread_cond_init (&cond
, NULL
);
553 PulsePlayer::~PulsePlayer ()
555 pthread_mutex_destroy (&mutex
);
556 pthread_cond_destroy (&cond
);
560 PulsePlayer::WaitLoop ()
562 pa_threaded_mainloop_wait (loop
);
566 PulsePlayer::WaitForOperation (pa_operation
*op
)
568 if (pa_threaded_mainloop_in_thread (loop
))
571 //while (pa_operation_get_state (op) != PA_OPERATION_DONE)
573 pa_operation_unref (op
);
577 PulsePlayer::SignalLoop ()
579 //pa_threaded_mainloop_signal (loop, 0);
583 PulsePlayer::LockLoop ()
585 if (!pa_threaded_mainloop_in_thread (loop
))
586 pa_threaded_mainloop_lock (loop
);
590 PulsePlayer::UnlockLoop ()
592 if (!pa_threaded_mainloop_in_thread (loop
))
593 pa_threaded_mainloop_unlock (loop
);
597 PulsePlayer::CreateNode (MediaPlayer
*mplayer
, AudioStream
*stream
)
599 return new PulseSource (this, mplayer
, stream
);
602 static int is_pulse_usable
= 0; // 0 = not tested, 1 = tested, usable, 2 = tested, not usable
603 static void *libpulse
= NULL
;
606 PulsePlayer::IsInstalled ()
611 switch (is_pulse_usable
) {
613 libpulse
= dlopen ("libpulse.so.0", RTLD_LAZY
);
614 if (libpulse
== NULL
) {
620 result
&= NULL
!= (d_pa_stream_new
= (dyn_pa_stream_new
*) dlsym (libpulse
, "pa_stream_new"));
621 result
&= NULL
!= (d_pa_stream_set_state_callback
= (dyn_pa_stream_set_state_callback
*) dlsym (libpulse
, "pa_stream_set_state_callback"));
622 result
&= NULL
!= (d_pa_stream_set_write_callback
= (dyn_pa_stream_set_write_callback
*) dlsym (libpulse
, "pa_stream_set_write_callback"));
623 result
&= NULL
!= (d_pa_stream_set_underflow_callback
= (dyn_pa_stream_set_underflow_callback
*) dlsym (libpulse
, "pa_stream_set_underflow_callback"));
624 result
&= NULL
!= (d_pa_stream_connect_playback
= (dyn_pa_stream_connect_playback
*) dlsym (libpulse
, "pa_stream_connect_playback"));
625 result
&= NULL
!= (d_pa_stream_disconnect
= (dyn_pa_stream_disconnect
*) dlsym (libpulse
, "pa_stream_disconnect"));
626 result
&= NULL
!= (d_pa_stream_unref
= (dyn_pa_stream_unref
*) dlsym (libpulse
, "pa_stream_unref"));
627 result
&= NULL
!= (d_pa_stream_get_state
= (dyn_pa_stream_get_state
*) dlsym (libpulse
, "pa_stream_get_state"));
628 result
&= NULL
!= (d_pa_stream_write
= (dyn_pa_stream_write
*) dlsym (libpulse
, "pa_stream_write"));
629 result
&= NULL
!= (d_pa_stream_writable_size
= (dyn_pa_stream_writable_size
*) dlsym (libpulse
, "pa_stream_writable_size"));
630 result
&= NULL
!= (d_pa_stream_cork
= (dyn_pa_stream_cork
*) dlsym (libpulse
, "pa_stream_cork"));
631 result
&= NULL
!= (d_pa_stream_trigger
= (dyn_pa_stream_trigger
*) dlsym (libpulse
, "pa_stream_trigger"));
632 result
&= NULL
!= (d_pa_stream_flush
= (dyn_pa_stream_flush
*) dlsym (libpulse
, "pa_stream_flush"));
633 result
&= NULL
!= (d_pa_stream_get_latency
= (dyn_pa_stream_get_latency
*) dlsym (libpulse
, "pa_stream_get_latency"));
635 result
&= NULL
!= (d_pa_context_new
= (dyn_pa_context_new
*) dlsym (libpulse
, "pa_context_new"));
636 result
&= NULL
!= (d_pa_context_errno
= (dyn_pa_context_errno
*) dlsym (libpulse
, "pa_context_errno"));
637 result
&= NULL
!= (d_pa_context_get_state
= (dyn_pa_context_get_state
*) dlsym (libpulse
, "pa_context_get_state"));
638 result
&= NULL
!= (d_pa_context_set_state_callback
= (dyn_pa_context_set_state_callback
*) dlsym (libpulse
, "pa_context_set_state_callback"));
639 result
&= NULL
!= (d_pa_context_connect
= (dyn_pa_context_connect
*) dlsym (libpulse
, "pa_context_connect"));
640 result
&= NULL
!= (d_pa_context_disconnect
= (dyn_pa_context_disconnect
*) dlsym (libpulse
, "pa_context_disconnect"));
641 result
&= NULL
!= (d_pa_context_unref
= (dyn_pa_context_unref
*) dlsym (libpulse
, "pa_context_unref"));
643 result
&= NULL
!= (d_pa_threaded_mainloop_new
= (dyn_pa_threaded_mainloop_new
*) dlsym (libpulse
, "pa_threaded_mainloop_new"));
644 result
&= NULL
!= (d_pa_threaded_mainloop_start
= (dyn_pa_threaded_mainloop_start
*) dlsym (libpulse
, "pa_threaded_mainloop_start"));
645 result
&= NULL
!= (d_pa_threaded_mainloop_get_api
= (dyn_pa_threaded_mainloop_get_api
*) dlsym (libpulse
, "pa_threaded_mainloop_get_api"));
646 result
&= NULL
!= (d_pa_threaded_mainloop_wait
= (dyn_pa_threaded_mainloop_wait
*) dlsym (libpulse
, "pa_threaded_mainloop_wait"));
647 result
&= NULL
!= (d_pa_threaded_mainloop_in_thread
= (dyn_pa_threaded_mainloop_in_thread
*) dlsym (libpulse
, "pa_threaded_mainloop_in_thread"));
648 result
&= NULL
!= (d_pa_threaded_mainloop_lock
= (dyn_pa_threaded_mainloop_lock
*) dlsym (libpulse
, "pa_threaded_mainloop_lock"));
649 result
&= NULL
!= (d_pa_threaded_mainloop_unlock
= (dyn_pa_threaded_mainloop_unlock
*) dlsym (libpulse
, "pa_threaded_mainloop_unlock"));
650 result
&= NULL
!= (d_pa_threaded_mainloop_signal
= (dyn_pa_threaded_mainloop_signal
*) dlsym (libpulse
, "pa_threaded_mainloop_signal"));
651 result
&= NULL
!= (d_pa_threaded_mainloop_stop
= (dyn_pa_threaded_mainloop_stop
*) dlsym (libpulse
, "pa_threaded_mainloop_stop"));
652 result
&= NULL
!= (d_pa_threaded_mainloop_free
= (dyn_pa_threaded_mainloop_free
*) dlsym (libpulse
, "pa_threaded_mainloop_free"));
654 result
&= NULL
!= (d_pa_channel_map_init_mono
= (dyn_pa_channel_map_init_mono
*) dlsym (libpulse
, "pa_channel_map_init_mono"));
655 result
&= NULL
!= (d_pa_channel_map_init_stereo
= (dyn_pa_channel_map_init_stereo
*) dlsym (libpulse
, "pa_channel_map_init_stereo"));
656 result
&= NULL
!= (d_pa_channel_map_init_auto
= (dyn_pa_channel_map_init_auto
*) dlsym (libpulse
, "pa_channel_map_init_auto"));
658 result
&= NULL
!= (d_pa_strerror
= (dyn_pa_strerror
*) dlsym (libpulse
, "pa_strerror"));
660 result
&= NULL
!= (d_pa_operation_get_state
= (dyn_pa_operation_get_state
*) dlsym (libpulse
, "pa_operation_get_state"));
661 result
&= NULL
!= (d_pa_operation_unref
= (dyn_pa_operation_unref
*) dlsym (libpulse
, "pa_operation_unref"));
663 result
&= NULL
!= (d_pa_get_library_version
= (dyn_pa_get_library_version
*) dlsym (libpulse
, "pa_get_library_version"));
665 if (d_pa_get_library_version
!= NULL
) {
666 version
= d_pa_get_library_version ();
667 LOG_AUDIO ("PulsePlayer: Found libpulse version: '%s'\n", version
);
671 LOG_AUDIO ("PulsePlayer: Failed to load one or more required functions in libpulse.so.\n");
673 is_pulse_usable
= result
? 1 : 2;
685 PulsePlayer::OnContextStateChanged (pa_context
*context
, void *userdata
)
687 ((PulsePlayer
*) userdata
)->OnContextStateChanged ();
691 PulsePlayer::GetPAState ()
693 pa_context_state_t result
;
696 result
= pa_context_get_state (context
);
703 get_pa_context_state_name (pa_context_state_t state
)
706 case PA_CONTEXT_CONNECTING
: return "PA_CONTEXT_CONNECTING";
707 case PA_CONTEXT_AUTHORIZING
: return "PA_CONTEXT_AUTHORIZING";
708 case PA_CONTEXT_SETTING_NAME
: return "PA_CONTEXT_SETTING_NAME";
709 case PA_CONTEXT_READY
: return "PA_CONTEXT_READY";
710 case PA_CONTEXT_TERMINATED
: return "PA_CONTEXT_TERMINATED";
711 case PA_CONTEXT_FAILED
: return "PA_CONTEXT_FAILED";
712 default: return "<UNKNOWN>";
717 PulsePlayer::OnContextStateChanged () {
719 pa_context_state_t state
;
721 state
= GetPAState ();
723 LOG_PULSE ("PulsePlayer::OnContextStateChanged (): %s (%i)\n", get_pa_context_state_name (state
), state
);
726 case PA_CONTEXT_CONNECTING
:
727 case PA_CONTEXT_AUTHORIZING
:
728 case PA_CONTEXT_SETTING_NAME
:
730 case PA_CONTEXT_READY
:
732 sources
.StartEnumeration ();
733 while ((source
= (PulseSource
*) sources
.GetNext (false)) != NULL
) {
734 source
->Initialize ();
738 pthread_mutex_lock (&mutex
);
739 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Signalling main thread that we've connected\n");
740 connected
= ConnectionSuccess
;
741 pthread_cond_signal (&cond
);
742 pthread_mutex_unlock (&mutex
);
744 case PA_CONTEXT_TERMINATED
:
746 case PA_CONTEXT_FAILED
:
748 pthread_mutex_lock (&mutex
);
749 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Signalling main thread that we've failed to connect\n");
750 connected
= ConnectionFailed
;
751 pthread_cond_signal (&cond
);
752 pthread_mutex_unlock (&mutex
);
753 fprintf (stderr
, "Moonlight: Connection failure while trying to connect to pulseaudio daemon: %s\n", pa_strerror (pa_context_errno (context
)));
759 PulsePlayer::AddInternal (AudioSource
*source
)
761 LOG_PULSE ("PulsePlayer::AddInternal (%p)\n", source
);
763 ((PulseSource
*) source
)->Initialize ();
767 PulsePlayer::RemoveInternal (AudioSource
*source
)
769 LOG_PULSE ("PulsePlayer::RemoveInternal (%p)\n", source
);
771 ((PulseSource
*) source
)->Close ();
776 PulsePlayer::Initialize ()
780 LOG_PULSE ("PulsePlayer::InitializeInternal ()\n");
782 loop
= pa_threaded_mainloop_new ();
784 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to create main loop.\n");
788 api
= pa_threaded_mainloop_get_api (loop
);
791 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to get api.\n");
795 context
= pa_context_new (api
, "Moonlight");
796 if (context
== NULL
) {
797 LOG_AUDIO ("PulsePlayer::InitializeInternal (); Failed to create context.\n");
801 pa_context_set_state_callback (context
, OnContextStateChanged
, this);
803 err
= pa_context_connect (context
, NULL
, (pa_context_flags_t
) 0, NULL
);
805 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Error %i while connecting to server.\n", err
);
808 if (connected
== ConnectionUnknown
) {
809 LOG_AUDIO ("PulsePlayer::InitializeInternal (): pa_context_connect returned but we're not connected.\n");
811 // It's possible that pulse can return an error to us async
812 // We need to aquire a lock, then start the mainloop
813 pthread_mutex_lock (&mutex
);
815 // Of course it wont raise the async error unless we try
816 // to start the mainloop
817 pa_threaded_mainloop_start (loop
);
820 // Wait until pulse has reported the connection status to
822 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Waiting to see if we can connect.\n");
823 pthread_cond_wait (&cond
, &mutex
);
824 } while (connected
== ConnectionUnknown
);
826 pthread_mutex_unlock (&mutex
);
828 // At this stag we have had connected set regardless of wether
829 // PA wants to be sync or async
830 if (connected
== ConnectionFailed
) {
831 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Asynchronous error while connecting to the pulse daemon\n");
835 LOG_AUDIO ("PulsePlayer::InitializeInternal (): pa_context_connect returned and connected.\n");
836 // We've already connected successfully in a sync fashion
837 // there is no need to lock, we can just start the loop
838 pa_threaded_mainloop_start (loop
);
845 PulsePlayer::PrepareShutdownInternal ()
850 PulsePlayer::FinishShutdownInternal ()
852 LOG_PULSE ("PulsePlayer::ShutdownInternal ()\n");
854 api
= NULL
; // CHECK: Do we need to unref this one?
857 pa_context_disconnect (context
);
858 pa_context_unref (context
);
863 pa_threaded_mainloop_stop (loop
);
864 pa_threaded_mainloop_free (loop
);