在使用usb摄像头获取视频时,有时需要获取此摄像头供应商ID(vendor id, vid)和产品ID(product id, pid),这里在Linux下提供获取vid和pid的8种方法:
1. 通过v4l2中结构体v4l2_capability的成员变量card:此变量中会包含设备名、vid、pid信息,其内容例如为“UVC Camera (046d:081b)”,其中”:”前四个字符为vid,”:”后四个字符为pid,代码段如下:其中str存放card的值
std::string str = (*it).second;
auto pos = str.find(":");
if (pos != std::string::npos) {
std::string vid_str = str.substr(pos-4, 4);
std::string pid_str = str.substr(pos+1, 4);
std::istringstream(vid_str) >> std::hex >> vid_value;
std::istringstream(pid_str) >> std::hex >> pid_value;
fprintf(stdout, " vid: str: %s, value: %d; pid: str: %s, value: %d\n", vid_str.c_str(), vid_value, pid_str.c_str(), pid_value);
}
2. 通过解析/sys/class/video4linux/xxxx/device/modalias文件,其中xxxx为video0或video1等,当有usb camera插入到Linux上时,则会有此文件,其内容例如为” usb:v046Dp081Bd0012dcEFdsc02dp01ic0Eisc01ip00in00”,其中”usb:v”后四个字符为vid,”usb:v046Dp”后四个字符为pid,代码段如下:其中str为设备地址,如"/dev/video0"
str = (*it).first;
pos = str.find_last_of("/");
if (pos == std::string::npos) {
fprintf(stderr, " fail to get vid and pid\n");
}
std::string name = str.substr(pos+1);
std::string modalias;
vid_value = 0; pid_value = 0;
if (!(std::ifstream("/sys/class/video4linux/" + name + "/device/modalias") >> modalias))
fprintf(stderr, " fail to read modalias\n");
if (modalias.size() < 14 || modalias.substr(0,5) != "usb:v" || modalias[9] != 'p')
fprintf(stderr, " not a usb format modalias\n");
if (!(std::istringstream(modalias.substr(5,4)) >> std::hex >> vid_value))
fprintf(stderr, " fail to read vid\n");
if (!(std::istringstream(modalias.substr(10,4)) >> std::hex >> pid_value))
fprintf(stderr, " fail to read pid\n");
fprintf(stdout, " vid value: %d, pid value: %d\n", vid_value, pid_value);
3. 通过解析/proc/bus/input/devices文件,输入设备名可以获取对应的vid和pid,代码段如下:其中bus_line为"I: Bus=0003 Vendor=046d Product=081b Version=0012"
auto pos = bus_line.find("Vendor");
if (pos != std::string::npos) {
std::string str = bus_line.substr(pos+7, 4);
std::istringstream(str) >> std::hex >> vid;
} else {
fprintf(stderr, "not found vid\n");
return -1;
}
pos = bus_line.find("Product");
if (pos != std::string::npos) {
std::string str = bus_line.substr(pos+8, 4);
std::istringstream(str) >> std::hex >> pid;
} else {
fprintf(stderr, "not found pid\n");
return -1;
}
4. 通过设备的event可以直接读取vid和pid,如event为event6,则为/sys/class/input/event6/device/id/vendor和/sys/class/input/event6/device/id/product,可通过解析/proc/bus/input/devices得到指定设备的event,代码段为:其中event_line为"H: Handlers=kbd event6"
auto pos = event_line.find(search_event_line);
if (pos != std::string::npos) {
std::string str = event_line.substr(pos, std::string::npos);
str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());
std::string vid_str, pid_str;
std::string prefix = "/sys/class/input/" + str + "/device/id/";
if (!(std::ifstream(prefix +"vendor") >> vid_str)) {
fprintf(stderr, "not found /device/id/vendor\n");
return -1;
}
if (!(std::ifstream(prefix + "product") >> pid_str)) {
fprintf(stderr, "not found /device/id/product\n");
return -1;
}
std::istringstream(vid_str) >> std::hex >> vid;
std::istringstream(pid_str) >> std::hex >> pid;
}
5. 通过解析/sys/kernel/debug/usb/devices可获取vid和pid,但是好像需要root权限。
6. 通过在Linux上安装libusb,然后调用其库提供的相应接口来获取vid和pid。
7. 可以通过执行lsusb命令获取vid和pid,执行结果如下:其中红框标注的为真实usb摄像头信息
8. 可以通过执行v4l2-ctl --list-devices命令获取vid和pid,执行结果如下:
以下是上面1--4种方法实现的完整C++代码:
#include "funset.hpp"
#include <map>
#include <string>
#include <sstream>
#include <fstream>
#include <algorithm>
#ifndef _MSC_VER
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#else
#endif
#ifndef _MSC_VER
namespace {
int v4l2_is_v4l_dev(const char *name)
{
return !strncmp(name, "video", 5) ||
!strncmp(name, "radio", 5) ||
!strncmp(name, "vbi", 3) ||
!strncmp(name, "v4l-subdev", 10);
}
int test_v4l2_get_device_list(std::map<std::string, std::string>& device_list)
{
device_list.clear();
const char* dir_name = "/dev";
DIR* dir = opendir(dir_name);
if (!dir) {
fprintf(stderr, "Error: couldn't open the directory: %s\n", dir_name);
return -1;
}
struct dirent* entry = nullptr;
int fd;
while ((entry = readdir(dir))) {
char device_name[256];
if (!v4l2_is_v4l_dev(entry->d_name))
continue;
snprintf(device_name, sizeof(device_name), "/dev/%s", entry->d_name);
if ((fd = device_open(device_name)) < 0)
continue;
struct v4l2_capability cap;
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
fprintf(stderr, "Error: cam_info: can't open device: %s\n", device_name);
goto fail;
}
device_list[device_name] = reinterpret_cast<char*>(cap.card);
close(fd);
continue;
fail:
if (fd >= 0) close(fd);
break;
}
closedir(dir);
return 0;
}
int parse_input_devices(const std::string& name, unsigned int& vid, unsigned int& pid)
{
const std::string device_list_file = "/proc/bus/input/devices";
std::ifstream file_input(device_list_file.c_str());
if (!file_input.is_open()) {
fprintf(stderr, "fail to open file: %s\n", device_list_file.c_str());
return -1;
}
std::string current_line, bus_line, search_name_line = name, search_bus_line = "Bus=";
while (getline(file_input, current_line)) {
auto pos = current_line.find(search_bus_line);
if (pos != std::string::npos)
bus_line = current_line;
pos = current_line.find(search_name_line);
if (pos != std::string::npos)
break;
}
file_input.close();
auto pos = bus_line.find("Vendor");
if (pos != std::string::npos) {
std::string str = bus_line.substr(pos+7, 4);
std::istringstream(str) >> std::hex >> vid;
} else {
fprintf(stderr, "not found vid\n");
return -1;
}
pos = bus_line.find("Product");
if (pos != std::string::npos) {
std::string str = bus_line.substr(pos+8, 4);
std::istringstream(str) >> std::hex >> pid;
} else {
fprintf(stderr, "not found pid\n");
return -1;
}
return 0;
}
int parse_input_devices2(const std::string& name, unsigned int& vid, unsigned int& pid)
{
const std::string device_list_file = "/proc/bus/input/devices";
std::ifstream file_input(device_list_file.c_str());
if (!file_input.is_open()) {
fprintf(stderr, "fail to open file: %s\n", device_list_file.c_str());
return -1;
}
std::string current_line, event_line, search_name_line = name, search_event_line = "event";
bool flag = false;
while (getline(file_input, current_line)) {
auto pos = current_line.find(search_name_line);
if (pos != std::string::npos)
flag = true;
else if (!flag)
continue;
if (flag) {
pos = current_line.find(search_event_line);
if (pos != std::string::npos) {
event_line = current_line;
break;
}
}
}
file_input.close();
if (!flag) {
fprintf(stderr, "not found event\n");
return -1;
}
auto pos = event_line.find(search_event_line);
if (pos != std::string::npos) {
std::string str = event_line.substr(pos, std::string::npos);
str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());
std::string vid_str, pid_str;
std::string prefix = "/sys/class/input/" + str + "/device/id/";
if (!(std::ifstream(prefix +"vendor") >> vid_str)) {
fprintf(stderr, "not found /device/id/vendor\n");
return -1;
}
if (!(std::ifstream(prefix + "product") >> pid_str)) {
fprintf(stderr, "not found /device/id/product\n");
return -1;
}
std::istringstream(vid_str) >> std::hex >> vid;
std::istringstream(pid_str) >> std::hex >> pid;
}
return 0;
}
} // namespace
int test_get_usb_camera_vid_pid()
{
// get usb video device list
std::map<std::string, std::string> device_list;
if (test_v4l2_get_device_list(device_list) !=0) {
fprintf(stderr, "fail to get usb video device list\n");
return -1;
}
int count = 1;
fprintf(stdout, "device count: %d\n", device_list.size());
for (auto it = device_list.cbegin(); it != device_list.cend(); ++it) {
fprintf(stdout, "%d. device address: %s, description(name): %s\n", count++, (*it).first.c_str(), (*it).second.c_str());
unsigned int vid_value, pid_value;
fprintf(stdout, " method1. get vid and pid through v4l2:\n");
std::string str = (*it).second;
auto pos = str.find(":");
if (pos != std::string::npos) {
std::string vid_str = str.substr(pos-4, 4);
std::string pid_str = str.substr(pos+1, 4);
std::istringstream(vid_str) >> std::hex >> vid_value;
std::istringstream(pid_str) >> std::hex >> pid_value;
fprintf(stdout, " vid: str: %s, value: %d; pid: str: %s, value: %d\n", vid_str.c_str(), vid_value, pid_str.c_str(), pid_value);
} else {
fprintf(stderr, " fail to get vid and pid\n");
}
fprintf(stdout, " method2. get vid and pid through device/modalias:\n");
str = (*it).first;
pos = str.find_last_of("/");
if (pos == std::string::npos) {
fprintf(stderr, " fail to get vid and pid\n");
}
std::string name = str.substr(pos+1);
std::string modalias;
vid_value = 0; pid_value = 0;
if (!(std::ifstream("/sys/class/video4linux/" + name + "/device/modalias") >> modalias))
fprintf(stderr, " fail to read modalias\n");
if (modalias.size() < 14 || modalias.substr(0,5) != "usb:v" || modalias[9] != 'p')
fprintf(stderr, " not a usb format modalias\n");
if (!(std::istringstream(modalias.substr(5,4)) >> std::hex >> vid_value))
fprintf(stderr, " fail to read vid\n");
if (!(std::istringstream(modalias.substr(10,4)) >> std::hex >> pid_value))
fprintf(stderr, " fail to read pid\n");
fprintf(stdout, " vid value: %d, pid value: %d\n", vid_value, pid_value);
fprintf(stdout, " method3. get vid and pid through /proc/bus/input/devices:\n");
vid_value = 0; pid_value = 0;
parse_input_devices((*it).second, vid_value, pid_value);
fprintf(stdout, " vid value: %d, pid value: %d\n", vid_value, pid_value);
fprintf(stderr, " method4. get vid and pid through /sys/class/input/eventXXX:\n");
vid_value = 0; pid_value = 0;
parse_input_devices2((*it).second, vid_value, pid_value);
fprintf(stdout, " vid value: %d, pid value: %d\n", vid_value, pid_value);
}
return 0;
}
#else
int test_get_usb_camera_vid_pid()
{
fprintf(stderr, "only support linux platform\n");
return -1;
}
#endif
执行结果如下:从结果可知,这四种方法与通过命令行获取到的pid和vid是一致的。