2009-12-07 Rolf Bjarne Kvinge <RKvinge@novell.com>
[moon.git] / src / audio-pulse.cpp
blobf405b86c55fffa5beb41531cb0e2a0f481f9d751
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 (Type::PULSESOURCE, 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 #ifdef LOGGING
303 static const char *
304 get_pa_stream_state_name (pa_stream_state_t state)
306 switch (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>";
314 #endif
316 pa_stream_state_t
317 PulseSource::GetPAState (pa_stream *pulse_stream)
319 pa_stream_state_t result;
321 player->LockLoop ();
323 if (pulse_stream == NULL)
324 pulse_stream = this->pulse_stream;
325 if (pulse_stream != NULL) {
326 result = pa_stream_get_state (pulse_stream);
327 } else {
328 result = PA_STREAM_FAILED;
330 player->UnlockLoop ();
332 return result;
335 void
336 PulseSource::OnUnderflow (pa_stream *pulse_stream, void *userdata)
338 ((PulseSource *) userdata)->OnUnderflow ();
341 void
342 PulseSource::OnUnderflow ()
344 AudioSource::Underflowed ();
347 void
348 PulseSource::StateChanged (AudioState old_state)
350 if (is_ready && GetState () == AudioPlaying)
351 WriteAvailable ();
354 void
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);
361 return;
364 state = GetPAState (pulse_stream);
366 SetCurrentDeployment (false);
368 LOG_PULSE ("PulseSource::OnStateChanged (): %s (%i)\n", get_pa_stream_state_name (state), state);
370 switch (state) {
371 case PA_STREAM_READY:
372 is_ready = true;
373 break;
374 case PA_STREAM_CREATING:
375 case PA_STREAM_TERMINATED:
376 is_ready = false;
377 break;
378 case PA_STREAM_FAILED:
379 default:
380 is_ready = false;
381 LOG_AUDIO ("PulseSource::OnStateChanged (): Stream error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
382 SetState (AudioError);
383 break;
387 void
388 PulseSource::OnWrite (pa_stream *s, size_t length, void *userdata)
390 ((PulseSource *) userdata)->OnWrite (length);
393 void
394 PulseSource::OnWrite (size_t length)
396 void *buffer;
397 int err;
398 size_t frames;
400 LOG_PULSE ("PulseSource::OnWrite (%" G_GINT64_FORMAT ")\n", (gint64) length);
402 if (pulse_stream == NULL) {
403 // We've been destroyed
404 return;
407 if (length == 0)
408 return;
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);
416 if (frames > 0) {
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);
420 if (err < 0) {
421 LOG_AUDIO ("PulseSource::OnWrite (): Write error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
422 } else if (play_pending) {
423 Played ();
425 } else {
426 g_free (buffer);
430 void
431 PulseSource::WriteAvailable ()
433 size_t available;
435 LOG_PULSE ("PulseSource::WriteAvailable ()\n");
437 player->LockLoop ();
438 if (pulse_stream != NULL && is_ready) {
439 available = pa_stream_writable_size (pulse_stream);
440 if (available != (size_t) -1) {
441 OnWrite (available);
442 } else {
443 LOG_AUDIO ("PulseSource::WriteAvailable (): Write error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
446 player->UnlockLoop ();
449 void
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));
456 void
457 PulseSource::PATrigger ()
459 LOG_PULSE ("PulseSource::PATrigger (), triggered: %i\n", triggered);
460 pa_operation_unref (pa_stream_trigger (pulse_stream, NULL, this));
461 triggered = true;
464 void
465 PulseSource::PAFlush ()
467 pa_operation_unref (pa_stream_flush (pulse_stream, NULL, this));
470 void
471 PulseSource::Played ()
473 LOG_PULSE ("PulseSource::Played ()\n");
475 if (!InitializePA ()) {
476 LOG_PULSE ("PulseSource::Played (): initialization failed.\n");
477 return;
480 player->LockLoop ();
481 triggered = false;
482 WriteAvailable ();
483 if (pulse_stream && is_ready) {
484 // Uncork the stream (if it was corked)
485 PACork (false);
486 // And start playing it.
487 PATrigger ();
488 play_pending = false;
489 } else {
490 play_pending = true;
492 player->UnlockLoop ();
495 void
496 PulseSource::Paused ()
498 player->LockLoop ();
499 play_pending = false;
500 if (pulse_stream && is_ready)
501 PACork (true);
502 player->UnlockLoop ();
505 void
506 PulseSource::Stopped ()
508 LOG_PULSE ("PulseSource::Stopped ()\n");
510 player->LockLoop ();
511 play_pending = false;
512 if (pulse_stream && is_ready) {
513 // Pause the stream and wait for the pause to complete
514 PACork (true);
515 // Drop all the samples we've sent
516 PAFlush ();
518 player->UnlockLoop ();
520 Close ();
523 guint64
524 PulseSource::GetDelayInternal ()
526 int err = 0;
527 pa_usec_t latency = 0;
528 int negative = 0;
529 guint64 result = 0;
531 player->LockLoop ();
532 if (pulse_stream && is_ready) {
533 err = pa_stream_get_latency (pulse_stream, &latency, &negative);
534 if (err < 0) {
535 LOG_AUDIO ("PulseSource::GetDelay (): Error: %s\n", pa_strerror (pa_context_errno (player->GetPAContext ())));
536 result = G_MAXUINT64;
537 } else {
538 result = MilliSeconds_ToPts (latency / 1000);
540 } else {
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);
549 return result;
553 * PulsePlayer
556 PulsePlayer::PulsePlayer ()
558 loop = NULL;
559 context = NULL;
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);
571 void
572 PulsePlayer::WaitLoop ()
574 pa_threaded_mainloop_wait (loop);
577 void
578 PulsePlayer::WaitForOperation (pa_operation *op)
580 if (pa_threaded_mainloop_in_thread (loop))
581 return;
583 //while (pa_operation_get_state (op) != PA_OPERATION_DONE)
584 // WaitLoop ();
585 pa_operation_unref (op);
588 void
589 PulsePlayer::SignalLoop ()
591 //pa_threaded_mainloop_signal (loop, 0);
594 void
595 PulsePlayer::LockLoop ()
597 if (!pa_threaded_mainloop_in_thread (loop))
598 pa_threaded_mainloop_lock (loop);
601 void
602 PulsePlayer::UnlockLoop ()
604 if (!pa_threaded_mainloop_in_thread (loop))
605 pa_threaded_mainloop_unlock (loop);
608 AudioSource *
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;
617 bool
618 PulsePlayer::IsInstalled ()
620 bool result = false;
621 const char *version;
623 switch (is_pulse_usable) {
624 case 0:
625 libpulse = dlopen ("libpulse.so.0", RTLD_LAZY);
626 if (libpulse == NULL) {
627 is_pulse_usable = 2;
628 return false;
630 result = true;
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);
682 if (!result)
683 LOG_AUDIO ("PulsePlayer: Failed to load one or more required functions in libpulse.so.\n");
685 is_pulse_usable = result ? 1 : 2;
686 return result;
687 case 1:
688 return true;
689 default:
690 return false;
693 return true;
696 void
697 PulsePlayer::OnContextStateChanged (pa_context *context, void *userdata)
699 ((PulsePlayer *) userdata)->OnContextStateChanged ();
702 pa_context_state_t
703 PulsePlayer::GetPAState ()
705 pa_context_state_t result;
707 LockLoop ();
708 result = pa_context_get_state (context);
709 UnlockLoop ();
711 return result;
714 #ifdef LOGGING
715 static const char *
716 get_pa_context_state_name (pa_context_state_t state)
718 switch (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>";
728 #endif
730 void
731 PulsePlayer::OnContextStateChanged () {
732 PulseSource *source;
733 pa_context_state_t state;
735 state = GetPAState ();
737 LOG_PULSE ("PulsePlayer::OnContextStateChanged (): %s (%i)\n", get_pa_context_state_name (state), state);
739 switch (state) {
740 case PA_CONTEXT_CONNECTING:
741 case PA_CONTEXT_AUTHORIZING:
742 case PA_CONTEXT_SETTING_NAME:
743 break;
744 case PA_CONTEXT_READY:
745 LockLoop ();
746 sources.StartEnumeration ();
747 while ((source = (PulseSource *) sources.GetNext (false)) != NULL) {
748 source->Initialize ();
749 source->unref ();
751 UnlockLoop ();
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);
757 break;
758 case PA_CONTEXT_TERMINATED:
759 break;
760 case PA_CONTEXT_FAILED:
761 default:
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)));
768 break;
772 void
773 PulsePlayer::AddInternal (AudioSource *source)
775 LOG_PULSE ("PulsePlayer::AddInternal (%p)\n", source);
777 ((PulseSource *) source)->Initialize ();
780 void
781 PulsePlayer::RemoveInternal (AudioSource *source)
783 LOG_PULSE ("PulsePlayer::RemoveInternal (%p)\n", source);
785 ((PulseSource *) source)->Close ();
789 bool
790 PulsePlayer::Initialize ()
792 int err;
794 LOG_PULSE ("PulsePlayer::InitializeInternal ()\n");
796 loop = pa_threaded_mainloop_new ();
797 if (loop == NULL) {
798 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to create main loop.\n");
799 return false;
802 api = pa_threaded_mainloop_get_api (loop);
804 if (api == NULL) {
805 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Failed to get api.\n");
806 return false;
809 context = pa_context_new (api, "Moonlight");
810 if (context == NULL) {
811 LOG_AUDIO ("PulsePlayer::InitializeInternal (); Failed to create context.\n");
812 return false;
815 pa_context_set_state_callback (context, OnContextStateChanged, this);
817 err = pa_context_connect (context, NULL, (pa_context_flags_t) 0, NULL);
818 if (err < 0) {
819 LOG_AUDIO ("PulsePlayer::InitializeInternal (): Error %i while connecting to server.\n", err);
820 return false;
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);
833 do {
834 // Wait until pulse has reported the connection status to
835 // us async.
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");
846 return false;
848 } else {
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);
855 return true;
858 void
859 PulsePlayer::PrepareShutdownInternal ()
863 void
864 PulsePlayer::FinishShutdownInternal ()
866 LOG_PULSE ("PulsePlayer::ShutdownInternal ()\n");
868 api = NULL; // CHECK: Do we need to unref this one?
870 if (context) {
871 pa_context_disconnect (context);
872 pa_context_unref (context);
873 context = NULL;
876 if (loop) {
877 pa_threaded_mainloop_stop (loop);
878 pa_threaded_mainloop_free (loop);
879 loop = NULL;