zoukankan      html  css  js  c++  java
  • V4L2 API详解 Buffer的准备和数据读取

    1. 初始化 Memory Mapping 或 User Pointer I/O.
    int ioctl(int fd, int requestbuf, struct v4l2_requestbuffers * argp);
    参数一:open()所产生的句柄。
    参数二:VIDIOC_REQBUFS(向设备申请缓存区)
    参数三:in/out结构体。
    struct v4l2_requestbuffers
    {
     __u32         count;
     enum v4l2_buf_type     type;
     enum v4l2_memory   memory; //Applications set this field to V4L2_MEMORY_MMAP
                     // or V4L2_MEMORY_USERPTR
     __u32         reserved[2];
    };
     
    注意,有两种方式的I/O。 Memory Mapping 和User Pointer
    Memory Mapping的Buffer由Driver申请为物理连续的内存空间(Kernel空间)。 在此ioctl调用时被分配,需要早于mmap()动作将他们映射到用户空间。
     
    1.1:Memory Mapping模式详解:
    在使用Memory Mapping模式时,参数三中结构体内每个field都需要设置。
     
     __u32        count;     //V4L2_MEMORY_MMAP时有效。表明要申请的buffer个数。
     enum v4l2_buf_type    type;       //Stream 或Buffer。此处为V4L2_BUF_TYPE_VIDEO_CAPTURE
     enum v4l2_memory     memory; //V4L2_MEMORY_MMAP

    注意:count是个输入输出函数。 因为你所申请到的Buffer个数不一定就是你所输入的Number。所以在ioctl执行后,driver会将真实申请到的buffer个数填充到此field. 这个数目有可能大于你想要申请的,也可能小与,甚至可能是0个。
     
    应用程序可以再次调用ioctl--VIDIOC_REQBUFS(向设备申请缓存区) 来修改buffer个数。但前提是必须先释放已经 mapped 的 buffer ,可以先 munmap ,然后设置参数 count 为 0 来释放所有的 buffer。
     
    支持Memory  Mapping  I/O方式的前提是:v4l2_capability  中支持V4L2_CAP_STREAMING。在这个模式下,数据本身不会被Copy,只是在Kernel和用户态之间交换。在应用程序想要访问到这些数据之前,它必须调用mmap()影射到用户态。
     
    同时也要注意,通过ioctl申请的内存,是物理内存,无法被交换入Disk,所以一定要释放:munmap()
     
     
    1.2:User Pointer模式:
    User Pointer模式时,应用程序实现申请。
    只需要填充Type=V4L2_BUF_TYPE_VIDEO_CAPTURE, memory=V4L2_MEMORY_USERPTR
     
     
    2. 询问Buffer状态:
    int ioctl(int fd, int request, struct v4l2_buffer* argp);
    参数一:open()所产生的句柄。
    参数二:VIDIOC_QUERYBUF(获取缓存帧的地址、长度) 
    参数三:v4l2_buffer 结构体。(IN/OUT参数)
     
    注意,此ioctl是Memory Mapping的I/O方法之一。User Pointer模式不需要。Buffer在ioctl-VIDIOC_REQBUFS(向设备申请缓存区) 执行时创建后,随时都可以调用此Ioctl得到buffer信息。
     
    我们首先通过v4l2_buffer结构体看看参数三这个输入输出参数需要输入些什么,以及能够得到什么信息。
     
    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;
     
     enum v4l2_memory   memory;
     union {
     __u32         offset;
     unsigned long       userptr;
     } m;
     __u32         length;
     __u32         input;
     __u32         reserved;
    };
     
    在调用ioctl--VIDIOC_QUERYBUF(获取缓存帧的地址、长度)时,需要写入的项目有:
    enum v4l2_buf_type   type;   //V4L2_BUF_TYPE_VIDEO_CAPTURE
    __u32           index;  //0到count-1
    解释一下:在调用ioctl-VIDIOC_REQBUFS(向设备申请缓存区) 时,建立了count个Buffer。所以,这里index的有效范围是:0到count-1.
     
     
    在调用ioctl-VIDIOC_QUERYBUF(获取缓存帧的地址、长度)后,Driver会填充v4l2_buffer 结构体内所有信息供用户使用。则:
    1. flags中:V4L2_BUF_FLAG_MAPPED, V4L2_BUF_FLAG_QUEUED and V4L2_BUF_FLAG_DONE被设置(分别表示当前缓存已经映射、缓存可以采集数据、缓存可以提取数据)。
    2. memory中,V4L2_MEMORY_MMAP被设置。
    3. m.offset中,从将要mapping 的device memory头到数据头的offset.
    4. length 中,填充当前Buffer长度。
    5.其它的Field有可能设置,也有可能不被设置。
     
    这样,mmap()想要有的信息就全了。而mmap()之后,Device Driver 申请的或者Device Memory就能映射到用户空间。数据就可以被应用程序使用了。这才是ioctl-VIDIOC_QUERYBUF(获取缓存帧的地址、长度)的关键作用。
     
    3.和Driver交换buffer: 
    对Camera这样的捕获设备来说,Device将数据放到Buffer中,用户得到数据。Device再次将数据放到Buffer中。那么Device Driver 怎样知道哪个Buffer是可以存放数据的呢?这就用到当前这两个ioctl-VIDIOC_QBUF(把帧放入队列), ioctl-VIDIOC_DQBUF(从队列中取出帧).
     
    ioctl-VIDIOC_QBUF(把帧放入队列): 将指定的Buffer放到输入队列中,即向Device表明这个Buffer可以存放东西。
    ioctl-VIDIOC_DQBUF(从队列中取出帧): 将输出队列中的数据 buffer取出。
     
    在 driver 内部管理着两个 buffer queues ,一个输入队列,一个输出队列。对于 capture device 来说,当输入队列中的 buffer被塞满数据以后会自动变为输出队列,等待调用 VIDIOC_DQBUF(从队列中取出帧) 将数据进行处理以后重新调用 VIDIOC_QBUF (把帧放入队列)将 buffer 重新放进输入队列.
     
    用法:
    ioctl--VIDIOC_QBUF(把帧放入队列)
    int ioctl(int fd, int request, struct v4l2_buffer* argp);
    参数一:open()所产生的句柄。
    参数二:VIDIOC_QBUF(把帧放入队列)
    参数三:v4l2_buffer 结构体。(IN/OUT参数)
     
    参数三是IN/OUT 参数。需要填充
    enum v4l2_buf_type  type; //V4L2_BUF_TYPE_VIDEO_CAPTURE
    __u32        index;  //调用ioctl-VIDIOC_REQBUFS(向设备申请缓存区)时,建立了count个Buffer。取值:0到count-1. 
     
    memory: V4L2_MEMORY_MMAP.
     
    则这个结构体指明的buffer被送入输出队列,表明此Buffer可以被device 填充数据。
     
    用法:
    ioctl--VIDIOC_DQBUF(从队列中取出帧)
    int ioctl(int fd, int request, struct v4l2_buffer* argp);
    参数一:open()所产生的句柄。
    参数二:VIDIOC_DQBUF(从队列中取出帧)
    参数三:v4l2_buffer 结构体。(IN/OUT参数)
     
    从输出队列中取出一个有数据的Buffer。 这个Buffer中的数据被处理后,此Buffer可以通过ioctl-VIDIOC_QBUF(把帧放入队列)再次放入输入队列中去。
     
     
     
    4. 开始和结束捕获:
    ioctl--VIDIOC_STREAMON(启动视频数据流). ioctl--VIDIOC_STREAMOFF(停止视频数据流)
     
    非常简单的调用。就是开始和结束。
     
     

    常用的VIDIOC命令:
     1. VIDIOC_QUERYCAP (查询设备属性)
     2. VIDIOC_ENUM_FMT (显示所有支持的格式)
     3. VIDIOC_S_FMT (设置视频捕获格式)
     4. VIDIOC_G_FMT (获取硬件现在的视频捕获格式)
     5. VIDIOC_TRY_FMT (检查是否支持某种帧格式)
     6. VIDIOC_ENUM_FRAMESIZES (枚举设备支持的分辨率信息)
     7. VIDIOC_ENUM_FRAMEINTERVALS (获取设备支持的帧间隔)
     8. VIDIOC_S_PARM && VIDIOC_G_PARM (设置和获取流参数)
     9. VIDIOC_QUERYCAP (查询驱动的修剪能力)
     10. VIDIOC_S_CROP (设置视频信号的边框)
     11. VIDIOC_G_CROP (读取设备信号的边框)
     12. VIDIOC_REQBUFS (向设备申请缓存区)
     13. VIDIOC_QUERYBUF (获取缓存帧的地址、长度)
     14. VIDIOC_QBUF (把帧放入队列)
     15. VIDIOC_DQBUF (从队列中取出帧)
     16. VIDIOC_STREAMON && VIDIOC_STREAMOFF (启动/停止视频数据流)

  • 相关阅读:
    javascript
    vue-cli3 按需加载loading,服务的方式调用
    chrome devTool
    vuex
    git
    DNS寻址以及IP解析
    es6小记
    webpack 安装vue(两种代码模式compiler 和runtime)
    游戏屏幕朝向
    C#读写Json
  • 原文地址:https://www.cnblogs.com/cyyljw/p/11269859.html
Copyright © 2011-2022 走看看