当前位置: 首页 > news >正文

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

相关文章:

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 处理视频的最新标准代码模块&…...

[AutoSar]BSW_Diagnostic_004 ReadDataByIdentifier(0x22)的配置和实现

目录 关键词平台说明背景一、配置DcmDspDataInfos二、配置DcmDspDatas三、创建DcmDspDidInfos四、创建DcmDspDids五、总览六、创建一个ASWC七、mapping DCM port八、打开davinci developer&#xff0c;创建runnabl九、生成代码 关键词 嵌入式、C语言、autosar、OS、BSW、UDS、…...

C语言笔记13

字符数组与字符串常量区别 #include <stdio.h> int main() {char str1[] "hello bit.";char str2[] "hello bit.";char *str3 "hello bit.";char *str4 "hello bit.";if(str1 str2)printf("str1 and str2 are same\n…...

JavaScript进阶——04-创建对象和继承

创建对象的几种方式 通过Object <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><title>01_Object构造函数模式</title></head><body><!--方式一: Object构造函数模式* 套路: 先创建空Ob…...

队列(详解)

一.队列的概念 队列&#xff08;Queue&#xff09;是一种常见的数据结构&#xff0c;它按照先进先出的原则管理数据。这意味着最先进入队列的元素将被最先移出队列&#xff0c;类似于现实生活中排队的场景。 在队列中&#xff0c;数据项被添加到队列的一端&#xff0c;称为队尾…...

【原创】nnUnet V1在win11下的安装与配置

安装之前可以先了解一下论文的主要内容&#xff0c;便于之后网络训练与推理&#xff0c;调试程序。 论文地址&#xff1a;nnU-Net: a self-configuring method for deep learning-based biomedical image segmentation | Nature Methods 也可以从其他博客快速浏览&#xff1a…...

C语言之指针初阶

目录 前言 一、内存与地址的关系 二、指针变量 三、野指针 四、const 五、传值调用与传址调用 总结 前言 本文主要介绍C语言指针的一些基础知识&#xff0c;为后面深入理解指针打下基础&#xff0c;因此本文内容主要包括内存与地址的关系&#xff0c;指针的基本语法&…...

异常检测的学习和实战

1.应用&#xff1a; 1.在工业上的应用 当检测设备是否处于异常工作状态时&#xff0c;可以由上图分析得到&#xff1a;那些零散的点对应的数据是异常数据。因为设备大多数时候都是处于正常工作状态的&#xff0c;所以数据点应该比较密集地集中在一个范围内&#xff0c;而那些明…...

RabbitMQ 面试题(一)

1. 简述为什么要使用 RabbitMQ ? 使用 RabbitMQ 的主要原因包括以下几点&#xff1a; 解耦&#xff1a;在复杂的系统中&#xff0c;不同的服务或组件之间往往需要通信和协作。RabbitMQ 作为消息队列&#xff0c;允许这些组件或服务通过发送和接收消息来交互&#xff0c;而无…...

org.postgresql.util.PSQLException: 错误: 关系 “dual“ 不存在

springboot 项目连接 postgreps&#xff0c;启动时报错 org.postgresql.util.PSQLException: 错误: 关系 "dual" 不存在。 查阅资料后发现这是由配置文件中的配置 datasource-dynamic-druid-validationQuery 导致的 spring:datasource:druid:stat-view-servlet:ena…...

mysql权限分类

USAGE --无权限,只有登录数据库,只可以使用test或test_*数据库 ALL --所有权限 select/update/delete/super/slave/reload --指定的权限 with grant option --允许把自己的权限授予其它用户(此用户拥有建立账号的权限) 权限级别&#xff1a; 1、. &#xff0d;&#xff0d;全…...

【C++11】列表初始化、右值引用的详细讲解(上)

前言 在一开始学C之前我们就简单的了解了一下C的发展历史。 相比较而言&#xff0c;C11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全&#xff0c;不仅功能更强大&#xff0c;而且能提升程序员的开发效率加了许多特性&#xff0c;约140个新特性。使得C…...

【JAVA进阶篇教学】第十三篇:Java中volatile关键字讲解

博主打算从0-1讲解下java进阶篇教学&#xff0c;今天教学第十三篇&#xff1a;volatile关键字讲解。 在 Java 中&#xff0c;volatile关键字是一种轻量级的同步机制&#xff0c;用于确保变量的可见性和禁止指令重排序。本文将详细解释volatile关键字的工作原理、可见性保证以及…...

蓝桥杯-地宫取宝

X 国王有一个地宫宝库&#xff0c;是 nm 个格子的矩阵&#xff0c;每个格子放一件宝贝&#xff0c;每个宝贝贴着价值标签。 地宫的入口在左上角&#xff0c;出口在右下角。 小明被带到地宫的入口&#xff0c;国王要求他只能向右或向下行走。 走过某个格子时&#xff0c;如果那个…...

带头单链表 C++实现

节点定义 带头单链表&#xff1a;我们只需要一个结点指针指向整个链表的第一个节点&#xff0c;这样我们就可以通过next指针访问整个链表内的所有节点 template<class T> struct ListNode {T _val;ListNode* _next;ListNode(const T &val):_val(val),_next(nullptr){…...

