【genius_platform软件平台开发】第六十二讲:Linux系统之V4L2视频驱动-VIDIOC_STREAMON开启视频流

喜欢的可以加微信群:

【genius_platform软件平台开发】第六十二讲:Linux系统之V4L2视频驱动-VIDIOC_STREAMON开启视频流_第1张图片
【genius_platform软件平台开发】第六十二讲:Linux系统之V4L2视频驱动-VIDIOC_STREAMON开启视频流_第2张图片

VIDIOC_STREAMON开启视频流

  • 喜欢的可以加微信群:
  • 1. 概述
  • 2.应用层
  • 3. 驱动层
    • 3.1 vb2_streamon函数
    • 3.2 vb2_core_streamon函数
    • 3.3 vb2_start_streaming函数
    • 3.4 __enqueue_in_driver函数
    • 3.5 vb2_buffer_done函数

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);  
}

你可能感兴趣的:(linux系统V4L2视频驱动,3,VIDIOC_STREAMON,V4L2)