【Linux应用】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;

在这里插入图片描述

五、V4L2应用编程流程

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

1、打开视频文件设备

  • 视频类设备对应的设备节点为/dev/videoX, X 为数字编号,通常从 0 开始 ,使用open打开节点, 应用程序能够使用阻塞模式非阻塞模式打开视频设备,如果使用非阻塞模式调用视频设备,即使尚未捕获到信息,驱动依旧会把缓存(DQBUFF)里的东西返回给应用程序。
//阻塞模式
fd = open("/dev/video0", O_RDWR);
//非阻塞模式
fd = open("/dev/video0", O_RDWR | O_NOBLOCK);

2、查询属性、功能

  • 查询设备的属性, 使用的指令为 VIDIOC_QUERYCAP,需要传入一个表示属性的结构体, 如下所示:
//查询设备的属性
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];	/* 保留 */	
};

可以从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 */

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);
  • 设置图像格式需要用到struct v4l2_format结构体,该结构体描述每帧图像的具体格式,包括帧类型以及图像的长、宽等信息。
//设置设备参数
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;
};
  • 使用摄像头设备时,type设置成V4L2_BUF_TYPE_VIDEO_CAPTURE ,使用struct v4l2_pix_format来描述摄像头属性。
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:    读取当前驱动的视频捕获格式;
  • 设置帧率,需要传入struct v4l2_streamparm结构体
ioctl(int fd, VIDIOC_S_PARM, struct v4l2_streamparm *streamparm);

4、申请帧缓存

  • 读取摄像头数据的方式有两种,一种是 read 方式(对应设备功能返回的 V4L2_CAP_READWRITE );另一种则是 streaming 方式 (使用mmap,对应设备功能返回的 V4L2_CAP_STREAMING )
//申请帧缓存
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];
    
};

例子:

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);
  • 使用 VIDIOC_REQBUFS 指令申请帧缓冲, 该缓冲区实质上是由内核所维护的,应用程序不能直接读

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

//需要查询帧缓冲的信息
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);

5、入队,开始采集

  • 将申请的缓冲帧依次放入缓冲帧输入队列,等待被图像采集设备依次填满
ioctl(int fd, VIDIOC_QBUF, struct v4l2_buffer *buf);
  • 开启采集
ioctl(int fd, VIDIOC_STREAMON, int *type); //开启视频采集
ioctl(int fd, VIDIOC_STREAMOFF, int *type); //停止视频采集

6、出队

  • 边采集边出队
ioctl(int fd, VIDIOC_DQBUF, struct v4l2_buffer *buf);

帧缓冲出队之后,接下来便可读取数据了,然后对数据进行处理, 比如对数据进行转化,如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;
}  

六、实现效果

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

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

参考资料

  • 11
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Linux摄像头V4L2编程是一种在Linux系统上使用V4L2(Video for Linux 2)接口进行摄像头编程的技术。它可以让开发者通过编写C/C++程序来控制摄像头的各种参数,如分辨率、帧率、曝光时间等,并实现视频流的采集、处理和显示等功能。这种技术在嵌入式系统、机器视觉、视频监控等领域得到广泛应用。 ### 回答2: Linux摄像头v4l2编程是一种基于Linux操作系统的摄像头驱动及其应用程序的编程技术。V4L2(Video4Linux2)是Linux下的一个视频采集标准,在Linux 装有摄像设备时,通过v4l2可以访问硬件设备,并对其进行控制及获取图像流数据。摄像头v4l2编程可以通过Linux系统下的应用程序对摄像头进行访问和控制,同时可以获取到相应的视频数据进行处理和分析。 在摄像头v4l2编程中,开发者可以使用v4l2库来进行编程,该库提供了一系列的API接口供开发者调用,通过这些接口可以实现对摄像头硬件设备的控制和调节,例如设置摄像头分辨率、亮度、对比度、曝光等参数。同时,开发者也可以通过v4l2库进行相关图像采集操作,获得摄像头的图像流数据,进行图像处理和算法分析。 在摄像头v4l2编程中,开发者需要了解一些基本的概念和技术,例如Linux系统的文件系统、驱动程序的编写、设备文件的读写操作等。此外,开发者还需要了解v4l2库的使用方式、相关API接口的调用和使用方法、摄像头图像采集的技术细节等方面的知识。 总之,Linux摄像头v4l2编程是一种基于Linux系统的应用程序开发技术,通过对v4l2库的学习和使用,开发者可以实现对摄像头硬件设备的控制和图像采集操作,为Linux系统下的视频应用程序提供了很好的支持和帮助。 ### 回答3: v4l2(Video for Linux 2)是在Linux内核中用于视频设备管理的API。它提供了摄像头、调节参数、读取视频流等功能,并且其文档资料非常充足,是Linux上最流行的视频编程接口。 v4l2编程分为锁定缓冲区(mmap)方式和非锁定缓冲区(read/write)方式。在v4l2编程中,摄像头设备会以文件方式进行管理,可以使用open()进行文件打开,并使用ioctl()控制摄像头参数。 摄像头的采集和显示需要经过如下步骤: 1. 打开设备文件:使用open()函数打开摄像头文件(/dev/video0等)。 2. 设置设备参数:可以使用ioctl()函数设置摄像头的相关参数,如视频格式、分辨率、帧率等。 3. 预览或采集:使用mmap()或read()/write()等函数进行视频流的采集或预览。 4. 销毁缓冲区:使用munmap()函数销毁缓冲区。 在v4l2编程中,使用带有ioctl()命令的结构体来设置和获取摄像头的参数。这些结构体是非常重要的,并且必须正确地填写和使用。常见的结构体包括: - v4l2_capability:获取摄像头驱动的属性信息。 - v4l2_format:设置图像的格式,如图像的大小、颜色格式等。 - v4l2_requestbuffers:请求缓冲区(用于存放采集到的图像数据)。 - v4l2_buffer:描述一个缓冲区,包括缓冲区地址和长度。 - v4l2_ioctl:控制设备驱动程序。 在v4l2编程中,一些常见的命令包括: - VIDIOC_QUERYCAP:查询设备的能力。 - VIDIOC_S_FMT:设置视频格式。 - VIDIOC_REQBUFS:请求缓冲区。 - VIDIOC_QUERYBUF:查询缓冲区的尺寸和位置。 - VIDIOC_QBUF:压入缓冲区到队列。 - VIDIOC_DQBUF:从队列取出一个缓冲区。 - VIDIOC_STREAMON:启动视频流。 - VIDIOC_STREAMOFF:停止视频流。 总的来说,v4l2编程确实有一定的复杂程度,但使用它可以非常方便地控制和操作摄像头,实现视频采集、预览和处理等功能。当然,在进行v4l2编程时,合理地理解和应用各种结构体和命令非常重要,需要多加练习和深入理解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ssq不是上上签

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值