ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

libudev+V4L2 linux usb摄像头列表发现以及热拔插事件

2021-09-22 14:29:51  阅读:324  来源: 互联网

标签:libudev std usb int udev 热拔 param 摄像头 string


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,std,usb,int,udev,热拔,param,摄像头,string
来源: https://blog.csdn.net/u011218356/article/details/120414220

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有