从vivi学习V4L2架构(九):开启数据流

一、应用开启Stream on

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

if (ioctl(fd, VIDIOC_STREAMON, &type) < 0)
      printf ("VIDIOC_STREAMON failed\n");

二、驱动处理流程

先看__video_do_ioctl, 根据cmd直接调用ioctl_ops的vidioc_streamon

static long __video_do_ioctl(struct file *file,
		unsigned int cmd, void *arg)
{
	struct video_device *vfd = video_devdata(file);
	const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
	void *fh = file->private_data;
	struct v4l2_fh *vfh = NULL;
	struct v4l2_format f_copy;
	int use_fh_prio = 0;
	long ret = -EINVAL;
... ...
	case VIDIOC_STREAMON:
	{
		enum v4l2_buf_type i = *(int *)arg;

		if (!ops->vidioc_streamon)
			break;
		dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
        //直接调用video_device.ioctrl_ops.vidioc_streamon
		ret = ops->vidioc_streamon(file, fh, i);
		break;
	}
... ...
}

进入看下vidioc_streamon

static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
	struct vivi_dev *dev = video_drvdata(file);
	return vb2_streamon(&dev->vb_vidq, i);
}

int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type)
{
	struct vb2_buffer *vb;
	int ret;

	if (q->fileio) {
		dprintk(1, "streamon: file io in progress\n");
		return -EBUSY;
	}

	if (type != q->type) {
		dprintk(1, "streamon: invalid stream type\n");
		return -EINVAL;
	}

	if (q->streaming) {
		dprintk(1, "streamon: already streaming\n");
		return -EBUSY;
	}

	/*
	 * Cannot start streaming on an OUTPUT device if no buffers have
	 * been queued yet.
	 */
	if (V4L2_TYPE_IS_OUTPUT(q->type)) {
		if (list_empty(&q->queued_list)) {
			dprintk(1, "streamon: no output buffers queued\n");
			return -EINVAL;
		}
	}

	/*
	 * Let driver notice that streaming state has been enabled.
	 */
    //前面判断都跳过,直接切入start_streaming
	ret = call_qop(q, start_streaming, q);
	if (ret) {
		dprintk(1, "streamon: driver refused to start streaming\n");
		return ret;
	}

    //将vb2_queue->streaming状态赋值为1
	q->streaming = 1;

	/*
	 * If any buffers were queued before streamon,
	 * we can now pass them to driver for processing.
	 */
    //在之前我们已经QBUF了,所以queued_list链表不为空,获取vb2_buffer
	list_for_each_entry(vb, &q->queued_list, queued_entry)
		__enqueue_in_driver(vb);

	dprintk(3, "Streamon successful\n");
	return 0;
}
EXPORT_SYMBOL_GPL(vb2_streamon);

start_streaming

static int start_streaming(struct vb2_queue *vq)
{
	struct vivi_dev *dev = vb2_get_drv_priv(vq);
	dprintk(dev, 1, "%s\n", __func__);
    //start_streaming也没有做什么,直接调用vivi_start_generating
	return vivi_start_generating(dev);
}

static int vivi_start_generating(struct vivi_dev *dev)
{
	struct vivi_dmaqueue *dma_q = &dev->vidq;

	dprintk(dev, 1, "%s\n", __func__);

	/* Resets frame counters */
    //复位一些值
	dev->ms = 0;
	dev->mv_count = 0;
	dev->jiffies = jiffies;

	dma_q->frame = 0;
	dma_q->ini_jiffies = jiffies;
    //创建启动vivi_thread线程,这个vivi_thread线程里面做了数据buffer的填充
	dma_q->kthread = kthread_run(vivi_thread, dev, dev->v4l2_dev.name);

	if (IS_ERR(dma_q->kthread)) {
		v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
		return PTR_ERR(dma_q->kthread);
	}
	/* Wakes thread */
    //唤醒dma_q->wq里面的vivi_thread线程
	wake_up_interruptible(&dma_q->wq);

	dprintk(dev, 1, "returning from %s\n", __func__);
	return 0;
}

