使用V4L2进行视频采集,一般有下面的五个主要步骤:
1.打开设备(一般是/dev/video),进行初始化参数设置,包括分辨率,像素格式等;
2.申请图像帧缓冲,并进行内存映射mmap;
3.把帧缓冲进行入队操作,开始视频流进行采集;
4.进行出队,然后对数据进行处理,然后入队,如此循环往复;
5.释放资源,停止采集工作。
主要流程和代码如下:
1、打开视频设备文件
#define FILE_VIDEO "/dev/video0"
//打开摄像头设备
if ((fd = open(FILE_VIDEO, O_RDWR)) == -1)
{
printf("Error opening V4L interface\n");
return FALSE;
}
2、查询或者设置设备参数
//查询设备属性
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
{
printf("Error opening device %s: unable to query device.\n",FILE_VIDEO);
return FALSE;
}
//显示所有支持帧格式
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Support format:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
{
printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
fmtdesc.index++;
}
//设置帧格式
if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
{
printf("Unable to set format\n");
return FALSE;
}
//获取帧格式
printf("get fmt...\n");
if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -1)
{
printf("Unable to get format\n");
return FALSE;
}
//设置及查看帧速率,这里设置30帧,就是1秒采集30张图
memset(&stream_para, 0, sizeof(struct v4l2_streamparm));
stream_para.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
stream_para.parm.capture.timeperframe.denominator = 30;
stream_para.parm.capture.timeperframe.numerator = 1;
if(ioctl(fd, VIDIOC_S_PARM, &stream_para) == -1)
{
printf("Unable to set frame rate\n");
return FALSE;
}
if(ioctl(fd, VIDIOC_G_PARM, &stream_para) == -1)
{
printf("Unable to get frame rate\n");
return FALSE;
}
3、申请帧缓冲,内存映射
int v4l2_mem_ops()
{
unsigned int n_buffers;
struct v4l2_requestbuffers req;
//申请帧缓冲
req.count=FRAME_NUM;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
if(ioctl(fd,VIDIOC_REQBUFS,&req)==-1)
{
printf("request for buffers error\n");
return FALSE;
}
// 申请用户空间的地址列
buffers = malloc(req.count*sizeof (*buffers));
if (!buffers)
{
printf ("out of memory!\n");
return FALSE;
}
// 进行内存映射
for (n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++)
{
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
//查询
if (ioctl (fd, VIDIOC_QUERYBUF, &buf) == -1)
{
printf("query buffer error\n");
return FALSE;
}
//映射
buffers[n_buffers].length = buf.length;
buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ|PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (buffers[n_buffers].start == MAP_FAILED)
{
printf("buffer map error\n");
return FALSE;
}
}
return TRUE;
}
4、入队,开启视频采集
//入队和开启采集
for (n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++)
{
buf.index = n_buffers;
ioctl(fd, VIDIOC_QBUF, &buf);
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMON, &type);
5、出队,进行数据处理,数据处理之后又入队,继续视频采集,如此循环往复
for(n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++)
{
//出队
buf.index = n_buffers;
ioctl(fd, VIDIOC_DQBUF, &buf);
//查看采集数据的时间戳之差,单位为微妙
buffers[n_buffers].timestamp = buf.timestamp.tv_sec*1000000+buf.timestamp.tv_usec;
cur_time = buffers[n_buffers].timestamp;
extra_time = cur_time - last_time;
last_time = cur_time;
printf("time_deta:%lld\n\n",extra_time);
printf("buf_len:%d\n",buffers[n_buffers].length);
//处理数据只是简单写入文件,名字以loop的次数和帧缓冲数目有关
printf("grab image data OK\n");
memset(file_name,0,sizeof(file_name));
memset(index_str,0,sizeof(index_str));
sprintf(index_str,"%d",loop*4+n_buffers);
strcpy(file_name,IMAGE);
strcat(file_name,index_str);
strcat(file_name,".jpg");
//strcat(file_name,".yuv");
FILE *fp2 = fopen(file_name, "wb");
if(!fp2)
{
printf("open %s error\n",file_name);
return(FALSE);
}
fwrite(buffers[n_buffers].start, IMAGEHEIGHT*IMAGEWIDTH*2,1,fp2);
fclose(fp2);
printf("save %s OK\n",file_name);
//入队循环
ioctl(fd, VIDIOC_QBUF, &buf);
}
6、结束采集
//关闭流
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMON, &type);
//关闭内存映射
for(n_buffers=0;n_buffers<FRAME_NUM;n_buffers++)
{
munmap(buffers[n_buffers].start,buffers[n_buffers].length);
}
//释放申请的内存
free(buffers);
//关闭设备
close(fd);
以上就是一个利用linux v4l2框架进行视频采集的一个完整流程,主要是对几个主要步骤的代码实现。