你的位置:首页 > 操作系统

[操作系统]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 N107   108   struct v4l2_fmtdesc {109     __u32      index;       // Format number   , 需要填充,从0开始,依次上升。110     enum v4l2_buf_type type;     // buffer type    Camera,则填写V4L2_BUF_TYPE_VIDEO_CAPTURE111     __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, sizeimage203   */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;    //颜色深度支持16208   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  *   drivers\media\video\videobuf-core.c 240  ops->buf_setup  - calculates the size of the video buffers and avoid they241       to waste more than some maximum limit of RAM;242  ops->buf_prepare - fills the video buffer structs and calls243       videobuf_iolock() to alloc and prepare mmaped memory;244  ops->buf_queue  - advices the driver that another buffer were245       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和size257 */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 0283   /* 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 #endif290   /* 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 operations361   ------------------------------------------------------------------*/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 device370   ------------------------------------------------------------------*/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 buf388   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_device411   // 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    4494   @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@git.oschina.net:linhao123/USB_CAMERA.git

   http路径:https://git.oschina.net/linhao123/USB_CAMERA.git