V4L2 videobuffer2的介绍,数据流分析

1. videobuffer2的介绍
一. vb2涉及的数据结构:
struct v4l2_buffer {                                            //供用户态使用
    __u32            index;
    __u32            type;
    __u32            bytesused;
    __u32            flags;
    __u32            field;
    struct timeval        timestamp;
    struct v4l2_timecode    timecode;
    __u32            sequence;

    /* memory location */
    __u32            memory;
    union {
        __u32           offset;                    //MMAP方式
        unsigned long   userptr;                //userptr
        struct v4l2_plane *planes;
        __s32        fd;                            //dma
    } m;
    __u32            length;
    __u32            reserved2;
    __u32            reserved;
};

struct v4l2_requestbuffers {                                    //由用户指定
    __u32            count;                                        //buffer的个数
    __u32            type;        /* enum v4l2_buf_type */        //vb2的类型 (不是很重要,与申请的内存无关)
    __u32            memory;        /* enum v4l2_memory */            //3种类型 VB2_MEMORY_MMAP,VB2_MEMORY_MMAP,VB2_MEMORY_DMABUF
    __u32            reserved[2];
};

struct vb2_queue {                                                //由内核态维护
    unsigned int            type;
    unsigned int            io_modes;
    const struct vb2_ops        *ops;                            //vb2_queue的接口操作,比如buf_prepare
    const struct vb2_mem_ops    *mem_ops;                        //内存申请方面,针对MMAP类型模式
    const struct vb2_buf_ops    *buf_ops;                        //vb2_queue的接口操作
    void                *drv_priv;
    unsigned int            buf_struct_size;
    u32                timestamp_flags;
    gfp_t                gfp_flags;
    u32                min_buffers_needed;                            //指定至少需要的buffer数,用户层申请的buffer块数不得小于此值
    /* private: internal use only */
    struct mutex            mmap_lock;
    unsigned int            memory;                                //用户指定的v4l2_memory(3种类型)
    struct vb2_buffer        *bufs[VB2_MAX_FRAME];                //存放vb2_buffer数据,格式是用户申请的count数目
    unsigned int            num_buffers;                        //实际申请到的buffer数目,防止用户态恶意申请非常大的buffer块数
    struct list_head        queued_list;                        //用户态申请的buffer块放在此列表内
    unsigned int            queued_count;
    //以下dqbuf取数据的标志有关
    struct list_head        done_list;
    spinlock_t            done_lock;
    wait_queue_head_t        done_wq;                            //dqbuf取数据时会检测这个变量
};

struct vb2_buffer {                                                //由内核态维护
    struct vb2_queue    *vb2_queue;
    unsigned int        index;
    unsigned int        type;
    unsigned int        memory;
    unsigned int        num_planes;
    struct vb2_plane    planes[VB2_MAX_PLANES];                    //length:记录一幅图像的大小
    u64            timestamp;

    /* private: internal use only
     *
     * state:        current buffer state; do not change
     * queued_entry:    entry on the queued buffers list, which holds
     *            all buffers queued from userspace
     * done_entry:        entry on the list that stores all buffers ready
     *            to be dequeued to userspace
     */
    enum vb2_buffer_state    state;

    struct list_head    queued_entry;
    struct list_head    done_entry;
}

struct vb2_plane {
    void            *mem_priv;                                    //存放具体的一幅图片的大小(针对MMAP类型模式)
    struct dma_buf        *dbuf;                                    //(针对DMA类型模式)
    unsigned int        dbuf_mapped;
    unsigned int        bytesused;
    unsigned int        length;                                    //记录一幅图像的大小
    unsigned int        min_length;                                //记录一幅图像的最小的大小
    union {
        unsigned int    offset;
        unsigned long    userptr;                                //(针对USERPTR类型模式)
        int        fd;
    } m;
    unsigned int        data_offset;
};


二. 常用的vb2接口介绍
2.1 以下3个回调函数数组在vb2接口使用时被调用。其中vb2_ops是用户根据项目需求自己去实现,其余2个是vb2架构平台自带的实现
static const struct vb2_buf_ops v4l2_buf_ops = {
    .verify_planes_array    = __verify_planes_array_core,
    .fill_user_buffer    = __fill_v4l2_buffer,                    //将内核态的一些信息告知用户态。交流的结构体就是struct v4l2_buffer
    .fill_vb2_buffer    = __fill_vb2_buffer,                    //VIDIOC_QBUF时会被调用
    .copy_timestamp        = __copy_timestamp,
};

static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *pb)                        //将内核态的一些信息告知用户态。交流的结构体就是struct v4l2_buffer    
static int __fill_vb2_buffer(struct vb2_buffer *vb, const void *pb, struct vb2_plane *planes)    //通过用户态v4l2_buffer去完成vb2_buffer的填充。针对USERPTR,USERDMA模式
    if (b->memory == VB2_MEMORY_USERPTR) {
        planes[0].m.userptr = b->m.userptr;
        planes[0].length = b->length;
    }
    if (b->memory == VB2_MEMORY_DMABUF) {
        planes[0].m.fd = b->m.fd;
        planes[0].length = b->length;
    }

