linux V4L2子系统——v4l2架构(7)之V4L2应用编程

article/2025/9/29 16:45:15

linux V4L2子系统——v4l2架构(7)之V4L2应用编程

备注:
  1. Kernel版本:5.4
  2. 使用工具:Source Insight 4.0
  3. 参考博客:
(1)Linux V4L2子系统-应用层访问video设备(四)
(2)深入学习Linux摄像头(一)v4l2应用编程

文章目录

  • linux V4L2子系统——v4l2架构(7)之V4L2应用编程
    • 概述
    • v4l2 API介绍
    • v4l2设备操作流程
      • 打开设备文件
      • 获取设备支持的功能
      • 获取和设置像素格式
      • 设置缓存
      • 开始采集视频
      • 处理数据
      • 停止采集视频
      • 关闭设备

概述

V4L2子系统向上提供了很多访问Video设备的接口,应用程序可以通过系统调用访问Video设备。但由于Video设备千差万别,很少有设备驱动程序能支持所有的接口功能,因此在使用之前,需要了解设备驱动程序支持的功能。

v4l2 API介绍

详见:linux V4L2子系统——v4l2的结构体(4)之ioctl

v4l2设备操作流程

V4L2支持多种接口:capture(捕获)、output(输出)、overlay(预览)等等这里讲解如何使用capture功能,下面讲解操作流程

打开设备文件

在Linux中,视频设备节点为/dev/videox,使用open函数将其打开。视频设备与其他设备一样可以视为一个文件,所以使用open打开文件。可以是阻塞打开,也可以是非阻塞打开,非阻塞打开,若没有数据,则会返回错误。

// 阻塞打开
fd = open(vmsg->dev, O_RDWR | O_NONBLOCK, 0);
if (fd == -1) {sys_log_err("Cann't open '%s': %d, %s\n",vmsg->dev, errno, strerror(errno));return -ENODEV;
}// 非阻塞打开
fd = open(vmsg->dev, O_RDWR);
if (fd == -1) {sys_log_err("Cann't open '%s': %d, %s\n",vmsg->dev, errno, strerror(errno));return -ENODEV;
}

获取设备支持的功能

在使用Video设备之前,需要了解Video设备支持哪些功能,如是图像捕获设备还是图像输出设备、可对视频信号进行何种调制。支持VBI、是否具有音频功能等。可通过VIDIOC_QUERYCAP命令获取Video设备支持的功能。最终通过检查struct v4l2_capability中的capabilities变量获取设备支持的功能。

struct v4l2_capability cap;
......
ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
if (ret < 0) {sys_log_err("%s is no V4L2 device %d-%d, %s\n", vmsg->dev, ret, errno, strerror(errno));goto error;
}// 判断是否支持某些功能
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {sys_log_err("%s isn't video capture device\n", vmsg->dev);ret = -ENODEV;goto error;
}if (!(cap.capabilities & V4L2_CAP_STREAMING)) {sys_log_err("%s does not support streaming i/o\n", vmsg->dev);ret = -ENODEV;goto error;
}
......

设备的功能保存在 struct v4l2_capability 结构体中,capabilities 变量具体表示了设备具有的功能,功能由宏定义 V4L2_CAP_XXXX 表示。

// 源码:include/uapi/linux/videodev2.h
struct v4l2_capability {__u8	driver[16];		// 驱动模块的名称,如"sun6i-video"__u8	card[32];		// 品牌名称,如"sunxi WinTV"__u8	bus_info[32];	// 总线名称,如"PCI:" + pci_name(pci_dev)__u32   version;		// 版本信息,KERNEL_VERSION__u32	capabilities;	// 设备整体的功能__u32	device_caps;__u32	reserved[3];
};

其中域 capabilities 代表设备支持的操作模式,常见的值有 V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING 表示是一个视频捕捉设备并且具有数据流控制模式;另外 driver 域需要和 struct video_device 中的 name 匹配。
如下:

