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>
35 static int OpenV4L (vlc_object_t *);
36 static int OpenALSA (vlc_object_t *);
37 static int OpenDisc (vlc_object_t *);
38 static void Close (vlc_object_t *);
39 static int vlc_sd_probe_Open (vlc_object_t *);
45 set_shortname (N_("Video capture"))
46 set_description (N_("Video capture (Video4Linux)"))
47 set_category (CAT_PLAYLIST)
48 set_subcategory (SUBCAT_PLAYLIST_SD)
49 set_capability ("services_discovery", 0)
50 set_callbacks (OpenV4L, Close)
54 set_shortname (N_("Audio capture"))
55 set_description (N_("Audio capture (ALSA)"))
56 set_category (CAT_PLAYLIST)
57 set_subcategory (SUBCAT_PLAYLIST_SD)
58 set_capability ("services_discovery", 0)
59 set_callbacks (OpenALSA, Close)
63 set_shortname (N_("Discs"))
64 set_description (N_("Discs"))
65 set_category (CAT_PLAYLIST)
66 set_subcategory (SUBCAT_PLAYLIST_SD)
67 set_capability ("services_discovery", 0)
68 set_callbacks (OpenDisc, Close)
71 VLC_SD_PROBE_SUBMODULE
75 static int vlc_sd_probe_Open (vlc_object_t *obj)
77 vlc_probe_t *probe = (vlc_probe_t *)obj;
79 struct udev *udev = udev_new ();
81 return VLC_PROBE_CONTINUE;
83 struct udev_monitor *mon = udev_monitor_new_from_netlink (udev, "udev");
86 vlc_sd_probe_Add (probe, "v4l{longname=\"Video capture\"}",
87 N_("Video capture"), SD_CAT_DEVICES);
88 vlc_sd_probe_Add (probe, "alsa{longname=\"Audio capture\"}",
89 N_("Audio capture"), SD_CAT_DEVICES);
90 vlc_sd_probe_Add (probe, "disc{longname=\"Discs\"}", N_("Discs"),
92 udev_monitor_unref (mon);
95 return VLC_PROBE_CONTINUE;
100 dev_t devnum; /* must be first */
102 services_discovery_t *sd;
108 char * (*get_mrl) (struct udev_device *dev);
109 char * (*get_name) (struct udev_device *dev);
110 char * (*get_cat) (struct udev_device *dev);
114 struct services_discovery_sys_t
116 const struct subsys *subsys;
117 struct udev_monitor *monitor;
123 * Compares two devices (to support binary search).
125 static int cmpdev (const void *a, const void *b)
127 const dev_t *da = a, *db = b;
128 dev_t delta = *da - *db;
130 if (sizeof (delta) > sizeof (int))
131 return delta ? ((delta > 0) ? 1 : -1) : 0;
132 return (signed)delta;
135 static void DestroyDevice (void *data)
137 struct device *d = data;
140 services_discovery_RemoveItem (d->sd, d->item);
141 vlc_gc_decref (d->item);
145 static char *decode_property (struct udev_device *, const char *);
148 * Adds a udev device.
150 static int AddDevice (services_discovery_t *sd, struct udev_device *dev)
152 services_discovery_sys_t *p_sys = sd->p_sys;
154 char *mrl = p_sys->subsys->get_mrl (dev);
156 return 0; /* don't know if it was an error... */
157 char *name = p_sys->subsys->get_name (dev);
158 input_item_t *item = input_item_NewWithType (VLC_OBJECT (sd), mrl,
161 p_sys->subsys->item_type);
162 msg_Dbg (sd, "adding %s (%s)", mrl, name);
168 struct device *d = malloc (sizeof (*d));
171 vlc_gc_decref (item);
174 d->devnum = udev_device_get_devnum (dev);
178 struct device **dp = tsearch (d, &p_sys->root, cmpdev);
179 if (dp == NULL) /* Out-of-memory */
184 if (*dp != d) /* Overwrite existing device */
190 name = p_sys->subsys->get_cat (dev);
191 services_discovery_AddItem (sd, item, name ? name : "Generic");
198 * Removes a udev device (if present).
200 static void RemoveDevice (services_discovery_t *sd, struct udev_device *dev)
202 services_discovery_sys_t *p_sys = sd->p_sys;
204 dev_t num = udev_device_get_devnum (dev);
205 struct device **dp = tfind (&(dev_t){ num }, &p_sys->root, cmpdev);
209 struct device *d = *dp;
210 tdelete (d, &p_sys->root, cmpdev);
214 static void *Run (void *);
217 * Probes and initializes.
219 static int Open (vlc_object_t *obj, const struct subsys *subsys)
221 services_discovery_t *sd = (services_discovery_t *)obj;
222 services_discovery_sys_t *p_sys = malloc (sizeof (*p_sys));
227 p_sys->subsys = subsys;
230 struct udev_monitor *mon = NULL;
231 struct udev *udev = udev_new ();
235 mon = udev_monitor_new_from_netlink (udev, "udev");
237 || udev_monitor_filter_add_match_subsystem_devtype (mon, subsys->name,
240 p_sys->monitor = mon;
242 /* Enumerate existing devices */
243 struct udev_enumerate *devenum = udev_enumerate_new (udev);
246 if (udev_enumerate_add_match_subsystem (devenum, subsys->name))
248 udev_enumerate_unref (devenum);
252 udev_monitor_enable_receiving (mon);
253 /* Note that we enumerate _after_ monitoring is enabled so that we do not
254 * loose device events occuring while we are enumerating. We could still
255 * loose events if the Netlink socket receive buffer overflows. */
256 udev_enumerate_scan_devices (devenum);
257 struct udev_list_entry *devlist = udev_enumerate_get_list_entry (devenum);
258 struct udev_list_entry *deventry;
259 udev_list_entry_foreach (deventry, devlist)
261 const char *path = udev_list_entry_get_name (deventry);
262 struct udev_device *dev = udev_device_new_from_syspath (udev, path);
264 udev_device_unref (dev);
266 udev_enumerate_unref (devenum);
268 if (vlc_clone (&p_sys->thread, Run, sd, VLC_THREAD_PRIORITY_LOW))
269 { /* Fallback without thread */
270 udev_monitor_unref (mon);
272 p_sys->monitor = NULL;
278 udev_monitor_unref (mon);
288 static void Close (vlc_object_t *obj)
290 services_discovery_t *sd = (services_discovery_t *)obj;
291 services_discovery_sys_t *p_sys = sd->p_sys;
293 if (p_sys->monitor != NULL)
295 struct udev *udev = udev_monitor_get_udev (p_sys->monitor);
297 vlc_cancel (p_sys->thread);
298 vlc_join (p_sys->thread, NULL);
299 udev_monitor_unref (p_sys->monitor);
303 tdestroy (p_sys->root, DestroyDevice);
307 static void *Run (void *data)
309 services_discovery_t *sd = data;
310 services_discovery_sys_t *p_sys = sd->p_sys;
311 struct udev_monitor *mon = p_sys->monitor;
313 int fd = udev_monitor_get_fd (mon);
314 struct pollfd ufd = { .fd = fd, .events = POLLIN, };
318 while (poll (&ufd, 1, -1) == -1)
322 int canc = vlc_savecancel ();
323 struct udev_device *dev = udev_monitor_receive_device (mon);
327 const char *action = udev_device_get_action (dev);
328 if (!strcmp (action, "add"))
330 else if (!strcmp (action, "remove"))
331 RemoveDevice (sd, dev);
332 else if (!strcmp (action, "change"))
334 RemoveDevice (sd, dev);
337 udev_device_unref (dev);
338 vlc_restorecancel (canc);
344 * Converts an hexadecimal digit to an integer.
346 static int hex (char c)
348 if (c >= '0' && c <= '9')
350 if (c >= 'A' && c <= 'F')
352 if (c >= 'a' && c <= 'f')
358 * Decodes a udev hexadecimal-encoded property.
360 static char *decode (const char *enc)
362 char *ret = enc ? strdup (enc) : NULL;
367 for (const char *in = ret; *in; out++)
371 if ((in[0] == '\\') && (in[1] == 'x')
372 && ((h1 = hex (in[2])) != -1)
373 && ((h2 = hex (in[3])) != -1))
375 *out = (h1 << 4) | h2;
388 static char *decode_property (struct udev_device *dev, const char *name)
390 return decode (udev_device_get_property_value (dev, name));
394 /*** Video4Linux support ***/
395 static bool is_v4l_legacy (struct udev_device *dev)
399 version = udev_device_get_property_value (dev, "ID_V4L_VERSION");
400 return version && !strcmp (version, "1");
403 static char *v4l_get_mrl (struct udev_device *dev)
405 /* Determine media location */
406 const char *scheme = "v4l2";
407 if (is_v4l_legacy (dev))
409 const char *node = udev_device_get_devnode (dev);
412 if (asprintf (&mrl, "%s://%s", scheme, node) == -1)
417 static char *v4l_get_name (struct udev_device *dev)
419 const char *prd = udev_device_get_property_value (dev, "ID_V4L_PRODUCT");
420 return prd ? strdup (prd) : NULL;
423 static char *v4l_get_cat (struct udev_device *dev)
425 return decode_property (dev, "ID_VENDOR_ENC");
428 int OpenV4L (vlc_object_t *obj)
430 static const struct subsys subsys = {
431 "video4linux", v4l_get_mrl, v4l_get_name, v4l_get_cat, ITEM_TYPE_CARD,
434 return Open (obj, &subsys);
438 /*** Advanced Linux Sound Architecture support ***/
439 static int alsa_get_device (struct udev_device *dev, unsigned *restrict pcard,
440 unsigned *restrict pdevice)
442 const char *node = udev_device_get_devpath (dev);
445 node = strrchr (node, '/');
448 if (sscanf (node, "/pcmC%uD%u%c", pcard, pdevice, &type) < 3)
456 static char *alsa_get_mrl (struct udev_device *dev)
458 /* Determine media location */
460 unsigned card, device;
462 if (alsa_get_device (dev, &card, &device))
465 if (asprintf (&mrl, "alsa://plughw:%u,%u", card, device) == -1)
470 static char *alsa_get_name (struct udev_device *dev)
472 const char *model = NULL;
474 unsigned card, device;
476 if (alsa_get_device (dev, &card, &device))
479 dev = udev_device_get_parent (dev);
481 model = udev_device_get_property_value (dev,
482 "ID_MODEL_FROM_DATABASE");
486 if (asprintf (&name, "%s (%u)", model, device) == -1)
491 static char *alsa_get_cat (struct udev_device *dev)
495 dev = udev_device_get_parent (dev);
499 vnd = udev_device_get_property_value (dev, "ID_VENDOR_FROM_DATABASE");
501 /* FIXME: USB may take time to settle... the parent device */
502 vnd = udev_device_get_property_value (dev, "ID_BUS");
503 return vnd ? strdup (vnd) : NULL;
506 int OpenALSA (vlc_object_t *obj)
508 static const struct subsys subsys = {
509 "sound", alsa_get_mrl, alsa_get_name, alsa_get_cat, ITEM_TYPE_CARD,
512 return Open (obj, &subsys);
516 /*** Discs support ***/
517 static char *disc_get_mrl (struct udev_device *dev)
521 val = udev_device_get_property_value (dev, "ID_CDROM");
523 return NULL; /* Ignore non-optical block devices */
525 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_STATE");
526 if ((val == NULL) || !strcmp (val, "blank"))
527 return NULL; /* ignore empty drives and virgin recordable discs */
529 const char *scheme = "file";
530 val = udev_device_get_property_value (dev,
531 "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO");
532 if (val && atoi (val))
533 scheme = "cdda"; /* Audio CD rather than file system */
534 #if 0 /* we can use file:// for DVDs */
535 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_DVD");
536 if (val && atoi (val))
539 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_BD");
540 if (val && atoi (val))
543 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_HDDVD");
544 if (val && atoi (val))
548 val = udev_device_get_devnode (dev);
551 if (asprintf (&mrl, "%s://%s", scheme, val) == -1)
556 static char *disc_get_name (struct udev_device *dev)
558 return decode_property (dev, "ID_FS_LABEL_ENC");
561 static char *disc_get_cat (struct udev_device *dev)
563 struct udev_list_entry *list, *entry;
565 list = udev_device_get_properties_list_entry (dev);
566 if (unlikely(list == NULL))
569 const char *cat = NULL;
570 udev_list_entry_foreach (entry, list)
572 const char *name = udev_list_entry_get_name (entry);
574 if (strncmp (name, "ID_CDROM_MEDIA_", 15))
576 if (!atoi (udev_list_entry_get_value (entry)))
580 if (!strncmp (name, "CD", 2))
582 else if (!strncmp (name, "DVD", 3))
584 else if (!strncmp (name, "BD", 2))
586 else if (!strncmp (name, "HDDVD", 5))
594 cat = N_("Unknown type");
595 return strdup (vlc_gettext (cat));
598 int OpenDisc (vlc_object_t *obj)
600 static const struct subsys subsys = {
601 "block", disc_get_mrl, disc_get_name, disc_get_cat, ITEM_TYPE_DISC,
604 return Open (obj, &subsys);