vivi虚拟摄像头驱动程序

时间:2021-03-05 23:34:27

一、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 * drivers\media\video\videobuf-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的方式来调用一个虚拟摄像头的驱动程序,代码流程如下分析,以上驱动代码一一对应:

vivi虚拟摄像头驱动程序

 

 

git ssh路径:git@git.oschina.net:linhao123/USB_CAMERA.git

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