vivi_thead

static int vivi_thread(void *data)
{
	struct vivi_dev *dev = data;

	dprintk(dev, 1, "thread started\n");

    //设置内核线程可以冻结标志,不知道为什么要这么设置?
	set_freezable();

    //死循环调用vivi_sleep函数,除非该线程应该停止,才会跳出循环
	for (;;) {
		vivi_sleep(dev);

		if (kthread_should_stop())
			break;
	}
	dprintk(dev, 1, "thread: exit\n");
	return 0;
}

vivi_sleep, 这个名字觉得起的不是很好,看代码是先填充buffer再休眠。

static void vivi_sleep(struct vivi_dev *dev)
{
	struct vivi_dmaqueue *dma_q = &dev->vidq;
	int timeout;
    //创建waitqueue
	DECLARE_WAITQUEUE(wait, current);

	dprintk(dev, 1, "%s dma_q=0x%08lx\n", __func__,
		(unsigned long)dma_q);

    //将waitqueue加入到等待队列dma_q->wq里面去
	add_wait_queue(&dma_q->wq, &wait);
	if (kthread_should_stop())
		goto stop_task;

	/* Calculate time to wake up */
    //计算唤醒时间,这里是一帧视频时间
	timeout = msecs_to_jiffies(frames_to_ms(1));

    //vivi_thread_tick主要任务是填充buffer
	vivi_thread_tick(dev);

    //将当前线程调度出去,并在timeout时间到了后唤醒执行
	schedule_timeout_interruptible(timeout);

stop_task:
    //将wait从dma_q->wq中删除
	remove_wait_queue(&dma_q->wq, &wait);
    //不太懂这里的作用
	try_to_freeze();
}

vivi_thread_tick

static void vivi_thread_tick(struct vivi_dev *dev)
{
	struct vivi_dmaqueue *dma_q = &dev->vidq;
	struct vivi_buffer *buf;
	unsigned long flags = 0;

	dprintk(dev, 1, "Thread tick\n");

	spin_lock_irqsave(&dev->slock, flags);
    //理论上第一次进入这个函数dma_q->active是空的,所以后面直接返回
    //要等到vb2_streamon中执行完__enqueue_in_driver(vb);后dma_q->active不为空,才能往下执行。
	if (list_empty(&dma_q->active)) {
		dprintk(dev, 1, "No active queue to serve\n");
		goto unlock;
	}

    //从dma_q->active中获取待填充的vivi_buffer
	buf = list_entry(dma_q->active.next, struct vivi_buffer, list);
	list_del(&buf->list);

    //获取当前时间
	do_gettimeofday(&buf->vb.v4l2_buf.timestamp);

	/* Fill buffer */
    //填充buffer
	vivi_fillbuff(dev, buf);
	dprintk(dev, 1, "filled buffer %p\n", buf);

    //设置vb2_buffer相应标志为VB2_BUF_STATE_DONE
	vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
	dprintk(dev, 2, "[%p/%d] done\n", buf, buf->vb.v4l2_buf.index);
unlock:
	spin_unlock_irqrestore(&dev->slock, flags);
}

vivi_fillbuff

