Linux v4l2框架分析

背景

说明:

  1. Kernel版本:4.14

  2. ARM64处理器,Contex-A53,双核

  3. 使用工具:Source Insight 3.5, Visio

1. 概述

  • V4L2(Video for Linux 2):Linux内核中关于视频设备驱动的框架,对上向应用层提供统一的接口,对下支持各类复杂硬件的灵活扩展;

  • V4L2框架,主要包括v4l2-coremeida frameworkvideobuf2等模块,这也是本文将要展开的内容,仅提纲挈领;

开始吧。

2. v4l2-core

2.1 应用视角

先从应用的角度来看如何使用v4l2吧:

e92e369a2acbd0950c20011a11828328.png


假如要进行视频数据采集,大体的步骤如上图左侧所示:

  1. 打开设备文件/dev/videoX

  2. 根据打开的设备,查询设备能力集;

  3. 设置视频数据的格式、参数等;

  4. 分配buffer,这个buffer可以是用户态分配的,也可以是从内核中获取的;

  5. 开始视频流采集工作;

  6. 将buffer enqueue到v4l2框架,底层负责将视频数据填充后,应用层再将buffer dequeue以便获取数据,然后再将buffer enqueue,如此循环往复;

上图右侧是v4l2-core的大体框架,右侧是对硬件的抽象,要想理解好它,可以先看一下较常见的硬件拓扑结构:

408f029fb9491fd0f0a0ec4ae039e253.png


  • 通常一个camera的模组如图所示,通常包括Lens、Sensor、CSI接口等,其中CSI接口用于视频数据的传输;

  • SoC的Mipi接口对接Camera,并通过I2C/SPI控制camera模组;

  • Camera模组中也可以包含ISP模块,用于对图像进行处理,有的SoC中也集成了ISP的IP,接收camera的raw数据后,进行图像处理;

2.2 数据结构

如果以上图的硬件为例,对摄像头的硬件该怎么来抽象呢?没错,就是以v4l2_devicev4l2_subdev来进行抽象,以v4l2_device来代表整个输入设备,以v4l2_subdev来代表子模块,比如CSISensor等;

0aafd312babc88d1b6576c45c32d1194.png


  • v4l2_device:对视频设备的整体进行抽象,可以看成是一个纽带,将各个子设备联系在一起,通常它会嵌入在其他结构体中以提供v4l2框架的功能,比如strcut isp_device

  • v4l2_subdev:对子设备进行抽象,该结构体中包含的struct v4l2_subdev_ops是一个完备的操作函数集,用于对接各种不同的子设备,比如video、audio、sensor等,同时还有一个核心的函数集struct v4l2_subdev_core_ops,提供更通用的功能。子设备驱动根据设备特点实现该函数集中的某些函数即可;

  • video_device:用于向系统注册字符设备节点,以便用户空间可以进行交互,包括各类设置以及数据buffer的获取等,在该结构体中也能看到struct v4l2_ioctl_opsstruct vb2_queue结构体字段,这些与上文中的应用层代码编写息息相关;

  • 如果子设备不需要与应用层交互,struct v4l2_subdev中内嵌的video_device也可以不向系统注册字符设备;

  • video_device结构体,可以内嵌在其他结构体中,以便提供用户层交互的功能,比如struct isp_video

  • 针对图中回调函数集,v4l2-core提供了一些实现,所以driver在实现时,非特殊情况下可以不用重复造轮子;

2.3 流程分析

来进一步看一下内部的注册,及调用流程吧:

4024c479437a77b9a7ff68b189b32c75.png


  • 在驱动实现中,驱动结构体中内嵌struct video_device,同时实现struct v4l2_file_operations结构体中的函数,最终通过video_register_device向提供注册;

  • v4l2_register_device函数通过cdev_add向系统注册字符设备,并指定了file_operations,用户空间调用open/read/write/ioctl等接口,便可回调到驱动实现中;

  • v4l2_register_device函数中,通过device_register向系统注册设备,会在/sys文件系统下创建节点;

完成注册后,用户空间便可通过文件描述符来进行访问,从应用层看,大部分都是通过ioctl接口来完成,流程如下:

e6c35f059441bce425b8f87470c82d79.png


  • 用户层的ioctl回调到__video_do_ioctl中,该函数会对系统提供的struct v4l2_ioctl_info v4l2_ioctls[]表进行查询,找到对应的项后进行调用;

  • 驱动做的工作就是填空题,实现对应的回调,在合适的时候被调用;

下一个小节,让我们看看更复杂一点的情况。

3. media framework

3.1 问题引入

为了更好的描述,本节以omap3isp为例,先看一下它的硬件构成:

