3 * @brief List of multimedia devices for VLC media player
5 /*****************************************************************************
6 * Copyright © 2009 Rémi Denis-Courmont
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program 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 Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_services_discovery.h>
37 #include <vlc_plugin.h>
39 # include <vlc_modules.h>
43 static int OpenV4L (vlc_object_t *);
45 static int OpenALSA (vlc_object_t *);
47 static int OpenDisc (vlc_object_t *);
48 static void Close (vlc_object_t *);
49 static int vlc_sd_probe_Open (vlc_object_t *);
55 set_shortname (N_("Video capture"))
56 set_description (N_("Video capture (Video4Linux)"))
57 set_category (CAT_PLAYLIST)
58 set_subcategory (SUBCAT_PLAYLIST_SD)
59 set_capability ("services_discovery", 0)
60 set_callbacks (OpenV4L, Close)
61 add_shortcut ("v4l", "video")
64 set_shortname (N_("Audio capture"))
65 set_description (N_("Audio capture (ALSA)"))
66 set_category (CAT_PLAYLIST)
67 set_subcategory (SUBCAT_PLAYLIST_SD)
68 set_capability ("services_discovery", 0)
69 set_callbacks (OpenALSA, Close)
70 add_shortcut ("alsa", "audio")
73 set_shortname (N_("Discs"))
74 set_description (N_("Discs"))
75 set_category (CAT_PLAYLIST)
76 set_subcategory (SUBCAT_PLAYLIST_SD)
77 set_capability ("services_discovery", 0)
78 set_callbacks (OpenDisc, Close)
81 VLC_SD_PROBE_SUBMODULE
85 static int vlc_sd_probe_Open (vlc_object_t *obj)
87 vlc_probe_t *probe = (vlc_probe_t *)obj;
89 struct udev *udev = udev_new ();
91 return VLC_PROBE_CONTINUE;
93 struct udev_monitor *mon = udev_monitor_new_from_netlink (udev, "udev");
96 vlc_sd_probe_Add (probe, "v4l{longname=\"Video capture\"}",
97 N_("Video capture"), SD_CAT_DEVICES);
99 if (!module_exists ("pulselist"))
100 vlc_sd_probe_Add (probe, "alsa{longname=\"Audio capture\"}",
101 N_("Audio capture"), SD_CAT_DEVICES);
103 vlc_sd_probe_Add (probe, "disc{longname=\"Discs\"}", N_("Discs"),
105 udev_monitor_unref (mon);
108 return VLC_PROBE_CONTINUE;
113 dev_t devnum; /* must be first */
115 services_discovery_t *sd;
121 char * (*get_mrl) (struct udev_device *dev);
122 char * (*get_name) (struct udev_device *dev);
126 struct services_discovery_sys_t
128 const struct subsys *subsys;
129 struct udev_monitor *monitor;
135 * Compares two devices (to support binary search).
137 static int cmpdev (const void *a, const void *b)
139 const dev_t *da = a, *db = b;
148 static void DestroyDevice (void *data)
150 struct device *d = data;
153 services_discovery_RemoveItem (d->sd, d->item);
154 vlc_gc_decref (d->item);
158 static char *decode_property (struct udev_device *, const char *);
161 * Adds a udev device.
163 static int AddDevice (services_discovery_t *sd, struct udev_device *dev)
165 services_discovery_sys_t *p_sys = sd->p_sys;
167 char *mrl = p_sys->subsys->get_mrl (dev);
169 return 0; /* don't know if it was an error... */
170 char *name = p_sys->subsys->get_name (dev);
171 input_item_t *item = input_item_NewWithType (mrl, name ? name : mrl,
173 p_sys->subsys->item_type);
174 msg_Dbg (sd, "adding %s (%s)", mrl, name);
180 struct device *d = malloc (sizeof (*d));
183 vlc_gc_decref (item);
186 d->devnum = udev_device_get_devnum (dev);
190 struct device **dp = tsearch (d, &p_sys->root, cmpdev);
191 if (dp == NULL) /* Out-of-memory */
196 if (*dp != d) /* Overwrite existing device */
202 services_discovery_AddItem (sd, item, NULL);
208 * Removes a udev device (if present).
210 static void RemoveDevice (services_discovery_t *sd, struct udev_device *dev)
212 services_discovery_sys_t *p_sys = sd->p_sys;
214 dev_t num = udev_device_get_devnum (dev);
215 struct device **dp = tfind (&(dev_t){ num }, &p_sys->root, cmpdev);
219 struct device *d = *dp;
220 tdelete (d, &p_sys->root, cmpdev);
224 static void *Run (void *);
227 * Probes and initializes.
229 static int Open (vlc_object_t *obj, const struct subsys *subsys)
231 services_discovery_t *sd = (services_discovery_t *)obj;
232 services_discovery_sys_t *p_sys = malloc (sizeof (*p_sys));
237 p_sys->subsys = subsys;
240 struct udev_monitor *mon = NULL;
241 struct udev *udev = udev_new ();
245 mon = udev_monitor_new_from_netlink (udev, "udev");
247 || udev_monitor_filter_add_match_subsystem_devtype (mon, subsys->name,
250 p_sys->monitor = mon;
252 /* Enumerate existing devices */
253 struct udev_enumerate *devenum = udev_enumerate_new (udev);
256 if (udev_enumerate_add_match_subsystem (devenum, subsys->name))
258 udev_enumerate_unref (devenum);
262 udev_monitor_enable_receiving (mon);
263 /* Note that we enumerate _after_ monitoring is enabled so that we do not
264 * loose device events occuring while we are enumerating. We could still
265 * loose events if the Netlink socket receive buffer overflows. */
266 udev_enumerate_scan_devices (devenum);
267 struct udev_list_entry *devlist = udev_enumerate_get_list_entry (devenum);
268 struct udev_list_entry *deventry;
269 udev_list_entry_foreach (deventry, devlist)
271 const char *path = udev_list_entry_get_name (deventry);
272 struct udev_device *dev = udev_device_new_from_syspath (udev, path);
274 udev_device_unref (dev);
276 udev_enumerate_unref (devenum);
278 if (vlc_clone (&p_sys->thread, Run, sd, VLC_THREAD_PRIORITY_LOW))
279 { /* Fallback without thread */
280 udev_monitor_unref (mon);
282 p_sys->monitor = NULL;
288 udev_monitor_unref (mon);
298 static void Close (vlc_object_t *obj)
300 services_discovery_t *sd = (services_discovery_t *)obj;
301 services_discovery_sys_t *p_sys = sd->p_sys;
303 if (p_sys->monitor != NULL)
305 struct udev *udev = udev_monitor_get_udev (p_sys->monitor);
307 vlc_cancel (p_sys->thread);
308 vlc_join (p_sys->thread, NULL);
309 udev_monitor_unref (p_sys->monitor);
313 tdestroy (p_sys->root, DestroyDevice);
317 static void *Run (void *data)
319 services_discovery_t *sd = data;
320 services_discovery_sys_t *p_sys = sd->p_sys;
321 struct udev_monitor *mon = p_sys->monitor;
323 int fd = udev_monitor_get_fd (mon);
324 struct pollfd ufd = { .fd = fd, .events = POLLIN, };
328 while (poll (&ufd, 1, -1) == -1)
332 int canc = vlc_savecancel ();
333 struct udev_device *dev = udev_monitor_receive_device (mon);
337 const char *action = udev_device_get_action (dev);
338 if (!strcmp (action, "add"))
340 else if (!strcmp (action, "remove"))
341 RemoveDevice (sd, dev);
342 else if (!strcmp (action, "change"))
344 RemoveDevice (sd, dev);
347 udev_device_unref (dev);
348 vlc_restorecancel (canc);
354 * Converts an hexadecimal digit to an integer.
356 static int hex (char c)
358 if (c >= '0' && c <= '9')
360 if (c >= 'A' && c <= 'F')
362 if (c >= 'a' && c <= 'f')
368 * Decodes a udev hexadecimal-encoded property.
370 static char *decode (const char *enc)
372 char *ret = enc ? strdup (enc) : NULL;
377 for (const char *in = ret; *in; out++)
381 if ((in[0] == '\\') && (in[1] == 'x')
382 && ((h1 = hex (in[2])) != -1)
383 && ((h2 = hex (in[3])) != -1))
385 *out = (h1 << 4) | h2;
398 static char *decode_property (struct udev_device *dev, const char *name)
400 return decode (udev_device_get_property_value (dev, name));
404 /*** Video4Linux support ***/
405 static bool v4l_is_legacy (struct udev_device *dev)
409 version = udev_device_get_property_value (dev, "ID_V4L_VERSION");
410 return (version != NULL) && !strcmp (version, "1");
413 static bool v4l_can_capture (struct udev_device *dev)
417 caps = udev_device_get_property_value (dev, "ID_V4L_CAPABILITIES");
418 return (caps != NULL) && (strstr (caps, ":capture:") != NULL);
421 static char *v4l_get_mrl (struct udev_device *dev)
423 /* Determine media location */
424 if (v4l_is_legacy (dev) || !v4l_can_capture (dev))
427 const char *node = udev_device_get_devnode (dev);
430 if (asprintf (&mrl, "v4l2://%s", node) == -1)
435 static char *v4l_get_name (struct udev_device *dev)
437 const char *prd = udev_device_get_property_value (dev, "ID_V4L_PRODUCT");
438 return prd ? strdup (prd) : NULL;
441 int OpenV4L (vlc_object_t *obj)
443 static const struct subsys subsys = {
444 "video4linux", v4l_get_mrl, v4l_get_name, ITEM_TYPE_CARD,
447 return Open (obj, &subsys);
452 /*** Advanced Linux Sound Architecture support ***/
453 #include <alsa/asoundlib.h>
455 static int alsa_get_device (struct udev_device *dev, unsigned *restrict pcard,
456 unsigned *restrict pdevice)
458 const char *node = udev_device_get_devpath (dev);
461 node = strrchr (node, '/');
464 if (sscanf (node, "/pcmC%uD%u%c", pcard, pdevice, &type) < 3)
472 static char *alsa_get_mrl (struct udev_device *dev)
474 /* Determine media location */
476 unsigned card, device;
478 if (alsa_get_device (dev, &card, &device))
481 if (asprintf (&mrl, "alsa://plughw:%u,%u", card, device) == -1)
486 static char *alsa_get_name (struct udev_device *dev)
489 unsigned card, device;
491 if (alsa_get_device (dev, &card, &device))
494 char card_name[4 + 3 * sizeof (int)];
495 snprintf (card_name, sizeof (card_name), "hw:%u", card);
498 if (snd_ctl_open (&ctl, card_name, 0))
501 snd_pcm_info_t *pcm_info;
502 snd_pcm_info_alloca (&pcm_info);
503 snd_pcm_info_set_device (pcm_info, device);
504 snd_pcm_info_set_subdevice (pcm_info, 0);
505 snd_pcm_info_set_stream (pcm_info, SND_PCM_STREAM_CAPTURE);
506 if (snd_ctl_pcm_info (ctl, pcm_info))
509 name = strdup (snd_pcm_info_get_name (pcm_info));
515 int OpenALSA (vlc_object_t *obj)
517 static const struct subsys subsys = {
518 "sound", alsa_get_mrl, alsa_get_name, ITEM_TYPE_CARD,
521 return Open (obj, &subsys);
523 #endif /* HAVE_ALSA */
526 /*** Discs support ***/
527 static char *disc_get_mrl (struct udev_device *dev)
529 const char *node = udev_device_get_devnode (dev);
532 val = udev_device_get_property_value (dev, "ID_CDROM");
534 return NULL; /* Ignore non-optical block devices */
536 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_STATE");
538 { /* Force probing of the disc in the drive if any. */
539 int fd = open (node, O_RDONLY|O_CLOEXEC);
544 if (!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 return vlc_path2uri (node, scheme);
573 static char *disc_get_name (struct udev_device *dev)
576 struct udev_list_entry *list, *entry;
578 list = udev_device_get_properties_list_entry (dev);
579 if (unlikely(list == NULL))
582 const char *cat = NULL;
583 udev_list_entry_foreach (entry, list)
585 const char *name = udev_list_entry_get_name (entry);
587 if (strncmp (name, "ID_CDROM_MEDIA_", 15))
589 if (!atoi (udev_list_entry_get_value (entry)))
593 if (!strncmp (name, "CD", 2))
595 else if (!strncmp (name, "DVD", 3))
597 else if (!strncmp (name, "BD", 2))
599 else if (!strncmp (name, "HDDVD", 5))
607 cat = N_("Unknown type");
609 char *label = decode_property (dev, "ID_FS_LABEL_ENC");
612 if (asprintf(&name, "%s (%s)", label, vlc_gettext(cat)) < 0)
619 int OpenDisc (vlc_object_t *obj)
621 static const struct subsys subsys = {
622 "block", disc_get_mrl, disc_get_name, ITEM_TYPE_DISC,
625 return Open (obj, &subsys);