I have an application that allows for recording audio/video from input devices. I now want to add to it the ability to autodetect plugged-in USB webcams via udev. The problem is that audio and video are handled separately, by a video4linux and an ALSA device. The goal is to discover the ALSA device name and the Video4Linux device node that are associated (that is, belonging to the same camera input feed).

Surprisingly, there does not seem to be any interface in V4L2 for this, so I had to resort to looking at the udev properties. I came up with a very dirty hack that accomplishes what I want. I pasted it below.

This code relies on the ID_USB_INTERFACES property to associate the V4L2 and ALSA devices, which may or may not be fragile. Furthermore, it traverses directories in /sys/ . I use libgudev here.

I now ask the mailing list if there is an easier way to do this. Note that I cannot rely on tools like the gst device monitor. It would also be preferable to not have to rely on specific udev rules, though I will add them if it is truly necessary.

Also, I found it difficult to get a meaningful label that I can present the user. The V4L2 properties produced fairly useless labels (something like "UVC camera (xxxx:yyyy)"). What produced the best results was the ID_MODEL_FROM_DATABASE - but this is a property of the ALSA devices.

So, anybody has a better idea how to accomplish this?

The hacky code:

    std::regex sndcard_regex(".*/sound/card[[:digit:]]+/controlC[[:digit:]]+$");
    std::regex pcmc_regex("pcmC([[:digit:]]+)D([[:digit:]]+)c");

    void process_added_device(GUdevDevice *p_added_device)
        gchar const *bus_cstr = g_udev_device_get_property(p_added_device, "ID_BUS");
        if (g_strcmp0(bus_cstr, "usb") != 0)

        gchar const *path_cstr = g_udev_device_get_sysfs_path(p_added_device);         gchar const *subsystem_cstr = g_udev_device_get_property(p_added_device, "SUBSYSTEM");         gchar const *usb_interfaces_cstr = g_udev_device_get_property(p_added_device, "ID_USB_INTERFACES");

        std::string id = std::string("usbif_") + usb_interfaces_cstr;

        capture_device new_capture_device;
        new_capture_device.m_is_hdmi_device = false;
        new_capture_device.m_label = id; // initially use the ID as label, as fallback if no other label can be found
        new_capture_device.m_id = id;

        auto &id_view = m_entries.get < id_tag > ();
        auto iter = id_view.find(id);
        if (iter != id_view.end()) // check if an entry exists already; if so, retrieve it
            new_capture_device = *iter;
            // remove the device from the boost multi-index container. we'll reinsert a modified version later.

        if (g_strcmp0(subsystem_cstr, "video4linux") == 0)
            gchar const *devnode_cstr = g_udev_device_get_device_file(p_added_device);
            new_capture_device.m_v4l2_device = devnode_cstr;
        else if (g_strcmp0(subsystem_cstr, "sound") == 0)
            gchar const *model_from_db = g_udev_device_get_property(p_added_device, "ID_MODEL_FROM_DATABASE");
            if (model_from_db != nullptr)
                new_capture_device.m_label = model_from_db;

            std::smatch base_match;
            std::string path_str = path_cstr;
            if (std::regex_match(path_str, base_match, sndcard_regex)) // check if this sound device path is one to an ALSA control device
                boost::filesystem::path path = path_str;
                path = path.parent_path();
                boost::system::error_code ec;
                boost::filesystem::directory_iterator dir_iter(path, ec);
                if (ec)

                // search the parent directory for a PCM capture device
                boost::filesystem::directory_iterator end_dir_iter;
                for (; dir_iter != end_dir_iter; ++dir_iter)
                    std::smatch pcm_match;
                    if (std::regex_match(dir_iter->path().filename().string(), pcm_match, pcmc_regex))                         new_capture_device.m_alsa_device = std::string("plughw:") + std::string(pcm_match[1]) + "," + std::string(pcm_match[2]);


Reply via email to