V4L2 零基础入门(一)——打开摄像头和获取摄像头基本信息

2023-11-10

今天工作需要用V4L2获取摄像头的数据,所以稍微了解了一些相关内容。在这里记录一下。

参考了很多优秀的博客,大家如果感兴趣也可以直接参考,链接放在文末。

我也是初学者,有什么问题大家多多交流。

测试环境

  • Ubuntu 2004
  • 两根usb线的摄像头(公司里随便找的,不知品牌)

V4L2介绍

VideoForLinuxTwo(Video4Linux2)简称为V4L2,是linux内核中自带的采集图片,视频,音频的一套api接口。

因为它是linux内核自带的,所以可以在linux上直接使用。主流的视频设备几乎都适配了这套框架,因此我们可以很方便的使用这一套api操作不同的视频设备,免去了学习视频设备驱动的成本(程序员福音!)。

下面我们介绍一下用V4L2采集视频数据的流程。

采集流程

打开设备

将usb摄像头连接到主机上,一般V4L2的设备结点为/dev/videoN,N取值0,1,2等等。如果该设备是当前连接的第一个视频设备会从0开始。

我的摄像头有两根usb线,因此有两个设备,/dev/video0,/dev/video1。

在这里插入图片描述
Linux中万物即文件,因此我们需要打开对应的设备文件才能进行接下来的操作。

//打开设备
int fd = open("/dev/video0", O_RDWR);

查询摄像头功能

方法一:

//存储设备信息的结构体 v4l2_capability
struct v4l2_capability cap = {0};

//用ioctl查询设备支持的功能
//int ioctl(int fd, int request, struct v4l2_capability *argp);
//fd:打开设备返回的文件句柄
//request:选择VIDIOC_QUERYCAP:是来查询视频设备是否支持V4L2规范的宏
ioctl(fd,VIDIOC_QUERYCAP, &cap);

//输出设备信息
printf("cap.driver = %s \n",cap.driver);
printf("cap.card = %s \n",cap.card);
printf("cap.bus_info = %s \n",cap.bus_info);
printf("cap.version = %d \n",cap.version);
printf("cap.capabilities = %x \n",cap.capabilities);
printf("cap.device_caps = %x \n",cap.device_caps);
printf("cap.reserved = %x \n",cap.reserved);

输出如下:

cap.driver = uvcvideo 
cap.card = Yitu USB Camera RGB: Yitu USB C 
cap.bus_info = usb-0000:00:14.0-2 
cap.version = 328896 
cap.capabilities = 84a00001 
cap.device_caps = 4200001 
cap.reserved = 6016507c 

这里介绍一些相关知识

VIDIOC_QUERYCAP

所有支持V4L2的设备都支持用VIDIOC_QUERYCAP来查询。当驱动程序与此规范不兼容时,ioctl 返回EINVAL错误代码。

查询到的数据存储在结构体v4l2_capability中。

v4l2_capability
struct v4l2_capability
{
 	__u8  	driver[16];   //驱动名,通常是uvcvideo
 	__u8  	card[32];     // Device名,厂商会写
 	__u8  	bus_info[32];  //在Bus系统中存放位置
	__u32 	version;       //driver 版本
 	__u32 	capabilities;  //能力集
 	__u32	device_caps;
 	__u32 	reserved[4];
};

其中最有价值的是capabilities字段,这个字段的值是若干个宏或的结果(应该是,我找到的博客都一笔带过没有详细解释这些 ? )。

这个集合里有一系列标识设备功能的宏,这些宏都在linux/videodev2.h

在这里插入图片描述

像我这个摄像头的capabilities就是

0x84a00001 = 0x80000000 | 0x04000000 | 0x00800000 | 0x00200000 | 0x00000001

对比上面的宏列表可知摄像头支持的功能为

#define V4L2_CAP_VIDEO_CAPTURE		0x00000001  /* 支持视频捕获 */
#define V4L2_CAP_EXT_PIX_FORMAT		0x00200000  /* 支持扩展像素格式 */
#define V4L2_CAP_META_CAPTURE		0x00800000  /* 支持元数据捕获 */
#define V4L2_CAP_STREAMING          0x04000000  /* 支持流式I/O ioctl功能 */
#define V4L2_CAP_DEVICE_CAPS        0x80000000  /* 设备支持capabilities字段 只有有这个flag,device_caps字段才会被设置*/					

