PulseAudio: compile helper only once (refs #9367)
authorRémi Denis-Courmont <remi@remlab.net>
Fri, 13 Sep 2013 19:49:42 +0000 (22:49 +0300)
committerRémi Denis-Courmont <remi@remlab.net>
Fri, 13 Sep 2013 19:49:42 +0000 (22:49 +0300)
modules/Makefile.am
modules/access/Modules.am
modules/access/pulse.c
modules/audio_output/Modules.am
modules/audio_output/pulse.c
modules/audio_output/vlcpulse.c [deleted file]
modules/audio_output/vlcpulse.h [deleted file]
modules/services_discovery/Modules.am
modules/services_discovery/pulse.c
modules/vlcpulse.c [new file with mode: 0644]
modules/vlcpulse.h [new file with mode: 0644]

index e622ecb..82d0cbd 100644 (file)
@@ -1,4 +1,4 @@
-NULL =
+noinst_LTLIBRARIES =
 BASE_SUBDIRS = \
        access \
        audio_filter \
@@ -29,11 +29,10 @@ EXTRA_SUBDIRS = \
        altivec \
        arm_neon \
        hw/vdpau \
-       lua \
-       $(NULL)
+       lua
 
-SUBDIRS = $(BASE_SUBDIRS)
-DIST_SUBDIRS = $(BASE_SUBDIRS) $(EXTRA_SUBDIRS) .
+SUBDIRS = $(BASE_SUBDIRS)
+DIST_SUBDIRS = . $(BASE_SUBDIRS) $(EXTRA_SUBDIRS)
 if ENABLE_SOUT
 SUBDIRS += access_output mux stream_out
 endif
@@ -55,11 +54,19 @@ endif
 if BUILD_LUA
 SUBDIRS += lua
 endif
-SUBDIRS += .
 
 dist_noinst_SCRIPTS = genmf list.sh module.rc.in
 dist_noinst_DATA = LIST
 
+libvlc_pulse_la_SOURCES = vlcpulse.c vlcpulse.h
+libvlc_pulse_la_CPPFLAGS = -DMODULE_STRING=\"pulse\"
+libvlc_pulse_la_CFLAGS = $(AM_CFLAGS) $(PULSE_CFLAGS)
+libvlc_pulse_la_LDFLAGS = -static
+libvlc_pulse_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS)
+if HAVE_PULSE
+noinst_LTLIBRARIES += libvlc_pulse.la
+endif
+
 if HAVE_WIN32
 module.rc: $(top_builddir)/modules/module.rc.in $(top_builddir)/config.status
        $(AM_V_GEN) cd "$(top_builddir)" && $(SHELL) ./config.status --file="modules/$@"
index 1f6c574..5b81e14 100644 (file)
@@ -146,12 +146,10 @@ if HAVE_ALSA
 libvlc_LTLIBRARIES += libaccess_alsa_plugin.la
 endif
 
-libpulsesrc_plugin_la_SOURCES = \
-       ../audio_output/vlcpulse.c \
-       ../audio_output/vlcpulse.h \
-       pulse.c
+libpulsesrc_plugin_la_SOURCES = pulse.c
 libpulsesrc_plugin_la_CFLAGS= $(AM_CFLAGS) $(PULSE_CFLAGS)
-libpulsesrc_plugin_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS)
+libpulsesrc_plugin_la_LIBADD = ../libvlc_pulse.la \
+       $(AM_LIBADD) $(PULSE_LIBS)
 if HAVE_PULSE
 libvlc_LTLIBRARIES += libpulsesrc_plugin.la
 endif
index 7018330..a792d75 100644 (file)
@@ -29,7 +29,7 @@
 #include <vlc_demux.h>
 #include <vlc_plugin.h>
 #include <pulse/pulseaudio.h>
-#include "../audio_output/vlcpulse.h"
+#include "../vlcpulse.h"
 
 #define HELP_TEXT N_( \
     "Pass pulse:// to open the default PulseAudio source, " \
index 5c04aaa..3af36c7 100644 (file)
@@ -55,9 +55,10 @@ if HAVE_ALSA
 libvlc_LTLIBRARIES += libalsa_plugin.la
 endif
 
-libpulse_plugin_la_SOURCES = vlcpulse.c vlcpulse.h pulse.c
+libpulse_plugin_la_SOURCES = pulse.c
 libpulse_plugin_la_CFLAGS = $(AM_CFLAGS) $(PULSE_CFLAGS)
