3 * \brief PulseAudio support library for LibVLC plugins
5 /*****************************************************************************
6 * Copyright (C) 2009-2011 RĂ©mi Denis-Courmont
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
27 #include <vlc_common.h>
28 #include <pulse/pulseaudio.h>
30 #include "audio_output/vlcpulse.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 void context_state_cb (pa_context *ctx, void *userdata)
45 pa_threaded_mainloop *mainloop = userdata;
47 switch (pa_context_get_state(ctx))
49 case PA_CONTEXT_READY:
50 case PA_CONTEXT_FAILED:
51 case PA_CONTEXT_TERMINATED:
52 pa_threaded_mainloop_signal (mainloop, 0);
58 static bool context_wait (pa_context *ctx, pa_threaded_mainloop *mainloop)
60 pa_context_state_t state;
62 while ((state = pa_context_get_state (ctx)) != PA_CONTEXT_READY)
64 if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
66 pa_threaded_mainloop_wait (mainloop);
71 static void context_event_cb(pa_context *c, const char *name, pa_proplist *pl,
74 vlc_object_t *obj = userdata;
76 msg_Warn (obj, "unhandled context event \"%s\"", name);
82 * Initializes the PulseAudio main loop and connects to the PulseAudio server.
83 * @return a PulseAudio context on success, or NULL on error
85 pa_context *vlc_pa_connect (vlc_object_t *obj, pa_threaded_mainloop **mlp)
87 msg_Dbg (obj, "using library version %s", pa_get_library_version ());
88 msg_Dbg (obj, " (compiled with version %s, protocol %u)",
89 pa_get_headers_version (), PA_PROTOCOL_VERSION);
91 /* Initialize main loop */
92 pa_threaded_mainloop *mainloop = pa_threaded_mainloop_new ();
93 if (unlikely(mainloop == NULL))
96 if (pa_threaded_mainloop_start (mainloop) < 0)
98 pa_threaded_mainloop_free (mainloop);
102 /* Fill in context (client) properties */
103 char *ua = var_InheritString (obj, "user-agent");
104 pa_proplist *props = pa_proplist_new ();
105 if (likely(props != NULL))
110 pa_proplist_sets (props, PA_PROP_APPLICATION_NAME, ua);
112 str = var_InheritString (obj, "app-id");
115 pa_proplist_sets (props, PA_PROP_APPLICATION_ID, str);
118 str = var_InheritString (obj, "app-version");
121 pa_proplist_sets (props, PA_PROP_APPLICATION_VERSION, str);
124 str = var_InheritString (obj, "app-icon-name");
127 pa_proplist_sets (props, PA_PROP_APPLICATION_ICON_NAME, str);
130 //pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE, _("C"));
131 pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE,
132 setlocale (LC_MESSAGES, NULL));
134 pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_ID, "%lu",
135 (unsigned long) getpid ());
136 //pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_BINARY,
139 for (size_t max = sysconf (_SC_GETPW_R_SIZE_MAX), len = max % 1024 + 1024;
140 len < max; len += 1024)
142 struct passwd pwbuf, *pw;
145 if (getpwuid_r (getuid (), &pwbuf, buf, sizeof (buf), &pw) == 0)
148 pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_USER,
154 for (size_t max = sysconf (_SC_HOST_NAME_MAX), len = max % 1024 + 1024;
155 len < max; len += 1024)
159 if (gethostname (hostname, sizeof (hostname)) == 0)
161 pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_HOST,
167 const char *session = getenv ("XDG_SESSION_COOKIE");
170 pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_MACHINE_ID,
171 "%.32s", session); /* XXX: is this valid? */
172 pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_SESSION_ID,
177 /* Connect to PulseAudio daemon */
179 pa_mainloop_api *api;
181 pa_threaded_mainloop_lock (mainloop);
182 api = pa_threaded_mainloop_get_api (mainloop);
183 ctx = pa_context_new_with_proplist (api, ua, props);
186 pa_proplist_free (props);
187 if (unlikely(ctx == NULL))
190 pa_context_set_state_callback (ctx, context_state_cb, mainloop);
191 pa_context_set_event_callback (ctx, context_event_cb, obj);
192 if (pa_context_connect (ctx, NULL, 0, NULL) < 0
193 || context_wait (ctx, mainloop))
195 vlc_pa_error (obj, "PulseAudio server connection failure", ctx);
196 pa_context_unref (ctx);
199 msg_Dbg (obj, "connected %s to %s as client #%"PRIu32,
200 pa_context_is_local (ctx) ? "locally" : "remotely",
201 pa_context_get_server (ctx), pa_context_get_index (ctx));
202 msg_Dbg (obj, "using protocol %"PRIu32", server protocol %"PRIu32,
203 pa_context_get_protocol_version (ctx),
204 pa_context_get_server_protocol_version (ctx));
206 pa_threaded_mainloop_unlock (mainloop);
211 pa_threaded_mainloop_unlock (mainloop);
212 pa_threaded_mainloop_stop (mainloop);
213 pa_threaded_mainloop_free (mainloop);
218 * Closes a connection to PulseAudio.
220 void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx,
221 pa_threaded_mainloop *mainloop)
223 pa_threaded_mainloop_lock (mainloop);
224 pa_context_disconnect (ctx);
225 pa_context_set_event_callback (ctx, NULL, NULL);
226 pa_context_set_state_callback (ctx, NULL, NULL);
227 pa_context_unref (ctx);
228 pa_threaded_mainloop_unlock (mainloop);
230 pa_threaded_mainloop_stop (mainloop);
231 pa_threaded_mainloop_free (mainloop);
236 * Frees a timer event.
237 * \note Timer events can be created with pa_context_rttime_new().
238 * \warning This function must be called from the mainloop,
239 * or with the mainloop lock held.
241 void vlc_pa_rttime_free (pa_threaded_mainloop *mainloop, pa_time_event *e)
243 pa_mainloop_api *api = pa_threaded_mainloop_get_api (mainloop);
248 #undef vlc_pa_get_latency
250 * Gets latency of a PulseAudio stream.
251 * \return the latency or VLC_TS_INVALID on error.
253 mtime_t vlc_pa_get_latency(vlc_object_t *obj, pa_context *ctx, pa_stream *s)
258 if (pa_stream_get_latency(s, &latency, &negative)) {
259 if (pa_context_errno (ctx) != PA_ERR_NODATA)
260 vlc_pa_error(obj, "unknown latency", ctx);
261 return VLC_TS_INVALID;
263 return negative ? -latency : +latency;