红联Linux门户
Linux帮助

ubuntu通过V4L2采集jpg并显示jpg

发布时间:2017-06-02 11:40:11来源:linux网站作者:dong_beijing
用ubuntu想在写代码时,随时观察自己身后的状态,用cheese做录像太明显了,而且还得动鼠标,打算写个用的摄像头拍照的工具。用v4l2应该最容易。
 
头文件,包括mem,dev和ioctl相关的头文件
#include <fcntl.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/ioctl.h>  
#include <unistd.h>  
#include <errno.h>  
#include <sys/mman.h>  
#include <linux/videodev2.h>  
 
定义一个方便传递的结构体
struct buffer  
{  
void* start;  
unsigned int length;  
};  
 
所有的函数定义:
int main()  
{  
FILE* fp;  
int fd_video;  
struct buffer *buffers;
//打开摄像头设备  
fd_video = open("/dev/video0", O_RDWR);  
if (fd_video < 0)  
{  
perror("video capture open");  
return fd_video;  
}
fp = fopen("/root/test.jpg", "wb+");  
if (fp < 0)  
{  
perror("fb open error.");  
return -1;  
}
//打印支持的视频格式  
struct v4l2_fmtdesc fmtdesc;  
fmtdesc.index=0;  
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;  
printf("Support format:\n");  
while(ioctl(fd_video, VIDIOC_ENUM_FMT, &fmtdesc) != -1)  
{  
printf("\t%d.%s ,fmtdesc:%d\n",fmtdesc.index+1,fmtdesc.description,fmtdesc.pixelformat);  
fmtdesc.index++;  
}  
//设置视频的格式,720P JPEG  
struct v4l2_format s_fmt;  
s_fmt.fmt.pix.width = 1280;  
s_fmt.fmt.pix.height = 720;  
s_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;  
printf("s_fmt.fmt.pix.pixelformat:%d\n",s_fmt.fmt.pix.pixelformat);  
s_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
int flag= ioctl(fd_video,VIDIOC_S_FMT,&s_fmt);  
if(flag != 0)  
{  
printf("set format error\n");  
return -1;  
}
printf("jpeg:%d %d\n",s_fmt.fmt.pix.width,s_fmt.fmt.pix.height);
//申请1个缓冲区  
struct v4l2_requestbuffers req;  
req.count=1;  
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;  
req.memory=V4L2_MEMORY_MMAP;  
ioctl(fd_video,VIDIOC_REQBUFS,&req);  
//缓冲区与应用程序关联
//申请1个struct buffer空间  
buffers = (struct buffer*)calloc (req.count, sizeof (struct buffer));  
if (!buffers)  
{  
perror ("Out of memory");  
exit (EXIT_FAILURE);  
}  
unsigned int n_buffers;  
for (n_buffers = 0; n_buffers < req.count; n_buffers++)  
{  
struct v4l2_buffer buf;  
memset(&buf,0,sizeof(buf));  
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
buf.memory = V4L2_MEMORY_MMAP;  
buf.index = n_buffers;  
if (-1 == ioctl (fd_video, VIDIOC_QUERYBUF, &buf))  
exit(-1);  
buffers[n_buffers].length = buf.length;  
buffers[n_buffers].start = mmap (NULL,  
buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd_video, buf.m.offset);  
if (MAP_FAILED == buffers[n_buffers].start)  
exit(-1);  
}
enum v4l2_buf_type type;  
for (n_buffers = 0; n_buffers < req.count; n_buffers++)  
{  
struct v4l2_buffer buf;  
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
buf.memory = V4L2_MEMORY_MMAP;  
buf.index = n_buffers;  
ioctl (fd_video, VIDIOC_QBUF, &buf);  
}
//开始捕获图像  
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
ioctl (fd_video, VIDIOC_STREAMON, &type);
struct v4l2_buffer buf;  
memset(&(buf), 0, sizeof(buf));  
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
buf.memory = V4L2_MEMORY_MMAP;  
//取出图像数据  
ioctl (fd_video, VIDIOC_DQBUF, &buf);  
//保存图像  
fwrite(buffers[buf.index].start,1,buffers[buf.index].length,fp);//mjpeg  
fflush(fp);  
//放回缓冲区  
ioctl (fd_video,VIDIOC_QBUF,&buf);
for (n_buffers = 0; n_buffers < req.count; n_buffers++)  
munmap(buffers[n_buffers].start, buffers[n_buffers].length);  
free(buffers);
close(fd_video);  
fclose(fp);  
printf("capture jpg finish..\n");  
system("eog -w /root/test.jpg &");  
}
 
说明:
打开摄像头和图片
fd_video = open("/dev/video0", O_RDWR);  
fp = fopen("/root/test.jpg", "wb+");  
 
查询摄像头支持的格式,我的电脑支持yuyv和mjpeg
ioctl(fd_video, VIDIOC_ENUM_FMT, &fmtdesc)  
 
设置摄像头拍摄的格式,尺寸是720P,格式是MJPEG
ioctl(fd_video,VIDIOC_S_FMT,&s_fmt)  
 
申请缓冲区,一张图片就需要一帧的缓冲区
ioctl(fd_video,VIDIOC_REQBUFS,&req);  
 
用calloc是申请多个缓冲区用的,这里申请1个缓冲区,与malloc一样,这里是在摄像基础上改的,用calloc就不改了。
buffers = (struct buffer*)calloc (req.count, sizeof (struct buffer));  
 
用mmap映射内存的目的,是省去了memcpy的花销,更加合理
buffers[n_buffers].start = mmap (NULL,  
buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd_video, buf.m.offset);  
 
设置缓冲区,并放回缓冲区
ioctl (fd_video, VIDIOC_QBUF, &buf)  
 
开始捕获图像
ioctl (fd_video, VIDIOC_STREAMON, &type)  
 
从缓冲区取出一帧数据
ioctl (fd_video, VIDIOC_DQBUF, &buf)  
 
取完数据,再放回缓冲区
ioctl (fd_video, VIDIOC_QBUF, &buf)  
 
之后释放资源,就不赘述了。
 
通过eye of gnome将jpg显示出来。
system("eog -w /root/test.jpg &");
 
本文永久更新地址:http://www.linuxdiyf.com/linux/31224.html