【v4l2】V4L2框架-videobuf2(二)

系列文章目录

【V4L2】V4L2框架简述
【V4L2】V4L2框架之驱动结构体
【V4L2】V4L2子设备
【V4L2】V4L2框架-media device
【V4L2】V4L2框架-videobuf2


文章目录

  • 系列文章目录
  • 用户空间的操作
  • /dev/video 节点与 videobuf2 联系
  • 编程注意事项


用户空间的操作

在这里插入图片描述

  • 用户空间 stream 操作 ioctl 调用流程:
  1. VIDIOC_S_PARAM::参数为 struct v4l2_streamparm,主要设置帧率,type 与 capturemode/outputmode 等。内核的 ioctl 需要保存 capturemode(mode需自定义)帧率等数据,可以保存到自定义的结构体里面。也可以在内核里面通过 v4l2_subdev_call 来调用

  2. VIDIOC_S_FMT::参数为 struct v4l2_format,主要设置长宽,数据格式等。内核的 ioctl 需要保存长宽,像素格式,所在的 field 等。必要时需要调用相关的子设备的结构体进行子设备的格式设置。

  3. VIDIOC_REQBUFS::参数为 struct v4l2_requestbuffers,设置 count,type 与 memory 请求数据,在 queue_setup 回调函数里面需要设置一些东西。参见上面关于 queue_setup 的描述。

  4. VIDIOC_QUERYBUF::参数为 struct v4l2_buffer,需设置 index,type,memory,length(plane length) 等参数

  5. VIDIOC_QBUF,该操作与上一个不断循环,直到获取所有的 buf 并将 buf 入队到内核驱动的的 buf 管理列表中

  6. VIDIOC_STREAMON::参数为 enum v4l2_buf_type,开启指定类型的 stream。

  7. select 等待 buf 可读。在内核驱动里面需要获取 plane 的地址,vb2_plane_vaddr,在数据填充完毕之后需要调用 vb2_buffer_done 来标注该 buffer 已经成功填充完毕,以便 v4l2 把数据放入 done_list,以待用户空间进行读取。

  8. VIDIOC_DQBUF,该操作与上一个操作不断循环,直到停止数据采集

  9. VIDIOC_STREAMOFF,结束数据采集工作。


/dev/video 节点与 videobuf2 联系

首先 video_device 需要有自己的 open,release,unlocked_ioctl 等等,一个常见的初始化操作如下「此处参照了 omap3isp 里面的代码」:

static struct v4l2_file_operations isp_video_fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = video_ioctl2,
    .open = isp_video_open,
    .release = isp_video_release,
    .poll = isp_video_poll,
    .mmap = isp_video_mmap,
};

其中那几个函数几乎都是与 videobuf2 是挂钩的,如果去看下内核里面的代码就可以了然了,其中实用频率最高的就是 .unlocked_ioctl = video_ioctl2, 这个回调函数了,这个回调函数是用户空间通过 /dev/videoX 节点通往 videobuf2 的不二路径,所以干脆直接就把这个成员赋值为 video_ioctl2 了,这个是 videobuf2 为我们提供的操作函数,那就拿起来并使用它吧。从用户空间的角度看,只有一个 ioctl 的入口,但是我们穿过重重关卡,来到内核之后,再往前走两步,就可谓之「初极狭,才通人。复行数十步,豁然开朗」。里面还有一大坨分门别类的 ioctl 回调函数,截取如下:

static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
    .vidioc_querycap        = isp_video_querycap,
    .vidioc_g_fmt_vid_cap        = isp_video_get_format,
    .vidioc_s_fmt_vid_cap        = isp_video_set_format,
    .vidioc_try_fmt_vid_cap        = isp_video_try_format,
    .vidioc_g_fmt_vid_out        = isp_video_get_format,
    .vidioc_s_fmt_vid_out        = isp_video_set_format,
    .vidioc_try_fmt_vid_out        = isp_video_try_format,
    .vidioc_cropcap            = isp_video_cropcap,
    .vidioc_g_crop            = isp_video_get_crop,
    .vidioc_s_crop            = isp_video_set_crop,
    .vidioc_g_parm            = isp_video_get_param,
    .vidioc_s_parm            = isp_video_set_param,
    .vidioc_reqbufs            = isp_video_reqbufs,
    .vidioc_querybuf        = isp_video_querybuf,
    .vidioc_qbuf            = isp_video_qbuf,
    .vidioc_dqbuf            = isp_video_dqbuf,
    .vidioc_streamon        = isp_video_streamon,
    .vidioc_streamoff        = isp_video_streamoff,
    .vidioc_enum_input        = isp_video_enum_input,
    .vidioc_g_input            = isp_video_g_input,
    .vidioc_s_input            = isp_video_s_input,
};

有了上面两个,我们只需要在初始化 video_device 的时候做以下的操作,此时便可以把整个串联起来了:

