v4l2应用框架-摄像头v4l2编程(15)_出队VIDIOC_DQBUF
参考链接:
(127条消息) 从应用调用vivi驱动分析v4l2 – 出队列(VIDIOC_DQBUF)_dianlong_lee的博客-CSDN博客_vidioc_dqbuf
(127条消息) V4L2框架-缓存出队列(VIDIOC_DQBUF)_深海带鲤鱼的博客-CSDN博客_vidioc_dqbuf
VIDIOC_QBUF
这里单独说一下:VIDIOC_QBUF和VIDIOC_DQBUF
VIDIOC_QBUF /* 将空闲的内存加入可捕获视频的队列
VIDIOC_DQBUF /* 将已经捕获好视频的内存拉出已捕获视频的队列 *
在uvc_gadget.c里执行:
if ((ret = ioctl(dev->fd, VIDIOC_QBUF, &buf)) < 0) {
printf("Unable to requeue buffer: %s (%d).\n", strerror(errno),
errno);
return ret;
}
会调用到内核态。
使用VIDIOC_DQBUF命令调用ioctl,最终会调用到vb2_dqbuf函数,内核使用vb2_dqbuf函数将填充满数据的缓存从驱动中返回给应用。
[include/media/videobuf2-core.h]
// q-缓冲区队列数据结构struct vb2_queue指针
// b-v4l2_buffer结构体指针
// nonblocking-阻塞标志,根据file->f_flags & O_NONBLOCK决定
// 返回值-0成功,小于0失败
int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking);
vb2_dqbuf主要的工作如下:
(1)检查缓冲区是否可用。图像采集模式下,需要等待图像数据填充到缓冲区中才能被使用。非阻塞且无缓冲区可用,则直接返回-EAGAIN错误。阻塞且无缓冲区可用,睡眠等待。
(2)缓冲区可用,则获取一个可用的缓冲区并将其从done_list链表中删除。
(3)将可用的缓冲区信息拷贝到用户空间。
(4)将可用的缓冲区从queued_list链表中删除。
(5)设置缓冲区状态为VB2_BUF_STATE_DEQUEUED。
vb2_dqbuf
->vb2_internal_dqbuf
// 等待缓冲器是否可用,缓冲区可用,返回0
// 非阻塞且无缓冲区可用,则直接返回-EAGAIN错误
// 阻塞且无缓冲区可用,睡眠等待
->__vb2_wait_for_done_vb
// 睡眠等待缓冲区
call_void_qop(q, wait_prepare, q)
->vb2_ops_wait_prepare // wait_prepare最终调用vb2_ops_wait_prepare函数
->vb2_ops_wait_prepare
->mutex_unlock(vq->lock); // 释放锁
->wait_event_interruptible // 睡眠等待缓冲区可用
// 缓冲区可用被唤醒,执行wait_finish函数,最终调用vb2_ops_wait_finish函数
call_void_qop(q, wait_finish, q)
->vb2_ops_wait_finish
->mutex_lock(vq->lock); // 获取锁
->spin_lock_irqsave(&q->done_lock, flags) // 获取自旋锁
// 获取done_list链表中一个可用的缓冲区
list_first_entry(&q->done_list, struct vb2_buffer, done_entry)
->__verify_planes_array // 确保取出的缓冲区所有planes可用
list_del(&(*vb)->done_entry) // 缓冲区所有planes可用,则将缓冲区从done_list链表移除
spin_unlock_irqrestore(&q->done_lock, flags) // 释放自旋锁
// 调用buf_finish函数,imx6ull平台没有实现
call_void_vb_qop(vb, buf_finish, vb)
->__fill_v4l2_buffer(vb, b) // 将出队缓冲区信息填充到用户空间
list_del(&vb->queued_entry); // 将缓冲区从queued_entry链表中删除
q->queued_count--; // 入队缓冲区减1
->__vb2_dqbuf
vb->state = VB2_BUF_STATE_DEQUEUED; // 设置缓冲器为VB2_BUF_STATE_DEQUEUED状态
内核态的执行流程
uvc_gadget.c的VIDIOC_DQBUF和VIDIOC_QBUF对应到内核的驱动接口是:uvc_v4l2_dqbuf和uvc_v4l2_qbuf,
结构体定义在: kernel/msm-4.14/include/media/v4l2-ioctl.h
const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
.vidioc_querycap = uvc_v4l2_querycap,
.vidioc_g_fmt_vid_out = uvc_v4l2_get_format,
.vidioc_s_fmt_vid_out = uvc_v4l2_set_format,
.vidioc_reqbufs = uvc_v4l2_reqbufs,
.vidioc_querybuf = uvc_v4l2_querybuf,
.vidioc_qbuf = uvc_v4l2_qbuf,
.vidioc_dqbuf = uvc_v4l2_dqbuf,
.vidioc_streamon = uvc_v4l2_streamon,
.vidioc_streamoff = uvc_v4l2_streamoff,
.vidioc_subscribe_event = uvc_v4l2_subscribe_event,
.vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event,
.vidioc_default = uvc_v4l2_ioctl_default,
};
VIDIOC_DQBUF程序分析
uvc_v4l2_dqbuf
static int
uvc_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
>---struct video_device *vdev = video_devdata(file);
>---struct uvc_device *uvc = video_get_drvdata(vdev);
>---struct uvc_video *video = &uvc->video;
>---return uvcg_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK);
}
uvcg_dequeue_buffer
uvc_v4l2_dqbuf
–>uvcg_dequeue_buffer
/*
* Dequeue a video buffer. If nonblocking is false, block until a buffer is
* available.
*/
int uvcg_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
>--->---int nonblocking)
{
return vb2_dqbuf(&queue->queue, buf, nonblocking);
}
vb2_dqbuf
uvc_v4l2_dqbuf
–>uvcg_dequeue_buffer
---->vb2_dqbuf
/**
* vb2_dqbuf() - Dequeue a buffer to the userspace
* @q:>->---videobuf2 queue
* @b:>->---buffer structure passed from userspace to VIDIOC_DQBUF() handler
*>->---in driver
* @nonblocking: if true, this call will not sleep waiting for a buffer if no
*>->--- buffers ready for dequeuing are present. Normally the driver
*>->--- would be passing (file->f_flags & O_NONBLOCK) here
*
* Should be called from VIDIOC_DQBUF() ioctl handler of a driver.
*
* This function:
*
* #) verifies the passed buffer,
* #) calls buf_finish callback in the driver (if provided), in which
* driver can perform any additional operations that may be required before
* returning the buffer to userspace, such as cache sync,
* #) the buffer struct members are filled with relevant information for
* the userspace.
*
* The return values from this function are intended to be directly returned
* from VIDIOC_DQBUF() handler in driver.
*/
int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking)
{
int ret;
if (vb2_fileio_is_active(q)) {
dprintk(1, "file io in progress\n");
return -EBUSY;
}
if (b->type != q->type) {
dprintk(1, "invalid buffer type\n");
return -EINVAL;
}
ret = vb2_core_dqbuf(q, NULL, b, nonblocking);
/*
* After calling the VIDIOC_DQBUF V4L2_BUF_FLAG_DONE must be
* cleared.
*/
b->flags &= ~V4L2_BUF_FLAG_DONE;
return ret;
}
vb2_core_dqbuf
uvc_v4l2_dqbuf
–>uvcg_dequeue_buffer
---->vb2_dqbuf
------>vb2_core_dqbuf
int vb2_core_dqbuf(struct vb2_queue *q, unsigned int *pindex, void *pb,
bool nonblocking)
{
struct vb2_buffer *vb = NULL;
int ret;
//ret = 0的时候,得到有效的buf的地址 vb 看5.4.1
// 等待缓冲器是否可用,缓冲区可用,返回0
// 非阻塞且无缓冲区可用,则直接返回-EAGAIN错误
// 阻塞且无缓冲区可用,睡眠等待
ret = __vb2_get_done_vb(q, &vb, pb, nonblocking);
if (ret < 0)
return ret;
switch (vb->state) {
//正常来说,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;
}
/*
* vb可以访问到缓冲区,驱动可以最后对缓冲区的数据进行修改
* 不修改的话,后面将释放放给用户空间
*/
call_void_vb_qop(vb, buf_finish, vb);
if (pindex)
*pindex = vb->index;
/* Fill buffer information for the userspace */
if (pb)
/*
* __vb2_get_done_vb中分析过
* 如果这个last_buffer_dequeued置位了,会不会忽略最后一帧
* last_buffer_dequeued的条件是
* 1.V4L2_BUF_FLAG_DONE,vb2_buffer_done中会置位
* 2.V4L2_BUF_FLAG_DONE,驱动中可以置位
* 以上2个条件成立才会将last_buffer_dequeued置1
* 关键是last_buffer_dequeued在哪里置1呢?
* 就是下面这个fill_user_buffer
* 说明出来完标记了V4L2_BUF_FLAG_DONE和V4L2_BUF_FLAG_LAST的
* 这最后一帧后,才将last_buffer_dequeued置1,所以最后一帧不会丢
*/
call_void_bufop(q, fill_user_buffer, vb, pb);
/* Remove from videobuf queue */
list_del(&vb->queued_entry);
q->queued_count--;
trace_vb2_dqbuf(q, vb);
/* go back to dequeued state */
/*
* 对于MMAP
* 主要就是 vb->state = VB2_BUF_STATE_DEQUEUED;
* 更改状态
*/
__vb2_dqbuf(vb);
dprintk(2, "dqbuf of buffer %d, with state %d\n",
vb->index, vb->state);
return 0;
}
__vb2_get_done_vb
uvc_v4l2_dqbuf
–>uvcg_dequeue_buffer
---->vb2_dqbuf
------>vb2_core_dqbuf
-------->__vb2_get_done_vb
/**
* __vb2_get_done_vb() - get a buffer ready for dequeuing
*
* Will sleep if required for nonblocking == false.
*/
static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb,
void *pb, int nonblocking)
{
unsigned long flags;
int ret = 0;
/*
* 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);
//得到一个有效的buffer,*vb是地址
*vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry);
/*
* Only remove the buffer from done_list if all planes can be
* handled. Some cases such as V4L2 file I/O and DVB have pb
* == NULL; skip the check then as there's nothing to verify.
*/
if (pb)
ret = call_bufop(q, verify_planes_array, *vb, pb);
//verify_planes_array 这里对应的是多平台
if (!ret)
//将buffer从done_list上移除
list_del(&(*vb)->done_entry);
spin_unlock_irqrestore(&q->done_lock, flags);
return ret;
}
__vb2_wait_for_done_vb
uvc_v4l2_dqbuf
–>uvcg_dequeue_buffer
---->vb2_dqbuf
------>vb2_core_dqbuf
-------->__vb2_get_done_vb
---------->__vb2_wait_for_done_vb
/**
* __vb2_wait_for_done_vb() - wait for a buffer to become available
* for dequeuing
*
* Will sleep if required for nonblocking == false.
*/
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 这里streaming值为1
* 如何为0,说明执行了stream_off操作,就不会有数据继续产生
* 所以这里也不用继续执行了
*/
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;
}
/*
* 如果这个last_buffer_dequeued置位了
* 那么说明后面没有有效的帧数据了,所以直接返回就行
* 问题来了,这里会不会忽略最后一帧?
* 答案是不会,原因后面分析
*/
if (q->last_buffer_dequeued) {
dprintk(3, "last buffer dequeued already, will not wait for buffers\n");
return -EPIPE;
}
/*
* done_list上有buffer则跳出这个循环,继续往下走
* 对于使用了select的方式,这里应该就返回了
*/
if (!list_empty(&q->done_list)) {
/*
* Found a buffer that we were waiting for.
*/
break;
}
/*
* 如果应用传递的是不堵塞
* 那么这里直接返回
*/
if (nonblocking) {
dprintk(3, "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.
*/
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");
/*
* wait_event_interruptible(wq, condition)
* 对于condition来说
* condition = 0 休眠
* condition = 1 唤醒
* 前提是wake_up_interruptible唤醒后,进一步才是condition
* 对于返回值
* 1.condition = 1时调用这个函数将直接返回0
* 2.正常wakeup且condition=1 返回0
* 3.其他信号唤醒,返回负值
*/
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.
*/
/*
* wait_finish 对应vivi驱动的vivi_lock
* 主要就是代码 mutex_lock(&dev->mutex);
* 获取自旋锁
*
* 如果这样的话,就算stream_off,自旋锁还是没有释放 ???
*/
call_void_qop(q, wait_finish, q);
if (ret) {
dprintk(1, "sleep was interrupted\n");
return ret;
}
}
return 0;
}
__vb2_dqbuf
uvc_v4l2_dqbuf
–>uvcg_dequeue_buffer
---->vb2_dqbuf
------>vb2_core_dqbuf
-------->__vb2_dqbuf
/**
* __vb2_dqbuf() - bring back the buffer to the DEQUEUED state
*/
static void __vb2_dqbuf(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
unsigned int i;
/* nothing to do if the buffer is already dequeued */
if (vb->state == VB2_BUF_STATE_DEQUEUED)
return;
vb->state = VB2_BUF_STATE_DEQUEUED;
/* unmap DMABUF buffer */
if (q->memory == VB2_MEMORY_DMABUF)
for (i = 0; i < vb->num_planes; ++i) {
if (!vb->planes[i].dbuf_mapped)
continue;
call_void_memop(vb, unmap_dmabuf, vb->planes[i].mem_priv);
vb->planes[i].dbuf_mapped = 0;
}
}