linux音视频采集技术: v4l2

简介

在 Linux 系统中,视频设备的支持和管理离不开 V4L2(Video for Linux 2)。作为 Linux 内核的一部分,V4L2 提供了一套统一的接口,允许开发者与视频设备(如摄像头、视频采集卡等)进行交互。无论是视频采集、处理,还是编码和显示,V4L2 都提供了强大的支持。本文将简单介绍一下 V4L2 的工作流程以及如何使用它进行视频采集。

参数介绍

v4l2并没有提供单独封装的API接口,而是通过 ioctl 系统调用以及v4l2所提供的特定参数来对设备进行控制和采集。

下面是主要的 ioctl 控制参数:

1.VIDIOC_QUERYCAP:查询设备能力。
可用于查询枚举视频设备,获取设备名、总线名、支持的能力等。并非所有设备都支持,有可能会查询失败。
相关结构定义:

struct v4l2_capability {__u8 driver[16];      // 驱动名称__u8 card[32];        // 设备名称__u8 bus_info[32];    // 设备的总线信息__u32 version;        // 驱动版本号__u32 capabilities;   // 设备支持的功能__u32 device_caps;    // 设备的具体能力__u32 reserved[3];    // 保留字段
};

2.VIDIOC_S_FMT:设置视频格式。
不同设备所支持的 pixelformat 不尽相同,所以设置的某些格式可能不会生效,比如我使用的海康摄像头只支持mjpg和yuyv。因此最好先使用 VIDIOC_ENUM_FMT 查询设备设备支持的格式,以确保设置生效。
相关结构定义:

struct v4l2_format {enum v4l2_buf_type type; // 缓冲区类型(如视频采集)union {struct v4l2_pix_format pix; // 视频帧格式// 其他格式(如 overlay、视频输出等)} fmt;
};struct v4l2_pix_format {__u32 width;          // 视频宽度__u32 height;         // 视频高度__u32 pixelformat;    // 像素格式(如 V4L2_PIX_FMT_YUYV)__u32 field;          // 场序(如逐行扫描、隔行扫描)__u32 bytesperline;   // 每行的字节数__u32 sizeimage;      // 每帧的总字节数// 其他字段
};

3.VIDIOC_REQBUFS:请求分配缓冲区。
memory类型使用MMAP,后续用于mmap内核缓冲区到用户态,避免内存拷贝
相关结构定义:

struct v4l2_requestbuffers {__u32 count;          // 请求的缓冲区数量enum v4l2_buf_type type; // 缓冲区类型enum v4l2_memory memory; // 内存类型(如 MMAP、USERPTR)// 其他字段
};

4.VIDIOC_QUERYBUF:查询缓冲区信息。
相关结构定义:

struct v4l2_buffer {__u32 index;          // 缓冲区索引enum v4l2_buf_type type; // 缓冲区类型__u32 bytesused;      // 缓冲区中实际使用的字节数__u32 flags;          // 缓冲区标志__u32 field;          // 场序struct timeval timestamp; // 时间戳// 其他字段
};

5.VIDIOC_QBUF:将缓冲区加入队列。
将申请的 v4l2_buffer 实例入队

6.VIDIOC_DQBUF:从队列中取出缓冲区。
弹出 v4l2_buffer 实例,并通过mmap映射的地址读取采集数据

7.VIDIOC_STREAMON:开始视频采集。

8.VIDIOC_STREAMOFF:停止视频采集。

9.VIDIOC_ENUM_FMT:枚举设备支持的像素格式。
用于提前枚举支持的图像采集格式,以便选择最合适的采集方式。
相关结构定义:

struct v4l2_fmtdesc {__u32 index;             // 格式索引(从 0 开始)enum v4l2_buf_type type; // 缓冲区类型(如视频采集)__u32 flags;             // 格式标志__u8 description[32];    // 格式描述__u32 pixelformat;       // 像素格式(如 V4L2_PIX_FMT_YUYV)__u32 reserved[4];       // 保留字段
};

流程

通常使用的采集流程如下:

1.查询设备能力:使用 VIDIOC_QUERYCAP 查询枚举设备是否支持采集。

2.打开设备:使用 open 打开设备节点。

3.查询设备图像能力:使用 VIDIOC_ENUM_FMT 查询设备支持的像素格式是否匹配。

4.设置视频格式:使用 VIDIOC_S_FMT 设置分辨率、像素格式等。

