2009-09-12 Chris Toshok <toshok@ximian.com>
[moon.git] / src / audio-pulse.cpp
bloba66b973490fef828b3db961993e2f5762187ee38
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * audio-pulse.cpp:
5 * Contact:
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.
13 #include <config.h>
15 #include <dlfcn.h>
17 #include "audio-pulse.h"
18 #include "runtime.h"
19 #include "clock.h"
20 #include "debug.h"
22 // stream.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);
37 // context.h
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);
45 // thread-mainloop.h
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);
56 // channelmap.h
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);
60 // error.h
61 typedef const char* (dyn_pa_strerror) (int error);
62 // operation.h
63 typedef pa_operation_state_t (dyn_pa_operation_get_state) (pa_operation *o);
64 typedef void (dyn_pa_operation_unref) (pa_operation *o);
65 // version.h
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
146 * PulseSource
149 PulseSource::PulseSource (PulsePlayer *player, MediaPlayer *mplayer, AudioStream *stream) : AudioSource (player, mplayer, stream)
151 LOG_PULSE ("PulseSource::PulseSource ()\n");
153 this->player = player;
154 pulse_stream = NULL;
155 initialized = false;
156 triggered = false;
157 is_ready = false;
158 play_pending = false;
159 closed = false;
162 PulseSource::~PulseSource ()
164 LOG_PULSE ("PulseSource::~PulseSource ()\n");
165 Close ();
168 bool
169 PulseSource::InitializeInternal ()
171 LOG_PULSE ("PulseSource::InitializeInternal (), initialized: %i\n", initialized);
173 if (initialized)
174 return true;
176 if (player->GetPAState () != PA_CONTEXT_READY)
177 return true;
179 initialized = true;
181 if (!InitializePA ()) {
182 SetState (AudioError);
183 return false;
186 return true;
189 bool
190 PulseSource::InitializePA ()
192 int err;
193 pa_sample_spec format;
194 pa_channel_map channel_map;
195 bool result = false;
197 LOG_AUDIO ("PulseSource::InitializePA ()\n");
199 player->LockLoop ();
201 switch (GetInputBytesPerSample ()) {
202 case 2:
203 format.format = PA_SAMPLE_S16NE;
204 SetOutputBytesPerSample (2);
205 break;
206 case 3:
207 format.format = PA_SAMPLE_S32NE;
208 SetOutputBytesPerSample (4);
209 break;
210 default:
211 LOG_AUDIO ("PulseSource::InitializePA (): Invalid bytes per sample: %i (expected 1, 2 or 3)\n", GetInputBytesPerSample ());
212 goto cleanup;
213 break;
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;
239 } else {
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);
242 goto cleanup;
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 ())));
249 goto cleanup;
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);
257 if (err < 0) {
258 LOG_AUDIO ("PulseSource::InitializePA (): failed to connect stream: %s.\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
259 goto cleanup;
262 result = true;
264 cleanup:
265 player->UnlockLoop ();
267 return result;
270 void
271 PulseSource::CloseInternal ()
273 LOG_PULSE ("PulseSource::CloseInternal ()\n");
275 is_ready = false;
277 if (closed)
278 return;
279 closed = true;
281 player->LockLoop ();
282 if (pulse_stream) {
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);
288 pulse_stream = NULL;
290 player->UnlockLoop ();
293 void
294 PulseSource::OnStateChanged (pa_stream *pulse_stream, void *userdata)
296 ((PulseSource *) userdata)->OnStateChanged (pulse_stream);
299 static const char *
300 get_pa_stream_state_name (pa_stream_state_t state)
302 switch (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>";
311 pa_stream_state_t
312 PulseSource::GetPAState (pa_stream *pulse_stream)
314 pa_stream_state_t result;
316 player->LockLoop ();
318 if (pulse_stream == NULL)
319 pulse_stream = this->pulse_stream;
320 if (pulse_stream != NULL) {
321 result = pa_stream_get_state (pulse_stream);
322 } else {
323 result = PA_STREAM_FAILED;
325 player->UnlockLoop ();
327 return result;
330 void
331 PulseSource::OnUnderflow (pa_stream *pulse_stream, void *userdata)
333 ((PulseSource *) userdata)->OnUnderflow ();
336 void
337 PulseSource::OnUnderflow ()
339 AudioSource::Underflowed ();
342 void
343 PulseSource::StateChanged (AudioState old_state)
345 if (is_ready && GetState () == AudioPlaying)
346 WriteAvailable ();
349 void
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);
356 return;
359 state = GetPAState (pulse_stream);
361 SetCurrentDeployment (false);
363 LOG_PULSE ("PulseSource::OnStateChanged (): %s (%i)\n", get_pa_stream_state_name (state), state);
365 switch (state) {
366 case PA_STREAM_READY:
367 is_ready = true;
368 break;
369 case PA_STREAM_CREATING:
370 case PA_STREAM_TERMINATED:
371 is_ready = false;
372 break;
373 case PA_STREAM_FAILED:
374 default:
375 is_ready = false;
376 LOG_AUDIO ("PulseSource::OnStateChanged (): Stream error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
377 SetState (AudioError);
378 break;
382 void
383 PulseSource::OnWrite (pa_stream *s, size_t length, void *userdata)
385 ((PulseSource *) userdata)->OnWrite (length);
388 void
389 PulseSource::OnWrite (size_t length)
391 void *buffer;
392 int err;
393 size_t frames;
395 LOG_PULSE ("PulseSource::OnWrite (%lld)\n", (gint64) length);
397 if (pulse_stream == NULL) {
398 // We've been destroyed
399 return;
402 if (length == 0)
403 return;
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);
411 if (frames > 0) {
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);
415 if (err < 0) {
416 LOG_AUDIO ("PulseSource::OnWrite (): Write error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
417 } else if (play_pending) {
418 Played ();
420 } else {
421 g_free (buffer);
425 void
426 PulseSource::WriteAvailable ()
428 size_t available;
430 LOG_PULSE ("PulseSource::WriteAvailable ()\n");
432 player->LockLoop ();
433 if (pulse_stream != NULL && is_ready) {
434 available = pa_stream_writable_size (pulse_stream);
435 if (available != (size_t) -1) {
436 OnWrite (available);
437 } else {
438 LOG_AUDIO ("PulseSource::WriteAvailable (): Write error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
441 player->UnlockLoop ();
444 void
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));
451 void
452 PulseSource::PATrigger ()
454 LOG_PULSE ("PulseSource::PATrigger (), triggered: %i\n", triggered);
455 pa_operation_unref (pa_stream_trigger (pulse_stream, NULL, this));
456 triggered = true;
459 void
460 PulseSource::PAFlush ()
462 pa_operation_unref (pa_stream_flush (pulse_stream, NULL, this));
465 void
466 PulseSource::Played ()
468 LOG_PULSE ("PulseSource::Played ()\n");
470 player->LockLoop ();
471 triggered = false;
472 WriteAvailable ();
473 if (pulse_stream && is_ready) {
474 // Uncork the stream (if it was corked)
475 PACork (false);
476 // And start playing it.
477 PATrigger ();
478 play_pending = false;
479 } else {
480 play_pending = true;
482 player->UnlockLoop ();
485 void
486 PulseSource::Paused ()
488 player->LockLoop ();
489 play_pending = false;
490 if (pulse_stream && is_ready)
491 PACork (true);
492 player->UnlockLoop ();
495 void
496 PulseSource::Stopped ()
498 LOG_PULSE ("PulseSource::Stopped ()\n");
500 player->LockLoop ();
501 play_pending = false;
502 if (pulse_stream && is_ready) {
503 // Pause the stream and wait for the pause to complete
504 PACork (true);
505 // Drop all the samples we've sent
506 PAFlush ();
508 player->UnlockLoop ();
511 guint64
512 PulseSource::GetDelayInternal ()
514 int err = 0;
515 pa_usec_t latency = 0;
516 int negative = 0;
517 guint64 result = 0;
519 player->LockLoop ();
520 if (pulse_stream && is_ready) {
521 err = pa_stream_get_latency (pulse_stream, &latency, &negative);
522 if (err < 0) {
523 LOG_AUDIO ("PulseSource::GetDelay (): Error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
524 result = G_MAXUINT64;
525 } else {
526 result = MilliSeconds_ToPts (latency / 1000);
528 } else {
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);
537 return result;
541 * PulsePlayer
544 PulsePlayer::PulsePlayer ()
546 loop = NULL;
547 context = NULL;
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);
559 void
560 PulsePlayer::WaitLoop ()
562 pa_threaded_mainloop_wait (loop);
565 void
566 PulsePlayer::WaitForOperation (pa_operation *op)
568 if (pa_threaded_mainloop_in_thread (loop))
569 return;
571 //while (pa_operation_get_state (op) != PA_OPERATION_DONE)
572 // WaitLoop ();
573 pa_operation_unref (op);
576 void
577 PulsePlayer::SignalLoop ()
579 //pa_threaded_mainloop_signal (loop, 0);
582 void
583 PulsePlayer::LockLoop ()
585 if (!pa_threaded_mainloop_in_thread (loop))
586 pa_threaded_mainloop_lock (loop);
589 void
590 PulsePlayer::UnlockLoop ()
592 if (!pa_threaded_mainloop_in_thread (loop))
593 pa_threaded_mainloop_unlock (loop);
596 AudioSource *
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;
605 bool
606 PulsePlayer::IsInstalled ()
608 bool result = false;
609 const char *version;
611 switch (is_pulse_usable) {
612 case 0:
613 libpulse = dlopen ("libpulse.so.0", RTLD_LAZY);
614 if (libpulse == NULL) {
615 is_pulse_usable = 2;
616 return false;
618 result = true;
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);
670 if (!result)
671 LOG_AUDIO ("PulsePlayer: Failed to load one or more required functions in libpulse.so.\n");
673 is_pulse_usable = result ? 1 : 2;
674 return result;
675 case 1:
676 return true;
677 default:
678 return false;
681 return true;
684 void
685 PulsePlayer::OnContextStateChanged (pa_context *context, void *userdata)
687 ((PulsePlayer *) userdata)->OnContextStateChanged ();
690 pa_context_state_t
691 PulsePlayer::GetPAState ()
693 pa_context_state_t result;
695 LockLoop ();
696 result = pa_context_get_state (context);
697 UnlockLoop ();
699 return result;
702 static const char *
703 get_pa_context_state_name (pa_context_state_t state)
705 switch (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>";
716 void
717 PulsePlayer::OnContextStateChanged () {
718 PulseSource *source;
719 pa_context_state_t state;
721 state = GetPAState ();
723 LOG_PULSE ("PulsePlayer::OnContextStateChanged (): %s (%i)\n", get_pa_context_state_name (state), state);
725 switch (state) {
726 case PA_CONTEXT_CONNECTING:
727 case PA_CONTEXT_AUTHORIZING:
728 case PA_CONTEXT_SETTING_NAME:
729 break;
730 case PA_CONTEXT_READY:
731 LockLoop ();
732 sources.StartEnumeration ();
733 while ((source = (PulseSource *) sources.GetNext (false)) != NULL) {
734 source->Initialize ();
735 source->unref ();
737 UnlockLoop ();
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);
743 break;
744 case PA_CONTEXT_TERMINATED:
745 break;
746 case PA_CONTEXT_FAILED:
747 default:
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)));
754 break;
758 void
759 PulsePlayer::AddInternal (AudioSource *source)
761 LOG_PULSE ("PulsePlayer::AddInternal (%p)\n", source);
763 ((PulseSource *) source)->Initialize ();
766 void
767 PulsePlayer::RemoveInternal (AudioSource *source)
769 LOG_PULSE ("PulsePlayer::RemoveInternal (%p)\n", source);
771 ((PulseSource *) source)->Close ();
775 bool
776 PulsePlayer::Initialize ()
778 int err;
780 LOG_PULSE ("PulsePlayer::InitializeInternal ()\n");
782 loop = pa_threaded_mainloop_new ();
783 if (loop == NULL) {
784 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to create main loop.\n");
785 return false;
788 api = pa_threaded_mainloop_get_api (loop);
790 if (api == NULL) {
791 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to get api.\n");
792 return false;
795 context = pa_context_new (api, "Moonlight");
796 if (context == NULL) {
797 LOG_AUDIO ("PulsePlayer::InitializeInternal (); Failed to create context.\n");
798 return false;
801 pa_context_set_state_callback (context, OnContextStateChanged, this);
803 err = pa_context_connect (context, NULL, (pa_context_flags_t) 0, NULL);
804 if (err < 0) {
805 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Error %i while connecting to server.\n", err);
806 return false;
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);
819 do {
820 // Wait until pulse has reported the connection status to
821 // us async.
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");
832 return false;
834 } else {
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);
841 return true;
844 void
845 PulsePlayer::PrepareShutdownInternal ()
849 void
850 PulsePlayer::FinishShutdownInternal ()
852 LOG_PULSE ("PulsePlayer::ShutdownInternal ()\n");
854 api = NULL; // CHECK: Do we need to unref this one?
856 if (context) {
857 pa_context_disconnect (context);
858 pa_context_unref (context);
859 context = NULL;
862 if (loop) {
863 pa_threaded_mainloop_stop (loop);
864 pa_threaded_mainloop_free (loop);
865 loop = NULL;