[vlc-devel] commit: Initial udev service discovery plugin ( Rémi Denis-Courmont )

git version control git at videolan.org
Sun Oct 18 13:47:35 CEST 2009


vlc | branch: 1.0-bugfix | Rémi Denis-Courmont <remi at remlab.net> | Sun Oct 18 10:44:24 2009 +0300| [2c5d2eedcc51dc9410fad9bd66962092e419c893] | committer: Rémi Denis-Courmont 

Initial udev service discovery plugin

This implements device discovery with libudev, which is part of the udev
source package (and hence available in any recent Linux distribution).
Both cold and hot plugging (w.r.t. VLC) are implemented.

TODO:
 * V4L1 devices (currently V4L2 is assumed)
 * other subsystems (linux-dvb -> DVB, block -> discs)
 * item removal on unplug
 * better item name and category for non-USB devices
(cherry picked from commit 020c20ecbb40fa7722cfaa121079ae17891809fd)

Conflicts:

	configure.ac
	modules/services_discovery/Modules.am

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

 configure.ac                          |   13 ++
 modules/services_discovery/Modules.am |    1 +
 modules/services_discovery/udev.c     |  250 +++++++++++++++++++++++++++++++++
 3 files changed, 264 insertions(+), 0 deletions(-)

diff --git a/configure.ac b/configure.ac
index 20648af..5f57cab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -980,6 +980,19 @@ then
   )
 fi
 
+dnl Check for libudev
+AC_ARG_ENABLE(udev,
+  [  --enable-udev           Linux udev services discovery (default auto)])
+AS_IF([test "$enable_udev" != "no"], [
+  PKG_CHECK_MODULES(UDEV, libudev, [
+    VLC_ADD_PLUGIN([udev])
+    VLC_ADD_LIBS([udev],[$UDEV_LIBS])
+    VLC_ADD_CFLAGS([udev],[$UDEV_CFLAGS])
+  ], [
+    true
+  ])
+])
+
 dnl Check for mtp
 AC_ARG_ENABLE(mtp,
   [  --enable-mtp            MTP devices support (default enabled)])
diff --git a/modules/services_discovery/Modules.am b/modules/services_discovery/Modules.am
index 055500e..ed6ae66 100644
--- a/modules/services_discovery/Modules.am
+++ b/modules/services_discovery/Modules.am
@@ -6,3 +6,4 @@ SOURCES_upnp_intel = upnp_intel.cpp upnp_intel.hpp
 SOURCES_bonjour = bonjour.c
 SOURCES_podcast = podcast.c
 SOURCES_mtp = mtp.c