cd34f89b605614f7f4b0477c15f323b9.png


  • CSI:camera接口,接收图像数据,RGB/YUV/JPEG等;

  • CCDC:视频处理前端,CCDC为图像传感器和数字视频源提供接口,并处理图像数据;

  • Preview/Resizer:视频处理后端,Preview提供预览功能,可针对不同类型的传感器进行定制,Resizer提供将输入图像数据按所需的显示或视频编码分辨率调整大小的方法;

  • H3A/HIST:静态统计模块,H3A支持AF、AWB、AE的回路控制,HIST根据输入数据,提供各种3A算法所需的统计数据;

上述硬件模块,可以对应到驱动结构体struct isp_device中的各个字段。

omap3isp的硬件模块,支持多种数据流通路,它并不是唯一的,以RGB为例,如下图:

0cd424df7df5baeaa33a4466bd2501a4.png


  • Raw RGB数据进入ISP模块后,可以在运行过程中,根据实际的需求进行通路设置;

  • 所以,重点是:它需要动态设置路径!

那么,软件该如何满足这种需求呢?

3.2 框架

没错,pipeline框架的引入可以解决这个问题。说来很巧,我曾经也实现过一个类似的框架,在阅读media framework时有一种似曾相识的感觉,核心的思想大体一致。

590d9f19fca7b229bb5a65dcbc89e6af.png


  • 模块之间相互独立,通过struct media_entity来进行抽象,通常会将struct media_entity嵌入到其他结构中,以支持media framework功能;

  • entity模块包含struct media_pad,pad可以认为是端口,与其他模块进行联系的媒介,针对特定模块来说它是确定的;

  • pad通过struct media_link来建立连接,指定source和sink,即可将通路建立起来;

  • 各个模块之间最终建立一条数据流,便是一条pipeline了,同一条pipeline中的模块,可以根据前一个模块查找到下一个模块,因此也可以很方便进行遍历,并做进一步的设置操作;

因此,只需要将struct media_entity嵌入到特定子模块中,最终便可以将子模块串联起来,构成数据流。所以,omap3isp的驱动中,数据流就如下图所示:

f98b0103f2223703715021034c272f70.png


  • video devnode代表video device,也就是前文中提到的导出到用户空间的节点,用于与用户进行控制及数据交互;

  • 每个模块分别有source pad和sink pad,从连接图就可以看出,数据通路灵活多变;

  • 至于数据通路选择问题,可以在驱动初始化的时候进行链接创建,比如isp_create_links

还是看一下数据结构吧:

acd9f32bb1bfc36a08f289726e3b4a0d.png


  • media_device:与v4l2_device类似,也是负责将各个子模块集中进行管理,同时在注册的时候,会向系统注册设备节点,方便用户层进行操作;

  • media_entitymedia_padmedia_link等结构体的功能在上文中描述过,注意,这几个结构体会添加到media_device的链表中,同时它们结构体的开始字段都需是struct media_gobj,该结构中的mdev将会指向它所属的media_device。这种设计方便结构之间的查找;

  • media_entity中包含多个media_pad,同时media_pad又会指向它所属的media_entity

  • media_graphmedia_pipelinemedia_entity的集合,直观来理解,就是由一些模块构成的一条数据通路,由一个统一的数据结构来组织管理;

罗列一下常见的几个接口吧,细节不表了:

/* 初始化entity的pads */
int media_entity_pads_init(struct media_entity *entity, u16 num_pads,struct media_pad *pads);/* 在两个entity之间创建link */
int media_create_pad_links(const struct media_device *mdev,const u32 source_function,struct media_entity *source,const u16 source_pad,const u32 sink_function,struct media_entity *sink,const u16 sink_pad,u32 flags,const bool allow_both_undefined);/* 开始graph的遍历,从指定的entity开始 */
void media_graph_walk_start(struct media_graph *graph,struct media_entity *entity);/* 启动pipeline */
__must_check int media_pipeline_start(struct media_entity *entity,struct media_pipeline *pipe);

media frameworkv4l2_devicev4l2_subdev结合起来,就可以将各个子设备构建pipeline,完美!

4. videobuf2

4.1 框架分析

  • 框架可以分成两个部分看:控制流+数据流,上文已经大概描述了控制流,数据流的部分就是video buffer了。

  • V4L2的buffer管理是通过videobuf2来完成的,它充当用户空间和驱动之间的中间层,并提供low-level,模块化的内存管理功能;

