深入理解linux内核v4l2框架之videobuf

深入理解linux内核v4l2框架之videobuf

 

Videobuf


下面来介绍以下 videobuffer 相关的一些东西。

V4L2 核心 api 提供了一套标准的方法来处理视频缓冲,这些方法允许驱动实现 read(),mmap(), overlay() 等操作。同样也有方法支持 DMA scatter/gather操作,并且支持 vmallocbuffer( 这个大多用在 USB 驱动上 )

videobuf 层功能是一种在 v4l2 驱动和用户空间当中的依附层,这话看起来有点绕,说白了就是提供一种功能框架,用来分配和管理视频缓冲区,它相对独立,却又被 v4l2 驱动使用。它有一组功能函数集用来实现许多标准的 POSIX 系统调用,包括 read(),poll() mmap() 等等,还有一组功能函数集用来实现流式 (streaming)IO v4l2_ioctl调用,包括缓冲区的分配,入队和出队以及数据流控制等操作。使用 videobuf需要驱动程序作者遵从一些强制的设计规则,但带来的好处是代码量的减少和 v4l2框架 API 的一致。

缓冲类型

并不是所有的视频设备都使用相同的缓冲类型。实际上,有三种通用的类型:

被分散在物理和内核虚拟地址空间的缓冲,几乎所有的用户空间缓冲都是这种类型,


如果可能的话分配内核空间的缓冲也很有意义,但是不幸的是,这个通常需要那些支持离散聚合 DMA 操作的硬件设备。

物理上离散的但是虚拟地址是连续的,换句话说,就是用 vmalloc分配的内核缓冲。 这些缓冲很难用于 DMA 操作。

物理上连续的缓冲。

videobuf 可以很好地处理这三种类型的缓冲,但是在此之前,驱动程序作者必须选择一种类型,并且以此类型为基础设计驱动。

数据结构,回调函数和初始化

根据选择的类型,包含不同的头文件,这些头文件在 include/media/下面

<media/videobuf-dma-sg.h>

<media/videobuf-vmalloc.h>

<media/videobuf-dma-contig.h>

v4l2 驱动需要包含一个 videobuf_queue 的实例用来管理缓冲队列,同时还要一个链表来维护这个队列,另外还要一个中断安全的 spin_lock 来保护队列的操作。

下一步就是要填充一个回调函数集来处理实际的缓冲区队列,这个函数集用 videobuf_queue_ops来描述:

struct videobuf_queue_ops {

int *(buf_setup)(struct videobuf_queue*q, uint *count, uint *size);

int *(buf_prepare)(structvideobuf_queue *q, struct videobuf_buffer *vb,

enum v4l2_field field);

void *(buf_queue)(structvideobuf_queue*q,struct videobuf_buffer *vb);

void *(buf_release)(...);

}

buf_setup IO 处理请求之前被调用。目的是告诉 videobuf 关于 IO的信息。 count 参数提供一个缓冲区个数的参考,驱动必须检查它的合理性,一个 经验是大于等于 2,小于等于 32 个。 Size 参数指定了每一帧数据的大小。

buf_prepare 每一个缓冲 (videobuf_buffer结构描述的 ) 将被传递给该回调函数,用 来配置缓冲的 height,width fileds 。如果 field 参数被设置为 VIDEOBUF_NEEDS_INIT,那么驱动将把 vb 传递给 videobuf_iolock() 这个函数。除此之外,该回调函数通常也将为 vb分配内存,最后把 vb 状态置为 VIDEOBUF_PREPARED

buf_queue 当一个 vb 需要被放入 IO 请求队列时,调用该回调。它将把这个 buffer放到可用的 buffer 链表当中去,然后把状态置为 VIDEOBUF_QUEUED

buf_release 当一个 buffer 不再使用的时候,调用该回调函数。驱动必须保证 buffer 上没有活跃的 IO请求,之后就可以将这个 buffer 传递给合适的 free 函数,根据申请的 buffer 类型调用对应的释放函数:

scatter/gather 类型的调用

videobuf_dma_unmap(structvideobuf_queue, videobuf_dmabuf)

videobuf_dma_free(videobuf_dmabuf)

vmalloc 类型的调用

videobuf_vmalloc_free(videobuf_buffer)

contiguous 类型的调用

videobuf_dma_contig_free(videobuf_queue,videobuf_buffer)

有一种方法可以保证 buffer 上没有 IO 请求,调用函数

videobuf_waiton(videobuf_buffer,non_blocking, intr)


