作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生在读,研究方向无线联邦学习
擅长领域:驱动开发,嵌入式软件开发,BSP开发
作者主页:一个平凡而乐于分享的小比特的个人主页
文章收录专栏:IMX8MP,本专栏记录imx8mp开发板,学习开发过程中的问题及解决方法记录
欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖
Camera开发-V4L2/media controller框架介绍
由于本人是初次接触摄像头这块,最近一直在学习这一块的知识,也参考了大量的其他优秀博主的文章,我只是粗略讲讲大概内容,因为v4l2框架和media controller框架涉及的知识点实在过多,无法在一篇文章中把他们联系以及每个函数调用关系讲明白,如果要深入学习,我推荐几篇优质博客和文章,大家详细学习:
1.一口Linux公众号
2.深入学习Linux摄像头(二)v4l2驱动框架-CSDN博客
3.深入学习Linux摄像头(三)虚拟摄像头驱动分析_深入学习linux摄像头(三)-CSDN博客
4.V4L2框架-media device_v4l2与media-CSDN博客
5.V4L2框架和media controller框架的联系_mediarecorder和v4l2有关系吗-CSDN博客
6.linux v4l2架构分析——media_device的注册过程分析_v4l2注册流程-CSDN博客
7.分析ov5640.c驱动_v4l2架构实现ov5640驱动-CSDN博客
好了。现在进入主题
1. v4l2框架
1.1 相关对象
v4l2驱动框架主要的对象有video_device
、v4l2_device
、v4l2_subdev
、videobuf
-
video_device
一个字符设备,为用户空间提供设备节点(/dev/videox),提供系统调用的相关操作(open、ioctl…)
-
v4l2_device
嵌入到video_device中,表示一个v4l2设备的实例
-
v4l2_subdev
依附在v4l2_device之下,并表示一个v4l2设备的子设备,一个v4l2_devide下可以有多个sub_device
-
videobuf
v4l2驱动的缓存管理
下面有必要对v4l2_device和v4l2_subdev来进行说明
subdev的设计目的是为了多路复用,就是用一个v4l2_device可以服务多个v4l2_subdev
下面以我们手机的摄像头来举例
-
CMOS摄像头
对于一款CMOS摄像头来说,有两个接口,一个是
摄像头接口
,一个是I2C接口
摄像头接口负责传输图像数据,I2C接口负责传输控制信息,所以又可以将CMOS摄像头看作是一个I2C模块
如下图所示
-
芯片片上资源
在一款芯片上面,摄像头相关的有
摄像头控制器
、摄像头接口
、I2C总线
SOC上可以有多个摄像头控制器,多个摄像头接口,多个I2C总线
摄像头控制器负责接收和处理摄像头数据,摄像头接口负责传输图像数据,I2C总线负责传输控制信息
如下图所示
对于手机而言,一般都有两个摄像头,一个前置摄像头,一个后置摄像头,其接发下图所示
我们可以选择让控制器去操作哪一个摄像头,这就做到了使用一个摄像头控制器来控制多个摄像头,这就是多路复用
上面说要使用一个摄像头控制器去操作多个摄像头,这是我们的目的,那么在软件中是怎么实现的呢?
我们回到V4L2来,再来谈
v4l2_device
和v4l2_subdev
上面我们介绍到
v4l2_device
表示一个v4l2实例在V4L2驱动中,使用
v4l2_device
来表示摄像头控制器
使用
v4l2_subdev
来表示具体的某一个摄像头的I2C控制模块
,进而通过其控制摄像头v4l2_device
里有一个v4l2_subdev
链表,可以选择v4l2_device
去控制哪一个v4l2_subdev
相信到此,你对
v4l2_device
和v4l2_subdev
就有所了解了当然某些驱动是没有v4l2_subdev,只有video_device
经过上面的讲解,我们用一张图来总结
前面说video_device是一个字符设备,从图中可以看出,video_device内含一个cdev
v4l2_device是一个v4l2实例,嵌入到video_device中
v4l2_device维护者一个链表管理v4l2_subdev,v4l2_subdev表示摄像头的I2C控制模块
1.2 V4L2 框架
在理清楚V4L2中的主要对象后,我们来介绍V4L2的框架
在介绍V4L2驱动框架前,我们先回顾一下简单的字符设备的编写
- 分配一个字符设备(cdev)
- 设置一个fops
- 注册字符设备
复杂的字符设备
对于复杂的字符设备,内核都是采用分层的方法,一般分驱动核心层还有硬件相关层
核心层会帮你完成字符设备的分配,fops的设置,注册字符设备,并向硬件相关层提供一个相应的对象和注册接口
硬件相关层则需要分配相应的对象,设置对象和对象的fops,并注册到核心层中
当应用层发生系统调用,会先来到核心层,核心层再通过回调函数调用到硬件相关层的驱动
对于V4L2的驱动框架也是如此,可分为V4L2驱动核心层和硬件相关层
下面先用一张图来总结大致V4L2的驱动框架
从图中可以看出V4L2分为核心层还有硬件相关层
核心层负责注册字符设备,然后提供video_device对象和相应的注册接口给硬件相关层使用
硬件相关层需要分配一个video_device
并设置它,然后向核心层注册,核心层会为其注册字符设备并且创建设备节点(/dev/videox)。同时硬件相关层还需要分配和设置相应的v4l2_device
和v4l2_subdev
,其中v4l2_device
的一个比较重要的意义就是管理v4l2_subdev
,当然有一些驱动并不需要实现v4l2_subdev
,此时v4l2_device
的意义就不是很大了
当应用层通过/dev/video
来操作设备的时候,首先会来到V4L2的核心层,核心层通过注册进的video_device
的回调函数调用相应的操作函数,video_device
可以直接操作硬件或者是通过v4l2_subdev
来操作硬件
2.media controller框架
2.1 media framework简介
相关的控制 API 在 Documentation/DocBook/media/v4l/media-controller.xml,运行时设备控制,也就是设备启动之后的数据流线路控制,就像一个工厂流水线一样,流水线上面的一个个节点(贴商标、喷丝印、打包)就形同于输入设备中的一个个子设备,运行时设备控制就是要达到能够控制节点的效果,比如贴商标的机器有好几台,应该选择哪一台进行此次流水线处理,要不要把喷丝印加上去,加哪一个机子等等。
作用
提供实时的 pipeline 管理,pipeline 就理解为管道,想象一下水管,里面的水就是数据流,输入设备中的 csi->isp->video 就组成了一个 pipeline 线路。media framework 提供 pipeline 的开启、关停、效果控制、节点控制等功能。
如何使用
内核当中主要利用四个结构体把众多的节点组织起来:media_device,media_entity,media_link,media_pad。整个 media framework 都是围绕这四个结构体来进行使用的。
2.2 抽象设备模型
media framework 其中一个目的是:在运行时状态下发现设备拓扑并对其进行配置。为了达到这个目的,media framework将硬件设备抽象为一个个的entity,它们之间通过links连接。
entity:硬件设备模块抽象(类比电路板上面的各个元器件、芯片)
pad:硬件设备端口抽象(类比元器件、芯片上面的管脚)
link:硬件设备的连线抽象,link的两端是pad(类比元器件管脚之间的连线)
可以想象一下,如果各个 entity 之间需要建立连接的话,就需要在 pad 中存储 link 以及 entity 信息,link 中需要存储 pad 与 entity 信息,entity 里面需要存储 link 与 pad 信息,属于你中有我,我中有你的情况。
2.3 V4L2框架和media controller框架的联系
V4L2框架:提供给用户空间一个标准化的视频设备控制接口,允许应用程序进行视频捕获、视频流处理和视频播放等操作。V4L2定义了一系列的API,包括设备操作、参数设置、缓冲区管理和视频流控制等。
media controller框架:是Linux内核中的一个更高层次的抽象,它提供了一个统一的方法来枚举和控制与视频设备相关的不同硬件组件。media controller框架可以管理和协调多个视频子设备,这些子设备可能包括摄像头传感器、图像信号处理器(ISP)、视频编码器等。
两者之间的关系体现在:
- 设备枚举:media controller框架负责枚举系统中的媒体设备,并为每个设备提供一个唯一的标识符。这允许V4L2框架通过media controller来识别和操作这些设备。
- 拓扑管理:media controller框架维护了一个拓扑图,这个拓扑图描述了系统中各个子设备之间的连接关系。这对于定义数据流的路径至关重要,因为在视频捕获过程中,数据需要在不同的子设备之间传递,例如从摄像头传感器到ISP,再到编码器。
- 统一管理:media controller框架提供了一个统一的视图,使得V4L2框架可以更容易地与这些设备交互,而不必关心具体的硬件细节。
- 数据流控制:在视频捕获和处理过程中,数据流的控制和路由是通过media controller框架来管理的,它确保数据按照拓扑图中定义的路径在各个子设备之间流动。
V4L2框架专注于提供视频设备的控制接口,而media controller框架则负责设备发现、拓扑管理和数据流路由,两者相互配合,共同为Linux系统上的视频设备提供全面的支持。
3.分析ov5640.c驱动
ov5640驱动,就是对应我们第一部分v4l2框架中的v4l2_subdev部分。在ov5640.c驱动中我们会注意到几个重要结构体
static const struct v4l2_subdev_ops ov5640_subdev_ops = {
.core = &ov5640_core_ops, //用于处理与v4l2子设备核心功能相关的操作
.video = &ov5640_video_ops, //用于处理与v4l2子设备视频相关的操作。
.pad = &ov5640_pad_ops, //用于处理与v4l2子设备pad相关的操作
};
在v4l2子设备中,pad是指输入和输出端口。每个子设备可能有多个输入和输出端口(pad),并且每个端口都可以连接到不同的设备或者 其他子设备。pad操作主要涉及对输入和输出端口的配置、查询和控制
我们把每个相关操作拿出来分析
static const struct v4l2_subdev_pad_ops ov5640_pad_ops = {
.enum_mbus_code = ov5640_enum_mbus_code,
//这是一个函数指针,指向ov5640_enum_mbus_code函数,用于枚举支持的视频总线编码格式。
.get_fmt = ov5640_get_fmt,
//这是一个函数指针,指向ov5640_get_fmt函数,用于获取当前视频格式设置。
.set_fmt = ov5640_set_fmt,
//这是一个函数指针,指向ov5640_set_fmt函数,用于设置视频格式。
.enum_frame_size = ov5640_enum_frame_size,
//这是一个函数指针,指向ov5640_enum_frame_size函数,用于枚举支持的帧大小。
.enum_frame_interval = ov5640_enum_frame_interval,
//这是一个函数指针,指向ov5640_enum_frame_interval函数,用于枚举支持的帧间隔。
};
static const struct v4l2_subdev_video_ops ov5640_video_ops = {
.g_frame_interval = ov5640_g_frame_interval, //该函数用于获取帧间隔信息
.s_frame_interval = ov5640_s_frame_interval, //该函数用于设置帧间隔信息。
.s_stream = ov5640_s_stream, //该函数用于开启和关闭视频流。
};
static const struct v4l2_subdev_core_ops ov5640_core_ops = {
.s_power = ov5640_s_power, //用于控制子设备的电源
.log_status = v4l2_ctrl_subdev_log_status, //用于记录子设备的状态信息
.subscribe_event = v4l2_ctrl_subdev_subscribe_event, //用于订阅子设备的事件
.unsubscribe_event = v4l2_event_subdev_unsubscribe, //取消订阅子设备的事件
};