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 *****************************************************************************/
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)
56 add_shortcut ("v4l", "video")
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)
65 add_shortcut ("alsa", "audio")
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);
120 struct services_discovery_sys_t
122 const struct subsys *subsys;
123 struct udev_monitor *monitor;
129 * Compares two devices (to support binary search).
131 static int cmpdev (const void *a, const void *b)
133 const dev_t *da = a, *db = b;
134 dev_t delta = *da - *db;
136 if (sizeof (delta) > sizeof (int))
137 return delta ? ((delta > 0) ? 1 : -1) : 0;
138 return (signed)delta;
141 static void DestroyDevice (void *data)
143 struct device *d = data;
146 services_discovery_RemoveItem (d->sd, d->item);
147 vlc_gc_decref (d->item);
151 static char *decode_property (struct udev_device *, const char *);
154 * Adds a udev device.
156 static int AddDevice (services_discovery_t *sd, struct udev_device *dev)
158 services_discovery_sys_t *p_sys = sd->p_sys;
160 char *mrl = p_sys->subsys->get_mrl (dev);
162 return 0; /* don't know if it was an error... */
163 char *name = p_sys->subsys->get_name (dev);
164 input_item_t *item = input_item_NewWithType (mrl, name ? name : mrl,
166 p_sys->subsys->item_type);
167 msg_Dbg (sd, "adding %s (%s)", mrl, name);
173 struct device *d = malloc (sizeof (*d));
176 vlc_gc_decref (item);
179 d->devnum = udev_device_get_devnum (dev);
183 struct device **dp = tsearch (d, &p_sys->root, cmpdev);
184 if (dp == NULL) /* Out-of-memory */
189 if (*dp != d) /* Overwrite existing device */
195 services_discovery_AddItem (sd, item, NULL);
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 v4l_is_legacy (struct udev_device *dev)
402 version = udev_device_get_property_value (dev, "ID_V4L_VERSION");
403 return (version != NULL) && !strcmp (version, "1");
406 static bool v4l_can_capture (struct udev_device *dev)
410 caps = udev_device_get_property_value (dev, "ID_V4L_CAPABILITIES");
411 return (caps != NULL) && (strstr (caps, ":capture:") != NULL);
414 static char *v4l_get_mrl (struct udev_device *dev)
416 /* Determine media location */
417 if (v4l_is_legacy (dev) || !v4l_can_capture (dev))
420 const char *node = udev_device_get_devnode (dev);
423 if (asprintf (&mrl, "v4l2://%s", node) == -1)
428 static char *v4l_get_name (struct udev_device *dev)
430 const char *prd = udev_device_get_property_value (dev, "ID_V4L_PRODUCT");
431 return prd ? strdup (prd) : NULL;
434 int OpenV4L (vlc_object_t *obj)
436 static const struct subsys subsys = {
437 "video4linux", v4l_get_mrl, v4l_get_name, 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 int OpenALSA (vlc_object_t *obj)
510 static const struct subsys subsys = {
511 "sound", alsa_get_mrl, alsa_get_name, ITEM_TYPE_CARD,
514 return Open (obj, &subsys);
516 #endif /* HAVE_ALSA */
519 /*** Discs support ***/
520 static char *disc_get_mrl (struct udev_device *dev)
524 val = udev_device_get_property_value (dev, "ID_CDROM");
526 return NULL; /* Ignore non-optical block devices */
528 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_STATE");
529 if (val && !strcmp (val, "blank"))
530 return NULL; /* ignore empty drives and virgin recordable discs */
532 const char *scheme = NULL;
533 val = udev_device_get_property_value (dev,
534 "ID_CDROM_MEDIA_TRACK_COUNT_AUDIO");
535 if (val && atoi (val))
536 scheme = "cdda"; /* Audio CD rather than file system */
537 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_DVD");
538 if (val && atoi (val))
541 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_BD");
542 if (val && atoi (val))
545 val = udev_device_get_property_value (dev, "ID_CDROM_MEDIA_HDDVD");
546 if (val && atoi (val))
550 /* We didn't get any property that could tell we have optical disc
555 val = udev_device_get_devnode (dev);
556 return make_URI (val, scheme);
559 static char *disc_get_name (struct udev_device *dev)
562 struct udev_list_entry *list, *entry;
564 list = udev_device_get_properties_list_entry (dev);
565 if (unlikely(list == NULL))
568 const char *cat = NULL;
569 udev_list_entry_foreach (entry, list)
571 const char *name = udev_list_entry_get_name (entry);
573 if (strncmp (name, "ID_CDROM_MEDIA_", 15))
575 if (!atoi (udev_list_entry_get_value (entry)))
579 if (!strncmp (name, "CD", 2))
581 else if (!strncmp (name, "DVD", 3))
583 else if (!strncmp (name, "BD", 2))
585 else if (!strncmp (name, "HDDVD", 5))
593 cat = N_("Unknown type");
595 char *label = decode_property (dev, "ID_FS_LABEL_ENC");
598 if (asprintf(&name, "%s (%s)", label, vlc_gettext(cat)) < 0)
605 int OpenDisc (vlc_object_t *obj)
607 static const struct subsys subsys = {
608 "block", disc_get_mrl, disc_get_name, ITEM_TYPE_DISC,
611 return Open (obj, &subsys);