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 *);
40 static int OpenALSA (vlc_object_t *);
42 static int OpenDisc (vlc_object_t *);
43 static void Close (vlc_object_t *);
44 static int vlc_sd_probe_Open (vlc_object_t *);
50 set_shortname (N_("Video capture"))
51 set_description (N_("Video capture (Video4Linux)"))
52 set_category (CAT_PLAYLIST)
53 set_subcategory (SUBCAT_PLAYLIST_SD)
54 set_capability ("services_discovery", 0)
55 set_callbacks (OpenV4L, Close)
59 set_shortname (N_("Audio capture"))
60 set_description (N_("Audio capture (ALSA)"))
61 set_category (CAT_PLAYLIST)
62 set_subcategory (SUBCAT_PLAYLIST_SD)
63 set_capability ("services_discovery", 0)
64 set_callbacks (OpenALSA, Close)
68 set_shortname (N_("Discs"))
69 set_description (N_("Discs"))
70 set_category (CAT_PLAYLIST)
71 set_subcategory (SUBCAT_PLAYLIST_SD)
72 set_capability ("services_discovery", 0)
73 set_callbacks (OpenDisc, Close)
76 VLC_SD_PROBE_SUBMODULE
80 static int vlc_sd_probe_Open (vlc_object_t *obj)
82 vlc_probe_t *probe = (vlc_probe_t *)obj;
84 struct udev *udev = udev_new ();
86 return VLC_PROBE_CONTINUE;
88 struct udev_monitor *mon = udev_monitor_new_from_netlink (udev, "udev");
91 vlc_sd_probe_Add (probe, "v4l{longname=\"Video capture\"}",
92 N_("Video capture"), SD_CAT_DEVICES);
94 vlc_sd_probe_Add (probe, "alsa{longname=\"Audio capture\"}",
95 N_("Audio capture"), SD_CAT_DEVICES);
97 vlc_sd_probe_Add (probe, "disc{longname=\"Discs\"}", N_("Discs"),
99 udev_monitor_unref (mon);
102 return VLC_PROBE_CONTINUE;
107 dev_t devnum; /* must be first */
109 services_discovery_t *sd;
115 char * (*get_mrl) (struct udev_device *dev);
116 char * (*get_name) (struct udev_device *dev);
117 char * (*get_cat) (struct udev_device *dev);
121 struct services_discovery_sys_t
123 const struct subsys *subsys;
124 struct udev_monitor *monitor;
130 * Compares two devices (to support binary search).
132 static int cmpdev (const void *a, const void *b)
134 const dev_t *da = a, *db = b;
135 dev_t delta = *da - *db;
137 if (sizeof (delta) > sizeof (int))
138 return delta ? ((delta > 0) ? 1 : -1) : 0;
139 return (signed)delta;
142 static void DestroyDevice (void *data)
144 struct device *d = data;
147 services_discovery_RemoveItem (d->sd, d->item);
148 vlc_gc_decref (d->item);
152 static char *decode_property (struct udev_device *, const char *);
155 * Adds a udev device.
157 static int AddDevice (services_discovery_t *sd, struct udev_device *dev)
159 services_discovery_sys_t *p_sys = sd->p_sys;
161 char *mrl = p_sys->subsys->get_mrl (dev);
163 return 0; /* don't know if it was an error... */
164 char *name = p_sys->subsys->get_name (dev);
165 input_item_t *item = input_item_NewWithType (mrl, name ? name : mrl,
167 p_sys->subsys->item_type);
168 msg_Dbg (sd, "adding %s (%s)", mrl, name);
174 struct device *d = malloc (sizeof (*d));
177 vlc_gc_decref (item);
180 d->devnum = udev_device_get_devnum (dev);
184 struct device **dp = tsearch (d, &p_sys->root, cmpdev);
185 if (dp == NULL) /* Out-of-memory */
190 if (*dp != d) /* Overwrite existing device */
196 name = p_sys->subsys->get_cat (dev);
197 services_discovery_AddItem (sd, item, name ? name : "Generic");
204 * Removes a udev device (if present).
206 static void RemoveDevice (services_discovery_t *sd, struct udev_device *dev)
208 services_discovery_sys_t *p_sys = sd->p_sys;
210 dev_t num = udev_device_get_devnum (dev);
211 struct device **dp = tfind (&(dev_t){ num }, &p_sys->root, cmpdev);
215 struct device *d = *dp;
216 tdelete (d, &p_sys->root, cmpdev);
220 static void *Run (void *);
223 * Probes and initializes.
225 static int Open (vlc_object_t *obj, const struct subsys *subsys)
227 services_discovery_t *sd = (services_discovery_t *)obj;
228 services_discovery_sys_t *p_sys = malloc (sizeof (*p_sys));
233 p_sys->subsys = subsys;
236 struct udev_monitor *mon = NULL;
237 struct udev *udev = udev_new ();
241 mon = udev_monitor_new_from_netlink (udev, "udev");
243 || udev_monitor_filter_add_match_subsystem_devtype (mon, subsys->name,
246 p_sys->monitor = mon;
248 /* Enumerate existing devices */
249 struct udev_enumerate *devenum = udev_enumerate_new (udev);
252 if (udev_enumerate_add_match_subsystem (devenum, subsys->name))
254 udev_enumerate_unref (devenum);
258 udev_monitor_enable_receiving (mon);
259 /* Note that we enumerate _after_ monitoring is enabled so that we do not
260 * loose device events occuring while we are enumerating. We could still
261 * loose events if the Netlink socket receive buffer overflows. */
262 udev_enumerate_scan_devices (devenum);
263 struct udev_list_entry *devlist = udev_enumerate_get_list_entry (devenum);
264 struct udev_list_entry *deventry;
265 udev_list_entry_foreach (deventry, devlist)
267 const char *path = udev_list_entry_get_name (deventry);
268 struct udev_device *dev = udev_device_new_from_syspath (udev, path);
270 udev_device_unref (dev);
272 udev_enumerate_unref (devenum);
274 if (vlc_clone (&p_sys->thread, Run, sd, VLC_THREAD_PRIORITY_LOW))
275 { /* Fallback without thread */
276 udev_monitor_unref (mon);
278 p_sys->monitor = NULL;
284 udev_monitor_unref (mon);
294 static void Close (vlc_object_t *obj)
296 services_discovery_t *sd = (services_discovery_t *)obj;
297 services_discovery_sys_t *p_sys = sd->p_sys;
299 if (p_sys->monitor != NULL)
301 struct udev *udev = udev_monitor_get_udev (p_sys->monitor);
303 vlc_cancel (p_sys->thread);
304 vlc_join (p_sys->thread, NULL);
305 udev_monitor_unref (p_sys->monitor);
309 tdestroy (p_sys->root, DestroyDevice);
313 static void *Run (void *data)
315 services_discovery_t *sd = data;
316 services_discovery_sys_t *p_sys = sd->p_sys;
317 struct udev_monitor *mon = p_sys->monitor;
319 int fd = udev_monitor_get_fd (mon);
320 struct pollfd ufd = { .fd = fd, .events = POLLIN, };
324 while (poll (&ufd, 1, -1) == -1)
328 int canc = vlc_savecancel ();
329 struct udev_device *dev = udev_monitor_receive_device (mon);
333 const char *action = udev_device_get_action (dev);
334 if (!strcmp (action, "add"))
336 else if (!strcmp (action, "remove"))
337 RemoveDevice (sd, dev);
338 else if (!strcmp (action, "change"))
340 RemoveDevice (sd, dev);
343 udev_device_unref (dev);
344 vlc_restorecancel (canc);
350 * Converts an hexadecimal digit to an integer.
352 static int hex (char c)
354 if (c >= '0' && c <= '9')
356 if (c >= 'A' && c <= 'F')
358 if (c >= 'a' && c <= 'f')
364 * Decodes a udev hexadecimal-encoded property.
366 static char *decode (const char *enc)
368 char *ret = enc ? strdup (enc) : NULL;
373 for (const char *in = ret; *in; out++)
377 if ((in[0] == '\\') && (in[1] == 'x')
378 && ((h1 = hex (in[2])) != -1)
379 && ((h2 = hex (in[3])) != -1))
381 *out = (h1 << 4) | h2;
394 static char *decode_property (struct udev_device *dev, const char *name)
396 return decode (udev_device_get_property_value (dev, name));
400 /*** Video4Linux support ***/
401 static bool is_v4l_legacy (struct udev_device *dev)
405 version = udev_device_get_property_value (dev, "ID_V4L_VERSION");
406 return version && !strcmp (version, "1");
409 static char *v4l_get_mrl (struct udev_device *dev)
411 /* Determine media location */
412 if (is_v4l_legacy (dev))
415 const char *node = udev_device_get_devnode (dev);
418 if (asprintf (&mrl, "v4l2://%s", node) == -1)
423 static char *v4l_get_name (struct udev_device *dev)
425 const char *prd = udev_device_get_property_value (dev, "ID_V4L_PRODUCT");
426 return prd ? strdup (prd) : NULL;
429 static char *v4l_get_cat (struct udev_device *dev)
431 return decode_property (dev, "ID_VENDOR_ENC");
434 int OpenV4L (vlc_object_t *obj)
436 static const struct subsys subsys = {
437 "video4linux", v4l_get_mrl, v4l_get_name, v4l_get_cat, ITEM_TYPE_CARD,
440 return Open (obj, &subsys);
445 /*** Advanced Linux Sound Architecture support ***/
446 #include <alsa/asoundlib.h>
448 static int alsa_get_device (struct udev_device *dev, unsigned *restrict pcard,
449 unsigned *restrict pdevice)
451 const char *node = udev_device_get_devpath (dev);
454 node = strrchr (node, '/');
457 if (sscanf (node, "/pcmC%uD%u%c", pcard, pdevice, &type) < 3)
465 static char *alsa_get_mrl (struct udev_device *dev)
467 /* Determine media location */
469 unsigned card, device;
471 if (alsa_get_device (dev, &card, &device))
474 if (asprintf (&mrl, "alsa://plughw:%u,%u", card, device) == -1)
479 static char *alsa_get_name (struct udev_device *dev)
482 unsigned card, device;
484 if (alsa_get_device (dev, &card, &device))
487 char card_name[4 + 3 * sizeof (int)];
488 snprintf (card_name, sizeof (card_name), "hw:%u", card);
491 if (snd_ctl_open (&ctl, card_name, 0))
494 snd_pcm_info_t *pcm_info;
495 snd_pcm_info_alloca (&pcm_info);
496 snd_pcm_info_set_device (pcm_info, device);
497 snd_pcm_info_set_subdevice (pcm_info, 0);
498 snd_pcm_info_set_stream (pcm_info, SND_PCM_STREAM_CAPTURE);
499 if (snd_ctl_pcm_info (ctl, pcm_info))
502 name = strdup (snd_pcm_info_get_name (pcm_info));
508 static char *alsa_get_cat (struct udev_device *dev)
512 dev = udev_device_get_parent (dev);
516 vnd = udev_device_get_property_value (dev, "ID_VENDOR_FROM_DATABASE");
518 /* FIXME: USB may take time to settle... the parent device */
519 vnd = udev_device_get_property_value (dev, "ID_BUS");
520 return vnd ? strdup (vnd) : NULL;
523 int OpenALSA (vlc_object_t *obj)
525 static const struct subsys subsys = {
526 "sound", alsa_get_mrl, alsa_get_name, alsa_get_cat, ITEM_TYPE_CARD,
529 return Open (obj, &subsys);
531 #endif /* HAVE_ALSA */
534 /*** Discs support ***/
535 static char *disc_get_mrl (struct udev_device *dev)
539 val = udev_device_get_property_value (dev, "ID_CDROM");
541 return NULL; /* Ignore non-optical block devices */
543 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_STATE");
544 if (val && !strcmp (val, "blank"))
545 return NULL; /* ignore empty drives and virgin recordable discs */
547 const char *scheme = NULL;
548 val = udev_device_get_property_value (dev,
549 "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO");
550 if (val && atoi (val))
551 scheme = "cdda"; /* Audio CD rather than file system */
552 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_DVD");
553 if (val && atoi (val))
556 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_BD");
557 if (val && atoi (val))
560 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_HDDVD");
561 if (val && atoi (val))
565 /* We didn't get any property that could tell we have optical disc
570 val = udev_device_get_devnode (dev);
571 return make_URI (val, scheme);
574 static char *disc_get_name (struct udev_device *dev)
576 return decode_property (dev, "ID_FS_LABEL_ENC");
579 static char *disc_get_cat (struct udev_device *dev)
581 struct udev_list_entry *list, *entry;
583 list = udev_device_get_properties_list_entry (dev);
584 if (unlikely(list == NULL))
587 const char *cat = NULL;
588 udev_list_entry_foreach (entry, list)
590 const char *name = udev_list_entry_get_name (entry);
592 if (strncmp (name, "ID_CDROM_MEDIA_", 15))
594 if (!atoi (udev_list_entry_get_value (entry)))
598 if (!strncmp (name, "CD", 2))
600 else if (!strncmp (name, "DVD", 3))
602 else if (!strncmp (name, "BD", 2))
604 else if (!strncmp (name, "HDDVD", 5))
612 cat = N_("Unknown type");
613 return strdup (vlc_gettext (cat));
616 int OpenDisc (vlc_object_t *obj)
618 static const struct subsys subsys = {
619 "block", disc_get_mrl, disc_get_name, disc_get_cat, ITEM_TYPE_DISC,
622 return Open (obj, &subsys);