从应用调用vivi驱动分析v4l2 -- 查询(VIDIOC_QUERYBUF)缓存信息

Linux v4l2架构学习总链接

vivi代码

v4l2测试代码

step 5 : 设置缓存

2,查询并映射缓存

memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer));
v4l2_buffer.index = i; //想要查询的缓存
v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buffer.memory = V4L2_MEMORY_MMAP;

/* 查询缓存信息 */
ret = ioctl(fd, VIDIOC_QUERYBUF, &v4l2_buffer);

调用vidioc_querybuf

static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
	struct vivi_dev *dev = video_drvdata(file);
	return vb2_querybuf(&dev->vb_vidq, p);
}

int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
	struct vb2_buffer *vb;
	int ret;



        /*
         * 查询的类型和申请的buffer的类型要匹配
         */



	if (b->type != q->type) {
		dprintk(1, "wrong buffer type\n");
		return -EINVAL;
	}


        /*
         * buffer个数有限
         */



	if (b->index >= q->num_buffers) {
		dprintk(1, "buffer index out of range\n");
		return -EINVAL;
	}


        /*
         * 取出对应编号的vb2_buffer
         */


	vb = q->bufs[b->index];
    
        
        /*
         * 检测是不是多平面视频格式
         */


	ret = __verify_planes_array(vb, b);
	if (!ret)


                /*
                 * 这里分析非多平面视频格式的情况
                 */


		vb2_core_querybuf(q, b->index, b);
	return ret;
}

对应q->buf_ops->fill_user_buffer

buf_ops是在vb2_queue_init中注册的,对应v4l2_buf_ops

调用__fill_v4l2_buffer

