zoukankan      html  css  js  c++  java
  • V4L2开发要点

    首先来看 Read/Write ,如果 VIDIOC_QUERYCAP 调用返回的 v4l2_capability 参数中, V4L2_CAP_READWRITE 被设置成真了的话,就说明支持 Read/Write I/O 。这是最简单最原始的方法,它需要进行数据 的拷贝 ( 而不是像memory map 那样只需要进行指针的交换 ) ,而且不会交换元数据 ( 比如说帧计数器和时间戳之类的可用于识别帧丢失和进行帧同步 ) ,虽然它是最原始的方法,但因为其简单,所以对于简单的应用 程序比如只需要 capture静态图像是很有用的 。

    如果使用 Read/Write 方法支持的话,必须同时支持另外两个函数 select()  poll() ,这两个函数用来进行 I/0 的多路复用。

    对于 streaming 它有两种方式, driver 对两种方式的支持要使用 VIDIOC_REQBUFS 来确定:

    int ioctl(int fd, int request, struct v4l2_requestbuffers *argp);

    对于 memory mapped 方式, Memory mapped buffers 是通过 VIDIOC_REQBUFS  device memory 中申请的,而且必须在 map 进应用程序虚拟地址空间 之前就申请好。而对于 User pointers  User buffers 是在应用程序自己开辟的,只是通过 VIDIOC_REQBUFS 将驱动转化到 user pointer  I/O 模式下。这两种方式都不会拷贝数据,而只是 buffer 指针的交互。

    首先来看一下 v4l2_requestbuffers 这个数据结构:

    __u32 count

    // 要申请的 buffer 的数量,只有当 memory 被设置成 V4L2_MEMORY_MMAP 的时候才会设置这个参数

    enum v4l2_buf_type type

    enum v4l2_memory memory

    // 要么是 V4L2_MEMORY_MMAP ,要么是 V4L2_MEMORY_USERPTR

    对于 memory mapped 模式,要在 device memory 下申请 buffer ,应用程序必须初始化上面的 3 个参数,驱动最后返回的 buffer 的个数可能等于 count ,也可能少于或者多于 count ,少于可能是因为内存不足,多于则可能是驱动为更好地完成相应功能增加的 buffer 。如果 driver 不支持 memory mapped 调用这个 ioctl 就会返回 EINVAL

    因为 memory map 模式下分配的是实实在在的物理内存,不是虚拟内存,所以使用完以后一定要使用 munmap()释放。

    应用程序可以重新调用 VIDICO_REQBUFS 来改变 buffer 的个数,但前提是必须先释放已经 mapped  buffer ,可以先 munmap ,然后设置参数 count  0 来释放所有的 buffer 

    对于 User pointer I/O ,应用程序只需设置上面的 type  memory 类型就可以了。

    申请好 buffer 后在进行 memory mapped 之前,首先要使用 VIDIOC_QUERYBUF 来获得分配的 buffer 信息,以传给函数 mmap() 来进行 map 

    int ioctl(int fd, int request, struct v4l2_buffer *argp);

    VIDIOC_QUERYBUF  memory mapped 这种模式下使用的方法,在 User pointer 模式下不需要使用这个函数,在调用之前应用程序需要设定 v4l2_buffer 中的两个参数,一个是 buffer 类型,另外一个是 index number( 有效值从0 到申请的 buffer 数目减 1) ,调用这个 ioctl 会将相应 buffer 中的 flag  V4L2_BUF_FLAG_MAPPED, V4L2_BUF_FLAG_QUEUED  V4L2_BUF_FLAG_DONE 设置为有效。下面我们来仔细看看 v4l2_buffer 这个数据结构:

    __u32 index

    // 应用程序来设定,仅仅用来申明是哪个 buffer

    enum v4l2_buf_type type

    __u32 bytesused

    //buffer 中已经使用的 byte 数,如果是 input stream  driver 来设定,相反则由应用程序来设定

    __u32 flags

    // 定义了 buffer 的一些标志位,来表明这个 buffer 处在哪个队列,比如输入队列或者输出队列(V4L2_BUF_FLAG_QUEUED V4L2_BUF_FLAG_DONE) ,是否关键帧等等,具体可以参照 spec

    enum v4l2_memory memory

    //V4L2_MEOMORY_MMAP  V4L2_MEMORY_USERPTR  V4L2_MEMORY_OVERLAY

    union m

    __u32 offset

    //  memory 类型是 V4L2_MEOMORY_MMAP 的时候,主要用来表明 buffer  device momory 中相对起始位置的偏移,主要用在 mmap() 参数中,对应用程序没有左右

    unsigned long userptr

    //  memory 类型是 V4L2_MEMORY_USERPTR 的时候,这是一个指向虚拟内存中 buffer 的指针,由应用程序来设定。

    __u32 length

    //buffer  size

     driver 内部管理 着两个 buffer queues ,一个输入队列,一个输出队列。对于 capture device 来说,当输入队列中的 buffer 被塞满数据以后会自动变为输出队列,等待调用 VIDIOC_DQBUF 将数据进行处理以后重新调用VIDIOC_QBUF  buffer 重新放进输入队列;对于 output device 来说 buffer 被显示以后自动变为输出队列。

    刚初始化的所有 map 过的 buffer 开始都处于 dequeced 的状态,由 driver 来管理对应用程序是不可访问的。对于 capture 应用程序来说,首先是通过 VIDIOC_QBUF 将所有 map 过的 buffer 加入队列,然后通过VIDIOC_STREAMON 开始 capture ,并进入 read loop ,在这里应用程序会等待直到有一个 buffer 被填满可以从队列中 dequeued ,当数据使用完后再 enqueue 进输入队列;对于 output 应用程序来说,首先应用程序会buffer 装满数据然后 enqueued ,当足够的 buffer 进入队列以后就调用 VIDIOC_STREAMON 将数据输出。

    有两种方法来阻塞应用程序的执行,直到有 buffer 能被 dequeued ,默认的是当调用 VIDIOC_DQBUF 的时候会被阻塞,直到有数据在 outgoing queue ,但是如果打开设备文件 的时候使用了 O_NONBLOCK ,则当调用VIDIOC_DQBUF 而又没有数据可读的时候就会立即返回。另外一种方法是调用 select  poll 来对文件描述符进行监听是否有数据可读。

    VIDIOC_STREAMON  VIDIOC_STREAMOFF 两个 ioctl 用来开始和停止 capturing 或者 output ,而且VIDIOC_STREAMOFF 会删除输入和输出队列中的所有 buffer 

    因此 drvier 如果要实现 memory mapping I/O 必须支持 VIDIOC_REQBUFS, VIDIOC_QUERYBUF, VIDIOC_QBUF, VIDIOC_DQBUF, VIDIOC_STREAMON  VIDIOC_STREAMOFF ioctl, the mmap(), munmap(), select()  poll() 函数

     

    User Pointers 是一种综合了 Read/Write  memory mappded 优势的 I/O 方法, buffer 是由应用程序自己申请的,可以是在虚拟内存或者共享内存中。在 capture  output 方面基本来说和 memory mapped 方式是相同的,在这里只提一下它申请内存的方式。

    User pointer 方式下,申请的内存也 memory page size 为单位对齐,而且 buffersize 也有一定限制,例示代码中是这样计算 buffer size 的,暂时还不知道这样分配 buffer size 的依据是什么,先简单地这样用就好了:

    page_size = getpagesize ();

    buffer_size = (buffer_size + page_size - 1) & ~(page_size – 1);

    buffers[n_buffers].start = memalign ( page_size,

    buffer_size);

     

    3  start_capturing

    经过上面的一系列的数据协商已经 buffer 的分配以后就可以调用 VIDIOC_QBUF  buffer 全部加入输入队列中,并调用 VIDIOC_STREAM0N 开始捕获数据了:

    int ioctl(int fd, int request, struct v4l2_buffer *argp);

    //VIDIOC_QBUF VIDIOC_DQBUF

    int ioctl(int fd, int request, const int *argp);

    //VIDIOC_STREAM0N VIDIOC_STREAMOFF  int 参数是 buffer 类型)

     

    4  mainloop

    开始捕获数据以后就会进入一个主循环,可以使用 select 或者 poll 来监听文件描述符的状态,一旦有数据可读,就调用函数来读取数据。

     

    5  read_frame

    读取数据根据 I/O 方式的不同而不同:

    Read/Write 方式直接从文件描述符中读一个帧大小的数据;

    Memory mapped 方式下先从输出队列中 dequeued 一个 buffer ,然后对帧数据进行处理,处理完成以后再放入输入队列。

    User pointer 方式下也是首先从输出队列中 dequeued 一个 buffer ,然后对这个 buffer 进行判断,看是否是应用程序开始申请的 buffer ,然后再对这个 buffer 进行处理,最后放入输入队列。

     

    6  stop_capturing / uninit_device / close device

    最后就是捕捉以及资源释放并关闭 device


    下面给出一个示例代码:

     

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #include <string.h>
    4. #include <fcntl.h> /* low-level i/*/
    5. #include <unistd.h>
    6. #include <errno.h>
    7. #include <malloc.h>
    8. #include <sys/stat.h>
    9. #include <sys/types.h>
    10. #include <sys/time.h>
    11. #include <sys/mman.h>
    12. #include <sys/ioctl.h>
    13. #include <linux/videodev2.h>
    14. #define DEVICE "/dev/video"
    15. static struct v4l2_requestbuffers req;
    16. struct buffer
    17. {
    18.     void* start;
    19.     unsigned int length;
    20. }; 
    21. static struct buffer *buffers;
    22. static struct v4l2_buffer buf;

    usb_camera.c

    1. #include "head.h"
    2. int main()
    3. {
    4.     int fd;
    5.     fd=open_device();
    6.     get_device_info(fd);
    7.     get_frame_fmt(fd);
    8.     get_current_frame_info(fd);
    9.     try_format_support(fd);
    10.     set_frame_format(fd);
    11.     apply_memory_buf(fd);
    12.     memory_mapping(fd);
    13.     buffer_enqueue(fd);
    14.     close(fd);
    15.     return 0;
    16. }
    17. int open_device()
    18. {
    19.     int fd;
    20.     if(-1==(fd=open(DEVICE,O_RDWR)))
    21.         printf("info:Can't open video device\n");
    22.     else
    23.         printf("info:Open the device :%d\n",fd);
    24.     return fd;
    25. }
    26. int get_device_info(int fd)
    27. {
    28.     struct v4l2_capability cap;
    29.     if(-1==ioctl(fd,VIDIOC_QUERYCAP,&cap))
    30.         printf("info:VIDIOC_QUERYCAP ERROR\n");
    31.     else
    32.         printf("info:Driver Name:%s....Card Name:%s....Bus info:%s....Driver Version:%u.%u.%u\n",
    33.         cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF,(cap.version>>8)&0XFF,cap.version&0XFF);
    34.     return 1;
    35. }
    36. int get_frame_fmt(int fd)
    37. {
    38.     struct v4l2_fmtdesc fmtdesc;
    39.     fmtdesc.index=0;
    40.     fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    41.     printf("info:Support format:");
    42.     while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    43.     {
    44.         printf("\t%d.%s",fmtdesc.index+1,fmtdesc.description);
    45.         fmtdesc.index++;
    46.     }
    47.     printf("\n");
    48.     return 1;
    49. }
    50. int get_current_frame_info(int fd)
    51. {
    52.     struct v4l2_format fmt;
    53.     fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    54.     ioctl(fd,VIDIOC_G_FMT,&fmt);
    55.     printf("info:Current data format information:\n\t%d\n\theight:%d\n",fmt.fmt.pix.width,fmt.fmt.pix.height);
    56.     struct v4l2_fmtdesc fmtdesc;
    57.     fmtdesc.index=0;
    58.     fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    59.     while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    60.     {
    61.         if(fmtdesc.pixelformat & fmt.fmt.pix.pixelformat)
    62.         {
    63.             printf("\tformat:%s\n",fmtdesc.description);
    64.             break;
    65.         }
    66.         fmtdesc.index++;
    67.     } 
    68.     return 1;
    69. }
    70. int try_format_support(int fd)
    71. {
    72.     struct v4l2_format fmt;
    73.     fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    74.     //fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_RGB32;
    75.     fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
    76.     if(ioctl(fd,VIDIOC_TRY_FMT,&fmt)==-1)
    77.     if(errno==EINVAL)
    78.     printf("info:not support format RGB32!\n"); 
    79.     return 1;
    80. }
    81. int set_frame_format(int fd)
    82. {
    83.     struct v4l2_format fmt;
    84.     fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    85.     fmt.fmt.pix.width=640;
    86.     fmt.fmt.pix.height=480;
    87.     fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    88.     fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 
    89.     if(ioctl(fd,VIDIOC_S_FMT,&fmt)==-1)
    90.     if(errno==EINVAL)
    91.     printf("info:set frame format error!\n");
    92.     return 1;
    93. }
    94. int apply_memory_buf(int fd)
    95. {
    96.     //struct v4l2_requestbuffers req;
    97.     req.count=4;
    98.     req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    99.     req.memory=V4L2_MEMORY_MMAP;
    100.     if(-1==ioctl(fd,VIDIOC_REQBUFS,&req))
    101.         printf("info:VIDIOC_REQBUFS FAILED\n");
    102.     else
    103.         printf("info:VIDIOC_REQBUFS SUCCESS\n");
    104.     return 1;
    105. }
    106. int memory_mapping(int fd)
    107. {
    108.     unsigned int n_buffers;
    109.     buffers = (struct buffer*)calloc(req.count,sizeof(struct buffer));
    110.     if (!buffers) {
    111.         fprintf (stderr, "Out of memory\n");
    112.         exit (EXIT_FAILURE);
    113.     }
    114.     // 映射
    115.     for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
    116.         //struct v4l2_buffer buf;
    117.         memset(&buf,0,sizeof(buf));
    118.         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    119.         buf.memory = V4L2_MEMORY_MMAP;
    120.         buf.index = n_buffers;
    121.         // 查询序号为n_buffers 的缓冲区,得到其起始物理地址和大小
    122.         if (-== ioctl (fd, VIDIOC_QUERYBUF, &buf))
    123.             exit(-1);
    124.         buffers[n_buffers].length = buf.length;
    125.         // 映射内存
    126.         buffers[n_buffers].start =mmap (NULL,buf.length,PROT_READ | PROT_WRITE,MAP_SHARED,fd, buf.m.offset);
    127.         if (MAP_FAILED == buffers[n_buffers].start)
    128.             exit(-1);
    129.     } 
    130.     printf("info:memory mapping success\n");
    131.     return 1;
    132. }
    133. int buffer_enqueue(int fd)
    134. {
    135.     unsigned int i;
    136.     enum v4l2_buf_type type;
    137.     // 将缓冲帧放入队列
    138.     for (= 0; i < 4; ++i)
    139.     {
    140.         struct v4l2_buffer buf;
    141.         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    142.         buf.memory = V4L2_MEMORY_MMAP;
    143.         buf.index = i;
    144.         if(-1==ioctl (fd, VIDIOC_QBUF, &buf))
    145.             printf("buffer enqueue failed\n");
    146.     }
    147.     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    148.     //open stream 
    149.     if(-1==ioctl (fd, VIDIOC_STREAMON, &type))
    150.         printf("info:open stream failed\n");
    151.     else
    152.         printf("info:open stream success\n");
    153.     return 1;
  • 相关阅读:
    Visual Studio 2005 Starter Kits
    怎样去做才是朋友?
    C#读写日志文本文件
    [文摘20080707]马云追加投资20亿 淘宝首定10年超沃尔玛目标
    [转]在WinForm应用程序中实现自动升级
    [转]官方Flash CS3简体中文帮助文档下载,AS3.0简体中文帮助文档下载
    [引]MySQL INNODB类型表的外键关联设置
    [转]winfrom让弹出的MessageBox在指定时间内自动销毁
    生活开心一笑 之 "我家半"与"QQ病毒"
    [English20080721]疯狂英语365句
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/2458021.html
Copyright © 2011-2022 走看看