class="hide-preCode-box">

可以从capabilities 成员获得很多信息,如是否是capture设备 ,使用内存映射还是直接读的方式获取图像数据。

/* 判断是否是视频采集设备 */
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
	fprintf(stderr, "Error: No capture video device!\n");
	return -1;
}


#define V4L2_CAP_VIDEO_CAPTURE		0x00000001  /* Is a video capture device */
#define V4L2_CAP_VIDEO_OUTPUT		0x00000002  /* Is a video output device */


#define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */
#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */
#define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

3、设置设备参数

//枚举出摄像头支持的所有像素格式:VIDIOC_ENUM_FMT
ioctl(int fd, VIDIOC_ENUM_FMT, struct v4l2_fmtdesc *fmtdesc);
//枚举摄像头所支持的所有视频采集分辨率:VIDIOC_ENUM_FRAMESIZES
ioctl(int fd, VIDIOC_ENUM_FRAMESIZES, struct v4l2_frmsizeenum *frmsize);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
//设置设备参数
ioctl(int fd, VIDIOC_G_FMT, struct v4l2_format *fmt);



/**
 * struct v4l2_format - stream data format
 * @type:	enum v4l2_buf_type; type of the data stream
 * @pix:	definition of an image format
 * @pix_mp:	definition of a multiplanar image format
 * @win:	definition of an overlaid image
 * @vbi:	raw VBI capture or output parameters
 * @sliced:	sliced VBI capture or output parameters
 * @raw_data:	placeholder for future extensions and custom formats
 */
struct v4l2_format {
	__u32	 type; 			// 帧类型,应用程序设置
	union {
		struct v4l2_pix_format		pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE 视频设备使用*/
		struct v4l2_pix_format_mplane	pix_mp;  /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
		struct v4l2_window		win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
		struct v4l2_vbi_format		vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
		struct v4l2_sliced_vbi_format	sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
		struct v4l2_sdr_format		sdr;     /* V4L2_BUF_TYPE_SDR_CAPTURE */
		struct v4l2_meta_format		meta;    /* V4L2_BUF_TYPE_META_CAPTURE */
		__u8	raw_data[200];                   /* user-defined */
	} fmt;
};
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 800;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565;
if (0 > ioctl(fd, VIDIOC_S_FMT, &fmt)) { //设置格式
	perror("ioctl error");
	return -1;
}



struct v4l2_pix_format {
	__u32			width;			//视频帧的宽度(单位:像素)
	__u32			height;			//视频帧的高度(单位:像素)
	__u32			pixelformat;	//像素格式	
	__u32			field;		/* enum v4l2_field */
	__u32			bytesperline;	/* for padding, zero if unused */
	__u32			sizeimage;
	__u32			colorspace;	/* enum v4l2_colorspace */
	__u32			priv;		/* private data, depends on pixelformat */
	__u32			flags;		/* format flags (V4L2_PIX_FMT_FLAG_*) */
	union {
		/* enum v4l2_ycbcr_encoding */
		__u32			ycbcr_enc;
		/* enum v4l2_hsv_encoding */
		__u32			hsv_enc;
	};
	__u32			quantization;	/* enum v4l2_quantization */
	__u32			xfer_func;	/* enum v4l2_xfer_func */
};

VIDIOC_S_FMT:    设置当前驱动的视频捕获格式;
VIDIOC_G_FMT:    读取当前驱动的视频捕获格式;
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
ioctl(int fd, VIDIOC_S_PARM, struct v4l2_streamparm *streamparm);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

4、申请帧缓存

//申请帧缓存
ioctl(int fd, VIDIOC_REQBUFS, struct v4l2_requestbuffers *reqbuf);



struct v4l2_requestbuffers {
	__u32			count;		/* 帧缓存区的数量 */
	__u32			type;		/* enum v4l2_buf_type  缓冲帧数据格式*/
	__u32			memory;		/* enum v4l2_memory 是内存映射还是用户指针方式*/
	__u32			capabilities;
	__u32			reserved[1];
    
};
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

例子:

memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
tV4l2ReqBuffs.count = 3;
tV4l2ReqBuffs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //缓冲帧数据格式
tV4l2ReqBuffs.memory = V4L2_MEMORY_MMAP;			//内存映射的方式
iError = ioctl(Fd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

取该缓冲区的数据,我们需要将其映射到用户空间

//需要查询帧缓冲的信息
ioctl(int fd, VIDIOC_QUERYBUF, struct v4l2_buffer *buf);



//申请帧缓冲后、需要查询帧缓冲的信息用于mmap参数,调用 mmap()将帧缓冲映射到用户地址空间
iError = ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);