当然按照我这种一个一个算太麻烦了,我们可以用更简单的方法来检查设备是否有我们需要的功能

      /* 检查设备是否支持视频捕获 */
    if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
    {
        printf("the video device support capture\n");
    }

让capabilities与我们想要查询的对应功能的宏进行与运算,如果结果不为0说明支持此功能。

方法二:

v4l2-ctl -D

输出

Driver Info:
	Driver name      : uvcvideo
	Card type        : USB 2.0 Camera: ZL Camera
	Bus info         : usb-0000:00:14.0-10.1.1
	Driver version   : 5.4.44
	Capabilities     : 0x84a00001
		Video Capture
		Metadata Capture
		Streaming
		Extended Pix Format
		Device Capabilities
	Device Caps      : 0x04200001
		Video Capture
		Streaming
		Extended Pix Format

这些信息就是我们通过ioctl(fd,VIDIOC_QUERYCAP, &cap)查询到的信息,Capabilities会自动解析出支持的功能,是不是比第一种方法方便很多!

查询设备支持的输出格式

方法一:

    //存储输出格式的结构体 v4l2_fmtdesc
    struct v4l2_fmtdesc fmt;
    //从第一个输出格式开始查询
    fmt.index = 0;
    //查询照片的输出格式,所以type选择V4L2_BUF_TYPE_VIDEO_CAPTURE
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	//输出当前设备的输出格式
    while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) != -1)
    {
        printf("pixelformat = %c%c%c%c, description = %s \n",
                  fmt.pixelformat & 0xFF, (fmt.pixelformat >> 8) & 0xFF, (fmt.pixelformat >> 16) & 0xFF,
                  (fmt.pixelformat >> 24) & 0xFF, fmt.description);
        fmt.index ++;
    }

我的输出是

pixelformat = MJPG, description = Motion-JPEG
pixelformat = YUYV, description = YUYV 4:2:2
ioctl
int ioctl(int fd, VIDIOC_ENUM_FMT, struct v4l2_fmtdesc *argp)

查询摄像头相关信息用到的都是ioctl。

函数的第二个参数选择VIDIOC_ENUM_FMT来查询设备支持的输出格式。将查询到的存储信息存储到v4l2_fmtdesc结构体中。

成功时返回0,失败时返回-1。

v4l2_fmtdesc
struct v4l2_fmtdesc {
	__u32		    index;             /* 格式编号 */
	__u32		    type;              /* 数据流的类型,v4l2_buf_type类型 */
	__u32           flags;
	__u8		    description[32];   /* 描述*/
	__u32		    pixelformat;       /* 图像格式标识符,这是一个由 v4l2_fourcc() 宏计算的四字符代码*/
	__u32		    reserved[4];
};

真正的输出格式在pixelformat里,一般也会把description打出来方便阅读。

pixelformat

pixelformat是一个由 v4l2_fourcc() 宏计算的四字符代码,像我的设备支持的输出格式在头文件里是这样被定义的。

#define v4l2_fourcc(a, b, c, d)\
	((__u32)(a) | ((__u32)(b) << 8) | ((__u32)(c) << 16) | ((__u32)(d) << 24))
	
//测试设备的输出格式	
#define V4L2_PIX_FMT_MJPEG    v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG   */

可以看到v4l2_fourcc的处理流程,因此我们逆向处理一下,就可以还原原本的字符。

    printf("pixelformat = %c%c%c%c, description = %s \n",
                  fmt.pixelformat & 0xFF, (fmt.pixelformat >> 8) & 0xFF, (fmt.pixelformat >> 16) & 0xFF,
                  (fmt.pixelformat >> 24) & 0xFF, fmt.description);
type

type是v4l2_buf_type型的变量,只能取相应的值。

在这里插入图片描述

