概述

前面简单分析了内核中虚拟摄像头驱动 vivi 的框架与实现,本文参考 vivi 来写一个虚拟摄像头驱动,查询、设置视频格式相对简单,难点在于 vb2_buf 的处理过程。

数据采集流程分析

在我的程序中,大概的数据采集流程如上图所示,启动视频采集之后,创建了一个内核线程,内核线程每30ms 唤醒一次,每一次唤醒都会尝试用 queue_list 中取出一个 buffer 填充数据之后挂入 done_list ,挂入 done_list 之后就会唤醒应用程序(poll 中休眠),应用程序唤醒之后就会 dqbuf 获取数据,处理完数据再 qbuf 把 buffer 挂入 queue_list 的头部,一直循环。

代码

  1. #include <linux/module.h>
  2. #include <linux/errno.h>
  3. #include <linux/kernel.h>
  4. #include <linux/init.h>
  5. #include <linux/sched.h>
  6. #include <linux/slab.h>
  7. #include <linux/font.h>
  8. #include <linux/mutex.h>
  9. #include <linux/videodev2.h>
  10. #include <linux/kthread.h>
  11. #include <linux/freezer.h>
  12. #include <media/videobuf2-vmalloc.h>
  13. #include <media/v4l2-device.h>
  14. #include <media/v4l2-ioctl.h>
  15. #include <media/v4l2-ctrls.h>
  16. #include <media/v4l2-fh.h>
  17. #include <media/v4l2-event.h>
  18. #include <media/v4l2-common.h>
  19.  
  20. #define VFL_TYPE_GRABBER 0
  21.  
  22. #define MAX_WIDTH 1920
  23. #define MAX_HEIGHT 1200
  24. static unsigned int vid_limit = ;
  25.  
  26. static struct video_device *video_dev; // video_device 结构,用来描述一个 video 设备
  27. static struct vb2_queue vivi_queue; // vivi_queue 用来存放缓冲区信息,缓冲区链表等
  28. struct task_struct *kthread; // 内核线程,用来向缓冲区中填充数据
  29. DECLARE_WAIT_QUEUE_HEAD(wait_queue_head); // 等待队列头
  30. struct list_head my_list; // 链表头
  31.  
  32. // 用来存放应用程序设置的视频格式
  33. static struct mformat {
  34. __u32 width;
  35. __u32 height;
  36. __u32 pixelsize;
  37. __u32 field;
  38. __u32 fourcc;
  39. __u32 depth;
  40. }mformat;
  41.  
  42. static void mvideo_device_release(struct video_device *vdev)
  43. {
  44.  
  45. }
  46.  
  47. static long mvideo_ioctl(struct file *file, unsigned int cmd, void *arg)
  48. {
  49. int ret = ;
  50. struct v4l2_fh *fh = NULL;
  51. switch (cmd) {
  52.  
  53. case VIDIOC_QUERYCAP:
  54. {
  55. struct v4l2_capability *cap = (struct v4l2_capability *)arg;
  56. cap->version = LINUX_VERSION_CODE;
  57. ret = video_dev->ioctl_ops->vidioc_querycap(file, NULL, cap);
  58. break;
  59. }
  60. case VIDIOC_ENUM_FMT:
  61. {
  62. struct v4l2_fmtdesc *f = arg;
  63. if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
  64. ret = video_dev->ioctl_ops->vidioc_enum_fmt_vid_cap(file, fh, f);
  65. }else{
  66. printk("V4L2_BUF_TYPE_VIDEO_CAPTURE error\n");
  67. }
  68. break;
  69. }
  70. case VIDIOC_G_FMT:
  71. {
  72. struct v4l2_format *f = (struct v4l2_format *)arg;
  73. if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
  74. ret = video_dev->ioctl_ops->vidioc_g_fmt_vid_cap(file, fh, f);
  75. }else{
  76. printk("V4L2_BUF_TYPE_VIDEO_CAPTURE error\n");
  77. }
  78. break;
  79. }
  80. case VIDIOC_TRY_FMT:
  81. {
  82. struct v4l2_format *f = (struct v4l2_format *)arg;
  83. if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
  84. ret = video_dev->ioctl_ops->vidioc_try_fmt_vid_cap(file, fh, f);
  85. }else{
  86. printk("V4L2_BUF_TYPE_VIDEO_CAPTURE error\n");
  87. }
  88. break;
  89. }
  90. case VIDIOC_S_FMT:
  91. {
  92. struct v4l2_format *f = (struct v4l2_format *)arg;
  93. if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
  94. video_dev->ioctl_ops->vidioc_s_fmt_vid_cap(file, fh, f);
  95. }else{
  96. printk("V4L2_BUF_TYPE_VIDEO_CAPTURE error\n");
  97. }
  98. break;
  99. }
  100. case VIDIOC_REQBUFS:
  101. {
  102. struct v4l2_requestbuffers *p = arg;
  103. ret = video_dev->ioctl_ops->vidioc_reqbufs(file, fh, p);
  104. break;
  105. }
  106. case VIDIOC_QUERYBUF:
  107. {
  108. struct v4l2_buffer *p = arg;
  109. ret = video_dev->ioctl_ops->vidioc_querybuf(file, fh, p);
  110. break;
  111. }
  112. case VIDIOC_QBUF:
  113. {
  114. struct v4l2_buffer *p = arg;
  115. ret = video_dev->ioctl_ops->vidioc_qbuf(file, fh, p);
  116. break;
  117. }
  118. case VIDIOC_DQBUF:
  119. {
  120. struct v4l2_buffer *p = arg;
  121. ret = video_dev->ioctl_ops->vidioc_dqbuf(file, fh, p);
  122. break;
  123. }
  124. case VIDIOC_STREAMON:
  125. {
  126. enum v4l2_buf_type i = *(int *)arg;
  127. ret = video_dev->ioctl_ops->vidioc_streamon(file, fh, i);
  128. break;
  129. }
  130. case VIDIOC_STREAMOFF:
  131. {
  132. enum v4l2_buf_type i = *(int *)arg;
  133. ret = video_dev->ioctl_ops->vidioc_streamoff(file, fh, i);
  134. break;
  135. }
  136. }
  137. return ret;
  138. }
  139.  
  140. static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
  141. {
  142. int ret;
  143. printk("enter mmap\n");
  144. ret = vb2_mmap(&vivi_queue, vma);
  145. if(ret == ){
  146. printk("mmap ok\n");
  147. }else{
  148. printk("mmap error\n");
  149. }
  150. return ret;
  151. }
  152.  
  153. // 查询设备能力
  154. static int mvidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
  155. {
  156. strcpy(cap->driver, "vivi");
  157. strcpy(cap->card, "vivi");
  158. strcpy(cap->bus_info, "mvivi");
  159. cap->device_caps = V4L2_CAP_VIDEO_CAPTURE;
  160. cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING;
  161. printk("mvidioc_querycap \n");
  162. return ;
  163. }
  164.  
  165. // 枚举视频支持的格式
  166. static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f)
  167. {
  168. printk("vidioc_enum_fmt_vid_cap \n");
  169. if (f->index >= )
  170. return -EINVAL;
  171.  
  172. strcpy(f->description, "mvivi");
  173. f->pixelformat = mformat.fourcc;
  174. printk("vidioc_enum_fmt_vid_cap \n");
  175. return ;
  176. }
  177.  
  178. // 修正应用层传入的视频格式
  179. static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
  180. {
  181. printk("vidioc_try_fmt_vid_cap\n");
  182. if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) {
  183. return -EINVAL;
  184. }
  185.  
  186. f->fmt.pix.field = V4L2_FIELD_INTERLACED;
  187. v4l_bound_align_image(&f->fmt.pix.width, , MAX_WIDTH, ,
  188. &f->fmt.pix.height, , MAX_HEIGHT, , );
  189. f->fmt.pix.bytesperline = (f->fmt.pix.width * mformat.depth) / ;
  190. f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
  191. if (mformat.fourcc == V4L2_PIX_FMT_YUYV)
  192. f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
  193. else
  194. f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
  195. return ;
  196. }
  197.  
  198. // 获取支持的格式
  199. static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
  200. {
  201. // 将参数写回用户空间
  202. f->fmt.pix.width = mformat.width;
  203. f->fmt.pix.height = mformat.height;
  204. f->fmt.pix.field = mformat.field;
  205. f->fmt.pix.pixelformat = mformat.fourcc;
  206. f->fmt.pix.bytesperline = (f->fmt.pix.width * mformat.depth) / ;
  207. f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
  208. if (mformat.fourcc == V4L2_PIX_FMT_YUYV)
  209. f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
  210. else
  211. f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
  212. printk("vidioc_g_fmt_vid_cap \n");
  213. return ;
  214. }
  215.  
  216. // 设置视频格式
  217. static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
  218. {
  219. int ret = vidioc_try_fmt_vid_cap(file, priv, f);
  220. if (ret < )
  221. return ret;
  222. // 存储用户空间传入的参数设置
  223. mformat.fourcc = V4L2_PIX_FMT_YUYV;
  224. mformat.pixelsize = mformat.depth / ;
  225. mformat.width = f->fmt.pix.width;
  226. mformat.height = f->fmt.pix.height;
  227. mformat.field = f->fmt.pix.field;
  228. printk("vidioc_s_fmt_vid_capp \n");
  229. return ;
  230. }
  231.  
  232. // vb2 核心层 vb2_reqbufs 中调用它,确定申请缓冲区的大小
  233. static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
  234. unsigned int *nbuffers, unsigned int *nplanes,
  235. unsigned int sizes[], void *alloc_ctxs[])
  236. {
  237. unsigned long size;
  238. printk("mformat.width %d \n",mformat.width);
  239. printk("mformat.height %d \n",mformat.height);
  240. printk("mformat.pixelsize %d \n",mformat.pixelsize);
  241.  
  242. size = mformat.width * mformat.height * mformat.pixelsize;
  243.  
  244. if ( == *nbuffers)
  245. *nbuffers = ;
  246.  
  247. while (size * *nbuffers > vid_limit * * )
  248. (*nbuffers)--;
  249.  
  250. *nplanes = ;
  251.  
  252. sizes[] = size;
  253. return ;
  254. }
  255.  
  256. static int buffer_init(struct vb2_buffer *vb)
  257. {
  258. return ;
  259. }
  260.  
  261. static int buffer_finish(struct vb2_buffer *vb)
  262. {
  263. return ;
  264. }
  265.  
  266. static int buffer_prepare(struct vb2_buffer *vb)
  267. {
  268. unsigned long size;
  269. size = mformat.width * mformat.height * mformat.pixelsize;
  270. vb2_plane_size(vb, );
  271. //vb2_set_plane_payload(&buf->vb, 0, size);
  272. return ;
  273. }
  274.  
  275. static void buffer_queue(struct vb2_buffer *vb)
  276. {
  277.  
  278. }
  279.  
  280. // 内核线程中填充数据,效果是一个逐渐放大的圆形,视频大小为 640 * 480
  281. static void vivi_fillbuff(struct vb2_buffer *vb)
  282. {
  283. void *vbuf = NULL;
  284. unsigned char (*p)[mformat.width][mformat.pixelsize];
  285. unsigned int i,j;
  286. vbuf = vb2_plane_vaddr(vb, );
  287. static unsigned int t = ;
  288. p = vbuf;
  289.  
  290. for(j = ; j < mformat.height; j++)
  291. for(i = ; i < mformat.width; i++){
  292. if((j - )*(j - ) + (i - )*(i - ) < (t * t)){
  293. *(*(*(p+j)+i)+) = (unsigned char)0xff;
  294. *(*(*(p+j)+i)+) = (unsigned char)0xff;
  295. }else{
  296. *(*(*(p+j)+i)+) = (unsigned char);
  297. *(*(*(p+j)+i)+) = (unsigned char);
  298. }
  299. }
  300. t++;
  301. printk("%d\n",t);
  302. if( t >= mformat.height/) t = ;
  303. }
  304.  
  305. // 内核线程每一次唤醒调用它
  306. static void vivi_thread_tick(void)
  307. {
  308. struct vb2_buffer *buf = NULL;
  309. struct list_head *list;
  310. struct vb2_buffer *task;
  311. unsigned long flags;
  312. if (list_empty(&vivi_queue.queued_list)) {
  313. //printk("No active queue to serve\n");
  314. return;
  315. }
  316. // 注意我们这里取出之后就删除了,剩的重复工作,但是在 dqbuf 时,vb2_dqbuf 还会删除一次,我做的处理是在dqbuf之前将buf随便挂入一个链表
  317. buf = list_entry(vivi_queue.queued_list.next, struct vb2_buffer, queued_entry);
  318. list_del(&buf->queued_entry);
  319.  
  320. /* 填充数据 */
  321. vivi_fillbuff(buf);
  322. printk("filled buffer %p\n", buf->planes[].mem_priv);
  323.  
  324. // 它干两个工作,把buffer 挂入done_list 另一个唤醒应用层序,让它dqbuf
  325. vb2_buffer_done(buf, VB2_BUF_STATE_DONE);
  326. }
  327.  
  328. #define WAKE_NUMERATOR 30
  329. #define WAKE_DENOMINATOR 1001
  330. #define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
  331. #define frames_to_ms(frames) \
  332. ((frames * WAKE_NUMERATOR * ) / WAKE_DENOMINATOR)
  333.  
  334. static void vivi_sleep(void)
  335. {
  336. int timeout;
  337. DECLARE_WAITQUEUE(wait, current);
  338.  
  339. add_wait_queue(&wait_queue_head, &wait);
  340. if (kthread_should_stop())
  341. goto stop_task;
  342.  
  343. /* Calculate time to wake up */
  344. timeout = msecs_to_jiffies(frames_to_ms());
  345.  
  346. vivi_thread_tick();
  347.  
  348. schedule_timeout_interruptible(timeout);
  349.  
  350. stop_task:
  351. remove_wait_queue(&wait_queue_head, &wait);
  352. try_to_freeze();
  353. }
  354.  
  355. static int vivi_thread(void *data)
  356. {
  357. set_freezable();
  358.  
  359. for (;;) {
  360. vivi_sleep();
  361.  
  362. if (kthread_should_stop())
  363. break;
  364. }
  365. printk("thread: exit\n");
  366. return ;
  367. }
  368.  
  369. static int vivi_start_generating(void)
  370. {
  371. kthread = kthread_run(vivi_thread, video_dev, video_dev->name);
  372.  
  373. if (IS_ERR(kthread)) {
  374. printk("kthread_run error\n");
  375. return PTR_ERR(kthread);
  376. }
  377.  
  378. /* Wakes thread */
  379. wake_up_interruptible(&wait_queue_head);
  380.  
  381. return ;
  382. }
  383.  
  384. static int start_streaming(struct vb2_queue *vq, unsigned int count)
  385. {
  386. vivi_start_generating();
  387. return ;
  388. }
  389. static int stop_streaming(struct vb2_queue *vq)
  390. {
  391. if (kthread) {
  392. kthread_stop(kthread);
  393. kthread = NULL;
  394. }
  395. /*
  396.  
  397. while (!list_empty(&vivi_queue.queued_list)) {
  398. struct vb2_buffer *buf;
  399. buf = list_entry(vivi_queue.queued_list.next, struct vb2_buffer, queued_entry);
  400. list_del(&buf->queued_entry);
  401. vb2_buffer_done(buf, VB2_BUF_STATE_ERROR);
  402. }
  403. */
  404. return ;
  405. }
  406. static struct vb2_ops vivi_video_qops = {
  407. .queue_setup = queue_setup,
  408. .buf_init = buffer_init,
  409. .buf_finish = buffer_finish,
  410. .buf_prepare = buffer_prepare,
  411. .buf_queue = buffer_queue,
  412. .start_streaming = start_streaming,
  413. .stop_streaming = stop_streaming,
  414. };
  415.  
  416. static int mvivi_open(struct file *filp)
  417. {
  418. vivi_queue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  419. vivi_queue.io_modes = VB2_MMAP;
  420. vivi_queue.drv_priv = video_dev;
  421. //vivi_queue.buf_struct_size = sizeof(struct vivi_buffer);
  422. vivi_queue.ops = &vivi_video_qops;
  423. vivi_queue.mem_ops = &vb2_vmalloc_memops;
  424. vivi_queue.name = "vb2";
  425. vivi_queue.buf_struct_size = sizeof(struct vb2_buffer);
  426. INIT_LIST_HEAD(&vivi_queue.queued_list);
  427. INIT_LIST_HEAD(&vivi_queue.done_list);
  428. spin_lock_init(&vivi_queue.done_lock);
  429. init_waitqueue_head(&vivi_queue.done_wq);
  430. mformat.fourcc = V4L2_PIX_FMT_YUYV;
  431. mformat.depth = ;
  432. INIT_LIST_HEAD(&my_list);
  433. return ;
  434. }
  435.  
  436. static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p)
  437. {
  438. printk("vidioc_reqbufs \n");
  439. printk("count %d\n",p->count);
  440. printk("memory %d\n",p->memory);
  441. return vb2_reqbufs(&vivi_queue, p);
  442. }
  443.  
  444. static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
  445. {
  446. printk("vidioc_querybuf \n");
  447. return vb2_querybuf(&vivi_queue, p);
  448. }
  449.  
  450. static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
  451. {
  452. printk("vidioc_qbuf buffer \n");
  453. return vb2_qbuf(&vivi_queue, p);
  454. }
  455.  
  456. static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
  457. {
  458. printk("vidioc_dqbuf buffer \n");
  459. struct vb2_buffer *vb;
  460. vb = list_first_entry(&vivi_queue.done_list, struct vb2_buffer, done_entry);
  461. list_add_tail(&vb->queued_entry, &my_list);
  462. return vb2_dqbuf(&vivi_queue, p, file->f_flags & O_NONBLOCK);
  463. }
  464.  
  465. static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
  466. {
  467. printk("vidioc_streamon \n");
  468. return vb2_streamon(&vivi_queue, i);
  469. }
  470.  
  471. static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
  472. {
  473. printk("vidioc_streamoff \n");
  474. return vb2_streamoff(&vivi_queue, i);
  475. }
  476.  
  477. static struct v4l2_ioctl_ops mvivi_ioctl_ops = {
  478. .vidioc_querycap = mvidioc_querycap,
  479. .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
  480. .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
  481. .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
  482. .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
  483. .vidioc_reqbufs = vidioc_reqbufs,
  484. .vidioc_querybuf = vidioc_querybuf,
  485. .vidioc_qbuf = vidioc_qbuf,
  486. .vidioc_dqbuf = vidioc_dqbuf,
  487. .vidioc_streamon = vidioc_streamon,
  488. .vidioc_streamoff = vidioc_streamoff,
  489. };
  490.  
  491. static unsigned int mvivi_poll(struct file *file, struct poll_table_struct *wait)
  492. {
  493. struct vb2_buffer *vb = NULL;
  494. int res = ;
  495. printk("enter the poll \n");
  496.  
  497. poll_wait(file, &vivi_queue.done_wq, wait);
  498.  
  499. if (!list_empty(&vivi_queue.done_list))
  500. vb = list_first_entry(&vivi_queue.done_list, struct vb2_buffer, done_entry);
  501.  
  502. if (vb && (vb->state == VB2_BUF_STATE_DONE || vb->state == VB2_BUF_STATE_ERROR)) {
  503. return (V4L2_TYPE_IS_OUTPUT(vivi_queue.type)) ?
  504. res | POLLOUT | POLLWRNORM :
  505. res | POLLIN | POLLRDNORM;
  506. }
  507. return ;
  508. }
  509.  
  510. long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg)
  511. {
  512. return video_usercopy(file, cmd, arg, mvideo_ioctl);
  513. }
  514.  
  515. static struct v4l2_file_operations mvivi_fops = {
  516. .owner = THIS_MODULE,
  517. .open = mvivi_open,
  518. .unlocked_ioctl = video_ioctl2,
  519.  
  520. //.release = mvivi_close,
  521. .poll = mvivi_poll,
  522. .mmap = vivi_mmap,
  523. };
  524.  
  525. static struct video_device vivi_template = {
  526. .name = "mvivi",
  527. .fops = &mvivi_fops,
  528. .ioctl_ops = &mvivi_ioctl_ops,
  529. .release = mvideo_device_release,
  530. };
  531.  
  532. static int mvivi_init(void)
  533. {
  534. int ret;
  535. video_dev = video_device_alloc();
  536. *video_dev = vivi_template;
  537. ret = video_register_device(video_dev, VFL_TYPE_GRABBER, -);
  538. if(ret != ){
  539. printk(" video_register_device error\n");
  540. }else{
  541. printk(" video_register_device ok\n");
  542. }
  543. return ret;
  544. }
  545.  
  546. static void mvivi_exit(void)
  547. {
  548. video_unregister_device(video_dev);
  549. }
  550.  
  551. module_init(mvivi_init);
  552. module_exit(mvivi_exit);
  553. MODULE_LICENSE("GPL");

