Linux基于v4l2的视频采集(含代码)

Video4linux2(简称V4L2),linux中关于视频设备的内核驱动。

V4L2V4L有较大的改动,并已成为2.6的标准接口,函盖video\dvb\FM...,多数驱动都在向V4l2迁移。更好地了解V4L2先从应用入手,然后再深入到内核中结合物理设备/接口的规范实现相应的驱动。V4L2采用流水线的方式,操作更简单直观,基本遵循打开视频设备、设置格式、处理数据、关闭设备,更多的具体操作通过ioctl函数来实现。

Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0

操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。

v4l2一共有三种视频采集方式:使用readwrite方式;内存映射方式和用户指针模式。

readwrite方式,在用户空间和内核空间不断拷贝数据,占用了大量用户内存空间,效率不高。

内存映射方式:把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。

用户指针模式:内存片段由应用程序自己分配。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR

 

注意:

利用v4l2采集时,ARM上的采集卡支持3种模式,普通usb摄像头不支持直接读取模式;
采集卡支持V4L2_PIX_FMT_YUYV和V4L2_PIX_FMT_YUV420;

但是普通usb摄像头不支持V4L2_PIX_FMT_YUV420

 

captrue(直接读取方式):

代码如下:

#include <stdio.h>   
#include <stdlib.h>   
#include <string.h>   
#include <assert.h>   
#include <getopt.h>             /* getopt_long() */   
#include <fcntl.h>              /* low-level i/o */   
#include <unistd.h>   
#include <errno.h>   
#include <malloc.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>

#define CODEC_NODE  "/dev/video0"
#define LCD_WIDTH  320 
#define LCD_HEIGHT 240
#define YUV_FRAME_BUFFER_SIZE (LCD_WIDTH*LCD_HEIGHT)+(LCD_WIDTH*LCD_HEIGHT)/2  /* YCBCR 420 */

static int cam_c_init(void);
static void exit_from_app() ;
static int cam_c_fp = -1;

static void exit_from_app()
{
  close(cam_c_fp);
 
}

static int cam_c_init(void) 
{
 int dev_fp = -1;

 dev_fp = open(CODEC_NODE, O_RDWR);

 if (dev_fp < 0) {
  perror(CODEC_NODE);
  printf("CODEC : Open Failed \n");
  return -1;
 }
 return dev_fp;
}


int main()


 
  
     struct v4l2_capability cap;
     struct v4l2_format codec_fmt;
/* Camera codec initialization */
 if ((cam_c_fp = cam_c_init()) < 0)
  exit_from_app();
/* Codec set */
 /* Get capability */
 int ret = ioctl(cam_c_fp , VIDIOC_QUERYCAP, &cap);
 if (ret < 0) {
  printf("V4L2 : ioctl on VIDIOC_QUERYCAP failled\n");
  exit(1);
 }
 //printf("V4L2 : Name of the interface is %s\n", cap.driver);


 /* Check the type - preview(OVERLAY) */
 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
  printf("V4L2 : Can not capture(V4L2_CAP_VIDEO_CAPTURE is false)\n");
  exit(1);
 }

 /* Set format */
 codec_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 codec_fmt.fmt.pix.width = LCD_WIDTH;
 codec_fmt.fmt.pix.height = LCD_HEIGHT;  
 codec_fmt.fmt.pix.pixelformat=  V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YUV420;(The average captrue equiment can't support. )
 ret = ioctl(cam_c_fp , VIDIOC_S_FMT, &codec_fmt);
 if (ret < 0) {
  printf("V4L2 : ioctl on VIDIOC_S_FMT failled\n");
  exit(1);
 }
        int start=1;
        ret = ioctl(cam_c_fp, VIDIOC_STREAMON, &start);
 if (ret < 0) {
  printf("V4L2 : ioctl on VIDIOC_STREAMON failed\n");
  exit(1);
 }

        unsigned char yuv_buf[YUV_FRAME_BUFFER_SIZE];
        char YUV_name[100];
        static FILE *YUV_fp;
        sprintf(&YUV_name[0], "Cam%dx%d-%d.yuv", LCD_WIDTH, LCD_HEIGHT, 888);
        YUV_fp = fopen(&YUV_name[0], "wb");
   if (!YUV_fp) {
    perror(&YUV_name[0]);
   }

      


        int i=0;
        int framecount=200;
           for(i=0;i<framecount;i++)
         {
             /* read YUV frame from camera device */
  if (read(cam_c_fp, yuv_buf, YUV_FRAME_BUFFER_SIZE) < 0)
               {
   perror("read()");
  }
              
              //write down YUV
             fwrite(yuv_buf, 1, YUV_FRAME_BUFFER_SIZE, YUV_fp);

                
          }///for

     

     
        ret = ioctl(cam_c_fp, VIDIOC_STREAMOFF, &start);
 if (ret < 0)
         {
  printf("V4L2 : ioctl on VIDIOC_STREAMOFF failed\n");
  exit(1);
  }
     
        fclose(YUV_fp);
      
        exit_from_app();
 
}

 

 captrue(内存映射方式):

