前面 6 章拆解了 CamX-CHI 的每个组件——Usecase、Session、Pipeline、Node、Feature2。现在是时候把它们串起来了。
从 App 的 capture() 开始,到 ISP 硬件处理完成、结果回到 App——完整走一遍。

App process_capture_request()
│
HAL3 入口 (camxhal3entry.cpp)
│
Usecase::SubmitChiRequest()
│
├──→ [如果启用了 Feature2] Feature Graph Manager 接管
│ → FGS 选择 Graph → FGM 创建 FRO → 逐个 Feature 处理
│
├──→ [如果未启用 Feature2] 直接到 Session
│
Session::ProcessCaptureRequest()
│ ┌────────────────────────────────────┐
├──│ Pipeline 0 (RT, Preview) │
│ │ IFE → IPE → Display (不过 BPS) │
│ └────────────────────────────────────┘
│ ┌────────────────────────────────────┐
├──│ Pipeline 1 (Offline, JPEG/后处理) │
│ │ IFE → BPS → IPE → JPEG (拍照) │
│ └────────────────────────────────────┘
│
CSLSubmit() → Kernel → ISP HW
│
HW 处理完成 → CSLFenceSignaled()
│
Node::ProcessResult()
→ Pipeline::ProcessPipelineResult()
→ Session::ProcessPipelineResult()
→ [如有 Feature2] Feature 汇聚 Result
→ Usecase → HAL → App
---
## 17.2 分步详解
### Stage 1: App → HAL3 入口
App 调用 capture() 或 setRepeatingRequest()
→ CameraService → Provider → camxhal3entry.cpp
```log
// 源码: camx/src/core/hal/camxhal3entry.cpp
[CamX] [HAL ] camxhal3entry.cpp: ProcessCaptureRequest() frame_number=42
你需要知道的:camxhal3entry.cpp 是 HAL3 的入口文件。所有 process_capture_request 调用都在这里被截获,然后转发给 CHI 层。如果你在 logcat 中看到这个日志但后面没有 SubmitChiRequest,说明请求在 HAL 入口之后、到达 CHI 之前丢失了。
HALDevice → Usecase::SubmitChiRequest() → Session::ProcessCaptureRequest()
// 源码: chi-cdk/core/chiframework/chxusecase.cpp
[CHI] chxusecase.cpp: SubmitChiRequest() frame_number=42
// 源码: camx/src/core/camxsession.cpp
[CamX] [REQMAP] camxsession.cpp: chiFrameNum: 287 <==> requestId: 42
chiFrameNum <==> requestId是 CHI 帧号和 App Request ID 的映射关系。调试时经常需要从 App 的 Request ID 反查 CHI 帧号。源码在
camx/src/core/camxsession.cpp的ProcessCaptureRequest()中生成这个映射——如果你想看这个映射的生成逻辑,直接打开这个文件搜索 "chiFrameNum"。
Session 解析 PipelineIndex → 分发到 RT 和 Offline Pipeline
[CamX] [CORE] camxpipeline.cpp: Pipeline::ProcessRequest() pipelineId=0 req=42
Pipeline 遍历所有 Node,检查 DependencyUnit
Preview Pipeline: IFE 无输入依赖 → ready; IPE 需要 IFE→wait
Snapshot Pipeline: BPS 需要 IFE→wait; IPE 需要 BPS→wait; JPEG 需要 IPE→wait
[CamX] [CORE] camxnode.cpp: CheckAndSetDependencies() node=IFE satisfied=true
[CamX] [CORE] camxnode.cpp: CheckAndSetDependencies() node=IPE satisfied=false
Node 处理 → CSLSubmit() → ioctl → Kernel
[CamX] [CSL ] cslcommon.cpp: CSLSubmit() pipelineId=0 req=42
ISP 硬件处理 → DMA 写回 → 中断 → CSLMessageHandler
// 源码: camx/src/core/camxpipeline.cpp
[CamX] [CORE] camxpipeline.cpp:1843 CSLMessageHandler()
frameMessage.requestID=42, frameMessage.frameCount=37,
frameMessage.timestamp=265793404062
requestID=42 关联回 App 的 Request | frameCount=37 是 Sensor 输出的物理帧序号 | timestamp 是硬件时间戳
这个日志是 Pipeline 状态机分析的核心——如果你看到
requestID=0,说明 KMD 丢弃了无效帧(通常是 sensor 同步问题)。如果你看到requestID连续递增但 frameCount 跳跃,说明有丢帧。
Result 不是一次性回来的。CamX 支持 Partial Result——metadata 可以先回来,图像 Buffer 后回来。
第一段: Metadata 先回(Partial Result)
IFE 处理完成 → IFE Node 写回 Stats 数据(AE/AWB/AF 统计值)
→ Pipeline::NotifyNodePartialMetadataDone()
→ Session 收集 partial metadata
→ Usecase::ProcessPartialCaptureResult()
→ 不需要等图像数据,先回调 metadata
第二段: 图像 Buffer 后到(Full Result)
IPE 处理完成 → Buffer 通过 SinkPortFenceSignaled 到达
→ Pipeline::ProcessPipelineResult()
→ Session 汇聚所有 Pipeline Result → 合并 metadata + buffer
→ Usecase::ProcessCaptureResult()
→ HAL3 process_capture_result() → App
// Partial metadata 先回来
[CamX] [CORE] camxnode.cpp: ProcessPartialMetadataDone() node=IFE req=42
[CHI] chxusecase.cpp: ProcessPartialCaptureResult() requestId=42
// 图像 buffer 后到
[CamX] [CORE] camxsession.cpp: ProcessPipelineResult() pipelineId=0 req=42
[CHI] chxusecase.cpp: ProcessCaptureResult() requestId=42
[CamX] [HAL ] camxhal3entry.cpp: process_capture_result() frame_number=42
💡 Partial Result 的价值: AE 的统计数据在 IFE 处理完就出来了,不需要等 IPE 处理完。Usecase 拿到 partial metadata 后可以提前为下一帧计算 AE 参数——这就是"Pipeline"的真正含义:不等全部做完,每一站有结果就立刻送回来一部分。
// 完整的正常日志链:
① [CamX] [HAL ] camxhal3entry.cpp: ProcessCaptureRequest() frame=42
② [CHI] chxusecase.cpp: SubmitChiRequest() frame=42
③ [CamX] [REQMAP] chiFrameNum: 287 <==> requestId: 42
④ [CamX] [CORE] camxpipeline.cpp: Pipeline::ProcessRequest() pipelineId=0
⑤ [CamX] [CORE] camxnode.cpp: CheckAndSetDependencies() node=IFE satisfied=true
⑥ [CamX] [CSL ] CSLSubmit() pipelineId=0
⑦ [CamX] [CORE] camxpipeline.cpp: CSLMessageHandler() requestID=42
⑧ [CHI] chxusecase.cpp: ProcessCaptureResult() requestId=42
日志断在哪里,问题就在哪里:
断在 ① → App 没发起请求 / HAL3 入口问题
断在 ③ → Session 映射失败
断在 ⑤ → Node 依赖一直不满足(死锁!)
断在 ⑥ → CSL 提交失败
断在 ⑦ → HW 处理异常
断在 ⑧ → 回调链路异常
通过日志时间戳可以测量各阶段耗时:
t0: ProcessCaptureRequest (入口)
t1: CSLSubmit (提交到硬件)
t2: CSLMessageHandler (硬件完成)
t1 - t0 = Framework 处理延迟(通常 <5ms)
t2 - t1 = ISP 硬件处理时间(约 1 帧 = 16ms@60fps)
总延迟 = t2 - t0 = ~20~30ms(正常)
Request → Result 经历 8 个阶段:
App → HAL3 → Usecase → Session → Pipeline → Node → CSL → HW → 回传
关键日志:
① [HAL ] ProcessCaptureRequest — 入口
③ [REQMAP] chiFrameNum <==> requestId — 映射关系
⑦ CSLMessageHandler requestID — HW 完成
⑧ ProcessCaptureResult — 回调
日志断在哪里,问题就在哪里
打开相机 App,预览状态下抓一次完整的 logcat:
adb logcat -c && adb logcat -v threadtime | grep -E "CamX|CHI" > trace.txt
在 trace.txt 中找到第 17.2 节描述的 7 个阶段的对应日志。
从 CSLMessageHandler 日志中计算当前帧率(相邻两条的时间戳差)。
拍一张照片,对比拍照前后的日志新出现的 Pipeline 和 Node 日志。
这是最重要的一篇实战。
目标:独立追踪一次 capture_request 从 App 到 ISP 再回来的完整链路。
adb root && adb shell setprop persist.vendor.camera.logs.override 0x1F && adb logcat -c
# camxoverridesettings.txt 中添加: dumpNodeProcessingInfo=1, enableFPSLog=1
adb logcat -v threadtime | grep -E "CamX|CHI" > trace_capture.txt
# 同时:打开相机 → 拍一张照片 → 关闭
按照第 17 章的 7 个阶段,从日志中找到对应行:
ProcessCaptureRequest() — App 发起请求chiFrameNum <==> requestId — Session 建立映射Pipeline::ProcessRequest() — 分发到 PipelineCheckAndSetDependencies — Node 依赖检查CSLSubmit — 提交到硬件CSLMessageHandler — 硬件处理完成ProcessCaptureResult — 结果回调这一节把每个核心流程对应的源码位置集中列出来。当你拿到一份 CamX 源码不知道从哪里开始时,就从这里入手。
目的: 追踪从 HAL 加载到 ChiContext 初始化的完整路径
入口: camx/src/core/hal/camxhal3module.cpp
HAL_MODULE_INFO_SYM → g_camx_module_methods
① camxhal3module.cpp: g_camx_module_methods.open()
→ camxhaldevice.cpp: CamxHalDevice::Open()
→ 创建 CamxHalDevice 对象
→ camxhaldevice.cpp: Initialize()
→ 初始化 ChiContext
② camxchicontext.cpp: ChiContext::Create()
→ camxhwenvironment.cpp: HwEnvironment::GetCameraInfo()
→ 读取 roll/pitch/yaw → 确定 facing/orientation
→ camxchicontext.cpp: InitializeStaticMetadataPool()
→ 构建 CameraCharacteristics 的数据
③ camxhaldevice.cpp: ConfigureStreams()
→ chxusecase.cpp: UsecaseSelector::Select()
→ 根据 Stream 配置选择 Usecase
目的: 追踪一次 capture_request 从入口到结果回传的完整路径
入口: camx/src/core/hal/camxhal3entry.cpp
CamxHalDevice::ProcessCaptureRequest()
① camxhaldevice.cpp: ProcessCaptureRequest()
→ m_ChiAppCallbacks.chi_override_process_request()
→ CHI ExtensionModule 接管
② chxusecase.cpp: Usecase::SubmitChiRequest()
→ camxsession.cpp: Session::ProcessCaptureRequest()
→ 根据 PipelineIndex 分发
③ camxsession.cpp → camxpipeline.cpp: Pipeline::ProcessRequest()
→ camxnode.cpp: Node::CheckAndSetDependencies()
→ DRQ: m_readyNodes / m_deferredNodes 管理
④ camxnode.cpp: Node::ProcessRequest()
→ cslcommon.cpp: CSLSubmit()
→ ioctl → Kernel → ISP HW
⑤ camxpipeline.cpp: CSLMessageHandler()
→ HW 处理完成回调
→ camxsession.cpp: ProcessPipelineResult()
→ 结果回传
| 你想找什么 | 搜哪个文件 | 搜什么关键词 |
|---|---|---|
| Pipeline 状态迁移 | camxpipeline.cpp | SetStatus / m_status |
| Dependency 检查 | camxnode.cpp | CheckAndSetDependencies |
| Node 处理完成 | camxnode.cpp | ProcessRequestResult |
| Frame 完成回调 | camxpipeline.cpp | CSLMessageHandler |
| Static Metadata 构建 | camxchicontext.cpp | InitializeStaticMetadataPool |
| Usecase 选择 | chxusecase.cpp | UsecaseSelector::Select |
| Facing 判定 | camxhwenvironment.cpp | GetCameraInfo |
| Buffer 分配 | camxnode.cpp | CreateBufferManager |