V4L2学习(五)VIVI虚拟摄像头驱动的更多相关文章

  1. V4L2(二)虚拟摄像头驱动vivi深入分析【转】

    转自:http://www.cnblogs.com/tureno/articles/6694463.html 转载于: http://blog.csdn.net/lizuobin2/article/d ...

  2. vivi虚拟摄像头驱动程序

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

  3. 二十四、V4L2框架主要结构体分析和虚拟摄像头驱动编写

    一.V4L2框架主要结构体分析 V4L2(video for linux version 2),是内核中视频设备的驱动框架,为上层访问视频设备提供统一接口. V4L2整体框架如下图: 图中主要包括两层 ...

  4. 通过虚拟驱动vivi分析摄像头驱动

    Linux摄像头驱动学习之:(二)通过虚拟驱动vivi分析摄像头驱动 一.通过指令 "strace -o xawtv.log xawtv" 得到以下调用信息: // 1~7都是在v ...

  5. 2.2 vivi虚拟视频驱动测试

    学习目标:在linux终端安装xawtv,并测试vivi.ko驱动程序. 一.安装xawtv 1)ubuntu能上网情况下,使用命令:# sudo apt-get install xawtv 2)如果 ...

  6. Linux摄像头驱动学习之:(二)通过虚拟驱动vivi分析摄像头驱动

    一.通过指令 "strace -o xawtv.log xawtv" 得到以下调用信息:// 1~7都是在v4l2_open里调用1. open2. ioctl(4, VIDIOC ...

  7. (一)V4L2学习流程

    title: V4L2学习流程 date: 2019/4/23 18:00:00 toc: true --- V4L2学习流程 参考资料 关键资料,插图让人一下子就理解了 Linux摄像头驱动1--v ...

  8. V4l2初识(七)-----------浅析app获取虚拟摄像头数据的过程

    继续分析数据的获取过程: 1.请求分配的缓冲区: ioctl(4,VIDIOC_REQBUFS) vidioc_reqbufs 2.查询和映射缓冲区   ioctl(4,VIDIOC_QUERYBUF ...

  9. 2.3 摄像头驱动_vivi驱动程序分析

    学习目标:熟悉vivi的调用过程,分析vivi程序源码的ioctl函数: 一.vivi虚拟视频驱动测试方法 当我们接上usb摄像头设备时,系统会自动给我们安装对应的usb设备驱动程序.如果下次直接测试 ...

随机推荐

  1. .net core mvc项目部署nginx报错一直显示404错误

    遇到一个奇怪的问题,.net core mvc 项目部署到nginx上面,系统是linux,controller明明抛出500错误,但页面一直显示是404. 解决如下: 1.修改Startup.cs, ...

  2. cf314E. Sereja and Squares(dp)

    题意 题目链接 给你一个擦去了部分左括号和全部右括号的括号序列,括号有25种,用除x之外的小写字母a~z表示.求有多少种合法的括号序列.答案对4294967296取模.合法序列不能相交,如()[],( ...

  3. Vue通过状态为页面切换添加loading、为ajax加载添加loading

    以下方法需要引入vuex,另使用了vux的UI框架,ajax添加loading还引入了axios. 一.为页面切换添加loading. loading.js: import Vue from 'vue ...

  4. thinkphp实现简易签到

    老司机们,没时间了,直接贴代码: 视图: <!DOCTYPE html><html><meta charset="utf-8" /><ti ...

  5. Invoke 和 BeginInvoke 的区别(转发)

    在Invoke或者BeginInvoke的使用中无一例外地使用了委托Delegate. 一.为什么Control类提供了Invoke和BeginInvoke机制? 关于这个问题的最主要的原因已经是do ...

  6. log4j-初识

    1.配置文件介绍: 1.1. 控制台输出:log4j.rootLogger=DEBUG, Console ,File #Console log4j.appender.Console=org.apach ...

  7. Mac终端下使用***

    首先安装proxychains: brew install proxychains-ng 然后创建文件~/.proxychains/proxychains.conf,写入以下内容: strict_ch ...

  8. ehcache常用API整理

    鉴于csdn的blog的不稳定, 及混乱的编辑器, 和无上传功能, 遂决定彻底投诚javaeye的blog. 数月前整理的一个东西, 作为cache的扫盲文档.参考了它的官方文档. 对ehcache感 ...

  9. 在Spark集群中,集群的节点个数、RDD分区个数、​cpu内核个数三者与并行度的关系

    梳理一下Spark中关于并发度涉及的几个概念File,Block,Split,Task,Partition,RDD以及节点数.Executor数.core数目的关系. 输入可能以多个文件的形式存储在H ...

  10. 使用OpenFileDialog组件打开对话框

    实现效果: 知识运用: OpenFileDialog组件的ShowDialog方法 public DialogResult Show () //返回枚举值 DialogRrsult.OK  或  Di ...