5.请求缓冲区:使用 VIDIOC_REQBUFS 请求分配缓冲区。

6.映射缓冲区:使用 mmap 将缓冲区映射到用户空间。

7.开始采集:使用 VIDIOC_STREAMON 开始视频采集。

8.采集数据:使用 VIDIOC_DQBUF 从队列中取出缓冲区,处理数据后使用 VIDIOC_QBUF 将缓冲区重新加入队列。

9.停止采集:使用 VIDIOC_STREAMOFF 停止视频采集。

10.释放资源:使用 munmap 释放缓冲区,并关闭设备。

代码示例

#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <fstream>
#include <vector>
#include <cstring>#define VIDEO_DEVICE "/dev/video0"
#define WIDTH 640
#define HEIGHT 480
#define FPS 30
#define OUTPUT_FILE "output.yuv"
#define BUFFER_COUNT 4 // 缓冲区数量// 检查 V4L2 调用的返回值
#define CHECK(x) \if ((x) < 0) { \std::cerr << "ioctl error at " << __FILE__ << ":" << __LINE__ << " - " << strerror(errno) << std::endl; \exit(EXIT_FAILURE); \}// 检查设备是否支持指定格式
bool is_format_supported(int fd, unsigned int pixel_format) {struct v4l2_fmtdesc fmt_desc = {};fmt_desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt_desc.index = 0;while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt_desc) == 0) {if (fmt_desc.pixelformat == pixel_format) {std::cout << "Device supports format: " << fmt_desc.description << std::endl;return true;}fmt_desc.index++;}std::cerr << "Device does not support the required format (YUV420)" << std::endl;return false;
}int main() {// 打开视频设备int fd = open(VIDEO_DEVICE, O_RDWR);CHECK(fd);// 检查设备是否支持 YUV420P 格式if (!is_format_supported(fd, V4L2_PIX_FMT_YUV420)) {close(fd);return -1;}// 设置视频格式struct v4l2_format fmt = {};fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = WIDTH;fmt.fmt.pix.height = HEIGHT;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; // YUV420P 格式fmt.fmt.pix.field = V4L2_FIELD_NONE;CHECK(ioctl(fd, VIDIOC_S_FMT, &fmt));// 检查设备是否实际设置了 YUV420P 格式if (fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_YUV420) {std::cerr << "Device does not support YUV420P format" << std::endl;close(fd);return -1;}// 设置帧率struct v4l2_streamparm streamparm = {};streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;streamparm.parm.capture.timeperframe.numerator = 1;streamparm.parm.capture.timeperframe.denominator = FPS;CHECK(ioctl(fd, VIDIOC_S_PARM, &streamparm));// 请求缓冲区struct v4l2_requestbuffers req = {};req.count = BUFFER_COUNT; // 4 个缓冲区req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;CHECK(ioctl(fd, VIDIOC_REQBUFS, &req));// 映射所有缓冲区std::vector<unsigned char *> buffers(BUFFER_COUNT);std::vector<size_t> buffer_sizes(BUFFER_COUNT);for (unsigned int i = 0; i < BUFFER_COUNT; i++) {struct v4l2_buffer buf = {};buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = i;CHECK(ioctl(fd, VIDIOC_QUERYBUF, &buf));buffers[i] = (unsigned char *)mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);if (buffers[i] == MAP_FAILED) {std::cerr << "Failed to mmap buffer " << i << std::endl;close(fd);return -1;}buffer_sizes[i] = buf.length;// 将缓冲区加入队列CHECK(ioctl(fd, VIDIOC_QBUF, &buf));}// 开始采集enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;CHECK(ioctl(fd, VIDIOC_STREAMON, &type));// 打开输出文件std::ofstream outfile(OUTPUT_FILE, std::ios::binary);if (!outfile) {std::cerr << "Failed to open output file" << std::endl;close(fd);return -1;}// 采集 100 帧数据并保存到文件for (int i = 0; i < 100; i++) {struct v4l2_buffer buf = {};buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;// 从队列中取出缓冲区CHECK(ioctl(fd, VIDIOC_DQBUF, &buf));// 将 YUV420P 数据写入文件outfile.write((char *)buffers[buf.index], buf.bytesused);// 将缓冲区重新加入队列CHECK(ioctl(fd, VIDIOC_QBUF, &buf));}// 停止采集CHECK(ioctl(fd, VIDIOC_STREAMOFF, &type));// 释放资源for (unsigned int i = 0; i < BUFFER_COUNT; i++) {munmap(buffers[i], buffer_sizes[i]);}close(fd);outfile.close();std::cout << "YUV420P data saved to " << OUTPUT_FILE << std::endl;return 0;
}