ptVideoDevice->pucVideBuf[i] = mmap(0 /* start anywhere */ ,
        			  tV4l2Buf.length, PROT_READ, MAP_SHARED, iFd,
        			  tV4l2Buf.m.offset);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

5、入队,开始采集

ioctl(int fd, VIDIOC_QBUF, struct v4l2_buffer *buf);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
ioctl(int fd, VIDIOC_STREAMON, int *type); //开启视频采集
ioctl(int fd, VIDIOC_STREAMOFF, int *type); //停止视频采集
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

6、出队

ioctl(int fd, VIDIOC_DQBUF, struct v4l2_buffer *buf);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

帧缓冲出队之后,接下来便可读取数据了,然后对数据进行处理, 比如对数据进行转化,如RGB888转RGB565,将转换后的数据刷到FrameBuffer上进行显示。处理完后再入队继续采集。

7、关闭采集

​ 停止采集图像数据,首先使用VIDIOC_STREAMOFF命令,关闭捕获图像数据。同时要注意取消内存映射和关闭句柄,防止不必要的内存泄漏。

enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 > ioctl(fd, VIDIOC_STREAMOFF, &type)) {
	perror("ioctl error");
	return -1;
}  
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

六、实现效果

使用USB摄像头实现效果
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考资料

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/m0_61737429/article/details/129773764","extend1":"pc","ab":"new"}">>
id="article_content" class="article_content clearfix"> id="content_views" class="markdown_views prism-atom-one-dark">

class="toc">

Linux--V4L2应用编程

Linux–V4L2应用编程

一、V4L2简介

​ V4L全称是Video for Linux,是Linux内核中标准的关于视频驱动程序,目前使用比较多的版本是Video for Linux 2,简称V4L2。它为Linux下的视频驱动提供了统一的接口,使得应用程序可以使用统一的API操作不同的视频设备。从内核空间到用户空间,主要的数据流和控制类均由V4L2驱动程序的框架来定义。 V4L2支持三类设备:视频输入输出设备VBI设备radio设备,分别会在/dev目录下产生video*、radio*和vbi设备节点。

​ 在Linux中,摄像头方面的标准化程度比较高,这个标准就是V4L2驱动程序,这也是业界比较公认的方式。

二、V4L2整体框架

在这里插入图片描述

三、V4L2视频采集过程

重要的一步是分配帧缓冲区,并将分配的帧缓冲区从内核空间映射到用户空间,然后将申请到的帧缓冲区在视频采集输入队列排队,剩下的就是等待视频数据的到来。 当启动视频采集后,驱动程序开始采集一帧图像数据,会把采集的图像数据放入视频采集输入队列的第一个帧缓冲区,一阵图像数据就算采集完成了。第一个帧缓冲区存满一帧图像数据后,驱动程序将该帧缓冲区移至视频采集输出队列,等待应用程序从输出队列取出,应用程序取出图像数据可以对图像数据进行处理或存储操作,然后将帧该缓冲区放入视频采集输入队列的尾部。驱动程序接下来采集下一帧数据,放入第二个缓冲区,同样的帧缓冲区存满一帧数据后,驱动程序将该缓冲区移至视频采集输出队列,应用程序将该帧缓冲区的图像数据取出后又将该帧缓冲区放入视频输入队列尾部,这样循环往复就实现了循环采集。流程如下图所示:

在这里插入图片描述

四、V4L2应用层主要接口

​ V4L2应用层大多数用ioctl来实现对设备的IO操作。其中V4L2提供了很多ioctl宏来和应用层交互。主要定义在uapi/linux/videodev2.h文件中。 在应用程序代码中,需要包含头文件 linux/videodev2.h 。

