前面两章讲了"相机怎么成像"和"系统怎么分层"。但从这章开始,你要动手了。
做 Camera 开发和普通 App 开发最大的区别是:你不能只靠 logcat 活着。 你需要看懂源码目录结构、熟练使用 Source Insight 跳转、会用 dumpsys 查看内部状态、懂得用 setprop 控制调试开关。
这章就是你的工具箱——不要求一次记住所有命令,但要知道"遇到问题时去哪找答案"。

camx_chi_root/
├── camx/ CamX 核心层(高通官方,OEM 一般不修改)
├── chi-cdk/ CHI 定制层(OEM 主要修改的地方)
└── camera-devicetree/ 设备树配置(dtsi 文件)
这是 CamX 的主体代码。你需要重点掌握其中几个子目录:
camx/src/
├── core/ ★★★★★ 你花时间最多的地方
│ ├── camxsession.h Session 定义
│ ├── camxpipeline.h Pipeline 定义 + 状态机
│ ├── camxnode.h Node 定义 + DependencyUnit
│ ├── hal/ HAL3 接口适配层
│ │ ├── camxhal3entry.cpp HAL3 入口函数
│ │ ├── camxhaldevice.cpp camera3_device_t 实现
│ │ └── camxhal3module.cpp HAL_MODULE_INFO_SYM
│ └── chi/ ChiContext(全局上下文)
│
├── hwl/ ★★★★ 硬件封装层
│ ├── ife/ IFE Node 实现
│ ├── bps/ BPS Node 实现
│ ├── ipe/ IPE Node 实现
│ └── xxx/ Titan ISP 寄存器抽象
│
├── swl/ ★★★ 软件算法层
│ ├── eisv3/ EIS 电子防抖
│ ├── jpeg/ 软件 JPEG 编码
│ └── stats/ 3A 统计处理
│
├── csl/ ★★★ Camera Services Layer
│ └── common/ CSLSubmit / CSLFenceSignaled
│
└── settings/
└── xxx/ xxx 平台配置(camxoverridesettings.txt)
最优先读的 3 个文件:
| 文件 | 为什么优先读 |
|---|---|
camxsession.h |
核心数据结构,理解 PipelineInfo/LinkInfo/PortInfo/NodeInfo |
camxpipeline.h |
Pipeline 状态机,8 个状态的枚举定义 |
camxnode.h |
DependencyUnit,理解 Node 之间的依赖机制 |
chi-cdk/
├── core/
│ ├── chiframework/ ★★★★★ 框架层
│ │ ├── chxusecase.h Usecase 基类 + UsecaseSelector
│ │ ├── chxsession.h CHI Session 封装
│ │ └── chxpipeline.h CHI Pipeline 封装
│ ├── chifeature2/ ★★★★ Feature2 框架
│ │ └── chifeature2graph.h Feature Graph 数据结构
│ └── chiusecase/ ★★★ Usecase 实现
│ └── chxusecasedefault.h 默认 Usecase
├── api/ API 头文件(node/isp/sensor/stats)
├── configs/ XML 配置文件
└── oem/qcom/ OEM 定制示例
理解 camx/ 和 chi-cdk/ 的区别:
camx/ = 核心引擎 —— 高通维护,一般不修改
chi-cdk/ = 定制框架 —— OEM 在此做 customization
读这类大型 C++ 源码,Source Insight 是最好的工具。它比 VS Code 在符号索引上快得多。
Project → New Project
工程名: camx_chi_source
源码路径: <你的 CamX-CHI 源码根目录>
Project → Add and Remove Project Files
勾选:
✓ camx/src/**/*.h
✓ camx/src/**/*.cpp
✓ chi-cdk/core/**/*.h
✓ chi-cdk/core/**/*.cpp
| 操作 | 快捷键 | 什么时候用 |
|---|---|---|
| 搜索符号 | Ctrl + / |
找类/函数/变量的定义 |
| 跳转到定义 | Ctrl + 点击 |
看到函数名 → 想看实现 |
| 查找引用 | Ctrl + / → References |
想看谁调了这个函数 |
| 向后/向前导航 | Alt + , / . |
跳转了之后想回去 |
| 搜索文件 | Ctrl + O |
想快速打开已知文件名的文件 |
你在源码中搜索最多的词应该就是这些:
Session::ProcessCaptureRequest → 请求入口
Pipeline::ProcessRequest → Pipeline 处理
Node::ProcessRequest → Node 处理
DependencyUnit → 依赖机制
CSLMessageHandler → 硬件完成回调
UsecaseSelector::Select → Usecase 选择
ChiFeature2GraphDesc → Feature2 Graph 结构
InitializeStaticMetadataPool → Static Metadata 构建
| logcat 标签 | 来自哪 | 什么时候看 |
|---|---|---|
[CamX] |
CamX 核心 | 任何时候——查看 Session/Pipeline/Node 状态 |
[CHI] |
CHI 层 | Usecase/Feature2 相关的问题 |
[CSL] |
CSL 层 | 怀疑 HAL↔Kernel 通信有问题时 |
[EISV3] |
EIS 算法 | 防抖效果不对时 |
[SWMCTF] |
多帧融合 | MFNR/HDR 处理异常时 |
# 只看 CamX 核心日志
adb logcat | grep "\[CamX\]"
# 看 CamX + CHI
adb logcat -v threadtime | grep -E "\[CamX\]|CHI"
# 带完整信息保存到文件
adb logcat -v threadtime | grep -E "\[CamX\]|CHI|CSL|ERROR|FATAL" > camx_log.txt
# 实时监控帧率
adb logcat | grep "\[CamX\]" | grep "frameMessage:requestID"
① 硬件完成回调
[CamX] [CORE] camxpipeline.cpp: CSLMessageHandler()
frameMessage:requestID=42, frameCount=37, timestamp=265793404062
这是最重要的日志——它告诉你硬件处理完成了一帧。requestID 关联回 App 的 Request,frameCount 是 Sensor 的物理帧序号,相邻两条日志的 timestamp 差就是帧间隔。
② Request 映射
[CamX] [REQMAP] camxsession.cpp: ProcessRequest()
chiFrameNum: 287 <==> requestId: 42
CHI 内部的帧号和 App 侧的 Request ID 的对应关系。调试时经常需要从 App 的 Request ID 反查 CHI 帧号。
③ Node 处理时间
[CamX] [CORE] camxnode.cpp: ProcessRequestResult()
node=IFE req=42 processingTime=2.1ms
如果帧率不稳,先看这个——看哪个 Node 耗时最长。
dumpsys media.camera 是最强大的运行时诊断工具。它告诉你 CameraService 当前知道什么。
# 完整输出
adb shell dumpsys media.camera > camera_dump.txt
# 只看摄像头列表
adb shell dumpsys media.camera | grep -E "Camera ID|Facing|Orientation"
# 看当前 Activate 的 Session
adb shell dumpsys media.camera | grep -A 20 "Active Sessions"
# 看 Stream 配置
adb shell dumpsys media.camera | grep -A 10 "Stream"
从 dumpsys 中能读到什么:
Camera ID: 0
Facing: BACK
Orientation: 90°
支持的输出大小:
- 4032x3024 (12MP JPEG)
- 1920x1080 (1080p YUV)
- 1280x720 (720p PRIVATE)
FPS 范围: [15, 30]
是否支持 RAW: yes
什么时候用 dumpsys?
- 怀疑 CameraService 没有正确识别摄像头 → 看 Camera ID 列表
- 怀疑 facing/orientation 配错了 → 看 Facing/Orientation 字段
- 想确认设备支持哪些分辨率 → 看输出大小列表
- 想确认 Stream 有没有被正确配置 → 看 Stream 信息
大部分 CamX 的调试功能通过 setprop 控制。
# === 日志等级 ===
# 设置 overrideLogLevels(0x1F=ERROR~VERBOSE)
adb shell setprop persist.vendor.camera.logs.override 0x1F
# === 数据 dump(通过 camxoverridesettings.txt 控制)===
# 图像 dump:在 camxoverridesettings.txt 中设置 dumpOutput 相关开关
# 具体由 CamX Node 中的 ImageDump::Dump() 实现
# EEPROM dump(OTP 校准数据)
adb shell setprop vendor.debug.camera.dumpSensorEEPROMData TRUE
# === 帧率 ===
# 开启实时帧率日志
adb shell setprop persist.vendor.camera.enableFPSLog 1
persist.vsvendor.的区别:
persist.开头的属性重启后保留vendor.开头的属性重启后丢失- 部分属性需要
setenforce 0(关 SELinux)才生效
这是 CamX 的配置文件,修改后可以控制运行时行为。
位置:camx/src/settings/xxx/ 或设备上 /vendor/etc/camera/
# 常用配置项
# 日志等级(最常用)
overrideLogLevels=0x1F
# EIS 配置
EISV2Enable=1
EISV3Enable=2
# 帧率日志
enableFPSLog=1
# Node 处理时间
dumpNodeProcessingInfo=1
# 启动时 dump EEPROM
dumpSensorEEPROMData=1
推送并生效:
adb root
adb remount
adb push camxoverridesettings.txt /vendor/etc/camera/
adb shell chmod 644 /vendor/etc/camera/camxoverridesettings.txt
adb shell killall -9 vendor.camera-provider-2-4
这应该是你接手一个 Camera 问题时的标准操作流程:
# 第一步:清空 logcat 缓存
adb logcat -c
# 第二步:开启 CamX 详细日志
adb shell setprop persist.vendor.camera.logs.override 0x1F
adb shell setprop persist.vendor.camera.enableFPSLog 1
# 第三步:抓 logcat(同时操作相机)
adb logcat -v threadtime | grep -E "CamX|CHI|CSL|ERROR|FATAL" > camx_log.txt
# 第四步:操作完成后,跑 dumpsys
adb shell dumpsys media.camera > camera_dump.txt
# 第五步:查看系统日志
adb shell dmesg > dmesg.txt
有了这 3 个文件(camx_log.txt、camera_dump.txt、dmesg.txt),大部分问题都能定位。
工具清单
──────────────────────────────────
源码阅读: Source Insight + Ctrl+/ 搜索
日志查看: adb logcat | grep "\[CamX\]" 过滤
状态诊断: adb shell dumpsys media.camera
调试开关: adb shell setprop persist.vendor.camera.*
配置文件: camxoverridesettings.txt
──────────────────────────────────
这些工具会贯穿整个课程。后续每一章在讲完原理后,都会用这些工具做实操验证。
目标:独立完成一次完整的"相机状态采集"操作。
在 Source Insight 中建立 CamX-CHI 源码工程,添加 camx/src/core/ 和 chi-cdk/core/ 目录。
搜索以下符号,确认你能找到它们的定义位置:
class SessionPipelineStatusDependencyUnitUsecaseSelector连接设备,执行本章 3.7 节的"采集一次完整的调试信息"操作。把采集到的三个文件(logcat、dumpsys、dmesg)保存下来——后续章节的动手验证会反复用到。
目标:独立完成一次完整的相机信息采集,形成一份"相机状态报告"。
adb devices
adb logcat -c
adb shell dumpsys media.camera > camera_static_info.txt
从 camera_static_info.txt 中提取并记录:
adb shell setprop persist.vendor.camera.logs.override 0x1F
# 打开相机 App,等 5 秒,关闭
adb logcat -v threadtime -d | grep -E "CamX|CHI|CSL|ERROR" > camera_runtime_log.txt
ProcessCaptureRequest 日志(请求入口)CSLMessageHandler 日志(硬件完成第一帧)CSLMessageHandler,计算帧间隔 → 实际帧率整理一份"相机状态报告",包含:设备基本情况、启动延迟、当前帧率、发现的问题。
setenforce 0。