当前位置:   article > 正文

Video4Linux框架简介(5) - Streaming_start_streaming

start_streaming

译注:在前几节我们介绍了如何初始化v4l2驱动的框架、查询能力值、设置输入/视频标准/格式,但是还没有真正地传输过一帧数据。

万事俱备,只欠东风,本节将会重点介绍"流媒体"中的数据流


流模式,数据流主要通过如下几种方式进行传输:

read/write接口:这种的基本比较少用。

内存映射流 I / O:驱动程序分配的内存,mmap()到用户空间。

用户指针流 I / O:由用户空间分配的内存,由于用户空间的内存可能是零散的,只在虚拟空间上连续,因此需要分散 - 聚集DMA支持。

DMABUF流 I / O:由另一个设备驱动分配的内存,导出为DMABUF文件处理程序并在此驱动程序中导入。


本例子使用的是第二种,驱动程序分配内存。

  1. #include <media/videobuf2-dma-contig.h> // 处理videobuf需添加的头文件一共有三种,该头文件代表使用的dma内存是连续的
  2. // 离散dma使用 videobuf2-dma-sg.h,用户空间内存使用 videobuf2-vmalloc.h
  3. struct skeleton {
  4. ...
  5. struct vb2_queue queue; //video buffer放置在该队列中
  6. struct vb2_alloc_ctx *alloc_ctx; //用于分配内存的上下文
  7. spinlock_t ; //用于streaming的同步,和核心锁配合使用
  8. struct list_head buf_list;
  9. unsigned int sequence; //可以认为是帧号
  10. };
  11. struct skel_buffer { //本地用于管理buffer的结构体
  12. struct vb2_buffer vb;
  13. struct list_head list;
  14. };
  15. static inline struct skel_buffer *to_skel_buffer(struct vb2_buffer *vb2)
  16. {
  17. return container_of(vb2, struct skel_buffer, vb);
  18. }

  1. /* 处理videobuf需添加的头文件一共有三种,该头
  2. * 文件代表使用的dma内存是连续的离散dma使用
  3. * videobuf2-dma-sg.h,用户空间内存使用
  4. * videobuf2-vmalloc.h
  5. */
  6. #include <media/videobuf2-dma-contig.h>
  7. struct skeleton {
  8. ...
  9. struct vb2_queue queue; //video buffer放置在该队列中
  10. struct vb2_alloc_ctx *alloc_ctx; //用于分配内存的上下文
  11. spinlock_t ; //用于streaming的同步,和核心锁配合使用
  12. struct list_head buf_list;
  13. unsigned int sequence; //可以认为是帧号
  14. };
  15. struct skel_buffer { //本地用于管理buffer的结构体
  16. struct vb2_buffer vb;
  17. struct list_head list;
  18. };
  19. static inline struct skel_buffer *to_skel_buffer(struct vb2_buffer *vb2)
  20. {
  21. return container_of(vb2, struct skel_buffer, vb);
  22. }



从上述代码可以看出,在原有的skeleton结构体中需要添加一系列的成员以外,还需要skel_buffer用于本地管理内存。

  1. static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
  2. {
  3. ...
  4. q = &skel->queue;
  5. q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //代表是视频捕获设备,也就是Camera
  6. q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
  7. q->drv_priv = skel;
  8. q->buf_struct_size = sizeof(struct skel_buffer);
  9. q->ops = &skel_qops;
  10. /* Required ops for USERPTR types: get_userptr, put_userptr.
  11. * Required ops for MMAP types: alloc, put, num_users, mmap.
  12. * Required ops for read/write access types: alloc, put, num_users, vaddr
  13. * Required ops for DMABUF types: attach_dmabuf, detach_dmabuf, map_dmabuf,
  14. * unmap_dmabuf.
  15. * get more in videobuf2-core.h
  16. */
  17. q->mem_ops = &vb2_dma_contig_memops; //使用v4l2框架的内存申请操作,开发者也可以根据系统情况自行定义
  18. q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
  19. q->lock = &skel->lock;
  20. q->gfp_flags = GFP_DMA32; //系统支持32位dma
  21. ret = vb2_queue_init(q);
  22. if (ret)
  23. goto v4l2_dev_unreg;
  24. skel->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); //三种内存类型中只有dma连续内存需要
  25. //alloc_ctx
  26. if (IS_ERR(skel->alloc_ctx)) {
  27. dev_err(&pdev->dev, "Can't allocate buffer context");
  28. ret = PTR_ERR(skel->alloc_ctx);
  29. goto v4l2_dev_unreg;
  30. }
  31. INIT_LIST_HEAD(&skel->buf_list);
  32. spin_lock_init(&skel->qlock);
  33. ...
  34. vdev->queue = q;
  35. ...
  36. }