//以下是队列的相关操作函数,在使用vb2的标准接口时会调用到以下接口。由于接口的具体实现内容由项目实际情况决定,故作为开放的接口。
const struct vb2_ops v4l2_model_qops = {
    .queue_setup = v4l2_model_qops_queue_setup,                //VIDIOC_REQBUF时会被调用

    /* do nothing */
    .wait_prepare = v4l2_model_qops_wait_prepare,            //VIDIOC_DQBUF时会被调用
    .wait_finish = v4l2_model_qops_wait_finish,                //VIDIOC_DQBUF时会被调用
    .buf_init = v4l2_model_qops_buf_init,                    //VIDIOC_REQBUF时会被调用

    .buf_prepare = v4l2_model_qops_buf_prepare,                //VIDIOC_QBUF时会被调用
    .buf_cleanup = v4l2_model_qops_buf_cleanup,                //与buf_prepare相对应
    .buf_finish = v4l2_model_qops_buf_finish,                //VIDIOC_DQBUF时会被调用
    .start_streaming = v4l2_model_qops_start_streaming,
    .stop_streaming = v4l2_model_qops_stop_streaming,
    .buf_queue = v4l2_model_qops_buf_queue,
};

支持3种方式:(用于具体的内存申请)
V4L2_MODEL_BUF_TYPE_VMALLOC    (分配的内存在物理地址上是不连续的,虚拟地址连续)
    q->mem_ops = &vb2_vmalloc_memops;            //申请的物理地址不连续. 内部vmalloc, 返回值 struct vb2_vmalloc_buf *buf;
V4L2_MODEL_BUF_TYPE_DMA_CONT (分配的内存在物理地址上是连续的)
    q->mem_ops = &vb2_dma_contig_memops;        //申请的物理地址连续. 内部 dma_alloc_from_coherent(), 返回值 struct vb2_dc_buf *buf;
V4L2_MODEL_BUF_TYPE_DMA_SG (需要硬件支持iommu功能,申请的地址物理地址上不连续,虚拟地址也不连续)
    q->mem_ops = &vb2_dma_sg_memops;            //iommu相关,运行申请的物理地址不连续.

2.2 初始化队列
int vb2_queue_init(struct vb2_queue *q)
    q->buf_ops = &v4l2_buf_ops;
    vb2_core_queue_init(q);            //无实际作用,用于判断
此初始化函数全部由vb2架构平台来实现

2.3 申请buffer块.(涉及的ioctl操作时VIDIOC_REQBUF)
.vidioc_reqbufs    =    vb2_ioctl_reqbufs,
int vb2_ioctl_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p)
    vb2_core_reqbufs(struct vb2_queue *q, enum vb2_memory memory, unsigned int *count)    //队列,申请的内存类型,申请的内存个数
        *count == 0时,清空目前队列所有的buffer数据
        *count != 0时, queue_setup指定,一幅图片的大小以及num_planes,一般num_planes=1
        allocated_buffers = __vb2_queue_alloc(q, memory, num_buffers(用户态设置的大小), num_planes(1), plane_sizes(一幅图片的大小));    //具体的申请空间
            //内部调用alloc() 和 buf_init()函数,涉及的结构体是struct vb2_buffer以及struct vb2_plane
        

2.4 QBUF,将用户申请的buffer块压入队列。(涉及的ioctl操作时VIDIOC_QBUF)
.vidioc_qbuf =    vb2_ioctl_qbuf,
int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
    vb2_core_qbuf(q, b->index(用户态申请的buffer块索引), b(v4l2_buffer));
        vb = q->bufs[index];                            //取出vb2_buffer
        list_add_tail(&vb->queued_entry, &q->queued_list);
        q->queued_count++;
        __buf_prepare(vb, pb);
            switch (q->memory) {
            case VB2_MEMORY_MMAP:
                ret = __qbuf_mmap(vb, pb);
                    fill_vb2_buffer()
                    buf_prepare()
                break;
            case VB2_MEMORY_USERPTR:
                ret = __qbuf_userptr(vb, pb);
                    fill_vb2_buffer()
                    get_userptr()                        //获取用户态申请到的内存空间
                    buf_init()
                    buf_prepare()
                break;
            case VB2_MEMORY_DMABUF:
                ret = __qbuf_dmabuf(vb, pb);
                break;

2.4 DQBUF,从队列取出申请的buffer。(涉及的ioctl操作时VIDIOC_DQBUF)
.vidioc_dqbuf         = vb2_ioctl_dqbuf,
int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)            //从队列取出数据,类似于读数据,涉及是否阻塞的问题
    vb2_core_dqbuf(q, NULL, b, nonblocking);
        __vb2_get_done_vb(q, &vb, pb, nonblocking);                                    //获取已经完成操作的队列数据
            __vb2_wait_for_done_vb(q, nonblocking);                                    //等待done_list上有数据
                wait_prepare()
                wait_event_interruptible(q->done_wq);
                wait_finish()
            *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);    //从done_list上取出数据(何时往done_list上放数据,见以下2.5)
        buf_finish()
        fill_user_buffer()
        list_del(&vb->queued_entry);
        q->queued_count--;
        __vb2_dqbuf(vb);
            unmap_dmabuf

