一、vivi虛擬攝像頭驅動 基於V4L2(video for linux 2)攝像頭驅動程式,我們減去不需要的ioctl_fops的函數,只增加ioctl函數增加的必要的攝像頭流查詢等函數; 二、虛擬攝像頭驅動應用程式調用過程流程圖: 虛擬攝像頭一般不用自己寫的程式,而是採用網路上提供的應用程式直接 ...
一、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);