d9556da37d223932356c3112072940e6.png


  • 上图大体包含了videobuf2的框架;

  • vb2_queue:核心的数据结构,用于描述buffer的队列,其中struct vb2_buffer *bufs[]是存放buffer节点的数组,该数组中的成员代表了vb2 buffer,并将在queued_listdone_list两个队列中进行流转;

  • struct vb2_buf_ops:buffer的操作函数集,由驱动来实现,并由框架通过call_bufop宏来对特定的函数进行调用;

  • struct vb2_mem_ops:内存buffer分配函数接口,buffer类型分为三种:1)虚拟地址和物理地址都分散,可以通过dma-sg来完成;2)物理地址分散,虚拟地址连续,可以通过vmalloc分配;3)物理地址连续,可以通过dma-contig来完成;三种类型也vb2框架中都有实现,框架可以通过call_memop来进行调用;

  • struct vb2_ops:vb2队列操作函数集,由驱动来实现对应的接口,并在框架中通过call_vb_qop宏被调用;

4.2 流程分析

本节以omap3isp为例进行简要分析,感觉直接看图就可以了:

  1. buffer申请

ceb37f7d41fd423430eb1d7f1d7fdb2b.png


  1. buffer enqueue

c88ea745284763fcc601b0fab76e2dd2.png


  1. buffer dequeue

a4ba0dba556fe8f25af53735b2a73beb.png


  1. stream on

863807a6a6d70899516b5172f5b1904b.png


行文至此,主体讲完了,相信看完本文应该有个大概的轮廓了,还有一些细节未进一步描述,就此打住。

参考

https://lwn.net/Articles/416649/ 

《OMAP35x Technical Reference Manual (Rev. Y).pdf》

end


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

c73fb91b5f45af5d6d321ed8d65b84a3.png

嵌入式Linux

微信扫描二维码,关注我的公众号

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA自学笔记23

JAVA自学笔记23 1、多线程 1)引入: 2)进程 是正在运行的程序。是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。 多进程: 单进程的计算机只能做一件事情,而现在的计算机都可以做…

上午写了一段代码,下午就被开除了~

俗话说得好,“代码写的少,离职少不了”。最近畅游互联网,发现一些离职小技巧,读后,内心被深深地打动了……但是,细细品过之后,发现对我们程序员不太适用了。例如:领导夹菜你转桌&…

nginx加载html目录下图片,nginx配置访问图片路径以及html静态页面的调取方法

nginx配置访问图片路径以及html静态页面的调取方法发布时间:2017-03-09 12:06来源:互联网当前栏目:web技术类给大家讲一个快速配置nginx访问图片地址,以及访问html静态页面的配置。1.实验环境首先随便某个路径下创建相应的目录。如…

微信小程序继续入坑指南

微信小程序继续入坑指南 wxml 类似于html 感觉和ejs灰常的相似 数据绑定 js Page({data: {message: "hello world"} })wxml <view>{{message}}</view> 使用的是https://mustache.github.io/模板引擎系统 对组件的属性和控制属性的更改 <view id"…

思科收购网络安全管理厂商Pari Networks

思科收购网络安全管理厂商Pari Networkshttp://netsecurity.51cto.com 2011-01-28 09:39 胡杨 译 网界网 我要评论(0)摘要&#xff1a;思科本星期宣布&#xff0c;它打算收购私营企业Pari Networks。这个企业是前思科工程师创建的&#xff0c;主要提供网络配置、变更和合规…

20年软件工程师的经验

软件工程师在做设计的时候&#xff0c;一定要有设计的思维&#xff0c;码农如果只是砌砖的&#xff0c;那么他的可替代性和技能能力并不高。前段时间看到一个设计师傅&#xff0c;在很狭小的空间内设计了非常非常不错的室内设计&#xff0c;利用了每一个可以利用的地方。如果我…

html超市代码,前端 CSS : 5# 纯 CSS 实现24小时超市

