v4l2 --是Linux内核中关于视频设备的内核驱动框架,为上层访问底层的视频设备提供了统一的接口。
/dev/vidioX
1.打开设备文件
fd=open("/dev/video3",O_RDWR);
/dev/video3:视频设备文件名
O_RDWR:可读可写
fd: open成功反返回文件描述符
jpeg
yuv
2.查询设备支持哪种格式
struct v4l2_fmtdesc fmt; //查询设备格式所用结构体
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.index = 0;
int ret;
//通过ioctl(文件描述符,命令,补充参数(依赖于命令))函数发送命令,成功返回0
ret = ioctl(fd,VIDIOC_ENUM_FMT,&fmt);
printf("format:%s\n",fmt.description);//成功可显示视频所支持的格式
3.设置视频格式
struct v4l2_format s_fmt;
s_fmt.fmt.pix.width = 650;
s_fmt.fmt.pix.height = 480;
s_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
s_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;//4
s_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd,VIDIOC_S_FMT,&s_fmt);
4.申请内核态缓冲
struct v4l2_requestbuffers reqbuf;
//memset(&reqbuf,0,sizeof(reqbuf));
reqbuf.count = 1;
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd,VIDIOC_REQBUFS,&reqbuf);
5.查询内核缓冲
struct v4l2_buffer buf;
//memset(&buf,0,sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = 0;
ret = ioctl(fd,VIDIOC_QUERYBUF,&buf);
6.把内核空间分配好的缓冲映射到用户空间
ub.len = buf.length;
ub.start = mmap(NULL,buf.length,
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,buf.m.offset);
if(ub.start == MAP_FAILED)
{
perror("mmap");
return -1;
}
7.添加到采集队列
ret = ioctl(fd,VIDIOC_QBUF,&buf);
8.启动视频采集
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_STREAMON,&type);
9. 监测视频采集是否完成
int select(int nfds, //最大文件描述符加1
fd_set *readfds,//?
fd_set *writefds, //0
fd_set *exceptfds, //0
struct timeval *timeout);
10.从队列中取出缓冲
ioctl(fd,VIDIOC_DQBUF,&buf);
11.处理图像
process_image(ub.start,ub.len);
12.停止/再次采集
ioctl(fd,VIDIOC_STREAMOFF,&type);/ioctl(fd,VIDIOC_QBUF,&buf);
13.资源回收
munmap(ub.start,ub.len);
close(fd);
#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>
#define CLEAR(x) memset(&(x), 0, sizeof(x))
int fd_fb;
char* pfb;
char yuyv[640*480*2];
char rgb[640*480*3];
struct buffer
{
void* start;
unsigned int length;
};
void process_image(struct buffer bb)
{
int i, j;
int y0, u0, y1, v0;
int r, g, b;
memcpy(yuyv, bb.start, bb.length);
//保存原始yuyv数据
//把yuyv转为rgb数据
for (i = 0; i < 640*480/2 ; i++)
{
y0 = yuyv[i*4];
y1 = yuyv[i*4+2];
u0 = yuyv[i*4+1];
v0 = yuyv[i*4+3];
//第i*2个点
b = (1.164383 * (y0 - 16) + 2.017232*(u0 - 128));
if (b > 255) b = 255;
if (b < 0) b = 0;
g= 1.164383 * (y0 - 16) - 0.391762*(u0 - 128) - 0.812968*(v0 - 128);
if (g > 255) g = 255;
if (g < 0) g = 0;
r= 1.164383 * (y0 - 16) + 1.596027*(v0 - 128);
if (r > 255) r = 255;
if (r < 0) r = 0;
rgb[i*3*2 + 0] = b;
rgb[i*3*2 + 1] = g;
rgb[i*3*2 + 2] = r;
//第i*2+1个点
b = (1.164383 * (y1 - 16) + 2.017232*(u0 - 128));
if (b > 255) b = 255;
if (b < 0) b = 0;
g= 1.164383 * (y1 - 16) - 0.391762*(u0 - 128) - 0.812968*(v0 - 128);
if (g > 255) g = 255;
if (g < 0) g = 0;
r= 1.164383 * (y1 - 16) + 1.596027*(v0 - 128);
if (r > 255) r = 255;
if (r < 0) r = 0;
rgb[i*3*2 + 3] = b;
rgb[i*3*2 + 4] = g;
rgb[i*3*2 + 5] = r;
}
for ( i = 0; i < 480; i++)
{
for(j=0; j<80; j++)
{
*(pfb+(i*800+j)*4 + 0) = 0x00;
*(pfb+(i*800+j)*4 + 1) = 0x00;
*(pfb+(i*800+j)*4 + 2) = 0x00;
}
for (j = 80; j < 720; j++)//????????640
{
*(pfb+(i*800+j)*4 + 0) = rgb[(i*640+j-80)*3 + 0];
*(pfb+(i*800+j)*4 + 1) = rgb[(i*640+j-80)*3 + 1];
*(pfb+(i*800+j)*4 + 2) = rgb[(i*640+j-80)*3 + 2];
}
for(j=720; j<800; j++)
{
*(pfb+(i*800+j)*4 + 0) = 0x00;
*(pfb+(i*800+j)*4 + 1) = 0x00;
*(pfb+(i*800+j)*4 + 2) = 0x00;
}
}
}
int main()
{
int fd_video;
//打开摄像头设备
fd_video = open("/dev/video3", O_RDWR);
if (fd_video < 0)
{
perror("video3 open");
return fd_video;
}
//打开屏幕设备
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
perror("fb open");
return fd_fb;
}
//设置视频的格式
struct v4l2_format s_fmt;
s_fmt.fmt.pix.width = 800;
s_fmt.fmt.pix.height = 480;
s_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
//s_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;//4
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("%d %d\n",s_fmt.fmt.pix.width,s_fmt.fmt.pix.height);
//映射
pfb = (char *)mmap(NULL, 800*480*4, PROT_WRITE|PROT_READ,MAP_SHARED, fd_fb, 0);
if (pfb == NULL)
{
perror ("mmap");
return -1;
}
//此处作用为申请缓冲区
struct v4l2_requestbuffers req;
req.count=4;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
ioctl(fd_video,VIDIOC_REQBUFS,&req);
//缓冲区与应用程序关联
struct buffer *buffers;//start, length
//申请4个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;
int i;
for (i = 0; i < 4; ++i)
{
struct v4l2_buffer buf;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
ioctl (fd_video, VIDIOC_QBUF, &buf);
}
while(1)
{
//开始捕获图像
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl (fd_video, VIDIOC_STREAMON, &type);
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
//取出图像数据
ioctl (fd_video, VIDIOC_DQBUF, &buf);
//在LCD屏幕上显示图像
process_image (buffers[buf.index]); //YUYV
//告知buf可以使用
ioctl (fd_video,VIDIOC_QBUF,&buf);
}
for (i = 0; i < 4; i++)
munmap(buffers[i].start, buffers[i].length);
free(buffers);
munmap(pfb, 800*480*4);
close(fd_video);
close(fd_fb);
}
代码不够完善,因为要结束摄像头按CTRL + C 最后面6行不会执行,这样的话不会执行munmap释放映射,free内存,close文件操作,
可以用信号signal函数SIGINT (ctrl +c)写个信号函数当按下CTRL + C 时处理最后面6行;
看代码
#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>
#include <signal.h>
#define CLEAR(x) memset(&(x), 0, sizeof(x))
int fd_fb;
char* pfb;
int fd_video;
char yuyv[640*480*2];
char rgb[640*480*3];
struct buffer *buffers;//start, length
struct buffer
{
void* start;
unsigned int length;
};
void mysignal(int signo)
{
printf("success\n");
int i;
for (i = 0; i < 4; i++)
{
munmap(buffers[i].start, buffers[i].length);
}
free(buffers);
munmap(pfb, 800*480*4);
close(fd_video);
close(fd_fb);
exit(0);
}
void process_image(struct buffer bb)
{
int i, j;
int y0, u0, y1, v0;
int r, g, b;
memcpy(yuyv, bb.start, bb.length);
//保存原始yuyv数据
//把yuyv转为rgb数据
for (i = 0; i < 640*480/2 ; i++)
{
y0 = yuyv[i*4];
y1 = yuyv[i*4+2];
u0 = yuyv[i*4+1];
v0 = yuyv[i*4+3];
//第i*2个点
b = (1.164383 * (y0 - 16) + 2.017232*(u0 - 128));
if (b > 255) b = 255;
if (b < 0) b = 0;
g= 1.164383 * (y0 - 16) - 0.391762*(u0 - 128) - 0.812968*(v0 - 128);
if (g > 255) g = 255;
if (g < 0) g = 0;
r= 1.164383 * (y0 - 16) + 1.596027*(v0 - 128);
if (r > 255) r = 255;
if (r < 0) r = 0;
rgb[i*3*2 + 0] = b;
rgb[i*3*2 + 1] = g;
rgb[i*3*2 + 2] = r;
//第i*2+1个点
b = (1.164383 * (y1 - 16) + 2.017232*(u0 - 128));
if (b > 255) b = 255;
if (b < 0) b = 0;
g= 1.164383 * (y1 - 16) - 0.391762*(u0 - 128) - 0.812968*(v0 - 128);
if (g > 255) g = 255;
if (g < 0) g = 0;
r= 1.164383 * (y1 - 16) + 1.596027*(v0 - 128);
if (r > 255) r = 255;
if (r < 0) r = 0;
rgb[i*3*2 + 3] = b;
rgb[i*3*2 + 4] = g;
rgb[i*3*2 + 5] = r;
}
for ( i = 0; i < 480; i++)
{
for(j=0; j<80; j++)
{
*(pfb+(i*800+j)*4 + 0) = 0x00;
*(pfb+(i*800+j)*4 + 1) = 0x00;
*(pfb+(i*800+j)*4 + 2) = 0x00;
}
for (j = 80; j < 720; j++)//????????640
{
*(pfb+(i*800+j)*4 + 0) = rgb[(i*640+j-80)*3 + 0];
*(pfb+(i*800+j)*4 + 1) = rgb[(i*640+j-80)*3 + 1];
*(pfb+(i*800+j)*4 + 2) = rgb[(i*640+j-80)*3 + 2];
}
for(j=720; j<800; j++)
{
*(pfb+(i*800+j)*4 + 0) = 0x00;
*(pfb+(i*800+j)*4 + 1) = 0x00;
*(pfb+(i*800+j)*4 + 2) = 0x00;
}
}
}
int main()
{
//打开摄像头设备
fd_video = open("/dev/video3", O_RDWR);
if (fd_video < 0)
{
perror("video3 open");
return fd_video;
}
//打开屏幕设备
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
perror("fb open");
return fd_fb;
}
//设置视频的格式
struct v4l2_format s_fmt;
s_fmt.fmt.pix.width = 800;
s_fmt.fmt.pix.height = 480;
s_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
//s_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;//4
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("%d %d\n",s_fmt.fmt.pix.width,s_fmt.fmt.pix.height);
//映射
pfb = (char *)mmap(NULL, 800*480*4, PROT_WRITE|PROT_READ,MAP_SHARED, fd_fb, 0);
if (pfb == NULL)
{
perror ("mmap");
return -1;
}
//此处作用为申请缓冲区
struct v4l2_requestbuffers req;
req.count=4;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
ioctl(fd_video,VIDIOC_REQBUFS,&req);
//缓冲区与应用程序关联
//申请4个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;
int i;
for (i = 0; i < 4; ++i)
{
struct v4l2_buffer buf;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
ioctl (fd_video, VIDIOC_QBUF, &buf);
}
signal(SIGINT,mysignal);
while(1)
{
//开始捕获图像
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl (fd_video, VIDIOC_STREAMON, &type);
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
//取出图像数据
ioctl (fd_video, VIDIOC_DQBUF, &buf);
//在LCD屏幕上显示图像
process_image (buffers[buf.index]); //YUYV
//告知buf可以使用
ioctl (fd_video,VIDIOC_QBUF,&buf);
}
}