喜欢的可以加微信群:
VIDIOC_STREAMON开启视频流
1. 概述
- 主要就是让设备启动视频流,驱动中不停的获取视频数据,queued_list中产生源源不断的视频数据帧。
2.应用层
// 开始视频流数据的采集
unBuffType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == this->ioCtrl(m_nFd, VIDIOC_STREAMON, &unBuffType))
{
LOGERROR("CV4l2CaptureIr::startCapture is error... VIDIOC_STREAMON m_unIoMethod=[%d]", m_unIoMethod);
return ReturnCode_Error;
}
LOGMSG("CV4l2CaptureIr::startCapture is suc... VIDIOC_STREAMON nTotalCostTm=[%llu] unBuffType=[%u]", (currentTime() - nTotalCostTm).count(), unBuffType);
3. 驱动层
3.1 vb2_streamon函数
int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
{
return vb2_core_streamon(q, type);
}
3.2 vb2_core_streamon函数
int vb2_core_streamon(struct vb2_queue *q, unsigned int type)
{
int ret;
// 各种条件检查
if (type != q->type) {
dprintk(1, "invalid stream type\n");
return -EINVAL;
}
if (q->streaming) {
dprintk(3, "already streaming\n");
return 0;
}
if (!q->num_buffers) {
dprintk(1, "no buffers have been allocated\n");
return -EINVAL;
}
if (q->num_buffers < q->min_buffers_needed) {
dprintk(1, "need at least %u allocated buffers\n",
q->min_buffers_needed);
return -EINVAL;
}
// 如果有足够的buffer 设备使能,则启动数据流
/*
* Tell driver to start streaming provided sufficient buffers
* are available.
*/
if (q->queued_count >= q->min_buffers_needed) {
ret = v4l_vb2q_enable_media_source(q);
if (ret)
return ret;
ret = vb2_start_streaming(q);
if (ret) {
__vb2_queue_cancel(q);
return ret;
}
}
q->streaming = 1;
dprintk(3, "successful\n");
return 0;
}
EXPORT_SYMBOL_GPL(vb2_core_streamon);
3.3 vb2_start_streaming函数
/**
* vb2_start_streaming() - Attempt to start streaming.
* @q: videobuf2 queue
*
* Attempt to start streaming. When this function is called there must be
* at least q->min_buffers_needed buffers queued up (i.e. the minimum
* number of buffers required for the DMA engine to function). If the
* @start_streaming op fails it is supposed to return all the driver-owned
* buffers back to vb2 in state QUEUED. Check if that happened and if
* not warn and reclaim them forcefully.
*/
static int vb2_start_streaming(struct vb2_queue *q)
{
struct vb2_buffer *vb;
int ret;
// 如果任何缓冲区在streamon之前排队,我们现在可以将它们传递给驱动程序进行处理
// 在 queued_list 链表中取出每一个 buffer 调用buffer queue, 放入 特定设备的active 链表
/*
* If any buffers were queued before streamon,
* we can now pass them to driver for processing.
*/
list_for_each_entry(vb, &q->queued_list, queued_entry)
__enqueue_in_driver(vb);
// 告诉驱动开始取流
// start_streaming_called = 1
/* Tell the driver to start streaming */
q->start_streaming_called = 1;
ret = call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count));
if (!ret)
{
// 这里成功应该会返回0
return 0;
}
// 走到下面应该是启动失败了,start_streaming_called = 0
q->start_streaming_called = 0;
dprintk(1, "driver refused to start streaming\n");
/*
* If you see this warning, then the driver isn't cleaning up properly
* after a failed start_streaming(). See the start_streaming()
* documentation in videobuf2-core.h for more information how buffers
* should be returned to vb2 in start_streaming().
*/
if (WARN_ON(atomic_read(&q->owned_by_drv_count))) {
unsigned i;
/*
* Forcefully reclaim buffers if the driver did not
* correctly return them to vb2.
*/
for (i = 0; i < q->num_buffers; ++i) {
vb = q->bufs[i];
if (vb->state == VB2_BUF_STATE_ACTIVE)
vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED);
}
/* Must be zero now */
WARN_ON(atomic_read(&q->owned_by_drv_count));
}
/*
* If done_list is not empty, then start_streaming() didn't call
* vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED) but STATE_ERROR or
* STATE_DONE.
*/
WARN_ON(!list_empty(&q->done_list));
return ret;
}
3.4 __enqueue_in_driver函数
/**
* __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing
*/
static void __enqueue_in_driver(struct vb2_buffer *vb)
{
struct vb2_queue *q = vb->vb2_queue;
// on之后状态就变味了VB2_BUF_STATE_ACTIVE
// 设置vb state = VB2_BUF_STATE_ACTIVE;
vb->state = VB2_BUF_STATE_ACTIVE;
// on之后 增加 owned_by_drv_count计数了
atomic_inc(&q->owned_by_drv_count);
trace_vb2_buf_queue(q, vb);
call_void_vb_qop(vb, buf_queue, vb);
}
3.5 vb2_buffer_done函数
- 经过特定设备在on之后开启内核线程,通过中断不停的填充数据到vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);然后更新vb->state = VB2_BUF_STATE_DONE。标识已经好了。
void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
{
struct vb2_queue *q = vb->vb2_queue;
unsigned long flags;
unsigned int plane;
/* sync buffers */
for (plane = 0; plane < vb->num_planes; ++plane)
call_memop(q, finish, vb->planes[plane].mem_priv);
// 加锁
/* Add the buffer to the done buffers list */
spin_lock_irqsave(&q->done_lock, flags);
// 更新vb状态未VB2_BUF_STATE_DONE
vb->state = state;
// 添加到done_list列表
list_add_tail(&vb->done_entry, &q->done_list);
// 减少queued_count
atomic_dec(&q->queued_count);
#ifdef CONFIG_SYNC
sw_sync_timeline_inc(q->timeline, 1);
#endif
// 解锁
spin_unlock_irqrestore(&q->done_lock, flags);
/* 应用程序select 时 poll_wait 里休眠,现在有数据了唤醒 */
wake_up(&q->done_wq);
}