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 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_services_discovery.h>
35 #include <vlc_plugin.h>
37 # include <vlc_modules.h>
41 static int OpenV4L (vlc_object_t *);
43 static int OpenALSA (vlc_object_t *);
45 static int OpenDisc (vlc_object_t *);
46 static void Close (vlc_object_t *);
47 static int vlc_sd_probe_Open (vlc_object_t *);
53 set_shortname (N_("Video capture"))
54 set_description (N_("Video capture (Video4Linux)"))
55 set_category (CAT_PLAYLIST)
56 set_subcategory (SUBCAT_PLAYLIST_SD)
57 set_capability ("services_discovery", 0)
58 set_callbacks (OpenV4L, Close)
59 add_shortcut ("v4l", "video")
62 set_shortname (N_("Audio capture"))
63 set_description (N_("Audio capture (ALSA)"))
64 set_category (CAT_PLAYLIST)
65 set_subcategory (SUBCAT_PLAYLIST_SD)
66 set_capability ("services_discovery", 0)
67 set_callbacks (OpenALSA, Close)
68 add_shortcut ("alsa", "audio")
71 set_shortname (N_("Discs"))
72 set_description (N_("Discs"))
73 set_category (CAT_PLAYLIST)
74 set_subcategory (SUBCAT_PLAYLIST_SD)
75 set_capability ("services_discovery", 0)
76 set_callbacks (OpenDisc, Close)
79 VLC_SD_PROBE_SUBMODULE
83 static int vlc_sd_probe_Open (vlc_object_t *obj)
85 vlc_probe_t *probe = (vlc_probe_t *)obj;
87 struct udev *udev = udev_new ();
89 return VLC_PROBE_CONTINUE;
91 struct udev_monitor *mon = udev_monitor_new_from_netlink (udev, "udev");
94 vlc_sd_probe_Add (probe, "v4l{longname=\"Video capture\"}",
95 N_("Video capture"), SD_CAT_DEVICES);
97 if (!module_exists ("pulselist"))
98 vlc_sd_probe_Add (probe, "alsa{longname=\"Audio capture\"}",
99 N_("Audio capture"), SD_CAT_DEVICES);
101 vlc_sd_probe_Add (probe, "disc{longname=\"Discs\"}", N_("Discs"),
103 udev_monitor_unref (mon);
106 return VLC_PROBE_CONTINUE;
111 dev_t devnum; /* must be first */
113 services_discovery_t *sd;
119 char * (*get_mrl) (struct udev_device *dev);
120 char * (*get_name) (struct udev_device *dev);
124 struct services_discovery_sys_t
126 const struct subsys *subsys;
127 struct udev_monitor *monitor;
133 * Compares two devices (to support binary search).
135 static int cmpdev (const void *a, const void *b)
137 const dev_t *da = a, *db = b;
138 dev_t delta = *da - *db;
140 if (sizeof (delta) > sizeof (int))
141 return delta ? ((delta > 0) ? 1 : -1) : 0;
142 return (signed)delta;
145 static void DestroyDevice (void *data)
147 struct device *d = data;
150 services_discovery_RemoveItem (d->sd, d->item);
151 vlc_gc_decref (d->item);
155 static char *decode_property (struct udev_device *, const char *);
158 * Adds a udev device.
160 static int AddDevice (services_discovery_t *sd, struct udev_device *dev)
162 services_discovery_sys_t *p_sys = sd->p_sys;
164 char *mrl = p_sys->subsys->get_mrl (dev);
166 return 0; /* don't know if it was an error... */
167 char *name = p_sys->subsys->get_name (dev);
168 input_item_t *item = input_item_NewWithType (mrl, name ? name : mrl,
170 p_sys->subsys->item_type);
171 msg_Dbg (sd, "adding %s (%s)", mrl, name);
177 struct device *d = malloc (sizeof (*d));
180 vlc_gc_decref (item);
183 d->devnum = udev_device_get_devnum (dev);
187 struct device **dp = tsearch (d, &p_sys->root, cmpdev);
188 if (dp == NULL) /* Out-of-memory */
193 if (*dp != d) /* Overwrite existing device */
199 services_discovery_AddItem (sd, item, NULL);
205 * Removes a udev device (if present).
207 static void RemoveDevice (services_discovery_t *sd, struct udev_device *dev)
209 services_discovery_sys_t *p_sys = sd->p_sys;
211 dev_t num = udev_device_get_devnum (dev);
212 struct device **dp = tfind (&(dev_t){ num }, &p_sys->root, cmpdev);
216 struct device *d = *dp;
217 tdelete (d, &p_sys->root, cmpdev);
221 static void *Run (void *);
224 * Probes and initializes.
226 static int Open (vlc_object_t *obj, const struct subsys *subsys)
228 services_discovery_t *sd = (services_discovery_t *)obj;
229 services_discovery_sys_t *p_sys = malloc (sizeof (*p_sys));
234 p_sys->subsys = subsys;
237 struct udev_monitor *mon = NULL;
238 struct udev *udev = udev_new ();
242 mon = udev_monitor_new_from_netlink (udev, "udev");
244 || udev_monitor_filter_add_match_subsystem_devtype (mon, subsys->name,
247 p_sys->monitor = mon;
249 /* Enumerate existing devices */
250 struct udev_enumerate *devenum = udev_enumerate_new (udev);
253 if (udev_enumerate_add_match_subsystem (devenum, subsys->name))
255 udev_enumerate_unref (devenum);
259 udev_monitor_enable_receiving (mon);
260 /* Note that we enumerate _after_ monitoring is enabled so that we do not
261 * loose device events occuring while we are enumerating. We could still
262 * loose events if the Netlink socket receive buffer overflows. */
263 udev_enumerate_scan_devices (devenum);
264 struct udev_list_entry *devlist = udev_enumerate_get_list_entry (devenum);
265 struct udev_list_entry *deventry;
266 udev_list_entry_foreach (deventry, devlist)
268 const char *path = udev_list_entry_get_name (deventry);
269 struct udev_device *dev = udev_device_new_from_syspath (udev, path);
271 udev_device_unref (dev);
273 udev_enumerate_unref (devenum);
275 if (vlc_clone (&p_sys->thread, Run, sd, VLC_THREAD_PRIORITY_LOW))
276 { /* Fallback without thread */
277 udev_monitor_unref (mon);
279 p_sys->monitor = NULL;
285 udev_monitor_unref (mon);
295 static void Close (vlc_object_t *obj)
297 services_discovery_t *sd = (services_discovery_t *)obj;
298 services_discovery_sys_t *p_sys = sd->p_sys;
300 if (p_sys->monitor != NULL)
302 struct udev *udev = udev_monitor_get_udev (p_sys->monitor);
304 vlc_cancel (p_sys->thread);
305 vlc_join (p_sys->thread, NULL);
306 udev_monitor_unref (p_sys->monitor);
310 tdestroy (p_sys->root, DestroyDevice);
314 static void *Run (void *data)
316 services_discovery_t *sd = data;
317 services_discovery_sys_t *p_sys = sd->p_sys;
318 struct udev_monitor *mon = p_sys->monitor;
320 int fd = udev_monitor_get_fd (mon);
321 struct pollfd ufd = { .fd = fd, .events = POLLIN, };
325 while (poll (&ufd, 1, -1) == -1)
329 int canc = vlc_savecancel ();
330 struct udev_device *dev = udev_monitor_receive_device (mon);
334 const char *action = udev_device_get_action (dev);
335 if (!strcmp (action, "add"))
337 else if (!strcmp (action, "remove"))
338 RemoveDevice (sd, dev);
339 else if (!strcmp (action, "change"))
341 RemoveDevice (sd, dev);
344 udev_device_unref (dev);
345 vlc_restorecancel (canc);
351 * Converts an hexadecimal digit to an integer.
353 static int hex (char c)
355 if (c >= '0' && c <= '9')
357 if (c >= 'A' && c <= 'F')
359 if (c >= 'a' && c <= 'f')
365 * Decodes a udev hexadecimal-encoded property.
367 static char *decode (const char *enc)
369 char *ret = enc ? strdup (enc) : NULL;
374 for (const char *in = ret; *in; out++)
378 if ((in[0] == '\\') && (in[1] == 'x')
379 && ((h1 = hex (in[2])) != -1)
380 && ((h2 = hex (in[3])) != -1))
382 *out = (h1 << 4) | h2;
395 static char *decode_property (struct udev_device *dev, const char *name)
397 return decode (udev_device_get_property_value (dev, name));
401 /*** Video4Linux support ***/
402 static bool v4l_is_legacy (struct udev_device *dev)
406 version = udev_device_get_property_value (dev, "ID_V4L_VERSION");
407 return (version != NULL) && !strcmp (version, "1");
410 static bool v4l_can_capture (struct udev_device *dev)
414 caps = udev_device_get_property_value (dev, "ID_V4L_CAPABILITIES");
415 return (caps != NULL) && (strstr (caps, ":capture:") != NULL);
418 static char *v4l_get_mrl (struct udev_device *dev)
420 /* Determine media location */
421 if (v4l_is_legacy (dev) || !v4l_can_capture (dev))
424 const char *node = udev_device_get_devnode (dev);
427 if (asprintf (&mrl, "v4l2://%s", node) == -1)
432 static char *v4l_get_name (struct udev_device *dev)
434 const char *prd = udev_device_get_property_value (dev, "ID_V4L_PRODUCT");
435 return prd ? strdup (prd) : NULL;
438 int OpenV4L (vlc_object_t *obj)
440 static const struct subsys subsys = {
441 "video4linux", v4l_get_mrl, v4l_get_name, ITEM_TYPE_CARD,
444 return Open (obj, &subsys);
449 /*** Advanced Linux Sound Architecture support ***/
450 #include <alsa/asoundlib.h>
452 static int alsa_get_device (struct udev_device *dev, unsigned *restrict pcard,
453 unsigned *restrict pdevice)
455 const char *node = udev_device_get_devpath (dev);
458 node = strrchr (node, '/');
461 if (sscanf (node, "/pcmC%uD%u%c", pcard, pdevice, &type) < 3)
469 static char *alsa_get_mrl (struct udev_device *dev)
471 /* Determine media location */
473 unsigned card, device;
475 if (alsa_get_device (dev, &card, &device))
478 if (asprintf (&mrl, "alsa://plughw:%u,%u", card, device) == -1)
483 static char *alsa_get_name (struct udev_device *dev)
486 unsigned card, device;
488 if (alsa_get_device (dev, &card, &device))
491 char card_name[4 + 3 * sizeof (int)];
492 snprintf (card_name, sizeof (card_name), "hw:%u", card);
495 if (snd_ctl_open (&ctl, card_name, 0))
498 snd_pcm_info_t *pcm_info;
499 snd_pcm_info_alloca (&pcm_info);
500 snd_pcm_info_set_device (pcm_info, device);
501 snd_pcm_info_set_subdevice (pcm_info, 0);
502 snd_pcm_info_set_stream (pcm_info, SND_PCM_STREAM_CAPTURE);
503 if (snd_ctl_pcm_info (ctl, pcm_info))
506 name = strdup (snd_pcm_info_get_name (pcm_info));
512 int OpenALSA (vlc_object_t *obj)
514 static const struct subsys subsys = {
515 "sound", alsa_get_mrl, alsa_get_name, ITEM_TYPE_CARD,
518 return Open (obj, &subsys);
520 #endif /* HAVE_ALSA */
523 /*** Discs support ***/
524 static char *disc_get_mrl (struct udev_device *dev)
528 val = udev_device_get_property_value (dev, "ID_CDROM");
530 return NULL; /* Ignore non-optical block devices */
532 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_STATE");
533 if (val && !strcmp (val, "blank"))
534 return NULL; /* ignore empty drives and virgin recordable discs */
536 const char *scheme = NULL;
537 val = udev_device_get_property_value (dev,
538 "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO");
539 if (val && atoi (val))
540 scheme = "cdda"; /* Audio CD rather than file system */
541 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_DVD");
542 if (val && atoi (val))
545 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_BD");
546 if (val && atoi (val))
549 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_HDDVD");
550 if (val && atoi (val))
554 /* We didn't get any property that could tell we have optical disc
559 val = udev_device_get_devnode (dev);
560 return vlc_path2uri (val, scheme);
563 static char *disc_get_name (struct udev_device *dev)
566 struct udev_list_entry *list, *entry;
568 list = udev_device_get_properties_list_entry (dev);
569 if (unlikely(list == NULL))
572 const char *cat = NULL;
573 udev_list_entry_foreach (entry, list)
575 const char *name = udev_list_entry_get_name (entry);
577 if (strncmp (name, "ID_CDROM_MEDIA_", 15))
579 if (!atoi (udev_list_entry_get_value (entry)))
583 if (!strncmp (name, "CD", 2))
585 else if (!strncmp (name, "DVD", 3))
587 else if (!strncmp (name, "BD", 2))
589 else if (!strncmp (name, "HDDVD", 5))
597 cat = N_("Unknown type");
599 char *label = decode_property (dev, "ID_FS_LABEL_ENC");
602 if (asprintf(&name, "%s (%s)", label, vlc_gettext(cat)) < 0)
609 int OpenDisc (vlc_object_t *obj)
611 static const struct subsys subsys = {
612 "block", disc_get_mrl, disc_get_name, ITEM_TYPE_DISC,
615 return Open (obj, &subsys);