2.5 此函数可以唤醒VIDIOC_DQBUF中等待数据部分(因为若打开方式设置为阻塞,用户态在调用VIDIOC_DQBUF时将一直等待,直到内核态驱动调用vb2_buffer_done函数)
void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)            //往done_list上放数据
    finish()
    list_add_tail(&vb->done_entry, &q->done_list);
    wake_up(&q->done_wq);                //唤醒done_wq队列。dqbuf里wait_event_interruptible(q->done_wq);所以dqbuf阻塞状态时会一直阻塞,直到调用vb2_buffer_done()

2.6 查询用户申请的buffer块(struct v4l2_buffer是用户态与内核态下进行数据交流的数据结构。如果使用USERPTR方式,则v4l2_buffer里会保存用户态下申请的数据首地址以及大小)
.vidioc_querybuf =    vb2_ioctl_querybuf,
int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
    int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
        vb = q->bufs[b->index];                //根据index取出vb2_buffer
        vb2_core_querybuf(q, b->index, b);
            fill_user_buffer();

            
3. 用户态与内核态的数据交流
struct vb2_plane结构体存储的是真正申请的内存的地址以及长度.有3种方式:
a. USERPTR:此种方式由用户态去申请空间(比如用vmalloc,申请的大小可能有限).在VIDIOC_QBUF时会将用户态信息v4l2_buffer转换为内核态下vb2_buffer信息
b. DMA的方式没有仔细研究,确定的是也是由用户态去申请空间
c. MMAP方式: 此方式由内核态去申请,申请方式在内核态下又分为3种,上面有介绍过

问:那么这部分数据在内核态下怎么走?
答:举例以MMAP方式申请,V4L2_MODEL_BUF_TYPE_DMA_CONT类型申请到的一块连续的物理地址值和对应的一个虚拟地址值。
buf_descs[0].addr = vb2_dma_contig_plane_dma_addr(vb, i);    //得到物理地址.这个值很有可能写到具体的寄存器中
buf_descs[0].size = vb2_plane_size(vb, i);                    //得到大小(通常一幅图片大小)
以下2行是针对V4L2_MODEL_BUF_TYPE_SG_CONT类型
buf_descs[j].addr = sg_dma_address(sg);
buf_descs[j].size = sg_dma_len(sg);
所以一旦dma采集到数据,则对应的MMAP方式申请的空间也就有了数据。然后调用video_buf_done()函数将数据放到done_list链表,并执行wakeup()函数来唤醒dqbuf()函数取出数据。此时用户态调用VIDIOC_DQBUF时就可以采集到一帧的视频数据了。

总结:
使用MMAP的方式在内核态下申请数据(又分3种申请方式),申请的数据可以是在物理地址上连续,从而使用DMA。但是USERPTR方式,由于用户态下只能malloc,所以申请的数据基本上不能用于dma传输。

 

用户指定buffer的个数,具体由内核去分配,分配方式也有用户态指定。内核态的多个buffer以链表的形式进行组织。

 

  • 0
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
v4l2_buffer 是一个在 Linux 内核中使用的结构体,用于描述视频缓冲区的信息。V4L2Video for Linux 2)是一个用于视频设备的内核接口。 该结构体的定义如下: ```c struct v4l2_buffer { __u32 index; // 缓冲区索引号 __u32 type; // 缓冲区所属的数据流类型 __u32 bytesused; // 已使用的字节数 __u32 flags; // 缓冲区状态标志 struct timeval timestamp; // 缓冲区时间戳 struct v4l2_timecode timecode; // 缓冲区时间码 __u32 sequence; // 缓冲区序列号 __u32 memory; // 缓冲区所在的内存类型 union { __u32 offset; // 缓冲区在内存中的偏移量 unsigned long userptr; // 用户空间缓冲区指针 struct v4l2_plane *planes; // 多平面缓冲区的指针 int fd; // 文件描述符 } m; __u32 length; // 缓冲区的长度(字节数) __u32 field; // 采集或输出的场序号 __u32 sequence; // 当前帧的序列号 }; ``` 通过使用 v4l2_buffer 结构体,可以获取视频缓冲区的相关信息,比如缓冲区索引号、已使用的字节数、缓冲区状态标志、时间戳等。根据不同的使用场景,可以通过设置结构体的字段来传递参数给视频设备驱动程序,并获取视频数据。 v4l2_buffer 结构体中的 memory 字段指示了缓冲区所在的内存类型,可以是 V4L2_MEMORY_MMAP(使用 mmap 映射到用户空间)、V4L2_MEMORY_USERPTR(用户空间缓冲区指针)或 V4L2_MEMORY_DMABUF(DMA 缓冲区)等。 希望以上信息能够帮助到你!如果还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值