linux下编写摄像头拍照程序

前几个月在忙项目像linux系统移植的事情,遇到了不少的问题,其中一个问题就是linux 下使用摄像头;

由于项目需要调用摄像头进行获取图像操作,java工程师也不知道在linux使用摄像头的接口,急需一个接口打通上层应用和底层驱动之间的问题。linux中摄像头驱动采用的是V4L2标准,所以可以查看内核的V4l2例子进行修改,修改后的程序如下:

#include "video.h"

//摄像头采集的YUYV格式转换为JPEG格式
int compress_yuyv_to_jpeg(unsigned char *buf, FILE *fp, int quality) {
	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr jerr;
	JSAMPROW row_pointer[1];
	unsigned char *yuyv;
	unsigned char line_buffer[1920];
	int z;
	static int written;
	unsigned char *ptr;
	int x;
	int r, g, b;
	int y, u, v;

	//int count = 0;
	//printf("%s\n", buf);
	//line_buffer = calloc (WIDTH * 3, 1);
	yuyv = buf;//将YUYV格式的图片数据赋给YUYV指针
	printf("compress start...\n");
	cinfo.err = jpeg_std_error (&jerr);
	jpeg_create_compress (&cinfo);
	/* jpeg_stdio_dest (&cinfo, file); */
	jpeg_stdio_dest(&cinfo, fp);
	if(debug1)	
		printf("compress start1 \n");
	//dest_buffer(&cinfo, buffer, size, &written);
	if(debug1)	
		printf("compress start2 \n");
	cinfo.image_width = WIDTH;
	cinfo.image_height = HEIGHT;
	cinfo.input_components = 3;
	cinfo.in_color_space = JCS_RGB;
	
	jpeg_set_defaults (&cinfo);
	jpeg_set_quality (&cinfo, quality, TRUE);
	jpeg_start_compress (&cinfo, TRUE);
	if(debug1)	
		printf("compress start3 \n");

	z = 0;
	while (cinfo.next_scanline < HEIGHT) {
		//int x;
		ptr= line_buffer;
		for (x = 0; x < WIDTH; x++) {
			r=g=b=0;
			y=u=v=0;
			if (!z)
				y = yuyv[0] << 8;
			else
				y = yuyv[2] << 8;
			u = yuyv[1] - 128;
			v = yuyv[3] - 128;
 
			r = (y + (359 * v)) >> 8;
			g = (y - (88 * u) - (183 * v)) >> 8;
			b = (y + (454 * u)) >> 8;
 
			*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
			*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
			*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
			if (z++) {
				z = 0;
				yuyv += 4;
			}
		}
		row_pointer[0] = line_buffer;
		jpeg_write_scanlines (&cinfo, row_pointer, 1);
	}
 if(debug1)	
		printf("compress start4 \n");


	jpeg_finish_compress (&cinfo);
	jpeg_destroy_compress (&cinfo);
	//free (line_buffer);

	return (written);
}
//读取一帧的内容
static int read_frame (int pixelformat)
{
	struct v4l2_buffer buf;
	int ret;
	unsigned int i;
	int ff;

	CLEAR (buf);
	if(debug1)	
		printf("read_frame1 \n");
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;
	ff = ioctl (fd, VIDIOC_DQBUF, &buf); //出列采集的帧缓冲
	if(ff<0)
		printf("failture\n");
	printf("read_frame2 \n");
	
	
	if(debug1)	
		printf("read_frame3 \n");
	if(0)
	{
		assert (buf.index < n_buffers);
		printf ("buf.index dq is %d,\n",buf.index);
	}
	if(pixelformat == V4L2_PIX_FMT_MJPEG)
	{	
		if(debug1)
			printf("read_frame4 \n");
		fwrite(buffers[buf.index].start,/*(WIDTH * HEIGHT)*/ buffers[buf.index].length, 1, file_fd);
	} 
	else{
		if(debug1)	
			printf("read_frame4 :%d\t\t%d\n",buffers[buf.index].length,buf.length);
		//memcpy(src, buffers[buf.index].start, buf.length);//buffers[buf.index].length);
		printf("read_frame5 \n");
		ret = compress_yuyv_to_jpeg(buffers[buf.index].start,file_fd,100);//数据转换
		if(debug1)	
			printf("read_frame6 \n");
		//fwrite(dest, ret, 1, file_fd);//转换后的数据写入
	}
	//重新入列
	if(debug1)	
		printf("read_frame7 \n");
	if(ioctl (fd, VIDIOC_QBUF, &buf)<0)
		printf("failture VIDIOC_QBUF\n");
	return 1;
}
int check_camera()
{	static int  dev_camera_fd = -1;
	dev_camera_fd = open (dev_name, O_RDWR | O_NONBLOCK, 0);
	if (dev_camera_fd == -1)
	{
		fprintf(stderr,"Can't open device %s",dev_name);
		return CAMERA_DEVICE_NOT_EXIST;
	}
	return CAMERA_DEVICE_EXIST;
	close(dev_camera_fd);	
} 
int get_picture(char *file_name,int pic_width,int pic_height)
//int main()
{
	struct v4l2_capability cap;
	struct v4l2_format fmt;
	unsigned int i;
	enum v4l2_buf_type type;
	struct v4l2_fmtdesc fmt1;
	struct v4l2_requestbuffers req;
	int ret;
 
	file_fd = fopen(file_name, "w");
	if(file_fd==NULL)
	{
		printf("can't open file");
		return -1;
	}
	fd = open (dev_name, O_RDWR | O_NONBLOCK, 0);
	if (fd == -1)
	{
		fprintf(stderr,"Can't open device %s",dev_name);
		return -1;
	}
	
	//获取摄像头参数
	if(ioctl(fd, VIDIOC_QUERYCAP, &cap)<0)
		printf("failture VIDIOC_QUERYCAP\n");
	if (debug){
		if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
			printf("Error opening device %s: video capture not supported.\n",dev_name);
		}
		if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
			printf("%s does not support streaming i/o\n", dev_name);
		}
		if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
			printf("%s does not support read i/o\n", dev_name);
		}
	}
	if (debug)
	{
		printf("driver name	:%s\n",cap.driver);
		printf("card name	:%s\n",cap.card);
		printf("bus info	:%s\n",cap.bus_info);
		printf("driver version	:%u.%u.%u\n",(cap.version>>16)&0xff,(cap.version>>8)&0xff,(cap.version)&0xff);
		printf("video capability:%u.%u.%u\n",(cap.capabilities>>16)&0xff,(cap.capabilities>>8)&0xff,(cap.capabilities)&0xff);
	}

	memset(&fmt1, 0, sizeof(fmt1));
	fmt1.index = 0;
	fmt1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (debug)
	{
		while ((ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmt1)) == 0) //查看摄像头所支持的格式
		{
			fmt1.index++;
			printf("{ pixelformat = '%c%c%c%c', description = '%s' }\n",fmt1.pixelformat&0xFF,(fmt1.pixelformat>>8)&0xFF,(fmt1.pixelformat >>16)&0xFF,(fmt1.pixelformat>>24)&0xFF,fmt1.description);
		}
	}
	CLEAR (fmt);
	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmt.fmt.pix.width = WIDTH;
	fmt.fmt.pix.height = HEIGHT;
	fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//YUYV;//
	fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
 
	//设置图像格式
	if(ioctl (fd,VIDIOC_S_FMT,&fmt)<0)
	{
		fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//YUYV;
		if(ioctl (fd,VIDIOC_S_FMT,&fmt)<0)
			printf("failture VIDIOC_S_FMT to YUYV\n");
 	}
	file_length = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; //计算图片大小
	printf("width:%d\t\theight:%d\n",fmt.fmt.pix.width,fmt.fmt.pix.height);
	CLEAR (req);
	req.count = 1;
	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	req.memory = V4L2_MEMORY_MMAP;
 
	//申请缓冲,count是申请的数量
	if(ioctl(fd, VIDIOC_REQBUFS, &req)<0)
		printf("failture VIDIOC_REQBUFS\n");
	if (req.count < 1)
	printf("Insufficient buffer memory\n");
	buffers = calloc (req.count, sizeof (*buffers));//内存中建立对应空间
	for (n_buffers = 0; n_buffers < req.count; ++n_buffers)
	{
		struct v4l2_buffer buf; 
		CLEAR (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");
		buffers[n_buffers].length = buf.length;
		buffers[n_buffers].start=mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset); //通过mmap建立映射关系
		if (MAP_FAILED == buffers[n_buffers].start)
			printf ("mmap failed\n");
	}
 
	for (i = 0; i < n_buffers; ++i)
	{
		struct v4l2_buffer buf;
		CLEAR (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 failed\n");
	}
	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if (-1 == ioctl (fd, VIDIOC_STREAMON, &type)) //开始捕捉图像数据
		printf ("VIDIOC_STREAMON failed\n");
 printf("file name :%s\n",file_name);

	for (;;) //这一段涉及到异步IO
	{
		fd_set fds;
		struct timeval tv;
		int r;
		FD_ZERO (&fds);//将指定的文件描述符集清空
		FD_SET (fd, &fds);//在文件描述符集合中增加一个新的文件描述符
		/* Timeout. */
		tv.tv_sec = 2;
		tv.tv_usec = 0;
		r = select (fd+1, &fds, NULL, NULL, &tv);//判断是否可读(即摄像头是否准备好),tv是定时
		if (-1 == r)
		{
			if (EINTR == errno)
				continue;
			printf ("select err\n");
		}
		if (0 == r)
		{
			fprintf (stderr, "select timeout\n");
			exit (EXIT_FAILURE);
		}
		if (debug1)
			printf("run read_frame\n");		
		if (read_frame (fmt.fmt.pix.pixelformat))//如果可读,执行read_frame函数
			break;
	}

	unmap:
		for (i = 0; i < n_buffers; ++i)
			if (-1 == munmap (buffers[i].start, buffers[i].length))
				printf ("munmap error");
		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
		if (-1 == ioctl(fd, VIDIOC_STREAMOFF, &type))   
			printf("VIDIOC_STREAMOFF"); 
		close (fd);
		fclose (file_fd);
	//	exit (EXIT_SUCCESS);	//这里会直接退出程序
		//free (buffers);
		return 0;
}
/*
int main()
{
	unsigned char *file_name="2015.jpg";
	get_picture(file_name,WIDTH,HEIGHT);
}
*/


你可能感兴趣的:(个人回顾)