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

嵌入式linux相机(1)

  本人从0开始学习linux,使用的是韦东山的教程,在跟着课程学习的情况下的所遇到的问题的总结,理论虽枯燥但是是基础。本人将前几章的内容大致学完之后,考虑到后续驱动方面得更多的开始实操,后续的内容将以韦东山教程Linux项目的内容为主,学习其中的代码并手敲。做到锻炼动手能力的同时钻研其中的理论知识点。
摘要:相机部分代码的理解
摘要关键词:相机驱动

第一个项目相关使用的命令行

bear make
ctrl+h
arm-buildroot-linux-gnueabihf-gcc
adb push video2lcd root
ls /dev/video*
mv /etc/init.d/S99myirhmi2  /root
mv /etc/init.d/S05lvgl  /root
./video2lcd  /dev/video1

这个项目主要是测试在屏幕上实时显示摄像头的数据。
在这里插入图片描述
本人出现了没有文件内容但是不影响。

在这里插入图片描述
实验测试效果,摄像头我是买的别的厂家的比较便宜,只要支持ubuntu,linux驱动免安装就行。
在这里插入图片描述
实验二相关的命令行。

bear make
ctrl+h
arm-buildroot-linux-gnueabihf-gcc
adb push video2lcd root
ls /dev/video*
mv /etc/init.d/S99myirhmi2  /root
mv /etc/init.d/S05lvgl  /root
./lv_100ask_camera_demo  /dev/video1

第二个项目和第一个类似,只是加了一点拍照功能。
在这里插入图片描述

补充实验