代码如下

captrue_mmap.h

#include <stdio.h>   
#include <stdlib.h>   
#include <string.h>   
#include <assert.h>   
#include <getopt.h>             /* getopt_long() */   
#include <fcntl.h>              /* low-level i/o */   
#include <unistd.h>   
#include <errno.h>   
#include <malloc.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>   
#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; //buffer's length is different from cap_image_size   
};  
static   char  * dev_name = NULL;  
static  io_method io = IO_METHOD_MMAP; //IO_METHOD_READ;//IO_METHOD_MMAP;   
static   int  fd = -1;  
struct  buffer * buffers = NULL;  
static  unsigned  int  n_buffers = 0;  
static   FILE  * outf = 0;  
static  unsigned  int  cap_image_size = 0; //to keep the real image size!! 

static   void  open_device( void );
static   void  init_device( void );
static   void  init_mmap( void ) ;
static   void  start_capturing( void );
static   void  mainloop( void );
static   int  read_frame( void );
static   void  process_image( const   void  * p,  int  len);
static   void  errno_exit( const   char  * s);
static   int  xioctl( int  fd,  int  request,  void  * arg);
static   void  stop_capturing( void );
static   void  uninit_device( void );
static   void  close_device( void );

 

captrue_mmap.c

 

#include "captrue_mmap.h"
static   void  open_device( void ) {  
    struct  stat st;  
    if  (-1 == stat(dev_name, &st)) {  
        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);  
    if  (-1 == fd) {  
        fprintf(stderr, "Cannot open '%s': %d, %s\n" , dev_name, errno,  
                strerror(errno));  
        exit(EXIT_FAILURE);  
    }  
}