static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
{
	int wmax = dev->width;
	int hmax = dev->height;
	struct timeval ts;
    //获取到buffer的内存地址,用于存放视频数据
	void *vbuf = vb2_plane_vaddr(&buf->vb, 0);
	unsigned ms;
	char str[100];
	int h, line = 1;

	if (!vbuf)
		return;

    //dev->line存放着需要输出的数据,数据是在之前precalculate_line(dev)函数中处理好的
    //通过memcpy赋值到vbuf
	for (h = 0; h < hmax; h++)
		memcpy(vbuf + h * max * 2, dev->line + (dev->mv_count % wmax) * 2, wmax * 2);

	/* Updates stream time */

	dev->ms += jiffies_to_msecs(jiffies - dev->jiffies);
	dev->jiffies = jiffies;
	ms = dev->ms;
	snprintf(str, sizeof(str), " %02d:%02d:%02d:%03d ",
			(ms / (60 * 60 * 1000)) % 24,
			(ms / (60 * 1000)) % 60,
			(ms / 1000) % 60,
			ms % 1000);
    //下面gen_text主要是在每一帧图片上显示一些文字信息
	gen_text(dev, vbuf, line++ * 16, 16, str);
	snprintf(str, sizeof(str), " %dx%d, input %d ",
			dev->width, dev->height, dev->input);
	gen_text(dev, vbuf, line++ * 16, 16, str);

	mutex_lock(&dev->ctrl_handler.lock);
	snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ",
			dev->brightness->cur.val,
			dev->contrast->cur.val,
			dev->saturation->cur.val,
			dev->hue->cur.val);
	gen_text(dev, vbuf, line++ * 16, 16, str);
	snprintf(str, sizeof(str), " volume %3d ", dev->volume->cur.val);
	gen_text(dev, vbuf, line++ * 16, 16, str);
	snprintf(str, sizeof(str), " int32 %d, int64 %lld ",
			dev->int32->cur.val,
			dev->int64->cur.val64);
	gen_text(dev, vbuf, line++ * 16, 16, str);
	snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ",
			dev->boolean->cur.val,
			dev->menu->qmenu[dev->menu->cur.val],
			dev->string->cur.string);
	mutex_unlock(&dev->ctrl_handler.lock);
	gen_text(dev, vbuf, line++ * 16, 16, str);
	if (dev->button_pressed) {
		dev->button_pressed--;
		snprintf(str, sizeof(str), " button pressed!");
		gen_text(dev, vbuf, line++ * 16, 16, str);
	}

    
    //mv_count不知道干什么?
	dev->mv_count += 2;

	buf->vb.v4l2_buf.field = dev->field;
	dev->field_count++;
	buf->vb.v4l2_buf.sequence = dev->field_count >> 1;
	do_gettimeofday(&ts);
    //将当前时间戳赋值给buf->vb.v4l2_buf.timestamp
	buf->vb.v4l2_buf.timestamp = ts;
}

vb2_plane_vaddr

void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no)
{
	struct vb2_queue *q = vb->vb2_queue;

	if (plane_no > vb->num_planes)
		return NULL;

    //调用vb2_vmalloc_vaddr
	return call_memop(q, plane_no, vaddr, vb->planes[plane_no].mem_priv);

}
EXPORT_SYMBOL_GPL(vb2_plane_vaddr)

vb2_vmalloc_vaddr

static void *vb2_vmalloc_vaddr(void *buf_priv)
{
	struct vb2_vmalloc_buf *buf = buf_priv;

	BUG_ON(!buf);

	if (!buf->vaddr) {
		printk(KERN_ERR "Address of an unallocated plane requested\n");
		return NULL;
	}
    
    //直接返回vb->planes[plane_no].mem_priv->vaddr, 这个地址就是VIDIOC_REQBUFS时申请的
	return buf->vaddr;
}

再返回vb2_buffer_done函数

void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state)
{
	struct vb2_queue *q = vb->vb2_queue;
	unsigned long flags;

	if (vb->state != VB2_BUF_STATE_ACTIVE)
		return;

	if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR)
		return;

	dprintk(4, "Done processing on buffer %d, state: %d\n",
			vb->v4l2_buf.index, vb->state);

	/* Add the buffer to the done buffers list */
	spin_lock_irqsave(&q->done_lock, flags);
    //state设置为VB2_BUF_STATE_DONE
	vb->state = state;
    //将vb2_buffer->done_entry加入到vb2_queue->done_list链表上
	list_add_tail(&vb->done_entry, &q->done_list);
    //减少计数
	atomic_dec(&q->queued_count);
	spin_unlock_irqrestore(&q->done_lock, flags);

	/* Inform any processes that may be waiting for buffers */ 
    //通知done_wq上的等待进程
	wake_up(&q->done_wq);
}
EXPORT_SYMBOL_GPL(vb2_buffer_done);

