zoukankan      html  css  js  c++  java
  • V4l2初识(七)-----------浅析app获取虚拟摄像头数据的过程

    继续分析数据的获取过程:  

      1、请求分配的缓冲区: ioctl(4,VIDIOC_REQBUFS)

          vidioc_reqbufs

      2、查询和映射缓冲区

         ioctl(4,VIDIOC_QUERYBUF)

         mmap

      3、把缓冲区放入队列

         ioctl(4,VIDIOC_QBUF)

      4、启动摄像头

        ioctl(4,VIDIOC_STREAMON

      5、用select函数查询是否有数据

        //驱动程序中必定有:产生数据、唤醒进程

      6、有数据后,从队列中取出缓冲区

         ioctl(4,VIDIOC_DQBUF  

    ------------------------------------------------------------------------------------------------------------------------

    app:     VIDIOC_REQBUFS

          vidioc_reqbufs

            vb2_reqbufs(&dev->vb_vidq, p)//第一个参数表示队列

    注意:这个ioctl只是分配缓冲区的头部信息,真正的缓存还没有被分配,在驱动程序里有一条原则,这些资源只有在用的时候才分配。

    队列是在哪个地方进行初始化的?

    /*此函数主要是返回dev->p->driver_data指针,

    device结构体其实是对内核中所有设备的抽象表示,所有的设备都有一个device实例与之对应

    device结构体的主要用法是将其嵌入到其他的结构体中,如platform_device*/

    void *dev_get_drvdata(const struct device *dev)
    {
      if (dev && dev->p)
        return dev->p->driver_data;
      return NULL;
    }

    static inline void *video_get_drvdata(struct video_device *vdev)
    {
      return dev_get_drvdata(&vdev->dev);
    }

    struct video_device *video_devdata(struct file *file)
    {
      return video_device[iminor(file->f_path.dentry->d_inode)];//根据次设备号从video_device[]数组中取出device设备
    }

    static inline void *video_drvdata(struct file *file)
    {
      return video_get_drvdata(video_devdata(file));
    }

    static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p)
    {
      struct vivi_dev *dev = video_drvdata(file);//根据次设备号得到dev这个结构体
      return vb2_reqbufs(&dev->vb_vidq, p);//把p这个缓冲区放入dev结构体中的vb_vidq队列中
    }

    -------------------------------------------------------------------------------------------------------------------

    app:    VIDIOC_QUERYBUF//查询所分配的缓冲区

          vidioc_querybuf

    注意,这里只是表示缓冲区将会有多大,并不表示这个缓冲区的内存被分配

    static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
    {
      struct vivi_dev *dev = video_drvdata(file);
      return vb2_querybuf(&dev->vb_vidq, p);/*获得缓冲区数据格式、大小、每一行长度、高度等*/
    }

    mmap  //在这里才分配缓存。注意在分析mmap函数时,所用的内核不是3.4.2.是根据韦老大讲解的整理的

      v4l2_mmap

        vivi_mmap

          videobuf_mmap_mapper

            _videobuf_mmap_mapper //该函数在videobuf_vmalloc.c里面

    -----------------------------------------------------------------------------------------------------------

    app:    VIDIOC_QBUF //把缓冲区放入队列(也是根据韦老大讲解的进行整理,内核不是3.4.2)

          vidioc_qbuf

            videobuf_qbuf

              q->ops->buf_prepare(q,buf,field)//调用驱动程序里面提供的函数做一些预处理工作

              list_add_tail//把缓冲区放入队列的尾部

              q->ops->buf_queue(q,buf)调用驱动程序里面提供的入队列函数

    ----------------------------------------------------------------------------------------------------------------

    app :  VIDIOC_STREAMON  //启动

    ----------------------------------------------------------------------------------------------------------------

    用select查询是否有数据(根据韦老大讲解的进行整理,内核不是3.4.2)

    在驱动程序里面对应的是poll机制。

    V4l2_poll

      vdev->fops->poll(vivi_poll)

        vivi_poll

          videobuf_poll_stream

            buf=list_entry(q->stream.next, struct  videobuf_buffer,stream)//从队列的头部获得缓冲区

            如果缓冲区里面没有数据的话,就调用poll_wait等待

            poll_wait(file,&buf->done,wait)//在这里休眠

    谁来产生数据,谁来唤醒它(在vivi.c中搜索done)

    vivi_thread_tick

      wake_up(&buf->vb.done)

    唤醒进程:谁来调用vivi_thread_tick这个函数呢?

    因为这里是一个虚拟的摄像头驱动程序,它会怎样产生数据呢?

    如果是一个真实的摄像头的话,是硬件来产生数据的。但是在虚拟摄像头驱动里面,是用内核线程来产生数据的。

    timeout = msecs_to_jiffies();

    创建一个内核线程,每隔30ms就会执行一次,每次就会调用vivi_thread_tick(fh)来产生数据。在vivi_thread_tick()中会

    调通过vivi_fillbuf(fh,buf)来构造数据。调完之后,又开始休眠schedule_timeout_interruptible(timeout)

    ---------------------------------------------------------------------------------

    /*有数据后,从队列中取出缓冲区。有那么多缓冲区,app如何知道哪一个缓冲区有数据,

    调用VIDIOC_DQBUF(根据韦老大讲解的进行整理,内核不是3.4.2)

    app : VIDIOC_DQBUF  

        vidioc_dqbuf

          /*在队列中获得有数据的缓冲区*/

          retval = stream_next_buffer(q,&buf,nonblocking);

          /*把它从队列中删掉*/

          list_del(&buf->stream)

          /*把缓冲区的状态返回给应用程序*/

          videobuf_status(q,b,buf,q->type);

    -------------------------------------------------------------------------------------------

    应用程序根据VIDIOC_DQBUF所得到缓冲区的状态,知道哪一个缓冲区有数据,就去读对应的地址(该地址来自前面的mmap)

    ------------------------------------------------------------------------------------------

          

              

            

                  

  • 相关阅读:
    支付宝H5支付---证书模式
    Redis分布式锁
    Docker+Nginx+Ssl
    Java调用函数传递参数到底是值传递还是引用传递
    Mysql索引查询失效的情况
    mysql索引之最左前缀法则
    数据的三大范式以及什么是反三大范式
    SpringMvc的工作流程
    Android 环境搭建
    Python 爬虫: 抓取花瓣网图片
  • 原文地址:https://www.cnblogs.com/-glb/p/10322033.html
Copyright © 2011-2022 走看看