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

谁借莪1个温暖的怀抱¢ 2023-06-21 10:25 115阅读 0赞

在使用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) {

    1. std::string vid_str = str.substr(pos-4, 4);
    2. std::string pid_str = str.substr(pos+1, 4);
    3. std::istringstream(vid_str) >> std::hex >> vid_value;
    4. std::istringstream(pid_str) >> std::hex >> pid_value;
    5. 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) {

    1. 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))

    1. fprintf(stderr, " fail to read modalias\n");

    if (modalias.size() < 14 || modalias.substr(0,5) != “usb:v” || modalias[9] != ‘p’)

    1. fprintf(stderr, " not a usb format modalias\n");

    if (!(std::istringstream(modalias.substr(5,4)) >> std::hex >> vid_value))

    1. fprintf(stderr, " fail to read vid\n");

    if (!(std::istringstream(modalias.substr(10,4)) >> std::hex >> pid_value))

    1. 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) {

    1. std::string str = bus_line.substr(pos+7, 4);
    2. std::istringstream(str) >> std::hex >> vid;

    } else {

    1. fprintf(stderr, "not found vid\n");
    2. return -1;

    }

    pos = bus_line.find(“Product”);
    if (pos != std::string::npos) {

    1. std::string str = bus_line.substr(pos+8, 4);
    2. std::istringstream(str) >> std::hex >> pid;

    } else {

    1. fprintf(stderr, "not found pid\n");
    2. 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) {

    1. std::string str = event_line.substr(pos, std::string::npos);
    2. str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());
    3. std::string vid_str, pid_str;
    4. std::string prefix = "/sys/class/input/" + str + "/device/id/";
    5. if (!(std::ifstream(prefix +"vendor") >> vid_str)) {
    6. fprintf(stderr, "not found /device/id/vendor\n");
    7. return -1;
    8. }
    9. if (!(std::ifstream(prefix + "product") >> pid_str)) {
    10. fprintf(stderr, "not found /device/id/product\n");
    11. return -1;
    12. }
    13. std::istringstream(vid_str) >> std::hex >> vid;
    14. 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摄像头信息

20191212140933219.png

  1. 可以通过执行v4l2-ctl —list-devices命令获取vid和pid,执行结果如下:

20191212141124864.png

