[vlc-commits] Add service discovery for PulseAudio input

Rémi Denis-Courmont git at videolan.org
Sat Oct 8 19:54:43 CEST 2011


vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Sat Oct  8 20:53:26 2011 +0300| [bd55fe88464e224a0931a4d82ff2c91f73d4e9a9] | committer: Rémi Denis-Courmont

Add service discovery for PulseAudio input

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=bd55fe88464e224a0931a4d82ff2c91f73d4e9a9
---

 modules/LIST                          |    1 +
 modules/services_discovery/Modules.am |   10 ++
 modules/services_discovery/pulse.c    |  250 +++++++++++++++++++++++++++++++++
 po/POTFILES.in                        |    1 +
 4 files changed, 262 insertions(+), 0 deletions(-)

diff --git a/modules/LIST b/modules/LIST
index d3d23f3..ec2509e 100644
--- a/modules/LIST
+++ b/modules/LIST
@@ -257,6 +257,7 @@ $Id$
  * ps: input module for MPEG PS decapsulation
  * psychedelic: Psychedelic video filter
  * pulse: PulseAudio audio output
+ * pulselist: PulseAudio audio input services discovery
  * pulsesrc: PulseAudio audio input
  * puzzle: puzzle video filter
  * pva: PVA demuxer
diff --git a/modules/services_discovery/Modules.am b/modules/services_discovery/Modules.am
index 4798d49..c3868e5 100644
--- a/modules/services_discovery/Modules.am
+++ b/modules/services_discovery/Modules.am
@@ -5,6 +5,16 @@ SOURCES_podcast = podcast.c
 SOURCES_mtp = mtp.c
 SOURCES_mediadirs = mediadirs.c
 
+libpulselist_plugin_la_SOURCES = \
+	../audio_output/vlcpulse.c ../audio_output/vlcpulse.h \
+	pulse.c
+libpulselist_plugin_la_CFLAGS = $(AM_CFLAGS) $(PULSE_CFLAGS)
+libpulselist_plugin_la_LIBADD = $(AM_LIBADD) $(PULSE_LIBS)
+lubpulselist_plugin_la_DEPENDENCIES =
+if HAVE_PULSE
+libvlc_LTLIBRARIES += libpulselist_plugin.la
+endif
+
 libudev_plugin_la_SOURCES = udev.c
 libudev_plugin_la_CFLAGS = $(AM_CFLAGS) $(UDEV_CFLAGS)
 libudev_plugin_la_LIBADD = $(AM_LIBADD) $(UDEV_LIBS)