// 源码:include/uapi/linux/videodev2.h/* Values for 'capabilities' field */
#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_VIDEO_OVERLAY		0x00000004  /* Can do video overlay      支持预览功能 */
#define V4L2_CAP_VBI_CAPTURE		0x00000010  /* Is a raw VBI capture device */
#define V4L2_CAP_VBI_OUTPUT		0x00000020  /* Is a raw VBI output device */
#define V4L2_CAP_SLICED_VBI_CAPTURE	0x00000040  /* Is a sliced VBI capture device */
#define V4L2_CAP_SLICED_VBI_OUTPUT	0x00000080  /* Is a sliced VBI output device */
#define V4L2_CAP_RDS_CAPTURE		0x00000100  /* RDS data capture */
#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY	0x00000200  /* Can do video output overlay */
#define V4L2_CAP_HW_FREQ_SEEK		0x00000400  /* Can do hardware frequency seek  */
#define V4L2_CAP_RDS_OUTPUT		0x00000800  /* Is an RDS encoder *//* Is a video capture device that supports multiplanar formats */
#define V4L2_CAP_VIDEO_CAPTURE_MPLANE	0x00001000
/* Is a video output device that supports multiplanar formats */
#define V4L2_CAP_VIDEO_OUTPUT_MPLANE	0x00002000
/* Is a video mem-to-mem device that supports multiplanar formats */
#define V4L2_CAP_VIDEO_M2M_MPLANE	0x00004000
/* Is a video mem-to-mem device */
#define V4L2_CAP_VIDEO_M2M		0x00008000#define V4L2_CAP_TUNER			0x00010000  /* has a tuner */
#define V4L2_CAP_AUDIO			0x00020000  /* has audio support */
#define V4L2_CAP_RADIO			0x00040000  /* is a radio device */
#define V4L2_CAP_MODULATOR		0x00080000  /* has a modulator */#define V4L2_CAP_SDR_CAPTURE		0x00100000  /* Is a SDR capture device */
#define V4L2_CAP_EXT_PIX_FORMAT		0x00200000  /* Supports the extended pixel format */
#define V4L2_CAP_SDR_OUTPUT		0x00400000  /* Is a SDR output device */
#define V4L2_CAP_META_CAPTURE		0x00800000  /* Is a metadata capture 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 */
#define V4L2_CAP_META_OUTPUT		0x08000000  /* Is a metadata output device */#define V4L2_CAP_TOUCH                  0x10000000  /* Is a touch device */#define V4L2_CAP_DEVICE_CAPS            0x80000000  /* sets device capabilities field */

获取和设置像素格式

有些摄像头支持多个像素格式,有的摄像头只支持一种像素格式。因此在设置像素格式之前需要了解摄像头支持的像素格式,然后再进行设置。VIDIOC_ENUM_FMT 命令枚举设备支持的像素格式,VIDIOC_S_FMT 命令设置设备当前采用的像素格式。

  • 枚举支持的像素格式
struct v4l2_fmtdesc fmtdesc = {0};
......
// 获取支持的像素格式
while (!ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)) {printf("fmt: %s\n", fmtdesc.description);fmtdesc.index++;
}
......
  • 设置像素格式
struct v4l2_format fmt = {0};memset(&fmt, 0, sizeof(fmt));
fmt.type           = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width  = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_ANY;ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
if (ret < 0) {sys_log_err("%s VIDIOC_S_FMT error %d-%d, %s\n", vmsg->dev, ret, errno, strerror(errno));memset(&fmt, 0, sizeof(fmt));if (0 == ioctl(fd, VIDIOC_G_FMT, &fmt)) {sys_log_err("%s support pixformat:\n", vmsg->dev);video_show_format(&fmt, _LOG_ERR);}goto error;
}
  • 获取像素格式
struct v4l2_format fmt = {0};
......
ret = ioctl(fd, VIDIOC_G_FMT, &fmt);
if (ret < 0) {sys_log_err("%s VIDIOC_G_FMT error %d-%d, %s\n", vmsg->dev, ret, errno, strerror(errno));goto error;
}

设置缓存

v4l2设备读取数据的方式有两种,一种是 read 方式,一种是 streaming 方式,具体需要看其功能是支持 V4L2_CAP_READWRITE 还是V4L2_CAP_STREAMING

read 方式很容易理解,就是通过 read 函数读取,那么 streaming是什么意思呢?

streaming就是在内核空间中维护一个 缓存队列,然后将内存映射到用户空间,应用读取图像数据就是一个不断地 出队列 和 入队列 的过程,如下图所示
在这里插入图片描述