+SOURCES_udev = udev.c
diff --git a/modules/services_discovery/udev.c b/modules/services_discovery/udev.c
new file mode 100644
index 0000000..14c1703
--- /dev/null
+++ b/modules/services_discovery/udev.c
@@ -0,0 +1,250 @@
+/**
+ * @file udev.c
+ * @brief List of multimedia devices for VLC media player
+ */
+/*****************************************************************************
+ * Copyright © 2009 Rémi Denis-Courmont
+ *
+ * This library 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 library 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ ****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libudev.h>
+#include <vlc_common.h>
+#include <vlc_services_discovery.h>
+#include <vlc_plugin.h>
+#include <poll.h>
+#include <errno.h>
+
+static int  Open (vlc_object_t *);
+static void Close (vlc_object_t *);
+
+/*
+ * Module descriptor
+ */
+vlc_module_begin ()
+    set_shortname (N_("Devices"))
+    set_description (N_("Capture devices"))
+    set_category (CAT_PLAYLIST)
+    set_subcategory (SUBCAT_PLAYLIST_SD)
+    set_capability ("services_discovery", 0)
+    set_callbacks (Open, Close)
+
+    add_shortcut ("udev")
+vlc_module_end ()
+
+struct services_discovery_sys_t
+{
+    struct udev_monitor *monitor;
+    vlc_thread_t         thread;
+};
+
+static void *Run (void *);
+static void HandleDevice (services_discovery_t *, struct udev_device *, bool);
+
+/**
+ * Probes and initializes.
+ */
+static int Open (vlc_object_t *obj)
+{
+    const char subsys[] = "video4linux";
+    services_discovery_t *sd = (services_discovery_t *)obj;
+    services_discovery_sys_t *p_sys = malloc (sizeof (*p_sys));
+
+    if (p_sys == NULL)
+        return VLC_ENOMEM;
+    sd->p_sys = p_sys;
+
+    struct udev_monitor *mon = NULL;
+    struct udev *udev = udev_new ();
+    if (udev == NULL)
+        goto error;
+
+    mon = udev_monitor_new_from_netlink (udev, "udev");
+    if (mon == NULL
+     || udev_monitor_filter_add_match_subsystem_devtype (mon, subsys, NULL))
+        goto error;
+    p_sys->monitor = mon;
+
+    /* Enumerate existing devices */
+    struct udev_enumerate *devenum = udev_enumerate_new (udev);
+    if (devenum == NULL)
+        goto error;
+    if (udev_enumerate_add_match_subsystem (devenum, subsys))
+    {
+        udev_enumerate_unref (devenum);
+        goto error;
+    }
+
+    udev_monitor_enable_receiving (mon);
+    /* Note that we enumerate _after_ monitoring is enabled so that we do not
+     * loose device events occuring while we are enumerating. We could still
+     * loose events if the Netlink socket receive buffer overflows. */
+    udev_enumerate_scan_devices (devenum);
+    struct udev_list_entry *devlist = udev_enumerate_get_list_entry (devenum);
+    struct udev_list_entry *deventry;
+    udev_list_entry_foreach (deventry, devlist)
+    {
+        const char *path = udev_list_entry_get_name (deventry);
+        struct udev_device *dev = udev_device_new_from_syspath (udev, path);
+        HandleDevice (sd, dev, true);
+        udev_device_unref (dev);
+    }
+    udev_enumerate_unref (devenum);
+
+    if (vlc_clone (&p_sys->thread, Run, sd, VLC_THREAD_PRIORITY_LOW))
+        goto error;
+    return VLC_SUCCESS;
+
+error:
+    if (mon != NULL)
+        udev_monitor_unref (mon);
+    if (udev != NULL)
+        udev_unref (udev);
+    free (p_sys);
+    return VLC_EGENERIC;
+}
+
+
+/**
+ * Releases resources
+ */
+static void Close (vlc_object_t *obj)
+{
+    services_discovery_t *sd = (services_discovery_t *)obj;
+    services_discovery_sys_t *p_sys = sd->p_sys;
+    struct udev *udev = udev_monitor_get_udev (p_sys->monitor);
+
+    vlc_cancel (p_sys->thread);
+    vlc_join (p_sys->thread, NULL);
+    udev_monitor_unref (p_sys->monitor);
+    udev_unref (udev);
+    free (p_sys);
+}
+
+
+static void *Run (void *data)
+{
+    services_discovery_t *sd = data;
+    services_discovery_sys_t *p_sys = sd->p_sys;
+    struct udev_monitor *mon = p_sys->monitor;
+
+    int fd = udev_monitor_get_fd (mon);
+    struct pollfd ufd = { .fd = fd, .events = POLLIN, };
+
+    for (;;)
+    {
+        while (poll (&ufd, 1, -1) == -1)
+            if (errno != EINTR)
+                break;
+
+        struct udev_device *dev = udev_monitor_receive_device (mon);
+        if (dev == NULL)
+            continue;
+
+        /* FIXME: handle change, offline, online */
+        if (!strcmp (udev_device_get_action (dev), "add"))
+            HandleDevice (sd, dev, true);
+        else if (!strcmp (udev_device_get_action (dev), "remove"))
+            HandleDevice (sd, dev, false);
+
+        //udev_device_unref (dev);
+    }
+    return NULL;
+}
+
+static int hex (char c)
+{
+    if (c >= '0' && c <= '9')
+        return c - '0';
+    if (c >= 'A' && c <= 'F')
+        return c + 10 - 'A';
+    if (c >= 'a' && c <= 'f')
+        return c + 10 - 'a';
+    return -1;
+}
+
+static char *decode (const char *enc)
+{
+    char *ret = enc ? strdup (enc) : NULL;
+    if (ret == NULL)
+        return NULL;
+
+    char *out = ret;
+    for (const char *in = ret; *in; out++)
+    {
+        int h1, h2;
+
+        if ((in[0] == '\\') && (in[1] == 'x')
+         && ((h1 = hex (in[2])) != -1)
+         && ((h2 = hex (in[3])) != -1))
+        {
+            *out = (h1 << 4) | h2;
+            in += 4;
+        }
+        else
+        {
+            *out = *in;
+            in++;
+        }
+    }
+    *out = 0;
+    return ret;
+}
+
+static char *decode_property (struct udev_device *dev, const char *name)
+{
+    return decode (udev_device_get_property_value (dev, name));
+}
+
+static void HandleDevice (services_discovery_t *sd, struct udev_device *dev,
+                          bool add)
+{
+    //services_discovery_sys_t *p_sys = sd->p_sys;
+    if (!add)
+    {
+        msg_Err (sd, "FIXME: removing device not implemented!");
+        return;
+    }
+
+    const char *scheme = "v4l2"; /* FIXME: V4L v1 */
+    const char *node = udev_device_get_devnode (dev);
+    char *vnd = decode_property (dev, "ID_VENDOR_ENC");
+    char *name = decode_property (dev, "ID_MODEL_ENC");
+
+    char *mrl;
+    if (asprintf (&mrl, "%s://%s", scheme, node) == -1)
+        return;
+
+    /* FIXME: check for duplicates (race between monitor starting to receive
+     * and initial enumeration). */
+    input_item_t *item;
+    item = input_item_NewWithType (VLC_OBJECT (sd), mrl,
+                                   name ? name : "Unnamed",
+                                   0, NULL, 0, -1, ITEM_TYPE_CARD);
+    msg_Dbg (sd, "adding %s", mrl);
+    free (name);
+    free (mrl);
+
+    if (item != NULL)
+    {
+        services_discovery_AddItem (sd, item, vnd ? vnd : "Generic");
+        vlc_gc_decref (item);
+    }
+    free (vnd);
+}




More information about the vlc-devel mailing list