libudev+V4L2 linux usb摄像头列表发现以及热拔插事件
2021/9/23 7:13:53
本文主要是介绍libudev+V4L2 linux usb摄像头列表发现以及热拔插事件,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
libudev+V4L2 linux usb摄像头列表发现以及热拔插事件
简介
最近工作计划本来是重写CameraCtrl 控制类以及实现推流。但是由于需求变动导致之前调研废弃,就暂时放这吧。
libudev
详细地址: libudev Reference Manul
v4l2
相关地址:
v4l2-ctl
Video for Linux Two API Specification
v4l-utils
思路
设计初衷,当时设计思路为将CameraManager作为一个摄像头管理模块,用来管理摄像头列表,主要为热拔插事件对应的增删以及推流的管理。并且实现跨平台,兼容windows&&linux(X86_64,arm,aarch64)。目前只讲解event相关部分,管理模块需要根据实际业务需求来自己进行针对性设计。
源码讲解
文件结构
src/deviceManager/cameraManager ├── cameraEvent.cpp ├── cameraEvent.h ├── cameraManager.cpp ├── cameraManager.h └── README.md tests ├── camera.cpp ├── CMakeLists.txt └── conanfile.py
详解
- 相关结构定义
主要定义信息结构如下:
FormatInfo: 帧信息 . 帧宽 . 帧高 . 帧率 CameraCardBindDeviceName: 摄像头绑定名称: 摄像头原始名称, 摄像头驱动名称 CameraInfo: 摄像头信息:摄像头名称(摄像头对外名称),摄像头原始名称,摄像头驱动名称,摄像头pid,摄像头vid,帧列表 EventInfo: 事件信息(主要记录日插拔事件,但是还有其他事件,所以没有命名为热插拔事件) 事件类型,摄像头名称,摄像头驱动名称,摄像头vid,摄像头pid
struct FormatInfo { unsigned int width; unsigned int height; unsigned int rate; }; struct CameraCardBindDeviceName { std::string cardNameOld; std::string cameraDeviceName; }; //因为摄像头设备存在着同名摄像头设备,无法从摄像头名称区分所需要的摄像头,所以另起了一个别名,用来区分管理摄像头设备。 //命名规则: eg. 存在两个摄像头设备为CameraLog,在遍历过程中第一个获取的为 CameraLog,使用原始名称,第二个获取到的设备名为 CameraLog(1)。即使用别名 struct CameraInfo { std::string cameraCardName; //摄像头别名即摄像头名称 std::string cameraCardNameOld; //摄像头原始名称 std::string cameraDeviceName; std::string pid; std::string vid; std::list<FormatInfo> formats; }; struct EventInfo { bool action; //true:add false:remove std::string cameraName; std::string cameraDeviceName; std::string vid; std::string pid; };
- 摄像头事件类
摄像头事件类,主要工作类,提供对外接口。负责摄像头事件的相关业务(列表获取,热拔插事件,摄像头信息)
推流函数接口,没什么实际意义就不具体放了。
class CameraEvent { private: /* data */ public: ~CameraEvent(); static CameraEvent* instance(); /** * @name: 获取摄像头列表 * @msg: * @param {*} * @return 返回摄像头列表—map */ std::map<std::string, CameraCardBindDeviceName> getCameraList(); /** * @name: 获取摄像头分辨率列表 * @msg: * @param {string} devicename - 摄像头驱动名称 * @return {*} 摄像头分辨率列表 */ std::list<FormatInfo> getCameraResolutions(std::string devicename); /** * @name: 获取摄像头图像格式列表 * @msg: * @param {*} * @return {*} 返回所有信息列表 */ std::list<CameraInfo> getCameraFormats(); /** * @name: 获取摄像头输入事件设备vid.pid列表 * @msg: * @param {*} * @return {*} 摄像头输入事件vid,pid列表 */ std::map<std::string, std::string> getInputVPIDs(); /** * @name: 摄像头列表添加摄像头 * @msg: * @param {string} devicename 摄像头驱动名称 * @param {string} vid * @param {string} pid * @param {list<CameraInfo>} &cameras 摄像头列表 * @return {*} 插入结果 */ bool addCameraInfo(std::string devicename, std::string vid, std::string pid, std::list<CameraInfo> &cameras); /** * @name: 摄像头列表移除摄像头 * @msg: * @param {string} devicename 摄像头驱动名称 * @param {list<CameraInfo>} &cameras 摄像头列表 * @return {*} 移除结果 */ bool removeCameraInfo(std::string devicename, std::list<CameraInfo> &cameras); /** * @name: 摄像头推流启动 * @msg: * @param {string} devicename 摄像头驱动名称 * @param {int} width 分辨率-宽 * @param {int} height 分辨率-高 * @param {string} host 目标主机地址 * @param {string} port 目标主机端口 * @return {*} */ void cameraGstPushStreamStart(std::string devicename, int width, int height, std::string host, std::string port); /** * @name: 摄像头推流停止 * @msg: * @param {string} devicename 摄像头驱动名称 * @return {*} */ void cameraGstPushStreamStop(std::string devicename); #ifdef __linux__ /** * @name: udev admMonitor 初始化 * @msg: * @param {udev} *udev udev指针 * @param {udev_monitor*} &kernelMonitor * @param {fd_set} &readFds * @return {*} */ int udevadmMonitor(struct udev *udev, struct udev_monitor* &kernelMonitor, fd_set &readFds); /** * @name: udev admmonitor item 子元素事件 * @msg: * @param {udev_monitor*} &kernelMonitor * @param {fd_set} &readFds * @return {*} */ EventInfo udevadmMonitorItem(struct udev_monitor* &kernelMonitor, fd_set &readFds); #endif private: CameraEvent(/* args */); //摄像头推流句柄管理容器 boost::unordered_map<std::string, Poco::ProcessHandle> _mapHandle; };
- instance()
CameraEvent* CameraEvent::instance() { static CameraEvent cameraEvent; return &cameraEvent; }
-
getCameraList()
主要工作流程为:遍历 /dev/文件夹下 video 类,存储,过滤,排序。遍历,过滤,获取所需要的摄像头列表。
在ubuntu18.04中。摄像头插入会有两个/dev/video 文件,按照顺序排序。其中原因为在18.04中,摄像头没有区分 V4L2_CAP_META_CAPTURE 和 V4L2_CAP_VIDEO_CAPTURE 导致输出两个 /dev/video 文件,在摄像头推流实际使用中, 是使用 V4L2_CAP_VIDEO_CAPTURE 类型摄像头,所以在过滤的时候需要区分。 Video Capture = V4L2_CAP_STREAMING + V4L2_CAP_EXT_PIX_FORMAT + V4L2_CAP_VIDEO_CAPTURE Metadata Capture = V4L2_CAP_STREAMING + V4L2_CAP_EXT_PIX_FORMAT + V4L2_CAP_META_CAPTURE ioctl(fd, VIDIOC_QUERYCAP, &vcap) 获取到 vcap.device_caps 值 来进行过滤区分。
相关链接:ubuntu18.04一个摄像头在/dev下对应两个video怎么回事
std::map<std::string, CameraCardBindDeviceName> CameraEvent::getCameraList() { std::map<std::string, CameraCardBindDeviceName> cameras; #ifdef _WIN32 #elif __linux__ DIR *dp; struct dirent *ep; dev_vec files; struct v4l2_capability vcap; dp = opendir("/dev"); if (dp == nullptr) { perror ("Couldn't open the directory"); return {}; } while ((ep = readdir(dp))) if (is_v4l_dev(ep->d_name) && std::string(ep->d_name).find("video") != std::string::npos) files.push_back(std::string("/dev/") + ep->d_name); closedir(dp); std::sort(files.begin(), files.end(), sort_on_device_name); for (const auto &file : files) { int fd = open(file.c_str(), O_RDWR); std::string bus_info; std::string card; if (fd < 0) continue; int err = ioctl(fd, VIDIOC_QUERYCAP, &vcap); bool is_mate = 0; if (err) { } else { if(vcap.device_caps == (V4L2_CAP_STREAMING + V4L2_CAP_EXT_PIX_FORMAT + V4L2_CAP_VIDEO_CAPTURE)) { bus_info = reinterpret_cast<const char *>(vcap.bus_info); card = reinterpret_cast<const char *>(vcap.card); #ifdef service_debug std::cout << std::string(card) <<" "<< std::string(file) << " " << std::string(bus_info) << std::endl; #elif LOG(INFO) << std::string(card) << std::string(file) << std::string(bus_info); #endif }else if(vcap.device_caps == (V4L2_CAP_STREAMING + V4L2_CAP_META_CAPTURE + V4L2_CAP_EXT_PIX_FORMAT)) is_mate = true; } close(fd); if (err || is_mate) continue; std::string cameraNameOld = card; if(cameras.count(std::string(card))) card = getCardName(cameras, card, 1); cameras[std::string(card)] = {cameraNameOld, file}; } #endif #ifdef service_debug for(auto it : cameras) { std::cout << it.first << " " << it.second.cardNameOld << " " << it.second.cameraDeviceName << std::endl; } #endif return cameras; }
- getCameraResolutions(std::string devicename)
根据devicename 获取摄像头分辨率,eg. /dev/video0 获取对应摄像头分辨率。
在实际中,摄像头通常支持多种视频格式,yuyv,mjeg等其他格式,但是在博主的实际使用中,只是用yuyv格式,所以只获取了yuyv对应分辨率列表。 if(fmt.pixelformat == V4L2_PIX_FMT_YUYV) 来进行过滤。
std::list<FormatInfo> CameraEvent::getCameraResolutions(std::string dev) { std::list<FormatInfo> resolutions = {}; #ifdef _WIN32 #elif __linux__ int fd = open(dev.c_str(), O_RDONLY); if (fd < 0) { #ifdef service_debug std::cout << dev << ":Open fail!!!" << std::endl;; #elif LOG(ERROR) << dev << ":Open fail!!!"; #endif } struct v4l2_format vfmt = {.type=V4L2_BUF_TYPE_VIDEO_OUTPUT}; if(ioctl(fd,VIDIOC_G_FMT, &vfmt)) { #ifdef service_debug std::string format = (vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) ? "YUYV" : std::to_string(vfmt.fmt.pix.pixelformat); std::cout << format << " " << vfmt.fmt.pix.width << " " << vfmt.fmt.pix.height << std::endl; #elif LOG(ERROR) << dev << ":Open fail!!!"; #endif }else { #ifdef service_debug std::cout << "vfmt:get fail!" << std::endl; #elif LOG(ERROR) << dev << ":Open fail!!!"; #endif } struct v4l2_fmtdesc fmt = {.index=0, .type=V4L2_BUF_TYPE_VIDEO_CAPTURE}; #ifdef service_debug std::cout << "Start Search format resolutions" << std::endl;; #elif LOG(ERROR) << << "Start Search format resolutions"; #endif while(ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >=0 ) { #ifdef service_debug std::cout << "Picture Format:" << fmt.description << std::endl; #elif LOG(INFO) << "Picture Format:" << fmt.description; #endif if(fmt.pixelformat == V4L2_PIX_FMT_YUYV) { #ifdef service_debug std::cout << "Picture Format is YUYV" << std::endl; #elif LOG(INFO) << "Picture Format is YUYV"; #endif FormatInfo fmtInfo; struct v4l2_frmsizeenum frmsize = {.index=0, .pixel_format=fmt.pixelformat}; while(ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) { fmtInfo.width = frmsize.discrete.width; fmtInfo.height = frmsize.discrete.height; fmtInfo.rate = 0; #ifdef service_debug std::cout << "Resolution: " << fmtInfo.width << "X" << fmtInfo.height << std::endl; #elif LOG(INFO) << "Resolution: " << fmtInfo.width << "X" << fmtInfo.height; #endif struct v4l2_frmivalenum frmival = {.index=0, .pixel_format=frmsize.pixel_format, .width=frmsize.discrete.width, .height=frmsize.discrete.height}; while(ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0) { unsigned int maxRate = 0; if(frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) { #ifdef service_debug std::cout << '\t' << "frmival.discrete: " << fract2fps_int(frmival.discrete) << std::endl; #elif LOG(INFO) << '\t' << "frmival.discrete: " << fract2fps_int(frmival.stepwise.max); #endif maxRate = fract2fps_int(frmival.discrete); } else if (frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS || frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE) { #ifdef service_debug std::cout << "stepwise.max: " << fract2fps(frmival.stepwise.max) << std::endl; #elif LOG(INFO) << "stepwise.max: " << fract2fps_int(frmival.stepwise.max); #endif maxRate = fract2fps_int(frmival.stepwise.max); } fmtInfo.rate = (maxRate > fmtInfo.rate) ? maxRate : fmtInfo.rate; frmival.index++ ; } resolutions.push_back(fmtInfo); frmsize.index++; } break; } else fmt.index ++; } #ifdef service_debug std::cout << "End Search format resolutions" << std::endl;; #elif LOG(ERROR) << "End Search format resolutions"; #endif close(fd); #endif return resolutions; }
- getCameraFormats()
获取摄像头格式信息列表。
std::list<CameraInfo> CameraEvent::getCameraFormats() { std::list<CameraInfo> cameraInfos; std::map<std::string, CameraCardBindDeviceName> cameras = getCameraList(); #ifdef service_debug std::cout << "Start Search USB Camera VID and PID" << std::endl; #endif #ifdef _WIN32 #elif __linux__ std::map<std::string, std::string> vpIDs = getInputVPIDs(); for(auto it=cameras.begin(); it != cameras.end(); it++) { CameraInfo camInfo = {.cameraCardName = it->first, .cameraCardNameOld = it->second.cardNameOld, .cameraDeviceName=it->second.cameraDeviceName}; camInfo.formats = getCameraResolutions(camInfo.cameraDeviceName); camInfo.vid = vpIDs[camInfo.cameraCardNameOld].substr(0, 4); camInfo.pid = vpIDs[camInfo.cameraCardNameOld].substr(5, 4); cameraInfos.push_back(camInfo); } #endif #ifdef service_debug std::cout << "Output Cameras Info:" << std::endl; for(auto it: cameraInfos) { std::cout << it.cameraCardName << " " << it.cameraCardNameOld << " " << it.cameraDeviceName << " " << it.vid << " " << it.pid << std::endl; for(auto itr : it.formats) { std::cout << itr.height << " " << itr.width << " " << itr.rate << std::endl; } } #endif return cameraInfos; }
- CameraEvent::getInputVPIDs()
获取摄像头设备的VID,PID。在查相关资料的时候,经常能看到通过 摄像头名称获取VID.PID信息,但是在实际中打印摄像头名称全量并没有获取到,另一个种方法是 通过Input 事件 过滤获取,具体链接与源码不贴了。但是直接过滤为错误的。‘vid’,‘pid’。
博主获取VID,PID为两种情况 一种为 现有的video设备。通过 ioctl(fd, EVIOCGID, &inputId) 获取对应的信息。 第二种为 热拔插事件的时候,通过input事件来过滤vid,pid。
static int get_intputdevice_info(std::string file, std::string &cardname, std::string &vpID) { int fd = open(file.c_str(), O_RDWR); std::string bus_info; char cardName[256] = ""; struct input_id inputId; if (fd < 0) return 1; int err_id = ioctl(fd, EVIOCGID, &inputId); if (err_id) { #ifdef service_debug std::cout << "err_id:" << err_id << std::endl; #elif LOG(INFO) << "err_id:" << err_id << std::string(cardName); #endif } else { std::stringstream buf; buf << std::hex << std::setw(4) << std::setfill('0') << inputId.vendor << ":" << std::setw(4) << std::setfill('0') << inputId.product; buf >> vpID; int len = ioctl(fd, EVIOCGNAME(sizeof(cardName)), cardName); #ifdef service_debug std::cout << "cardName:" << std::string(cardName, len) << " VID/PID value:" << vpID << std::endl; #elif LOG(INFO) << "cardName:" << std::string(cardName, len) << " VID/PID value:" << vpID; #endif } close(fd); cardname = reinterpret_cast<const char *>(cardName);; return err_id; } std::map<std::string, std::string> CameraEvent::getInputVPIDs() { std::map<std::string, std::string> inputInfos; #ifdef _WIN32 #elif __linux__ DIR *dp; struct dirent *ep; dev_vec files; dp = opendir("/dev/input"); if (dp == nullptr) { perror ("Couldn't open the directory"); return {}; } while ((ep = readdir(dp))) if (std::string(ep->d_name).find("event") != std::string::npos) files.push_back(std::string("/dev/input/") + ep->d_name); closedir(dp); std::sort(files.begin(), files.end(), sort_on_device_name); for (const auto &file : files) { std::string card,vpID; int err_id = get_intputdevice_info(file, card, vpID); if(err_id) continue; if(!inputInfos.count(card)){ inputInfos[card] = vpID; } } #endif return inputInfos; }
- addCameraInfo(std::string devicename, std::string vid, std::string pid, std::list &cameras)
热拔插 :插入事件
bool CameraEvent::addCameraInfo(std::string devicename, std::string vid, std::string pid, std::list<CameraInfo> &cameras) { CameraInfo info = {.cameraCardName = "", .cameraCardNameOld = "", .cameraDeviceName = devicename, .pid = pid, .vid = vid}; #ifdef _WIN32 #elif __linux__ int fd = open(devicename.c_str(), O_RDWR); std::string bus_info; std::string card; struct v4l2_capability vcap; if (fd < 0){ #ifdef service_debug std::cout << "Open file fail:" << devicename << std::endl; #elif LOG(WARNING) << "Open file fail:" << devicename << std::string(bus_info); #endif return false; } int err = ioctl(fd, VIDIOC_QUERYCAP, &vcap); bool is_mate = 0; if (err) { } else { if(vcap.device_caps == (V4L2_CAP_STREAMING + V4L2_CAP_EXT_PIX_FORMAT + V4L2_CAP_VIDEO_CAPTURE)) { bus_info = reinterpret_cast<const char *>(vcap.bus_info); card = reinterpret_cast<const char *>(vcap.card); #ifdef service_debug std::cout << std::string(card) <<" "<< std::string(devicename) << " " << std::string(bus_info) << std::endl; #elif LOG(INFO) << std::string(card) << std::string(devicename) << std::string(bus_info); #endif }else if(vcap.device_caps == (V4L2_CAP_STREAMING + V4L2_CAP_META_CAPTURE + V4L2_CAP_EXT_PIX_FORMAT)) is_mate = true; } close(fd); if(err || is_mate) { #ifdef service_debug std::cout << "Open devicename fail:" << devicename << " or deveice type is mate" <<std::endl; #elif LOG(WARNING) << "Open devicename fail:" << devicename << " or deveice type is mate"; #endif return false; } bool InsertFlags = false; int count = 0;; for(auto it: cameras) { if(std::string(card) == it.cameraCardNameOld) { count++; InsertFlags = true; } } info.cameraCardName = (count) ? card + "(" + std::to_string(count) + ")" : card; info.cameraCardNameOld = card; info.formats = getCameraResolutions(devicename); #endif cameras.push_back(info); return true; }
- removeCameraInfo(std::string cardName, std::list &cameras)
热拔插:拔出事件
bool CameraEvent::removeCameraInfo(std::string cardName, std::list<CameraInfo> &cameras) { auto it = cameras.begin(); bool ret = false; for(it; it != cameras.end(); it++) { if((*it).cameraCardName == cardName) { ret = true; it = cameras.erase(it); cameraGstPushStreamStop((*it).cameraDeviceName); } } return ret; }
- udevadmMonitor(struct udev udev, struct udev_monitor &kernelMonitor, fd_set &readFds)
热拔插检测事件
#ifdef __linux__ int CameraEvent::udevadmMonitor(struct udev *udev, struct udev_monitor* &kernelMonitor, fd_set &readFds) { if(getuid() != 0) { #ifdef service_debug std::cout << "root privileges needed to subscribe to kernel events." << std::endl; #elif LOG(INFO) << "root privileges needed to subscribe to kernel events." ; #endif udev_monitor_unref(kernelMonitor); return 0; } #ifdef service_debug std::cout << "monitor will print the received events." << std::endl; #elif LOG(INFO) << "monitor will print the received events." ; #endif kernelMonitor = udev_monitor_new_from_netlink(udev, "udev"); if(kernelMonitor == nullptr) { udev_monitor_unref(kernelMonitor); return 3; } //事件过滤器:过滤 video4linux 事件 udev_monitor_filter_add_match_subsystem_devtype(kernelMonitor, "video4linux", nullptr); if(udev_monitor_enable_receiving(kernelMonitor) < 0) { udev_monitor_unref(kernelMonitor); return 4; } #ifdef service_debug std::cout << "UEVENT the kernel uevent:" << std::endl; #elif LOG(INFO) << "UEVENT the kernel uevent:" ; #endif return 1; } //单次事件具体信息获取 EventInfo CameraEvent::udevadmMonitorItem(struct udev_monitor* &kernelMonitor, fd_set &readFds) { EventInfo info; int fdCount = 0; FD_ZERO(&readFds); if(kernelMonitor != nullptr) { FD_SET(udev_monitor_get_fd(kernelMonitor), &readFds); } fdCount = select(udev_monitor_get_fd(kernelMonitor)+1, &readFds, nullptr, nullptr, nullptr); if(!fdCount) { if(errno != EINTR) { #ifdef service_debug std::cout << "error receiving uevent message" << std::endl; #elif LOG(INFO) << "error receiving uevent message:" ; #endif } return {}; } if((kernelMonitor != nullptr) && FD_ISSET(udev_monitor_get_fd(kernelMonitor), &readFds)) { struct udev_device *device = udev_monitor_receive_device(kernelMonitor); if(device == nullptr) { return {}; } if(std::string(udev_device_get_action(device)) == std::string("add") || std::string(udev_device_get_action(device)) == std::string("remove")) { struct udev_list_entry *devAttributes; udev_list_entry_foreach(devAttributes, udev_device_get_properties_list_entry(device)) { std::string name(udev_list_entry_get_name(devAttributes)); std::string value(udev_list_entry_get_value(devAttributes)); if(name == std::string("ACTION")) info.action = (value == std::string("add")) ? true : false; if(name == std::string("ID_V4L_PRODUCT")) info.cameraName = value; if(name == std::string("ID_MODEL_ID")) info.pid = value; if(name == std::string("ID_VENDOR_ID")) info.vid = value; } info.cameraDeviceName = reinterpret_cast<const char *>(udev_device_get_devnode(device)); #ifdef service_debug std::cout << udev_device_get_action(device) << " " << udev_device_get_devpath(device) << " " << udev_device_get_subsystem(device) << " " << udev_device_get_devnode(device) << " " << info.cameraName << " " << info.vid << " " << info.pid << " " << std::endl; #elif LOG(INFO) << std::string(udev_device_get_action(device)) << " " << std::string(udev_device_get_devpath(device)) << " " << std::string(udev_device_get_subsystem(device)) << " " << std::string(udev_device_get_devnode(device)) << " " << info.cameraName << " " << info.vid << " " << info.pid; #endif } udev_device_unref(device); } return info; } #endif
测试样例
- camera.cpp
#include "cameraEvent.h" #include <libudev.h> #include <signal.h> #undef asmlinkage #ifdef __i386__ #define asmlinkage __attribute__((regparm(0))) #else #define asmlinkage #endif static int udev_exit = 0; static void asmlinkage sig_handler(int signum) { if (signum == SIGINT || signum == SIGTERM) udev_exit = 1; } int main() { struct sigaction act; memset(&act, 0x00, sizeof(struct sigaction)); act.sa_handler = (void (*)(int)) sig_handler; sigemptyset(&act.sa_mask); act.sa_flags = SA_RESTART; sigaction(SIGINT, &act, nullptr); sigaction(SIGTERM, &act, nullptr); std::list<CameraInfo> cameras = CameraEvent::instance()->getCameraFormats(); CameraEvent::instance()->cameraGstPushStreamStart((*cameras.begin()).cameraDeviceName, 640, 480, "172.26.106.87", "5600"); struct udev *udev = udev_new(); if(udev == nullptr) { udev_unref(udev); return 0; } fd_set readFds; struct udev_monitor* kernelMonitor = nullptr; int ret = CameraEvent::instance()->udevadmMonitor(udev, kernelMonitor, readFds); if(kernelMonitor == nullptr) { std::cout << "kernelMonitor == nullptr" << std::endl; return 0; } while(!udev_exit) { EventInfo info = CameraEvent::instance()->udevadmMonitorItem(kernelMonitor, readFds); if(info.cameraName == "") continue; if(info.action) { bool ret = CameraEvent::instance()->addCameraInfo(info.cameraDeviceName, info.vid, info.pid, cameras); std::cout << "Add: " << cameras.size() << std::endl; } else { bool ret = CameraEvent::instance()->removeCameraInfo(info.cameraName, cameras); std::cout << "Remove: " << cameras.size() << std::endl; } } std::cout << "Ouput result: " << ret << std::endl; udev_unref(udev); CameraEvent::instance()->cameraGstPushStreamStop((*cameras.begin()).cameraDeviceName); return 0; }
- cmakelists.txt
cmake_minimum_required(VERSION 3.10) # 工程名称 project(CameraTest) include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) conan_basic_setup() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -fpermissive -g") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fPIC -fpermissive -g") set(CMAKE_INCLUDE_CURRENT_DIR ON) SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) set(service_debug true) add_definitions(-Dservice_debug) find_package(Poco) # 包含头文件 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src/deviceManager/cameraManager) # 头文件包含目录 include_directories(${Poco_INCLUDE_DIRS}) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCE_FILES) aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/../src/deviceManager/cameraManager camer_FILES) add_executable(CameraTest ${camer_FILES} ${SOURCE_FILES} ) target_link_libraries(CameraTest udev ${CONAN_LIBS})
- conanfile.py
from conans import ConanFile, CMake, tools import os class CameraManager(ConanFile): name = "CameraManger" version = "1.0.0" settings = "os", "compiler", "build_type", "arch" generators = [("cmake"), ("cmake_find_package"), ("gcc")] build_policy = "always" def export_sources(self): self.copy("CMakeLists.txt") self.copy("*", dst="src", src="src") self.copy("*", dst="config", src="config") self.copy("*", dst="deployment", src="deployment") def build(self): cmake = CMake(self) cmake.configure() cmake.build() def imports(self): self.copy("*.dll", dst="bin", src="bin") self.copy("*.dylib*", dst="bin", src="lib") self.copy('*.so*', dst='bin', src='lib') def package(self): self.copy("*", dst='bin', src='bin') def requirements(self): self.requires("boost/1.69.0") self.requires("caf/0.17.6") self.requires("yaml-cpp/0.7.0") self.requires("poco/1.11.0")
其他:并没有提供所有接口,只是提供了测试样例,以及关键函数接口说明。CMakeLists.txt 和 conanfile.py 并不通用,需要根据自己的实际配置来更改
这篇关于libudev+V4L2 linux usb摄像头列表发现以及热拔插事件的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法