static   void  init_device( void ) {  
    struct  v4l2_capability cap;  
    struct  v4l2_cropcap cropcap;  
    struct  v4l2_crop crop;  
    struct  v4l2_format fmt;  
    unsigned int  min;  
    if  (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap))
   {  
        if  (EINVAL == errno)
       {  
            fprintf(stderr, "%s is no V4L2 device\n" , dev_name);  
            exit(EXIT_FAILURE);  
        }
        else
       {  
            errno_exit("VIDIOC_QUERYCAP" );  
        }  
    }  
    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);  
    }  
      
    //////not all capture support crop!!!!!!!   
    /* Select video input, video standard and tune here. */   
    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );  
    CLEAR (cropcap);  
    cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
    if  (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {  
        crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
#ifndef CROP_BY_JACK   
        crop.c = cropcap.defrect; /* reset to default */   
#else   
        crop.c.left = cropcap.defrect.left;  
        crop.c.top = cropcap.defrect.top;  
        crop.c.width = 352;  
        crop.c.height = 288;  
#endif   
        printf("----->has ability to crop!!\n" );  
        printf("cropcap.defrect = (%d, %d, %d, %d)\n" , cropcap.defrect.left,  
                cropcap.defrect.top, cropcap.defrect.width,  
                cropcap.defrect.height);  
        if  (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {  
            switch  (errno) {  
            case  EINVAL:  
                /* Cropping not supported. */   
                break ;  
            default :  
                /* Errors ignored. */   
                break ;  
            }  
            printf("-----!!but crop to (%d, %d, %d, %d) Failed!!\n" ,  
                    crop.c.left, crop.c.top, crop.c.width, crop.c.height);  
        } else  {  
            printf("----->sussess crop to (%d, %d, %d, %d)\n" , crop.c.left,  
                    crop.c.top, crop.c.width, crop.c.height);  
        }  
    } else  {  
        /* Errors ignored. */   
        printf("!! has no ability to crop!!\n" );  
    }  
    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );  
    printf("/n" );  
    ////////////crop finished!   
    //////////set the format   
    CLEAR (fmt);  
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
    fmt.fmt.pix.width = 640;  
    fmt.fmt.pix.height = 480;  
    //V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUV420 鈥?Planar formats with 1/2 horizontal and vertical chroma resolution, also known as YUV 4:2:0   
    //V4L2_PIX_FMT_YUYV 鈥?Packed format with 1/2 horizontal chroma resolution, also known as YUV 4:2:2   
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YUV420;//V4L2_PIX_FMT_YUYV;   
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;  
    {  
        printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );  
        printf("=====will set fmt to (%d, %d)--" , fmt.fmt.pix.width,  
                fmt.fmt.pix.height);  
        if  (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {  
            printf("V4L2_PIX_FMT_YUYV\n" );  
        } else   if  (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {  
            printf("V4L2_PIX_FMT_YUV420\n" );  
        } else   if  (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) {  
            printf("V4L2_PIX_FMT_NV12\n" );  
        }  
    }  
    if  (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))  
        errno_exit("VIDIOC_S_FMT" );  
    {  
        printf("=====after set fmt\n" );  
        printf("    fmt.fmt.pix.width = %d\n" , fmt.fmt.pix.width);  
        printf("    fmt.fmt.pix.height = %d\n" , fmt.fmt.pix.height);  
        printf("    fmt.fmt.pix.sizeimage = %d\n" , fmt.fmt.pix.sizeimage);  
        cap_image_size = fmt.fmt.pix.sizeimage;  
        printf("    fmt.fmt.pix.bytesperline = %d\n" , fmt.fmt.pix.bytesperline);  
        printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );  
        printf("/n" );  
    }  
    cap_image_size = fmt.fmt.pix.sizeimage;  
    /* Note VIDIOC_S_FMT may change width and height. */   
    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );  
    /* 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;  
    printf("After Buggy driver paranoia\n" );  
    printf("    >>fmt.fmt.pix.sizeimage = %d\n" , fmt.fmt.pix.sizeimage);  
    printf("    >>fmt.fmt.pix.bytesperline = %d\n" , fmt.fmt.pix.bytesperline);  
    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );  
    printf("\n" );  
    
   
    init_mmap();  
      
}

static   void  init_mmap( void )
{  
    struct  v4l2_requestbuffers req;  
    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));  
    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;  
        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))  
            errno_exit("VIDIOC_QUERYBUF" );  
        buffers[n_buffers].length = buf.length;  
        buffers[n_buffers].start = mmap(NULL /* start anywhere */ , 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  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))  
                errno_exit("VIDIOC_QBUF" );  
        }  
     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
     if  (-1 == xioctl(fd, VIDIOC_STREAMON, &type))  
            errno_exit("VIDIOC_STREAMON" );  
      

static   void  mainloop( void )
 {  
    unsigned int  count;  
    count = 1000;  
    while  (count-- > 0)
    {  
        for  (;;)
        {  
          
            if  (read_frame())  
                break ;  
            /* EAGAIN - continue select loop. */   
        }  
    }  
}   

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)) {  
            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);  
        //      printf("length = %d/r", buffers[buf.index].length);   
        //      process_image(buffers[buf.index].start, buffers[buf.index].length);   
        printf("image_size = %d,\t IO_METHOD_MMAP buffer.length=%d\r" ,  
                cap_image_size, buffers[0].length);  
        process_image(buffers[0].start, cap_image_size);  
        if  (-1 == xioctl(fd, VIDIOC_QBUF, &buf))  
            errno_exit("VIDIOC_QBUF" );  
     
      
   
    return  1;  
}  
static   void  process_image( const   void  * p,  int  len) {  
    //  static char[115200] Outbuff ;   
    fputc('.' , stdout);  
    if  (len > 0) {  
        fputc('.' , stdout);  
        fwrite(p, 1, len, outf);  
    }  
    fflush(stdout);  
}
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  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  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  close_device( void ) {  
    if  (-1 == close(fd))  
        errno_exit("close" );  
    fd = -1;  
}  
int main()
{
    dev_name = "/dev/video0" ;  
    outf = fopen("out.yuv" ,  "wb" );  
    open_device();  
    init_device();  
    start_capturing();  
    mainloop();  
    printf("\n" );  
    stop_capturing();  
    fclose(outf);  
    uninit_device();  
    close_device();  
    exit(EXIT_SUCCESS);  
    return  0;  

}
代码参考官方代码;

 

 

 

 

你可能感兴趣的:(Linux基于v4l2的视频采集(含代码))