3 * @brief List of multimedia devices for VLC media player
5 /*****************************************************************************
6 * Copyright © 2009 Rémi Denis-Courmont
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1
11 * of the License, or (at your option) any later version.
13 * This library 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 General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 ****************************************************************************/
28 #include <vlc_common.h>
29 #include <vlc_services_discovery.h>
30 #include <vlc_plugin.h>
38 static int OpenV4L (vlc_object_t *);
39 static int OpenALSA (vlc_object_t *);
40 static int OpenDisc (vlc_object_t *);
41 static void Close (vlc_object_t *);
42 static int vlc_sd_probe_Open (vlc_object_t *);
48 set_shortname (N_("Video capture"))
49 set_description (N_("Video capture (Video4Linux)"))
50 set_category (CAT_PLAYLIST)
51 set_subcategory (SUBCAT_PLAYLIST_SD)
52 set_capability ("services_discovery", 0)
53 set_callbacks (OpenV4L, Close)
57 set_shortname (N_("Audio capture"))
58 set_description (N_("Audio capture (ALSA)"))
59 set_category (CAT_PLAYLIST)
60 set_subcategory (SUBCAT_PLAYLIST_SD)
61 set_capability ("services_discovery", 0)
62 set_callbacks (OpenALSA, Close)
66 set_shortname (N_("Discs"))
67 set_description (N_("Discs"))
68 set_category (CAT_PLAYLIST)
69 set_subcategory (SUBCAT_PLAYLIST_SD)
70 set_capability ("services_discovery", 0)
71 set_callbacks (OpenDisc, Close)
74 VLC_SD_PROBE_SUBMODULE
78 static int vlc_sd_probe_Open (vlc_object_t *obj)
80 vlc_probe_t *probe = (vlc_probe_t *)obj;
82 struct udev *udev = udev_new ();
84 return VLC_PROBE_CONTINUE;
86 struct udev_monitor *mon = udev_monitor_new_from_netlink (udev, "udev");
89 vlc_sd_probe_Add (probe, "v4l{longname=\"Video capture\"}",
90 N_("Video capture"), SD_CAT_DEVICES);
91 vlc_sd_probe_Add (probe, "alsa{longname=\"Audio capture\"}",
92 N_("Audio capture"), SD_CAT_DEVICES);
93 vlc_sd_probe_Add (probe, "disc{longname=\"Discs\"}", N_("Discs"),
95 udev_monitor_unref (mon);
98 return VLC_PROBE_CONTINUE;
103 dev_t devnum; /* must be first */
105 services_discovery_t *sd;
111 char * (*get_mrl) (struct udev_device *dev);
112 char * (*get_name) (struct udev_device *dev);
113 char * (*get_cat) (struct udev_device *dev);
117 struct services_discovery_sys_t
119 const struct subsys *subsys;
120 struct udev_monitor *monitor;
126 * Compares two devices (to support binary search).
128 static int cmpdev (const void *a, const void *b)
130 const dev_t *da = a, *db = b;
131 dev_t delta = *da - *db;
133 if (sizeof (delta) > sizeof (int))
134 return delta ? ((delta > 0) ? 1 : -1) : 0;
135 return (signed)delta;
138 static void DestroyDevice (void *data)
140 struct device *d = data;
143 services_discovery_RemoveItem (d->sd, d->item);
144 vlc_gc_decref (d->item);
148 static char *decode_property (struct udev_device *, const char *);
151 * Adds a udev device.
153 static int AddDevice (services_discovery_t *sd, struct udev_device *dev)
155 services_discovery_sys_t *p_sys = sd->p_sys;
157 char *mrl = p_sys->subsys->get_mrl (dev);
159 return 0; /* don't know if it was an error... */
160 char *name = p_sys->subsys->get_name (dev);
161 input_item_t *item = input_item_NewWithType (VLC_OBJECT (sd), mrl,
164 p_sys->subsys->item_type);
165 msg_Dbg (sd, "adding %s (%s)", mrl, name);
171 struct device *d = malloc (sizeof (*d));
174 vlc_gc_decref (item);
177 d->devnum = udev_device_get_devnum (dev);
181 struct device **dp = tsearch (d, &p_sys->root, cmpdev);
182 if (dp == NULL) /* Out-of-memory */
187 if (*dp != d) /* Overwrite existing device */
193 name = p_sys->subsys->get_cat (dev);
194 services_discovery_AddItem (sd, item, name ? name : "Generic");
201 * Removes a udev device (if present).
203 static void RemoveDevice (services_discovery_t *sd, struct udev_device *dev)
205 services_discovery_sys_t *p_sys = sd->p_sys;
207 dev_t num = udev_device_get_devnum (dev);
208 struct device **dp = tfind (&(dev_t){ num }, &p_sys->root, cmpdev);
212 struct device *d = *dp;
213 tdelete (d, &p_sys->root, cmpdev);
217 static void *Run (void *);
220 * Probes and initializes.
222 static int Open (vlc_object_t *obj, const struct subsys *subsys)
224 services_discovery_t *sd = (services_discovery_t *)obj;
225 services_discovery_sys_t *p_sys = malloc (sizeof (*p_sys));
230 p_sys->subsys = subsys;
233 struct udev_monitor *mon = NULL;
234 struct udev *udev = udev_new ();
238 mon = udev_monitor_new_from_netlink (udev, "udev");
240 || udev_monitor_filter_add_match_subsystem_devtype (mon, subsys->name,
243 p_sys->monitor = mon;
245 /* Enumerate existing devices */
246 struct udev_enumerate *devenum = udev_enumerate_new (udev);
249 if (udev_enumerate_add_match_subsystem (devenum, subsys->name))
251 udev_enumerate_unref (devenum);
255 udev_monitor_enable_receiving (mon);
256 /* Note that we enumerate _after_ monitoring is enabled so that we do not
257 * loose device events occuring while we are enumerating. We could still
258 * loose events if the Netlink socket receive buffer overflows. */
259 udev_enumerate_scan_devices (devenum);
260 struct udev_list_entry *devlist = udev_enumerate_get_list_entry (devenum);
261 struct udev_list_entry *deventry;
262 udev_list_entry_foreach (deventry, devlist)
264 const char *path = udev_list_entry_get_name (deventry);
265 struct udev_device *dev = udev_device_new_from_syspath (udev, path);
267 udev_device_unref (dev);
269 udev_enumerate_unref (devenum);
271 if (vlc_clone (&p_sys->thread, Run, sd, VLC_THREAD_PRIORITY_LOW))
272 { /* Fallback without thread */
273 udev_monitor_unref (mon);
275 p_sys->monitor = NULL;
281 udev_monitor_unref (mon);
291 static void Close (vlc_object_t *obj)
293 services_discovery_t *sd = (services_discovery_t *)obj;
294 services_discovery_sys_t *p_sys = sd->p_sys;
296 if (p_sys->monitor != NULL)
298 struct udev *udev = udev_monitor_get_udev (p_sys->monitor);
300 vlc_cancel (p_sys->thread);
301 vlc_join (p_sys->thread, NULL);
302 udev_monitor_unref (p_sys->monitor);
306 tdestroy (p_sys->root, DestroyDevice);
310 static void *Run (void *data)
312 services_discovery_t *sd = data;
313 services_discovery_sys_t *p_sys = sd->p_sys;
314 struct udev_monitor *mon = p_sys->monitor;
316 int fd = udev_monitor_get_fd (mon);
317 struct pollfd ufd = { .fd = fd, .events = POLLIN, };
321 while (poll (&ufd, 1, -1) == -1)
325 int canc = vlc_savecancel ();
326 struct udev_device *dev = udev_monitor_receive_device (mon);
330 const char *action = udev_device_get_action (dev);
331 if (!strcmp (action, "add"))
333 else if (!strcmp (action, "remove"))
334 RemoveDevice (sd, dev);
335 else if (!strcmp (action, "change"))
337 RemoveDevice (sd, dev);
340 udev_device_unref (dev);
341 vlc_restorecancel (canc);
347 * Converts an hexadecimal digit to an integer.
349 static int hex (char c)
351 if (c >= '0' && c <= '9')
353 if (c >= 'A' && c <= 'F')
355 if (c >= 'a' && c <= 'f')
361 * Decodes a udev hexadecimal-encoded property.
363 static char *decode (const char *enc)
365 char *ret = enc ? strdup (enc) : NULL;
370 for (const char *in = ret; *in; out++)
374 if ((in[0] == '\\') && (in[1] == 'x')
375 && ((h1 = hex (in[2])) != -1)
376 && ((h2 = hex (in[3])) != -1))
378 *out = (h1 << 4) | h2;
391 static char *decode_property (struct udev_device *dev, const char *name)
393 return decode (udev_device_get_property_value (dev, name));
397 /*** Video4Linux support ***/
398 static bool is_v4l_legacy (struct udev_device *dev)
402 version = udev_device_get_property_value (dev, "ID_V4L_VERSION");
403 return version && !strcmp (version, "1");
406 static char *v4l_get_mrl (struct udev_device *dev)
408 /* Determine media location */
409 const char *scheme = "v4l2";
410 if (is_v4l_legacy (dev))
412 const char *node = udev_device_get_devnode (dev);
415 if (asprintf (&mrl, "%s://%s", scheme, node) == -1)
420 static char *v4l_get_name (struct udev_device *dev)
422 const char *prd = udev_device_get_property_value (dev, "ID_V4L_PRODUCT");
423 return prd ? strdup (prd) : NULL;
426 static char *v4l_get_cat (struct udev_device *dev)
428 return decode_property (dev, "ID_VENDOR_ENC");
431 int OpenV4L (vlc_object_t *obj)
433 static const struct subsys subsys = {
434 "video4linux", v4l_get_mrl, v4l_get_name, v4l_get_cat, ITEM_TYPE_CARD,
437 return Open (obj, &subsys);
442 /*** Advanced Linux Sound Architecture support ***/
443 #include <alsa/asoundlib.h>
445 static int alsa_get_device (struct udev_device *dev, unsigned *restrict pcard,
446 unsigned *restrict pdevice)
448 const char *node = udev_device_get_devpath (dev);
451 node = strrchr (node, '/');
454 if (sscanf (node, "/pcmC%uD%u%c", pcard, pdevice, &type) < 3)
462 static char *alsa_get_mrl (struct udev_device *dev)
464 /* Determine media location */
466 unsigned card, device;
468 if (alsa_get_device (dev, &card, &device))
471 if (asprintf (&mrl, "alsa://plughw:%u,%u", card, device) == -1)
476 static char *alsa_get_name (struct udev_device *dev)
479 unsigned card, device;
481 if (alsa_get_device (dev, &card, &device))
484 char card_name[4 + 3 * sizeof (int)];
485 snprintf (card_name, sizeof (card_name), "hw:%u", card);
488 if (snd_ctl_open (&ctl, card_name, 0))
491 snd_pcm_info_t *pcm_info;
492 snd_pcm_info_alloca (&pcm_info);
493 snd_pcm_info_set_device (pcm_info, device);
494 snd_pcm_info_set_subdevice (pcm_info, 0);
495 snd_pcm_info_set_stream (pcm_info, SND_PCM_STREAM_CAPTURE);
496 if (snd_ctl_pcm_info (ctl, pcm_info))
499 name = strdup (snd_pcm_info_get_name (pcm_info));
505 static char *alsa_get_cat (struct udev_device *dev)
509 dev = udev_device_get_parent (dev);
513 vnd = udev_device_get_property_value (dev, "ID_VENDOR_FROM_DATABASE");
515 /* FIXME: USB may take time to settle... the parent device */
516 vnd = udev_device_get_property_value (dev, "ID_BUS");
517 return vnd ? strdup (vnd) : NULL;
520 int OpenALSA (vlc_object_t *obj)
522 static const struct subsys subsys = {
523 "sound", alsa_get_mrl, alsa_get_name, alsa_get_cat, ITEM_TYPE_CARD,
526 return Open (obj, &subsys);
528 #endif /* HAVE_ALSA */
531 /*** Discs support ***/
532 static char *disc_get_mrl (struct udev_device *dev)
536 val = udev_device_get_property_value (dev, "ID_CDROM");
538 return NULL; /* Ignore non-optical block devices */
540 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_STATE");
541 if (val && !strcmp (val, "blank"))
542 return NULL; /* ignore empty drives and virgin recordable discs */
544 const char *scheme = NULL;
545 val = udev_device_get_property_value (dev,
546 "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO");
547 if (val && atoi (val))
548 scheme = "cdda"; /* Audio CD rather than file system */
549 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_DVD");
550 if (val && atoi (val))
553 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_BD");
554 if (val && atoi (val))
557 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_HDDVD");
558 if (val && atoi (val))
562 /* We didn't get any property that could tell we have optical disc
567 val = udev_device_get_devnode (dev);
568 return make_URI (val, scheme);
571 static char *disc_get_name (struct udev_device *dev)
573 return decode_property (dev, "ID_FS_LABEL_ENC");
576 static char *disc_get_cat (struct udev_device *dev)
578 struct udev_list_entry *list, *entry;
580 list = udev_device_get_properties_list_entry (dev);
581 if (unlikely(list == NULL))
584 const char *cat = NULL;
585 udev_list_entry_foreach (entry, list)
587 const char *name = udev_list_entry_get_name (entry);
589 if (strncmp (name, "ID_CDROM_MEDIA_", 15))
591 if (!atoi (udev_list_entry_get_value (entry)))
595 if (!strncmp (name, "CD", 2))
597 else if (!strncmp (name, "DVD", 3))
599 else if (!strncmp (name, "BD", 2))
601 else if (!strncmp (name, "HDDVD", 5))
609 cat = N_("Unknown type");
610 return strdup (vlc_gettext (cat));
613 int OpenDisc (vlc_object_t *obj)
615 static const struct subsys subsys = {
616 "block", disc_get_mrl, disc_get_name, disc_get_cat, ITEM_TYPE_DISC,
619 return Open (obj, &subsys);