static void __fill_v4l2_buffer(struct vb2_buffer *vb, void *pb)
{
	struct v4l2_buffer *b = pb;

        
        /*
         * 对于vb2_v4l2_buffer好像是第一次见到,这里分析一下来源
         * 先说一下这个函数的2个参数
         * vb是来源于bufs[index],申请buffer的时候填充的
         * pb则是来自于用户空间的地址
         * vbuf = to_vb2_v4l2_buffer(vb)
         * 说明和vb有关系
         * __vb2_queue_alloc中vb创建如下
         * vb = kzalloc(q->buf_struct_size, GFP_KERNEL);
         * 而q->buf_struct_size值在
         * vb2_queue_init
         * if (q->buf_struct_size == 0)
         *     q->buf_struct_size = sizeof(struct vb2_v4l2_buffer);
         * 也就是不为0的时候,这里会赋值
         * 但是vivi驱动中已经对其赋值了
         * q->buf_struct_size = sizeof(struct vivi_buffer);
         * 这样就找到了,对应的vb2_v4l2_buffer 
         * 实际看vivi_buffer,
         * struct vivi_buffer {
	 *    struct vb2_buffer	vb;
	 *    struct list_head	list;
	 *    struct vivi_fmt        *fmt;
         * }
         * 没看到vb2_v4l2_buffer相关的东西
         * 于是去看最新的vivi驱动代码,如下
         * struct vivid_buffer {
	 *     struct vb2_v4l2_buffer vb;
	 *     struct list_head	list;
         * };
         * 这里就可以看到vb2_v4l2_buffer的来源了
         * 其实主要原因还是vivi驱动太老,linux内核太新不匹配造成的
         */


	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
	struct vb2_queue *q = vb->vb2_queue;
	unsigned int plane;


        /*
         * 以下就是填充用户空间需要的信息
         */


	/* Copy back data such as timestamp, flags, etc. */
	b->index = vb->index;
	b->type = vb->type;
	b->memory = vb->memory;
	b->bytesused = 0;
	b->flags = vbuf->flags;
	b->field = vbuf->field;
	b->timestamp = ns_to_timeval(vb->timestamp);
	b->timecode = vbuf->timecode;


        /*
         * 这里的sequence值应该是用户表示帧数据编号
         */

	b->sequence = vbuf->sequence;
	b->reserved2 = 0;
	b->reserved = 0;


        /* 针对多平面视频格式,这里暂时不分析 */

        
        if (q->is_multiplanar) {
		/*
		 * Fill in plane-related data if userspace provided an array
		 * for it. The caller has already verified memory and size.
		 */
		b->length = vb->num_planes;
		for (plane = 0; plane < vb->num_planes; ++plane) {
			struct v4l2_plane *pdst = &b->m.planes[plane];
			struct vb2_plane *psrc = &vb->planes[plane];

			pdst->bytesused = psrc->bytesused;
			pdst->length = psrc->length;
			if (q->memory == VB2_MEMORY_MMAP)
				pdst->m.mem_offset = psrc->m.offset;
			else if (q->memory == VB2_MEMORY_USERPTR)
				pdst->m.userptr = psrc->m.userptr;
			else if (q->memory == VB2_MEMORY_DMABUF)
				pdst->m.fd = psrc->m.fd;
			pdst->data_offset = psrc->data_offset;
			memset(pdst->reserved, 0, sizeof(pdst->reserved));
		}
	} else {
		/*
		 * We use length and offset in v4l2_planes array even for
		 * single-planar buffers, but userspace does not.
		 */


                /*
                 * length: 平面的大小
                 * byteused: 帧数据的大小,这里为0,后面的分析中可以看到更新
                 * 应用代码中有些会用length代表帧数据的大小,这个值有可能是不准确的
                 * 比如帧大小是4812Byte,那么驱动中有可能为了方便,将length设置为5000
                 * 所以最好使用byteused
                 */


		b->length = vb->planes[0].length;
		b->bytesused = vb->planes[0].bytesused;


                /*
                 * 对于MMAP类型 
                 * 获取之前计算的offset
                 * 也许这里还没有看明白offset的作用
                 * 等后面mmap的时候就可以明白了
                 */


		if (q->memory == VB2_MEMORY_MMAP)
			b->m.offset = vb->planes[0].m.offset;
		else if (q->memory == VB2_MEMORY_USERPTR)
			b->m.userptr = vb->planes[0].m.userptr;
		else if (q->memory == VB2_MEMORY_DMABUF)
			b->m.fd = vb->planes[0].m.fd;
	}

	/*
	 * Clear any buffer state related flags.
	 */

        /*
         * q->timestamp_flags 初始值 V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
         * 表示使用内核的 monotonic 时钟生成的递增的时间戳
         * V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC & V4L2_BUF_FLAG_TIMESTAMP_MASK
         * 值 V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
         */
    
	b->flags &= ~V4L2_BUFFER_MASK_FLAGS;
	b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK;


        /*
         * copy_timestamp值为0,if满足
         */


	if (!q->copy_timestamp) {
		/*
		 * For non-COPY timestamps, drop timestamp source bits
		 * and obtain the timestamp source from the queue.
		 */
		b->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
		b->flags |= q->timestamp_flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
	}


        /*
         * 对于vb->state的值
         * 在使用reqbuf的时候VIDIOC_REQBUFS,看上一篇
         * vb->state = VB2_BUF_STATE_DEQUEUED
         * 表示该buffer在用户空间控制,直接break
         */



	switch (vb->state) {
	case VB2_BUF_STATE_QUEUED:
	case VB2_BUF_STATE_ACTIVE:
		b->flags |= V4L2_BUF_FLAG_QUEUED;
		break;
	case VB2_BUF_STATE_ERROR:
		b->flags |= V4L2_BUF_FLAG_ERROR;
		/* fall through */
	case VB2_BUF_STATE_DONE:
		b->flags |= V4L2_BUF_FLAG_DONE;
		break;
	case VB2_BUF_STATE_PREPARED:
		b->flags |= V4L2_BUF_FLAG_PREPARED;
		break;
	case VB2_BUF_STATE_PREPARING:
	case VB2_BUF_STATE_DEQUEUED:
	case VB2_BUF_STATE_REQUEUEING:
		/* nothing */
		break;
	}
        

        /* 
         * 对于 vb2_buffer_in_use后面分析
         */


	if (vb2_buffer_in_use(q, vb))
		b->flags |= V4L2_BUF_FLAG_MAPPED;

        
        /*
         * 说实话这里我看了大半个小时没有看明白,为什么要加这个
         * 对分析vivi来说,这都是新申请的内存,为什么会有这么多的flags
         * 这里解释一下下面的flags
         * V4L2_BUF_FLAG_DONE
         *     设置此标志后,缓冲区当前位于传出队列中,准备从驱动程序中出队
         *     调用VIDIOC_QUERYBUF ioctl时,驱动程序会设置或清除此标志
         *     调用VIDIOC_QBUF或VIDIOC_DQBUF之后,始终将其清除。 
         *     当然,缓冲区不能同时在两个队列上,V4L2_BUF_FLAG_QUEUED和
         *     V4L2_BUF_FLAG_DONE标志是互斥的。
         * 
         * V4L2_BUF_FLAG_LAST
         *     硬件产生的最后一个缓冲区。 当调用ioctl VIDIOC_QUERYBUF或
         *     VIDIOC_DQBUF ioctl时,mem2mem编解码器驱动程序在捕获队列上
         *     为最后一个缓冲区设置此标志。 由于硬件限制,最后一个缓冲区可能为空。 
         *     在这种情况下,驱动程序会将字节使用的字段设置为0,而不考虑格式。 
         *     任何后续对VIDIOC_DQBUF ioctl的调用将不再阻塞,而是返回EPIPE错误代码。 
         *
         * last_buffer_dequeued
         *     如果最后一个已解码的缓冲区已经出队,
         *     则可在poll()和DQBUF中使用以立即返回。
         *     最后一个缓冲区出列是根据上面的V4L2_BUF_FLAG_LAST判断的
         * 
         * 总的来说,如果last_buffer_dequeued设置了,那么poll()和DQBUF中
         * 不需要等待,可以直接返回
         *
         * 现在相关的标志解释完了,解释一下为什么会出现在这里,毕竟追到现在还没什么帧数据
         * 产生。
         * 
         * 个人理解是这个是用在DQBUF中的,DQBUF主要是获取数据,而且也会用到这个函数
         */


        if (!q->is_output &&
       		b->flags & V4L2_BUF_FLAG_DONE &&
		b->flags & V4L2_BUF_FLAG_LAST)
		q->last_buffer_dequeued = true;

分析一下上面使用到的last_buffer_dequeued

bool vb2_buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
{
	unsigned int plane;
	for (plane = 0; plane < vb->num_planes; ++plane) {
		void *mem_priv = vb->planes[plane].mem_priv;
		/*
		 * If num_users() has not been provided, call_memop
		 * will return 0, apparently nobody cares about this
		 * case anyway. If num_users() returns more than 1,
		 * we are not the only user of the plane's memory.
		 */

            
                /*
                 * 分析每一个plane的buffer使用情况
                 * 主意这里判断是要大于1
                 */


		if (mem_priv && call_memop(vb, num_users, mem_priv) > 1)
			return true;
	}
	return false;
}

对应vb->vb2_queue->mem_ops->num_users

vb2_vmalloc_memops.num_users,,就是vb2_vmalloc_num_users

static unsigned int vb2_vmalloc_num_users(void *buf_priv)
{
	struct vb2_vmalloc_buf *buf = buf_priv;
	return refcount_read(&buf->refcount);
}

可以看到就是读取refcount的值,这个变量我们之前分析过,VIDIOC_REQBUFS的时候,

vb2_vmalloc_alloc将这个值设置为1。

 

老路子,继续写应用程序测试

        struct v4l2_buffer buf;        

        for(i = 0; i < req.count; i++) {
                memset(&buf, 0, sizeof(buf));
                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_MMAP;
                buf.index = i;
                if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) {
                        printf("Querybuf fail\n");
                        goto err;
                }

                printf("buffer[%d]: length = %d, offset = %d\n", i, buf.length, buf.m.offset);
        }

输出结果

buffer[0]: length = 4147200, offset = 0
buffer[1]: length = 4147200, offset = 4149248
buffer[2]: length = 4147200, offset = 8298496
buffer[3]: length = 4147200, offset = 12447744

分析一下这个结果,帧数据大小为1920*1080*2 = 4147200

而且是但平面视频格式

offset是length经过也对齐后的,4147200页对齐后大小为4149248

所以就出现了上面的打印结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dianlong_lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值