Linux下获取usb视频设备vendor id和product id的8种方法
在使用usb攝像頭獲取視頻時(shí),有時(shí)需要獲取此攝像頭供應(yīng)商ID(vendor id, vid)和產(chǎn)品ID(product id, pid),這里在Linux下提供獲取vid和pid的8種方法:
1. 通過v4l2中結(jié)構(gòu)體v4l2_capability的成員變量card:此變量中會(huì)包含設(shè)備名、vid、pid信息,其內(nèi)容例如為“UVC Camera (046d:081b)”,其中”:”前四個(gè)字符為vid,”:”后四個(gè)字符為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等,當(dāng)有usb camera插入到Linux上時(shí),則會(huì)有此文件,其內(nèi)容例如為” usb:v046Dp081Bd0012dcEFdsc02dp01ic0Eisc01ip00in00”,其中”usb:v”后四個(gè)字符為vid,”usb:v046Dp”后四個(gè)字符為pid,代碼段如下:其中str為設(shè)備地址,如"/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文件,輸入設(shè)備名可以獲取對(duì)應(yīng)的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. 通過設(shè)備的event可以直接讀取vid和pid,如event為event6,則為/sys/class/input/event6/device/id/vendor和/sys/class/input/event6/device/id/product,可通過解析/proc/bus/input/devices得到指定設(shè)備的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權(quán)限。
6. 通過在Linux上安裝libusb,然后調(diào)用其庫提供的相應(yīng)接口來獲取vid和pid。
7. 可以通過執(zhí)行l(wèi)susb命令獲取vid和pid,執(zhí)行結(jié)果如下:其中紅框標(biāo)注的為真實(shí)usb攝像頭信息
8. 可以通過執(zhí)行v4l2-ctl --list-devices命令獲取vid和pid,執(zhí)行結(jié)果如下:
以下是上面1--4種方法實(shí)現(xiàn)的完整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;
}} // namespaceint test_get_usb_camera_vid_pid()
{// get usb video device liststd::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
執(zhí)行結(jié)果如下:從結(jié)果可知,這四種方法與通過命令行獲取到的pid和vid是一致的。
GitHub:https://github.com//fengbingchun/OpenCV_Test
總結(jié)
以上是生活随笔為你收集整理的Linux下获取usb视频设备vendor id和product id的8种方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: FFmpeg在Windows上通过dsh
- 下一篇: FFmpeg在Windows上设置dsh