本人从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))
{
// mmap
bufs[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值