文件操作 (v4l2_file_operations)

到了这儿,很多工作也就做完了,剩下的事情就是将对 videobuf的调用传递给具体的驱动实现了。首先就是打开操作,这个操作要先对 videobuf_queue进行初始化,初始化取决于申请的 buffer 是什么类型,有如下三种初始化函数可供调用:

void videobuf_queue_sg_init(structvideobuf_queue *q,

struct videobuf_queue_ops *ops,

struct device *dev,

spinlock_t *irqlock,

enum v4l2_buf_type type,

enum v4l2_field_ field,

unsigned int msize,

void *priv

struct mutex *ext_lock)

void videobuf_queue_vmalloc_init(structvideobuf_queue *q,

struct videobuf_queue_ops *ops,

struct device *dev,

spinlock_t *irqlock,

enum v4l2_buf_type type,

enum v4l2_field field,

unsigned int mszie,

void *priv

struct mutex *ext_lock);

voidvideobuf_queue_dma_contig_init(struct videobuf_queue *q,

struct videobuf_queue_ops *ops,

struct device *dev,

spinlock_t *irqlock,

enum v4l2_buf_type type,

enum v4l2_field field,

unsigned int mszie,

void *priv

struct mutex *ext_lock);

以上三种初始化函数,有相同的参数,这些参数的从他们的名称就可以看出来锁代表的意义是什么。

这里着重说下 v4l2_buf_type 类型,

V4L2_BUF_TYPE_VIDEO_CAPTURE 指定 buf的类型为 capture ,用于视频捕获设备

V4L2_BUF_TYPE_VIDEO_OUTPUT 指定 buf的类型 output ,用于视频输出设备

V4L2_BUF_TYPE_VIDEO_OVERLAY 指定 buf的类型为 overlay ,用于 overlay 设备

V4L2_BUF_TYPE_VBI_CAPTURE 用于 vbi 捕获设备

V4L2_BUF_TYPE_VBI_OUTPUT 用于 vbi 输出设备

V4L2_BUF_TYPE_SLICED_VBI_CAPTURE 用于切片 vbi捕获设备

V4L2_BUF_TYPE_SLICED_VBI_OUTPUT 用于切片 vbi输出设备

V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY 用于视频输出 overlay设备

V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE 用于多平面存储格式的视频捕获设备

V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE 用于多平面存储格式的视频输出设备

v4l2_field 指定 video field ,也就是说 interleaved 或者 progressive 的,一般都是指定为 V4L2_FIELD_NONE,用于逐行扫描的设备。

V4L2 捕获设备驱动可以支持两种 API read() 系统调用和更为复杂的流机制。一般的做法是两种都支持以确保所有的应用都可以使用该设备。 videobuf框架使得这种驱动的编写变得更为简单。比如说要实现 read() 系统调用,那么驱动程序只需要调用

ssize_t videobuf_read_one(structvideobuf_queue *q,

char __user *data, size_t count,loff_t *ppos, int nonblocking)

ssize_t videobuf_read_streaming(structvideobuf_queue *q, char __user *data, size_t count, loff_t *ppos,int vbihack, int nonblocking)

这两个函数都是把帧数据读入到 data 当中,然后返回实际上读取的字节数。不同的是前者只读取一帧数据,而后者可以选择读取多帧。一个典型的应用 read() 系统调用必须开启捕获设备,然后返回之前停止该设备。


poll() 系统调用通常由以下函数来实现:

unsigned intvideobuf_poll_stream(struct file *file, struct videobuf_queue *q,

poll_table *wait)

注意,实际最终使用的 q 是可用的第一个 buffer

当内核空间缓冲的流 IO 请求完成后,驱动还必须支持 mmap 系统调用以使能用户空间可以访问 data数据。在 v4l2 驱动中,通常很复杂的 mmap 的实现被简化了,只需要调用下面这个函数就可以了:

int videobuf_mmap_mapper(structvideobuf_queue *q,

struct vma_area_struct * vma)

剩下的事情就交给 videobuf 核心层来完成好了。

release 函数需要调用两个单独的函数来完成:

void videobuf_stop(structvideobuf_queue *q);

int videobuf_mmap_free(structvideobuf_queue *q)

前者终止所有 buffer IO 操作。后者保证所有的 buffer unmap 掉,如果已经被 unmap 掉的话,这个 buffer 就会被传递给 buf_release回调函数。如果 buffer 还没有被 unmap ,那么后者将返回一个错误代码。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值