目录
一、V4L2简介
二、V4L2操作流程
1.打开摄像头
2.查询设备的属性/能力/功能
3.获取摄像头支持的格式
4.设置摄像头的采集通道
5.设置/获取摄像头的采集格式和参数
6.申请帧缓冲、内存映射、入队
(1)申请帧缓冲
(2)内存映射
(3)入队
7.开启视频采集
8.读取数据、对数据进行处理
9.结束视频采集
三、应用编程
一、V4L2简介V4L2(Video for linux two)是 Linux 内核中视频类设备的一套驱动框架,为视频类设备驱动开发和应用层提供了一套统一的接口规范。使用 V4L2 设备驱动框架注册的设备会在 Linux 系统/dev/目录下生成对应的设备节点文件,设备节点的名称通常为 videoX(X 标准一个数字编号:/dev/videox),每一个 videoX 设备文件就代表一个视频类设备。应用程序通过对 videoX 设备文件进行 I/O 操作来配置、使用设备类设备。
二、V4L2操作流程V4L2摄像头驱动框架的访问是通过系统IO的接口 ------ ioctl函数,ioctl专用于硬件控制的系统IO的接口。
#include //包含头文件 int ioctl(int fd, unsigned long request, ...); fd: 文件描述符 request: 此参数与具体要操作的对象有关, 表示向文件描述符请求相应的操作 ...: 可变参函数, 第三个参数需要根据 request 参数来决定,配合 request 来使用 返回值: 成功返回 0,失败返回-1
ioctl()是一个文件 IO 操作的杂物箱,可以处理的事情非常杂、不统一,一般用于操作特殊文件或硬件外设,可以通过 ioctl 获取外设相关信息。通过 ioctl()来完成,搭配不同的 V4L2 指令(request参数)请求不同的操作,这些指令定义在头文件 linux/videodev2.h 中,常用的如下图。
视频类设备对应的设备节点为/dev/videoX, X 为数字编号,通常从 0 开始,调用 open 打开,得到文件描述符 fd。
int fd = -1;
fd = open("/dev/video0", O_RDWR);
if (0 > fd)
{
perror( "open error");
exit(-1);
}
2.查询设备的属性/能力/功能
打开设备后,需要查询设备的属性,确定该设备是否是一个视频采集类设备。通过 ioctl()将获取到一个 struct v4l2_capability 类型数据, struct v4l2_capability 数据结构描述了设备的一些属性,结构体定义如下:
struct v4l2_capability {
__u8 driver[16]; /* 驱动的名字 */
__u8 card[32]; /* 设备的名字 */
__u8 bus_info[32]; /* 总线的名字 */
__u32 version; /* 版本信息 */
__u32 capabilities; /* 设备拥有的能力 */
__u32 device_caps;
__u32 reserved[3]; /* 保留字段 */
};
这里关注capabilities 字段,该字段描述了设备拥有的能力,该字段的值如下
所以可以通过判断 capabilities字段是否包含 V4L2_CAP_VIDEO_CAPTURE、 来确定它是否是一个摄像头设备。
struct v4l2_capability cap = {};
ioctl(fd,VIDIOC_QUERYCAP,&cap);
if(cap.capabilities&V4L2_CAP_VIDEO_CAPTURE)
{
//是一个摄像头
}
3.获取摄像头支持的格式
获取支持的像素格式使用 VIDIOC_ENUM_FMT 指令
struct v4l2_fmtdesc {
__u32 index; /*格式编号*/
__u32 type; /*摄像头的格式 V4L2_BUF_TYPE_VIDEO_CAPTURE*/
__u32 flags;
__u8 description[32]; /*描述信息:描述 pixelformat 像素格式。*/
__u32 pixelformat; /*类型格式 --- 4字节:像素格式编号*/
__u32 reserved[4];
};
pixelfoemat像素格式:
//定义格式的宏
#define v4l2_fourcc(a, b, c, d)\
((__u32)(a) | ((__u32)(b) 255) b = 255;
if(r < 0) r = 0;
if(g < 0) g = 0;
if(b < 0) b = 0;
pixel[0] = r;
pixel[1] = g;
pixel[2] = b;
return pixel24;
}
int yuyv2rgb0(unsigned char *yuv, unsigned char *rgb, unsigned int width, unsigned int height)
{
unsigned int in, out;
int y0, u, y1, v;
unsigned int pixel24;
unsigned char *pixel = (unsigned char *)&pixel24;
unsigned int size = width*height*2;
for(in = 0, out = 0; in < size; in += 4, out += 6)
{
y0 = yuv[in+0];
u = yuv[in+1];
y1 = yuv[in+2];
v = yuv[in+3];
sign3 = 1;
pixel24 = yuyv2rgb(y0, u, v);
rgb[out+0] = pixel[0];
rgb[out+1] = pixel[1];
rgb[out+2] = pixel[2];
pixel24 = yuyv2rgb(y1, u, v);
rgb[out+3] = pixel[0];
rgb[out+4] = pixel[1];
rgb[out+5] = pixel[2];
}
return 0;
}
//LCD初始化
void lcd_init()
{
lcd_fd = open("/dev/fb0",O_RDWR);
if(lcd_fd==-1){
perror("open");;
exit(-1);
}
//映射
memp = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd_fd,0);
if(memp==MAP_FAILED){
perror("mmap");;
exit(-1);
}
}
void lcd_uninit()
{
munmap(memp,800*480*4);
close(lcd_fd);
}
int main()
{
lcd_init();
//1.打开摄像头
int fd = open("/dev/video7",O_RDWR);
if(fd==-1){
perror("open");
exit(-1);
}
//2.获取功能参数
struct v4l2_capability cap = {};
int res = ioctl(fd,VIDIOC_QUERYCAP,&cap);
if(res==-1){
perror("ioctl cap");
exit(-1);
}
if(cap.capabilities&V4L2_CAP_VIDEO_CAPTURE){
//设备是一个摄像头
printf("capture device!\n");
}
else{
printf("not a capture device!\n");
exit(-1);
}
//3.获取摄像头支持的格式
struct v4l2_fmtdesc fmt = {};
fmt.index = 0;//第一种格式
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//获取摄像头的格式
while((res=ioctl(fd,VIDIOC_ENUM_FMT,&fmt))==0){
printf("pixformat=%c%c%c%c,description=%s\n",fmt.pixelformat&0xff,(fmt.pixelformat>>8)&0xff,
(fmt.pixelformat>>16)&0xff,(fmt.pixelformat>>24)&0xff,fmt.description);
fmt.index++;
}
//4.设置采集通道
int index = 0;//使用通道0
res = ioctl(fd,VIDIOC_S_INPUT,&index);
if(res==-1){
perror("ioctl s_input");
exit(-1);
}
//5.设置摄像头的采集格式
struct v4l2_format format = {};
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = 640;
format.fmt.pix.height = 480;
format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//YUYV格式
format.fmt.pix.field= V4L2_FIELD_NONE;
res = ioctl(fd,VIDIOC_S_FMT,&format);
if(res==-1){
perror("ioctl s_fmt");
exit(-1);
}
//6.申请缓存空间
struct v4l2_requestbuffers req = {};
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
res = ioctl(fd,VIDIOC_REQBUFS,&req);
if(res==-1){
perror("ioctl reqbufs");
exit(-1);
}
//7.分配,映射,入队
size_t i,max_len = 0;
for(i=0;imax_len)
max_len = buf.length;
//映射
buffer[i].length = buf.length;
buffer[i].start = mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset);
if(buffer[i].start==MAP_FAILED){
perror("mmap");
exit(-1);
}
//入队
res = ioctl(fd,VIDIOC_QBUF,&buf);
if(res==-1){
perror("ioctl qbuf");
exit(-1);
}
}
//申请临时缓冲区
current.start = malloc(max_len);
if(current.start==NULL){
perror("malloc");
exit(-1);
}
//8.启动摄像头
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
res = ioctl(fd,VIDIOC_STREAMON,&buf_type);
if(res==-1){
perror("ioctl streamon");
exit(-1);
}
//延时
sleep(1);
//RGB缓冲区
char rgb[640*480*3];
//9.采集数据
while(1){
struct v4l2_buffer buf = {};
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
//出队
res = ioctl(fd,VIDIOC_DQBUF,&buf);
if(res==-1){
perror("ioctl dqbuf");
}
//拷贝数据
memcpy(current.start,buffer[buf.index].start,buf.bytesused);
current.length = buf.bytesused;
//入队
res = ioctl(fd,VIDIOC_QBUF,&buf);
if(res==-1){
perror("ioctl qbuf");
}
//显示 保存 传输.....
//YUYV转RGB
yuyv2rgb0(current.start,rgb,640,480);
//显示到LCD
int x,y;
for(y=0;y
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?