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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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…

读人工智能时代与人类未来笔记01_重塑人类社会秩序

1. AlphaZero 1.1. 2017年年底&#xff0c;由谷歌旗下DeepMind公司开发的人工智能程序AlphaZero击败了当时世界上最强大的国际象棋程序Stockfish 1.1.1. AlphaZero对Stockfish的百场战绩是28胜72平0负&#xff0c;可以说获得了压倒性的胜利 1.1.2. …

手撕C语言题典——反转链表

目录 前言 一.思路 1&#xff09;创建新链表 2&#xff09;创建三个指针 二.代码实现 搭配食用更佳哦~~ 数据结构之单单单——链表-CSDN博客 数据结构之单链表的基本操作-CSDN博客 前面学了单链表的相关知识&#xff0c;我们来尝试做一下关于顺序表的经典算法题~ 前言 反转…

RocketMQ(一)

作用 1. 限流削峰 2. 异步解耦 组成 Producer&#xff1a;消息的发送者&#xff0c;生产者&#xff1b;举例&#xff1a;发件人 Consumer&#xff1a;消息接收者&#xff0c;消费者&#xff1b;举例&#xff1a;收件人 Broker&#xff1a;暂存和传输消息的通道&#xff1…

C语言 | Leetcode C语言题解之第85题最大矩形

题目&#xff1a; 题解&#xff1a; int maximalRectangle(char** matrix, int matrixSize, int* matrixColSize) {int m matrixSize;if (m 0) {return 0;}int n matrixColSize[0];int left[m][n];memset(left, 0, sizeof(left));for (int i 0; i < m; i) {for (int j …

---随笔--Java实现TCP通信(双端通信接收与发送)

---随笔--Java实现TCP通信&#xff08;双端通信接收与发送&#xff09; 引言1. 什么是TCP通信2. 服务器与客户端核心代码2.1 服务器ServerSocket端核心代码2.2 用户Socket端核心代码2.3 小贴士之关于try-with-resources自动关闭资源的使用 3. 具体服务器端实现4. 具体客户端实现…

LLM量化

Efficient Finetuning prefix tuning 针对每种任务&#xff0c;学习prefix vector 启发于prompting&#xff0c;调整上下文内容让模型去输出自己想要的内容 核心就是找到一个上下文去引导模型解决NLP生成任务 传统情况下&#xff0c;我们为了得到想要的结果&#xff0c;会…

jdk8的新特征

1&#xff1a; jdk8中新增的方法 在jdk8中对接口进行了增强&#xff0c;在jdk8之前 interface 接口名{ 静态常量&#xff1a; 抽象方法&#xff1a; } 在jdk8之后 interface 接口名{ 静态常量&#xff1a; 抽象方法&#xff1a; 默认方法&#xff1a; 静态方法&#xff1a; } 2…

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…

97. 交错字符串-----回溯、动态规划

题目链接 97. 交错字符串 - 力扣&#xff08;LeetCode&#xff09; 解答 递归回溯 题目所述为两个字符串交替组成第三个字符串&#xff0c;之前好像做过相似的题目&#xff0c;直接联想到可以考虑使用递归回溯的做法&#xff0c;让字符串s1和字符串s2分别作为起始字符串&…

vm16安装最新版本的ubuntu虚拟机,并安装g++的步骤记录

背景 低版本的ubuntu安装G一直不成功&#xff0c;干脆安装最新版的 官网下载 bing搜索ubuntu 下载完成 vm16新建虚拟机 一直下一步&#xff0c;安装完成 终端输入命令 sudo apt-get update ᅟᅠ       sudo apt install gcc ᅟᅠ      sudo apt install g

工程师工具箱系列(1)MapStruct

文章目录 工程师工具箱系列&#xff08;1&#xff09;MapStruct芸芸众生初窥门径引入POM依赖创建转换器与方法进行使用IDEA好基友 游刃有余示例说明避免编写重复转换器实现复杂灵活转换 温故知新 工程师工具箱系列&#xff08;1&#xff09;MapStruct 芸芸众生 在Java项目开发…

SpringAI 技术解析

1. 发展历史 SpringAI 的发展历史可以追溯到对 Spring 框架的扩展和改进&#xff0c;以支持人工智能相关的功能。随着人工智能技术的快速发展&#xff0c;SpringAI 逐渐成为 Spring 生态系统中的一个重要组成部分&#xff0c;为开发者提供了便捷、灵活的解决方案。 项目的灵感来…

算法提高之单词接龙

算法提高之单词接龙 核心思想&#xff1a;dfs 预处理每两个字符串之间最短的公共部分长度 求最短公共 最终字符串是最长 dfs所有开头字符串 #include <iostream>#include <cstring>#include <algorithm>using namespace std;const int N 25;int g[N][N…

雷军-2022.8小米创业思考-6-互联网七字诀之快:天下武功,唯快不破;快不是目的,快是手段;不要用战术上的勤奋掩盖战略上的懒惰。

第六章 互联网七字诀 专注、极致、口碑、快&#xff0c;这就是我总结的互联网七字诀&#xff0c;也是我对互联网思维的高度概括。 快 我们期待非常多的快&#xff0c;比如研发进展快、库存周转快、资金回笼快等等。但在这里&#xff0c;我们集中讨论的是公司在业务发展和面对…

LeetCode题练习与总结:二叉树的中序遍历--94

一、题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&#xff1a;roo…

C语言(指针)5

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸各位能阅读我的文章&#xff0c;诚请评论指点&#xff0c;关注收藏&#xff0c;欢迎欢迎~~ &#x1f4a5;个人主页&#xff1a;小羊在奋斗 &#x1f4a5;所属专栏&#xff1a;C语言 本系列文章为个人学习笔记&#x…

Busybox 在 Docker 中的部署和启动

可以使用 docker pull 指令下载 busybox:latest 镜像&#xff1a; PS C:\Users\yhu> docker pull busybox:latest latest: Pulling from library/busybox ec562eabd705: Pull complete Digest: sha256:5eef5ed34e1e1ff0a4ae850395cbf665c4de6b4b83a32a0bc7bcb998e24e7bbb St…

COX回归特征筛选

任务&#xff1a;利用cox筛选出P值小于0.05的特征 数据的格式第一列为标签&#xff0c;第二列为时间&#xff0c;第三列及后为特征 先想一想&#xff0c;想好了再更新 这里我们先举一个例子&#xff1a; import pandas as pd from lifelines import CoxPHFitter# 创建示例数…

项目管理-计算题公式【复习】

1.【进度】相关公式 1.1三点估算 PERT 三点估算法是基于 任务成本的三种估算值&#xff08;最可能成本CM&#xff0c;最乐观成本CO&#xff0c;最悲观成本CP&#xff09;来计算预期成本的方法。 三角 分布&#xff1a;预期成本&#xff08;最乐观成本最可能成本最悲观成本&am…