在进行V4L2开发中,常用的命令标识符如下:
(1) VIDIOC_REQBUFS:  分配内存;
(2) VIDIOC_QUERYBUF: 把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址;
(3) VIDIOC_QUERYCAP: 查询驱动功能;
(4) VIDIOC_ENUM_FMT: 获取当前驱动支持的视频格式;
(5) VIDIOC_S_FMT:    设置当前驱动的视频捕获格式;
(6) VIDIOC_G_FMT:    读取当前驱动的视频捕获格式;
(7) VIDIOC_TRY_FMT:  验证当前驱动的显示格式;
(8) VIDIOC_CROPCAP:  查询驱动的修剪功能;
(9) VIDIOC_S_CROP:   设置视频信号的边框;
(10)VIDIOC_G_CROP:   读取视频信号的边框;
(11)VIDIOC_QBUF:     把数据从缓存中读取出来;
(12)VIDIOC_DQBUF:    把数据放回缓存队列;
(13)VIDIOC_STREAMON: 开始视频显示函数;
(14)VIDIOC_STREAMOFF:结束视频显示函数;
(15)VIDIOC_QUERYSTD: 检查当前视频设备支持的标准,例如PAL或NTSC;
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

在这里插入图片描述

五、V4L2应用编程流程

V4L2 设备驱动框架向应用层提供了一套统一、标准的接口规范,应用程序按照该接口规范来进行应用
编程。 多数采用ioctl来实现。
在这里插入图片描述

1、打开视频文件设备

//阻塞模式
fd = open("/dev/video0", O_RDWR);
//非阻塞模式
fd = open("/dev/video0", O_RDWR | O_NOBLOCK);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

2、查询属性、功能

//查询设备的属性
ioctl(int fd, VIDIOC_QUERYCAP, struct v4l2_capability *cap);  


/**
  * struct v4l2_capability - Describes V4L2 device caps returned by VIDIOC_QUERYCAP
  *
  * @driver:	   name of the driver module (e.g. "bttv")
  * @card:	   name of the card (e.g. "Hauppauge WinTV")
  * @bus_info:	   name of the bus (e.g. "PCI:" + pci_name(pci_dev) )
  * @version:	   KERNEL_VERSION
  * @capabilities: capabilities of the physical device as a whole
  * @device_caps:  capabilities accessed via this particular device (node)
  * @reserved:	   reserved fields for future extensions
  */
struct v4l2_capability {
	__u8	driver[16];		/* 驱动名字 */
	__u8	card[32];		/* 设备名字 */	
	__u8	bus_info[32];	/* 总线名字 */
	__u32   version;		/* 版本信息 */	
	__u32	capabilities;	/* 功能 */
	__u32	device_caps;	/* 通过特定设备(节点)访问的能力 */
	__u32	reserved[3];	/* 保留 */	
};
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

可以从capabilities 成员获得很多信息,如是否是capture设备 ,使用内存映射还是直接读的方式获取图像数据。

/* 判断是否是视频采集设备 */
if (!(V4L2_CAP_VIDEO_CAPTURE & vcap.capabilities)) {
	fprintf(stderr, "Error: No capture video device!\n");
	return -1;
}


#define V4L2_CAP_VIDEO_CAPTURE		0x00000001  /* Is a video capture device */
#define V4L2_CAP_VIDEO_OUTPUT		0x00000002  /* Is a video output device */


#define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */
#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */
#define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

3、设置设备参数

//枚举出摄像头支持的所有像素格式:VIDIOC_ENUM_FMT
ioctl(int fd, VIDIOC_ENUM_FMT, struct v4l2_fmtdesc *fmtdesc);
//枚举摄像头所支持的所有视频采集分辨率:VIDIOC_ENUM_FRAMESIZES
ioctl(int fd, VIDIOC_ENUM_FRAMESIZES, struct v4l2_frmsizeenum *frmsize);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
//设置设备参数
ioctl(int fd, VIDIOC_G_FMT, struct v4l2_format *fmt);



/**
 * struct v4l2_format - stream data format
 * @type:	enum v4l2_buf_type; type of the data stream
 * @pix:	definition of an image format
 * @pix_mp:	definition of a multiplanar image format
 * @win:	definition of an overlaid image
 * @vbi:	raw VBI capture or output parameters
 * @sliced:	sliced VBI capture or output parameters
 * @raw_data:	placeholder for future extensions and custom formats
 */
