当前位置: 首页 > 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.xdnf.cn/news/1400437.html

相关文章:

  • PostgreSQL数据类型一览(数值类型)
  • opencv实现轮廓绘制和选择
  • 生成式 AI 重构内容生产:效率提升背后的创作版权边界争议
  • day43-Ansible-PlayBook
  • 如何使用快照将 AWS OpenSearch 服务中的数据从开发环境复制到生产环境
  • 知料觅得-新一代AI搜索引擎
  • Linux网络服务发现在VPS云服务器自动化配置的关键技术与实践
  • 给某个conda环境安装CUDA 12.4版本 全局CUDA不变
  • C++的迭代器和指针的区别
  • 【小白笔记】基本的Linux命令来查看服务器的CPU、内存、磁盘和系统信息
  • Java SpringAI应用开发面试全流程解析:RAG、流式推理与企业落地
  • 物联网(IoT)中常用的通信协议
  • GD32VW553-IOT 基于 vscode 的 bootloader 移植(基于Cmake)
  • 微论-突触的作用赋能思考(可能是下一代人工智能架构的启发式理论)
  • 响应式编程框架Reactor【5】
  • Spring代理的特点
  • AI-调查研究-65-机器人 机械臂控制技术的前世今生:从PLC到MPC
  • 【MCP系列教程】 Python 实现 FastMCP StreamableHTTP MCP:在通义灵码 IDE 开发并部署至阿里云百炼
  • JsMind 常用配置项
  • 【计算机网络】HTTP是什么?
  • 基于Docker部署的Teable应用
  • Linux驱动开发重要操作汇总
  • “人工智能+”政策驱动下的技术重构、商业变革与实践路径研究 ——基于国务院《关于深入实施“人工智能+”行动的意见》的深度解读
  • wpf之依赖属性
  • 桌面GIS软件FlatGeobuf转Shapefile代码分享
  • 学习游戏制作记录(视觉上的优化)
  • 第三弹、AI、LLM大模型是什么?
  • Visual Studio(vs)免费版下载安装C/C++运行环境配置
  • openEuler2403安装部署Redis8
  • FPGA学习笔记——SPI读写FLASH