linux音视频采集技术: v4l2

news2025/1/9 0:26:30

简介

在 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.coloradmin.cn/o/2273488.html

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

相关文章

Python生日祝福烟花

1. 实现效果 2. 素材加载 2个图片和3个音频 shoot_image pygame.image.load(shoot(已去底).jpg) # 加载拼接的发射图像 flower_image pygame.image.load(flower.jpg) # 加载拼接的烟花图 烟花不好去底 # 调整图像的像素为原图的1/2 因为图像相对于界面来说有些大 shoo…

智能手机租赁系统全新模式改变消费习惯与商家盈利路径

内容概要 智能手机租赁系统的崛起&#xff0c;让我们瞄到了一个消费市场的新风向标。想象一下&#xff0c;传统上人们总是为了最新款手机奋不顾身地排队、借钱甚至是透支信用卡。现在&#xff0c;通过灵活的租赁选项&#xff0c;消费者可以更加随意地体验高科技产品&#xff0…

【简博士统计学习方法】第1章:3. 统计学习方法的三要素

3. 统计学习方法的三要素 3.1 监督学习的三要素 3.1.1 模型 假设空间&#xff08;Hypothesis Space&#xff09;&#xff1a;所有可能的条件概率分布或决策函数&#xff0c;用 F \mathcal{F} F表示。 若定义为决策函数的集合&#xff1a; F { f ∣ Y f ( X ) } \mathcal{F…

牛客网刷题 ——C语言初阶(2分支和循环-for)——打印菱形

1. 题目描述 用C语言在屏幕上输出以下图案&#xff1a; 2. 思路 我是先上手&#xff0c;先把上半部分打印出来&#xff0c;然后慢慢再来分析&#xff0c;下面这是我先把整个上半部分打印出来&#xff0c;因为空格不方便看是几个&#xff0c;这里先用&代替空格了 然后这里…

STM32——系统滴答定时器(SysTick寄存器详解)