这个实验主要是测试摄像头所支持的格式,以及分辨率大小。


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <stdio.h>
#include <string.h>
/* ./video_test </dev/video0>  */int main(int argc, char **agrv)
{int fd;struct v4l2_fmtdesc fmtdesc;int fmt_index = 0;int frame_index = 0;struct v4l2_frmsizeenum fsenum;if (argc != 2){printf("Usage: %s </dev/videoX>,print detail for video device\n",agrv[0]);return -1;}/* open */fd = open(agrv[1],O_RDWR);if (fd < 0){printf("can not open %s \n",agrv[1]);return -1;}/* */while(1){/* 枚举格式 *//* 枚举这种格式所支持的帧大小*/fmtdesc.index = fmt_index;  // 比如从0开始fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 指定type为"捕获"if(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != 0) break;frame_index = 0;while(1){//枚举所支持的帧大小memset(&fsenum,0,sizeof(struct v4l2_frmsizeenum));fsenum.pixel_format = fmtdesc.pixelformat;fsenum.index = frame_index;if(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&fsenum) == 0){printf("format %s %d ,framesize %d x %d \n",fmtdesc.description,fmtdesc.pixelformat,fsenum.discrete.width,fsenum.discrete.height);}else break;frame_index++;}fmt_index++;}return 0;
}

整体代码。

int main(int argc, char **agrv)
{int fd;struct v4l2_fmtdesc fmtdesc;int fmt_index = 0;int frame_index = 0;struct v4l2_frmsizeenum fsenum;
fd: 文件描述符,用于操作视频设备
fmtdesc: V4L2格式描述结构体
fmt_index: 格式索引,用于枚举所有支持的格式
frame_index: 帧大小索引,用于枚举特定格式支持的所有分辨率
fsenum: V4L2帧大小枚举结构体

在这里插入图片描述
在这里插入图片描述

这段代码中最关键的就是那两个结构体,这两个结构体是Video for Linux 2 (V4L2) API 的一部分,用于枚举(列出)视频设备(如摄像头、采集卡)所支持的视频数据格式,枚举某个特定像素格式(Pixel Format)所支持的所有帧尺寸(分辨率)。
当你编写一个程序想要从摄像头获取视频时,你需要告诉驱动程序你希望以什么样的格式接收数据(例如,是压缩的 MJPEG 还是未压缩的 YUYV,或者是 H.264?)。不同的设备支持不同的格式。

这个结构体就是用来查询设备到底支持哪些格式的。你通过一个循环,不断询问驱动程序“第 0 个格式是什么?”、“第 1 个格式是什么?”… 直到驱动程序返回一个错误(表示没有更多格式了),从而获得所有支持的格式列表。然后你选中一个格式(比如 MJPEG),再用 v4l2_frmsizeenum去查询这个格式具体支持哪些分辨率(比如 1920x1080, 1280x720, 640x480)

fmtdesc index:用途: 你要查询的格式的序号。
用法: 你从 0 开始,依次递增这个值去调用 ioctl,直到获取失败(即枚举完了所有支持的格式)。
fmtdesc type:用途: 指定你要枚举哪种类型的缓冲区的格式。
用法: 对于最常见的从摄像头捕获视频的情况,你会使用 V4L2_BUF_TYPE_VIDEO_CAPTURE。它还可以是其他类型,如 V4L2_BUF_TYPE_VIDEO_OUTPUT(用于输出)。

fsenum. index:用途: 要查询的帧尺寸的序号。
用法: 和 v4l2_fmtdesc 的 index 一样,从 0 开始递增,直到枚举完所有支持的分辨率。
fsenum. pixel_format: 用途: 这是最关键的联系字段。指定你要查询哪个格式所支持的分辨率。
用法: 这里你要填入之前用 v4l2_fmtdesc 枚举得到的某个格式的 pixelformat 值(例如 V4L2_PIX_FMT_MJPEG)。你必须先知道格式,才能查询其分辨率。

while(1){/* 枚举格式 */fmtdesc.index = fmt_index;  // 比如从0开始fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;  // 指定type为"捕获"if(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != 0) break;
枚举视频格式:
设置格式索引和类型(视频捕获)
使用VIDIOC_ENUM_FMT ioctl命令枚举设备支持的格式
如果枚举失败(返回非0),跳出循环

详细展开说一下工作步骤:

a. 查找文件描述符对应的驱动
内核根据您提供的文件描述符 fd,找到您之前打开的 /dev/video0 这个设备文件。
这个设备文件在内核中关联着一个特定的设备驱动程序——就是控制您摄像头硬件的那个 V4L2 驱动。

b. 派遣 ioctl 命令
内核发现这是一个 ioctl 调用,于是它会找到该设备驱动程序中实现的 ioctl 函数接口(通常是一个包含大量 switch-case 语句的函数),并将控制权交给它。
内核同时将 VIDIOC_ENUM_FMT 命令和您传递的用户空间内存地址 &fmtdesc 也交给驱动。

c. 驱动处理命令
驱动程序的 ioctl 函数里有一个很大的 switch (cmd) 语句,它会检查收到的命令是什么。
当它看到 cmd == VIDIOC_ENUM_FMT 时,它就明白:“啊,用户想要枚举支持的格式”。
驱动知道,对于这个命令,用户传递的参数是一个 struct v4l2_fmtdesc 类型结构的地址。

d. 安全地访问用户内存
驱动程序现在处于内核态,不能直接解引用您提供的用户空间指针 &fmtdesc,因为那是属于另一个地址空间(用户进程)的内存,直接访问会导致内核崩溃或安全漏洞。
内核会使用像 copy_from_user() 这样的函数,安全地将您填充好的 fmtdesc 结构体从用户空间复制到内核空间的一个临时副本中。这样驱动就可以安全地读取您的 index 和 type 了。

e. 执行请求的操作
驱动程序根据您传来的 index 和 type,在其内部的数据结构(通常是一个支持的格式列表)中查找第 index 个捕获格式。
找到后,驱动程序会填充那个内核空间的临时结构体副本:它将格式的 FourCC 码写入 pixelformat,将描述字符串写入 description,设置好 flags,等等。

f. 返回结果给用户
填充完成后,驱动程序再使用像 copy_to_user() 这样的函数,将已经填充好信息的结构体内核副本,安全地复制回您用户空间原来那个 fmtdesc 结构体所在的内存地址。
驱动程序的 ioctl 处理函数返回成功(0)或错误(-1)。

所以就能实现-1结束,0继续往下走。

frame_index = 0;
while(1){//枚举所支持的帧大小memset(&fsenum,0,sizeof(struct v4l2_frmsizeenum));fsenum.pixel_format = fmtdesc.pixelformat;fsenum.index = frame_index;
枚举帧大小:
重置帧索引为0
清空帧大小枚举结构体
设置像素格式和帧索引

其实我挺好奇的fsenum.pixel_format = fmtdesc.pixelformat;这个变量,但是这个变量并没有给值,那他有什么用呢? 后来问了一下D老师,确实被赋予了值,但这个赋值不是通过直接的赋值语句完成的,而是通过 ioctl 调用间接完成的。
当这个 ioctl 调用成功时,内核视频驱动程序会填充 fmtdesc 结构体的所有字段,包括 pixelformat。这就是为什么在内层循环中可以直接使用 fmtdesc.pixelformat 的原因。这个调用告诉内核:“请给我第0个视频捕获格式的信息”。

 if(ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&fsenum) == 0){printf("format %s %d ,framesize %d x %d \n",fmtdesc.description,fmtdesc.pixelformat,fsenum.discrete.width,fsenum.discrete.height);}else break;frame_index++;
获取并打印帧大小信息:
使用VIDIOC_ENUM_FRAMESIZES ioctl命令获取特定格式支持的帧大小
如果成功,打印格式描述、像素格式值和分辨率
如果失败,跳出内部循环
增加帧索引以枚举下一个分辨率

工作原理
设备打开:程序打开指定的视频设备文件(如/dev/video0)
格式枚举:通过循环调用VIDIOC_ENUM_FMT ioctl命令,枚举设备支持的所有视频格式
分辨率枚举:对于每个支持的格式,通过循环调用VIDIOC_ENUM_FRAMESIZES ioctl命令,枚举该格式支持的所有分辨率
信息输出:将枚举到的格式信息和分辨率信息打印到控制台
循环终止:当所有格式和分辨率都枚举完毕后,ioctl调用会返回错误,循环终止

外层循环次数 = 摄像头支持的格式数量
内层循环次数(对于每种格式)= 该格式支持的分辨率数量

book@100ask:~/test/03_video_params$ arm-buildroot-linux-gnueabihf-gcc -o video_test video_test.c
book@100ask:~/test/03_video_params$ adb push video_test root
video_test: 1 file pushed. 0.5 MB/s (8488 bytes in 0.016s)
book@100ask:~/test/03_video_params$ ./video_test /dev/video1

最终效果
在这里插入图片描述

http://www.xdnf.cn/news/18928.html

相关文章:

  • CPU、IO、网络与内核参数调优
  • 【目标检测】论文阅读5
  • 6.8 学习ui组件方法和Element Plus介绍
  • 【C++】类型系统:内置类型与自定义类型的对比
  • FlashAttention算法原理
  • 元宇宙与医疗健康:重构诊疗体验与健康管理模式
  • 【开题答辩全过程】以 微信小程序的老年活动中心为例,包含答辩的问题和答案
  • LabVIEW 音频信号处理
  • 火焰传感器讲解
  • laravel学习并连接mysql数据库
  • 煤矸石检测数据集VOC+YOLO格式3090张2类别
  • Python爬虫获取1688商品列表与图片信息
  • AGDO-BP+NSGAII梯度下降优化算法优化BP神经网络+NSGAII多目标优化算法,三目标和四目标案例
  • 【Oracle篇】伪列之ROWID:行数据的物理地址(基于物理地址对行数据最快速度的查询、更新、删除)(第四篇,总共六篇)
  • Python 前后端框架实战:从选型到搭建简易全栈应用
  • 使用MP4视频格式链接地址的自适应视频弹窗实现方案HTML代码
  • 共享云服务器替代传统电脑做三维设计会卡顿吗
  • 移远 × 高通:从开源生态到场景验证,共筑端侧AI新生态
  • 电脑开机显示器不亮
  • 私域电商新范式:开源AI智能名片链动2+1模式S2B2C商城小程序赋能传统行业流量转化
  • electron离线开发核心环境变量npm_config_cache
  • LangGraph - API多种访问方式
  • Diagnosing bias and variance|诊断偏差和方差
  • Redis哨兵机制:高可用架构的守护神!⚔️ 主从秒级切换实战指南
  • Elasticsearch核心配置详解与优化
  • 【Linux】Docker洞察:掌握docker inspect命令与Go模板技巧
  • 免费开源图片压缩工具|绿色版本地运行,支持批量压缩+格式转换,不上传数据,隐私安全有保障!
  • 毕业项目推荐:27-基于yolov8/yolov5/yolo11的电塔缺陷检测识别系统(Python+卷积神经网络)
  • 软件测试工程师面试题(含答案)
  • 重写BeanFactory初始化方法并行加载Bean