zoukankan      html  css  js  c++  java
  • 调用视频The videobuf2 API

    文章结束给大家来个程序员笑话:[M]

        

     

        

    The videobuf2 API

        Author:CJOK

        Contact:cjok.liao#gmail.com

        SinaWeibo:@廖野cjok

        原文地址:http://lwn.net/Articles/447435/

        Video4linux2驱动重要负责从sensor(平日是通过DMA)上获取视频数据然后把这些视频帧传输到用户空间,大批数据的传输是性能要考虑。出于此目标,V4L2定义了庞杂的API去处置流数据。V4L2子系统为实现这些API也加入了不少庞杂的代码,当然大部分的代码沿用了V4L。为了使驱动工程师更方便实现视频驱动代码,videobuf子系统供给了一组用于管理流IO buffer的接口。

        Linux kernel在不断的更新和完善,videobuf在2009已经被videobuf2代替,比拟videobuf,videobuf2更加完善,更加实用。本文用于介绍videobuf2。

        为什么涌现videobuf2?虽然videobuf任务的很好,但是在一些方面并不是那么完善,各种不同的API相称依附buffer的类型,也没有供给相关的接口用于视频缓冲区管理。Videobuf2供给了一组统一的API接口,答应驱动自己有更多的配置。

        Buffers

        像原来的的videobuf一样,videobuf2也实现了三种buffer类型。Vmalloc buffer这类buffer由vmalloc()分配,因此在内核空间虚拟地址上是连续的,drivers working with these buffers almost invariably end up copyingthe data once as it passes through the system;contiguous DMA buffers在物理内存上连续,平日硬件设备需要在连续物理地址空间上执行DMA操纵;S/G DMA buffers在物理内存上是不连续的,如果硬件上支持scatter/gather DMA,驱动可以使用这种方式。

        驱动程序使用不同的类型buffer,需要包含以下对应的头文件:

        #include <media/videobuf2-vmalloc.h>

        #include <media/videobuf2-dma-contig.h>

        #include <media/videobuf2-dma-sg.h>

        和videobuf比拟,videobuf2有一个好处就是可以是一个驱动程序同时支持多种类型的buffer,下面的头文件不会互相冲突,而且videobuf2供给的这三种buffer类型的操纵接口非常类似。

        结构体vb2_buffer代表一个视频缓冲区,此结构体定义在<media/videobuf2-core.h>。平日驱动程序都想保存每一个缓冲区特有的信息,因此,驱动程序会自己定义一个缓冲区结构体,然后把vb2_buffer嵌入在其中。可是,videobuf2作者没有读过Neil Brown’s object-oriented design patterns in the kernel(内核中的面向对象设计模型),没有供给分配这些结构体的函数。这就意味着驱动程序需要把自定义结构体的大小通知videobuf2子系统,而且vb2_buffer实例必须放在自定义结构体的第一个成员域。

        一个视频驱动程序需要实现一组用于管理buffer的回调函数并注册到videobuf2子系统,在原来的videobuf中也有类似的回调函数。这五个函数如下:

        int (*buf_init)(struct vb2_buffer *vb);
        int (*buf_prepare)(struct vb2_buffer *vb);
        void (*buf_queue)(struct vb2_buffer *vb);
        int (*buf_finish)(struct vb2_buffer *vb);
        void (*buf_cleanup)(struct vb2_buffer *vb);

        videobuf2会调用buf_init()对每一个分配好的新buffer执行一些须要的额外初始化,如果buffer_init返回错误码将会中断缓冲队列的设置;

        buf_prepare()在用户空间入队这些buffer时被调用(比如,响应VIDIOC_QBUF操纵),在这些buffer被流I/O使用之前实现一些须要的准备任务。调用Buf_queue()把buffers传递给驱动程序,开始流I/O操纵。

        译者注:buf_queue是响应VIDIOC_STREAMON操纵,开始流IO操纵,然后会往buffers中填充视频数据。

        Buf_finish()在buffers传回到用户空间前被调用,你可能需要对这个回调函数考虑的问题是,驱动程序已经知道buffer有一帧新的数据传递到用户空间,事实上,还需要告知videobuf2。一个可能的谜底是实现流I/O数据到buffer的操纵平日是在中断上下文处置的,而buf_finish()会在进程上下文调用。

        译者注:buf_finish()在每一个buffer出队传递到用户空间前被调用,平日在用户空间访问buffers前做一些须要操纵。

        Buf_cleanup()在buffer释放前被调用,用来做一些清理任务。

        Other videobuf2 callbacks

        在原来的videobuf版本中,驱动程序只供给上述的回调函数来管理缓冲区,videobuf2新增了一些回调函数用来完善对缓冲区的操纵。

        int (*queue_setup)(struct vb2_queue *q, unsigned int *num_buffers,
                                                    unsigned int *num_planes, unsigned long sizes[],
                                                    void *alloc_ctxs[]);

        queue_setup()在用户空间执行ioctl:VIDIOC_REQBUFS操纵时被调用,作用是用来建立缓冲队列。结构体vb2_queue用来描述缓冲队列,num_buffers参数是应用程序申请的缓冲数。Num_planes是每一个buffer中的视频层数目。Size数组包含了每一个视频层的大小(bytes)。

        Alloc_ctxs指针数组包含了每一个视频层的“分配上下文”,以后还只被contiguous DMA模式使用到。使用contiguous DMA的驱动程序需要调用获取/注销上下文信息:

    void *vb2_dma_contig_init_ctx(struct device *dev)

        voidvb2_dma_contig_cleanup_ctx(void *alloc_ctx)

        下面两个回调函数用于开始和停止获取视频数据:

        int (*start_streaming)(struct vb2_queue *q);
        int (*stop_streaming)(struct vb2_queue *q);

        start_streaming()用来响应ioctl:VIDIOC_STREAMON操纵开始抓取视频数据,如果驱动程序实现了read()方法也会调用到start_streaming()。Stop_streaming()用来响应ioctl:VIDIOC_STREAMOFF操纵停止抓取视频数据,等待DMA停止后函数才会返回。这里值得注意的是,调用stop_streaming()后,videobuf2子系统会回收掉所有传递到驱动程序的buffers,此时驱动程序不能访问这些buffers。

        Locking

        最后两个回调函数是用于掩护视频设备访问——锁机制:

        l  调用驱动程序访问视频设备时需要获取到锁;

        l  Videobuf2子系统调用到驱动的回调函数前应当先获取到设备访问锁。比如,用户空间获取视频数据会导致start_streaming()调用,需要通过videobuf2子系统调用到相关的设备驱动程序,所以调用start_streaming()时,将要持有设备锁。

        每日一道理
    古人云:“海纳百川,有容乃大。”人世间,不可能没有矛盾和争吵,我们要以磊落的胸怀和宽容的微笑去面对它 。哈伯德也曾说过:“宽恕和受宽恕的难以言喻的快乐,是连神明都会为之羡慕的极大乐事。”让我们从宽容中享受快乐,从谅解中体会幸福吧!

        在这样的情况下,有一个需要考虑的问题:用户空间调用ioctl:VIDIOC_DQBUF从内核获取缓冲区的数据,当缓冲区不可用时,进程可能会阻塞。

    void (*wait_prepare)(struct vb2_queue *q);
        void (*wait_finish)(struct vb2_queue *q);

        在停止VIDIOC_DQBUF操纵阻塞等待一个buffer数据之前调用wait_prepare()去释放设备锁,一旦退出阻塞,会调用wait_finish()获取设备锁。可能这两个回调函数用release_lock()和reacquire_lock()名字更好。

        Queue setup

        下面介绍了vb2_ops,了解了视频驱动程序如何注册到videobuf2子系统。实现需要构建vb2_ops,并实现其中的回调函数:

    static const struct vb2_ops my_special_callbacks = {
                                             .queue_setup = my_special_queue_setup,
                                             /* ... */
        };

        然后,建立一个videobuf2队列(平日是在设备注册或者设备open的时候),驱动需要分配一个vb2_queue结构体:

        struct vb2_queue {
                        enum v4l2_buf_type                       type;
                        unsigned int                                                          io_modes;
                        unsigned int                                                          io_flags;
                        const struct vb2_ops                      *ops;
                        const struct vb2_mem_ops          *mem_ops;
                        void                                                                          *drv_priv;
                        unsigned int                                                          buf_struct_size;
                        /* Lots of private stuff omitted */
        };

        这个结构体需要实现下面的成员,type代表buffer类型,平日是V4L2_BUF_TYPE_VIDEO_CAPTURE,io_modes是一个标志位,用来描述以何种方式来访问buffer:

        l  VB2_MMAP:缓冲区由内核空间分配,通过mmap映射缓冲区到用户空间。平日vmalloc和contiguous DMA buffers来分配此方式的缓冲区;

        l  VB2_USERPTR:缓冲区由用户空间分配,平日需要设备支持集散IO才可以分配用户空间缓冲区。有趣的是,videobuf2支持分配连续的缓冲区在用户空间,但是需要应用一些特别的机制,比如android的pmem驱动。在用户空间分配连续大批的页面是不支持的。

        l  VB2_READ,VB2_WRITE:用户空间缓冲区供给read()和write()方式访问视频设备。

        Mem_ops成员是缓冲区操纵函数集合,供给给下层videobuf2子系统操纵缓冲区,内核已经实现三组供我们使用: vb2_vmalloc_memops,vb2_dma_contig_memops和 vb2_dma_sg_memops,如果这三组都不符合,驱动工程师可以自己实现一组。在撰写本文时,内核主线还有没有供给内存操纵的驱动程序。

        最后,drv_priv是驱动私有的指针(平日指向device结构),buf_struct_size用于表现buffer结构体的大小。Vb2_queue结构体实现后,调用以下函数停止初始化:

        intvb2_queue_init(struct vb2_queue *q)

        在设备关闭时调用 vb2_queue_release()停止销毁。

        Device operations

        到现在我们已经分析了videobuf2底层的基础设施,接下来看下videobuf2子系统是怎么把用户空间的操纵和视频设备驱动关联起来。平日第一步驱动程序需要实现大批的ioctl()调用接口,大部分的函数接口只是简单的获取设备锁,然后直接调用videobuf2实现的ops。下面我们来看这些ops:

        int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b);
        int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req);
        int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b);
        int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b,
                                               bool nonblocking);
        int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type);
        int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type);
        int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma);
        unsigned int vb2_poll(struct vb2_queue *q, struct file *file,
                                                                   poll_table *wait);
        size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
                                                 loff_t *ppos, int nonblock);
        size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count,
                                                  loff_t *ppos, int nonblock);

        这些函数接口屏蔽了底层大批缓冲区管理的细节,使代码非常简练,供给了统一的接口给下层调用。

        最后还有一点任务需要做:获取数据放到缓冲区里。对于vmalloc buffers,平日是通过memcpy()这样的操纵来实现,但是videobuf2供给了一个有效函数:

        void *vb2_plane_vaddr(struct vb2_buffer*vb, unsigned int plane_no)

        返回在缓冲区中给定的内核虚拟地址。

        Contiguous DMA驱动需要获取处置

        dma_addr_t vb2_dma_contig_plane_paddr(struct vb2_buffer *vb,
                                              unsigned int plane_no);
    对于scatter/gather DMA驱动,供给的接口略显庞杂:
    struct vb2_dma_sg_desc {
                        unsigned long                                   size;
                        unsigned int                                      num_pages;
                        struct scatterlist         *sglist;
    };
    static inline struct vb2_dma_sg_desc *vb2_dma_sg_plane_desc(
                                             struct vb2_buffer *vb, unsigned int plane_no)

        驱动程序从videobuf2获取到能用于设备的scatterlist。

        对于这三种情况,缓冲区将会填充满需要传递到用户空间的帧数据。Vb2_buffer结构体中内嵌了一个v4l2_buf,平日包含图像大小、序列号、时光帧等信息。最后调用以下函数:

        void vb2_buffer_done(struct vb2_buffer *vb,enum vb2_buffer_state state)

        state参数:

             VB2_BUF_STATE_DONE,通知videobuf2子系统有一个buffer处置实现;

             VB2_BUF_STATE_ERROR,通知videobuf2子系统有一个buffer处置时涌现ERROR。

        本文大致介绍总结videobuf2 API,如果想了解完全的接口函数,可以参考内核源码。内核供给一个虚拟视频驱动实例供参考(vivi.c)。

    文章结束给大家分享下程序员的一些笑话语录: 《诺基亚投资手机浏览器UCWEB,资金不详或控股》杯具了,好不容易养大的闺女嫁外国。(心疼是你养的吗?中国创业型公司创业初期哪个从国有银行贷到过钱?)

    --------------------------------- 原创文章 By
    调用和视频
    ---------------------------------

  • 相关阅读:
    charles 抓包iOS模拟器 HTTPS请求
    tableView reloadData页面跳动问题
    测试swiftc 命令 插件无法使用的问题( PluginLoading: Required plug-in compatibility UUID.... )
    linux网络设置
    博客园markdown语法高亮
    django继承user类来定制自己的user类
    Django开发bug及问题记录
    震惊!男子使用这一手机设置,从此告别UC!
    吐槽手机的迷之信号
    声控皮卡丘小游戏
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3106703.html
Copyright © 2011-2022 走看看