libudev+V4L2 Linux: обнаружение списка USB-камер и события горячего подключения
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В качестве модуля управления камерой,Используется для управления камер,В основном длягорячая заменасобытие, соответствующие добавления и удаления, а также управление push-уведомлениями. и достичь кроссплатформенности,совместимыйwindows&&linux(X86_64,arm,аарх64). В настоящее время объясняются только части, связанные с событиями. Модуль управления необходимо разработать в соответствии с реальными потребностями бизнеса.

Объяснение исходного кода

Структура файла

Язык кода: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: Информация о камере: Имя камеры(Внешнее имя камеры),Оригинальное название камеры,Имя драйвера камеры,идентификатор камеры, видео камеры,список кадров
EventInfo: информация о событиях (в основном записывает события ежедневного подключения и отключения, но есть и другие события, поэтому они не называются событиями горячего подключения) событиетип,Имя камеры,Имя драйвера камеры,видео с веб-камеры,идентификатор камеры
Язык кода:javascript
копировать
struct FormatInfo
{
    unsigned int width;
    unsigned int height;
    unsigned int rate;
};

struct CameraCardBindDeviceName
{
    std::string cardNameOld;
    std::string cameraDeviceName;
};

//Поскольку устройство камеры имеет одно и то же имя, невозможно отличить требуемую камеру от имени камеры, поэтому создается псевдоним для различения устройства камеры и управления им.
//Правила именования: eg. Есть два устройства камеры под названием 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;
};
  • Класс событий камеры

Класс событий камеры, основной рабочий класс, предоставляет внешний интерфейс. Отвечает за бизнес, связанный с событиями камеры (сбор списка, события горячего подключения, информация о камере)

Интерфейс функции push не имеет практического значения и не будет здесь подробно описываться.

Язык кода:javascript
копировать
class CameraEvent
{
private:
    /* data */
public:
    ~CameraEvent();
    static CameraEvent* instance();
    /**
     * @name: Получить список камер
     * @msg: 
     * @param {*}
     * @return Вернуться к списку камер — карта
     */    
    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 {*} Вид событий входа камеры, список 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()
Язык кода:javascript
копировать
CameraEvent* CameraEvent::instance()
{
    static CameraEvent cameraEvent;
    return &cameraEvent;
}

getCameraList()

Основной рабочий процесс: просмотр класса видео в папке /dev/, сохранение, фильтрация и сортировка. Просмотрите, отфильтруйте и получите необходимый список камер.

Язык кода:javascript
копировать
В убунту 18.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?

Язык кода: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)

Получите разрешение камеры на основе имени устройства, например, /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 получается через имя камеры. Однако на практике полное имя камеры не получается. Другой метод — получить его через фильтрацию входных событий. конкретная ссылка и исходный код не публикуются. Но фильтровать напрямую — неправильно. «вид», «пид».

Язык кода:javascript
копировать
Есть две ситуации, в которых блоггеры могут получить VID и PID. один для Имеющееся видеооборудование. проходить ioctl(fd, EVIOCGID, &inputId)  Получите соответствующую информацию.
Второй тип – это горячая заменасобытие, фильтр видео,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
Язык кода:javascript
копировать
#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
Язык кода:javascript
копировать
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
Язык кода:javascript
копировать
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 Он не является универсальным и его необходимо изменить в соответствии с вашей фактической конфигурацией.

boy illustration
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода