Chinaunix首页 | 论坛 | 博客
  • 博客访问: 447514
  • 博文数量: 98
  • 博客积分: 3265
  • 博客等级: 中校
  • 技术积分: 1227
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-23 00:29
文章分类

全部博文(98)

文章存档

2012年(6)

2011年(83)

2010年(9)

分类: LINUX

2012-06-19 10:24:16

利用linux内核提供的图像采集接口,可以方便的实现图像的采集,这里采用中星微摄像头ZC0303来实现USB摄像头图像的采集。该程序在V4L2 demo的基础上修改而成。
中星微摄像头ZC0303:单帧获取格式为:V4L2_PIX_FMT_JPEG;
现在常用的YUV格式的usb摄像头,单帧获取格式为:V4L2_PIX_FMT_YUYV;



视频设备的一般操作流程如下:

打开设备文件。 int fd=open(/dev/video0,O_RDWR)

取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。命令VIDIOC_QUERYCAP,结构体struct v4l2_capability

设置视频的帧格式,帧的格式个包括宽度和高度等。命令VIDIOC_S_FMT,结构体struct v4l2_format

向驱动申请帧缓冲,一般不超过5个。结构体struct v4l2_requestbuffers

将申请到的帧缓冲映射到用户空间,直接操作采集到的帧。

开始视频的采集。命令VIDIOC_STREAMON

出队列以取得已采集数据的帧缓冲,取得原始采集数据。命令VIDIOC_DQBUF

将缓冲重新入队列尾,这样可以循环采集。命令VIDIOC_QBUF

停止视频的采集,命令VIDIOC_STREAMOFF

关闭视频设备,close(fd)

/*
 *  V4L2 video capture example
 *
 *  This program can be used and distributed without restrictions.
 */

#include
#include
#include
#include

#include

#include             /* getopt_long() */

#include              /* low-level i/o */
#include
#include
#include
#include
#include
#include
#include
#include

#include          /* for videodev2.h */

#include

#define CLEAR(x) memset (&(x), 0, sizeof (x))

typedef enum 
{
IO_METHOD_READ,
IO_METHOD_MMAP,
IO_METHOD_USERPTR,
} io_method;

struct buffer 
{
        void *                  start;
        size_t                  length;
};

static char *   dev_name   = NULL;
static io_method io = IO_METHOD_MMAP;
static int              fd          = -1;
struct buffer *   buffers    = NULL;
static unsigned int     n_buffers  = 0;
FILE *file_fd;


//static int nn=1;


static void errno_exit(const char *s)
{
        fprintf (stderr, "%s error %d, %s\n", s, errno, strerror (errno));

        exit (EXIT_FAILURE);
}

static int xioctl(int fd, int request, void *arg)
{
        int r;

        do r = ioctl (fd, request, arg);
        while (-1 == r && EINTR == errno);

        return r;
}

static void process_image(const void *p)
{
//        fputc ('.', stdout);
//        fflush (stdout);
// struct v4l2_buffer buf;
// fwrite(p->start, p->length, 1, file_fd); //œ«ÆäDŽèëÎÄŒtÖD
}

static int read_frame(void)
{
    struct v4l2_buffer buf;
unsigned int i;

CLEAR (buf);

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;

    if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) //3ö¶óáDòÔè¡μÃòÑ2éŒˉêyŸYμÄÖ¡»o3壬è¡μÃÔ-êŒ2éŒˉêyŸY
{
            switch (errno) 
{
            case EAGAIN:
                    return 0;
case EIO:
/* Could ignore EIO, see spec. */
/* fall through */
default:
errno_exit ("VIDIOC_DQBUF");
}
}

        assert (buf.index < n_buffers);

//   process_image (buffers[buf.index].start);
//   process_image (&buffers[buf.index]);
fwrite(buffers[buf.index].start, buffers[buf.index].length, 1, file_fd);//œ«ÆäDŽèëÎÄŒtÖD
if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) //œ«»o3åÖØDÂèë¶óáDÎ2,ÕaÑù¿éòÔÑ-»·2éŒˉ
errno_exit ("VIDIOC_QBUF");
return 1;
}

static void mainloop(void)
{
unsigned int count;

        count = 100;

        while (count-- > 0) 
{
                for (;;) 
{
                        fd_set fds;
                        struct timeval tv;
                        int r;

                        FD_ZERO (&fds); //œ«Öž¶šμÄÎÄŒtÃèêö·ûŒˉÇå¿Õ
                        FD_SET (fd, &fds); //ÔúÎÄŒtÃèêö·ûŒˉoÏÖDÔöŒóò»žöDÂμÄÎÄŒtÃèêö·û

                        /* Timeout. */
                        tv.tv_sec = 2; //¶šê±2sÕû
                        tv.tv_usec = 0; //΢Ãë

                        r = select (fd + 1, &fds, NULL, NULL, &tv); //ÅD¶ÏêÇ·ñ¿é¶á£šŒŽéãÏñí·êÇ·ñ׌±žoã©£¬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 ())
{
// printf("%d\n",nn++);
                    break;
}
/* EAGAIN - continue select loop. */
                }
        }
}

static void stop_capturing(void)
{
    enum v4l2_buf_type type;

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))
errno_exit ("VIDIOC_STREAMOFF");
}

static void start_capturing(void)
{
        unsigned int i;
        enum v4l2_buf_type type;

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 == xioctl (fd, VIDIOC_QBUF, &buf)) //œ«éêÇëμœμÄÖ¡»o3åè«2¿èë¶óáD£¬òÔ±ãŽæ·Å2éŒˉμœμÄêyŸY
                    errno_exit ("VIDIOC_QBUF");
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
errno_exit ("VIDIOC_STREAMON");
}

static void uninit_device(void)
{
    unsigned int i;

for (i = 0; i < n_buffers; ++i)
if (-1 == munmap (buffers[i].start, buffers[i].length))
errno_exit ("munmap");
free (buffers);
}

static void init_mmap(void)
{
struct v4l2_requestbuffers req;//ÏòÇy¶ˉéêÇëÖ¡»o3åμÄÇëÇó£¬àïÃæ°üo¬éêÇëμÄžöêy

   CLEAR (req);

   req.count               = 4;
   req.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   req.memory              = V4L2_MEMORY_MMAP;

if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) 
{
               if (EINVAL == errno) 
{
                       fprintf (stderr, "%s does not support ""memory mapping\n", dev_name);
                       exit (EXIT_FAILURE);
               } 
else 
{
                       errno_exit ("VIDIOC_REQBUFS");
               }
    }

        if (req.count < 2) 
{
                fprintf (stderr, "Insufficient buffer memory on %s\n",
                         dev_name);
                exit (EXIT_FAILURE);
        }

        buffers = calloc (req.count, sizeof (*buffers)); //žùŸYéêÇëμœμÄÖ¡»oŽæžöêyàŽ·ÖÅäóû§¿ÕŒäμÄÄúŽæ

        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; //Žú±íÇy¶ˉÖDμÄò»Ö¡

                CLEAR (buf);

                buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory      = V4L2_MEMORY_MMAP;
                buf.index       = n_buffers; //μúŒžÖ¡

                if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf)) //°ÑVIDIOC_REQBUFSÖD·ÖÅäμÄêyŸY»oŽæ×a»»3éÎïàíμØÖ·
                        errno_exit ("VIDIOC_QUERYBUF");

                buffers[n_buffers].length = buf.length;
                buffers[n_buffers].start =
                        mmap (NULL /* start anywhere */, //°Ñ·ÖÅäμœóû§¿ÕŒäμÄÄúŽæó3éäμœÄúoË¿ÕŒäμÄÖ¡»oŽæ
                              buf.length,
                              PROT_READ | PROT_WRITE /* required */,
                              MAP_SHARED /* recommended */,
                              fd, buf.m.offset);

                if (MAP_FAILED == buffers[n_buffers].start)
                        errno_exit ("mmap");
        }
}

static void init_device(void)
{
        struct v4l2_capability cap; //»ñè¡êóÆμé豞μÄêôDÔ
        struct v4l2_cropcap cropcap; //éèÖÃá÷μÄêôDÔ
        struct v4l2_crop crop; //á÷μÄêôDÔ
        struct v4l2_format fmt; //Ö¡μÄžñꜣ¬±èèç¿í¶è£¬žß¶è
        struct v4l2_fmtdesc fmt1; //2éÑˉμ±Ç°éãÏñí·Ö¡2¶»ñμÄžñêœ
struct v4l2_rect rect;
unsigned int min;
unsigned int ret;
// rect.left=0;
// rect.top=0;
// rect.width=240;
// rect.height=240;
if (1)
{
        if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) //2éÑˉÇy¶ˉ1ŠÄü
{
                if (EINVAL == errno) 
{
                        fprintf (stderr, "%s is no V4L2 device\n", dev_name);//óDûóDV4L2é豞
                        exit (EXIT_FAILURE);
                } 
else 
{
                        errno_exit ("VIDIOC_QUERYCAP");
                }
        }

        if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) //êÇ2»êÇêóÆμ2¶»ñé豞
{
                fprintf (stderr, "%s is no video capture device\n", dev_name);
                exit (EXIT_FAILURE);
        }


if (!(cap.capabilities & V4L2_CAP_STREAMING)) //Çy¶ˉêÇ·ñÖ§3ÖÄúŽæó3éä·œêœ
{
fprintf (stderr, "%s does not support streaming i/o\n", dev_name);
exit (EXIT_FAILURE);
}

        /* Select video input, video standard and tune here. */


CLEAR (cropcap);

        cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        if (-1 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) //not surport for zc0301
{
fprintf (stderr, "VIDIOC_CROPCAP does not support! for zc0301\n");
/*
printf ("%d %d %d %d\n", cropcap.bounds.left,cropcap.bounds.top,cropcap.bounds.width,cropcap.bounds.height);
printf ("%d %d %d %d\n", cropcap.defrect.left,cropcap.defrect.top,cropcap.defrect.width,cropcap.defrect.height);
*/

                crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 //               crop.c = cropcap.defrect; /* reset to default */
//crop.c = cropcap.bounds;
// crop.c = rect;

crop.c.left = cropcap.defrect.left;   
        crop.c.top = cropcap.defrect.top;   
        crop.c.width = 240;   
        crop.c.height = 240;

                if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop))
{
                        switch (errno) 
{
                       case EINVAL:
                               /* Cropping not supported. */
fprintf (stderr, "VIDIOC_S_CROP does not support for zc0301\n");
                               break;
                       default:
                               /* Errors ignored. */
                               break;
                        }
                }
        } 
}





if(1) //excued faild //not surport for zc0301
{
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (-1 == xioctl (fd, VIDIOC_G_CROP, &crop)) 
{
// errno_exit ("VIDIOC_G_CROP");
// exit (EXIT_FAILURE);
fprintf (stderr, "VIDIOC_G_CROP does not support for zc0301\n");
}
else
{
printf ("%d %d %d %d %d\n", 
crop.type, crop.c.left, crop.c.top, crop.c.width, crop.c.height);
}
}









if(1)
{
struct v4l2_input input;

int index;

if (-1 == ioctl (fd, VIDIOC_G_INPUT, &index)) {

perror ("VIDIOC_G_INPUT");

exit (EXIT_FAILURE);

}

CLEAR (input);

input.index = index;

if (-1 == ioctl (fd, VIDIOC_ENUMINPUT, &input)) {

perror ("VIDIOC_ENUMINPUT");

exit (EXIT_FAILURE);

}

printf ("Current input name: %s\n", input.name);
// printf ("Current input index: %d\n", input.index);
printf ("Current input type: %d ==2 that's camera\n", input.type); //if 2,then V4L2_INPUT_TYPE_CAMERA
}



CLEAR (fmt1);
fmt1.index = 0;
fmt1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //êóÆμêäèëé豞
while ((ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmt1)) == 0) //»ñè¡μ±Ç°Çy¶ˉÖ§3ÖμÄêóÆμžñêœ
{
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);
printf("zc0301 surport %d only,that's jpeg!\n",fmt1.index);
}


        CLEAR (fmt);

        fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fmt.fmt.pix.width       = 320; 
        fmt.fmt.pix.height      = 240;
// 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_RGB24;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
        fmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;

        if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) //éèÖÃμ±Ç°Çy¶ˉμÄÆμ2¶»ñžñêœ
                errno_exit ("VIDIOC_S_FMT");

//printf("v4l2_buf_type(CAPTURE=1)    :%d\n",fmt.type); /* CAPTURE=1*/
printf("width              :%d\n",fmt.fmt.pix.width); 
printf("height             :%d\n",fmt.fmt.pix.height);
printf("zc0301 surport only 320*240 and 640*480!\n");

//printf("pixel'yuyv'             :%d\n",('Y'|'U'<<8|'Y'<<16|'V'<<24));
//printf("pixel'jpeg'         :%d\n",('J'|'P'<<8|'E'<<16|'G'<<24));
//printf("pixelformat              :%d\n",fmt.fmt.pix.pixelformat);
//printf("v4l1_filed(FIELD_INTERCACED=4) :%d\n",fmt.fmt.pix.field);
//printf("bytesperline         :%d\n",fmt.fmt.pix.bytesperline);
//printf("sizeimage         :%d\n",fmt.fmt.pix.sizeimage);
//printf("colorspace(COLOSPACE_JPEG=7)    :%d\n",fmt.fmt.pix.colorspace);
//printf("priv             :%d\n",fmt.fmt.pix.priv);

        /* Note VIDIOC_S_FMT may change width and height. */

/* Buggy driver paranoia. */
min = fmt.fmt.pix.width * 2;
if (fmt.fmt.pix.bytesperline < min)
fmt.fmt.pix.bytesperline = min;
min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
if (fmt.fmt.pix.sizeimage < min)
fmt.fmt.pix.sizeimage = min;

init_mmap ();
}

static void close_device(void)
{
        if (-1 == close (fd))
       errno_exit ("close");

        fd = -1;
}

static void open_device(void)
{
        struct stat st; 

        if (-1 == stat (dev_name, &st)) //»ñè¡ÎÄŒtÃèêö·ûμÄêôDÔ
{
                fprintf (stderr, "Cannot identify '%s': %d, %s\n", dev_name, errno, strerror (errno));
                exit (EXIT_FAILURE);
        }

        if (!S_ISCHR (st.st_mode)) 
{
                fprintf (stderr, "%s is no device\n", dev_name);
                exit (EXIT_FAILURE);
        }

        fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
file_fd = fopen("test-mmap.jpg", "w"); //íŒÆ¬ÎÄŒtÃû

        if (-1 == fd) 
{
                fprintf (stderr, "Cannot open '%s': %d, %s\n", dev_name, errno, strerror (errno));
                exit (EXIT_FAILURE);
        }
}

int main(int argc,char **argv)
{
        dev_name = "/dev/video0";
       
io = IO_METHOD_MMAP;
        open_device ();

        init_device ();

        start_capturing ();
// clock_t start,end;
// start = clock();

        mainloop ();

// end = clock();
// printf("%lu-%lu=%lu\n",end,start,(end-start));

        stop_capturing ();

        uninit_device ();

        close_device ();

        exit (EXIT_SUCCESS);

        return 0;
}


阅读(6370) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~