V4L2应用程序开发实战:枚举摄像头所有支持的格式和分辨率

news2026/5/6 11:40:11
V4L2应用程序开发实战枚举摄像头所有支持的格式和分辨率这节课我们只做一件事用手把手的方式从零写出一个完整的 V4L2 程序它能列出你的摄像头设备所有支持的像素格式比如 YUYV、MJPEG以及每种格式下的所有分辨率比如 640x480、1280x720。我会带你一步步写代码、编译、传到开发板并运行同时讲解每个关键知识点。你不需要有多个摄像头一个就够。写完这个程序你就掌握了 V4L2 中最常用的查询接口以后写任何摄像头应用都心里有底。 这节课你将收获什么技能点重要性说明编写完整的 V4L2 枚举程序⭐⭐⭐ 必须掌握你以后开发摄像头应用第一步几乎都是先查摄像头能力理解VIDIOC_ENUM_FMT和VIDIOC_ENUM_FRAMESIZES⭐⭐⭐ 必须掌握面试常问实际项目必备交叉编译主机 → ARM 开发板⭐⭐⭐ 必须掌握嵌入式开发的标配流程使用 ADB 上传并运行程序⭐⭐ 重点掌握调试嵌入式程序最快的方法调试常见错误权限、设备节点、工具链⭐⭐ 重点掌握实际开发中一定会遇到文章目录V4L2应用程序开发实战枚举摄像头所有支持的格式和分辨率 这节课你将收获什么一、准备工作1.1 你需要什么1.2 验证摄像头设备二、编写完整代码可直接复制三、在 Ubuntu 主机上先本地测试可选四、交叉编译生成开发板能运行的程序4.1 设置工具链路径4.2 交叉编译五、将程序传到开发板并运行5.1 连接 ADB5.2 上传程序5.3 进入开发板 shell 并运行5.4 查看输出六、逐行讲解代码中的关键点面试/考试重点6.1 为什么需要 memset 清零结构体6.2 VIDIOC_ENUM_FMT 的 index 从 0 开始递增6.3 VIDIOC_ENUM_FRAMESIZES 需要传入 pixel_format6.4 fsenum.type 的作用七、常见错误与解决方法八、必须搞懂的 5 个核心问题问题1为什么每次循环都要用 memset 清零问题2VIDIOC_ENUM_FMT 的 index 为什么从 0 开始递增它何时停止问题3为什么 VIDIOC_ENUM_FRAMESIZES 必须放在内层循环问题4分辨率的“离散”和“连续”是什么意思分别怎么处理问题5如果枚举过程中 ioctl 返回 -1 且 errno 不是 EINVAL代表什么九、面试官提问环节必背第1问V4L2 中如何枚举摄像头支持的像素格式请说明使用的 ioctl 和关键数据结构。第2问在枚举完一种格式后如何知道该格式支持哪些分辨率需要用到什么 ioctl为什么这个 ioctl 必须放在格式枚举的循环内部第3问v4l2_frmsizeenum 结构体中的 type 字段有哪些取值分别代表什么第4问枚举过程中ioctl 返回 -1 是否一定表示错误如何正确判断枚举结束第5问为什么要先用 memset 清零 V4L2 结构体不清零会有什么后果第6问请画出或描述枚举所有格式及分辨率的程序流程图。十、总结一、准备工作1.1 你需要什么一台Ubuntu 虚拟机或主机装有交叉编译工具链我们使用百问网 T113 开发板的工具链。一块6ull 开发板已烧录 Tina Linux 系统并通过 USB OTG 线连接到电脑ADB 能识别。一个USB 摄像头或开发板自带的摄像头接口插入开发板后会出现/dev/video0或/dev/video1。如果你还没有交叉编译工具链不用担心我会给出路径和设置方法。1.2 验证摄像头设备在开发板上通过adb shell或串口执行bashls /dev/video*如果显示/dev/video0或/dev/video1说明摄像头已经被驱动识别。二、编写完整代码可直接复制下面是一个完整的 C 程序它会打开指定的视频设备枚举所有格式和分辨率然后打印出来。代码里写了详尽的注释即使你没学过 V4L2也能看懂每一步在做什么。创建一个文件video_enum.cc#include stdio.h #include stdlib.h #include string.h #include fcntl.h #include unistd.h #include sys/ioctl.h #include linux/videodev2.h int main(int argc, char **argv) { int fd; struct v4l2_fmtdesc fmtdesc; struct v4l2_frmsizeenum fsenum; int fmt_index 0; int frame_index; /* 1. 检查命令行参数 */ if (argc ! 2) { fprintf(stderr, Usage: %s video_device\n, argv[0]); fprintf(stderr, Example: %s /dev/video0\n, argv[0]); return 1; } /* 2. 打开设备 */ fd open(argv[1], O_RDWR); if (fd 0) { perror(open); return 1; } printf(Device: %s\n, argv[1]); printf(\n); /* 3. 循环枚举所有像素格式 */ while (1) { memset(fmtdesc, 0, sizeof(fmtdesc)); fmtdesc.index fmt_index; fmtdesc.type V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd, VIDIOC_ENUM_FMT, fmtdesc) 0) { break; // 枚举结束 } /* 打印格式信息 */ printf(Format %d: %s (fourcc0x%08x)\n, fmt_index, fmtdesc.description, fmtdesc.pixelformat); /* 4. 对于当前格式枚举所有支持的分辨率 */ frame_index 0; while (1) { memset(fsenum, 0, sizeof(fsenum)); fsenum.index frame_index; fsenum.pixel_format fmtdesc.pixelformat; if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, fsenum) 0) { break; // 该格式的分辨率枚举结束 } /* 只处理离散分辨率绝大多数摄像头都是离散的 */ if (fsenum.type V4L2_FRMSIZE_TYPE_DISCRETE) { printf( %d x %d\n, fsenum.discrete.width, fsenum.discrete.height); } else if (fsenum.type V4L2_FRMSIZE_TYPE_STEPWISE) { /* 连续范围极少见简单打印范围 */ printf( continuous: %d..%d x %d..%d\n, fsenum.stepwise.min_width, fsenum.stepwise.max_width, fsenum.stepwise.min_height, fsenum.stepwise.max_height); } frame_index; } fmt_index; printf(\n); // 格式之间空一行 } close(fd); return 0; }代码核心逻辑打开设备文件。用VIDIOC_ENUM_FMT依次查询每种像素格式直到返回错误。对每一种格式用VIDIOC_ENUM_FRAMESIZES查询它支持的分辨率直到返回错误。打印出每种格式的名称、fourcc 码以及所有分辨率。三、在 Ubuntu 主机上先本地测试可选为了确保代码逻辑正确你可以在 Ubuntu 主机上插一个 USB 摄像头或者使用虚拟机里的虚拟摄像头先用本地 gcc 编译测试。bashsudo apt install gcc # 如果没有安装 gcc -o video_enum video_enum.c ./video_enum /dev/video0如果主机上有摄像头你会看到类似输出textDevice: /dev/video0 Format 0: YUYV 4:2:2 (fourcc0x56595559) 640 x 480 800 x 600 1280 x 720 Format 1: MJPEG (fourcc0x47504a4d) 640 x 480 1280 x 720如果主机没有摄像头可以跳过这一步直接交叉编译到开发板上运行。四、交叉编译生成开发板能运行的程序我们使用 T113 开发板的交叉编译工具链。工具链在 Tina-SDK 的prebuilt目录下。4.1 设置工具链路径假设你的 Tina-SDK 放在/home/ubuntu/tina-d1-h则执行bashexport PATH$PATH:/home/ubuntu/tina-d1-h/prebuilt/gcc/linux-x86/arm/toolchain-sunxi-musl/toolchain/bin为了确认是否设置成功输入basharm-buildroot-linux-gnueabihf-gcc --version应该能看到版本信息。如果没有请检查路径是否正确你的用户名和 SDK 目录可能不同。4.2 交叉编译basharm-buildroot-linux-gnueabihf-gcc -o video_enum video_enum.c编译成功后用file命令查看bashfile video_enum输出应该包含ELF 32-bit LSB executable, ARM说明是 ARM 架构的可执行文件。五、将程序传到开发板并运行5.1 连接 ADB用 USB 线连接开发板的OTG 口到电脑。在 Ubuntu 终端输入bashadb devices如果看到类似20080411 device的设备说明连接成功。如果显示no permissions可以尝试sudo adb kill-server后重新插拔。5.2 上传程序bashadb push video_enum /root/5.3 进入开发板 shell 并运行bashadb shell cd /root chmod x video_enum # 如果没有可执行权限 ./video_enum /dev/video0如果摄像头是video1就改成/dev/video1。5.4 查看输出你应该会看到类似下面的输出根据你的摄像头支持情况可能不同textDevice: /dev/video0 Format 0: YUYV 4:2:2 (fourcc0x56595559) 640 x 480 160 x 120 320 x 240 352 x 288 800 x 600 1280 x 720 1280 x 1024 Format 1: Motion-JPEG (fourcc0x47504a4d) 640 x 480 160 x 120 320 x 240 352 x 288 800 x 600 1280 x 720 1280 x 1024恭喜你已经成功列出了摄像头的所有能力。六、逐行讲解代码中的关键点面试/考试重点6.1 为什么需要memset清零结构体cmemset(fmtdesc, 0, sizeof(fmtdesc));V4L2 的结构体中有很多保留字段内核要求应用程序把它们初始化为 0否则 ioctl 可能失败或行为异常。这是一个好习惯。6.2VIDIOC_ENUM_FMT的 index 从 0 开始递增内核维护了一个格式列表你每次传入一个 index它返回第 index 个格式的信息。当传入的 index 超过最后一个时ioctl 返回 -1我们跳出循环。6.3VIDIOC_ENUM_FRAMESIZES需要传入pixel_format只有先知道格式才能查询该格式的分辨率。这也是为什么要在内层循环中的原因。6.4fsenum.type的作用V4L2_FRMSIZE_TYPE_DISCRETE离散分辨率直接读取discrete.width/height。V4L2_FRMSIZE_TYPE_STEPWISE连续范围如最小 32x32 到最大 1920x1080步长 8。这种摄像头非常少见我们简单打印范围即可。七、常见错误与解决方法错误现象原因解决办法open: Permission denied普通用户无权限访问设备开发板上用 root 用户adb shell 默认 root或chmod 666 /dev/video0VIDIOC_ENUM_FMT: Invalid argument传入了错误的type值确保type V4L2_BUF_TYPE_VIDEO_CAPTURE交叉编译时command not foundPATH 未设置重新执行export PATH...并确认路径正确ADB 推文件失败device offlineUSB 线松动或驱动没加载拔插 USB 线重新运行adb devices开发板执行后没有任何输出摄像头设备节点可能不是 video0先用ls /dev/video*查看用正确的节点名八、必须搞懂的 5 个核心问题问题1为什么每次循环都要用memset清零答V4L2 的结构体中包含reserved保留字段。内核要求这些字段必须为 0否则可能被视为要求启用尚未定义的功能导致 ioctl 失败或行为异常。清零是一个好习惯避免栈上的随机值污染。问题2VIDIOC_ENUM_FMT的index为什么从 0 开始递增它何时停止答内核内部维护一个格式的列表index就是数组下标。从 0 开始每成功一次就加 1直到内核返回 -1且errno为EINVAL。这表示已经枚举完所有格式。这种模式在 V4L2 中非常普遍枚举帧率、控制项等也类似。问题3为什么VIDIOC_ENUM_FRAMESIZES必须放在内层循环答该 ioctl 需要输入参数pixel_format即具体的格式 fourcc。这个 fourcc 只能从外层VIDIOC_ENUM_FMT获得。没有外层枚举你无法知道要查哪种格式的分辨率。因此它必须嵌套在格式循环内部。问题4分辨率的“离散”和“连续”是什么意思分别怎么处理答离散摄像头只支持固定的几个分辨率如 640x480、1280x720。直接读取fsenum.discrete.width/height。连续摄像头支持一段范围内的任何分辨率并给出步长。例如最小 16x16最大 1920x1080步长 8x8。应用程序可以选择任意满足步长倍数的分辨率。此类摄像头极少见通常出现在工业领域。问题5如果枚举过程中 ioctl 返回 -1 且errno不是EINVAL代表什么答真正的硬件错误或驱动异常。例如EIOI/O 错误、ENOMEM内存不足。在这种情况下不应继续循环应打印错误并退出。九、面试官提问环节必背第1问V4L2 中如何枚举摄像头支持的像素格式请说明使用的 ioctl 和关键数据结构。参考答案使用VIDIOC_ENUM_FMT命令配合结构体struct v4l2_fmtdesc。设置fmtdesc.type V4L2_BUF_TYPE_VIDEO_CAPTURE然后从index 0开始递增调用ioctl直到返回 -1。每次成功时fmtdesc.pixelformat是格式的 FourCC 码fmtdesc.description是可读字符串。第2问在枚举完一种格式后如何知道该格式支持哪些分辨率需要用到什么 ioctl为什么这个 ioctl 必须放在格式枚举的循环内部参考答案使用VIDIOC_ENUM_FRAMESIZES结构体为struct v4l2_frmsizeenum。必须传入fsenum.pixel_format即从格式枚举得到的 FourCC所以必须在格式枚举的内层循环中调用。内核根据该 FourCC 返回该格式支持的分辨率列表。第3问v4l2_frmsizeenum结构体中的type字段有哪些取值分别代表什么参考答案V4L2_FRMSIZE_TYPE_DISCRETE摄像头支持离散的固定分辨率分辨率信息在fsenum.discrete中。V4L2_FRMSIZE_TYPE_STEPWISE摄像头支持连续范围分辨率信息在fsenum.stepwise中包括最小、最大和步长。绝大多数消费级摄像头都是前一种。第4问枚举过程中ioctl返回 -1 是否一定表示错误如何正确判断枚举结束参考答案不是。当index超过最后一个条目时ioctl返回 -1 并且errno被设置为EINVAL这是正常的枚举结束。真正的错误应检查errno是否为其他值如EIO、ENOMEM。简单示例中通常只判断返回值 0 就退出严谨的程序应区分EINVAL和真实错误。第5问为什么要先用memset清零 V4L2 结构体不清零会有什么后果参考答案V4L2 结构体中有reserved保留字段内核要求这些字段必须为 0。如果不清零栈上的随机值会被内核读取可能被解释为尚未定义的扩展参数导致 ioctl 失败或产生不可预知的行为。清零是一种防御性编程确保兼容性。第6问请画出或描述枚举所有格式及分辨率的程序流程图。参考答案text开始 │ ├─ 打开 /dev/videoX │ ├─ fmt_index 0 │ ├─ while (1) { │ └─ 调用 ioctl( VIDIOC_ENUM_FMT ) │ ├─ 失败 → 跳出外层循环 │ └─ 成功 → 打印格式信息 │ frame_index 0 │ while (1) { │ 调用 ioctl( VIDIOC_ENUM_FRAMESIZES ) │ ├─ 失败 → 跳出内层循环 │ └─ 成功 → 打印分辨率 │ frame_index │ } │ fmt_index │ } │ └─ 关闭设备十、总结这一节我们完成了一个非常实用的 V4L2 程序它不采集图像却能帮你彻底摸清摄像头的“底细”。在真实项目中你会经常先运行类似程序来确认摄像头能力然后再写采集代码。你必须掌握的核心VIDIOC_ENUM_FMT和VIDIOC_ENUM_FRAMESIZES的用法。交叉编译的完整流程设置 PATH、编译、file 验证。ADB 部署与运行。下次当你拿到一个新的摄像头时别忘了先运行video_enum /dev/videoX看看它到底支持什么。 你已经迈出了 V4L2 应用开发的一大步下一个目标写一个可以实时显示摄像头画面的程序结合 LCD 显示。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…