v4l2框架-将缓存放入队列(VIDIOC_QBUF)

应用层编程:将缓存放入队列中

struct v4l2_buffer v4l2_buffer;
int number_buffers = 5;
 
for(i = 0; i < number_buffers; i++)
{
	memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer));
	v4l2_buffer.index = i; //想要放入队列的缓存index
	v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	v4l2_buffer.memory = V4L2_MEMORY_MMAP;	
    ret = ioctl(fd, VIDIOC_QBUF, &v4l2_buffer);
    
    ......
}

vidioc_qbuf->vb2_ioctl_qbuf

int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
	struct video_device *vdev = video_devdata(file);

	/*判断vdev->queue->owner && vdev->queue->owner != file->private_data
	* 在前面qbuf的时候也用到
	*/
	if (vb2_queue_is_busy(vdev, file))
		return -EBUSY;
	return vb2_qbuf(vdev->queue, p);
}

vidioc_qbuf->vb2_ioctl_qbuf->vb2_qbuf

int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
	/*判断q->fileio的值,默认为0*/
	if (vb2_fileio_is_active(q)) {
		dprintk(1, "file io in progress\n");
		return -EBUSY;
	}

	return vb2_internal_qbuf(q, b);
}

vidioc_qbuf->vb2_ioctl_qbuf->vb2_qbuf->vb2_internal_qbuf

static int vb2_internal_qbuf(struct vb2_queue *q, struct v4l2_buffer *b)
{
	/*
	*	查询buffer的type,memory类型是否匹配,后面分析
	*/
	int ret = vb2_queue_or_prepare_buf(q, b, "qbuf");
	struct vb2_buffer *vb;

	if (ret)
		return ret;

	/*取出要放入队列的缓存*/
	vb = q->bufs[b->index];

	switch (vb->state) {
	/*在reqbuf的时候设置为VB2_BUF_STATE_DEQUEUED*/
	case VB2_BUF_STATE_DEQUEUED:
		ret = __buf_prepare(vb, b);
		if (ret)
			return ret;
		break;
	case VB2_BUF_STATE_PREPARED:
		break;
	case VB2_BUF_STATE_PREPARING:
		dprintk(1, "buffer still being prepared\n");
		return -EINVAL;
	default:
		dprintk(1, "invalid buffer state %d\n", vb->state);
		return -EINVAL;
	}

	/*
	 * Add to the queued buffers list, a buffer will stay on it until
	 * dequeued in dqbuf.
	 */

    /*将要放入队列缓存的queued_entry放入vb2_queue的queued_list双向链表中*/
	list_add_tail(&vb->queued_entry, &q->queued_list);
	/*放入队列的buffer引用计数+1*/
	q->queued_count++;
	q->waiting_for_buffers = false;
	/*设置vb->state的状态已放入队列*/
	vb->state = VB2_BUF_STATE_QUEUED;
	/*不成立*/
	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
		/*
		 * For output buffers copy the timestamp if needed,
		 * and the timecode field and flag if needed.
		 */
		if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) ==
		    V4L2_BUF_FLAG_TIMESTAMP_COPY)
			vb->v4l2_buf.timestamp = b->timestamp;
		vb->v4l2_buf.flags |= b->flags & V4L2_BUF_FLAG_TIMECODE;
		if (b->flags & V4L2_BUF_FLAG_TIMECODE)
			vb->v4l2_buf.timecode = b->timecode;
	}

	/*
	 * If already streaming, give the buffer to driver for processing.
	 * If not, the buffer will be given to driver on next streamon.
	 */
	//如果已经streaming,不成立
	if (q->start_streaming_called)
		__enqueue_in_driver(vb);

	/* Fill buffer information for the userspace */
	__fill_v4l2_buffer(vb, b);

	/*
	 * If streamon has been called, and we haven't yet called
	 * start_streaming() since not enough buffers were queued, and
	 * we now have reached the minimum number of queued buffers,
	 * then we can finally call start_streaming().
	 */
	//如果正在streaming,但是streaming启动完毕,并且入队列的buffer
	//大于等于vb2_queue需要的最少buffer个数,不成立
	if (q->streaming && !q->start_streaming_called &&
	    q->queued_count >= q->min_buffers_needed) {
		ret = vb2_start_streaming(q);
		if (ret)
			return ret;
	}

	dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index);
	return 0;
}

