linux视频驱动接口V4L2视频采集程序完整版

继平台驱动模型之后的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);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值