zoukankan      html  css  js  c++  java
  • vivi虚拟摄像头驱动程序

    一、vivi虚拟摄像头驱动

    基于V4L2(video for linux 2)摄像头驱动程序,我们减去不需要的ioctl_fops的函数,只增加ioctl函数增加的必要的摄像头流查询等函数;

      1 #include <linux/module.h>
      2 #include <linux/module.h>
      3 #include <linux/delay.h>
      4 #include <linux/errno.h>
      5 #include <linux/fs.h>
      6 #include <linux/kernel.h>
      7 #include <linux/slab.h>
      8 #include <linux/mm.h>
      9 #include <linux/ioport.h>
     10 #include <linux/init.h>
     11 #include <linux/sched.h>
     12 #include <linux/pci.h>
     13 #include <linux/random.h>
     14 #include <linux/version.h>
     15 #include <linux/mutex.h>
     16 #include <linux/videodev2.h>
     17 #include <linux/dma-mapping.h>
     18 #include <linux/interrupt.h>
     19 #include <linux/kthread.h>
     20 #include <linux/highmem.h>
     21 #include <linux/freezer.h>
     22 #include <media/videobuf-vmalloc.h>
     23 #include <media/v4l2-device.h>
     24 #include <media/v4l2-ioctl.h>
     25 
     26 static struct video_device *myvivi_device;
     27 static struct timer_list myvivi_timer;
     28 static struct list_head myvivi_vb_local_queue;
     29 
     30 static void myvivi_timer_function(unsigned long data)
     31 {
     32     struct videobuf_buffer *vb;
     33     void *vbuf;
     34     struct timeval ts;
     35     
     36     /* 1. 构造数据: 从队列头部取出第1个videobuf到本地队列中, 填充数据
     37      */
     38 
     39     /* 1.1 从本地队列取出第1个videobuf */
     40     if (list_empty(&myvivi_vb_local_queue)) {
     41         goto out;
     42     }
     43     
     44     vb = list_entry(myvivi_vb_local_queue.next,
     45              struct videobuf_buffer, queue);
     46     
     47     /* Nobody is waiting on this buffer, return */
     48     if (!waitqueue_active(&vb->done))
     49         goto out;
     50     
     51 
     52     /* 1.2 填充数据 */
     53     vbuf = videobuf_to_vmalloc(vb);
     54     memset(vbuf, 0xff, vb->size);
     55     vb->field_count++;
     56     do_gettimeofday(&ts);
     57     vb->ts = ts;
     58     vb->state = VIDEOBUF_DONE;
     59 
     60     /* 1.3 把videobuf从本地队列中删除 */
     61     list_del(&vb->queue);
     62 
     63     /* 2. 唤醒进程: 唤醒videobuf->done上的进程 */
     64     wake_up(&vb->done);
     65     
     66 out:
     67     /* 3. 修改timer的超时时间 : 30fps, 1秒里有30帧数据
     68      *    每1/30 秒产生一帧数据
     69      */
     70     mod_timer(&myvivi_timer, jiffies + HZ/30);
     71 }
     72 /* ------------------------------------------------------------------
     73     IOCTL vidioc handling
     74    ------------------------------------------------------------------*/
     75 static int myvivi_videoc_querycap(struct file *file, void  *priv,
     76                     struct v4l2_capability *cap)
     77 {
     78     //VIDIOC_QUERYCAP 命令通过结构 v4l2_capability 获取设备支持的操作模式:
     79     /*
     80         struct v4l2_capability {
     81         __u8    driver[16];         i.e. "bttv" 
     82         __u8    card[32];        i.e. "Hauppauge WinTV"
     83         __u8    bus_info[32];    "PCI:" + pci_name(pci_dev) 
     84         __u32   version;        should use KERNEL_VERSION() 
     85         __u32    capabilities;     Device capabilities 
     86         __u32    reserved[4];
     87         };
     88      * 
     89      */
     90     strcpy(cap->driver, "myvivi");
     91     strcpy(cap->card, "myvivi");
     92     
     93     cap->version = 0x0001;
     94     cap->capabilities =    V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;        //V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING表示一个视
     95                                                                             //频捕捉设备并且具有数据流控制模式
     96     
     97     return 0;
     98 }
     99 
    100 /* 用于列举、获得、测试、设置摄像头的数据的格式 */
    101 /* 列举支持哪种格式 */
    102 static int myvivi_vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
    103                     struct v4l2_fmtdesc *f)
    104 {
    105     /*
    106     *    F O R M A T   E N U M E R A T I O N
    107     
    108     struct v4l2_fmtdesc {
    109         __u32            index;             // Format number      , 需要填充,从0开始,依次上升。
    110         enum v4l2_buf_type  type;          // buffer type       Camera,则填写V4L2_BUF_TYPE_VIDEO_CAPTURE
    111         __u32               flags;            //    如果压缩的,则Driver 填写:V4L2_FMT_FLAG_COMPRESSED,
    112         __u8            description[32];   // Description string ,image format的描述,如:YUV 4:2:2 (YUYV)
    113         __u32            pixelformat;       // Format fourcc ,所支持的格式。 如:V4L2_PIX_FMT_UYVY    
    114         __u32            reserved[4];
    115     };
    116     */
    117 
    118     if(f->index >= 1)
    119     {
    120         return -EINVAL;
    121     }
    122     strcpy(f->description, "4:2:2, packed, YUYV");
    123     
    124     //从vivi_fmt结构体中可以看见:
    125     f->pixelformat = V4L2_PIX_FMT_YUYV;
    126     
    127     return 0;
    128 }
    129 struct v4l2_format myvivi_format;
    130 /* 返回当前所使用的格式 */
    131 static int myvivi_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
    132                     struct v4l2_format *f)
    133 {
    134     
    135     memcpy(f, &myvivi_format, sizeof(myvivi_format));
    136 
    137     return (0);
    138 }
    139 
    140 /* 测试驱动程序是否支持某种格式 */
    141 static int myvivi_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
    142             struct v4l2_format *f)
    143 {
    144     unsigned int maxw, maxh;
    145     enum v4l2_field field;
    146     
    147     
    148     if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV)
    149         return -EINVAL;
    150 
    151     field = f->fmt.pix.field;
    152 
    153     if (field == V4L2_FIELD_ANY) {
    154         field = V4L2_FIELD_INTERLACED;
    155     } else if (V4L2_FIELD_INTERLACED != field) {
    156         return -EINVAL;
    157     }
    158     
    159     maxw = 1024;
    160     maxh = 768;
    161     
    162     /*
    163      v4l2_format:
    164      struct v4l2_format {  
    165         enum v4l2_buf_type type;  
    166         union {  
    167         struct v4l2_pix_format         pix;     // V4L2_BUF_TYPE_VIDEO_CAPTURE 
    168         struct v4l2_window             win;     // V4L2_BUF_TYPE_VIDEO_OVERLAY   
    169         struct v4l2_vbi_format         vbi;     // V4L2_BUF_TYPE_VBI_CAPTURE 
    170         struct v4l2_sliced_vbi_format  sliced;  // V4L2_BUF_TYPE_SLICED_VBI_CAPTURE 
    171         __u8   raw_data[200];                   // user-defined   
    172     } fmt;  
    173     };
    174 
    175     其中  
    176     enum v4l2_buf_type 
    177     {  
    178         V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,  
    179         V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,  
    180         V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,  
    181         ...  
    182         V4L2_BUF_TYPE_PRIVATE              = 0x80,  
    183     }; 
    184     
    185         struct v4l2_pix_format {  
    186         __u32                   width;  
    187         __u32                   height;  
    188         __u32                   pixelformat;  
    189         enum v4l2_field         field;  
    190         __u32                   bytesperline;   // for padding, zero if unused 
    191         __u32                   sizeimage;  
    192         enum v4l2_colorspace    colorspace;  
    193         __u32                   priv;           // private data, depends on pixelformat 
    194     };  
    195         
    196     常见的捕获模式为 V4L2_BUF_TYPE_VIDEO_CAPTURE 即视频捕捉模式,在此模式下 fmt 联合体采用域 v4l2_pix_format:其中 width 为
    197     视频的宽、height 为视频的高、pixelformat 为视频数据格式(常见的值有 V4L2_PIX_FMT_YUV422P | V4L2_PIX_FMT_RGB565)、
    198     bytesperline 为一行图像占用的字节数、sizeimage 则为图像占用的总字节数、colorspace 指定设备的颜色空间。     
    199     */    
    200     //设置最小宽度和最大宽度等
    201     /* 调整format的width, height, 
    202      * 计算bytesperline, sizeimage
    203      */
    204     v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, &f->fmt.pix.height, 32, maxh, 0, 0);
    205     
    206     f->fmt.pix.bytesperline =
    207         (f->fmt.pix.width * 16) >> 3;        //颜色深度支持16
    208     f->fmt.pix.sizeimage =
    209         f->fmt.pix.height * f->fmt.pix.bytesperline;
    210     
    211     
    212     return 0;
    213 }
    214 
    215 
    216 static int myvivi_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
    217                     struct v4l2_format *f)
    218 {
    219     int ret = myvivi_vidioc_try_fmt_vid_cap(file, NULL, f);
    220     if (ret < 0)
    221         return ret;
    222 
    223     memcpy(&myvivi_format, f, sizeof(myvivi_format));
    224     
    225     return ret;
    226 }
    227 /* 用于列举、获得、测试、设置摄像头的数据的格式 */
    228 
    229 
    230 
    231 
    232 /* 队列操作1: 定义 */
    233 static struct videobuf_queue myvivi_vb_vidqueue;
    234 //自旋锁
    235 static spinlock_t myvivi_queue_slock;
    236 
    237 
    238 /* 参考documentations/video4linux/v4l2-framework.txt:
    239  *     driversmediavideovideobuf-core.c 
    240  ops->buf_setup   - calculates the size of the video buffers and avoid they
    241             to waste more than some maximum limit of RAM;
    242  ops->buf_prepare - fills the video buffer structs and calls
    243             videobuf_iolock() to alloc and prepare mmaped memory;
    244  ops->buf_queue   - advices the driver that another buffer were
    245             requested (by read() or by QBUF);
    246  ops->buf_release - frees any buffer that were allocated.
    247  
    248  *
    249  */
    250 
    251  
    252  
    253  
    254 /*****************buffer operations***********************/
    255 /* APP调用ioctl VIDIOC_REQBUFS(videobuf_reqbufs)时会导致此函数被调用,
    256  * 它重新调整count和size
    257  */
    258  static int myvivi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
    259 {
    260     //第一个参数为
    261     *size = myvivi_format.fmt.pix.sizeimage;
    262 
    263     if (0 == *count)
    264         *count = 32;
    265 
    266     return 0;
    267 }
    268 /* APP调用ioctl VIDIOC_QBUF时导致此函数被调用,
    269  * 它会填充video_buffer结构体并调用videobuf_iolock来分配内存
    270  * 
    271  */
    272 static int myvivi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
    273                         enum v4l2_field field)
    274 {
    275     /* 1. 做些准备工作 */
    276      /* 0. 设置videobuf */
    277     vb->size = myvivi_format.fmt.pix.sizeimage;
    278     vb->bytesperline = myvivi_format.fmt.pix.bytesperline;
    279     vb->width  = myvivi_format.fmt.pix.width;
    280     vb->height = myvivi_format.fmt.pix.height;
    281     vb->field  = field;
    282 #if 0
    283     /* 2. 调用videobuf_iolock为类型为V4L2_MEMORY_USERPTR的videobuf分配内存 */
    284     if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
    285         rc = videobuf_iolock(vq, &buf->vb, NULL);
    286         if (rc < 0)
    287             goto fail;
    288     }
    289 #endif
    290     /* 3. 设置状态 */
    291     vb->state = VIDEOBUF_PREPARED;
    292 
    293     return 0;
    294 }
    295 /* APP调用ioctlVIDIOC_QBUF时:
    296  * 1. 先调用buf_prepare进行一些准备工作
    297  * 2. 把buf放入队列
    298  * 3. 调用buf_queue(起通知作用)通知应用程序正在请求调用
    299  */
    300 static void myvivi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
    301 {
    302     vb->state = VIDEOBUF_QUEUED;
    303     
    304     /* 把videobuf放入本地一个队列尾部
    305      * 定时器处理函数就可以从本地队列取出videobuf,新增内核链表
    306      */
    307     list_add_tail(&vb->queue, &myvivi_vb_local_queue);
    308 }
    309 /* APP不再使用队列时, 用它来释放内存 */
    310 static void myvivi_buffer_release(struct videobuf_queue *vq,
    311                struct videobuf_buffer *vb)
    312 {
    313     videobuf_vmalloc_free(vb);
    314     vb->state = VIDEOBUF_NEEDS_INIT;
    315 }
    316 /*****************buffer operations***********************/
    317 
    318 
    319 
    320 /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
    321 static int myvivi_vidioc_reqbufs(struct file *file, void *priv,
    322               struct v4l2_requestbuffers *p)
    323 {
    324     return (videobuf_reqbufs(&myvivi_vb_vidqueue, p));
    325 }
    326 static int myvivi_vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
    327 {
    328     return (videobuf_querybuf(&myvivi_vb_vidqueue, p));
    329 }
    330 static int myvivi_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
    331 {
    332     return (videobuf_qbuf(&myvivi_vb_vidqueue, p));
    333 }
    334 static int myvivi_vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
    335 {
    336     return (videobuf_dqbuf(&myvivi_vb_vidqueue, p,
    337                 file->f_flags & O_NONBLOCK));
    338 }
    339 /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
    340 /*------------------------------------------------------------------*/
    341 
    342 
    343 /*-------------------开启与关闭--------------------------*/
    344 static int myvivi_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
    345 {
    346     return videobuf_streamon(&myvivi_vb_vidqueue);
    347 }
    348 
    349 static int myvivi_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
    350 {
    351     videobuf_streamoff(&myvivi_vb_vidqueue);
    352     return 0;
    353 }
    354 /*-------------------开启与关闭--------------------------*/
    355 
    356 
    357 
    358 
    359 /* ------------------------------------------------------------------
    360     Videobuf operations
    361    ------------------------------------------------------------------*/
    362 static struct videobuf_queue_ops myvivi_video_qops = {
    363     .buf_setup      = myvivi_buffer_setup, /* 计算大小以免浪费 */
    364     .buf_prepare    = myvivi_buffer_prepare,
    365     .buf_queue      = myvivi_buffer_queue,
    366     .buf_release    = myvivi_buffer_release,
    367 };
    368 /* ------------------------------------------------------------------
    369     File operations for the device
    370    ------------------------------------------------------------------*/
    371 static int myvivi_open(struct file *file)
    372 {
    373     /* 队列操作2: 初始化 */
    374     videobuf_queue_vmalloc_init(&myvivi_vb_vidqueue, &myvivi_video_qops,
    375             NULL, &myvivi_queue_slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED,
    376             sizeof(struct videobuf_buffer), NULL); /* V4L2_BUF_TYPE_VIDEO_CAPTURE用于视频捕获设备,倒数第2个参数是buffer的头部大小 */
    377     //myvivi_video_qops这个结构体我们怎么去处理它呢?
    378     
    379     
    380     myvivi_timer.expires = jiffies + 1;
    381     add_timer(&myvivi_timer);
    382     return 0;
    383 }
    384 static int myvivi_close(struct file *file)
    385 {
    386     del_timer(&myvivi_timer);
    387     videobuf_stop(&myvivi_vb_vidqueue);        //stop the buf
    388     videobuf_mmap_free(&myvivi_vb_vidqueue);
    389     
    390     return 0;
    391 }
    392 static int myvivi_mmap(struct file *file, struct vm_area_struct *vma)
    393 {
    394     return videobuf_mmap_mapper(&myvivi_vb_vidqueue, vma);
    395 }
    396 static unsigned int myvivi_poll(struct file *file, struct poll_table_struct *wait)
    397 {
    398     return videobuf_poll_stream(file, &myvivi_vb_vidqueue, wait);
    399 }
    400 /*------------------------------------------------------------------*/
    401 
    402 
    403 
    404 
    405 
    406 
    407 
    408 /*
    409 ------------------------------------------------------------
    410 struct video_device
    411     // device ops 
    412     const struct v4l2_file_operations *fops;
    413 ------------------------------------------------------------
    414                 v4l2_file_operations=
    415                 {
    416                     struct module *owner;
    417                     long (*ioctl) (struct file *, unsigned int, unsigned long);
    418                 }
    419 
    420 
    421 
    422 ------------------------------------------------------------
    423     // callbacks 
    424     void (*release)(struct video_device *vdev);
    425 ------------------------------------------------------------
    426 */
    427 static const struct v4l2_ioctl_ops myvivi_ioctl_ops = 
    428 {
    429     //表示它是一个摄像头驱动
    430     .vidioc_querycap = myvivi_videoc_querycap,
    431     
    432 
    433     /* 用于列举、获得、测试、设置摄像头的数据的格式 */
    434     .vidioc_enum_fmt_vid_cap  = myvivi_vidioc_enum_fmt_vid_cap,
    435     .vidioc_g_fmt_vid_cap     = myvivi_vidioc_g_fmt_vid_cap,
    436     .vidioc_try_fmt_vid_cap   = myvivi_vidioc_try_fmt_vid_cap,
    437     .vidioc_s_fmt_vid_cap     = myvivi_vidioc_s_fmt_vid_cap,
    438     
    439     /* 缓冲区操作: 申请/查询/放入队列/取出队列 */
    440     .vidioc_reqbufs       = myvivi_vidioc_reqbufs,
    441     .vidioc_querybuf      = myvivi_vidioc_querybuf,
    442     .vidioc_qbuf          = myvivi_vidioc_qbuf,
    443     .vidioc_dqbuf         = myvivi_vidioc_dqbuf,
    444     
    445     // 启动/停止
    446     .vidioc_streamon      = myvivi_vidioc_streamon,
    447     .vidioc_streamoff     = myvivi_vidioc_streamoff,  
    448 
    449 };
    450 
    451 
    452 static const struct v4l2_file_operations myvivi_fops = 
    453 {
    454     .owner = THIS_MODULE,
    455     .open = myvivi_open,
    456     .release = myvivi_close,
    457     .mmap = myvivi_mmap,
    458     .poll = myvivi_poll,        //提供了poll函数,还是会出现select timeout的情况,需要构造数据唤醒队列了,在poll_wait队列中休眠了
    459     .ioctl = video_ioctl2, /* v4l2 ioctl handler,在这个函数中设置了ioctl的用法 */
    460 };
    461 static void myvivi_release(struct video_device *vdev)
    462 {
    463     
    464 }
    465 
    466 static int myvivi_init(void)
    467 {
    468     int error=0;
    469     
    470     /* 1. 分配一个video_device函数,分配空间 */
    471     myvivi_device = video_device_alloc();
    472     
    473     /* 2. 设置 */
    474     
    475     /* 2.1 */
    476     myvivi_device->release = myvivi_release;
    477     /* 2.2 */
    478     myvivi_device->fops    = &myvivi_fops;
    479     /* 2.3 */
    480     myvivi_device->ioctl_ops = &myvivi_ioctl_ops;
    481 
    482     
    483     /* 2.4 队列操作
    484     * a.定义/初始化一个队列(会用到一个自旋锁)
    485     */
    486     spin_lock_init(&myvivi_queue_slock);
    487     
    488     /*
    489     #define VFL_TYPE_GRABBER    0        //表明是一个图像采集设备-包括摄像头、调谐器
    490     #define VFL_TYPE_VBI        1        //从视频消隐的时间段取得信息的设备
    491     #define VFL_TYPE_RADIO        2        //代表无线电设备
    492     #define VFL_TYPE_VTX        3        //代表视传设备
    493     #define VFL_TYPE_MAX        4
    494     @nr:   which device number (0 == /dev/video0, 1 == /dev/video1, ...
    495              -1 == first free)
    496     */
    497     /* 3.注册,第二个参数为VFL_TYPE_GRABBER, -1 为 first free */
    498     error = video_register_device(myvivi_device, VFL_TYPE_GRABBER, -1);
    499 
    500     
    501     //用定时器产生数据并唤醒进程
    502     init_timer(&myvivi_timer);
    503     myvivi_timer.function = myvivi_timer_function;
    504     
    505     
    506     INIT_LIST_HEAD(&myvivi_vb_local_queue);
    507     return error;
    508 };
    509 
    510 
    511 /*
    512  *    出口函数
    513  * 
    514  */
    515 static void myvivi_exit(void)
    516 {
    517     video_unregister_device(myvivi_device);
    518     video_device_release(myvivi_device);
    519 }
    520 
    521 
    522 module_init(myvivi_init);
    523 module_exit(myvivi_exit);
    524 MODULE_LICENSE("GPL");

    二、虚拟摄像头驱动应用程序调用过程流程图:

    虚拟摄像头一般不用自己写的程序,而是采用网络上提供的应用程序直接使用的xawtv、webcam、spcaview、luvcview;我们是采用xawtv的方式来调用一个虚拟摄像头的驱动程序,代码流程如下分析,以上驱动代码一一对应:

    git ssh路径:git@github.com:lh233/USB_CAMERA.git

  • 相关阅读:
    centos8 安装vmware需要的内核头文件 kernel-headers.
    centos7开启ssh服务
    systemctl命令的使用及服务状态的查看
    centos WPS 字体安装
    CentOS8 使用 aliyun 阿里云 镜像站点的方法
    CentOS提示::unknown filesystem type 'ntfs'自动挂载NTFS分区的U盘或者移动硬盘
    Aria2 Centos8 安装配置
    centos7 更新Firefox版本
    线程内容详解
    进程池、进程池和多进程的性能测试、进程池的其他机制、进程池的回调函数
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/7356839.html
Copyright © 2011-2022 走看看