struct v4l2_format {
	__u32	 type; 			// 帧类型,应用程序设置
	union {
		struct v4l2_pix_format		pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE 视频设备使用*/
		struct v4l2_pix_format_mplane	pix_mp;  /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
		struct v4l2_window		win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
		struct v4l2_vbi_format		vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
		struct v4l2_sliced_vbi_format	sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
		struct v4l2_sdr_format		sdr;     /* V4L2_BUF_TYPE_SDR_CAPTURE */
		struct v4l2_meta_format		meta;    /* V4L2_BUF_TYPE_META_CAPTURE */
		__u8	raw_data[200];                   /* user-defined */
	} fmt;
};
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 800;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565;
if (0 > ioctl(fd, VIDIOC_S_FMT, &fmt)) { //设置格式
	perror("ioctl error");
	return -1;
}



struct v4l2_pix_format {
	__u32			width;			//视频帧的宽度(单位:像素)
	__u32			height;			//视频帧的高度(单位:像素)
	__u32			pixelformat;	//像素格式	
	__u32			field;		/* enum v4l2_field */
	__u32			bytesperline;	/* for padding, zero if unused */
	__u32			sizeimage;
	__u32			colorspace;	/* enum v4l2_colorspace */
	__u32			priv;		/* private data, depends on pixelformat */
	__u32			flags;		/* format flags (V4L2_PIX_FMT_FLAG_*) */
	union {
		/* enum v4l2_ycbcr_encoding */
		__u32			ycbcr_enc;
		/* enum v4l2_hsv_encoding */
		__u32			hsv_enc;
	};
	__u32			quantization;	/* enum v4l2_quantization */
	__u32			xfer_func;	/* enum v4l2_xfer_func */
};

VIDIOC_S_FMT:    设置当前驱动的视频捕获格式;
VIDIOC_G_FMT:    读取当前驱动的视频捕获格式;
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">
ioctl(int fd, VIDIOC_S_PARM, struct v4l2_streamparm *streamparm);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

4、申请帧缓存

//申请帧缓存
ioctl(int fd, VIDIOC_REQBUFS, struct v4l2_requestbuffers *reqbuf);



struct v4l2_requestbuffers {
	__u32			count;		/* 帧缓存区的数量 */
	__u32			type;		/* enum v4l2_buf_type  缓冲帧数据格式*/
	__u32			memory;		/* enum v4l2_memory 是内存映射还是用户指针方式*/
	__u32			capabilities;
	__u32			reserved[1];
    
};
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

例子:

memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
tV4l2ReqBuffs.count = 3;
tV4l2ReqBuffs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //缓冲帧数据格式
tV4l2ReqBuffs.memory = V4L2_MEMORY_MMAP;			//内存映射的方式
iError = ioctl(Fd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

取该缓冲区的数据,我们需要将其映射到用户空间

//需要查询帧缓冲的信息
ioctl(int fd, VIDIOC_QUERYBUF, struct v4l2_buffer *buf);



//申请帧缓冲后、需要查询帧缓冲的信息用于mmap参数,调用 mmap()将帧缓冲映射到用户地址空间
iError = ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);

ptVideoDevice->pucVideBuf[i] = mmap(0 /* start anywhere */ ,
        			  tV4l2Buf.length, PROT_READ, MAP_SHARED, iFd,
        			  tV4l2Buf.m.offset);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

5、入队,开始采集

ioctl(int fd, VIDIOC_QBUF, struct v4l2_buffer *buf);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
ioctl(int fd, VIDIOC_STREAMON, int *type); //开启视频采集
ioctl(int fd, VIDIOC_STREAMOFF, int *type); //停止视频采集
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

6、出队

ioctl(int fd, VIDIOC_DQBUF, struct v4l2_buffer *buf);
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

帧缓冲出队之后,接下来便可读取数据了,然后对数据进行处理, 比如对数据进行转化,如RGB888转RGB565,将转换后的数据刷到FrameBuffer上进行显示。处理完后再入队继续采集。

7、关闭采集

​ 停止采集图像数据,首先使用VIDIOC_STREAMOFF命令,关闭捕获图像数据。同时要注意取消内存映射和关闭句柄,防止不必要的内存泄漏。

enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 > ioctl(fd, VIDIOC_STREAMOFF, &type)) {
	perror("ioctl error");
	return -1;
}  
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

六、实现效果

使用USB摄像头实现效果
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考资料

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/m0_61737429/article/details/129773764","extend1":"pc","ab":"new"}">>
注:本文转载自blog.csdn.net的ssq不是上上签的文章"https://blog.csdn.net/m0_61737429/article/details/129773764"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!