继平台驱动模型之后的V4L2,现在贴上完整注释版的源码,我已经在三星exynos4412处理器上跑过,亲测无误,此文的注释我已经写的非常详细,要是有问题,欢迎留言哈~~~,希望对看到的你有所帮助~~
话不多说,直接上代码
/*
* 文件名:camera.c
* 文件描述:linux视频驱动接口V4L2视频采集
* 编写人:兰永祥
* 编写日期:2019-11-10
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <getopt.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h> /* for videodev2.h */
#include <linux/videodev2.h>
#include <linux/fb.h>
/*********函数声明***************/
struct buffer{
void * start;
size_t length;
};
const char * dev_name = "/dev/video0";
const char * dev_name_fb = "/dev/fb0";
static struct fb_var_screeninfo vinfo;
static struct fb_fix_screeninfo finfo;
static char *fbp=NULL;
static long screensize=0;
static unsigned int n_buffers = 0;
struct buffer * buffers = NULL;
/*打开设备文件*/
int open_device(const char *dev_name);
/*关闭设备文件*/
void close_device(int fd);
/*设备初始化*/
void init_device(int fd);
/*Framebuffer:帧缓冲初始化*/
void init_fb(int fbfd);
/*开始采集*/
void start_capturing(int fd);
/*读帧缓存*/
int read_frame(int fd);
/*compare big or small*/
inline int clip(int value, int min, int max);
/*处理图像算法*/
void process_image(const void * p);
/*停止采集*/
void stop_capturing(int fd);
/*Munmap*/
void Munmap(void);
/*连续采集*/
void run(void);
/*生成一张图片(拍照)*/
void GeneratePictures(int fd,FILE *fp);
/*********函数声明 End***************/
/***************Main()**************/
int main(int argc, char const *argv[])
{
int fd = -1,fbfd = -1;
FILE * fp = fopen("./test.jpg","w");
fd = open_device(dev_name);
/*打开帧缓冲*/
fbfd = open_device(dev_name_fb);
/*初始化*/
init_device(fd);
init_fb(fbfd);
/*开始采集 -->处理数据*/
start_capturing(fd);
/*连续采集*/
run(int fd);
/*生成一张照片(拍照)*/
//GeneratePictures(fd,fp);
/*停止采集*/
stop_capturing(fd);
/*取消内存映射*/
Munmap();
/*关闭设备*/
close(fd);
close(fbfd);
exit(EXIT_SUCCESS); /*程序成功退出*/
return 0;
}
/***********Main() End**************/
/*开始采集*/
void start_capturing(int fd)
{
unsigned int i;
enum v4l2_buf_type type;
for (i = 0; i < n_buffers; ++i)
{
/*VIDIOC_QBUF:投放一个空的视频缓冲区到视频缓冲区输入队列中 struct v4l2_buffer*/
struct v4l2_buffer buf;
memset(&buf,0,sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if(-1 == ioctl (fd, VIDIOC_QBUF, &buf))
{
printf("VIDIOC_QBUF error\n");
exit(EXIT_FAILURE);
}
}
/*VIDIOC_STREAMON:启动视频采集命令*/
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == ioctl (fd, VIDIOC_STREAMON, &type))
{
printf("VIDIOC_STREAMON error\n");
exit(EXIT_FAILURE);
}
}
/*读帧*/
int read_frame(int fd)
{
struct v4l2_buffer buf;
unsigned int i;
memset(&buf,0,sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
/*读缓存*/
if(-1 == ioctl (fd, VIDIOC_DQBUF, &buf))
{
switch(errno)
{
case EAGAIN:
return 0;
case EIO: /* Could ignore EIO, see spec. */
/* fall through */
default:
printf("VIDIOC_DQBUF error\n");
exit (EXIT_FAILURE);
}
}
/*index:缓存编号*/
assert(buf.index < n_buffers); /*assert 相当于if else 报错程序*/
assert(buf.field ==V4L2_FIELD_NONE);
/*process_image:视频处理算法*/
process_image(buffers[buf.index].start);
/*再将其放入缓存队列*/
if(-1 == ioctl (fd, VIDIOC_QBUF, &buf))
{
printf("VIDIOC_QBUF error\n\n");
exit (EXIT_FAILURE);
}
return 1;
}
/*生成一张图片(拍照)*/
void GeneratePictures(int fd,FILE * fp)
{
struct v4l2_buffer buf;
unsigned int i;
memset(&buf,0,sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
/*读缓存*/
if(-1 == ioctl (fd, VIDIOC_DQBUF, &buf))
{
switch(errno)
{
case EAGAIN:
return 0;
case EIO: /* Could ignore EIO, see spec. */
/* fall through */
default:
printf("VIDIOC_DQBUF error\n");
exit (EXIT_FAILURE);
}
}
/*index:缓存编号*/
assert(buf.index < n_buffers); /*assert 相当于if else 报错程序*/
assert(buf.field ==V4L2_FIELD_NONE);
fwrite(buffers[buf.index].start,buffers[buf.index].length,1,fp);
printf("Generate Pictures Success.\n");
/*再将其放入缓存队列*/
if(-1 == ioctl (fd, VIDIOC_QBUF, &buf))
{
printf("VIDIOC_QBUF error\n\n");
exit (EXIT_FAILURE);
}
}
/*compare*/
inline int clip(int value, int min, int max)
{
return (value > max ? max : value < min ? min : value);
}
/*处理图像算法*/
void process_image(const void * p)
{
//ConvertYUVToRGB32
unsigned char* in=(char*)p;
int width=640;
int height=480;
int istride=1280;
int x,y,j;
int y0,u,y1,v,r,g,b;
long location=0;
for(y = 100; y < height + 100; ++y)
{
for(j = 0, x=100; j < width * 2 ; j += 4,x +=2)
{
location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +(y+vinfo.yoffset) * finfo.line_length;
y0 = in[j];
u = in[j + 1] - 128;
y1 = in[j + 2];
v = in[j + 3] - 128;
r = (298 * y0 + 409 * v + 128) >> 8;
g = (298 * y0 - 100 * u - 208 * v + 128) >> 8;
b = (298 * y0 + 516 * u + 128) >> 8;
fbp[ location + 0] = clip(b, 0, 255);
fbp[ location + 1] = clip(g, 0, 255);
fbp[ location + 2] = clip(r, 0, 255);
fbp[ location + 3] = 255;
r = (298 * y1 + 409 * v + 128) >> 8;
g = (298 * y1 - 100 * u - 208 * v + 128) >> 8;
b = (298 * y1 + 516 * u + 128) >> 8;
fbp[ location + 4] = clip(b, 0, 255);
fbp[ location + 5] = clip(g, 0, 255);
fbp[ location + 6] = clip(r, 0, 255);
fbp[ location + 7] = 255;
}
in +=istride;
}
}
/*run*/
void run(int fd)
{
int frames;
int time_in_sec_capture = 5;
frames = 60 * time_in_sec_capture;
while (frames-- > 0)
{
while(1)
{
fd_set fds;
struct timeval tv;
int r;
FD_ZERO (&fds);
FD_SET (fd, &fds);
/* Timeout. */
tv.tv_sec = 10;
tv.tv_usec = 0;
r = select (fd + 1, &fds, NULL, NULL, &tv);
if (-1 == r)
{
if (EINTR == errno)
{
continue;
}
errno_exit ("select");
}
if(0 == r){
fprintf (stderr, "select timeout\n");
exit (EXIT_FAILURE);
}
if (read_frame (fd))
break;
/* EAGAIN - continue select loop. */
}
}
}
/*停止采集*/
void stop_capturing(int fd)
{
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(-1 == ioctl (fd, VIDIOC_STREAMOFF, &type))
{
printf("VIDIOC_STREAMOFF error\n");
exit(EXIT_FAILURE);
}
}
/*Munmap*/
void Munmap(void)
{
unsigned int i;
for (i = 0; i < n_buffers; ++i)
{
if (-1 == munmap (buffers[i].start, buffers[i].length))
{
printf("Munmap error\n");
exit(EXIT_FAILURE);
}
}
if (-1 == munmap(fbp, screensize))
{
printf(" Error: framebuffer device munmap() failed.\n");
exit (EXIT_FAILURE) ;
}
free (buffers);
}
/*设备初始化*/
void init_device(int fd)
{
struct v4l2_capability cap; /*结构体变量中的返回当前视频设备所支持的功能*/
struct v4l2_cropcap cropcap; /*v4l2_cropcap 结构体用来设置摄像头的捕捉能力,在捕捉上视频时应先先设置*/
struct v4l2_crop crop; /*v4l2_crop 设置视频的采集窗口参数*/
/*设置视频设备的视频数据格式,例如设置视频图像数据的长、宽,图像格式( JPEG、 YUYV 格式)*/
struct v4l2_format fmt;
/*申请缓冲区数据结构体*/
struct v4l2_requestbuffers req;
/*VIDIOC_QUERYCAP:查询驱动能力*/
if (-1 == ioctl (fd, VIDIOC_QUERYCAP, &cap))
{
if (EINVAL == errno)
{
fprintf (stderr, "%s is no V4L2 device\n",dev_name);
exit (EXIT_FAILURE);
}
else
{
exit(EXIT_FAILURE);
}
}
/*capabilities:域 capabilities 代表设备支持的操作模式,
*常见的值有 V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
*表示是一个视频捕捉设备并且具有数据流控制模式
*/
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
fprintf (stderr, "%s is no video capture device\n",dev_name);
exit (EXIT_FAILURE);
}
if (!(cap.capabilities & V4L2_CAP_STREAMING))
{
fprintf (stderr, "%s does not support streaming i/o\n",dev_name);
exit (EXIT_FAILURE);
}
memset(&cropcap,0,sizeof(cropcap));
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 == ioctl (fd, VIDIOC_CROPCAP, &cropcap))
{
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect; /* reset to default */
if (-1 == ioctl (fd, VIDIOC_S_CROP, &crop))
{
/*printf("Set video acquisition window parameters error\n");
exit (EXIT_FAILURE);*/
switch (errno)
{
case EINVAL: /*Cropping not supported. */
break;
default:/* Errors ignored. */
break;
}
}
}
else{
printf("Set video acquisition window parameters error\n");
exit (EXIT_FAILURE);
}
/*设置视频设备的视频数据格式*/
memset(&fmt,0,sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
//fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if(-1 == ioctl(fd, VIDIOC_S_FMT, &fmt))
{
printf("VIDIOC_S_FMT error\n");
exit (EXIT_FAILURE);
}
/*申请缓冲区数据*/
memset(&req,0,sizeof(req));
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == ioctl (fd, VIDIOC_REQBUFS, &req))
{
if (EINVAL == errno)
{
fprintf (stderr, "%s does not support memory mapping\n", dev_name);
exit (EXIT_FAILURE);
}
else
{
printf("VIDIOC_REQBUFS error\n");
exit (EXIT_FAILURE);
}
}
if (req.count < 4)
{
fprintf (stderr, "Insufficient buffer memory on %s\n",dev_name);
exit (EXIT_FAILURE);
}
/*查询已经分配的 V4L2 的视频缓冲区的相关信息,包括视频缓冲区的使
*用状态、在内核空间的偏移地址、缓冲区长度等。 在应用程序设计中通过调
*VIDIOC_QUERYBUF 来获取内核空间的视频缓冲区信息,然后调用函数 mmap
*把内核空间地址映射到用户空间.
*/
buffers = calloc (req.count, sizeof (*buffers));
if (!buffers)
{
fprintf (stderr, "Out of memory\n");
exit (EXIT_FAILURE);
}
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, VIDIOC_QUERYBUF, &buf))
{
printf("VIDIOC_QUERYBUF error\n");
exit (EXIT_FAILURE);
}
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 (MAP_FAILED == buffers[n_buffers].start)
{
printf("mmap erro\n");
exit(EXIT_FAILURE);
}
}
}
/*Framebuffer:帧缓冲初始化*/
void init_fb(int fbfd)
{
//*Get fixed screen information*/
/*FBIOGET_FSCREENINFO:Framebuffer有关的固定的信息,比如图形硬件上实际的帧缓存空间的大小、能否硬件加速等信息*/
if (-1 == ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo))
{
printf("Error reading fixed information.\n");
exit(EXIT_FAILURE);
}
/*Get variable screen information*/
/*FBIOGET_VSCREENINFO:返回的是与Framebuffer有关的可变信息*/
if (-1 == ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo))
{
printf("Error reading variable information.\n");
exit(EXIT_FAILURE);
}
/*xres、yres、bits_per_pixel,分别表示x轴的分辨率、y轴的分辨率以及每像素的颜色深度*/
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
/*映射内存*/
fbp = (char *)mmap(NULL,screensize,PROT_READ | PROT_WRITE,MAP_SHARED ,fbfd,0);
if(fbp == NULL)
{
printf("Error: failed to map framebuffer device to memory.\n");
exit (EXIT_FAILURE);
}
memset(fbp,0,sizeof(screensize));
}
/*打开设备文件*/
int open_device(const char *dev_name)
{
int fd = open(dev_name,O_RDWR| O_NONBLOCK, 0);
if(-1 == fd)
{
fprintf(stderr, "Cannot open '%s'\n", dev_name);
exit (EXIT_FAILURE);
}
return fd;
}
/*关闭设备文件*/
void close_device(int fd)
{
if(-1 == close(fd))
{
fprintf(stderr, "close device error");
exit (EXIT_FAILURE);
}
}