文章目录 1.SysTick简介2.工作原理3.SysTick寄存器4.代码延时逻辑5.附上整体代码6.一些重要解释 1.SysTick简介 Cortex-M处理器内集成了一个小型的名为SysTick(系统节拍)的定时器,它属于NVIC的一部分,且可以产生 SysTick异常(异常类型#15)。SysTick为简单的向下计数的24位计数…

《Opencv》信用卡信息识别项目

目录 一、项目介绍 二、数据材料介绍 1、模板图片&#xff08;1张&#xff09; 2、需要处理的信用卡图片&#xff08;5张&#xff09; 三、实现过程 1、导入需要用到的库 2、设置命令行参数 3、模板图像中数字的定位处理 4、信用卡图像处理 5、模板匹配 四、总结 一…

密码学科普

1 信息传输中的安全隐患 1. 窃听 解决方案&#xff1a;明文加密&#xff0c;X只能窃听到密文 2. 假冒 解决方案&#xff1a;消息认证码或者数字签名 3. 篡改 解决方案&#xff1a;消息认证码或者数字签名 4. 事后否认 解决方案&#xff1a;数字签名 2 对称加密/非对称加密 1…

复合机器人助力手机壳cnc加工向自动化升级

在当今竞争激烈的制造业领域&#xff0c;如何提高生产效率、降低成本、提升产品质量&#xff0c;成为众多企业面临的关键挑战。尤其是在手机壳 CNC 加工这一细分行业&#xff0c;随着市场需求的持续增长&#xff0c;对生产效能的要求愈发严苛。而复合机器人的出现&#xff0c;正…

爬虫学习记录

1.概念 通过编写程序,模拟浏览器上网,然后让其去互联网上抓取数据的过程 通用爬虫:抓取的是一整张页面数据聚焦爬虫:抓取的是页面中的特定局部内容增量式爬虫:监测网站中数据更新的情况,只会抓取网站中最新更新出来的数据 robots.txt协议: 君子协议,网站后面添加robotx.txt…

黑马头条平台管理实战

黑马头条 08平台管理 1.开始准备和开发思路1.1.开发网关1.2编写admin-gateway 代码 2.开发登录微服务2.1编写登录微服务 3.频道管理4.敏感词管理5.用户认证审核6.自媒体文章人工审核99. 最后开发中碰到的问题汇总1.关于nacos 配置 问题2.在开发频道管理新增频道后端无法接收到前…

实验四 数组和函数

实验名称 实验四 数组和函数 实验目的 &#xff08;1&#xff09;掌握一维、二维数组以及字符数组的定义、元素引用和编程方法。 &#xff08;2&#xff09;掌握字符串常用程序的设计方法。 &#xff08;3&#xff09;掌握函数定义和调用的方法&#xff0c;以及函数参数传…

Idea(中文版) 项目结构/基本设置/设计背景

目录 1. Idea 项目结构 1.1 新建项目 1.2 新建项目的模块 1.3 新建项目模块的包 1.4 新建项目模块包的类 2. 基本设置 2.1 设置主题 2.2 设置字体 2.3 设置注释 2.4 自动导包 2.5 忽略大小写 2.6 设置背景图片 3. 项目与模块操作 3.1 修改类名 3.2 关闭项目 1. I…

Android Audio基础(53)——PCM逻辑设备Write数据

1. 前言 本文,我们将以回放(Playback,播放音频)为例,讲解PCM Data是如何从用户空间到内核空间,最后传递到Codec。 在 ASoC音频框架简介中,我们给出了回放(Playback)PCM数据流示意图。: 对于Linux来说,由于分为 user space 和kernel space,而且两者之间数据不能随便…

Android Audio基础(54)——数字音频接口 I2S、PCM(TDM) 、PDM

1. 概述 本文介绍的数字音频接口全部是硬件接口,是实际的物理连线方式,即同一个PCB板上IC芯片和IC芯片之间的通讯协议。 PCM、PDM也可以用于表示音频编码格式,。编码格式是指模拟信号数字化的方式。 I2S和PCM(TDM)接口传输的数据是PCM格式的音频数据。这两种协议是最为常见…

HDFS架构原理

一、HDFS架构整体概述 HDFS是Hadoop Distribute File System 的简称&#xff0c;意为&#xff1a;Hadoop分布式文件系统。HDFS是Hadoop核心组件之一&#xff0c;作为大数据生态圈最底层的分布式存储服务而存在。HDFS解决的问题就是大数据如何存储,它是横跨在多台计算机上的文件…

windows11(或centos7)安装nvidia显卡驱动、CUDA、cuDNN

本文是我瞎搞时写的问题汇总及参考文献&#xff0c;记录了一些问题解决的进度及对问题的思考。 最近一次更新时间&#xff1a;2025年1月4日 一、安装或更新nvidia显卡驱动 首先&#xff0c;需要确保你的设备安装了最新的显卡驱动。 &#xff08;1&#xff09;centos7安装显…

2025-01-04 Unity插件 YodaSheet2 —— 基础用法

文章目录 环境配置1 创建 YadeSheetData2 读取方式2.1 表格读取2.2 列表读取 3 自定义设置3.1 修改代码生成位置3.2 添加列表支持3.2.1 修改 DataTypeMapper.cs3.2.2 修改 SheetDataExtensions.cs3.2.3 修改 CodeGeneratorEditor.cs3.2.4 测试 ​ 官方文档&#xff1a; Unity …

STM32 拓展 RTC(实时时钟)

RTC简介 RTC(Real Time Clock,实时时钟)。是一个掉电后仍然可以继续运行的独立定时器。 RTC模块拥有一个连续计数的计数器,在相应的软件配置下,可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式的自动唤醒单元。 RTC实质…

微信小程序实现登录注册

文章目录 1. 官方文档教程2. 注册实现3. 登录实现4. 关于作者其它项目视频教程介绍 1. 官方文档教程 https://developers.weixin.qq.com/miniprogram/dev/framework/路由跳转的几种方式&#xff1a; https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.switchTab…

[大模型开源]SecGPT 网络安全大模型

模型介绍 SecGPT的愿景是将人工智能技术引入网络安全领域&#xff0c;以提高网络防御的效率和效果。其使命是推动网络安全智能化&#xff0c;为社会提供更安全的数字生活环境。 ① SecGPT开源地址&#xff1a;https://github.com/Clouditera/secgpt② 模型地址&#xff1a;htt…