linux学习:视频输入+V4L2

目录

V4L2 视频采集流程

代码例子

核心命令字和结构体

VIDIOC_ENUM_FMT

VIDIOC_G_FMT / VIDIOC_S_FMT / VIDIOC_TRY_FM

VIDIOC_REQBUFS

VIDIOC_QUERYBUF

VIDIOC_QBUF /VIDIOC_DQBUF

VIDIOC_STREAMON / VIDIOC_STREAMOFF


V4L2 是 Linux 处理视频的最新标准代码模块,这其中包括对视频输入设备的处理,比 如高频头(即电视机信号输入端子)或者摄像头,还包括对视频输出设备的处理。一般而言, 最常见的是使用 V4L2 来处理摄像头数据采集的问题

我们平常所使用的摄像头,实际上就是一个图像传感器,将光线捕捉到之后经过视频芯 片的处理,编码成 JPG/MJPG 或者 YUV 格式输出。而通过 V4L2 我们可以很方便地跟摄像 头等视频设备“沟通”,比如设置或者获取它们的工作参数

V4L2 视频采集流程

在内核中,摄像头所捕获的视频数据,我们可以通过一个队列来存储,我们所做的工作 大致是这样的:首先配置好摄像头的相关参数,使之能正常工作,然后申请若干个内核视频 缓存,并且将它们一一送到队列中,就好比三个空盘子被一一放到传送带上一样。然后我们 还需要将这三个内核的缓存区通过 mmap 函数映射到用户空间,这样我们在用户层就可以 操作摄像头数据了,紧接着我们就可以启动摄像头了开始数据捕获,每捕获一帧数据我们就 可以做一个出队操作,读取数据,然后将读过数据的内核缓存再次入队,依次循环

代码例子

