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
- vidioc_dqbuf
- vb2_ioctl_dqbuf
- vb2_dqbuf
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;
}
- vidioc_dqbuf
- vb2_ioctl_dqbuf
- vb2_dqbuf
- vb2_internal_dqbuf
- __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;
}
- vidioc_dqbuf
- vb2_ioctl_dqbuf
- vb2_dqbuf
- vb2_internal_dqbuf
-
__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;
}
- vidioc_dqbuf
- vb2_ioctl_dqbuf
- vb2_dqbuf
- vb2_internal_dqbuf
-
__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;
}
- vidioc_dqbuf
- vb2_ioctl_dqbuf
- vb2_dqbuf
- vb2_internal_dqbuf
-
__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返回给应用层