vb2_buffer是V4L2框架中videobuf2库的核心数据结构,代表了一个视频缓冲区。它的作用是管理视频数据缓冲区的生命周期,包括缓冲区的分配、准备、排队、处理、完成和清理等。vb2_buffer通常与vb2_queue一起使用,后者管理着一个或多个vb2_buffer实例,形成了一个缓冲区队列。
以下是vb2_buffer的一些主要用途:
缓冲区分配:在vb2_queue的queue_setup回调中,根据用户空间的请求分配一定数量的vb2_buffer实例。
缓冲区准备:在buf_prepare回调中,对每个vb2_buffer进行必要的初始化,以供后续使用。
缓冲区排队:通过buf_queue回调,将vb2_buffer实例入队,准备被驱动程序的硬件使用。
数据采集:硬件在缓冲区中采集数据,这通常在start_streaming回调中开始。
缓冲区完成:在buf_finish回调中,对已经完成数据采集的vb2_buffer进行处理,准备将其返回给用户空间。
缓冲区清理:在buf_cleanup回调中,对不再使用的vb2_buffer进行清理。
内存映射:用户空间可以通过mmap系统调用将vb2_buffer映射到用户空间,从而直接访问缓冲区中的数据。
代码示例
以下是如何在V4L2驱动程序中使用vb2_buffer的示例:
#include
// 定义一个结构体以容纳额外的驱动特定信息
struct my_buffer {
struct vb2_buffer buf;
// 驱动特定的其他字段...
};
// 缓冲区队列的设置
static int my_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
unsigned int *num_planes, unsigned long sizes[],
void *alloc_ctxs[])
{
// 根据请求配置缓冲区数量和大小
*num_buffers = 10; // 分配10个缓冲区
// 设置每个缓冲区的大小等...
}
// 缓冲区初始化
static int my_buf_init(struct vb2_buffer *vb)
{
struct my_buffer *my_buf = container_of(vb, struct my_buffer, buf);
// 对缓冲区进行特定的初始化操作
// ...
return 0;
}
// 缓冲区准备
static int my_buf_prepare(struct vb2_buffer *vb)
{
// 准备缓冲区以供硬件使用
// ...
return 0;
}
// 缓冲区完成
static void my_buf_finish(struct vb2_buffer *vb)
{
// 缓冲区不再被硬件使用,进行必要的后处理
// ...
}
// 驱动的probe函数中初始化vb2_queue
static int my_camera_probe(struct platform_device *pdev)
{
struct vb2_queue q;
int ret;
// 初始化队列
memset(&q, 0, sizeof(q));
q.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
q.io_modes = VB2_MMAP;
q.ops = &my_vb2_ops;
q.mem_ops = &vb2_vmalloc_memops;
q.buf_struct_size = sizeof(struct my_buffer);
// 注册V4L2设备
ret = v4l2_device_register(&pdev->dev, NULL);
if (ret)
return ret;
// 初始化队列
ret = vb2_queue_init(&q);
if (ret)
goto err_v4l2_unregister;
// 其他初始化操作...
return 0;
err_v4l2_unregister:
v4l2_device_unregister(&pdev->dev);
return ret;
}
// 驱动的remove函数中清理
static int my_camera_remove(struct platform_device *pdev)
{
// 获取v4l2_device
struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
// 清理队列
vb2_queue_release(&v4l2_dev->vb2_dev.vb2_queue);
// 卸载V4L2设备
v4l2_device_unregister(v4l2_dev);
return 0;
}
// 驱动结构体注册
static struct platform_driver my_camera_driver = {
.probe = my_camera_probe,
.remove = my_camera_remove,
.driver = {
.name = "my_camera",
.owner = THIS_MODULE,
},
};
module_platform_driver(my_camera_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A demo camera driver");
在这个示例中,我们定义了一个包含vb2_buffer的my_buffer结构体,以便存储驱动特定的信息。在my_camera_probe函数中,我们初始化了一个vb2_queue实例,并设置了它的操作集(ops)、内存操作集(mem_ops)以及每个缓冲区的结构体大小(buf_struct_size)。然后,我们调用vb2_queue_init来完成队列的初始化。
在my_queue_setup函数中,我们配置了队列的缓冲区数量和大小。其他回调函数,如my_buf_init、my_buf_prepare和my_buf_finish,分别用于初始化、准备和完成缓冲区。
在my_camera_remove函数中,我们清理了队列,并注销了V4L2设备。
视频设备在抓取到视频图像后,会调用vb2_buffer_done回调函数将帧上报给v4l2 核心层函数,其中vb2_buffer_done是核心层实现
的回调函数。vb2_buffer_done是外部连接(EXPORT_SYMBOL_GPL)的符号,可以由模块调用。调用路径一般由VIDEO设备的中断触发
videobuf2用于链接v4l2驱动层与用户层,提供数据传输通道,它可以分配并管理视频帧数据。
videobuf层实现了很多ioctl函数,包括buffer分配、入队、出队和数据流控制。
struct v4l2_buffer用于用户空间和内核驱动交换数据,struct vb_buffer是缓存队列的基本单位。
当开始IO流时,帧以v4l2_buffer格式在应用和驱动之间传输,一个缓冲区可以有三种状态:
在驱动输入队列中: 用户空间通过VIDIOC_QBUF把缓冲区入队列,驱动程序将对此队列中的缓冲区进行处理。
对于一个视频捕捉设备,输入队列中的缓冲区是空的,驱动将会往里面填充数据。
在驱动输出队列中: 当驱动将一个输入队列中的缓冲区填满后,就转移到输出队列中,等待用户空间处理。
用户空间处理: 用户空间通过VIDIOC_DQBUF将缓冲区数据取出,通过mmap等方式进行处理。
当用户空间拿到v4l2_buffer,可以获取到缓冲区相关信息。
vb2_buffer在vb2_queue的不同链表上移动,也表现于不同的状态。
vb2_buffer->queued_entry和vb2_buffer->done_entry分别在vb2_queue->queued_list和
vb2_queue->done_list两个链表上插入/移出。
vb2_core_qbuf()将vb2_buffer->queued_entry加入到vb2_queue->queued_list中,
vb2_buffer->state变为VB2_BUF_STATE_QUEUED。
vb2_core_dqbuf()将vb2_buffer_queued_entry移除,vb2_buffer->state变成VB2_BUF_STATE_DEQUEUED。
在vb2_core_dqbuf()->__vb2_get_done_vb()将vb2_buffer->done_entry移除,
vb2_buffer_done()中将vb2_buffer->done_entry加入到vb2_queue->done_list中
struct vb2_queue代表一个video buffer队列,struct vb2_buffer是队列中的成员。
struct vb2_ops是管理队列中vb2_buffer的函数集,具体实例是由具体的驱动提供的。比如UVC由uvc_queue_init()初始化,
实例为uvc_queue_qops;DW MIPI设备对应vb2_video_qops。
struct vb2_mem_ops是操作vb2_buffer内存的函数集,有几种不同方式vb2_vmalloc_memops、vb2_dma_sg_memops、
vb2_dma_contig_memops。
struct vb2_buf_ops主要操作vb2_buffer或者v4l2_buffer结构体,在vb2_queue_init()对vb2_queue进行初始化的时候
指定为v4l2_buf_ops。
vb2_ops和vb2_mem_ops有具体的驱动选择合适的方式,vb2_buf_ops和具体的驱动无关。
1,调用vb2_queue_init 初始化队列 q 。
2,调用reqbuf 时候会根据请求(v4l2_requestbuffers)分配vb2结构,并且加入到q->buf中
3,调用querybuf时候,根据信息(v4l2_buffer)返回q->buf中对应的vb2_buffer的信息(v4l2_buffer)
4,mmap上面信息对应的 vb空间到用户空间
5,调用qbuf 时,将对应的vb2_buffer ( vivi_bufer->list )添加到 q->queued_list 队列中
6,使用select 调用poll 休眠等待 q->done_list 有数据
7, 调用qbuf 和 vidioc_streamon 时候都会查询,如果这两个条件都成立,则调用q->ops->buf_queue 将 核心中的vb2_buffer调入我们写的驱动中,放入一个列表,然后等待(上面的poll过程休眠)我们的驱动程序将数据放入该vb2_buffer
8, 数据存放完成后 调用vb2_buffer_done函数,即将上面有数据的vb2_buffer放入q->done_list中,然后唤醒上面poll休眠的进程
9, poll唤醒后会调用dqbuf将q->done_list 中的vb2_buffer提出来后,将此vb2的信息(v4l2_buffer)返回
10, 应用程序得到buffer信息后,就去对应的mmap后的用户空间中读数据。
首先用户 open 一个 /dev/videoX,获取其句柄,同时触发内核的 open 函数内部对 videobuf2 的 vb2_queue 进行初始化;然后进行一系列的 ioctl 操作,入口是 isp_video_fops->unlocked_ioctl 成员,再往后会细分为 isp_video_ioctl_ops 里面的一个个回调,这一个个回调与 vb2 众多的 ops 深度结合起来共同完成了数据流的管理工作。
阅读(309) | 评论(0) | 转发(0) |