V4L2框架-缓存出队列(VIDIOC_DQBUF)

1,082 阅读5分钟

1.应用层从队列中取出缓存

struct v4l2_buffer buffer;
 
buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer.memory = V4L2_MEMORY_MMAP;
 
if (ioctl(fd, VIDIOC_DQBUF, &buffer) < 0)
{
    printf("%s:VIDIOC_DQBUF failed\n", __func__);
    return -1;
}

2.底层调用

调用平台驱动代码vidioc_dqbuf

  1. vidioc_dqbuf
  2. vb2_ioctl_dqbuf
  3. vb2_dqbuf
  4. vb2_internal_dqbuf
int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
	struct video_device *vdev = video_devdata(file);

	/*return vdev->queue->owner && vdev->queue->owner != file->private_data*/
	if (vb2_queue_is_busy(vdev, file))
		return -EBUSY;
	/*file->f_flags & O_NONBLOCK判断是否为非阻塞方式读取数据*/
	return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK);
}

int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
{
        /*return q->fileio,成立则退出*/
	if (vb2_fileio_is_active(q)) {
		dprintk(1, "file io in progress\n");
		return -EBUSY;
	}
	return vb2_internal_dqbuf(q, b, nonblocking);
}

static int vb2_internal_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
{
	struct vb2_buffer *vb = NULL;
	int ret;

	/*如果应用层传下来的v4l2_buffer的type(capture)
	* vb2_queue的type则返回失败
	*/
	if (b->type != q->type) {
		dprintk(1, "invalid buffer type\n");
		return -EINVAL;
	}
	
	/*如果ret=0,表示获取到了有效的buffer*/
	ret = __vb2_get_done_vb(q, &vb, b, nonblocking);
	if (ret < 0)
		return ret;

	switch (vb->state) {
    //一般状态为VB2_BUF_STATE_DONE
	case VB2_BUF_STATE_DONE:
		dprintk(3, "returning done buffer\n");
		break;
	case VB2_BUF_STATE_ERROR:
		dprintk(3, "returning done buffer with errors\n");
		break;
	default:
		dprintk(1, "invalid buffer state\n");
		return -EINVAL;
	}

	//这个函数没有实现
	call_void_vb_qop(vb, buf_finish, vb);

	/* Fill buffer information for the userspace */
	//填充视频数据给v4l2_buffer,返回应用层
	__fill_v4l2_buffer(vb, b);
	/* Remove from videobuf queue */
	//删除vb2_buffer->queued_entry
	list_del(&vb->queued_entry);
	//表示出队列的buffer个数减1
	q->queued_count--;
	/* go back to dequeued state */
	/*将vb2_buffer->state置为VB2_BUF_STATE_DEQUEUED*/
	__vb2_dqbuf(vb);

	dprintk(1, "dqbuf of buffer %d, with state %d\n",
			vb->v4l2_buf.index, vb->state);

	return 0;
}
  1. vidioc_dqbuf
  2. vb2_ioctl_dqbuf
  3. vb2_dqbuf
  4. vb2_internal_dqbuf
    1. __vb2_get_done_vb
static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
   			struct v4l2_buffer *b, int nonblocking)
{
   unsigned long flags;
   int ret;

   /*
    * Wait for at least one buffer to become available on the done_list.
    */
    //如果ret=0,表示获取到了有效的buffer
   ret = __vb2_wait_for_done_vb(q, nonblocking);
   if (ret)
   	return ret;

   /*
    * Driver's lock has been held since we last verified that done_list
    * is not empty, so no need for another list_empty(done_list) check.
    */
   spin_lock_irqsave(&q->done_lock, flags);
   //从vb2_queue的done_list取出前面有效的vb2_buffer
   *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
   /*
    * Only remove the buffer from done_list if v4l2_buffer can handle all
    * the planes.
    */
    //检查planes是否符合要求
   ret = __verify_planes_array(*vb, b);
   if (!ret)
   	list_del(&(*vb)->done_entry);
   spin_unlock_irqrestore(&q->done_lock, flags);

