Linux摄像头数据接收——V4L2详细教程

在开发项目的过程中遇到了处理摄像头接收数据的需求,故而记录下,便于之后回忆.
V4L2是什么大家搜搜就有,简单来说V4L2 是专门为 linux 设备设计的一套视频框架。我们可以借助于这个框架来实现摄像头数据的接收。
我们可以把整个V4L2接收数据的代码分为以下几个部分:

①打开摄像头设备文件 ②查询摄像头相应的信息 ③设置摄像头的格式 ④(循环)接收数据 ⑤结束

①打开摄像头设备文件:

fb = open("/dev/video0",O_RDWR);//读写方式打开摄像头设备文件
if(fb == -1)	//返回值-1为直白
{
	perror("open video error!");		//输出失败的原因
	exit(-1);
}

②查询摄像头相应的信息

查询摄像头的信息就需要用ioctl()函数来进行交互,具体的命令在/usr/include/linux/videodev2.h 中,如下图所示。videodev2.h
这里有很多命令,有查询的也有设置的,需要什么用什么,这里我只据几个简单的例子。(ioctl函数的第三个参数类型使用相应命令后面的类型即可)
VIDIOC_QUERYCAP命令是判断这个设备是否和内核驱动相兼容,如果兼容就会返回相应的信息:

ret = ioctl(fb,VIDIOC_QUERYCAP,&v4_cap);
if(ret < 0)
{
	perror("get camera capbility error!");
}
printf("camera driver:%s,card:%s,bus_info:%s\n",v4_cap.driver,v4_cap.card,v4_cap.bus_info);

VIDIOC_ENUM_FMT命令用来获取设备传输支持的视频格式。

v4_fmat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//设置type,此时选择video捕捉
while(1)
{
	v4_fmat.index = i++;//因为可能支持很多格式,所以用index来获取每一个格式
	ret = ioctl(fb,VIDIOC_ENUM_FMT,&v4_fmat);
	if(ret < 0)
	{
		perror("get camera's format error!");
		break;
	}
	printf("index :%d\t",v4_fmat.index);
	printf("camera description:%s\n",(unsigned char *)v4_fmat.description);
}

VIDIOC_G_FMT命令用来查询摄像头视频的默认格式(可以修改,但是不一定成功),比如视频帧的宽、高和格式。

v4_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//设置type,此时选择video捕捉
ret = ioctl(fb,VIDIOC_G_FMT,&v4_format);
if(ret < 0)
{
	perror("query format error!");
}
printf("collect format is width:%d,height:%d\n",v4_format.fmt.pix.width,v4_format.fmt.pix.height);

注:此处的pixelformat是__u32类型的

③设置摄像头的格式

如果我们不想按照默认格式来接收,就需要更改摄像头的格式,用VIDIOC_S_FMT命令来设置设备格式,但是不一定成功(设备本身不支持),所以一般设置之后就会再用VIDIOC_G_FMT命令来查看是否设置成功。

v4_set.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4_set.fmt.pix.width = 640;
v4_set.fmt.pix.height = 480;
v4_set.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV

ret = ioctl(fb,VIDIOC_S_FMT,&v4_set);
if(ret < 0)
{
	perror("set format error!");
}

pixelformat 的具体格式还是在videodev2.h文件中,如下图videodev2.h

④(循环)接收数据

接下来就是具体的接收摄像头的数据,一般是循环接收数据,此处之展示接收一帧数据。
接收数据步骤:向驱动申请缓冲区,建立内存映射,开始采集
VIDIOC_REQBUFS命令用来申请缓冲区和设置映射方式

v4_rebuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	v4_rebuf.memory = V4L2_MEMORY_MMAP;//映射方式
	v4_rebuf.count = count;//申请count个缓冲区
	ret = ioctl(fb,VIDIOC_REQBUFS,&v4_rebuf);
	if(ret < 0)
	{
		perror("malloc buffer error!");
	}

之后就是建立内存映射,因为一个一个字节读写使得系统调用的很频繁,故而一般使用mmap映射的方式来回去数据。

v4_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for(i = 0;i < count; i++)
{
	v4_buf.index = i;
	ret = ioctl(fb,VIDIOC_QUERYBUF,&v4_buf);
	if(ret < 0)
	{
		perror("set memory mmap error!");
	}
	mmapbuf[i] = (unsigned char*)mmap(NULL,v4_buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fb,v4_buf.m.offset);
	mmapbuf_size[i] = v4_buf.length;
	printf("mmap:%d\t",i);
	ret = ioctl(fb,VIDIOC_QBUF,&v4_buf);//告诉内核这一次使用完成
	if(ret < 0)
	{
		perror("tell kernel fail in mmap!");
	}
}

切记在VIDIOC_QUERYBUF之后一定要VIDIOC_QBUF告诉内核使用完成!
具体的采集用如下三个命令,一般是循环VIDIOC_DQBUF和VIDIOC_QBUF命令来不停的获取试试设备的数据。
VIDIOC_STREAMON(开始采集)
VIDIOC_DQBUF(提取数据)
VIDIOC_QBUF(告诉内核这一次使用完成)
注:VIDIOC_DQBUF之获取到数据的index和length,具体的数据在之前映射好的内存地址那里。

⑤结束

VIDIOC_STREAMOFF(停止采集)
mmap之后要munmap,

munmap(mmapbuf[i],mmapbuf_size[i]);//第一个参数是地址,第二个参数是长度。
close(fb);//关闭打开的设备文件

具体代码放在了:这里点击

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值