介绍原文链接感謝 comehope 大佬的 [前端每日实战]效果预览源代码地址代码解读1. html 结构命名规则使用了 BEM常规样式初始化* {margin: 0;padding: 0;box-sizing: border-box;}body {height: 100vh;overflow: hidden;}2. 街道背景街道背景分为两部分深蓝色的天空.street {hei…

送30块树莓派PICO 开发板!

大家好&#xff0c;今天是周日&#xff0c;给大家搞个小抽奖&#xff0c;送30块。嵌入式猛男必备&#xff0c;学嵌入式看『我要学嵌入式』&#xff0c;知识持久有力。点击关注&#xff0c;回复【1031】参与抽奖&#xff0c;免费送 10块 树莓派最新PICO开发板。学C语言看『写代码…

嘉立创又搞大事情了,与你我相关!

你们一定不知道嘉立创最近又悄咪咪的做了一件大事儿&#xff0c;硬创社硬件项目共享平台上线公测啦&#xff0c;这是一个帮助电子工程师实现技术变现的平台&#xff0c;平台刚刚上线公测&#xff0c;前期还在邀请电子工程师填充内容阶段&#xff0c;即日起平台每上传一个项目就…

c html联调,JS与native 交互简单应用

JS与native 交互简单应用一、objectiveC 语法简介二、简易项目浏览器搭建新建项目步骤&#xff1a;1>DraggedImage.png2>2222.png3>33333.png4>4444.png建立一个小的浏览器即webview关键代码如下&#xff1a;// context 上下文也可以在此处获取&#xff0c;开始加载…

写给我弟

我堂弟今年24岁&#xff0c;也是我最小的一个堂弟&#xff0c;我想给他说点事&#xff0c;想告诉他一些东西&#xff0c;但是也担心自己所说的&#xff0c;并不能让他认同和接受。我心里一定是有我弟的&#xff0c;也是装着我弟的&#xff0c;所以我弟的事情&#xff0c;我自己…

Chrome插件(扩展)

【干货】Chrome插件(扩展)开发全攻略 写在前面 我花了将近一个多月的时间断断续续写下这篇博文&#xff0c;并精心写下完整demo&#xff0c;写博客的辛苦大家懂的&#xff0c;所以转载务必保留出处。本文所有涉及到的大部分代码均在这个demo里面&#xff1a;https://github.com…

Apache OpenJPA 2.1.0 发布

OpenJPA 是 Apache 组织提供的开源项目&#xff0c;它实现了 EJB 3.0 中的 JPA 标准&#xff0c;为开发者提供功能强大、使用简单的持久化数据管理框架。OpenJPA 封装了和关系型数据库交互的操作&#xff0c;让开发者把注意力集中在编写业务逻辑上。OpenJPA 可以作为独立的持久…

手把手带你写一个中断输入设备驱动

今天群里有人问&#xff0c;要开始驱动开发的话从什么开始比较好。我说&#xff0c;应该开始去摸索触摸屏驱动&#xff0c;现在我想了下&#xff0c;触摸屏驱动可能会难了些&#xff0c;但是从一个GPIO开始&#xff0c;我觉得一定是一件很容易的事情。所以这篇文章就来了。大家…

驱动调试神器printk你掌握了吗?

[导读] 刚刚开始做Linux相关开发工作时&#xff0c;深感Linux内核代码庞大&#xff0c;要加些自己的驱动进内核代码树&#xff0c;常常深陷bug的泥沼难以自拔&#xff0c;今天来分享一下内核调试利器printk的使用心得。前面一段时间很忙&#xff0c;后期更文频率会渐渐回归正常…

腾讯云技术专家卢萌凯手把手教你Demo一个人脸识别程序!

欢迎大家前往腾讯云社区&#xff0c;获取更多腾讯海量技术实践干货哦~ 本文来自腾讯云技术沙龙&#xff0c;本次沙龙主题为Serverless架构开发与SCF部署实践 卢萌凯&#xff1a;毕业于东南大学&#xff0c;曾就职于华为&#xff0c;熟悉云行业解决方案。目前负责腾讯云中间件产…

二十世纪最伟大的算法,你了解哪个?

导读&#xff1a;作者July总结了一篇关于计算方法的文章《 细数二十世纪最伟大的10大算法 》。一、1946 蒙特卡洛方法[1946: John von Neumann, Stan Ulam, and Nick Metropolis, all at the Los Alamos Scientific Laboratory, cook up the Metropolis algorithm, also known …

桌面计算机恢复出厂设置,windows7电脑怎么恢复出厂设置

我们使用电脑一段时间&#xff0c;由于各种问题&#xff0c;希望将电脑恢复出厂设置&#xff0c;那么windows7电脑怎么恢复出厂设置呢&#xff1f;下面跟着学习啦小编来一起了解下windows7电脑恢复出厂设置的方法吧。windows7电脑恢复出厂设置方法一按下开机键&#xff0c;启动…

关于Exchange管理控制台报“您的权限不足,无法此查看数据”的解决办法

今天朋友突然来电话&#xff0c;说自己的Exchange 2010 EMC突然报“you dont have sufficient permissions to view this data”&#xff08;您的权限不足&#xff0c;无法查看此数据&#xff09;&#xff0c;同时所有的cmdlet命令也不可以执行。询问我解决办法&#xff0c;出错…

内存为什么还有管理?

本文作者&#xff1a;度白嵌入式任何程序运行起来都需要分配内存空间存放该进程的资源信息的&#xff0c;C程序也不例外。C程序中的变量、常量、函数、代码等等的信息所存放的区域都有所不同&#xff0c;不同的区域又有不同的特性。C语言学习者、尤其是在学习嵌入式的朋友&…