zoukankan      html  css  js  c++  java
  • V4L2编程模型简介(一)

    V4L2编程模型简介(一)

    作者:邹南,华清远见嵌入式学院讲师。

    简介:本文所附代码是根据v4l2官方文档以及demo(capture.c)修改而来,纯粹为学习交流之用,请勿使用在商用场合。

    地址:由于官方网的域名有敏感词汇,所以请google一下。

    一 、操作流程简单看

    二、 模块概要分析

    以下是所附代码所涉及到的全局变量,摆出来只是参考,具体修改的话请自行安排。

    #define CLEAR(x) memset (&(x), 0, sizeof (x))
            typedef enum {

    #ifdef IO_READ
                    IO_METHOD_READ,
            #endif
            #ifdef IO_MMAP
                    IO_METHOD_MMAP,
            #endif
            #ifdef IO_USERPTR
                    IO_METHOD_USERPTR,
            #endif
                    } io_method;

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

    static io_method io = IO_METHOD_MMAP;
            static int fd = -1;
            struct buffer * buffers = NULL;
            static unsigned int n_buffers = 0;
            // global settings
            static unsigned int width = 640;
            static unsigned int height = 480;
            static unsigned char jpegQuality = 70;
            static char* jpegFilename = NULL;
            static char* deviceName = "/dev/video0";

    1.deviceOpen

    主要就是打开你的设备文件,一般情况下就是,/dev/vedio0 取决于你的设备数量。前面提到的stat这个结构体主要是记录了文件的基本信息。通过这一点来校验文件的打开权限。

    2.deviceInit

    这个模块稍微复杂些,它主要是使用了v4l2中定义的4种相关的数据结构。以下列出每种结构的具体属性。
            struct v4l2_cropcap {
                    enum v4l2_buf_type type;
                    struct v4l2_rect bounds;
                    struct v4l2_rect defrect;
                    struct v4l2_fract pixelaspect; 
            };
            struct v4l2_crop {
                     enum v4l2_buf_type type;
                    struct v4l2_rect c;
            };
            struct v4l2_capability {
                     __u8 driver[16]; /* i.e. "bttv" */
                    __u8 card[32]; /* i.e. "Hauppauge WinTV" */
                    __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */
                    __u32 version; /* should use KERNEL_VERSION() */
                    __u32 capabilities; /* Device capabilities */
                    __u32 reserved[4];
            };
            struct v4l2_format {
                    enum v4l2_buf_type type;
                    union {
                            struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
                            struct v4l2_window win; /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
                            struct v4l2_vbi_format vbi; /* V4L2_BUF_TYPE_VBI_CAPTURE */
                            struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
                             __u8 raw_data[200]; /* user-defined */
                    } fmt;
            };

    这里不得不提醒一点,通常usb摄像头驱动,都会提供3种不同的数据传输方式,1,read IO 2,mmap内存映射 3,USERPTR(这一种是测试方法,具体可以去查询)

    本文暂且只讨论常见的操作方法,即mmap内存映射方式.

    通过一段时间的学习,才知道为什么只支持mmap,其实是我们所用的去架构是基于uvc.在uvc架构中,是不支持read/write io mode 以及用户扩展模式。
            static void deviceInit(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)) { //get the capab info about
                    if (EINVAL == errno) {
                    fprintf(stderr, "%s is no V4L2 device ",deviceName);
                    exit(EXIT_FAILURE);
                    } else {
                    errno_exit("VIDIOC_QUERYCAP");
                    }
            }
            if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { //check is it support capture mode ?
                    fprintf(stderr, "%s is no video capture device ",deviceName);
                    exit(EXIT_FAILURE);
            }
            if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
                    fprintf(stderr, "%s does not support streaming i/o ",deviceName);
                    exit(EXIT_FAILURE);
            }
            /* Select video input, video standard and tune here. */
            CLEAR(cropcap);// init -0 it is a initialize func about set 0 to parameter 
            cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
                    crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                    crop.c = cropcap.defrect; /* reset to default */
                     if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
                            switch (errno) {
                            case EINVAL:
                            /* Cropping not supported. */
                            break;
                            default:
                             /* Errors ignored. */
                            break;
                            }
                    }
            } 
            CLEAR (fmt); 
            // v4l2_format
            fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //mode is capture 
            fmt.fmt.pix.width = width; //define pixee width
            fmt.fmt.pix.height = height; //define pixel height
            fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; //define pixel format
            fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 
            if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) //set fmt 
            errno_exit("VIDIOC_S_FMT");
            /* Note VIDIOC_S_FMT may change width and height. */
             if (width != fmt.fmt.pix.width) {
                    width = fmt.fmt.pix.width;
                    fprintf(stderr,"Image width set to %i by device %s. ",width,deviceName);
                    }
            if (height != fmt.fmt.pix.height) {
                    height = fmt.fmt.pix.height;
                    fprintf(stderr,"Image height set to %i by device %s. ",height,deviceName);
                    }
            /* 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;
            //this function is important to init mmap pre_work 
            mmapInit();
            }

    可以看到上面主要是初始化工作,具体的参数意义,请参看v4l2的specification 。

    static void mmapInit(void)
            {
                    struct v4l2_requestbuffers req;//apply for frame buffer
                    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 ", deviceName);
                                    exit(EXIT_FAILURE);
                            } else {
                    errno_exit("VIDIOC_REQBUFS");
                    }
            }
            if (req.count < 2) {
                    fprintf(stderr, "Insufficient buffer memory on %s ", deviceName);
                    exit(EXIT_FAILURE);
             }
             buffers = calloc(req.count, sizeof(*buffers));
            if (!buffers) {
                    fprintf(stderr, "Out of memory ");
                    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");
                    }
            }

    3.capture_start

    初始化以后就可以进行正题了,就是所谓的capture data.不过在此之前,应该打开数据流通道,重点在于最后那个ioctl函数的参数:VIDIOC_STREAMON

    static void captureStart(void) //grap after initialize
            {
                    unsigned int i;
                    enum v4l2_buf_type type; //page-68
                    #ifdef IO_MMAP
                    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");
            #endif

    上面出现的两个结构体的分别定义如下:
            enum v4l2_buf_type {
            V4L2_BUF_TYPE_VIDEO_CAPTURE = 1,
            V4L2_BUF_TYPE_VIDEO_OUTPUT = 2,
            V4L2_BUF_TYPE_VIDEO_OVERLAY = 3,
            V4L2_BUF_TYPE_VBI_CAPTURE = 4,
            V4L2_BUF_TYPE_VBI_OUTPUT = 5,
            V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6,
            V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7,
            #if 1
            /* Experimental */
            V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
            #endif
            V4L2_BUF_TYPE_PRIVATE = 0x80,
            };
             struct v4l2_buffer {
                     __u32 index;
                     enum v4l2_buf_type type;
                     __u32 bytesused;
                     __u32 flags;
                    enum v4l2_field field;
                    struct timeval timestamp;
                    struct v4l2_timecode timecode;
                    __u32 sequence;
                    /* memory location */
                    enum v4l2_memory memory;
                    union {
                            __u32 offset;
                            unsigned long userptr;
                     } m;
                    __u32 length;
                    __u32 input;
                    __u32 reserved;
            };

  • 相关阅读:
    攻防世界web新手区前六关
    JS-数组基础知识3
    CSRF攻击的原理和spring security对CSRF攻击的解决方法
    Java开发微信公众号
    内部类
    Java Web整合开发(30) -- Spring的ORM模块
    win10安装mysql
    jquery 事件冒泡的介绍以及如何阻止事件冒泡
    jquery中attr和prop的区别介绍
    jQuery 层次选择器
  • 原文地址:https://www.cnblogs.com/number10/p/3931146.html
Copyright © 2011-2022 走看看