video->video.fops = &isp_video_fops;               /*  重点 */
video->video.vfl_type = VFL_TYPE_GRABBER;
video->video.release = video_device_release_empty; /*  重点 */
video->video.ioctl_ops = &isp_video_ioctl_ops;     /*  重点 */
video->pipe.stream_state = ISP_PIPELINE_STREAM_STOPPED;

看到这里是不是就把 /dev/videoX,video_device,vb2_queue,videobuf2 这几个全部贯通起来了.
重新梳理一遍:首先用户 open 一个 /dev/videoX,获取其句柄,同时触发内核的 open 函数内部对 videobuf2 的 vb2_queue 进行初始化;然后进行一系列的 ioctl 操作,入口是 isp_video_fops->unlocked_ioctl 成员,再往后会细分为 isp_video_ioctl_ops 里面的一个个回调,这一个个回调与 vb2 众多的 ops 深度结合起来共同完成了数据流的管理工作。

编程注意事项

  • 要特别注意 plane 与 buffer 索引的区别,一个 buffer 下面有多个 plane,应该是这样的循环方式:
    for (buffer) {
        for (plane) {
            ... ...
        }
    }
  • 像下面的 ioctl 尽量可以使用 vb2 提供的回调函数,如果需要自己实现的话也需要显式调用 vb2 提供的回调函数:
V4L24L2 ioctls
    VIDIOC_REQBUFS
    VIDIOC_QUERYBUF
    VIDIOC_QBUF
    VIDIOC_DQBUF

VB2 ioctls
    .start_streaming
    .stop_streaming
  • 需要自己实现一个自旋锁用于 buffer 队列管理,关于为什么用自旋锁,原因是当驱动要获取数据的时候,可以加锁,然后从该队列删除,释放锁,填充数据可以等到释放锁之后进行,所以没必要用别的,自旋锁轻量,在这种情况下自旋锁的使用成本较低。通常在驱动里面维护一个 list 队列,存放激活的 buffer 数据,使用自旋锁自行管理,在 queue 的时候入队,数据填充完毕之后出队,调用 vb2_buffer_done 来放入 vb2 的 buffer 队列等待用户拿取。在 vb2_queue 初始化的时候可以设置 buf_struct_size 成员为自定义大小的帧结构体(自定义的结构体要把 vb2_v4l2_buffer 放在第一个成员的位置,在里面可以添加帧的属性等等),要用到的时候就使用 container_of 来获取自定义的帧结构体。
  • 使用wait队列来定时产生数据:
    init_waitqueue_head

kernel thread
    set_freezable

    DECLARE_WAITQUEUE
    add_wait_queue

    generate datas ...

    schedule_timeout_interruptible
    remove_wait_queue
    try_to_freeze

wake_up_interruptible
  • 在 streamoff 的时候需要调用 vb2_buffer_done(, ERR) 来归还所有的 buffer 到 vb2 里面。只要调用 vb2_buffer_done,该 buffer 就会被放入到 vb2 的各种队列里面,剩下的事情就是调用 vb2_ioctl_streamoff 让 vb2 来完成数据的释放啦。

  • 如何获取 vb2_buffer 的虚拟地址与物理地址

dma-contig, scatter/gather-dma,vmalloc(只有虚拟地址)
虚拟地址:vb2_plane_vaddr()
物理地址:
    dma-contig:vb2_dma_contig_plane_dma_addr
    scatter/gather-dma:vb2_dma_sg_plane_desc
  • 编程步骤
  1. 自定义一个 video 设备结构体,最简单的里面需要包含,video_devicevb2_queue 以及自定义的 buffer 帧结构体描述。

  2. 对上面提到的两个结构体 video_devicevb2_queue 进行初始化。

  3. 实现 vb2_ops 结构体,具体的实现以及注意事项参见上面的描述。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/450108.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【rk3229 android7.1.2 替换默认输入法】

问题平台描述 问题描述解决方法 郑重声明:本人原创博文,都是实战,均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Rockchip CPU:rk3229 OS:Android 7.1.2 Kernel: 3.10 问题描述 国内客户,觉得安卓自带的输入法不好用&#x…

C语言从入门到熟悉------第二阶段

printf的用法 printf的格式有四种: (1)printf("字符串\n"); 其中\n表示换行的意思。其中n是“new line”的缩写,即“新的一行”。此外需要注意的是,printf中的双引号和后面的分号必须是在英文输入法下。双引…

如何选择满足业务需求的CRM系统?六大评估标准全解析!

任何企业在最终部署CRM管理系统前,都会经历一系列决断环节,例如是否要使用CRM、选择什么样的系统、前期投入是多少、预期的投资回报率等等。在挑选CRM系统这个环节,企业更是面临着大量的选择。市场上CRM厂商数量众多,产品宣传让人…

【Python】一文带你了解如何获取 Python模块 安装路径

【Python】一文带你了解如何获取 Python模块 安装路径 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程👈 希望得到您的订阅…

ICC2:function eco / premask eco参考脚本

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 相关文章链接: ICC2:修short参考脚本 eco_netlist -by_verilog -file eco.v -write_changes eco.tcl source eco tcl place_eco_cells -eco_change_cells -no_legalize place_eco_cells -eco_cha…