   return ret;
}
  1. vidioc_dqbuf
  2. vb2_ioctl_dqbuf
  3. vb2_dqbuf
  4. vb2_internal_dqbuf
    1. __vb2_get_done_vb

      1.__vb2_wait_for_done_vb

static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking)
{
	/*
	 * All operations on vb_done_list are performed under done_lock
	 * spinlock protection. However, buffers may be removed from
	 * it and returned to userspace only while holding both driver's
	 * lock and the done_lock spinlock. Thus we can be sure that as
	 * long as we hold the driver's lock, the list will remain not
	 * empty if list_empty() check succeeds.
	 */

	for (;;) {
		int ret;

		//没有stream on,返回失败
		if (!q->streaming) {
			dprintk(1, "streaming off, will not wait for buffers\n");
			return -EINVAL;
		}

		//产生错误,返回失败
		if (q->error) {
			dprintk(1, "Queue in error state, will not wait for buffers\n");
			return -EIO;
		}

		//如果vb2_queue中done_list双向链表不为空,表示有数据,退出循环
		if (!list_empty(&q->done_list)) {
			/*
			 * Found a buffer that we were waiting for.
			 */
			break;
		}

		//如果是非阻塞方式,返回失败值,不等待
		if (nonblocking) {
			dprintk(1, "nonblocking and no buffers to dequeue, "
								"will not wait\n");
			return -EAGAIN;
		}

		//执行到这里,表示使用的是阻塞式获取摄像头数据

		/*
		 * We are streaming and blocking, wait for another buffer to
		 * become ready or for streamoff. Driver's lock is released to
		 * allow streamoff or qbuf to be called while waiting.
		 */
		 //释放互斥锁mutex_unlock(vb2_queue->lock)
		call_void_qop(q, wait_prepare, q);

		/*
		 * All locks have been released, it is safe to sleep now.
		 */
		dprintk(3, "will sleep waiting for buffers\n");
		/*如果!list_empty(&q->done_list) || !q->streaming ||q->error则唤醒所在的进程
		* 否则阻塞在这里,等待唤醒
		*/
		ret = wait_event_interruptible(q->done_wq,
				!list_empty(&q->done_list) || !q->streaming ||
				q->error);

		/*
		 * We need to reevaluate both conditions again after reacquiring
		 * the locks or return an error if one occurred.
		 */
		 //获取互斥锁mutex_lock(vb2_queue->lock)
		call_void_qop(q, wait_finish, q);
		if (ret) {
			dprintk(1, "sleep was interrupted\n");
			return ret;
		}
	}
	return 0;
}
  1. vidioc_dqbuf
  2. vb2_ioctl_dqbuf
  3. vb2_dqbuf
  4. vb2_internal_dqbuf
    1. __vb2_get_done_vb

      1.__vb2_wait_for_done_vb 2.__fill_v4l2_buffer __fill_v4l2_buffer用来将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;
	}

   /*
    * 在前面将vb->state置为VB2_BUF_STATE_DONE,给v4l2_buffer的flag
    * 添加VB2_BUF_STATE_DONE状态
    */
	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;
}
  1. vidioc_dqbuf
  2. vb2_ioctl_dqbuf
  3. vb2_dqbuf
  4. vb2_internal_dqbuf
    1. __vb2_get_done_vb

      1.__vb2_wait_for_done_vb 2.__fill_v4l2_buffer

    1. __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) {
   	/*获取之前设置的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
   		call_memop(vb, num_users, mem_priv)会返回atomic_read(&buf->refcount)的值
   	*/
   	if (mem_priv && call_memop(vb, num_users, mem_priv) > 1)
   		return true;
   }
   return false;
}

简单来说VIDIOC_DQBUF主要将放入队列中buffer返回给应用层