Linux下获取usb视频设备vendor id和product id的8种方法

在使用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是一致的。

GitHubhttps://github.com//fengbingchun/OpenCV_Test

发布了718 篇原创文章 · 获赞 1131 · 访问量 609万+

猜你喜欢

转载自blog.csdn.net/fengbingchun/article/details/103507754