// 1,打开摄像头设备文件
int cam_fd = open("/dev/video3", O_RDWR);// 2,获取摄像头当前的采集格式
struct v4l2_format *fmt = calloc(1, sizeof(*fmt));
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(cam_fd, VIDIOC_G_FMT, fmt);
show_camfmt(fmt); // 显示具体参数(详见 v4l2_jpeg_videostream.c)// 3,配置摄像头的采集格式为 JPEG
bzero(fmt, sizeof(*fmt));
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt->fmt.pix.width = lcdinfo.xres;
fmt->fmt.pix.height = lcdinfo.yres;
fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
ioctl(cam_fd, VIDIOC_S_FMT, fmt);// 4,设置即将要申请的摄像头缓存的参数
int nbuf = 3;
struct v4l2_requestbuffers reqbuf;
bzero(&reqbuf, sizeof(reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = nbuf;// 5,使用该参数 reqbuf 来申请缓存
ioctl(cam_fd, VIDIOC_REQBUFS, &reqbuf);// 6,根据刚设置的 reqbuf.count 的值,来定义相应数量的 struct v4l2_buffer
// 每一个 struct v4l2_buffer 对应内核摄像头驱动中的一个缓存
struct v4l2_buffer buffer[nbuf];
int length[nbuf];
unsigned char *start[nbuf];
for (i = 0; i < nbuf; i++) {bzero(&buffer[i], sizeof(buffer[i]));buffer[i].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buffer[i].memory = V4L2_MEMORY_MMAP;buffer[i].index = i;ioctl(cam_fd, VIDIOC_QUERYBUF, &buffer[i]);length[i] = buffer[i].length;start[i] = mmap(NULL, buffer[i].length, PROT_READ | PROT_WRITE, MAP_SHARED, cam_fd, buffer[i].m.offset);ioctl(cam_fd, VIDIOC_QBUF, &buffer[i]);
}// 7,启动摄像头数据采集
enum v4l2_buf_type vtype = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(cam_fd, VIDIOC_STREAMON, &vtype);
struct v4l2_buffer v4lbuf;
bzero(&v4lbuf, sizeof(v4lbuf));
v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4lbuf.memory = V4L2_MEMORY_MMAP;// 8,循环读取摄像头数据
i = 0;
while (1) {// 从队列中取出填满数据的缓存v4lbuf.index = i % nbuf;// VIDIOC_DQBUF 在摄像头没数据的时候会阻塞ioctl(cam_fd, VIDIOC_DQBUF, &v4lbuf);shooting(start[i % nbuf], length[i % nbuf], fb_mem); // 显示到 LCD// 将已经读取过数据的缓存块重新置入队列中v4lbuf.index = i % nbuf;ioctl(cam_fd, VIDIOC_QBUF, &v4lbuf);i++;
}

核心命令字和结构体

VIDIOC_ENUM_FMT

含义:枚举出当前摄像头(驱动)所支持的所有数据格式

使用方法:ioctl(fd, VIDIOC_ENUM_FMT, struct v4l2_fmtdesc *argp);

通过迭代结构体 struct v4l2_fmtdesc 中的 index 成员,来枚举罗列支持的所有格式, 该结构体的详细信息如下

struct v4l2_fmtdesc
{ 
__u32 index; // 数据格式的索引
__u32 type; // 一般设置为 V4L2_BUF_TYPE_VIDEO_CAPTURE 
__u32 flags; 
__u8 description[32]; 
__u32 pixelformat;//表示像素格式的值,是一个32位的标识符,用于指定视频数据的像素编码格式,如 YUV420、RGB24 等 
__u32 reserved[4];
};
  • 其中 type 跟 v4l2_format 中的 type 设置要一致。
  • 在成功调用ioctl 之后,description 将保存对当前获取的数据格式的描述。

VIDIOC_G_FMT / VIDIOC_S_FMT / VIDIOC_TRY_FM

含义: 1,获取当前摄像头驱动数据格式 2,设置摄像头驱动数据格式 3,尝试设置格式

具体用法:

  1. ioctl(fd, VIDIOC_G_FMT, struct v4l2_format *argp);
  2. ioctl(fd, VIDIOC_S_FMT, struct v4l2_format *argp);
  3. ioctl(fd, VIDIOC_TRY_FMT, struct v4l2_format *argp);

涉及数据结构

struct v4l2_format
{ 
__u32 type;
union
{
struct v4l2_pix_format pix;
struct v4l2_pix_format_mplane pix_mp;
struct v4l2_window win;
struct v4l2_vbi_format vbi;
struct v4l2_sliced_vbi_format sliced; __u8 raw_data[200];
} fmt;
};
  • V4l2_format 中的 fmt 是一个 union,其中哪个成员有效取决于 type 的取值,
  • 一般较常用的是取类型 type 为 V4L2_BUF_TYPE_VIDEO_CAPTURE,此时 pix 生效。
struct v4l2_pix_format
{ 
__u32 width; 
__u32 height; 
__u32 pixelformat; 
__u32 field;
__u32 bytesperline;
__u32 sizeimage; 
__u32 colorspace; 
__u32 priv;
};
  • 该结构体中的成员 pixelformat 代表视频输入驱动所使用的像素格式,常见的有 V4L2_PIX_FMT_JPEG、V4L2_PIX_FMT_YUV、V4L2_PIX_FMT_MJPG等。
  • 而成员field 代表视频帧传输的方式,选择 V4L2_FIELD_INTERLACED 为交错式

VIDIOC_REQBUFS

含义:向内核申请视频缓存(内核中处理视频数据的队列缓存)

用法: ioctl(fd, VIDIOC_REQBUFS, v4l2_requestbuffers *argp);

struct v4l2_requestbuffers
{ 
__u32 count; // 申请缓存总个数
__u32 type; // 与 struct v4l2_format 中的 type 一致
__u32 memory; 
__u32 reserved[2];
};
  •  其中 memory 的取值为 V4L2_MEMORY_MMAP 或 V4L2_MEMORY_USERPTR, 取决于,当该字段被设置为 V4L2_MEMORY_MMAP 时,count 字段才有效。

VIDIOC_QUERYBUF

含义:内核成功分配了缓存后,取得这些缓存的具体参数

用法: ioctl(fd, VIDIOC_QUERYBUF, v4l2_buffer *argp);

取得这些缓存的具体参数的目的是:这些缓存都是处在内核空间的,我们并不能直接操作他们,因此需要将他们通过 mmap 映射到用户空间,这就要求必须知道他们的大小、偏移等信息。这些信息统一被储存到如下结构体中

struct v4l2_buffer
{ 
__u32 index; // 内核缓存索引号,由用户指定,范围是[0 ~ count-1] 
__u32 type; // 与 v4l2_format 中的 type 一致
__u32 bytesused; 
__u32 flags; 
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence; 
__u32 memory; // 与 v4l2_requestbuffers 中的 memory 一致
union
{ 
__u32 offset; // 缓存相对于设备内存的偏移
unsigned long userptr;
struct v4l2_plane *planes; 
__s32 fd;
} m; 
__u32 length; // 缓存大小
__u32 reserved2; 
__u32 reserved;
};

VIDIOC_QBUF /VIDIOC_DQBUF

含义:

  1. 使一个空的(视频输入时)或者一个满的(视频输出时)缓存入队
  2. 使一个满的(视频输入时)或者一个空的(视频输出时)缓存出队

用法:

  1. ioctl(fd, VIDIOC_QBUF, v4l2_buffer *argp);
  2. ioctl(fd, VIDIOC_DQBUF, v4l2_buffer *argp);

 

  • 在尚未开启摄像头取像之前,需要将空的缓存一一入队。
  • 针对视频输入,出队的时候如果缓存没有数据,那么出队将阻塞。
  • 虽然内核对这些内存的定义是“队列”,但实际上不按顺序“加塞(插队)”也是 可以的。但一般不那么做

VIDIOC_STREAMON / VIDIOC_STREAMOFF

含义:

  1. 开启 I/O 流
  2. 关闭 I/O 流

用法:

  1. ioctl(fd, VIDIOC_STREAMON, const int *argp);
  2. ioctl(fd, VIDIOC_STREAMOFF, const int *argp);

不管 I/O 方式被设定为内存映射(MMAP)方式还是用户指针(USERPTR)方式,都可以使用 VIDIOC_STREAMON 和 VIDIOC_STREAMOFF 来启停 I/O 流。事实上,在使用 ioctl 调用 VIDIOC_STREAMON 之前,物理硬件将暂时被禁用且没有缓存被填充数据。

VIDIOC_STREAMOFF 除了终止进程的 DMA 操作(如果有的话)之外,还将解锁用户指针指向的物理内存,队列中的所有缓存都将被移除,这意味着如果是视频输入,那么那些没来得及读取的视频帧将被丢弃,如果是视频输出,那么那写没来及传输的视频帧也同样会被丢弃

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/11185.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java后端实现对象与文件接收数据(minio测试)

实现思路&#xff1a; 1. 两个接口实现&#xff0c;一个接对象数据(file)&#xff0c;一个接文件数据(json)。 2. json对象(base64String) 实体类信息 &#xff0c;请求体统一接收 3. file, String name ,String password ,String name &#xff0c; Controller层接收 统一…

如何查看PostgreSQL的版本

如何查看PostgreSQL的版本 要查看 PostgreSQL 的版本&#xff0c;有几种不同的方法可以使用&#xff0c;包括通过命令行和 SQL 查询。 1. 使用命令行 如果你有访问到服务器的命令行&#xff0c;并且 PostgreSQL 的命令行工具已经添加到了系统的 PATH 中&#xff0c;你可以非…

[muduo网络库]——muduo库noncopyable(剖析muduo网络库核心部分、设计思想)

接着之前我们[muduo网络库]——muduo库TcpConnection类&#xff08;剖析muduo网络库核心部分、设计思想&#xff09;&#xff0c;我们接下来继续看muduo库中的noncopyable。 这一类比较简单&#xff0c;并且我在另一篇博客里面&#xff0c;也有详细的介绍C 11以及muduo网络库中…

函数指针允许将函数作为参数传递或从函数返回函数

函数指针允许您将函数作为参数传递或从函数返回函数&#xff0c;从而实现灵活的行为和代码重用。它们在 C 编程中具有广泛的应用&#xff0c;包括回调、事件处理和算法选择。了解函数指针及其用法可以帮助您编写更模块化、可扩展和可定制的代码。以下是函数指针的一些实际应用示…

OpenHarmony 实战开发——如何编译OpenHarmony自带APP

概述 OpenHarmony 的主干代码是开源社区的重要学习资源&#xff0c;对于想进行应用开发和熟悉 OpenHarmony 能力的同学主干代码是非常重要的资源&#xff0c;在主干代码的 applications 目录里聚集了很多原生的应用实现&#xff0c;那么如何编译这些代码就是我们这篇文章的主要…

信息系统架构设计方法_1.ADM架构开发方法

1.TOGAF概述 TOGAF&#xff08;The Open Group Architecture Framework&#xff0c;TOGAF&#xff09;是一种开放式企业架构框架标准&#xff0c;它为标准、方法论和企业架构专业人员之间的沟通提供一致性保障。 TOGAF由国际标准权威组织The Open Group制定。The Open Group于1…

[初学rust] 01_简单打印

print println!() 基本使用 直接输出一个字符串 println!("hello world");{} 占位符 println!("{}", "hello world");

Vue3项目打包部署到云服务器的Nginx中

文章目录 一、打包vue3项目二、dist文件夹上传到服务器三、改nginx配置文件Docker安装nginx 一、打包vue3项目 npm run build 二、dist文件夹上传到服务器 将dist文件夹放到docker安装的nginx中的html目录下 三、改nginx配置文件 然后重启nginx【改了配置文件重启nginx才能…

消息中间件Kafka(PHP版本)

小编最近需要用到消息中间件&#xff0c;有需要要复习一下以前的东西&#xff0c;有需要的自取&#xff0c;强调一点&#xff0c;如果真的想了解透彻&#xff0c;一定要动手&#xff0c;脑袋会了不代表就会写了 Kafka是由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅…

等保一体机能过三级等保吗?过等保无需再买安全设备如何做到?

等保一体机能过三级等保吗&#xff1f;过等保无需再买安全设备如何做到&#xff1f; 全云在线 2024-03-28 12:08 广东 尽管等保建设的标准是统一的&#xff0c;但由于不同行业和用户规模的差异&#xff0c;建设方案呈现出多样化的特点。 虽然重点行业过等保现象确实已经十分…

免费思维13招之八:跨行业思维

免费思维13招之八:跨行业思维 免费思维的另一大战略思维——跨行业型思维。 跨行业型思维有两种:一种是通过跨行业,把自己的产品免费掉,从而赚取其他行业的利润。另一种是通过跨行业,把别人的主流产品免费掉,从而增大自己产品的销量。 第一种,把自己的产品免费,从而赚…

Hadoop3.4.0 完全分布式集群 运行环境搭建 VMware Workstation 虚拟机 大数据系列 一

一 生产环境集群模式部署&#xff0c;需要多台主机&#xff0c;主机之间通过密钥相互访问. 1 配置如图 节点名字节点IP系统版本master11192.168.50.11centos 8.5slave12192.168.50.12centos 8.5slave13192.168.50.13centos 8.5 2 安装服务器 #先安装一台master11&#xff…

C++ 容器(四)——List操作

一、List定义 内部核心是双向链表结构&#xff1a; 1、list内部以双向链表形式存储元素&#xff0c;每个节点包含指向前一个节点和后一个节点的指针。 2、动态大小&#xff1a;list的大小可以根据需要动态增长或缩小&#xff0c;不需要预先指定容器的大小。 3、插入和删除效…

Kubernetes 监控管理

目录 1. Metrics Server2. Prometheus & Grafana3. cAdvisor4. 日志收集5. 告警与通知6. 最佳实践 Kubernetes 监控管理是确保集群稳定运行和应用服务质量的关键环节。它涉及收集、聚合、分析集群及其上运行的应用程序的各种指标和日志数据。 1. Metrics Server 作用&…

Kubernetes备份恢复

目录 1. etcd 备份与恢复2. Velero3. Kubernetes Checkpoint API4.最佳实践 在Kubernetes中&#xff0c;备份和恢复策略是确保集群和应用程序数据安全的关键部分。这包括了对集群元数据&#xff08;如部署、服务、PV/PVC定义等&#xff09;以及持久卷中的数据进行备份。 1. et…

Hadoop-未授权访问-内置配合命令执行RCE

一、Hadoop常见端口及配置文件 Hadoop中有多种端口&#xff0c;这些端口用于不同的服务和通信。以下是Hadoop中常见的端口以及它们的用途&#xff1a; NameNode Web界面端口 (默认: 9870)NameNode 对客户端服务端口 (默认: 8020)Secondary NameNode Web界面端口 (默认: 9868)…

图层的遮盖判定算法实现1

图层的判定算法实现1 1. 算法思路2. 增加图层类属性3. 修改图层工具类4. 置于灰色5. 调用比较置灰 图层的判定算法实现1 学习于bilibili 尚学堂官方 1. 算法思路 与它上方的那一层所有牌进行比较&#xff0c;是否有交集 有&#xff0c;则盖住了&#xff0c;显示灰色 没有&am…

GitLab CI/CD的原理及应用详解(五)

本系列文章简介&#xff1a; 在当今快速变化的软件开发环境中&#xff0c;持续集成&#xff08;Continuous Integration, CI&#xff09;和持续交付&#xff08;Continuous Delivery, CD&#xff09;已经成为提高软件开发效率、确保代码质量以及快速响应市场需求的重要手段。Gi…

Ubuntu20.4部署Cuda12.4

准备Ubuntu20.4 VM 安装Cuda12.4 1.进入如下界面安装安装Cuda12.4版本&#xff1a; CUDA Toolkit 12.4 Update 1 Downloads | NVIDIA Developerhttps://developer.nvidia.com/cuda-downloads?target_osLinux&target_archx86_64&DistributionUbuntu&target_vers…

MySQL软件安装基于压缩包

打开mysql官网网址 MySQL :: Download MySQL Community Server 本次针对版本8的安装包方式进行安装&#xff0c;下载成功后接下来对MySQL进行安装 下载后有一个以zip后缀结尾的压缩包文件 对于安装包方式安装&#xff0c;比起可视化安装省去了许多安装步骤&#xff0c;这里直接…