申请缓冲区
Video设备捕获的视频数据应该存放在预先分配的缓冲区中。使用 VIDIOC_REQBUFS 命令向内核申请缓冲区。在申请之前,需要设置申请的缓冲区数量 nr_bufs、缓冲区数据流类型type和缓冲区内存使用方式 memory。

struct v4l2_requestbuffers *req = NULL;
req = &vmsg->reqbuf;memset(req, 0, sizeof(*req));
req->count   = buf_cnt;     //缓存数量
req->type    = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req->memory  = V4L2_MEMORY_MMAP; // 内存映射的方式,可提高效率,减少内存占用ret = ioctl(fd, VIDIOC_REQBUFS, req);
if (ret) {sys_log_err("%s request buffer failed %d-%d, %s\n", vmsg->dev, ret, errno, strerror(errno));return ret;
}

下面是struct v4l2_requestbuffers结构体的具体定义。

// 源码:include/uapi/linux/videodev2.h
/**	M E M O R Y - M A P P I N G   B U F F E R S*/
struct v4l2_requestbuffers {__u32			count;		// 缓冲区数量__u32			type;		/* enum v4l2_buf_type 数据流类型,通常为V4L2_BUF_TYPE_VIDEO_CAPTURE */__u32			memory;		/* enum v4l2_memory   缓冲区内存使用方式,通常为V4L2_MEMORY_MMAP */__u32			capabilities;__u32			reserved[1];
};enum v4l2_memory {V4L2_MEMORY_MMAP             = 1,	// 内存映射V4L2_MEMORY_USERPTR          = 2,	// 用户空间指针V4L2_MEMORY_OVERLAY          = 3,	// 内存重叠V4L2_MEMORY_DMABUF           = 4,	// DMABUF
};

映射缓存
为什么要映射缓存?

因为如果使用read方式读取的话,图像数据是从内核空间拷贝会应用空间,而一副图像的数据一般来讲是比较大的,所以效率会比较低。而如果使用映射的方式,讲内核空间的内存应用到用户空间,那么用户空间读取数据就想在操作内存一样,不需要经过内核空间到用户空间的拷贝,大大提高效率

映射缓存需要先查询缓存信息,然后再使用缓存信息进行映射,下面是一个例子

struct video_buff {void* start; size_t length;
};
......struct video_buff *vbuff = NULL;
struct v4l2_buffer buf;
......vbuff = (struct video_buff *)calloc(req->count, sizeof(struct video_buff));
if (NULL == vbuff) {sys_log_err("%s request vbuff failed %d, %s\n", vmsg->dev, errno, strerror(errno));return -ENOMEM;
}for (i = 0; i < req->count; i++) {memset(&buf, 0, sizeof(buf));buf.type    = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory  = V4L2_MEMORY_MMAP;buf.index   = i;ret = ioctl(fd, VIDIOC_QUERYBUF, &buf);if (ret) {sys_log_err("%s VIDIOC_QUERYBUF faild %d-%d, %s\r\n", vmsg->dev, ret, errno, strerror(errno));goto mmap_faild;}vbuff[i].length = buf.length;vbuff[i].start = mmap(NULL,buf.length,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_LOCKED,fd,buf.m.offset);if (MAP_FAILED == vbuff[i].start) {ret = (int)MAP_FAILED;sys_log_err("%s map buff faild\r\n", vmsg->dev);goto mmap_faild;}
}
......

将所有的缓存放入队列

struct v4l2_buffer buf;
enum v4l2_buf_type type;
struct v4l2_requestbuffers *req = NULL;
......req = &vmsg->reqbuf;
for (i = 0; i < req->count; i++) {memset(&buf, 0, sizeof(buf));buf.type     = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory   = V4L2_MEMORY_MMAP;buf.index    = i;ret = ioctl(vmsg->video_fd, VIDIOC_QBUF, &buf);if (ret < 0) {sys_log_err("%s VIDIOC_QBUF failed:%d-%d, %s\n", vmsg->dev, ret, errno, strerror(errno));goto error;}
}
......

开始采集视频

经过前面的准备工作,现在可以使能设备,开始采集视频数据了。VIDIOC_STREAMON命令使能设备,开始采集视频。

enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
......
ret = ioctl(vmsg->video_fd, VIDIOC_STREAMON, &type);
if (ret < 0) {sys_log_err("%s VIDIOC_STREAMON START faild::%d-%d, %s\n", vmsg->dev, ret, errno, strerror(errno));goto error;
}
......

