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相关部分,管理模块需要根据实际业务需求来自己进行针对性设计。
源码讲解
文件结构
代码语言:javascript复制src/deviceManager/cameraManager
├── cameraEvent.cpp
├── cameraEvent.h
├── cameraManager.cpp
├── cameraManager.h
└── README.md
tests
├── camera.cpp
├── CMakeLists.txt
└── conanfile.py
详解
- 相关结构定义
主要定义信息结构如下:
代码语言:javascript复制FormatInfo: 帧信息 . 帧宽 . 帧高 . 帧率
CameraCardBindDeviceName: 摄像头绑定名称: 摄像头原始名称, 摄像头驱动名称
CameraInfo: 摄像头信息:摄像头名称(摄像头对外名称),摄像头原始名称,摄像头驱动名称,摄像头pid,摄像头vid,帧列表
EventInfo: 事件信息(主要记录日插拔事件,但是还有其他事件,所以没有命名为热插拔事件) 事件类型,摄像头名称,摄像头驱动名称,摄像头vid,摄像头pid
代码语言:javascript复制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;
};
- 摄像头事件类
摄像头事件类,主要工作类,提供对外接口。负责摄像头事件的相关业务(列表获取,热拔插事件,摄像头信息)
推流函数接口,没什么实际意义就不具体放了。
代码语言:javascript复制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 类,存储,过滤,排序。遍历,过滤,获取所需要的摄像头列表。
代码语言:javascript复制在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怎么回事
代码语言:javascript复制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 获取对应摄像头分辨率。
代码语言:javascript复制在实际中,摄像头通常支持多种视频格式,yuyv,mjeg等其他格式,但是在博主的实际使用中,只是用yuyv格式,所以只获取了yuyv对应分辨率列表。
if(fmt.pixelformat == V4L2_PIX_FMT_YUYV) 来进行过滤。
代码语言:javascript复制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()
获取摄像头格式信息列表。
代码语言:javascript复制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’。
代码语言:javascript复制博主获取VID,PID为两种情况 一种为 现有的video设备。通过 ioctl(fd, EVIOCGID, &inputId) 获取对应的信息。
第二种为 热拔插事件的时候,通过input事件来过滤vid,pid。
代码语言:javascript复制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)
热拔插 :插入事件
代码语言:javascript复制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)
热拔插:拔出事件
代码语言:javascript复制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)
热拔插检测事件
代码语言:javascript复制#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 并不通用,需要根据自己的实际配置来更改