__fill_v4l2_buffer

static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b)
{
	struct vb2_queue *q = vb->vb2_queue;

	/* Copy back data such as timestamp, flags, etc. */
	/*拷贝vb->v4l2_buf中m变量对应地址偏移大小的内容给v4l2_buffer*/
	memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m));
	b->reserved2 = vb->v4l2_buf.reserved2;
	b->reserved = vb->v4l2_buf.reserved;

	/*如果是多平面视频方式,不成立*/
	if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) {
		/*
		 * Fill in plane-related data if userspace provided an array
		 * for it. The caller has already verified memory and size.
		 */
		/*直接将vb->num_planes赋值给传下来的b->length*/
		b->length = vb->num_planes;
		/*将vb->v4l2_planes中的内容拷贝给b->m.planes*/
		memcpy(b->m.planes, vb->v4l2_planes,
			b->length * sizeof(struct v4l2_plane));
	} else {/*如果是单平面视频方式*/
		/*
		 * We use length and offset in v4l2_planes array even for
		 * single-planar buffers, but userspace does not.
		 */
		/*将平面的大小赋值给b->length*/
		b->length = vb->v4l2_planes[0].length;
		
		/*帧数据的大小,这里为0,后面的分析中可以看到更新
		* 注意应用层需要使用bytesused表示帧数据大小
		*/
		b->bytesused = vb->v4l2_planes[0].bytesused;
		/*使用mmap方式,获取request_buffer的mem_offset
		* 对于user_ptr获取userptr,dma_buffer获取fd
		*/
		if (q->memory == V4L2_MEMORY_MMAP)
			b->m.offset = vb->v4l2_planes[0].m.mem_offset;
		else if (q->memory == V4L2_MEMORY_USERPTR)
			b->m.userptr = vb->v4l2_planes[0].m.userptr;
		else if (q->memory == V4L2_MEMORY_DMABUF)
			b->m.fd = vb->v4l2_planes[0].m.fd;
	}

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

	/*							
    * q->timestamp_flags 初始值  V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC| V4L2_BUF_FLAG_TSTAMP_SRC_EOF
    * 在xilinx-dma.c文件中初始化
    * 表示为递增类型,它在内核中的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;
	if ((q->timestamp_flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) !=
	    V4L2_BUF_FLAG_TIMESTAMP_COPY) {
		/*
		 * 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;
	}

	/*在reqbuf将vb->state设置为VB2_BUF_STATE_DEQUEUED*/
	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:
		/* nothing */
		break;
	}

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

接着分析__buffer_in_use

static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb)
{
	unsigned int plane;
	for (plane = 0; plane < vb->num_planes; ++plane) {
		/*获取reqbuf时设置的mem_priv*/
		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.
		 */
		 /*判断mem_priv是否被设置并且判断num_users回调函数的返回值是否大于1
			表示之前已经调用reqbuf
		*/
		if (mem_priv && call_memop(vb, num_users, mem_priv) > 1)
			return true;
	}
	return false;
}

call_memop(vb, num_users, mem_priv)会调用vb2_dma_contig_memops->vb2_dc_num_users

static unsigned int vb2_dc_num_users(void *buf_priv)
{
	struct vb2_dc_buf *buf = buf_priv;

	/*读取buf->refcount的值,在reqbuf时设置为1*/
	return atomic_read(&buf->refcount);
}

大概就是将需要放入队列的buffer放入vb2_queue队列中,并填充v4l2_buffer的信息返回给应用层

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值