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;
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
);
303 get_pa_stream_state_name (pa_stream_state_t state
)
306 case PA_STREAM_CREATING
: return "PA_STREAM_CREATING";
307 case PA_STREAM_TERMINATED
: return "PA_STREAM_TERMINATED";
308 case PA_STREAM_READY
: return "PA_STREAM_READY";
309 case PA_STREAM_FAILED
: return "PA_STREAM_FAILED";
310 default: return "<UNKNOWN>";
315 PulseSource::GetPAState (pa_stream
*pulse_stream
)
317 pa_stream_state_t result
;
321 if (pulse_stream
== NULL
)
322 pulse_stream
= this->pulse_stream
;
323 if (pulse_stream
!= NULL
) {
324 result
= pa_stream_get_state (pulse_stream
);
326 result
= PA_STREAM_FAILED
;
328 player
->UnlockLoop ();
334 PulseSource::OnUnderflow (pa_stream
*pulse_stream
, void *userdata
)
336 ((PulseSource
*) userdata
)->OnUnderflow ();
340 PulseSource::OnUnderflow ()
342 AudioSource::Underflowed ();
346 PulseSource::StateChanged (AudioState old_state
)
348 if (is_ready
&& GetState () == AudioPlaying
)
353 PulseSource::OnStateChanged (pa_stream
*pulse_stream
)
355 pa_stream_state_t state
;
357 if (pulse_stream
!= this->pulse_stream
&& this->pulse_stream
!= NULL
) {
358 LOG_AUDIO ("PulseSource::OnStateChanged (%p): Invalid stream.\n", pulse_stream
);
362 state
= GetPAState (pulse_stream
);
364 SetCurrentDeployment (false);
366 LOG_PULSE ("PulseSource::OnStateChanged (): %s (%i)\n", get_pa_stream_state_name (state
), state
);
369 case PA_STREAM_READY
:
372 case PA_STREAM_CREATING
:
373 case PA_STREAM_TERMINATED
:
376 case PA_STREAM_FAILED
:
379 LOG_AUDIO ("PulseSource::OnStateChanged (): Stream error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
380 SetState (AudioError
);
386 PulseSource::OnWrite (pa_stream
*s
, size_t length
, void *userdata
)
388 ((PulseSource
*) userdata
)->OnWrite (length
);
392 PulseSource::OnWrite (size_t length
)
398 LOG_PULSE ("PulseSource::OnWrite (%lld)\n", (gint64
) length
);
400 if (pulse_stream
== NULL
) {
401 // We've been destroyed
408 buffer
= g_malloc (length
);
410 frames
= Write (buffer
, length
/ GetOutputBytesPerFrame ());
412 LOG_PULSE ("PulseSource::OnWrite (%lld): Wrote %" G_GUINT64_FORMAT
" frames\n", (gint64
) length
, (gint64
) frames
);
415 // There is no need to lock here, if in a callback, the caller will have locked
416 // if called from WriteAvailable, that method has locked
417 err
= pa_stream_write (pulse_stream
, buffer
, frames
* GetOutputBytesPerFrame (), (pa_free_cb_t
) g_free
, 0, PA_SEEK_RELATIVE
);
419 LOG_AUDIO ("PulseSource::OnWrite (): Write error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
420 } else if (play_pending
) {
429 PulseSource::WriteAvailable ()
433 LOG_PULSE ("PulseSource::WriteAvailable ()\n");
436 if (pulse_stream
!= NULL
&& is_ready
) {
437 available
= pa_stream_writable_size (pulse_stream
);
438 if (available
!= (size_t) -1) {
441 LOG_AUDIO ("PulseSource::WriteAvailable (): Write error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
444 player
->UnlockLoop ();
448 PulseSource::PACork (bool cork
)
450 LOG_PULSE ("PulseSource::PACork (%i)\n", cork
);
451 pa_operation_unref (pa_stream_cork (pulse_stream
, cork
, NULL
, this));
455 PulseSource::PATrigger ()
457 LOG_PULSE ("PulseSource::PATrigger (), triggered: %i\n", triggered
);
458 pa_operation_unref (pa_stream_trigger (pulse_stream
, NULL
, this));
463 PulseSource::PAFlush ()
465 pa_operation_unref (pa_stream_flush (pulse_stream
, NULL
, this));
469 PulseSource::Played ()
471 LOG_PULSE ("PulseSource::Played ()\n");
473 if (!InitializePA ()) {
474 LOG_PULSE ("PulseSource::Played (): initialization failed.\n");
481 if (pulse_stream
&& is_ready
) {
482 // Uncork the stream (if it was corked)
484 // And start playing it.
486 play_pending
= false;
490 player
->UnlockLoop ();
494 PulseSource::Paused ()
497 play_pending
= false;
498 if (pulse_stream
&& is_ready
)
500 player
->UnlockLoop ();
504 PulseSource::Stopped ()
506 LOG_PULSE ("PulseSource::Stopped ()\n");
509 play_pending
= false;
510 if (pulse_stream
&& is_ready
) {
511 // Pause the stream and wait for the pause to complete
513 // Drop all the samples we've sent
516 player
->UnlockLoop ();
522 PulseSource::GetDelayInternal ()
525 pa_usec_t latency
= 0;
530 if (pulse_stream
&& is_ready
) {
531 err
= pa_stream_get_latency (pulse_stream
, &latency
, &negative
);
533 LOG_AUDIO ("PulseSource::GetDelay (): Error: %s\n", pa_strerror (pa_context_errno (player
->GetPAContext ())));
534 result
= G_MAXUINT64
;
536 result
= MilliSeconds_ToPts (latency
/ 1000);
539 result
= G_MAXUINT64
;
541 player
->UnlockLoop ();
544 LOG_PULSE ("PulseSource::GetDelay (), result: %" G_GUINT64_FORMAT
" ms, latency: %" G_GUINT64_FORMAT
", err: %i, negative: %i, is_ready: %i, pulse_stream: %p\n",
545 MilliSeconds_FromPts (result
), latency
, err
, negative
, is_ready
, pulse_stream
);
554 PulsePlayer::PulsePlayer ()
558 connected
= ConnectionUnknown
;
559 pthread_mutex_init (&mutex
, NULL
);
560 pthread_cond_init (&cond
, NULL
);
563 PulsePlayer::~PulsePlayer ()
565 pthread_mutex_destroy (&mutex
);
566 pthread_cond_destroy (&cond
);
570 PulsePlayer::WaitLoop ()
572 pa_threaded_mainloop_wait (loop
);
576 PulsePlayer::WaitForOperation (pa_operation
*op
)
578 if (pa_threaded_mainloop_in_thread (loop
))
581 //while (pa_operation_get_state (op) != PA_OPERATION_DONE)
583 pa_operation_unref (op
);
587 PulsePlayer::SignalLoop ()
589 //pa_threaded_mainloop_signal (loop, 0);
593 PulsePlayer::LockLoop ()
595 if (!pa_threaded_mainloop_in_thread (loop
))
596 pa_threaded_mainloop_lock (loop
);
600 PulsePlayer::UnlockLoop ()
602 if (!pa_threaded_mainloop_in_thread (loop
))
603 pa_threaded_mainloop_unlock (loop
);
607 PulsePlayer::CreateNode (MediaPlayer
*mplayer
, AudioStream
*stream
)
609 return new PulseSource (this, mplayer
, stream
);
612 static int is_pulse_usable
= 0; // 0 = not tested, 1 = tested, usable, 2 = tested, not usable
613 static void *libpulse
= NULL
;
616 PulsePlayer::IsInstalled ()
621 switch (is_pulse_usable
) {
623 libpulse
= dlopen ("libpulse.so.0", RTLD_LAZY
);
624 if (libpulse
== NULL
) {
630 result
&= NULL
!= (d_pa_stream_new
= (dyn_pa_stream_new
*) dlsym (libpulse
, "pa_stream_new"));
631 result
&= NULL
!= (d_pa_stream_set_state_callback
= (dyn_pa_stream_set_state_callback
*) dlsym (libpulse
, "pa_stream_set_state_callback"));
632 result
&= NULL
!= (d_pa_stream_set_write_callback
= (dyn_pa_stream_set_write_callback
*) dlsym (libpulse
, "pa_stream_set_write_callback"));
633 result
&= NULL
!= (d_pa_stream_set_underflow_callback
= (dyn_pa_stream_set_underflow_callback
*) dlsym (libpulse
, "pa_stream_set_underflow_callback"));
634 result
&= NULL
!= (d_pa_stream_connect_playback
= (dyn_pa_stream_connect_playback
*) dlsym (libpulse
, "pa_stream_connect_playback"));
635 result
&= NULL
!= (d_pa_stream_disconnect
= (dyn_pa_stream_disconnect
*) dlsym (libpulse
, "pa_stream_disconnect"));
636 result
&= NULL
!= (d_pa_stream_unref
= (dyn_pa_stream_unref
*) dlsym (libpulse
, "pa_stream_unref"));
637 result
&= NULL
!= (d_pa_stream_get_state
= (dyn_pa_stream_get_state
*) dlsym (libpulse
, "pa_stream_get_state"));
638 result
&= NULL
!= (d_pa_stream_write
= (dyn_pa_stream_write
*) dlsym (libpulse
, "pa_stream_write"));
639 result
&= NULL
!= (d_pa_stream_writable_size
= (dyn_pa_stream_writable_size
*) dlsym (libpulse
, "pa_stream_writable_size"));
640 result
&= NULL
!= (d_pa_stream_cork
= (dyn_pa_stream_cork
*) dlsym (libpulse
, "pa_stream_cork"));
641 result
&= NULL
!= (d_pa_stream_trigger
= (dyn_pa_stream_trigger
*) dlsym (libpulse
, "pa_stream_trigger"));
642 result
&= NULL
!= (d_pa_stream_flush
= (dyn_pa_stream_flush
*) dlsym (libpulse
, "pa_stream_flush"));
643 result
&= NULL
!= (d_pa_stream_get_latency
= (dyn_pa_stream_get_latency
*) dlsym (libpulse
, "pa_stream_get_latency"));
645 result
&= NULL
!= (d_pa_context_new
= (dyn_pa_context_new
*) dlsym (libpulse
, "pa_context_new"));
646 result
&= NULL
!= (d_pa_context_errno
= (dyn_pa_context_errno
*) dlsym (libpulse
, "pa_context_errno"));
647 result
&= NULL
!= (d_pa_context_get_state
= (dyn_pa_context_get_state
*) dlsym (libpulse
, "pa_context_get_state"));
648 result
&= NULL
!= (d_pa_context_set_state_callback
= (dyn_pa_context_set_state_callback
*) dlsym (libpulse
, "pa_context_set_state_callback"));
649 result
&= NULL
!= (d_pa_context_connect
= (dyn_pa_context_connect
*) dlsym (libpulse
, "pa_context_connect"));
650 result
&= NULL
!= (d_pa_context_disconnect
= (dyn_pa_context_disconnect
*) dlsym (libpulse
, "pa_context_disconnect"));
651 result
&= NULL
!= (d_pa_context_unref
= (dyn_pa_context_unref
*) dlsym (libpulse
, "pa_context_unref"));
653 result
&= NULL
!= (d_pa_threaded_mainloop_new
= (dyn_pa_threaded_mainloop_new
*) dlsym (libpulse
, "pa_threaded_mainloop_new"));
654 result
&= NULL
!= (d_pa_threaded_mainloop_start
= (dyn_pa_threaded_mainloop_start
*) dlsym (libpulse
, "pa_threaded_mainloop_start"));
655 result
&= NULL
!= (d_pa_threaded_mainloop_get_api
= (dyn_pa_threaded_mainloop_get_api
*) dlsym (libpulse
, "pa_threaded_mainloop_get_api"));
656 result
&= NULL
!= (d_pa_threaded_mainloop_wait
= (dyn_pa_threaded_mainloop_wait
*) dlsym (libpulse
, "pa_threaded_mainloop_wait"));
657 result
&= NULL
!= (d_pa_threaded_mainloop_in_thread
= (dyn_pa_threaded_mainloop_in_thread
*) dlsym (libpulse
, "pa_threaded_mainloop_in_thread"));
658 result
&= NULL
!= (d_pa_threaded_mainloop_lock
= (dyn_pa_threaded_mainloop_lock
*) dlsym (libpulse
, "pa_threaded_mainloop_lock"));
659 result
&= NULL
!= (d_pa_threaded_mainloop_unlock
= (dyn_pa_threaded_mainloop_unlock
*) dlsym (libpulse
, "pa_threaded_mainloop_unlock"));
660 result
&= NULL
!= (d_pa_threaded_mainloop_signal
= (dyn_pa_threaded_mainloop_signal
*) dlsym (libpulse
, "pa_threaded_mainloop_signal"));
661 result
&= NULL
!= (d_pa_threaded_mainloop_stop
= (dyn_pa_threaded_mainloop_stop
*) dlsym (libpulse
, "pa_threaded_mainloop_stop"));
662 result
&= NULL
!= (d_pa_threaded_mainloop_free
= (dyn_pa_threaded_mainloop_free
*) dlsym (libpulse
, "pa_threaded_mainloop_free"));
664 result
&= NULL
!= (d_pa_channel_map_init_mono
= (dyn_pa_channel_map_init_mono
*) dlsym (libpulse
, "pa_channel_map_init_mono"));
665 result
&= NULL
!= (d_pa_channel_map_init_stereo
= (dyn_pa_channel_map_init_stereo
*) dlsym (libpulse
, "pa_channel_map_init_stereo"));
666 result
&= NULL
!= (d_pa_channel_map_init_auto
= (dyn_pa_channel_map_init_auto
*) dlsym (libpulse
, "pa_channel_map_init_auto"));
668 result
&= NULL
!= (d_pa_strerror
= (dyn_pa_strerror
*) dlsym (libpulse
, "pa_strerror"));
670 result
&= NULL
!= (d_pa_operation_get_state
= (dyn_pa_operation_get_state
*) dlsym (libpulse
, "pa_operation_get_state"));
671 result
&= NULL
!= (d_pa_operation_unref
= (dyn_pa_operation_unref
*) dlsym (libpulse
, "pa_operation_unref"));
673 result
&= NULL
!= (d_pa_get_library_version
= (dyn_pa_get_library_version
*) dlsym (libpulse
, "pa_get_library_version"));
675 if (d_pa_get_library_version
!= NULL
) {
676 version
= d_pa_get_library_version ();
677 LOG_AUDIO ("PulsePlayer: Found libpulse version: '%s'\n", version
);
681 LOG_AUDIO ("PulsePlayer: Failed to load one or more required functions in libpulse.so.\n");
683 is_pulse_usable
= result
? 1 : 2;
695 PulsePlayer::OnContextStateChanged (pa_context
*context
, void *userdata
)
697 ((PulsePlayer
*) userdata
)->OnContextStateChanged ();
701 PulsePlayer::GetPAState ()
703 pa_context_state_t result
;
706 result
= pa_context_get_state (context
);
713 get_pa_context_state_name (pa_context_state_t state
)
716 case PA_CONTEXT_CONNECTING
: return "PA_CONTEXT_CONNECTING";
717 case PA_CONTEXT_AUTHORIZING
: return "PA_CONTEXT_AUTHORIZING";
718 case PA_CONTEXT_SETTING_NAME
: return "PA_CONTEXT_SETTING_NAME";
719 case PA_CONTEXT_READY
: return "PA_CONTEXT_READY";
720 case PA_CONTEXT_TERMINATED
: return "PA_CONTEXT_TERMINATED";
721 case PA_CONTEXT_FAILED
: return "PA_CONTEXT_FAILED";
722 default: return "<UNKNOWN>";
727 PulsePlayer::OnContextStateChanged () {
729 pa_context_state_t state
;
731 state
= GetPAState ();
733 LOG_PULSE ("PulsePlayer::OnContextStateChanged (): %s (%i)\n", get_pa_context_state_name (state
), state
);
736 case PA_CONTEXT_CONNECTING
:
737 case PA_CONTEXT_AUTHORIZING
:
738 case PA_CONTEXT_SETTING_NAME
:
740 case PA_CONTEXT_READY
:
742 sources
.StartEnumeration ();
743 while ((source
= (PulseSource
*) sources
.GetNext (false)) != NULL
) {
744 source
->Initialize ();
748 pthread_mutex_lock (&mutex
);
749 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Signalling main thread that we've connected\n");
750 connected
= ConnectionSuccess
;
751 pthread_cond_signal (&cond
);
752 pthread_mutex_unlock (&mutex
);
754 case PA_CONTEXT_TERMINATED
:
756 case PA_CONTEXT_FAILED
:
758 pthread_mutex_lock (&mutex
);
759 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Signalling main thread that we've failed to connect\n");
760 connected
= ConnectionFailed
;
761 pthread_cond_signal (&cond
);
762 pthread_mutex_unlock (&mutex
);
763 fprintf (stderr
, "Moonlight: Connection failure while trying to connect to pulseaudio daemon: %s\n", pa_strerror (pa_context_errno (context
)));
769 PulsePlayer::AddInternal (AudioSource
*source
)
771 LOG_PULSE ("PulsePlayer::AddInternal (%p)\n", source
);
773 ((PulseSource
*) source
)->Initialize ();
777 PulsePlayer::RemoveInternal (AudioSource
*source
)
779 LOG_PULSE ("PulsePlayer::RemoveInternal (%p)\n", source
);
781 ((PulseSource
*) source
)->Close ();
786 PulsePlayer::Initialize ()
790 LOG_PULSE ("PulsePlayer::InitializeInternal ()\n");
792 loop
= pa_threaded_mainloop_new ();
794 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to create main loop.\n");
798 api
= pa_threaded_mainloop_get_api (loop
);
801 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to get api.\n");
805 context
= pa_context_new (api
, "Moonlight");
806 if (context
== NULL
) {
807 LOG_AUDIO ("PulsePlayer::InitializeInternal (); Failed to create context.\n");
811 pa_context_set_state_callback (context
, OnContextStateChanged
, this);
813 err
= pa_context_connect (context
, NULL
, (pa_context_flags_t
) 0, NULL
);
815 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Error %i while connecting to server.\n", err
);
818 if (connected
== ConnectionUnknown
) {
819 LOG_AUDIO ("PulsePlayer::InitializeInternal (): pa_context_connect returned but we're not connected.\n");
821 // It's possible that pulse can return an error to us async
822 // We need to aquire a lock, then start the mainloop
823 pthread_mutex_lock (&mutex
);
825 // Of course it wont raise the async error unless we try
826 // to start the mainloop
827 pa_threaded_mainloop_start (loop
);
830 // Wait until pulse has reported the connection status to
832 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Waiting to see if we can connect.\n");
833 pthread_cond_wait (&cond
, &mutex
);
834 } while (connected
== ConnectionUnknown
);
836 pthread_mutex_unlock (&mutex
);
838 // At this stag we have had connected set regardless of wether
839 // PA wants to be sync or async
840 if (connected
== ConnectionFailed
) {
841 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Asynchronous error while connecting to the pulse daemon\n");
845 LOG_AUDIO ("PulsePlayer::InitializeInternal (): pa_context_connect returned and connected.\n");
846 // We've already connected successfully in a sync fashion
847 // there is no need to lock, we can just start the loop
848 pa_threaded_mainloop_start (loop
);
855 PulsePlayer::PrepareShutdownInternal ()
860 PulsePlayer::FinishShutdownInternal ()
862 LOG_PULSE ("PulsePlayer::ShutdownInternal ()\n");
864 api
= NULL
; // CHECK: Do we need to unref this one?
867 pa_context_disconnect (context
);
868 pa_context_unref (context
);
873 pa_threaded_mainloop_stop (loop
);
874 pa_threaded_mainloop_free (loop
);