最后返回vb2_streamon中的__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;

    //将vb2_buffer->state改成VB2_BUF_STATE_ACTIVE
	vb->state = VB2_BUF_STATE_ACTIVE;
    增加queued_count计算
	atomic_inc(&q->queued_count);
    //
	q->ops->buf_queue(vb);
}

static void buffer_queue(struct vb2_buffer *vb)
{
	struct vivi_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
	struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb);
	struct vivi_dmaqueue *vidq = &dev->vidq;
	unsigned long flags = 0;

	dprintk(dev, 1, "%s\n", __func__);

	spin_lock_irqsave(&dev->slock, flags);
    //将vivi_buffer->list挂到vidq->active上面去,回到vivi_thread_tick函数里面有对
    //vidq->active是否为空的判断,所以只有__enqueue_in_driver之后,vivi_thread_tick才能
    //往下执行。
	list_add_tail(&buf->list, &vidq->active);
	spin_unlock_irqrestore(&dev->slock, flags);
}

总结:stream_on主要做了,先从vb2_queue->queued_list链表中将vb2_buffer依次取下,挂载到vivi_dev->vivi_dmaqueue->active链表上去。启动vivi_thread线程, 线程里面有一个死循环,循环里从vivi_dev->vidq->active去获取需要填充的buffer,拿到对应buffer后在vivi_fillbuff里面填充,休眠直到下一帧图片需要填充时唤醒或主动调用wake_up_interruptible(&dma_q->wq)进行唤醒,然后继续vivi_thread里面的循环。填充好数据的vb2_buffer从vidq->active链表上删除并挂载到vb2_queue->done_list链表上去


 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据您引用的内容[1],当您在Ubuntu系统中执行命令`source /etc/bash.bashrc`时,出现了错误提示`bash: /usr/local/R16/bin/:是一个目录`。这个错误提示意味着您在执行`source /etc/bash.bashrc`命令时,系统无法找到文件`/usr/local/R16/bin/`,因为它是一个目录而不是文件。 根据您提供的引用,可能是由于解释器错误导致的。您的系统可能没有找到解释器`/usr/bin/python`。这可能是由于Python解释器不存在或未正确安装所致。 为了解决这个问题,您可以尝试以下步骤: 1. 确保您的系统中已经正确安装了Python解释器。您可以通过运行`python --version`命令来检查Python的安装情况。 2. 如果发现Python未正确安装,请根据您的系统要求安装Python解释器。 3. 如果Python已正确安装,但解释器路径不正确,请根据您的系统和Python版本修改`/opt/ros/melodic/_setup_util.py`文件中的解释器路径为正确的路径。 4. 确保您的系统中存在文件`/etc/bash.bashrc`,并且该文件包含所需的配置。您可以使用文本编辑器打开该文件,检查其中的内容。 5. 如果您对`/etc/bash.bashrc`文件进行了更改,请确保您重新执行了`source /etc/bash.bashrc`命令,使更改生效。 希望以上信息能够帮助您解决问题。如果问题仍然存在,请提供更多细节或引用以便我更好地帮助您。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [bash-completion:bash的可编程完成功能](https://download.csdn.net/download/weixin_42097967/14966072)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【关于运行roscore等命令报错:bash: /opt/ros/melodic/_setup_util.py: /usr/bin/python: 解释器错误: ...](https://blog.csdn.net/wxy98520/article/details/123953509)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Linux下virtualenvwrapper安装问题: /usr/local/bin/virtualenvwrapper.sh: No such file or directory的...](https://blog.csdn.net/tokyo_re_tao/article/details/119655866)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值