前几个月在忙项目像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);
}
*/