2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / audio-pulse.cpp
blob58967e19916d6d1d6ce308b516e0e3663588eca2
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;
161 PulseSource::~PulseSource ()
163 LOG_PULSE ("PulseSource::~PulseSource ()\n");
164 Close ();
167 bool
168 PulseSource::InitializeInternal ()
170 LOG_PULSE ("PulseSource::InitializeInternal (), initialized: %i\n", initialized);
171 // this is a no-op, initialization is done when needed.
172 return true;
175 bool
176 PulseSource::InitializePA ()
178 int err;
179 pa_sample_spec format;
180 pa_channel_map channel_map;
181 bool result = false;
183 LOG_AUDIO ("PulseSource::InitializePA ()\n");
185 if (initialized)
186 return true;
188 if (player->GetPAState () != PA_CONTEXT_READY) {
189 LOG_PULSE ("PulseSource::InitializePA (), PA isn't in the ready state.\n");
190 return false;
193 player->LockLoop ();
195 switch (GetInputBytesPerSample ()) {
196 case 2:
197 format.format = PA_SAMPLE_S16NE;
198 SetOutputBytesPerSample (2);
199 break;
200 case 3:
201 format.format = PA_SAMPLE_S32NE;
202 SetOutputBytesPerSample (4);
203 break;
204 default:
205 LOG_AUDIO ("PulseSource::InitializePA (): Invalid bytes per sample: %i (expected 1, 2 or 3)\n", GetInputBytesPerSample ());
206 goto cleanup;
207 break;
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;
233 } else {
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);
236 goto cleanup;
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 ())));
243 goto cleanup;
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);
251 if (err < 0) {
252 LOG_AUDIO ("PulseSource::InitializePA (): failed to connect stream: %s.\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
253 goto cleanup;
256 result = true;
257 initialized = true;
259 cleanup:
260 player->UnlockLoop ();
262 return result;
265 void
266 PulseSource::CloseInternal ()
268 LOG_PULSE ("PulseSource::CloseInternal ()\n");
270 ClosePA ();
273 void
274 PulseSource::ClosePA ()
276 LOG_PULSE ("PulseSource::ClosePA () initialized: %i\n", initialized);
278 if (!initialized)
279 return;
281 is_ready = false;
283 player->LockLoop ();
284 if (pulse_stream) {
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);
290 pulse_stream = NULL;
292 player->UnlockLoop ();
293 initialized = false;
296 void
297 PulseSource::OnStateChanged (pa_stream *pulse_stream, void *userdata)
299 ((PulseSource *) userdata)->OnStateChanged (pulse_stream);
302 static const char *
303 get_pa_stream_state_name (pa_stream_state_t state)
305 switch (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>";
314 pa_stream_state_t
315 PulseSource::GetPAState (pa_stream *pulse_stream)
317 pa_stream_state_t result;
319 player->LockLoop ();
321 if (pulse_stream == NULL)
322 pulse_stream = this->pulse_stream;
323 if (pulse_stream != NULL) {
324 result = pa_stream_get_state (pulse_stream);
325 } else {
326 result = PA_STREAM_FAILED;
328 player->UnlockLoop ();
330 return result;
333 void
334 PulseSource::OnUnderflow (pa_stream *pulse_stream, void *userdata)
336 ((PulseSource *) userdata)->OnUnderflow ();
339 void
340 PulseSource::OnUnderflow ()
342 AudioSource::Underflowed ();
345 void
346 PulseSource::StateChanged (AudioState old_state)
348 if (is_ready && GetState () == AudioPlaying)
349 WriteAvailable ();
352 void
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);
359 return;
362 state = GetPAState (pulse_stream);
364 SetCurrentDeployment (false);
366 LOG_PULSE ("PulseSource::OnStateChanged (): %s (%i)\n", get_pa_stream_state_name (state), state);
368 switch (state) {
369 case PA_STREAM_READY:
370 is_ready = true;
371 break;
372 case PA_STREAM_CREATING:
373 case PA_STREAM_TERMINATED:
374 is_ready = false;
375 break;
376 case PA_STREAM_FAILED:
377 default:
378 is_ready = false;
379 LOG_AUDIO ("PulseSource::OnStateChanged (): Stream error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
380 SetState (AudioError);
381 break;
385 void
386 PulseSource::OnWrite (pa_stream *s, size_t length, void *userdata)
388 ((PulseSource *) userdata)->OnWrite (length);
391 void
392 PulseSource::OnWrite (size_t length)
394 void *buffer;
395 int err;
396 size_t frames;
398 LOG_PULSE ("PulseSource::OnWrite (%lld)\n", (gint64) length);
400 if (pulse_stream == NULL) {
401 // We've been destroyed
402 return;
405 if (length == 0)
406 return;
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);
414 if (frames > 0) {
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);
418 if (err < 0) {
419 LOG_AUDIO ("PulseSource::OnWrite (): Write error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
420 } else if (play_pending) {
421 Played ();
423 } else {
424 g_free (buffer);
428 void
429 PulseSource::WriteAvailable ()
431 size_t available;
433 LOG_PULSE ("PulseSource::WriteAvailable ()\n");
435 player->LockLoop ();
436 if (pulse_stream != NULL && is_ready) {
437 available = pa_stream_writable_size (pulse_stream);
438 if (available != (size_t) -1) {
439 OnWrite (available);
440 } else {
441 LOG_AUDIO ("PulseSource::WriteAvailable (): Write error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
444 player->UnlockLoop ();
447 void
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));
454 void
455 PulseSource::PATrigger ()
457 LOG_PULSE ("PulseSource::PATrigger (), triggered: %i\n", triggered);
458 pa_operation_unref (pa_stream_trigger (pulse_stream, NULL, this));
459 triggered = true;
462 void
463 PulseSource::PAFlush ()
465 pa_operation_unref (pa_stream_flush (pulse_stream, NULL, this));
468 void
469 PulseSource::Played ()
471 LOG_PULSE ("PulseSource::Played ()\n");
473 if (!InitializePA ()) {
474 LOG_PULSE ("PulseSource::Played (): initialization failed.\n");
475 return;
478 player->LockLoop ();
479 triggered = false;
480 WriteAvailable ();
481 if (pulse_stream && is_ready) {
482 // Uncork the stream (if it was corked)
483 PACork (false);
484 // And start playing it.
485 PATrigger ();
486 play_pending = false;
487 } else {
488 play_pending = true;
490 player->UnlockLoop ();
493 void
494 PulseSource::Paused ()
496 player->LockLoop ();
497 play_pending = false;
498 if (pulse_stream && is_ready)
499 PACork (true);
500 player->UnlockLoop ();
503 void
504 PulseSource::Stopped ()
506 LOG_PULSE ("PulseSource::Stopped ()\n");
508 player->LockLoop ();
509 play_pending = false;
510 if (pulse_stream && is_ready) {
511 // Pause the stream and wait for the pause to complete
512 PACork (true);
513 // Drop all the samples we've sent
514 PAFlush ();
516 player->UnlockLoop ();
518 Close ();
521 guint64
522 PulseSource::GetDelayInternal ()
524 int err = 0;
525 pa_usec_t latency = 0;
526 int negative = 0;
527 guint64 result = 0;
529 player->LockLoop ();
530 if (pulse_stream && is_ready) {
531 err = pa_stream_get_latency (pulse_stream, &latency, &negative);
532 if (err < 0) {
533 LOG_AUDIO ("PulseSource::GetDelay (): Error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
534 result = G_MAXUINT64;
535 } else {
536 result = MilliSeconds_ToPts (latency / 1000);
538 } else {
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);
547 return result;
551 * PulsePlayer
554 PulsePlayer::PulsePlayer ()
556 loop = NULL;
557 context = NULL;
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);
569 void
570 PulsePlayer::WaitLoop ()
572 pa_threaded_mainloop_wait (loop);
575 void
576 PulsePlayer::WaitForOperation (pa_operation *op)
578 if (pa_threaded_mainloop_in_thread (loop))
579 return;
581 //while (pa_operation_get_state (op) != PA_OPERATION_DONE)
582 // WaitLoop ();
583 pa_operation_unref (op);
586 void
587 PulsePlayer::SignalLoop ()
589 //pa_threaded_mainloop_signal (loop, 0);
592 void
593 PulsePlayer::LockLoop ()
595 if (!pa_threaded_mainloop_in_thread (loop))
596 pa_threaded_mainloop_lock (loop);
599 void
600 PulsePlayer::UnlockLoop ()
602 if (!pa_threaded_mainloop_in_thread (loop))
603 pa_threaded_mainloop_unlock (loop);
606 AudioSource *
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;
615 bool
616 PulsePlayer::IsInstalled ()
618 bool result = false;
619 const char *version;
621 switch (is_pulse_usable) {
622 case 0:
623 libpulse = dlopen ("libpulse.so.0", RTLD_LAZY);
624 if (libpulse == NULL) {
625 is_pulse_usable = 2;
626 return false;
628 result = true;
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);
680 if (!result)
681 LOG_AUDIO ("PulsePlayer: Failed to load one or more required functions in libpulse.so.\n");
683 is_pulse_usable = result ? 1 : 2;
684 return result;
685 case 1:
686 return true;
687 default:
688 return false;
691 return true;
694 void
695 PulsePlayer::OnContextStateChanged (pa_context *context, void *userdata)
697 ((PulsePlayer *) userdata)->OnContextStateChanged ();
700 pa_context_state_t
701 PulsePlayer::GetPAState ()
703 pa_context_state_t result;
705 LockLoop ();
706 result = pa_context_get_state (context);
707 UnlockLoop ();
709 return result;
712 static const char *
713 get_pa_context_state_name (pa_context_state_t state)
715 switch (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>";
726 void
727 PulsePlayer::OnContextStateChanged () {
728 PulseSource *source;
729 pa_context_state_t state;
731 state = GetPAState ();
733 LOG_PULSE ("PulsePlayer::OnContextStateChanged (): %s (%i)\n", get_pa_context_state_name (state), state);
735 switch (state) {
736 case PA_CONTEXT_CONNECTING:
737 case PA_CONTEXT_AUTHORIZING:
738 case PA_CONTEXT_SETTING_NAME:
739 break;
740 case PA_CONTEXT_READY:
741 LockLoop ();
742 sources.StartEnumeration ();
743 while ((source = (PulseSource *) sources.GetNext (false)) != NULL) {
744 source->Initialize ();
745 source->unref ();
747 UnlockLoop ();
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);
753 break;
754 case PA_CONTEXT_TERMINATED:
755 break;
756 case PA_CONTEXT_FAILED:
757 default:
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)));
764 break;
768 void
769 PulsePlayer::AddInternal (AudioSource *source)
771 LOG_PULSE ("PulsePlayer::AddInternal (%p)\n", source);
773 ((PulseSource *) source)->Initialize ();
776 void
777 PulsePlayer::RemoveInternal (AudioSource *source)
779 LOG_PULSE ("PulsePlayer::RemoveInternal (%p)\n", source);
781 ((PulseSource *) source)->Close ();
785 bool
786 PulsePlayer::Initialize ()
788 int err;
790 LOG_PULSE ("PulsePlayer::InitializeInternal ()\n");
792 loop = pa_threaded_mainloop_new ();
793 if (loop == NULL) {
794 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to create main loop.\n");
795 return false;
798 api = pa_threaded_mainloop_get_api (loop);
800 if (api == NULL) {
801 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to get api.\n");
802 return false;
805 context = pa_context_new (api, "Moonlight");
806 if (context == NULL) {
807 LOG_AUDIO ("PulsePlayer::InitializeInternal (); Failed to create context.\n");
808 return false;
811 pa_context_set_state_callback (context, OnContextStateChanged, this);
813 err = pa_context_connect (context, NULL, (pa_context_flags_t) 0, NULL);
814 if (err < 0) {
815 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Error %i while connecting to server.\n", err);
816 return false;
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);
829 do {
830 // Wait until pulse has reported the connection status to
831 // us async.
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");
842 return false;
844 } else {
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);
851 return true;
854 void
855 PulsePlayer::PrepareShutdownInternal ()
859 void
860 PulsePlayer::FinishShutdownInternal ()
862 LOG_PULSE ("PulsePlayer::ShutdownInternal ()\n");
864 api = NULL; // CHECK: Do we need to unref this one?
866 if (context) {
867 pa_context_disconnect (context);
868 pa_context_unref (context);
869 context = NULL;
872 if (loop) {
873 pa_threaded_mainloop_stop (loop);
874 pa_threaded_mainloop_free (loop);
875 loop = NULL;