这个子系统里需要实现摄像头的初始化,Epoll事件的添加,处理函数的编写以及开始采集函数,这里为了方便起见,先把处理函数设置为保存读取到的图片。
为了方便描述定义一个结构:
struct v4l2_dev{
int fd;//设备文件fd
__u8 name[32];//保存摄像头标签
__u8 drv[16];//驱动名字
struct buf *buf;//图片数据的指针
struct event_ext *ev;//Epoll事件的附加结构
};
其中buf的结构如下:
struct buf{
void *start;
int len;
};
同时在v4l2中定义一个v4l2_dev结构:
struct cam
{
struct v4l2_dev *v4_dev;
};
摄像头子系统的初始化需要完成以下处理:
1、打开摄像头
2、获取驱动信息
3、设置图像格式
4、申请图像缓冲区
5、把内核空间的图像缓冲区映射到用户空间
6、图像缓冲入队列
7、往Epoll池中加入事件
整理之后摄像头子系统的程序如下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include "main.h"
struct buf{
void *start;
int len;
};
struct v4l2_dev{
int fd;//设备文件fd
__u8 name[32];//保存摄像头标签
__u8 drv[16];//驱动名字
struct buf *buf;//图片数据的指针
struct event_ext *ev;//Epoll事件的附加结构
};
struct cam
{
struct v4l2_dev *v4_dev;
};
/*事件处理函数*/
void cam_handler(int fd, void *arg)
{
struct v4l2_buffer buf;
struct v4l2_dev *v = arg;
int file_fd;
file_fd = open("test.jpg", O_RDWR | O_CREAT, 0777);
//帧出列
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
ioctl(v->fd, VIDIOC_DQBUF, &buf);
write(file_fd, v->buf[buf.index].start, v->buf[buf.index].len);
close(file_fd);
ioctl(v->fd, VIDIOC_QBUF, &buf);
}
/*初始化摄像头*/
struct v4l2_dev *v4l2_init()
{
struct v4l2_dev *v;
struct v4l2_capability cap;
struct v4l2_format fmt;
struct v4l2_requestbuffers req;
int i;
struct v4l2_buffer buf;
//打开摄像头
v = calloc(1, sizeof(struct v4l2_dev));
v->fd = open("/dev/video0",O_RDWR|O_NONBLOCK);
//获取驱动信息
ioctl(v->fd, VIDIOC_QUERYCAP, &cap);
if( !(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
printf("this is not a video device\n");
return -1;
}
strcpy((char *)v->name, (char *)cap.card);
strcpy((char *)v->drv, (char *)cap.driver);
//设置图像格式
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 1280;
fmt.fmt.pix.height = 1024;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
ioctl(v->fd, VIDIOC_S_FMT, &fmt);
//申请图像缓冲区
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
ioctl(v->fd, VIDIOC_REQBUFS, &req);
//把内核空间的图像缓冲区映射到用户空间
v->buf = calloc(req.count, sizeof(struct buf));
for(i=0; i < req.count; i++)
{
//获取缓冲区的信息
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
ioctl(v->fd, VIDIOC_QUERYBUF, &buf);
v->buf[i].len = buf.length;
v->buf[i].start = mmap(NULL,
buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
v->fd,
buf.m.offset);
}
//图像缓冲入队列
for(i=0; i<req.count; ++i)
{
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
ioctl(v->fd, VIDIOC_QBUF, &buf);
}
//往Epoll池中加入事件
v->ev = epoll_event_create(v->fd, EPOLLIN, cam_handler, v);
epoll_add_event(srv_main->epfd, v->ev);
return v;
}
void v4l2_start_capture(struct v4l2_dev *v)
{
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(v->fd, VIDIOC_STREAMON, &type);
}
struct cam *cam_sys_init()
{
struct cam *cam;
cam = calloc(1, sizeof(struct cam));
//初始化采集子系统
cam->v4_dev = v4l2_init();
//开始采集
v4l2_start_capture(cam->v4_dev);
}
编译后复制到开发板上运行,如果采集到了正确的图片信息说明代码编写成功。
更多Linux资料及视频教程点击这里