以下是上面1—4种方法实现的完整C++代码:

  1. #include "funset.hpp"
  2. #include <map>
  3. #include <string>
  4. #include <sstream>
  5. #include <fstream>
  6. #include <algorithm>
  7. #ifndef _MSC_VER
  8. #include <dirent.h>
  9. #include <fcntl.h>
  10. #include <unistd.h>
  11. #include <errno.h>
  12. #include <sys/stat.h>
  13. #include <sys/types.h>
  14. #include <sys/time.h>
  15. #include <sys/mman.h>
  16. #include <sys/ioctl.h>
  17. #include <linux/videodev2.h>
  18. #else
  19. #endif
  20. #ifndef _MSC_VER
  21. namespace {
  22. int v4l2_is_v4l_dev(const char *name)
  23. {
  24. return !strncmp(name, "video", 5) ||
  25. !strncmp(name, "radio", 5) ||
  26. !strncmp(name, "vbi", 3) ||
  27. !strncmp(name, "v4l-subdev", 10);
  28. }
  29. int test_v4l2_get_device_list(std::map<std::string, std::string>& device_list)
  30. {
  31. device_list.clear();
  32. const char* dir_name = "/dev";
  33. DIR* dir = opendir(dir_name);
  34. if (!dir) {
  35. fprintf(stderr, "Error: couldn't open the directory: %s\n", dir_name);
  36. return -1;
  37. }
  38. struct dirent* entry = nullptr;
  39. int fd;
  40. while ((entry = readdir(dir))) {
  41. char device_name[256];
  42. if (!v4l2_is_v4l_dev(entry->d_name))
  43. continue;
  44. snprintf(device_name, sizeof(device_name), "/dev/%s", entry->d_name);
  45. if ((fd = device_open(device_name)) < 0)
  46. continue;
  47. struct v4l2_capability cap;
  48. if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
  49. fprintf(stderr, "Error: cam_info: can't open device: %s\n", device_name);
  50. goto fail;
  51. }
  52. device_list[device_name] = reinterpret_cast<char*>(cap.card);
  53. close(fd);
  54. continue;
  55. fail:
  56. if (fd >= 0) close(fd);
  57. break;
  58. }
  59. closedir(dir);
  60. return 0;
  61. }
  62. int parse_input_devices(const std::string& name, unsigned int& vid, unsigned int& pid)
  63. {
  64. const std::string device_list_file = "/proc/bus/input/devices";
  65. std::ifstream file_input(device_list_file.c_str());
  66. if (!file_input.is_open()) {
  67. fprintf(stderr, "fail to open file: %s\n", device_list_file.c_str());
  68. return -1;
  69. }
  70. std::string current_line, bus_line, search_name_line = name, search_bus_line = "Bus=";
  71. while (getline(file_input, current_line)) {
  72. auto pos = current_line.find(search_bus_line);
  73. if (pos != std::string::npos)
  74. bus_line = current_line;
  75. pos = current_line.find(search_name_line);
  76. if (pos != std::string::npos)
  77. break;
  78. }
  79. file_input.close();
  80. auto pos = bus_line.find("Vendor");
  81. if (pos != std::string::npos) {
  82. std::string str = bus_line.substr(pos+7, 4);
  83. std::istringstream(str) >> std::hex >> vid;
  84. } else {
  85. fprintf(stderr, "not found vid\n");
  86. return -1;
  87. }
  88. pos = bus_line.find("Product");
  89. if (pos != std::string::npos) {
  90. std::string str = bus_line.substr(pos+8, 4);
  91. std::istringstream(str) >> std::hex >> pid;
  92. } else {
  93. fprintf(stderr, "not found pid\n");
  94. return -1;
  95. }
  96. return 0;
  97. }
  98. int parse_input_devices2(const std::string& name, unsigned int& vid, unsigned int& pid)
  99. {
  100. const std::string device_list_file = "/proc/bus/input/devices";
  101. std::ifstream file_input(device_list_file.c_str());
  102. if (!file_input.is_open()) {
  103. fprintf(stderr, "fail to open file: %s\n", device_list_file.c_str());
  104. return -1;
  105. }
  106. std::string current_line, event_line, search_name_line = name, search_event_line = "event";
  107. bool flag = false;
  108. while (getline(file_input, current_line)) {
  109. auto pos = current_line.find(search_name_line);
  110. if (pos != std::string::npos)
  111. flag = true;
  112. else if (!flag)
  113. continue;
  114. if (flag) {
  115. pos = current_line.find(search_event_line);
  116. if (pos != std::string::npos) {
  117. event_line = current_line;
  118. break;
  119. }
  120. }
  121. }
  122. file_input.close();
  123. if (!flag) {
  124. fprintf(stderr, "not found event\n");
  125. return -1;
  126. }
  127. auto pos = event_line.find(search_event_line);
  128. if (pos != std::string::npos) {
  129. std::string str = event_line.substr(pos, std::string::npos);
  130. str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());
  131. std::string vid_str, pid_str;
  132. std::string prefix = "/sys/class/input/" + str + "/device/id/";
  133. if (!(std::ifstream(prefix +"vendor") >> vid_str)) {
  134. fprintf(stderr, "not found /device/id/vendor\n");
  135. return -1;
  136. }
  137. if (!(std::ifstream(prefix + "product") >> pid_str)) {
  138. fprintf(stderr, "not found /device/id/product\n");
  139. return -1;
  140. }
  141. std::istringstream(vid_str) >> std::hex >> vid;
  142. std::istringstream(pid_str) >> std::hex >> pid;
  143. }
  144. return 0;
  145. }
  146. } // namespace
  147. int test_get_usb_camera_vid_pid()
  148. {
  149. // get usb video device list
  150. std::map<std::string, std::string> device_list;
  151. if (test_v4l2_get_device_list(device_list) !=0) {
  152. fprintf(stderr, "fail to get usb video device list\n");
  153. return -1;
  154. }
  155. int count = 1;
  156. fprintf(stdout, "device count: %d\n", device_list.size());
  157. for (auto it = device_list.cbegin(); it != device_list.cend(); ++it) {
  158. fprintf(stdout, "%d. device address: %s, description(name): %s\n", count++, (*it).first.c_str(), (*it).second.c_str());
  159. unsigned int vid_value, pid_value;
  160. fprintf(stdout, " method1. get vid and pid through v4l2:\n");
  161. std::string str = (*it).second;
  162. auto pos = str.find(":");
  163. if (pos != std::string::npos) {
  164. std::string vid_str = str.substr(pos-4, 4);
  165. std::string pid_str = str.substr(pos+1, 4);
  166. std::istringstream(vid_str) >> std::hex >> vid_value;
  167. std::istringstream(pid_str) >> std::hex >> pid_value;
  168. 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);
  169. } else {
  170. fprintf(stderr, " fail to get vid and pid\n");
  171. }
  172. fprintf(stdout, " method2. get vid and pid through device/modalias:\n");
  173. str = (*it).first;
  174. pos = str.find_last_of("/");
  175. if (pos == std::string::npos) {
  176. fprintf(stderr, " fail to get vid and pid\n");
  177. }
  178. std::string name = str.substr(pos+1);
  179. std::string modalias;
  180. vid_value = 0; pid_value = 0;
  181. if (!(std::ifstream("/sys/class/video4linux/" + name + "/device/modalias") >> modalias))
  182. fprintf(stderr, " fail to read modalias\n");
  183. if (modalias.size() < 14 || modalias.substr(0,5) != "usb:v" || modalias[9] != 'p')
  184. fprintf(stderr, " not a usb format modalias\n");
  185. if (!(std::istringstream(modalias.substr(5,4)) >> std::hex >> vid_value))
  186. fprintf(stderr, " fail to read vid\n");
  187. if (!(std::istringstream(modalias.substr(10,4)) >> std::hex >> pid_value))
  188. fprintf(stderr, " fail to read pid\n");
  189. fprintf(stdout, " vid value: %d, pid value: %d\n", vid_value, pid_value);
  190. fprintf(stdout, " method3. get vid and pid through /proc/bus/input/devices:\n");
  191. vid_value = 0; pid_value = 0;
  192. parse_input_devices((*it).second, vid_value, pid_value);
  193. fprintf(stdout, " vid value: %d, pid value: %d\n", vid_value, pid_value);
  194. fprintf(stderr, " method4. get vid and pid through /sys/class/input/eventXXX:\n");
  195. vid_value = 0; pid_value = 0;
  196. parse_input_devices2((*it).second, vid_value, pid_value);
  197. fprintf(stdout, " vid value: %d, pid value: %d\n", vid_value, pid_value);
  198. }
  199. return 0;
  200. }
  201. #else
  202. int test_get_usb_camera_vid_pid()
  203. {
  204. fprintf(stderr, "only support linux platform\n");
  205. return -1;
  206. }
  207. #endif

执行结果如下:从结果可知,这四种方法与通过命令行获取到的pid和vid是一致的。

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2ZlbmdiaW5nY2h1bg_size_16_color_FFFFFF_t_70

GitHub:https://github.com//fengbingchun/OpenCV_Test

发表评论

表情:
评论列表 (有 0 条评论,115人围观)

还没有评论,来说两句吧...

相关阅读