3、HAL与ISP的通信桥梁:V4L2框架、Media Controller拓扑、ISP subdev节点操作实战
好,咱们今天聊点硬核的。HAL和ISP之间怎么传话?说白了,就是靠V4L2这套机制。我刚开始接触这块时,也觉得V4L2不就是个视频采集框架嘛,跟ISP有什么关系?后来踩了坑才明白——V4L2不仅仅是采集,它还是整个Camera系统的“通信总线”。
3.1 V4L2框架:不只是视频采集
V4L2,全称Video for Linux 2。很多人以为它只管“把摄像头数据读出来”。其实不然。在Camera HAL和ISP之间,V4L2扮演了三个角色:
- 数据通道:把ISP处理完的帧数据送到HAL
- 控制通道:HAL通过ioctl设置ISP的参数
- 事件通知:ISP有异常或状态变化,通过V4L2事件通知HAL
我个人习惯把V4L2设备分成三类:
| 设备类型 | 节点名称 | 作用 |
|---|---|---|
| Video节点 | /dev/videoX | 数据流传输,比如输出YUV或RAW图 |
| Subdev节点 | /dev/v4l-subdevX | 控制ISP内部模块,比如AE、AWB、LSC |
| Media节点 | /dev/mediaX | 管理拓扑连接,告诉你怎么连的 |
嗯,这里要注意:很多新人只盯着video节点,忽略了subdev。结果发现调ISP参数时怎么都写不进去——因为你找错门了。
3.2 Media Controller拓扑:一张“电路图”
为什么需要Media Controller?你想想看,一个ISP内部可能有几十个模块:传感器输入、Bayer处理、去马赛克、3A统计、缩放、裁剪……它们之间怎么连接的?
Media Controller就是这张“电路图”。它用entity(实体)和pad(端口)来描述每个模块,用link(链接)来描述数据流向。
我在项目中遇到过一个问题:HAL往ISP写参数,结果图像全黑了。查了半天,发现是某个subdev的pad没连上。说白了,数据根本没流过去。
查看拓扑的命令很简单:
# 查看media拓扑
media-ctl -d /dev/media0 -p
# 输出示例:
# - entity 1: sensor (1 pad, 1 link)
# - pad0: Source [fmt:SRGGB10/1920x1080]
# - link: pad0 -> isp pad0
# - entity 2: isp (2 pads, 2 links)
# - pad0: Sink
# - pad1: Source [fmt:UYVY/1920x1080]
我个人建议,调试时第一步就是打印拓扑。别急着写代码,先看清楚谁连谁。
3.3 ISP subdev节点操作实战
好,到了动手环节。操作ISP subdev,核心就是ioctl。常用的几个命令:
VIDIOC_SUBDEV_G_FMT/S_FMT:获取/设置格式VIDIOC_SUBDEV_G_CROP/S_CROP:裁剪区域VIDIOC_SUBDEV_G_SELECTION/S_SELECTION:选择目标区域VIDIOC_SUBDEV_S_FRAME_INTERVAL:帧率控制
举个例子,设置ISP输入格式:
#include <linux/v4l2-subdev.h>
int fd = open("/dev/v4l-subdev0", O_RDWR);
struct v4l2_subdev_format fmt = {0};
fmt.pad = 0; // 哪个pad
fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; // 生效格式
fmt.format.width = 1920;
fmt.format.height = 1080;
fmt.format.code = MEDIA_BUS_FMT_SRGGB10_1X10; // 10-bit RAW
if (ioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt) < 0) {
perror("set subdev format failed");
}
close(fd);
我曾经踩过一个坑:which字段忘了设成ACTIVE,结果设了半天都是“试格式”,根本没生效。嗯,这种低级错误,犯一次就记住了。
3.4 HAL与ISP的典型交互流程
实际项目中,HAL和ISP的交互大概是这样的:
- 初始化阶段:HAL打开media节点,遍历拓扑,找到所有subdev
- 配置阶段:HAL通过subdev节点设置ISP的格式、裁剪、帧率
- 启动阶段:HAL通过video节点启动数据流
- 运行阶段:HAL通过subdev节点动态调整ISP参数(比如AE、AWB)
- 停止阶段:HAL关闭video节点,停止数据流
这里有个细节:启动流时,顺序很重要。我建议先配好所有subdev,再启动video节点。否则ISP还没准备好,数据就来了,容易丢帧。
v4l2-ctl和media-ctl命令行工具快速验证。比如:
# 设置sensor格式
v4l2-ctl -d /dev/v4l-subdev0 --set-fmt-video=width=1920,height=1080,pixelformat=RG10
# 查看当前格式
v4l2-ctl -d /dev/v4l-subdev0 --get-fmt-video
先命令行调通,再写代码,效率高很多。
3.5 实战中的常见问题
最后,分享几个我实际项目中遇到的问题:
- 问题1:subdev节点打不开 —— 权限问题,或者驱动没加载。检查
dmesg看看有没有probe失败。 - 问题2:设置格式后图像花屏 —— 格式不匹配。比如ISP输出是UYVY,但HAL配置成了NV12。用
media-ctl -p确认一下。 - 问题3:AE/AWB参数写不进去 —— 确认subdev是否支持该控制。用
v4l2-ctl -d /dev/v4l-subdev0 --list-ctrls查看支持的控件列表。 - 问题4:拓扑连接断开 —— 检查link是否enable。用
media-ctl -d /dev/media0 --set-link 'sensor pad0:isp pad0[1]'重新连接。
嗯,V4L2这套东西,说复杂也复杂,说简单也简单。核心就是搞清楚:数据从哪来,到哪去,中间经过谁。把拓扑图画清楚,代码写起来就顺了。
下一章,咱们聊聊HAL层怎么管理这些节点,以及如何做多路并发。到时候见。