处理数据

struct v4l2_buffer v4l2_buf = {0};
for (;;) {ret = ioctl(fd, VIDIOC_QBUF, &v4l2_buf);  // 从环形队列中获取一个缓冲区......data = buf[v4l2_buf.index].start;    // 缓冲区地址length = buf[v4l2_buf.index].length  // 缓冲区数据长度......ret = ioctl(fd, VIDIOC_QBUF, &v4l2_buf);  // 将缓冲区放入环形队列中......
}

停止采集视频

应用可使用VIDIOC_STREAMOFF命令停止视频采集,同时禁止设备。

enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
......
ret = ioctl(vmsg->video_fd, VIDIOC_STREAMOFF, &type);
if (ret < 0) {sys_log_err("%s VIDIOC_STREAMONVIDIOC_STREAMOFF STOP faild::%d-%d, %s\n", vmsg->dev, ret, errno, strerror(errno));goto error;
}

关闭设备

不再使用设备时,可使用close关闭设置,close调用后之前申请的缓冲区会被释放。


http://chatgpt.dhexx.cn/article/fHZ7eXVC.shtml

相关文章

[2022.8.15]v4l2-ctl基本使用方法

v4l2-ctl使用帮助可以参考&#xff1a;https://www.mankier.com/1/v4l2-ctl 1 v4l2-ctl --list-devices 列出所有设备 USB 2.0 Camera: USB Camera (usb-0000:00:14.0-9):/dev/video0/dev/video1 一个USB camera对应两个设备&#xff1a;一个是图像/视频采集&#xff0c;一…

V4L2应用层分析

V4L2应用层分析 一、总述二、例子 一、总述 V4L2&#xff0c;即Video for Linux 2&#xff0c;是第二代为Linux打造的音频、视频驱动。相比第一代V4L&#xff0c;V4L2支持更多的设备&#xff0c;同时更加稳定。现今的视频设备&#xff0c;如USB摄像头&#xff0c;基本都支持V4…

v4l2数据结构分析

v4l2数据结构分析 文章目录 v4l2数据结构分析Video4Linux2设备v4l2_device媒体设备media_deviceVideo4Linux2子设备v4l2_subdevVideo4Linux2子设备的操作集v4l2_subdev_opsVideo4Linux2子设备的内部操作集v4l2_subdev_internal_opsVideo4Linux2控制处理器v4l2_ctrl_handlerVide…

摄像头V4L2编程应用开发

1.V4L2简介 Video for Linux two(Video4Linux2)简称V4L2&#xff0c;是V4L的改进版。V4L2是linux系统操作系统下一套用于采集图片、视频、和音视频数据的通用API接口&#xff0c;配合适当的视频采集设备和相应的驱动程序&#xff0c;可以实现图片、视频、音频等的采集。 在linu…

V4L2框架

前言 在分析v4l2之前最好具有的知识&#xff1a; 1.字符设备,因为v4l2是被枚举为字符设备。 2.内存分配和映射,比如相关数据结构的分配和buffer。 3.DMA&#xff0c;因为v4l2的数据传输用到了DMA。 4.I2C&#xff0c;因为很多传感器都是用的i2c接口。 5.文件系统的基本操作&am…

V4L2系列 之 V4L2驱动框架

目录 前言一、V4L2驱动框架概览1、应用层 -》中间层-》驱动层2、主要代码文件(Linux 4.19版本内核) 二、怎么写V4L2驱动1、如何写一个设备的驱动&#xff1f;2、Video设备主要结构体3、怎么写V4L2驱动 三、V4L2的调试工具1、v4l2-ctl2、dev_debug3、v4l2-compliance 前言 本篇文…

V4L2框架概述

原文链接 本文开启 linux 内核 V4L2 框架部分的学习之旅&#xff0c;本文仅先对 V4L2 的框架做一个综述性的概括介绍&#xff0c;然后接下来的文章中会对 V4L2 框架的各个子模块进行一个全面的介绍&#xff0c;包括每一部分的实现原理&#xff0c;如何使用&#xff0c;用在什么…

V4l2框架分析