-libpulse_plugin_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS) $(LIBM)
+libpulse_plugin_la_LIBADD = ../libvlc_pulse.la \
+       $(AM_LIBADD) $(PULSE_LIBS) $(LIBM)
 if HAVE_PULSE
 libvlc_LTLIBRARIES += libpulse_plugin.la
 endif
index b477740..ab049be 100644 (file)
@@ -32,7 +32,7 @@
 #include <vlc_cpu.h>
 
 #include <pulse/pulseaudio.h>
-#include "vlcpulse.h"
+#include "../vlcpulse.h"
 #if !PA_CHECK_VERSION(0,9,22)
 # include <vlc_xlib.h>
 #endif
diff --git a/modules/audio_output/vlcpulse.c b/modules/audio_output/vlcpulse.c
deleted file mode 100644 (file)
index 02c9ff1..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-/**
- * \file vlcpulse.c
- * \brief PulseAudio support library for LibVLC plugins
- */
-/*****************************************************************************
- * Copyright (C) 2009-2011 Rémi Denis-Courmont
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <vlc_common.h>
-#include <pulse/pulseaudio.h>
-
-#include "vlcpulse.h"
-#include <assert.h>
-#include <stdlib.h>
-#include <locale.h>
-#include <unistd.h>
-#include <pwd.h>
-
-#undef vlc_pa_error
-void vlc_pa_error (vlc_object_t *obj, const char *msg, pa_context *ctx)
-{
-    msg_Err (obj, "%s: %s", msg, pa_strerror (pa_context_errno (ctx)));
-}
-
-static void context_state_cb (pa_context *ctx, void *userdata)
-{
-    pa_threaded_mainloop *mainloop = userdata;
-
-    switch (pa_context_get_state(ctx))
-    {
-        case PA_CONTEXT_READY:
-        case PA_CONTEXT_FAILED:
-        case PA_CONTEXT_TERMINATED:
-            pa_threaded_mainloop_signal (mainloop, 0);
-        default:
-            break;
-    }
-}
-
-static bool context_wait (pa_context *ctx, pa_threaded_mainloop *mainloop)
-{
-    pa_context_state_t state;
-
-    while ((state = pa_context_get_state (ctx)) != PA_CONTEXT_READY)
-    {
-        if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
-            return -1;
-        pa_threaded_mainloop_wait (mainloop);
-    }
-    return 0;
-}
-
-static void context_event_cb(pa_context *c, const char *name, pa_proplist *pl,
-                             void *userdata)
-{
-    vlc_object_t *obj = userdata;
-
-    msg_Warn (obj, "unhandled context event \"%s\"", name);
-    (void) c;
-    (void) pl;
-}
-
-/**
- * Initializes the PulseAudio main loop and connects to the PulseAudio server.
- * @return a PulseAudio context on success, or NULL on error
- */
-pa_context *vlc_pa_connect (vlc_object_t *obj, pa_threaded_mainloop **mlp)
-{
-    msg_Dbg (obj, "using library version %s", pa_get_library_version ());
-    msg_Dbg (obj, " (compiled with version %s, protocol %u)",
-             pa_get_headers_version (), PA_PROTOCOL_VERSION);
-
-    /* Initialize main loop */
-    pa_threaded_mainloop *mainloop = pa_threaded_mainloop_new ();
-    if (unlikely(mainloop == NULL))
-        return NULL;
-
-    if (pa_threaded_mainloop_start (mainloop) < 0)
-    {
-        pa_threaded_mainloop_free (mainloop);
-        return NULL;
-    }
-
-    /* Fill in context (client) properties */
-    char *ua = var_InheritString (obj, "user-agent");
-    pa_proplist *props = pa_proplist_new ();
-    if (likely(props != NULL))
-    {
-        char *str;
-
-        if (ua != NULL)
-            pa_proplist_sets (props, PA_PROP_APPLICATION_NAME, ua);
-
-        str = var_InheritString (obj, "app-id");
-        if (str != NULL)
-        {
-            pa_proplist_sets (props, PA_PROP_APPLICATION_ID, str);
-            free (str);
-        }
-        str = var_InheritString (obj, "app-version");
-        if (str != NULL)
-        {
-            pa_proplist_sets (props, PA_PROP_APPLICATION_VERSION, str);
-            free (str);
-        }
-        str = var_InheritString (obj, "app-icon-name");
-        if (str != NULL)
-        {
-            pa_proplist_sets (props, PA_PROP_APPLICATION_ICON_NAME, str);
-            free (str);
-        }
-        //pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE, _("C"));
-        pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE,
-                          setlocale (LC_MESSAGES, NULL));
-
-        pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_ID, "%lu",
-                          (unsigned long) getpid ());
-        //pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_BINARY,
-        //                  PACKAGE_NAME);
-
-        for (size_t max = sysconf (_SC_GETPW_R_SIZE_MAX), len = max % 1024 + 1024;
-             len < max; len += 1024)
-        {
-            struct passwd pwbuf, *pw;
-            char buf[len];
-
-            if (getpwuid_r (getuid (), &pwbuf, buf, sizeof (buf), &pw) == 0)
-            {
-                if (pw != NULL)
-                    pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_USER,
-                                      pw->pw_name);
-                break;
-            }
-        }
-
-        for (size_t max = sysconf (_SC_HOST_NAME_MAX), len = max % 1024 + 1024;
-             len < max; len += 1024)
-        {
-            char hostname[len];
-
-            if (gethostname (hostname, sizeof (hostname)) == 0)
-            {
-                pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_HOST,
-                                  hostname);
-                break;
-            }
-        }
-
-        const char *session = getenv ("XDG_SESSION_COOKIE");
-        if (session != NULL)
-        {
-            pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_MACHINE_ID,
-                              "%.32s", session); /* XXX: is this valid? */
-            pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_SESSION_ID,
-                              session);
-        }
-    }
-
-    /* Connect to PulseAudio daemon */
-    pa_context *ctx;
-    pa_mainloop_api *api;
-
-    pa_threaded_mainloop_lock (mainloop);
-    api = pa_threaded_mainloop_get_api (mainloop);
-    ctx = pa_context_new_with_proplist (api, ua, props);
-    free (ua);
-    if (props != NULL)
-        pa_proplist_free (props);
-    if (unlikely(ctx == NULL))
-        goto fail;
-
-    pa_context_set_state_callback (ctx, context_state_cb, mainloop);
-    pa_context_set_event_callback (ctx, context_event_cb, obj);
-    if (pa_context_connect (ctx, NULL, 0, NULL) < 0
-     || context_wait (ctx, mainloop))
-    {
-        vlc_pa_error (obj, "PulseAudio server connection failure", ctx);
-        pa_context_unref (ctx);
-        goto fail;
-    }
-    msg_Dbg (obj, "connected %s to %s as client #%"PRIu32,
-             pa_context_is_local (ctx) ? "locally" : "remotely",
-             pa_context_get_server (ctx), pa_context_get_index (ctx));
-    msg_Dbg (obj, "using protocol %"PRIu32", server protocol %"PRIu32,
-             pa_context_get_protocol_version (ctx),
-             pa_context_get_server_protocol_version (ctx));
-
-    pa_threaded_mainloop_unlock (mainloop);
-    *mlp = mainloop;
-    return ctx;
-
-fail:
-    pa_threaded_mainloop_unlock (mainloop);
-    pa_threaded_mainloop_stop (mainloop);
-    pa_threaded_mainloop_free (mainloop);
-    return NULL;
-}
-
-/**
- * Closes a connection to PulseAudio.
- */
-void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx,
-                        pa_threaded_mainloop *mainloop)
-{
-    pa_threaded_mainloop_lock (mainloop);
-    pa_context_disconnect (ctx);
-    pa_context_set_event_callback (ctx, NULL, NULL);
-    pa_context_set_state_callback (ctx, NULL, NULL);
-    pa_context_unref (ctx);
-    pa_threaded_mainloop_unlock (mainloop);
-
-    pa_threaded_mainloop_stop (mainloop);
-    pa_threaded_mainloop_free (mainloop);
-    (void) obj;
-}
-
-/**
- * Frees a timer event.
- * \note Timer events can be created with pa_context_rttime_new().
- * \warning This function must be called from the mainloop,
- * or with the mainloop lock held.
- */
-void vlc_pa_rttime_free (pa_threaded_mainloop *mainloop, pa_time_event *e)
-{
-    pa_mainloop_api *api = pa_threaded_mainloop_get_api (mainloop);
-
-    api->time_free (e);
-}
-
-#undef vlc_pa_get_latency
-/**
- * Gets latency of a PulseAudio stream.
- * \return the latency or VLC_TS_INVALID on error.
- */
-mtime_t vlc_pa_get_latency(vlc_object_t *obj, pa_context *ctx, pa_stream *s)
-{
-    pa_usec_t latency;
-    int negative;
-
-    if (pa_stream_get_latency(s, &latency, &negative)) {
-        if (pa_context_errno (ctx) != PA_ERR_NODATA)
-            vlc_pa_error(obj, "unknown latency", ctx);
-        return VLC_TS_INVALID;
-    }
-    return negative ? -latency : +latency;
-}
diff --git a/modules/audio_output/vlcpulse.h b/modules/audio_output/vlcpulse.h
deleted file mode 100644 (file)
index bfd3fe0..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * \file vlcpulse.h
- * \brief PulseAudio support library for LibVLC plugins
- */
-/****************************************************************************
- * Copyright (C) 2011 Rémi Denis-Courmont
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
- *****************************************************************************/
-
-#ifndef VLCPULSE_H
-# define VLCPULSE_H 1
-# ifdef __cplusplus
-extern "C" {
-# endif
-
-VLC_API pa_context *vlc_pa_connect (vlc_object_t *obj,
-                                    pa_threaded_mainloop **);
-VLC_API void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx,
-                                pa_threaded_mainloop *);
-
-VLC_API void vlc_pa_error (vlc_object_t *, const char *msg, pa_context *);
-#define vlc_pa_error(o, m, c) vlc_pa_error(VLC_OBJECT(o), m, c)
-
-VLC_API mtime_t vlc_pa_get_latency (vlc_object_t *, pa_context *, pa_stream *);
-#define vlc_pa_get_latency(o, c, s) vlc_pa_get_latency(VLC_OBJECT(o), c, s)
-
-VLC_API void vlc_pa_rttime_free (pa_threaded_mainloop *, pa_time_event *);
-
-# ifdef __cplusplus
-}
-# endif
-#endif
index 4f36784..8e12934 100644 (file)
@@ -13,11 +13,10 @@ if HAVE_WIN32
 libsap_plugin_la_DEPENDENCIES = libsap_plugin.rc.o
 endif
 