c++: 引用能否替代指针? 详解引用与指针的区别.

文章目录 前言1. 引用和指针的最大区别:引用不能改变指向2. 引用和指针在底层上面是一样的3. 引用和指针在sizeof面前大小不同4. 有多级指针,没有多级引用5.引用是引用的实体,指针会向后偏移同一个类型的大小 总结 前言 新来的小伙伴如果不知道引用是什么?可以看我的上一篇文…

应用方案 | D78040场扫描电路

一 描 述 D78040是一款场扫描电路,偏转电流可达1.7Ap-p,可用于中小型显示器。 二 特 点 1、有内置泵电源 2、垂直输出电路 3、热保护电路 4、偏转电流可达1.7Ap-p 三 基本参数 四 应用电路图 1、应用线路 2、PIN5脚输出波形如下&#…

java中数组的定义与使用

Java中的数组跟c语言的数组几乎不一样,我们要区分对待。在之后你就能理解到我为什么说这句话了。 1.java中数组的创建与初始化 数组的创建 如下,皆为数组的创建。 double[] a; int[] b; 创建时的[]里面绝对不能填数字。 数组的初始化 主要分为动态…

Javaweb之Maven高级分模块设计与开发的详细解析

1. 分模块设计与开发 1.1 介绍 所谓分模块设计,顾名思义指的就是我们在设计一个 Java 项目的时候,将一个 Java 项目拆分成多个模块进行开发。 1). 未分模块设计的问题 如果项目不分模块,也就意味着所有的业务代码是不是都写在这一个 Java 项…

如何使用最大努力通知实现分布式事务?与本地消息表区别?

什么是最大努力通知? 最大努力通知(Best-Effort Notification)是一种在分布式系统中处理分布式事务的方法之一,它强调尽力而为,不保证完全的事务一致性,但可以通过一定的机制来提供部分保证。在最大努力通…

程序员有哪些常用的技术网站呢?

在当今信息化时代,程序员们能够通过互联网接触到许多优秀的技术网站,这些网站为他们提供了丰富的学习资源和交流平台。这些技术网站涵盖了各种软件开发、设计、数据分析和人工智能等领域,为程序员们提供了广阔的学习空间和交流机会。在这篇文…

蓝桥杯[OJ 3412]-最小化战斗力差距-CPP-贪心

目录 一、问题描述: 二、整体思路: 三、代码: 一、问题描述: 二、整体思路: 首先每个值都有可能为min(b),那么对于每个可能为min(b)的值,要使得max(a)尽可能小,因此枚举所有相差最…

每日学习笔记:C++ STL 的List

定义 特点 操作函数 关于c.merge(c2)的分析,详见: 。。。。 C list merge()用法及代码示例 - 纯净天空 (vimsky.com) 异常安全性 运用实例

Python 导入Excel三维坐标数据 生成三维曲面地形图(面) 4-3、线条平滑曲面(原始颜色)去除无效点

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata fro…

Arm MMU深度解读

文章目录 一、MMU概念介绍二、虚拟地址空间和物理地址空间2.1、(虚拟/物理)地址空间的范围2.2、物理地址空间有效位(范围) 三、Translation regimes四、地址翻译/几级页表?4.1、思考:页表到底有几级?4.2、以4KB granule为例,页表的…

抽象工厂模式——创建型模式

抽象工厂模式——创建型模式 抽象工厂模式是一种软件设计模式,它解决了在创建一组相关或相互依赖的对象时的一些核心问题。其核心问题包括: 对象的创建与使用分离: 抽象工厂模式通过引入抽象工厂接口以及具体工厂类,将对象的创建与…

高浓度氨氮废水如何处理达标排放

高浓度氨氮废水的处理和达标排放是环境保护工作中的重要任务之一。随着工业化进程的加速和人们环保意识的增强,如何有效处理高浓度氨氮废水已成为一个迫切需要解决的问题。本文将探讨高浓度氨氮废水的处理方法和技术,以确保其达到排放标准,减…

面向IoT物联网的时间序列引擎

1、背景 随着近年来业务的发展,尤其是机器产生的数据占比越来越高的趋势下,时序数据因为其业务价值越来越被更多地关注,也因而催生了专用的时间序列数据库,简称时序数据库(TimeSeries Database,TSDB&#x…

leetcode 热题 100_螺旋矩阵

题解一: 模拟:定义四个边界,指针按右下左上的顺序遍历,每遍历一条边,边界就减一,并且在某个方向没有可以遍历的数时直接返回。 import java.util.ArrayList; import java.util.List;class Solution {publi…

Python中的异常处理及最佳实践【第125篇—异常处理】

Python中的异常处理及最佳实践 异常处理是编写健壮、可靠和易于调试的Python代码中不可或缺的一部分。在本文中,我们将深入探讨Python中的异常处理机制,并分享一些最佳实践和代码示例,以帮助您更好地处理错误情况和提高代码的稳定性。 异常…