学习c#第24天 枚举类型

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace enumType { //定义枚举 public enum Week { 星期一, 星期二, 星期三, 星期四, 星期…...

TensorFlow运行bug汇总

1、ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1 解决方案 pip install urllib31.26.15 -i https://pypi.tuna.tsinghua.edu.cn/simple 升级或者降级 (TF2.1) C:\Users\Administrator>pip install urllib31.26.15 -i https://pypi.tuna.tsinghua.edu.cn/sim…...

docker部署调度程序

Dockerfile(构建初始镜像) # python:3.8-slim-buster为精简版的python FROM python:3.8-slim-buster # 1059为组的id,newgroup为组名,1088为用户的id,newuser为新用户 RUN groupadd -g 1059 newgroup && \useradd -g -u 1088 -g newgroup -m newuser USER newuser RUN…...

websocket和http协议的区别

ws(websocket)协议和http协议是两种不同的协议。 http&#xff1a;http是一种用于传输超文本的应用层协议&#xff0c;通常用于web端浏览器和web端服务器之间传输数据。http也是基于tcp的&#xff0c;但是HTTP只能在同一时刻单向发送消息&#xff0c;是一种半双工通信。&#…...

CSS之定位

目录 CSS定位为什么需要定位定位组成定位的叠放顺序拓展 CSS定位 为什么需要定位 浮动可以让多个块级盒子一行没有缝隙排列显示&#xff0c;经常用于横向排列盒子定位则是可以让盒子自由的在某个盒子内移动位置或者固定屏幕中的某个位置&#xff0c;并且可以压住其他盒子 定…...

[IM002][Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序

解决办法&#xff1a; 安装驱动 下载 ODBC Driver for SQL Server - ODBC Driver for SQL Server | Microsoft Learn...

神经网络复习--神经网络算法模型及BP算法

文章目录 神经网络模型的构成BP神经网络 神经网络模型的构成 三种表示方式&#xff1a; 神经网络的三要素&#xff1a; 具有突触或连接&#xff0c;用权重表示神经元的连接强度具有时空整合功能的输入信号累加器激励函数用于限制神经网络的输出 感知神经网络 BP神经网络 …...

【Java】/*方法的使用-快速总结*/

目录 一、什么是方法 二、方法的定义 三、实参和形参的关系 四、方法重载 五、方法签名 一、什么是方法 Java中的方法可以理解为C语言中的函数&#xff0c;只是换了个名称而已。 二、方法的定义 1. 语法格式&#xff1a; public static 返回类型 方法名 (形参列表) { //方…...

kotlin中协程相关

协程 用同步的方式写出异步的效果协程最重要的是通过非阻塞挂起和恢复实现了异步代码的同步编写方式挂起函数(suspend)不一定就是在子线程中执行的&#xff0c;但是通常在定义挂起函数时都会为它指定其他线程&#xff0c;这样挂起才有意义解决多层嵌套回调 协程不是线程&…...

(自适应手机端)物流运输快递仓储网站模板 - 带三级栏目

(自适应手机端)物流运输快递仓储网站模板 - 带三级栏目PbootCMS内核开发的网站模板&#xff0c;该模板适用于物流运输网站、仓储货运网站等企业&#xff0c;当然其他行业也可以做&#xff0c;只需要把文字图片换成其他行业的即可&#xff1b;自适应手机端&#xff0c;同一个后台…...

Navicat导出表结构到Excel或Word

文章目录 sql语句复制到excel复制到Word sql语句 SELECTcols.COLUMN_NAME AS 字段,cols.COLUMN_TYPE AS 数据类型,IF(pks.CONSTRAINT_TYPE PRIMARY KEY, YES, NO) AS 是否为主键,IF(idxs.INDEX_NAME IS NOT NULL, YES, NO) AS 是否为索引,cols.IS_NULLABLE AS 是否为空,cols.…...

Golang编译优化——稀疏条件常量传播

文章目录 一、概述二、稀疏条件常量传播2.1 初始化worklist2.2 构建def-use链2.3 更新值的lattice2.4 传播constant值2.5 替换no-constant值 一、概述 常量传播&#xff08;constant propagation&#xff09;是一种转换&#xff0c;对于给定的关于某个变量 x x x和一个常量 c …...

人工智能培训讲师咨询叶梓介绍及智能医疗技术与ChatGPT临床应用三日深度培训提纲

1、授课老师简介 叶梓&#xff0c;上海交通大学计算机专业博士毕业&#xff0c;高级工程师。主研方向&#xff1a;数据挖掘、机器学习、人工智能。历任国内知名上市IT企业的AI技术总监、资深技术专家&#xff0c;市级行业大数据平台技术负责人。 长期负责城市信息化智能平台的…...

HCIP(BGP综合实验)--8

一&#xff1a;实验要求 二&#xff1a;实现过程 &#xff08;一&#xff09;配置IP地址&#xff1a; AR1: [AR1]int g0/0/0 [AR1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [AR1-GigabitEthernet0/0/0]int l0 [AR1-LoopBack0]ip add 172.16.0.1 32 [AR1-LoopBack0]int l1 […...

深入理解C++中的Vector容器:用容器构建高效程序

文章目录 vector介绍vector常用的成员函数有关vector定义的函数vector的迭代器使用vector关于空间操作的成员函数vector的增删查改 总结 vector介绍 在C语言的库中包含有公共数据结构的实现&#xff0c;C的这个部分内容就是众所周知的STL&#xff08;标准模版库&#xff09;&a…...