Table of Contents 1.V4L2框架概述 1.1 v4l2设备应用层流程 1.2 内核V4L2模块 1.2.1 video_device 1.2.2 v4l2_subdev 1.2.3 videobuf2 2. video_device结构体 2.1 图像处理模块 2.2 video_device处理流程 2.2.1 video_device 结构体成员介绍: 3. video_buf2 3.1 …

V4L2

V4L2(video 4 linux 2) V4L2有一段历史了。大约在1998的秋天&#xff0c;它的光芒第一次出现在Bill Dirks 的眼中。经过长足的发展&#xff0c;它于2002年11 月&#xff0c;发布2.5.46 时&#xff0c;融入了内核主干之中。然而直到今天&#xff0c;仍有一部分内核驱不支持新的A…

V4L2简介

http://work-blog.readthedocs.org/en/latest/v4l2%20intro.html 第一章 V4L2简介 1.1、什么是v4l2 V4L2&#xff08;Video4Linux的缩写&#xff09;是Linux下关于视频采集相关设备的驱动框架&#xff0c;为驱动和应用程序提供了一套统一的接口规范。 V4L2支持的设备十分广泛&…

我们一起学linux之V4L2摄像头应用流程

一、概述 Video for Linuxtwo(Video4Linux2)简称V4L2&#xff0c;是V4L的改进版。V4L2是linux操作系统下用于采集图片、视频和音频数据的API接口&#xff0c;配合适当的视频采集设备和相应的驱动程序&#xff0c;可以实现图片、视频、音频等的采集。在远程会议、可视电话、视频…

深入学习Linux摄像头(一)v4l2应用编程

深入学习Linux摄像头&#xff08;一&#xff09;v4l2应用编程 一、什么是v4l2 二、v4l2 API介绍 2.1 Querying Capabilities 2.2 Application Priority 2.3 Device Inputs and Outputs 2.4 Video Standards 2.5 Camera Control Reference 2.6 Image Format 2.7 Cropping, compo…

V4L2驱动框架详解

1. V4L2框架概述 1.1 v4l2设备应用层流程 1.2 内核V4L2模块 2 2. video_device 2.1图像处理模块 2.2 video注册流程 3. videobuf2 3.1 与video device的关系: 3.2 buffer类型 3.3 vb2_ops回调函数 3.4 mmap 流程 4. Subdev 4.1 概念 4.2 subdev注册流程 5. media fra…

html背景自动适应,css背景图片如何自适应?

css可以使用background-size属性设置背景图片自适应&#xff0c;为背景图片设置background-size:cover;样式即可使背景图片自适应。 css可以使用background-size属性设置背景图片自适应。background-size属性规定背景图像的尺寸。 background-size属性&#xff1a; 语法&#x…

css实现图片自适应容器的几种方式

css实现图片自适应容器 经常有这样一个场景,需要让图片自适应容器的大小。 1、img标签的方式 我们马上就能想到,把width、height 设置为100%啊。来看一哈效果。 <div class=div1><img src="./peiqi.png" alt=""> </div>.div1 {widt…

css 背景图片自适应属性整理

本篇博客主要记录一些使用 css 对背景图片自适应的操作整合 背景图片取消重复 background-image: url(image.jpg); background-repeat:no-repeat;修改前 修改后 背景图片固定&#xff08;不会随着内容滚动而改变位置&#xff09; background-image: url(image.jpg); back…

CSS实现图片自适应布局

CSS实现图片自适应布局 最轻松的写法 <div class"container">// 这里图片尺寸为440X440像素&#xff0c;<img src"./images/medium.jpg" alt"test"> </div><style>.container {width: 600px;height: 600px;border: 1p…

CSS图片自适应框架

CSS图片自适应框架 使用img来设置 <!DOCTYPE html> <html><style>body{margin: 0;padding: 10px;}#a_1{display: block;width: 100px;height: 50px;overflow: hidden;padding: 10px;border: dashed;margin: 10px;}#a_1img{width: 100%;height: 100%;}</s…

css之实现图片自适应

文章目录 一、position实现图片自适应二、padding补偿法三、图片以正方形显示 原图如下&#xff1a; 在开发中&#xff0c;想在固定的高度和宽度中显示不一样的图片时&#xff0c;就会出现压缩&#xff0c;导致图片最后显示不好看&#xff0c;以下提供几种方法进行调整&#xf…