diff --git a/modules/services_discovery/pulse.c b/modules/services_discovery/pulse.c
new file mode 100644
index 0000000..02e6802
--- /dev/null
+++ b/modules/services_discovery/pulse.c
@@ -0,0 +1,250 @@
+/**
+ * @file pulse.c
+ * @brief List of PulseAudio sources for VLC media player
+ */
+/*****************************************************************************
+ * Copyright © 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 <search.h>
+#include <assert.h>
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_services_discovery.h>
+#include <pulse/pulseaudio.h>
+#include "../audio_output/vlcpulse.h"
+
+static int Open (vlc_object_t *);
+static void Close (vlc_object_t *);
+
+VLC_SD_PROBE_HELPER("pulse", "Audio capture", SD_CAT_DEVICES);
+
+vlc_module_begin ()
+    set_shortname (N_("Video capture"))
+    set_description (N_("Video capture (Video4Linux)"))
+    set_category (CAT_PLAYLIST)
+    set_subcategory (SUBCAT_PLAYLIST_SD)
+    set_capability ("services_discovery", 0)
+    set_callbacks (Open, Close)
+    add_shortcut ("pulse", "pa", "pulseaudio", "audio")
+
+    VLC_SD_PROBE_SUBMODULE
+vlc_module_end ()
+
+struct services_discovery_sys_t
+{
+    void                 *root;
+    pa_context           *context;
+    pa_threaded_mainloop *mainloop;
+};
+
+static void SourceCallback(pa_context *, const pa_source_info *, int, void *);
+static void ContextCallback(pa_context *, pa_subscription_event_type_t,
+                            uint32_t, void *);
+
+static int Open (vlc_object_t *obj)
+{
+    services_discovery_t *sd = (services_discovery_t *)obj;
+    pa_operation *op;
+    pa_context *ctx;
+
+    services_discovery_sys_t *sys = malloc (sizeof (*sys));
+    if (unlikely(sys == NULL))
+        return VLC_ENOMEM;
+
+    ctx = vlc_pa_connect (obj, &sys->mainloop);
+    if (ctx == NULL)
+    {
+        free (sys);
+        return VLC_EGENERIC;
+    }
+
+    sd->p_sys = sys;
+    sys->context = ctx;
+    sys->root = NULL;
+
+    /* Subscribe for source events */
+    const pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_SOURCE;
+    pa_threaded_mainloop_lock (sys->mainloop);
+    pa_context_set_subscribe_callback (ctx, ContextCallback, sd);
+    op = pa_context_subscribe (ctx, mask, NULL, NULL);
+    if (likely(op != NULL))
+        pa_operation_unref (op);
+
+    /* Enumerate existing sources */
+    op = pa_context_get_source_info_list (ctx, SourceCallback, sd);
+    if (likely(op != NULL))
+    {
+        //while (pa_operation_get_state (op) == PA_OPERATION_RUNNING)
+        //    pa_threaded_mainloop_wait (sys->mainloop);
+        pa_operation_unref (op);
+    }
+    pa_threaded_mainloop_unlock (sys->mainloop);
+    return VLC_SUCCESS;
+/*
+error:
+    pa_threaded_mainloop_unlock (sys->mainloop);
+    vlc_pa_disconnect (obj, ctx, sys->mainloop);
+    free (sys);
+    return VLC_EGENERIC;*/
+}
+
+struct device
+{
+    uint32_t index;
+    input_item_t *item;
+    services_discovery_t *sd;
+};
+
+static void DestroySource (void *data)
+{
+    struct device *d = data;
+
+    if (d->sd)
+        services_discovery_RemoveItem (d->sd, d->item);
+    vlc_gc_decref (d->item);
+    free (d);
+}
+
+/**
+ * Compares two devices (to support binary search).
+ */
+static int cmpsrc (const void *a, const void *b)
+{
+    const uint32_t *pa = a, *pb = b;
+    uint32_t idxa = *pa, idxb = *pb;
+
+    return (idxa != idxb) ? ((idxa < idxb) ? -1 : +1) : 0;
+}
+
+/**
+ * Adds a source.
+ */
+static int AddSource (services_discovery_t *sd, const pa_source_info *info)
+{
+    services_discovery_sys_t *sys = sd->p_sys;
+
+    msg_Dbg (sd, "adding %s (%s)", info->name, info->description);
+
+    char *mrl;
+    if (unlikely(asprintf (&mrl, "pulse://%s", info->name) == -1))
+        return -1;
+
+    input_item_t *item = input_item_NewWithType (mrl, info->description,
+                                                 0, NULL, 0, -1,
+                                                 ITEM_TYPE_CARD);
+    free (mrl);
+    if (unlikely(item == NULL))
+        return -1;
+
+    struct device *d = malloc (sizeof (*d));
+    if (unlikely(d == NULL))
+    {
+        vlc_gc_decref (item);
+        return -1;
+    }
+    d->index = info->index;
+    d->item = item;
+    d->sd = NULL;
+
+    struct device **dp = tsearch (d, &sys->root, cmpsrc);
+    if (dp == NULL) /* Out-of-memory */
+    {
+        DestroySource (d);
+        return -1;
+    }
+    if (*dp != d) /* Replace existing source */
+    {
+        DestroySource (*dp);
+        *dp = d;
+    }
+
+    char *card;
+    if (info->card == PA_INVALID_INDEX
+     || unlikely(asprintf (&card, N_("Card %"PRIu32), info->card) == -1))
+        card = NULL;
+    services_discovery_AddItem (sd, item,
+                                (card != NULL) ? card : N_("Generic"));
+    d->sd = sd;
+    free (card);
+    return 0;
+}
+
+static void SourceCallback(pa_context *ctx, const pa_source_info *i, int eol,
+                           void *userdata)
+{
+    services_discovery_t *sd = userdata;
+
+    if (eol)
+        return;
+    AddSource (sd, i);
+    (void) ctx;
+}
+
+/**
+ * Removes a source (if present) by index.
+ */
+static void RemoveSource (services_discovery_t *sd, uint32_t idx)
+{
+    services_discovery_sys_t *sys = sd->p_sys;
+
+    struct device **dp = tfind (&idx, &sys->root, cmpsrc);
+    if (dp == NULL)
+        return;
+
+    struct device *d = *dp;
+    tdelete (d, &sys->root, cmpsrc);
+    DestroySource (d);
+}
+
+static void ContextCallback(pa_context *ctx, pa_subscription_event_type_t type,
+                            uint32_t idx, void *userdata)
+{
+    services_discovery_t *sd = userdata;
+    pa_operation *op;
+
+    assert ((type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK)
+                                              == PA_SUBSCRIPTION_EVENT_SOURCE);
+    switch (type & PA_SUBSCRIPTION_EVENT_TYPE_MASK)
+    {
+      case PA_SUBSCRIPTION_EVENT_NEW:
+      case PA_SUBSCRIPTION_EVENT_CHANGE:
+        op = pa_context_get_source_info_by_index(ctx, idx, SourceCallback, sd);
+        if (likely(op != NULL))
+            pa_operation_unref(op);
+        break;
+
+      case PA_SUBSCRIPTION_EVENT_REMOVE:
+        RemoveSource (sd, idx);
+        break;
+    }
+}
+
+static void Close (vlc_object_t *obj)
+{
+    services_discovery_t *sd = (services_discovery_t *)obj;
+    services_discovery_sys_t *sys = sd->p_sys;
+
+    vlc_pa_disconnect (obj, sys->context, sys->mainloop);
+    tdestroy (sys->root, DestroySource);
+    free (sys);
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3a92cad..38a0773 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -971,6 +971,7 @@ modules/services_discovery/bonjour.c
 modules/services_discovery/mediadirs.c
 modules/services_discovery/mtp.c
 modules/services_discovery/podcast.c
+modules/services_discovery/pulse.c
 modules/services_discovery/sap.c
 modules/services_discovery/udev.c
 modules/services_discovery/upnp.cpp



More information about the vlc-commits mailing list