From 7ff4e654c9e55c9f6151dfdfc1e6a92ccc4fd8b1 Mon Sep 17 00:00:00 2001 From: Evan Flynn Date: Sat, 20 Jan 2024 10:10:27 -0800 Subject: [PATCH] Use /sys/class/video4linux/ to get list of v4l2 devices Signed-off-by: Evan Flynn --- CMakeLists.txt | 4 ++-- include/usb_cam/utils.hpp | 44 +++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 791070e9..2387c328 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 3.5) project(usb_cam) -# Default to C++14 +# Default to C++17 if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 14) + set(CMAKE_CXX_STANDARD 17) endif() if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") diff --git a/include/usb_cam/utils.hpp b/include/usb_cam/utils.hpp index bf2dbf65..add1ff97 100644 --- a/include/usb_cam/utils.hpp +++ b/include/usb_cam/utils.hpp @@ -36,10 +36,14 @@ extern "C" { #include #include #include // for access + #include #include #include +#include +#include #include +#include #include #include @@ -47,6 +51,8 @@ extern "C" { #include "usb_cam/constants.hpp" +namespace fs = std::filesystem; + namespace usb_cam { @@ -146,31 +152,43 @@ inline std::map available_devices() // Initialize vector of device strings to fill in std::map v4l2_devices; - // V4L2 spec says there can only be a maximum of 64 devices - const uint8_t MAX_DEVICES = 64; + // Get a list of all v4l2 devices from /sys/class/video4linux + const std::string v4l2_symlinks_dir = "/sys/class/video4linux/"; + for (const auto & device_symlink : fs::directory_iterator(v4l2_symlinks_dir)) { + if (fs::is_symlink(device_symlink)) { + // device_str is the full path to the device in /sys/devices/* + std::string device_str = fs::canonical( + v4l2_symlinks_dir + fs::read_symlink( + device_symlink).generic_string()); + // get the proper device name (e.g. /dev/*) + std::ifstream uevent_file(device_str + "/uevent"); + std::string line; + std::string device_name; + while (std::getline(uevent_file, line)) { + auto dev_name_index = line.find("DEVNAME="); + if (dev_name_index != std::string::npos) { + device_name = "/dev/" + line.substr(dev_name_index + 8, line.size()); + break; + } + } - for (uint8_t i = 0; i < MAX_DEVICES; i++) { - std::string device_str = "/dev/video" + std::to_string(i); - // See if device exists - if (access(device_str.c_str(), F_OK) != -1) { int fd; // Try and open device to test access - if ((fd = open(device_str.c_str(), O_RDONLY)) == -1) { - std::cerr << "Cannot open device: `" << device_str << "`, "; + if ((fd = open(device_name.c_str(), O_RDONLY)) == -1) { + std::cerr << "Cannot open device: `" << device_name << "`, "; std::cerr << "double-check read / write permissions for device" << std::endl; } else { struct v4l2_capability device_capabilities = {}; - if (xioctl(fd, VIDIOC_QUERYCAP, &device_capabilities) == -1) { - std::cerr << "Could not retrieve device capabilities: `" << device_str; + std::cerr << "Could not retrieve device capabilities: `" << device_name; std::cerr << "`" << std::endl; } else { - v4l2_devices[device_str] = device_capabilities; + v4l2_devices[device_name] = device_capabilities; } } } else { - // device doesn't exist, break - break; + // device doesn't exist, continue + continue; } }