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

嵌入式linux相机(2)

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

问题汇总
1.为什么int变量给枚举不会报错?  int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
2.void *bufs[32] 是什么?
3.为什么需要配置和检查捕获能力?
4.申请缓冲区是向谁申请?
5.缓冲区位于哪里?
6.明明要传入函数指针,为什么可以传入结构体?
7.是将数据都放到了bufs[i]里面了对吧,是这段代码实现的数据存放吗? 
8.尝试了adb拉取所有图片发现拉取不了,只能从头开始设置IP地址。 

第一个项目相关使用的命令行,得记录使用的数据分辨率大小

在这里插入图片描述

arm-buildroot-linux-gnueabihf-gcc -o video_get_data video_get_data.c
adb push video_get_data root
./video_get_data /dev/video1
adb pull root/video_raw_data_0010.jpg  home

代码程序

代码实现输出支持的图像大小以及格式,并截取图片。


#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>
#include <poll.h> 
#include <sys/mman.h>
#include <unistd.h>/* ./video_test </dev/video0>  */int main(int argc, char **argv)
{int fd;struct v4l2_fmtdesc fmtdesc;              // 参数结构体初始化int fmt_index = 0;int frame_index = 0;struct v4l2_frmsizeenum fsenum;void *bufs[32];                          int buf_cnt;int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;struct pollfd fds[1];char filename[32];int file_cnt = 0;if (argc != 2){printf("Usage: %s </dev/videoX>,print detail for video device\n",argv[0]);return -1;}/* open */fd = open(argv[1],O_RDWR);if (fd < 0){printf("can not open %s \n",argv[1]);return -1;}// 查询能力struct v4l2_capability capability;memset(&capability, 0, sizeof(struct v4l2_capability));if (0 == ioctl(fd, VIDIOC_QUERYCAP, &capability)){if((capability.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {printf("Error opening device %s: video capture not supported.\n",argv[1]);return -1;}if(!(capability.capabilities & V4L2_CAP_STREAMING)) {printf("%s does not support streaming i/o\n", argv[1]);return -1;}}else {printf("can not get capability\n");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++;}// 设置摄像头分辨率struct v4l2_format fmt;memset(&fmt, 0, sizeof(struct v4l2_format));fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = 800;fmt.fmt.pix.height = 480;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;fmt.fmt.pix.field = V4L2_FIELD_ANY;if (0 == ioctl(fd, VIDIOC_S_FMT, &fmt)){printf("set format ok :%d x %d\n",fmt.fmt.pix.width,fmt.fmt.pix.height);}else {printf("can not set format\n");return -1;}/** 申请 buffers*/struct v4l2_requestbuffers rb;memset(&rb, 0, sizeof(struct v4l2_requestbuffers));rb.count = 32;rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;rb.memory = V4L2_MEMORY_MMAP;if ( 0 == ioctl(fd, VIDIOC_REQBUFS, &rb)){/** 申请成功后,map the buffers*/buf_cnt = rb.count;for(int i = 0; i < rb.count; i++) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if(0 == ioctl(fd, VIDIOC_QUERYBUF, &buf)){// mmapbufs[i] = mmap(0 /* start anywhere */ ,buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd,buf.m.offset);if(bufs[i] == MAP_FAILED) {printf("Unable to map buffer");return -1;}}else{printf("can not query buffer");return -1 ;}}printf("map %d bufders ok \n ",buf_cnt);}else {printf("can not request buffers\n");return -1;}// 把所有buffer放入"空闲链表"/** Queue the buffers.*/for(int i = 0; i < buf_cnt; ++i) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if ( 0 != ioctl(fd, VIDIOC_QBUF, &buf)){printf("Unable to queue buffer");return -1;}}printf("queue buffers ok \n");// ioctl VIDIOC_STREAMON:启动摄像头if( 0 == ioctl(fd, VIDIOC_STREAMON, &type)){printf("start capture ok");}else {printf("Unable to start capture");return -1;}//就是这段代码的问题查原因  while(1)     {/* poll */memset(fds,0,sizeof(fds));fds[0].fd = fd;fds[0].events = POLLIN;/* 把buffer取出队列 */if (1 == poll(fds,1,-1)){struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 == ioctl(fd, VIDIOC_DQBUF, &buf));else{printf("Unable to dequeue buffer");return -1;}/* 把buffer的数据存储为文件 */sprintf(filename,"video_raw_data_%04d.jpg",file_cnt++);int fd_file = open(filename,O_RDWR | O_CREAT, 0666);if (fd_file < 0){printf("can not create file: %s \n",filename);}printf("capture to %s\n",filename);write(fd_file,bufs[buf.index], buf.bytesused);close(fd_file);/* 把buffer放入队列 */if (0 == ioctl(fd, VIDIOC_DQBUF, &buf));else{printf("Unable to dequeue buffer");return -1;}}}if (0 == ioctl(fd, VIDIOC_STREAMOFF, &type)){printf("stop capture");}else {printf("Unable to stop capture");return -1;}close(fd);return 0;}

代码不详细解释,只有本人不熟的问题。

1.为什么int变量给枚举不会报错? int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
在C++中,枚举类型本质上就是整数类型。每个枚举值在编译时会被替换为对应的整数值。因此,将枚举值赋给int变量是完全合法的,编译器不会报错。

2.void bufs[32] 是什么?
这是一个包含32个void指针的数组:void
是一种通用指针类型,可以指向任何类型的数据
[32] 表示这是一个包含32个元素的数组,每个元素都是一个指针,可以存储任何数据类型的地址这种结构在系统编程中很常见,特别是在需要管理多种类型数据或需要通用数据存储的场景中。

3.为什么需要配置和检查捕获能力?
1. 设备多样性
不是所有视频设备都支持视频捕获功能。有些设备可能是:
视频输出设备(如HDMI输出)
音频设备
无线电接收设备
其他特殊用途设备

2. 功能支持差异
不同设备支持不同的I/O方法:
V4L2_CAP_STREAMING: 支持流式I/O(内存映射或用户指针)
V4L2_CAP_READWRITE: 支持传统的read()/write() I/O
V4L2_CAP_VIDEO_CAPTURE: 支持视频捕获
V4L2_CAP_VIDEO_OUTPUT: 支持视频输出

4.申请缓冲区是向谁申请?
不是直接向摄像头硬件申请,而是向内核中的V4L2驱动程序申请
驱动程序负责管理视频捕获所需的内存区域
这些内存区域用于存储摄像头捕获的帧数据

5.缓冲区位于哪里?
缓冲区位于内核空间,通过mmap系统调用映射到用户空间,摄像头硬件通过DMA(直接内存访问) 直接将数据写入这些缓冲区

void *memset(void *s, int c, size_t count)
{char *xs = s;while (count--)*xs++ = c;return s;
}   memset

6.明明要传入函数指针,为什么可以传入结构体? `

struct v4l2_requestbuffers rb;
memset(&rb, 0, sizeof(struct v4l2_requestbuffers));

void* 的通用性​​:
void* 是“无类型指针”,可接受任何类型的数据指针(如 int*、char*、结构体指针等)。编译器会自动将具体指针类型转换为 void*。
​​结构体的内存本质​​:
结构体在内存中是连续的字节块。memset 按字节操作内存,与数据类型无关。传入 &rb 时,函数会从该地址开始,将 sizeof(struct v4l2_requestbuffers) 个字节全部设为 0

7.是将数据都放到了bufs[i]里面了对吧,是这段代码实现的数据存放吗? 是的,你的理解非常准确!这段代码确实是实现数据存放的关键步骤之一。它通过 mmap 系统调用,​​将驱动在内核空间申请的内存映射到你的用户空间应用程序中​​,这样你就可以直接读取或处理采集到的视频帧数据了。
为了让这个过程更清晰,我们来分解一下:
内存映射 (mmap) 的作用 直接访问​​:mmap 让你无需在用户态和内核态之间拷贝数据(避免了 read/write 之类的系统调用开销),就能直接访问驱动中申请的帧缓冲区。这对于需要高效处理大量视频数据的应用至关重要。

8.尝试了adb拉取所有图片发现拉取不了,只能从头开始设置IP地址。

在这里插入图片描述
直接输入ifconfig eth0 192.168.5.9设置IP地址。
在这里插入图片描述
方案2:更改永久保存如图所示输入 vi/etc/network/interfaces 命令行,更改设置,设置内容如图所示。
在这里插入图片描述
重启之后可以看到。
在这里插入图片描述
电脑之间实现相互应答。

在这里插入图片描述

在这里插入图片描述

arm-buildroot-linux-gnueabihf-gcc -o video_brightness video_brightness.c
adb push video_brightness root
./video_brightness /dev/video1
shift+鼠标左键指定选择区域
d+回车要慢一点按

实现摄像机亮度调节如图所示。
在这里插入图片描述
pthread_create(&thread, NULL, thread_brightness_control, (void )fd);
void 的通用性
:void* 是C语言中的通用指针类型,可以指向任何数据类型,类型转换:C语言允许在整数类型和指针类型之间进行显式,转换大小兼容:在大多数系统上,指针的大小足以存储一个int值

http://www.dtcms.com/a/358293.html

相关文章:

  • 设计模式 - 静态工厂模式 + 策略模式,
  • 【Java后端】MySQL 常见 SQL 语句优化指南
  • AI 赋能综合能源管理系统:开启智慧能源新时代
  • 掌握表单:React中的受控组件与表单处理
  • 详解Vue2、Vue3与React的Diff算法
  • 【Android】OkHttp发起GET请求 POST请求
  • React Router 6 获取路由参数
  • 【自然语言处理与大模型】如何进行大模型多模态微调
  • 【ASP.NET Core】双Token机制在ASP.NET Core中的实现
  • OpenCV 图像形态学操作与边缘检测实战指南
  • ESPTimer vs GPTimer:ESP32 定时器系统深度解析
  • 机器学习 - Kaggle项目实践(6)Dogs vs. Cats Redux: Kernels Edition 猫狗二分类
  • 最强分布式锁工具:Redisson
  • Git 的核心工作流程(三区域模型)
  • github同一台电脑支持两个或以上的ssh账户(macos或Linux系统),解决Key is already in use问题
  • 医院排班|医护人员排班系统|基于springboot医护人员排班系统设计与实现(源码+数据库+文档)
  • 苍穹外卖Day7 | 缓存商品、购物车、SpringCache、缓存雪崩、缓存套餐
  • SpringCloud Alibaba微服务--Sentinel的使用
  • docker 部署Skywalking
  • 基于大模型与 PubMed 检索的光谱数据分析系统
  • 大语言模型的“可解释性”探究——李宏毅大模型2025第三讲笔记
  • Java类加载与JVM详解:从基础到双亲委托机制
  • idea 普通项目转换成spring boot项目
  • Python实现半角数字转全角数字的完整教程
  • 《中国棒垒球》垒球世界纪录多少米·垒球8号位
  • Visual Studio(vs)免费版下载安装C/C++运行环境配置
  • LeetCode 287.寻找重复数
  • Java试题-选择题(23)
  • 【LeetCode 热题 100】62. 不同路径——(解法四)组合数学
  • 聊一聊 .NET 的 AssemblyLoadContext 可插拔程序集