-libpulselist_plugin_la_SOURCES = \
-       ../audio_output/vlcpulse.c ../audio_output/vlcpulse.h \
-       pulse.c
+libpulselist_plugin_la_SOURCES = pulse.c
 libpulselist_plugin_la_CFLAGS = $(AM_CFLAGS) $(PULSE_CFLAGS)
-libpulselist_plugin_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS)
+libpulselist_plugin_la_LIBADD = ../libvlc_pulse.la \
+       $(AM_LIBADD) $(PULSE_LIBS)
 if HAVE_PULSE
 libvlc_LTLIBRARIES += libpulselist_plugin.la
 endif
index 0b81950..92244ee 100644 (file)
@@ -31,7 +31,7 @@
 #include <vlc_plugin.h>
 #include <vlc_services_discovery.h>
 #include <pulse/pulseaudio.h>
-#include "../audio_output/vlcpulse.h"
+#include "../vlcpulse.h"
 
 static int Open (vlc_object_t *);
 static void Close (vlc_object_t *);
diff --git a/modules/vlcpulse.c b/modules/vlcpulse.c
new file mode 100644 (file)
index 0000000..02c9ff1
--- /dev/null
@@ -0,0 +1,264 @@
+/**
+ * \file vlcpulse.c
+ * \brief PulseAudio support library for LibVLC plugins
+ */
+/*****************************************************************************
+ * Copyright (C) 2009-2011 Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <pulse/pulseaudio.h>
+
+#include "vlcpulse.h"
+#include <assert.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#undef vlc_pa_error
+void vlc_pa_error (vlc_object_t *obj, const char *msg, pa_context *ctx)
+{
+    msg_Err (obj, "%s: %s", msg, pa_strerror (pa_context_errno (ctx)));
+}
+
+static void context_state_cb (pa_context *ctx, void *userdata)
+{
+    pa_threaded_mainloop *mainloop = userdata;
+
+    switch (pa_context_get_state(ctx))
+    {
+        case PA_CONTEXT_READY:
+        case PA_CONTEXT_FAILED:
+        case PA_CONTEXT_TERMINATED:
+            pa_threaded_mainloop_signal (mainloop, 0);
+        default:
+            break;
+    }
+}
+
+static bool context_wait (pa_context *ctx, pa_threaded_mainloop *mainloop)
+{
+    pa_context_state_t state;
+
+    while ((state = pa_context_get_state (ctx)) != PA_CONTEXT_READY)
+    {
+        if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED)
+            return -1;
+        pa_threaded_mainloop_wait (mainloop);
+    }
+    return 0;
+}
+
+static void context_event_cb(pa_context *c, const char *name, pa_proplist *pl,
+                             void *userdata)
+{
+    vlc_object_t *obj = userdata;
+
+    msg_Warn (obj, "unhandled context event \"%s\"", name);
+    (void) c;
+    (void) pl;
+}
+
+/**
+ * Initializes the PulseAudio main loop and connects to the PulseAudio server.
+ * @return a PulseAudio context on success, or NULL on error
+ */
+pa_context *vlc_pa_connect (vlc_object_t *obj, pa_threaded_mainloop **mlp)
+{
+    msg_Dbg (obj, "using library version %s", pa_get_library_version ());
+    msg_Dbg (obj, " (compiled with version %s, protocol %u)",
+             pa_get_headers_version (), PA_PROTOCOL_VERSION);
+
+    /* Initialize main loop */
+    pa_threaded_mainloop *mainloop = pa_threaded_mainloop_new ();
+    if (unlikely(mainloop == NULL))
+        return NULL;
+
+    if (pa_threaded_mainloop_start (mainloop) < 0)
+    {
+        pa_threaded_mainloop_free (mainloop);
+        return NULL;
+    }
+
+    /* Fill in context (client) properties */
+    char *ua = var_InheritString (obj, "user-agent");
+    pa_proplist *props = pa_proplist_new ();
+    if (likely(props != NULL))
+    {
+        char *str;
+
+        if (ua != NULL)
+            pa_proplist_sets (props, PA_PROP_APPLICATION_NAME, ua);
+
+        str = var_InheritString (obj, "app-id");
+        if (str != NULL)
+        {
+            pa_proplist_sets (props, PA_PROP_APPLICATION_ID, str);
+            free (str);
+        }
+        str = var_InheritString (obj, "app-version");
+        if (str != NULL)
+        {
+            pa_proplist_sets (props, PA_PROP_APPLICATION_VERSION, str);
+            free (str);
+        }
+        str = var_InheritString (obj, "app-icon-name");
+        if (str != NULL)
+        {
+            pa_proplist_sets (props, PA_PROP_APPLICATION_ICON_NAME, str);
+            free (str);
+        }
+        //pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE, _("C"));
+        pa_proplist_sets (props, PA_PROP_APPLICATION_LANGUAGE,
+                          setlocale (LC_MESSAGES, NULL));
+
+        pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_ID, "%lu",
+                          (unsigned long) getpid ());
+        //pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_BINARY,
+        //                  PACKAGE_NAME);
+
+        for (size_t max = sysconf (_SC_GETPW_R_SIZE_MAX), len = max % 1024 + 1024;
+             len < max; len += 1024)
+        {
+            struct passwd pwbuf, *pw;
+            char buf[len];
+
+            if (getpwuid_r (getuid (), &pwbuf, buf, sizeof (buf), &pw) == 0)
+            {
+                if (pw != NULL)
+                    pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_USER,
+                                      pw->pw_name);
+                break;
+            }
+        }
+
+        for (size_t max = sysconf (_SC_HOST_NAME_MAX), len = max % 1024 + 1024;
+             len < max; len += 1024)
+        {
+            char hostname[len];
+
+            if (gethostname (hostname, sizeof (hostname)) == 0)
+            {
+                pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_HOST,
+                                  hostname);
+                break;
+            }
+        }
+
+        const char *session = getenv ("XDG_SESSION_COOKIE");
+        if (session != NULL)
+        {
+            pa_proplist_setf (props, PA_PROP_APPLICATION_PROCESS_MACHINE_ID,
+                              "%.32s", session); /* XXX: is this valid? */
+            pa_proplist_sets (props, PA_PROP_APPLICATION_PROCESS_SESSION_ID,
+                              session);
+        }
+    }
+
+    /* Connect to PulseAudio daemon */
+    pa_context *ctx;
+    pa_mainloop_api *api;
+
+    pa_threaded_mainloop_lock (mainloop);
+    api = pa_threaded_mainloop_get_api (mainloop);
+    ctx = pa_context_new_with_proplist (api, ua, props);
+    free (ua);
+    if (props != NULL)
+        pa_proplist_free (props);
+    if (unlikely(ctx == NULL))
+        goto fail;
+
+    pa_context_set_state_callback (ctx, context_state_cb, mainloop);
+    pa_context_set_event_callback (ctx, context_event_cb, obj);
+    if (pa_context_connect (ctx, NULL, 0, NULL) < 0
+     || context_wait (ctx, mainloop))
+    {
+        vlc_pa_error (obj, "PulseAudio server connection failure", ctx);
+        pa_context_unref (ctx);
+        goto fail;
+    }
+    msg_Dbg (obj, "connected %s to %s as client #%"PRIu32,
+             pa_context_is_local (ctx) ? "locally" : "remotely",
+             pa_context_get_server (ctx), pa_context_get_index (ctx));
+    msg_Dbg (obj, "using protocol %"PRIu32", server protocol %"PRIu32,
+             pa_context_get_protocol_version (ctx),
+             pa_context_get_server_protocol_version (ctx));
+
+    pa_threaded_mainloop_unlock (mainloop);
+    *mlp = mainloop;
+    return ctx;
+
+fail:
+    pa_threaded_mainloop_unlock (mainloop);
+    pa_threaded_mainloop_stop (mainloop);
+    pa_threaded_mainloop_free (mainloop);
+    return NULL;
+}
+
+/**
+ * Closes a connection to PulseAudio.
+ */
+void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx,
+                        pa_threaded_mainloop *mainloop)
+{
+    pa_threaded_mainloop_lock (mainloop);
+    pa_context_disconnect (ctx);
+    pa_context_set_event_callback (ctx, NULL, NULL);
+    pa_context_set_state_callback (ctx, NULL, NULL);
+    pa_context_unref (ctx);
+    pa_threaded_mainloop_unlock (mainloop);
+
+    pa_threaded_mainloop_stop (mainloop);
+    pa_threaded_mainloop_free (mainloop);
+    (void) obj;
+}
+
+/**
+ * Frees a timer event.
+ * \note Timer events can be created with pa_context_rttime_new().
+ * \warning This function must be called from the mainloop,
+ * or with the mainloop lock held.
+ */
+void vlc_pa_rttime_free (pa_threaded_mainloop *mainloop, pa_time_event *e)
+{
+    pa_mainloop_api *api = pa_threaded_mainloop_get_api (mainloop);
+
+    api->time_free (e);
+}
+
+#undef vlc_pa_get_latency
+/**
+ * Gets latency of a PulseAudio stream.
+ * \return the latency or VLC_TS_INVALID on error.
+ */
+mtime_t vlc_pa_get_latency(vlc_object_t *obj, pa_context *ctx, pa_stream *s)
+{
+    pa_usec_t latency;
+    int negative;
+
+    if (pa_stream_get_latency(s, &latency, &negative)) {
+        if (pa_context_errno (ctx) != PA_ERR_NODATA)
+            vlc_pa_error(obj, "unknown latency", ctx);
+        return VLC_TS_INVALID;
+    }
+    return negative ? -latency : +latency;
+}
diff --git a/modules/vlcpulse.h b/modules/vlcpulse.h
new file mode 100644 (file)
index 0000000..bfd3fe0
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ * \file vlcpulse.h
+ * \brief PulseAudio support library for LibVLC plugins
+ */
+/****************************************************************************
+ * Copyright (C) 2011 Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLCPULSE_H
+# define VLCPULSE_H 1
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+VLC_API pa_context *vlc_pa_connect (vlc_object_t *obj,
+                                    pa_threaded_mainloop **);
+VLC_API void vlc_pa_disconnect (vlc_object_t *obj, pa_context *ctx,
+                                pa_threaded_mainloop *);
+
+VLC_API void vlc_pa_error (vlc_object_t *, const char *msg, pa_context *);
+#define vlc_pa_error(o, m, c) vlc_pa_error(VLC_OBJECT(o), m, c)
+
+VLC_API mtime_t vlc_pa_get_latency (vlc_object_t *, pa_context *, pa_stream *);
+#define vlc_pa_get_latency(o, c, s) vlc_pa_get_latency(VLC_OBJECT(o), c, s)
+
+VLC_API void vlc_pa_rttime_free (pa_threaded_mainloop *, pa_time_event *);
+
+# ifdef __cplusplus
+}
+# endif
+#endif