Chinaunix首页 | 论坛 | 博客
  • 博客访问: 312486
  • 博文数量: 101
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 774
  • 用 户 组: 普通用户
  • 注册时间: 2018-10-15 14:13
个人简介

搭建一个和linux开发者知识共享和学习的平台

文章分类

全部博文(101)

文章存档

2024年(15)

2023年(24)

2022年(27)

2019年(8)

2018年(27)

分类: LINUX

2024-08-16 15:42:28

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) |
给主人留下些什么吧!~~