1 /*****************************************************************************
2 * vlcpulse.c : PulseAudio support library for LibVLC plugins
3 *****************************************************************************
4 * Copyright (C) 2008 the VideoLAN team
5 * Copyright (C) 2009-2011 RĂ©mi Denis-Courmont
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20 *****************************************************************************/
26 #define MODULE_STRING "pulse"
27 #include <vlc_common.h>
28 #include <pulse/pulseaudio.h>
30 #include <vlc_pulse.h>
38 void vlc_pa_error (vlc_object_t *obj, const char *msg, pa_context *ctx)
40 msg_Err (obj, "%s: %s", msg, pa_strerror (pa_context_errno (ctx)));
43 static pa_threaded_mainloop *vlc_pa_mainloop;
44 static unsigned refs = 0;
45 static vlc_mutex_t lock = VLC_STATIC_MUTEX;
48 * Creates and references the VLC PulseAudio threaded main loop.
49 * @return 0 on success, -1 on failure
51 static int vlc_pa_mainloop_init (void)
53 vlc_mutex_lock (&lock);
56 vlc_pa_mainloop = pa_threaded_mainloop_new ();
57 if (unlikely(vlc_pa_mainloop == NULL))
60 if (pa_threaded_mainloop_start (vlc_pa_mainloop) < 0)
62 pa_threaded_mainloop_free (vlc_pa_mainloop);
68 if (unlikely(refs >= UINT_MAX))
72 vlc_mutex_unlock (&lock);
75 vlc_mutex_unlock (&lock);
80 * Releases a reference to the VLC PulseAudio main loop.
82 static void vlc_pa_mainloop_deinit (void)
84 vlc_mutex_lock (&lock);
88 pa_threaded_mainloop_stop (vlc_pa_mainloop);
89 pa_threaded_mainloop_free (vlc_pa_mainloop);
91 vlc_mutex_unlock (&lock);
95 * Acquires the main loop lock.
97 void vlc_pa_lock (void)
99 pa_threaded_mainloop_lock (vlc_pa_mainloop);
103 * Releases the main loop lock.
105 void vlc_pa_unlock (void)
107 pa_threaded_mainloop_unlock (vlc_pa_mainloop);
111 * Signals the main loop.
113 void vlc_pa_signal (int do_wait)
115 pa_threaded_mainloop_signal (vlc_pa_mainloop, do_wait);
119 * Waits for the main loop to be signaled.
121 void vlc_pa_wait (void)
123 pa_threaded_mainloop_wait (vlc_pa_mainloop);
127 static void context_state_cb (pa_context *ctx, void *userdata)
129 switch (pa_context_get_state(ctx))
131 case PA_CONTEXT_READY:
132 case PA_CONTEXT_FAILED:
133 case PA_CONTEXT_TERMINATED:
141 static bool context_wait (pa_context *ctx)
143 pa_context_state_t state;
145 while ((state = pa_context_get_state (ctx)) != PA_CONTEXT_READY)
147 if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
155 * Initializes the PulseAudio main loop and connects to the PulseAudio server.
156 * @return a PulseAudio context on success, or NULL on error
158 pa_context *vlc_pa_connect (vlc_object_t *obj)
160 if (unlikely(vlc_pa_mainloop_init ()))
163 msg_Dbg (obj, "using library version %s", pa_get_library_version ());
164 msg_Dbg (obj, " (compiled with version %s, protocol %u)",
165 pa_get_headers_version (), PA_PROTOCOL_VERSION);
167 char *ua = var_InheritString (obj, "user-agent");
170 /* Fill in context (client) properties */
171 pa_proplist *props = pa_proplist_new ();
172 if (likely(props != NULL))
174 pa_proplist_sets (props, PA_PROP_APPLICATION_NAME, ua);
175 pa_proplist_sets (props, PA_PROP_APPLICATION_ID, "org.VideoLAN.VLC");
176 pa_proplist_sets (props, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
177 pa_proplist_sets (props, PA_PROP_APPLICATION_ICON_NAME, PACKAGE_NAME);
178 //pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE, _("C"));
179 pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE,
180 setlocale (LC_MESSAGES, NULL));
182 pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_ID, "%lu",
183 (unsigned long) getpid ());
184 //pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_BINARY,
187 char buf[sysconf (_SC_GETPW_R_SIZE_MAX)];
188 struct passwd pwbuf, *pw;
190 if (getpwuid_r (getuid (), &pwbuf, buf, sizeof (buf), &pw) == 0
192 pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_USER,
195 char hostname[sysconf (_SC_HOST_NAME_MAX)];
196 if (gethostname (hostname, sizeof (hostname)) == 0)
197 pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_HOST,
200 const char *session = getenv ("XDG_SESSION_COOKIE");
203 pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_MACHINE_ID,
204 "%.32s", session); /* XXX: is this valid? */
205 pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_SESSION_ID,
210 /* Connect to PulseAudio daemon */
213 ctx = pa_context_new_with_proplist (pa_threaded_mainloop_get_api (vlc_pa_mainloop), ua, props);
216 pa_proplist_free(props);
217 if (unlikely(ctx == NULL))
220 pa_context_set_state_callback (ctx, context_state_cb, NULL);
221 if (pa_context_connect (ctx, NULL, 0, NULL) < 0
222 || context_wait (ctx))
224 vlc_pa_error (obj, "PulseAudio server connection failure", ctx);
225 pa_context_unref (ctx);
228 msg_Dbg (obj, "connected %s to %s as client #%"PRIu32,
229 pa_context_is_local (ctx) ? "locally" : "remotely",
230 pa_context_get_server (ctx), pa_context_get_index (ctx));
231 msg_Dbg (obj, "using protocol %"PRIu32", server protocol %"PRIu32,
232 pa_context_get_protocol_version (ctx),
233 pa_context_get_server_protocol_version (ctx));
240 vlc_pa_mainloop_deinit ();
245 * Closes a connection to PulseAudio.
247 void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx)
250 pa_context_disconnect (ctx);
251 pa_context_set_state_callback (ctx, NULL, NULL);
252 pa_context_unref (ctx);
255 vlc_pa_mainloop_deinit ();
260 * Frees a timer event.
261 * \note Timer events can be created with pa_context_rttime_new().
262 * \warning This function must be called from the mainloop,
263 * or with the mainloop lock held.
265 void vlc_pa_rttime_free(pa_time_event *e)
267 (pa_threaded_mainloop_get_api (vlc_pa_mainloop))->time_free (e);