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 (Type::PULSESOURCE
, player
, mplayer
, stream
)
151 LOG_PULSE ("PulseSource::PulseSource ()\n");
153 this->player
= player
;
158 play_pending
= false;
161 PulseSource::~PulseSource ()
163 LOG_PULSE ("PulseSource::~PulseSource ()\n");
168 PulseSource::InitializeInternal ()
170 LOG_PULSE ("PulseSource::InitializeInternal (), initialized: %i\n", initialized
);
171 // this is a no-op, initialization is done when needed.
176 PulseSource::InitializePA ()
179 pa_sample_spec format
;
180 pa_channel_map channel_map
;
183 LOG_AUDIO ("PulseSource::InitializePA ()\n");
188 if (player
->GetPAState () != PA_CONTEXT_READY
) {
189 LOG_PULSE ("PulseSource::InitializePA (), PA isn't in the ready state.\n");
195 switch (GetInputBytesPerSample ()) {
197 format
.format
= PA_SAMPLE_S16NE
;
198 SetOutputBytesPerSample (2);
201 format
.format
= PA_SAMPLE_S32NE
;
202 SetOutputBytesPerSample (4);
205 LOG_AUDIO ("PulseSource::InitializePA (): Invalid bytes per sample: %i (expected 1, 2 or 3)\n", GetInputBytesPerSample ());
210 format
.rate
= GetSampleRate ();
211 format
.channels
= GetChannels ();
213 if (format
.channels
== 1) {
214 pa_channel_map_init_mono (&channel_map
);
215 } else if (format
.channels
== 2) {
216 pa_channel_map_init_stereo (&channel_map
);
217 } else if (format
.channels
== 6 || format
.channels
== 8) {
218 channel_map
.channels
= format
.channels
;
219 for (unsigned int c
= 0; c
< PA_CHANNELS_MAX
; c
++)
220 channel_map
.map
[c
] = PA_CHANNEL_POSITION_INVALID
;
222 // this map needs testing with a 5.1/7.1 system.
223 channel_map
.map
[0] = PA_CHANNEL_POSITION_FRONT_LEFT
;
224 channel_map
.map
[1] = PA_CHANNEL_POSITION_FRONT_RIGHT
;
225 channel_map
.map
[2] = PA_CHANNEL_POSITION_FRONT_CENTER
;
226 channel_map
.map
[3] = PA_CHANNEL_POSITION_LFE
;
227 channel_map
.map
[4] = PA_CHANNEL_POSITION_REAR_LEFT
;
228 channel_map
.map
[5] = PA_CHANNEL_POSITION_REAR_RIGHT
;
229 if (format
.channels
== 8) {
230 channel_map
.map
[6] = PA_CHANNEL_POSITION_SIDE_LEFT
;
231 channel_map
.map
[7] = PA_CHANNEL_POSITION_SIDE_RIGHT
;
234 if (pa_channel_map_init_auto (&channel_map
, format
.channels
, PA_CHANNEL_MAP_DEFAULT
) == NULL
) {
235 LOG_AUDIO ("PulseSource::InitializePA (): Invalid number of channels: %i\n", format
.channels
);
240 pulse_stream
= pa_stream_new (player
->GetPAContext (), "Audio stream", &format
, &channel_map
);
241 if (pulse_stream
== NULL
) {
242 LOG_AUDIO ("PulseSource::InitializePA (): Stream creation failed: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
246 pa_stream_set_state_callback (pulse_stream
, OnStateChanged
, this);
247 pa_stream_set_write_callback (pulse_stream
, OnWrite
, this);
248 pa_stream_set_underflow_callback (pulse_stream
, OnUnderflow
, this);
250 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
);
252 LOG_AUDIO ("PulseSource::InitializePA (): failed to connect stream: %s.\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
260 player
->UnlockLoop ();
266 PulseSource::CloseInternal ()
268 LOG_PULSE ("PulseSource::CloseInternal ()\n");
274 PulseSource::ClosePA ()
276 LOG_PULSE ("PulseSource::ClosePA () initialized: %i\n", initialized
);
285 pa_stream_set_state_callback (pulse_stream
, NULL
, NULL
);
286 pa_stream_set_write_callback (pulse_stream
, NULL
, NULL
);
287 pa_stream_set_underflow_callback (pulse_stream
, NULL
, NULL
);
288 pa_stream_disconnect (pulse_stream
);
289 pa_stream_unref (pulse_stream
);
292 player
->UnlockLoop ();
297 PulseSource::OnStateChanged (pa_stream
*pulse_stream
, void *userdata
)
299 ((PulseSource
*) userdata
)->OnStateChanged (pulse_stream
);
304 get_pa_stream_state_name (pa_stream_state_t state
)
307 case PA_STREAM_CREATING
: return "PA_STREAM_CREATING";
308 case PA_STREAM_TERMINATED
: return "PA_STREAM_TERMINATED";
309 case PA_STREAM_READY
: return "PA_STREAM_READY";
310 case PA_STREAM_FAILED
: return "PA_STREAM_FAILED";
311 default: return "<UNKNOWN>";
317 PulseSource::GetPAState (pa_stream
*pulse_stream
)
319 pa_stream_state_t result
;
323 if (pulse_stream
== NULL
)
324 pulse_stream
= this->pulse_stream
;
325 if (pulse_stream
!= NULL
) {
326 result
= pa_stream_get_state (pulse_stream
);
328 result
= PA_STREAM_FAILED
;
330 player
->UnlockLoop ();
336 PulseSource::OnUnderflow (pa_stream
*pulse_stream
, void *userdata
)
338 ((PulseSource
*) userdata
)->OnUnderflow ();
342 PulseSource::OnUnderflow ()
344 AudioSource::Underflowed ();
348 PulseSource::StateChanged (AudioState old_state
)
350 if (is_ready
&& GetState () == AudioPlaying
)
355 PulseSource::OnStateChanged (pa_stream
*pulse_stream
)
357 pa_stream_state_t state
;
359 if (pulse_stream
!= this->pulse_stream
&& this->pulse_stream
!= NULL
) {
360 LOG_AUDIO ("PulseSource::OnStateChanged (%p): Invalid stream.\n", pulse_stream
);
364 state
= GetPAState (pulse_stream
);
366 SetCurrentDeployment (false);
368 LOG_PULSE ("PulseSource::OnStateChanged (): %s (%i)\n", get_pa_stream_state_name (state
), state
);
371 case PA_STREAM_READY
:
374 case PA_STREAM_CREATING
:
375 case PA_STREAM_TERMINATED
:
378 case PA_STREAM_FAILED
:
381 LOG_AUDIO ("PulseSource::OnStateChanged (): Stream error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
382 SetState (AudioError
);
388 PulseSource::OnWrite (pa_stream
*s
, size_t length
, void *userdata
)
390 ((PulseSource
*) userdata
)->OnWrite (length
);
394 PulseSource::OnWrite (size_t length
)
400 LOG_PULSE ("PulseSource::OnWrite (%" G_GINT64_FORMAT
")\n", (gint64
) length
);
402 if (pulse_stream
== NULL
) {
403 // We've been destroyed
410 buffer
= g_malloc (length
);
412 frames
= Write (buffer
, length
/ GetOutputBytesPerFrame ());
414 LOG_PULSE ("PulseSource::OnWrite (%" G_GINT64_FORMAT
"): Wrote %" G_GUINT64_FORMAT
" frames\n", (gint64
) length
, (gint64
) frames
);
417 // There is no need to lock here, if in a callback, the caller will have locked
418 // if called from WriteAvailable, that method has locked
419 err
= pa_stream_write (pulse_stream
, buffer
, frames
* GetOutputBytesPerFrame (), (pa_free_cb_t
) g_free
, 0, PA_SEEK_RELATIVE
);
421 LOG_AUDIO ("PulseSource::OnWrite (): Write error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
422 } else if (play_pending
) {
431 PulseSource::WriteAvailable ()
435 LOG_PULSE ("PulseSource::WriteAvailable ()\n");
438 if (pulse_stream
!= NULL
&& is_ready
) {
439 available
= pa_stream_writable_size (pulse_stream
);
440 if (available
!= (size_t) -1) {
443 LOG_AUDIO ("PulseSource::WriteAvailable (): Write error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
446 player
->UnlockLoop ();
450 PulseSource::PACork (bool cork
)
452 LOG_PULSE ("PulseSource::PACork (%i)\n", cork
);
453 pa_operation_unref (pa_stream_cork (pulse_stream
, cork
, NULL
, this));
457 PulseSource::PATrigger ()
459 LOG_PULSE ("PulseSource::PATrigger (), triggered: %i\n", triggered
);
460 pa_operation_unref (pa_stream_trigger (pulse_stream
, NULL
, this));
465 PulseSource::PAFlush ()
467 pa_operation_unref (pa_stream_flush (pulse_stream
, NULL
, this));
471 PulseSource::Played ()
473 LOG_PULSE ("PulseSource::Played ()\n");
475 if (!InitializePA ()) {
476 LOG_PULSE ("PulseSource::Played (): initialization failed.\n");
483 if (pulse_stream
&& is_ready
) {
484 // Uncork the stream (if it was corked)
486 // And start playing it.
488 play_pending
= false;
492 player
->UnlockLoop ();
496 PulseSource::Paused ()
499 play_pending
= false;
500 if (pulse_stream
&& is_ready
)
502 player
->UnlockLoop ();
506 PulseSource::Stopped ()
508 LOG_PULSE ("PulseSource::Stopped ()\n");
511 play_pending
= false;
512 if (pulse_stream
&& is_ready
) {
513 // Pause the stream and wait for the pause to complete
515 // Drop all the samples we've sent
518 player
->UnlockLoop ();
524 PulseSource::GetDelayInternal ()
527 pa_usec_t latency
= 0;
532 if (pulse_stream
&& is_ready
) {
533 err
= pa_stream_get_latency (pulse_stream
, &latency
, &negative
);
535 LOG_AUDIO ("PulseSource::GetDelay (): Error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
536 result
= G_MAXUINT64
;
538 result
= MilliSeconds_ToPts (latency
/ 1000);
541 result
= G_MAXUINT64
;
543 player
->UnlockLoop ();
546 LOG_PULSE ("PulseSource::GetDelay (), result: %" G_GUINT64_FORMAT
" ms, latency: %" G_GUINT64_FORMAT
", err: %i, negative: %i, is_ready: %i, pulse_stream: %p\n",
547 MilliSeconds_FromPts (result
), latency
, err
, negative
, is_ready
, pulse_stream
);
556 PulsePlayer::PulsePlayer ()
560 connected
= ConnectionUnknown
;
561 pthread_mutex_init (&mutex
, NULL
);
562 pthread_cond_init (&cond
, NULL
);
565 PulsePlayer::~PulsePlayer ()
567 pthread_mutex_destroy (&mutex
);
568 pthread_cond_destroy (&cond
);
572 PulsePlayer::WaitLoop ()
574 pa_threaded_mainloop_wait (loop
);
578 PulsePlayer::WaitForOperation (pa_operation
*op
)
580 if (pa_threaded_mainloop_in_thread (loop
))
583 //while (pa_operation_get_state (op) != PA_OPERATION_DONE)
585 pa_operation_unref (op
);
589 PulsePlayer::SignalLoop ()
591 //pa_threaded_mainloop_signal (loop, 0);
595 PulsePlayer::LockLoop ()
597 if (!pa_threaded_mainloop_in_thread (loop
))
598 pa_threaded_mainloop_lock (loop
);
602 PulsePlayer::UnlockLoop ()
604 if (!pa_threaded_mainloop_in_thread (loop
))
605 pa_threaded_mainloop_unlock (loop
);
609 PulsePlayer::CreateNode (MediaPlayer
*mplayer
, AudioStream
*stream
)
611 return new PulseSource (this, mplayer
, stream
);
614 static int is_pulse_usable
= 0; // 0 = not tested, 1 = tested, usable, 2 = tested, not usable
615 static void *libpulse
= NULL
;
618 PulsePlayer::IsInstalled ()
623 switch (is_pulse_usable
) {
625 libpulse
= dlopen ("libpulse.so.0", RTLD_LAZY
);
626 if (libpulse
== NULL
) {
632 result
&= NULL
!= (d_pa_stream_new
= (dyn_pa_stream_new
*) dlsym (libpulse
, "pa_stream_new"));
633 result
&= NULL
!= (d_pa_stream_set_state_callback
= (dyn_pa_stream_set_state_callback
*) dlsym (libpulse
, "pa_stream_set_state_callback"));
634 result
&= NULL
!= (d_pa_stream_set_write_callback
= (dyn_pa_stream_set_write_callback
*) dlsym (libpulse
, "pa_stream_set_write_callback"));
635 result
&= NULL
!= (d_pa_stream_set_underflow_callback
= (dyn_pa_stream_set_underflow_callback
*) dlsym (libpulse
, "pa_stream_set_underflow_callback"));
636 result
&= NULL
!= (d_pa_stream_connect_playback
= (dyn_pa_stream_connect_playback
*) dlsym (libpulse
, "pa_stream_connect_playback"));
637 result
&= NULL
!= (d_pa_stream_disconnect
= (dyn_pa_stream_disconnect
*) dlsym (libpulse
, "pa_stream_disconnect"));
638 result
&= NULL
!= (d_pa_stream_unref
= (dyn_pa_stream_unref
*) dlsym (libpulse
, "pa_stream_unref"));
639 result
&= NULL
!= (d_pa_stream_get_state
= (dyn_pa_stream_get_state
*) dlsym (libpulse
, "pa_stream_get_state"));
640 result
&= NULL
!= (d_pa_stream_write
= (dyn_pa_stream_write
*) dlsym (libpulse
, "pa_stream_write"));
641 result
&= NULL
!= (d_pa_stream_writable_size
= (dyn_pa_stream_writable_size
*) dlsym (libpulse
, "pa_stream_writable_size"));
642 result
&= NULL
!= (d_pa_stream_cork
= (dyn_pa_stream_cork
*) dlsym (libpulse
, "pa_stream_cork"));
643 result
&= NULL
!= (d_pa_stream_trigger
= (dyn_pa_stream_trigger
*) dlsym (libpulse
, "pa_stream_trigger"));
644 result
&= NULL
!= (d_pa_stream_flush
= (dyn_pa_stream_flush
*) dlsym (libpulse
, "pa_stream_flush"));
645 result
&= NULL
!= (d_pa_stream_get_latency
= (dyn_pa_stream_get_latency
*) dlsym (libpulse
, "pa_stream_get_latency"));
647 result
&= NULL
!= (d_pa_context_new
= (dyn_pa_context_new
*) dlsym (libpulse
, "pa_context_new"));
648 result
&= NULL
!= (d_pa_context_errno
= (dyn_pa_context_errno
*) dlsym (libpulse
, "pa_context_errno"));
649 result
&= NULL
!= (d_pa_context_get_state
= (dyn_pa_context_get_state
*) dlsym (libpulse
, "pa_context_get_state"));
650 result
&= NULL
!= (d_pa_context_set_state_callback
= (dyn_pa_context_set_state_callback
*) dlsym (libpulse
, "pa_context_set_state_callback"));
651 result
&= NULL
!= (d_pa_context_connect
= (dyn_pa_context_connect
*) dlsym (libpulse
, "pa_context_connect"));
652 result
&= NULL
!= (d_pa_context_disconnect
= (dyn_pa_context_disconnect
*) dlsym (libpulse
, "pa_context_disconnect"));
653 result
&= NULL
!= (d_pa_context_unref
= (dyn_pa_context_unref
*) dlsym (libpulse
, "pa_context_unref"));
655 result
&= NULL
!= (d_pa_threaded_mainloop_new
= (dyn_pa_threaded_mainloop_new
*) dlsym (libpulse
, "pa_threaded_mainloop_new"));
656 result
&= NULL
!= (d_pa_threaded_mainloop_start
= (dyn_pa_threaded_mainloop_start
*) dlsym (libpulse
, "pa_threaded_mainloop_start"));
657 result
&= NULL
!= (d_pa_threaded_mainloop_get_api
= (dyn_pa_threaded_mainloop_get_api
*) dlsym (libpulse
, "pa_threaded_mainloop_get_api"));
658 result
&= NULL
!= (d_pa_threaded_mainloop_wait
= (dyn_pa_threaded_mainloop_wait
*) dlsym (libpulse
, "pa_threaded_mainloop_wait"));
659 result
&= NULL
!= (d_pa_threaded_mainloop_in_thread
= (dyn_pa_threaded_mainloop_in_thread
*) dlsym (libpulse
, "pa_threaded_mainloop_in_thread"));
660 result
&= NULL
!= (d_pa_threaded_mainloop_lock
= (dyn_pa_threaded_mainloop_lock
*) dlsym (libpulse
, "pa_threaded_mainloop_lock"));
661 result
&= NULL
!= (d_pa_threaded_mainloop_unlock
= (dyn_pa_threaded_mainloop_unlock
*) dlsym (libpulse
, "pa_threaded_mainloop_unlock"));
662 result
&= NULL
!= (d_pa_threaded_mainloop_signal
= (dyn_pa_threaded_mainloop_signal
*) dlsym (libpulse
, "pa_threaded_mainloop_signal"));
663 result
&= NULL
!= (d_pa_threaded_mainloop_stop
= (dyn_pa_threaded_mainloop_stop
*) dlsym (libpulse
, "pa_threaded_mainloop_stop"));
664 result
&= NULL
!= (d_pa_threaded_mainloop_free
= (dyn_pa_threaded_mainloop_free
*) dlsym (libpulse
, "pa_threaded_mainloop_free"));
666 result
&= NULL
!= (d_pa_channel_map_init_mono
= (dyn_pa_channel_map_init_mono
*) dlsym (libpulse
, "pa_channel_map_init_mono"));
667 result
&= NULL
!= (d_pa_channel_map_init_stereo
= (dyn_pa_channel_map_init_stereo
*) dlsym (libpulse
, "pa_channel_map_init_stereo"));
668 result
&= NULL
!= (d_pa_channel_map_init_auto
= (dyn_pa_channel_map_init_auto
*) dlsym (libpulse
, "pa_channel_map_init_auto"));
670 result
&= NULL
!= (d_pa_strerror
= (dyn_pa_strerror
*) dlsym (libpulse
, "pa_strerror"));
672 result
&= NULL
!= (d_pa_operation_get_state
= (dyn_pa_operation_get_state
*) dlsym (libpulse
, "pa_operation_get_state"));
673 result
&= NULL
!= (d_pa_operation_unref
= (dyn_pa_operation_unref
*) dlsym (libpulse
, "pa_operation_unref"));
675 result
&= NULL
!= (d_pa_get_library_version
= (dyn_pa_get_library_version
*) dlsym (libpulse
, "pa_get_library_version"));
677 if (d_pa_get_library_version
!= NULL
) {
678 version
= d_pa_get_library_version ();
679 LOG_AUDIO ("PulsePlayer: Found libpulse version: '%s'\n", version
);
683 LOG_AUDIO ("PulsePlayer: Failed to load one or more required functions in libpulse.so.\n");
685 is_pulse_usable
= result
? 1 : 2;
697 PulsePlayer::OnContextStateChanged (pa_context
*context
, void *userdata
)
699 ((PulsePlayer
*) userdata
)->OnContextStateChanged ();
703 PulsePlayer::GetPAState ()
705 pa_context_state_t result
;
708 result
= pa_context_get_state (context
);
716 get_pa_context_state_name (pa_context_state_t state
)
719 case PA_CONTEXT_CONNECTING
: return "PA_CONTEXT_CONNECTING";
720 case PA_CONTEXT_AUTHORIZING
: return "PA_CONTEXT_AUTHORIZING";
721 case PA_CONTEXT_SETTING_NAME
: return "PA_CONTEXT_SETTING_NAME";
722 case PA_CONTEXT_READY
: return "PA_CONTEXT_READY";
723 case PA_CONTEXT_TERMINATED
: return "PA_CONTEXT_TERMINATED";
724 case PA_CONTEXT_FAILED
: return "PA_CONTEXT_FAILED";
725 default: return "<UNKNOWN>";
731 PulsePlayer::OnContextStateChanged () {
733 pa_context_state_t state
;
735 state
= GetPAState ();
737 LOG_PULSE ("PulsePlayer::OnContextStateChanged (): %s (%i)\n", get_pa_context_state_name (state
), state
);
740 case PA_CONTEXT_CONNECTING
:
741 case PA_CONTEXT_AUTHORIZING
:
742 case PA_CONTEXT_SETTING_NAME
:
744 case PA_CONTEXT_READY
:
746 sources
.StartEnumeration ();
747 while ((source
= (PulseSource
*) sources
.GetNext (false)) != NULL
) {
748 source
->Initialize ();
752 pthread_mutex_lock (&mutex
);
753 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Signalling main thread that we've connected\n");
754 connected
= ConnectionSuccess
;
755 pthread_cond_signal (&cond
);
756 pthread_mutex_unlock (&mutex
);
758 case PA_CONTEXT_TERMINATED
:
760 case PA_CONTEXT_FAILED
:
762 pthread_mutex_lock (&mutex
);
763 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Signalling main thread that we've failed to connect\n");
764 connected
= ConnectionFailed
;
765 pthread_cond_signal (&cond
);
766 pthread_mutex_unlock (&mutex
);
767 fprintf (stderr
, "Moonlight: Connection failure while trying to connect to pulseaudio daemon: %s\n", pa_strerror (pa_context_errno (context
)));
773 PulsePlayer::AddInternal (AudioSource
*source
)
775 LOG_PULSE ("PulsePlayer::AddInternal (%p)\n", source
);
777 ((PulseSource
*) source
)->Initialize ();
781 PulsePlayer::RemoveInternal (AudioSource
*source
)
783 LOG_PULSE ("PulsePlayer::RemoveInternal (%p)\n", source
);
785 ((PulseSource
*) source
)->Close ();
790 PulsePlayer::Initialize ()
794 LOG_PULSE ("PulsePlayer::InitializeInternal ()\n");
796 loop
= pa_threaded_mainloop_new ();
798 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to create main loop.\n");
802 api
= pa_threaded_mainloop_get_api (loop
);
805 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to get api.\n");
809 context
= pa_context_new (api
, "Moonlight");
810 if (context
== NULL
) {
811 LOG_AUDIO ("PulsePlayer::InitializeInternal (); Failed to create context.\n");
815 pa_context_set_state_callback (context
, OnContextStateChanged
, this);
817 err
= pa_context_connect (context
, NULL
, (pa_context_flags_t
) 0, NULL
);
819 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Error %i while connecting to server.\n", err
);
822 if (connected
== ConnectionUnknown
) {
823 LOG_AUDIO ("PulsePlayer::InitializeInternal (): pa_context_connect returned but we're not connected.\n");
825 // It's possible that pulse can return an error to us async
826 // We need to aquire a lock, then start the mainloop
827 pthread_mutex_lock (&mutex
);
829 // Of course it wont raise the async error unless we try
830 // to start the mainloop
831 pa_threaded_mainloop_start (loop
);
834 // Wait until pulse has reported the connection status to
836 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Waiting to see if we can connect.\n");
837 pthread_cond_wait (&cond
, &mutex
);
838 } while (connected
== ConnectionUnknown
);
840 pthread_mutex_unlock (&mutex
);
842 // At this stag we have had connected set regardless of wether
843 // PA wants to be sync or async
844 if (connected
== ConnectionFailed
) {
845 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Asynchronous error while connecting to the pulse daemon\n");
849 LOG_AUDIO ("PulsePlayer::InitializeInternal (): pa_context_connect returned and connected.\n");
850 // We've already connected successfully in a sync fashion
851 // there is no need to lock, we can just start the loop
852 pa_threaded_mainloop_start (loop
);
859 PulsePlayer::PrepareShutdownInternal ()
864 PulsePlayer::FinishShutdownInternal ()
866 LOG_PULSE ("PulsePlayer::ShutdownInternal ()\n");
868 api
= NULL
; // CHECK: Do we need to unref this one?
871 pa_context_disconnect (context
);
872 pa_context_unref (context
);
877 pa_threaded_mainloop_stop (loop
);
878 pa_threaded_mainloop_free (loop
);