当 App 调用 createCaptureSession() 时,Camera HAL 内部的第一个决策发生在 UsecaseSelector——它决定了"这次用什么方案来完成 App 的需求"。
Usecase 是 CHI 层中最靠近 App 的一层。理解它,你就理解了为什么打开相机 App 有时走预览路线、有时走 HDR 路线、为什么拍照时预览不会卡。
Usecase 翻译成"用例"或"场景"——它回答了"当前要做什么"这个问题。
App 传入的 Surface: Usecase 做出的决定:
[previewSurface] → 预览模式(只用 RT Pipeline)
[previewSurface, jpegSurface] → 拍照模式(RT + Offline)
[previewSurface, videoSurface] → 录像模式
源码定义在 chi-cdk/core/chiframework/chxusecase.h。Usecase 的职责分为四个阶段:
① 选择 — UsecaseSelector 根据 Stream 配置决定用哪个 Usecase
② 创建 — 创建 Session + Pipeline + 分配 Buffer
③ 调度 — 接收 App 的 capture_request,分发给正确的 Pipeline
④ 汇聚 — 收集各 Pipeline 的 result,回调给 App
Usecase(基类, chi-cdk/core/chiframework/chxusecase.h)
│
├── UsecaseDefault → 标准预览 + 拍照
│ ├── 只传 previewSurface → 纯预览(1 个 Pipeline)
│ └── 传 preview + jpeg → 预览 + 拍照(2 个 Pipeline)
│
├── UsecaseHDR → HDR 多帧融合拍照
├── UsecaseMFNR → 多帧降噪
├── UsecaseVideo → 标准录像(30fps)
├── UsecaseHFR → 高帧率录像(>60fps)
├── UsecaseRaw → RAW 数据输出
└── [OEM Custom]
App: createCaptureSession([previewSurface])
↓
HAL: configure_streams(1 个 Stream, format=PRIVATE)
↓
UsecaseSelector 开始工作:
① 检查 Stream 数量 = 1
② 检查 format = PRIVATE(预览用)
③ 没有 JPEG、没有 RAW、没有视频
↓
决策结果: UsecaseDefault(预览模式)
→ 创建 1 个 RT Pipeline: IFE → IPE → Display
→ Offline Pipeline: 不需要
App 重建 Session: createCaptureSession([previewSurface, jpegSurface])
↓
HAL: configure_streams(2 个 Stream: PRIVATE + BLOB)
↓
UsecaseSelector 开始工作:
① Stream 数量 = 2
② 第二个 Stream 的 format = BLOB → 这是 JPEG 输出
↓
决策结果: UsecaseDefault(拍照模式)
→ RT Pipeline: IFE → IPE → Display (预览用,一直开)
→ Offline Pipeline: IFE → BPS → IPE → JPEG Node(拍照时激活)
App: createCaptureSession([previewSurface, jpegSurface])
加上 vendor tag: enableMFNR=true
↓
UsecaseSelector 检查:
① 有 JPEG 输出
② 暗光 + MFNR 开启
↓
决策结果: UsecaseMFNR
→ RT Pipeline: IFE → IPE → Display
→ MFNR 处理需要: 连拍 N 帧 + 对齐 + 融合 → JPEG
→ 需要多帧 Pipeline

文件: chi-cdk/core/chiusecase/chxusecaseutils.cpp (4166 行)
关键函数: UsecaseSelector::DefaultMatchingUsecase() (第 1564 行)
匹配函数: UsecaseMatches() → IsMatchingUsecase()
UsecaseSelector 不使用 if-else 分支。它使用表驱动匹配(table-driven matching):
// 伪代码:DefaultMatchingUsecase 的核心逻辑
ChiUsecase* UsecaseSelector::DefaultMatchingUsecase(
camera3_stream_configuration_t* pStreamConfig)
{
ChiUsecase* pSelected = NULL;
// Step 1: 特殊场景优先检查
// 如果是 secure mode → 走 secure usecase
// 如果是 GPU 旋转 → 走 GPU usecase
// 如果是 HFR(高帧率)→ 走 HFR usecase
// Step 2: 根据 Stream 数量从对应表中选择
for (int i = 0; i < tableSize; i++) {
if (UsecaseMatches(pStreamConfig, &candidateTable[i])) {
pSelected = &candidateTable[i];
break;
}
}
return pSelected;
}
每个 Usecase 表(如 Usescases2Target[])中的 Usecase 是按优先级排好的。
匹配时从第一个开始依次尝试,第一个匹配成功的当选。
假设 2 Stream 的场景,优先级顺序大致是:
① Secure Usecase (如果开启了安全模式)
② HFR Usecase (如果帧率 > 60)
③ GPU Usecase (如果开启了 GPU 处理)
④ MFNR Usecase (暗光 + MFNR 开关)
⑤ HDR Usecase (高对比场景 + HDR 开关)
⑥ UsecaseDefault (普通预览 + 拍照)
⑦ RAW Usecase (如果输出 RAW)
尝试的顺序决定了: 当多个 Usecase 都"可能匹配"时,谁优先。
每个 Usecase 预先定义了一组 Targets(目标 Surface 的描述)。
IsMatchingUsecase 检查 App 的 Stream 列表和 Usecase 的 Target 描述是否匹配:
① Stream 数量是否一致?
② 每个 Stream 的格式(PRIVATE / BLOB / RAW / YUV)是否匹配?
③ 每个 Stream 的用途(预览 / 拍照 / 录像)是否匹配?
④ 分辨率是否在 Usecase 支持的范围内?
如果有 vendor tag / operation_mode 的额外约束,也在这里检查。
这是 Usecase 中最重要的概念,决定了拍照时预览会不会卡。
RT (Real-Time) Pipeline = 实时管线,用于预览和录像
- 持续工作,从 Sensor 开始到 Display 结束
- 严格截止时间:每帧必须按时完成(比如 33ms @30fps)
- 超时后果:掉帧 → 预览卡顿
- 全程 STREAM_ON
Offline Pipeline = 离线管线,用于拍照后处理
- 只在需要时激活,比如 JPEG 编码
- 没有严格截止时间(多花几 ms 没关系)
- 平时 STREAM_OFF,拍照时 STREAM_ON
预览时(仅 RT 工作):
Sensor → IFE → IPE → Display [RT Pipeline, STREAM_ON]
(Offline Pipeline 不存在或 STREAM_OFF)
拍照时(RT + Offline 同时工作):
Sensor → IFE → IPE → Display [RT Pipeline, 持续预览]
↘ JPEG Node [Offline Pipeline, 异步编码]
→ 预览不受影响!
如果 JPEG 编码在 RT Pipeline 中:
Sensor → IFE → IPE → JPEG → Display
↑
JPEG 编码时预览被阻塞 → 卡一帧
分离后:
Sensor → IFE → IPE → Display [RT,流畅预览]
↘ JPEG Node [Offline,慢慢编]
当 ISP 负载过高,无法同时支持 Preview + JPEG 双路输出时,系统进入降级模式:
慢速 JPEG 模式的表现:
- Offline Pipeline 无法获得 ISP 资源
- JPEG 编码塞回 RT Pipeline
- ZSL 不可用
- 拍照时预览可能卡顿
Usecase = 场景调度器
UsecaseSelector = 表驱动匹配引擎
RT Pipeline: 实时工作(预览/录像),严格截止时间
Offline Pipeline: 按需工作(JPEG 编码),无截止时间
匹配过程: Stream 配置 → 查表 → 逐个匹配 → 第一个匹配的当选
adb shell dumpsys media.camera | grep -A 5 "Pipeline",看有几个 Pipeline。