以下这些都根据需要,驱动可以使用v4l2框架的实现或者自行实现。

  1. static struct vb2_ops skel_qops = {
  2. .queue_setup = queue_setup, //在内存分配之前从VIDIOC_REQBUFS和VIDIOC_CREATE_BUFS处理程序调用,
  3. //如果* num_planes!= 0,在分配后验证较少数量的buffer。驱动程序
  4. //应该返回* num_buffers中所需的buffer数量以及* num_planes中每个buffer
  5. //所需的planar数量;每个planar的大小应该在alloc_ctxs[] 数组中的sizes []数
  6. //组和可选的每平面分配器特定上下文中设置。当从VIDIOC_REQBUFS,
  7. // fmt == NULL调用时,驱动程序必须使用当前配置的格式,* num_buffers
  8. //是正在分配的buffer总数。当从VIDIOC_CREATE_BUFS,fmt!= NULL调用时,
  9. //它描述目标帧格式。在这种情况下,* num_buffers另外被分配到q-> num_buffers。
  10. .buf_prepare = buffer_prepare, //每次buffer从用户空间queue入和VIDIOC_PREPARE_BUF ioctl时调用;驱动程序可以
  11. //在该回调中的硬件操作之前执行任何初始化;如果返回错误,则buffer不
  12. //会在驱动程序中排队;
  13. .buf_queue = buffer_queue, //传递buffer vb到驱动程序;驱动器可以在该buffer上开始硬件操作;驱动程序
  14. //应该通过调用vb2_buffer_done()函数返回buffer;它调用STREAMON ioctl之
  15. //后被调用;如果在调用STREAMON之前用户预排队buffer,可能在start_streaming
  16. //回调之前调用
  17. .start_streaming = start_streaming, //只需调用一次进入“流”状态;驱动程序可以在调用@start_streaming之前
  18. //接收带有@buf_queue回调的buffer;驱动程序在count参数中获取已排队
  19. //buffer的数量;驱动程序可能会返回错误,如果硬件失败或没有足够的
  20. //已排队buffer,在这种情况下,所有已经由@buf_queue回调给出的buffer无效。
  21. .stop_streaming = stop_streaming, //当'streaming'状态必须被禁用时调用;驱动程序应停止任何DMA事务或等待,
  22. //直到它们完成并返回它从buf_queue()获得的所有buffer
  23. .wait_prepare = vb2_ops_wait_prepare, //释放调用vb2函数时发生的任何锁;因此有些驱动直接就命名为XXX_unlock;
  24. //它在ioctl需要等待新的buffer到达之前被调用;需要避免阻塞访问类型中的死锁
  25. .wait_finish = vb2_ops_wait_finish, //重新获取在wait_prepare中释放的所有锁;等待新的buffer到达后需要在继续休眠前的操作
  26. };
  27. static const struct v4l2_ioctl_ops skel_ioctl_ops = {
  28. ...
  29. .vidioc_reqbufs = vb2_ioctl_reqbufs,
  30. .vidioc_querybuf = vb2_ioctl_querybuf,
  31. .vidioc_qbuf = vb2_ioctl_qbuf,
  32. .vidioc_dqbuf = vb2_ioctl_dqbuf,
  33. .vidioc_streamon = vb2_ioctl_streamon,
  34. .vidioc_streamoff = vb2_ioctl_streamoff,
  35. };
  36. static const struct v4l2_file_operations skel_fops = {
  37. .owner = THIS_MODULE,
  38. .open = v4l2_fh_open,
  39. .release = vb2_fop_release,
  40. .unlocked_ioctl = video_ioctl2,
  41. .read = vb2_fop_read,
  42. .mmap = vb2_fop_mmap,
  43. .poll = vb2_fop_poll,
  44. };

  1. static int queue_setup(struct vb2_queue *vq,
  2. const struct v4l2_format *fmt,
  3. unsigned int *nbuffers,
  4. unsigned int *nplanes,
  5. unsigned int sizes[],
  6. void *alloc_ctxs[])
  7. {
  8. struct skeleton *skel = vb2_get_drv_priv(vq);
  9. if (*nbuffers < 3)
  10. *nbuffers = 3;
  11. *nplanes = 1;
  12. sizes[0] = skel->format.sizeimage;
  13. alloc_ctxs[0] = skel->alloc_ctx;
  14. return 0;
  15. }

  1. static int start_streaming(struct vb2_queue *vq, unsigned int count)
  2. {
  3. struct skeleton *skel = vb2_get_drv_priv(vq);
  4. if (count < 2) //这里控制在启流前需要queue入buffer的最小个数
  5. return -ENOBUFS;
  6. skel->sequence = 0;
  7. /* TODO: start DMA */
  8. return 0;
  9. }
  10. static int stop_streaming(struct vb2_queue *vq)
  11. {
  12. struct skeleton *skel = vb2_get_drv_priv(vq);
  13. struct skel_buffer *buf, *node;
  14. unsigned long flags;
  15. /* TODO: stop DMA */
  16. /* Release all active buffers */
  17. spin_lock_irqsave(&skel->qlock, flags);
  18. list_for_each_entry_safe(buf, node, &skel->buf_list, list) {
  19. vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
  20. list_del(&buf->list);
  21. }
  22. spin_unlock_irqrestore(&skel->qlock, flags);
  23. return 0;
  24. }

  1. static int buffer_prepare(struct vb2_buffer *vb)
  2. {
  3. struct skeleton *skel = vb2_get_drv_priv(vb->vb2_queue);
  4. unsigned long size = skel->format.sizeimage;
  5. if (vb2_plane_size(vb, 0) < size) {
  6. dev_err(&skel->pdev->dev, "buffer too small (%lu < %lu)\n",
  7. vb2_plane_size(vb, 0), size);
  8. return -EINVAL;
  9. }
  10. vb2_set_plane_payload(vb, 0, size);
  11. vb->v4l2_buf.field = skel->format.field;
  12. return 0;
  13. }
  14. static void buffer_queue(struct vb2_buffer *vb)
  15. {
  16. struct skeleton *skel = vb2_get_drv_priv(vb->vb2_queue);
  17. struct skel_buffer *buf = to_skel_buffer(vb);
  18. unsigned long flags;
  19. spin_lock_irqsave(&skel->qlock, flags);
  20. list_add_tail(&buf->list, &skel->buf_list);
  21. /* TODO: Update any DMA pointers if necessary */
  22. spin_unlock_irqrestore(&skel->qlock, flags);
  23. }


在buffer被DMA填充后的中断处理函数:
  1. static irqreturn_t skeleton_irq(int irq, void *dev_id)
  2. {
  3. struct skeleton *skel = dev_id;
  4. /* TODO: handle interrupt */
  5. if (captured_new_frame) {
  6. ...
  7. spin_lock(&skel->qlock);
  8. list_del(&new_buf->list);
  9. spin_unlock(&skel->qlock);
  10. new_buf->vb.v4l2_buf.sequence = skel->sequence++;
  11. v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp);
  12. vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE);
  13. }
  14. return IRQ_HANDLED;
  15. }

最后,在如下函数中

skeleton_s_input()

skeleton_s_std()

skeleton_s_dv_timings()

skeleton_s_fmt_vid_cap() 

增加这项检查:

if(vb2_is_busy(&skel->queue))

return-EBUSY;


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/263111
推荐阅读
相关标签
  

闽ICP备14008679号