一种设备往往支持不止一种输出格式,每种输出格式都对应v4l2_fmtdesc的一个index,因此在index自增的情况下循环查询对应的pixelformat和description来获取全部的输出格式。

在ioctl查询没有失败(返回 -1)的情况下就继续查询,直到查询到所有的输出格式。

方法二:

v4l2-ctl  --list-formats -d /dev/video0

输出

ioctl: VIDIOC_ENUM_FMT
	Type: Video Capture

	[0]: 'MJPG' (Motion-JPEG, compressed)
	[1]: 'YUYV' (YUYV 4:2:2)

查询出来视频设备支持两种输出格式,这种方式也比第一种简洁很多。

小结

没想到不知不觉竟然有差不多4000字了,其实这么一点才只是开头而已。不想让一篇文章太多字,打算分成几篇文章来写。

我在网上看了很多相关的博客,V4L2的大体流程大家都写的很清楚,可是参数为什么这么设置,为什么在这么多的参数中选择这一个等等细节的问题却几乎没什么人讲,所以我打算自己写一篇,是学习也是分享给大家。

参考

V4L2 API详解 <二> Camera详细设置【转】
Linux内核 API手册

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

V4L2 零基础入门(一)——打开摄像头和获取摄像头基本信息 的相关文章

  • Linux shell 根据第二列对文件进行排序?

    我有一个这样的文件 FirstName FamilyName Address PhoneNumber 如何按 FamilyName 排序 如果这是 UNIX sort k 2 file txt 您可以使用多个 k用于对多列进行排序的标志 例
  • Bash:检查是否给出了参数(例如是否有参数“-a”?)

    我有一个脚本 它应该接受 2 个参数 s 和 d 如果未给出 d 参数 我想删除我的调试文件 与 s 相同 如何检查 1 或 2 是否为 s 或 d 舒尔有两个参数 我可以做到 蛮力 if test 1 d test 2 d then rm
  • 如何告诉 mex 链接到 /usr/lib 中的 libstdc++.so.6 而不是 MATLAB 目录中的 libstdc++.so.6?

    现在 MATLAB 2012a 中的 mex 仅正式支持 gcc 4 4 6 但我想使用 gcc 4 7 风险自负 现在如果我直接用 mex 编译一些东西 它会抱怨 usr lib gcc i686 linux gnu 4 7 cc1plu
  • 段错误...关于你好世界

    这段代码非常简单 但我在 x86 64 Linux 系统上遇到了段错误 这让我很烦恼 刚开始接触asm 请耐心等待 与 NASM 组装nasm f elf64 test asm 与连接ld o test test o SECTION tex
  • PIL 的 Image.show() 带来*两个*不同的查看器

    在 python shell 中处理图像时 我使用 image show 其中 image 是 Image 的实例 很久以前什么也没发生 但在定义了一个名为 xv 的 Mirage 符号链接后 我很高兴 最近几天 show 将显示 Imag
  • 如何将一个文本文件拆分为多个 *.txt 文件?

    我有一个文本文件file txt 12 MB 包含 something1 something2 something3 something4 有没有办法分开file txt分成 12 个 txt 文件 比方说file2 txt file3 t
  • Linux 内核使用的设备树文件 (dtb) 可视化工具? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个可以图形化表示Linux内核中使用的硬件设备树的工具 我正在尝试了解特定 Arm 芯片组
  • 使用脚本检查 git 分支是否领先于另一个分支

    I have branch1 and branch2我想要某种 git branch1 isahead branch2 这将显示如果branch1已承诺branch2没有 也可能指定这些提交 我无法检查差异原因branch2 is在之前br
  • bash 将输出重定向到文件,但结果不完整

    重定向命令输出的问题已经被问过很多次了 但是我有一个奇怪的行为 我使用的是 bash shell debian 版本 4 3 30 1 release 并尝试将输出重定向到文件 但并非所有内容都记录在文件中 我尝试运行的 bin 文件是 l
  • MySQL 与 PHP 的连接无法正常工作

    这是我的情况 我正在尝试使用 Apache 服务器上的 PHP 文件连接到 MySQL 数据库 现在 当我从终端运行 PHP 时 我的 PHP 可以连接到 MySQL 数据库 使用 php f file php 但是当我从网页执行它时 它只
  • 链接错误:命令行中缺少 DSO

    我对 Linux 使用 Ubuntu 14 04 LTS 64 位 相当陌生 来自 Windows 并且正在尝试移植我现有的 CUDA 项目 当通过链接时 usr local cuda bin nvcc arch compute 30 co
  • 如何在 Linux 和 C 中使用文件作为互斥体?

    我有不同的进程同时访问 Linux 中的命名管道 并且我想让此访问互斥 我知道可以使用放置在共享内存区域中的互斥体来实现这一点 但作为一种家庭作业 我有一些限制 于是 我想到的是对文件使用锁定原语来实现互斥 我做了一些尝试 但无法使其发挥作
  • /sys/device/ 和 dmidecode 报告的不同 CPU 缓存大小

    我正在尝试获取系统中不同缓存级别的大小 我尝试了两种技术 a 使用 sys device 中的信息 这是输出 cat sys devices system cpu cpu0 cache index1 size 32K cat sys dev
  • Linux无法删除文件

    当我找到文件时 我在删除它们时遇到问题 任务 必须找到带有空格的文件并将其删除 我的尝试 rm find L root grep i 但我有错误 rm cannot remove root test No such file or dire
  • 如何在线程创建和退出时调用函数?

    include
  • 如何在 Linux 上通过 FTP 递归下载文件夹 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话 但却具有历史意义 目前不接受新的答案
  • 为什么 fork 炸弹没有使 android 崩溃?

    这是最简单的叉子炸弹 我在许多 Linux 发行版上执行了它 但它们都崩溃了 但是当我在 android 终端中执行此操作时 即使授予后也没有效果超级用户权限 有什么解释为什么它没有使 Android 系统崩溃吗 一句话 ulimit Li
  • vmsplice() 和 TCP

    在原来的vmsplice 执行 有人建议 http lwn net Articles 181169 如果您的用户态缓冲区是管道中可容纳的最大页面数的 2 倍 则缓冲区后半部分成功的 vmsplice 将保证内核使用缓冲区的前半部分完成 但事
  • 执行命令而不将其保留在历史记录中[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 在进行软件开发时 经常需要在命令行命令中包含机密信息 典型示例是将项目部署到服务器的凭据设置为环境变量 当我不想将某些命令存储在命令历史记
  • 如何使用 JSch 将多行命令输出存储到变量中

    所以 我有一段很好的代码 我很难理解 它允许我向我的服务器发送命令 并获得一行响应 该代码有效 但我想从服务器返回多行 主要类是 JSch jSch new JSch MyUserInfo ui new MyUserInfo String

随机推荐

  • java:成绩统计 :从键盘上输入若干学生(假设不超过100)的成绩,计算平均成绩,并输出高于平均分的学生人数及成绩。约定输入成绩为101时结束。

    import java util Scanner import java util Random public class three public static void main String args Scanner input ne
  • 【南風-视觉】Ubuntu命令行安装QT

    快速安装QT 1 直接在终端输入以下命令即可安装 安装完成并重启后就可以发现成功安装QT sudo apt get install qt5 default qtcreator y sudo reboot 注意 此次更新于2023年七月 似乎
  • MySQL——视图(VIEW)详解

    今天我们一起来学起视图 VIEW 那么视图是什么呢 视图有什么作用呢 视图一方面可以帮我们使用表的一部分而不是所有的表 另一方面也可以针对不同的用户制定不同的查询视图 带着问题一起来寻找答案吧 1 常见的数据库对象 对象 描述 表 TABL
  • 算法案例:如何判断链表有环?

    本文转载于 算法 如何判断链表有环 如何判断单链表是否存在环 有一个单向链表 链表当中有可能出现 环 就像题图这样 如何用程序判断出这个链表是有环链表 不允许修改链表结构 时间复杂度O n 空间复杂度O 1 方法一 穷举遍历 方法一 首先从
  • B - Mayor's posters

    Time Limit 1000MS Memory Limit 65536KB 64bit IO Format I64d I64u Submit Go Back Status Description
  • STM32最小系统板下载后断电程序丢失问题

    前者为运行代码时跳线帽接口 后者是下载代码时的跳线帽接口
  • myhttp

    HTTP 是一个属于应用层的面向对象的协议 由于其简捷 快速的方式 适用于分布式超媒体信息系统 实现一个服务器程序 支持HTTP协议的服务器 浏览器进行访问 从请求角度上 支持GET和POST方法 从相应角度上 支持静态页面也支持动态页面
  • flex布局换行后元素不足一行怎样均匀分布

  • 看懂时序图

    1 如何看懂时序图 掌握这几个程序就够了 chooseboy的博客 CSDN博客 软件时序图怎么看 2 sip 时序图 简单几步让你看懂单片机时序图 weixin 39710966的博客 CSDN博客 3 如何看懂时序图 掌握这几个程序就够
  • Linux gdm问题解决解决

    修改 etc inittab 改变runlevel为5 例如 root QRedHat etc cat etc inittab id 5 initdefault 修改GDM的配置文件 etc X11 gdm gdm conf 将 xdmcp
  • 路由器的工作原理是什么

    路由器工作原理 传统地 路由器工作于osi七层协议中的第三层 其主要任务是接收来自一个网络接口的数据包 根据其中所含的目的地址 决定转发到下一个目的地址 因此 路由器首先得在转发路由表中查找它的目的地址 若找到了目的地址 就在数据包的帧格前
  • ananconda powershell init失败&无法激活环境问题

    4 6以后版本使用conda init后仍然无法激活环境 尝试了好多次 断断续续花了得有几十个小时的时间 今天再尝试conda init时候发现了这个 从图片上看出它修改了onedrive下的profile文件 但是我的powershell
  • 趣味小游戏——井字棋

    目录 一 前言 二 主体框架 三 游戏内容的实现 四 结语 一 前言 想必大家在小时候或多或少的都接触过井字棋吧 那么今天我就来带大家了解如何在编程中实现这一游戏吧 二 主体框架 include
  • objcopy使用说明

    objcopy用于将object的部分获全部内容拷贝到另一个object 从而可以实现格式的变换 objcopy可用用于将文件转换成S record格式或者raw二进制格式 例如 xxxx elf objcopy O srec test o
  • tensorflow保存训练好的深度学习模型

    最近在看MKR的算法 复现跑通了以后 把训练好的模型保存下来 加入以下代码即可 saver tf train Saver saver save sess model MKRmodel ckpt print 模型已保存 上面的代码要写到和se
  • 【Spring】怎么配置事务?看这一篇就够了

    0 什么是事务 想象一个场景 A给B转账 A的钱转出去之后发生异常B没有收到钱 A的钱少了B的钱没有多 这不就乱套了吗 所以就引出了事务这个概念 事务是数据库操作最基本的单元 逻辑上的一组操作 这一组操作要么都执行成功 要么都失败 事务的作
  • Unity获取场景中所有根物体

    每个物体都属于一个Scene 而通过Scene GetRootGameObjects 接口就可以获得该Scene的所有根物体 我们只需要遍历所有Scene即可 List
  • Python Matplotlib 颜色名 与默认color_list获取

    绘制图像的时候需要选择美化的曲线 网上的图有很多 但是不少颜色的关键字并不支持 所以还是多参考官方的颜色名表 参考 https matplotlib org 3 1 0 gallery color named colors html mat
  • C++:type A is not dircet base of B

    原因 B的构造函数上直接使用了A的构造 但是在定义类的时候又没有继承 如下 class A A class B B B B A 报错
  • V4L2 零基础入门(一)——打开摄像头和获取摄像头基本信息

    今天工作需要用V4L2获取摄像头的数据 所以稍微了解了一些相关内容 在这里记录一下 参考了很多优秀的博客 大家如果感兴趣也可以直接参考 链接放在文末 我也是初学者 有什么问题大家多多交流 文章目录 测试环境 V4L2介绍 采集流程 打开设备