← 返回课程

开发环境

Camera全栈开发(Qcom Camx) 第 5 / 30 节

第 3 章:开发环境与调试基础


本章导读

前面两章讲了"相机怎么成像"和"系统怎么分层"。但从这章开始,你要动手了

做 Camera 开发和普通 App 开发最大的区别是:你不能只靠 logcat 活着。 你需要看懂源码目录结构、熟练使用 Source Insight 跳转、会用 dumpsys 查看内部状态、懂得用 setprop 控制调试开关。

这章就是你的工具箱——不要求一次记住所有命令,但要知道"遇到问题时去哪找答案"。


3.1 源码目录结构

CamX-CHI 源码根目录下,你会看到 3 个顶层目录:

camx_chi_root/
  ├── camx/               CamX 核心层(高通官方,OEM 一般不修改)
  ├── chi-cdk/            CHI 定制层(OEM 主要修改的地方)
  └── camera-devicetree/  设备树配置(dtsi 文件)

3.1.1 camx/ — 核心 HAL

这是 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 之间的依赖机制

3.1.2 chi-cdk/ — OEM 定制层

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

3.2 Source Insight 配置

读这类大型 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 构建

3.3 logcat — 你的第一诊断工具

CamX 日志标签

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 耗时最长。


3.4 dumpsys — 查看相机内部状态

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 信息

3.5 setprop — 调试开关

大部分 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. vs vendor. 的区别

  • persist. 开头的属性重启后保留
  • vendor. 开头的属性重启后丢失
  • 部分属性需要 setenforce 0(关 SELinux)才生效

3.6 camxoverridesettings.txt

这是 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

3.7 实操:采集一次完整的调试信息

这应该是你接手一个 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.txtcamera_dump.txtdmesg.txt),大部分问题都能定位。


3.8 第 3 章总结

工具清单
──────────────────────────────────
源码阅读:  Source Insight + Ctrl+/ 搜索
日志查看:  adb logcat | grep "\[CamX\]" 过滤
状态诊断:  adb shell dumpsys media.camera
调试开关:  adb shell setprop persist.vendor.camera.*
配置文件:  camxoverridesettings.txt
──────────────────────────────────

这些工具会贯穿整个课程。后续每一章在讲完原理后,都会用这些工具做实操验证


动手验证

目标:独立完成一次完整的"相机状态采集"操作。

  1. 在 Source Insight 中建立 CamX-CHI 源码工程,添加 camx/src/core/chi-cdk/core/ 目录。

  2. 搜索以下符号,确认你能找到它们的定义位置:

    • class Session
    • PipelineStatus
    • DependencyUnit
    • UsecaseSelector
  3. 连接设备,执行本章 3.7 节的"采集一次完整的调试信息"操作。把采集到的三个文件(logcat、dumpsys、dmesg)保存下来——后续章节的动手验证会反复用到。


篇末实战:采集完整的相机状态报告

目标:独立完成一次完整的相机信息采集,形成一份"相机状态报告"。

步骤 1:环境准备

adb devices
adb logcat -c

步骤 2:采集相机静态信息

adb shell dumpsys media.camera > camera_static_info.txt

camera_static_info.txt 中提取并记录:

步骤 3:采集运行时信息

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

步骤 4:分析日志

步骤 5:写报告

整理一份"相机状态报告",包含:设备基本情况、启动延迟、当前帧率、发现的问题。

常见误解