编译前需要先安装v4l2的开发包:

sudo apt install libv4l-dev

也可以同时安装v4l2的工具包,用于信息查询:

sudo apt install v4l-utils

注:webrtc在linux下提供了两种采集方式,一种是v4l2,另一种是pipewire,感兴趣的可以看一下它们的实现

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

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

相关文章

LabVIEW 系统诊断

LabVIEW 系统诊断是指通过各种工具和方法检测、评估、分析和解决 LabVIEW 程序和硬件系统中可能存在的故障和性能问题。系统诊断不仅涵盖软件层面的调试与优化&#xff0c;还包括硬件交互、数据传输、实时性能等方面的检查和分析。一个成功的系统诊断能够显著提升LabVIEW应用程…

电脑之一键备份系统(One Click Backup System for Computer)

电脑之一键备份系统 相信使用电脑的的人都遇到过&#xff0c;电脑系统崩溃&#xff0c;开机蓝屏等原因&#xff0c;这个时候你急着用电脑办公&#xff0c;电脑却给你罢工是多么气人了&#xff0c;其实可以给电脑做一个系统备份。 最近每天都有系统蓝屏崩溃&#xff0c;这个实难…

课题推荐——基于GPS的无人机自主着陆系统设计

关于“基于GPS的无人机自主着陆系统设计”的详细展开&#xff0c;包括项目背景、具体内容、实施步骤和创新点。如需帮助&#xff0c;或有导航、定位滤波相关的代码定制需求&#xff0c;请点击文末卡片联系作者 文章目录 项目背景具体内容实施步骤相关例程MATLAB例程python例程 …

【小程序】5分钟快速入门抓包微信小程序

期末周无聊&#xff0c;抽点时间看看小程序渗透&#xff0c;先讲下微信小程序的抓包 工具&#xff1a;BurpsuiteProxifier step1 bp先开个端口代理&#xff0c;演示用的8080(懒得再导证书) step2 Proxifier设置好bp的代理 step3 随便启动个微信小程序&#xff0c;任务管理…

腾讯云AI代码助手-公司职位分析AI助手

作品简介 腾讯云AI代码助手是一款智能工具&#xff0c;专注于为公司提供职位分析服务。通过自然语言处理和机器学习技术&#xff0c;它能快速解析职位描述&#xff0c;提取关键信息&#xff0c;并提供数据驱动的洞察&#xff0c;帮助公司优化招聘流程和职位设计。 技术架构 …

网络基础1 http1.0 1.1 http/2的演进史

http1.0 1.1 http/2的演进史&#x1f60e; &#xff08;连接复用 队头阻塞 服务器推送 2进制分帧&#xff09; 概述 我们主要关注的是应用层 传输层 http协议发展历史 http的报文结构&#xff1a;起始行 Header Body http的典型特征 http存在的典型问题 Keep Alive机制 chun…

快速上手:采用Let‘sEncrypt免费SSL证书配置网站Https (示例环境:Centos7.9+Nginx+Let‘sEncrypt)

1 关于Let’s Encrypt与Cerbot DNS验证 Let’s Encrypt 是一个提供 免费证书 的 认证机构。 Cerbot 是 Let’s Encrypt 提供的一个工具&#xff0c;用于自动化生成、验证和续订证书。 DNS验证是 Cerbot 支持的验证方式之一。相比 HTTP 验证或 TLS-ALPN 验证&#xff0c;DNS …

网络安全-XSS跨站脚本攻击(基础篇)

漏洞扫描的原理 1.跨站脚本攻击介绍 xss跨站脚本攻击&#xff1a; xSS 全称&#xff08;Cross site Scripting &#xff09;跨站脚本攻击&#xff0c;是最常见的Web应用程序安全漏洞之一&#xff0c;位于OWASP top 10 2013/2017年度分别为第三名和第七名&#xff0c;XSS是指攻…

毕业项目推荐:基于yolov8/yolov5/yolo11的动物检测识别系统(python+卷积神经网络)

文章目录 概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式&#xff09;功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…

xtu oj 1614 数字(加强版)

