您当前的位置: 首页 >  linux

风间琉璃•

暂无认证

  • 3浏览

    0关注

    337博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Linux之V4L2驱动框架

风间琉璃• 发布时间:2022-08-30 10:11:27 ,浏览量:3

目录

一、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 中,常用的如下图。

 1.打开摄像头

视频类设备对应的设备节点为/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            
关注
打赏
1665385461
查看更多评论
0.0390s