输出格式# 每行输出一个样例的结果&#xff0c;为一个整数。 样例输入# 3 1 10 101 样例输出# 1 2 3 解题思路&#xff1a;这个题不要想复杂了&#xff0c;很容易超时。 首先需要注意的点&#xff0c;n<10的10000次方&#xff0c;用int或者long long都会爆&#xff0c;所…

了解RabbitMQ:强大的开源消息队列中间件

在现代分布式系统中&#xff0c;消息队列&#xff08;Message Queue&#xff0c;简称MQ&#xff09;作为一种重要的组件&#xff0c;承担着上下游消息传递和通信的重任。其中&#xff0c;RabbitMQ作为一款流行的开源消息队列中间件&#xff0c;凭借其高可用性、可扩展性和易用性…

这是什么操作?强制迁移?GitLab 停止中国区用户访问

大家好&#xff0c;我是鸭鸭&#xff01; 全球知名代码托管平台 GitLab 发布通告&#xff0c;宣布不再为位于中国大陆、香港及澳门地区的用户提供访问服务&#xff0c;并且“贴心”建议&#xff0c;可以访问极狐 GitLab。 极狐 GitLab 是一家中外合资公司&#xff0c;宣称获得…

第二届 Sui 游戏峰会将于 3 月 18 日在旧金山举行

3 月中旬&#xff0c;Sui 基金会和 Mysten Labs 将共同举办第二届 Sui 游戏峰会&#xff08;Sui Gaming Summit&#xff09;&#xff0c;这是一个专注于 Sui 游戏平台的 GDC 周边活动。此次峰会将与旧金山的年度游戏开发者大会&#xff08;GDC&#xff0c;Game Developers Conf…

易支付二次元网站源码及部署教程

易支付二次元网站源码及部署教程 引言 在当今数字化时代&#xff0c;二次元文化逐渐成为年轻人生活中不可或缺的一部分。为了满足这一庞大用户群体的需求&#xff0c;搭建一个二次元主题网站显得尤为重要。本文将为您详细介绍易支付二次元网站源码的特点及其部署教程&#xf…

计算机毕业设计hadoop+spark知网文献论文推荐系统 知识图谱 知网爬虫 知网数据分析 知网大数据 知网可视化 预测系统 大数据毕业设计 机器学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

LabVIEW四旋翼飞行器姿态监测系统

四旋翼飞行器姿态监测系统是一个集成了高度、速度、俯仰角与滚转角数据采集与分析的系统&#xff0c;提高飞行器在复杂环境中的操作精确度与安全性。系统利用LabVIEW平台与硬件传感器相结合&#xff0c;实现实时数据处理与显示&#xff0c;有效地提升了四旋翼飞行器的监测与控制…

3D机器视觉的类型、应用和未来趋势

3D相机正在推动机器视觉市场的增长。很多制造企业开始转向自动化3D料箱拣选&#xff0c;专注于使用3D视觉和人工智能等先进技术来简化操作并减少开支。 预计3D相机将在未来五年内推动全球机器视觉市场&#xff0c;这得益于移动机器人和机器人拣选的强劲增长。到 2028 年&#…

JavaFX基础之环境配置,架构,FXML

文章目录 1 JavaFX1.1 简介1.2 环境准备1.2.1 手动管理依赖1.2.2 maven或Gradle管理 1.3 JavaFX 架构1.3.1 JavaFX 架构图1.3.2 JavaFX组件1.3.2.1 舞台1.3.2.2 场景1.3.2.3 控件1.3.2.4 布局1.3.2.5 图表1.3.2.6 2D图形1.3.2.7 3D图形1.3.2.8 声音1.3.2.9 视频 1.4 简单使用1.…

php命名空间

什么是命名空间 从广义上来说&#xff0c;命名空间是一种封装事物的方法&#xff0c;在很多地方都可以见到这种抽象概念。 例如&#xff0c;在操作系统中目录用来将相关文件分组&#xff0c;对于目录中的文件来说&#xff0c;它就扮演了命名空间的角色。 具体举个例子&#xf…

【Unity3D】导出Android项目以及Java混淆

Android Studio 下载文件归档 | Android Developers Android--混淆配置&#xff08;比较详细的混淆规则&#xff09;_android 混淆规则-CSDN博客 Unity版本&#xff1a;2019.4.0f1 Gradle版本&#xff1a;5.6.4&#xff08;或5.1.1&#xff09; Gradle Plugin版本&#xff…