Camera 的架构是分层的。OEM 想加功能(比如美颜),可以在不同层面动手:
这一章的重点是帮你搞清楚这三个层面分别是什么、分别怎么改。我们会用美颜这个具体例子演示层次 1(加 ChiNode),因为这是最常用的定制方式;然后用高通官方例子 HDRDemo 演示层次 3(新增 Feature),让你理解 Feature 的真正用途。
在动手之前,你必须先搞清楚 Qualcomm CAMX/CHI 的两层 pipeline 结构。这是整个定制体系的基石。
📊 打开图表:
/api/course-files/camera-fullstack/documents/diagrams/26_two_layer_architecture.png— 两层 Pipeline 架构总览
┌─────────────────────────────────────────────────────────────┐
│ 第 1 层(上层):Feature Graph —— "功能模块图" │
│ │
│ [RealTime Feature] → [Bayer2YUV Feature] → [JPEG Feature] │
│ │ │ │ │
│ 一个 Feature 是 一个 Feature 是 一个 Feature│
│ 一个"逻辑容器" 一个"逻辑容器" 是"逻辑容器"│
└─────────────────────────────┬───────────────────────────────┘
│ 每个 Feature 内部都包含:
▼
┌─────────────────────────────────────────────────────────────┐
│ 第 2 层(下层):CamX Pipeline —— "真正的 ISP 处理管线" │
│ │
│ 例如 RealTime Feature 内部: │
│ [Sensor] → [IFE] → [IPE] → [CVP] → [Display] │
│ ↑ ↑ ↑ ↑ │
│ 图像传感器 前端处理 图像引擎 计算机视觉 │
│ (Raw) (YUV) (DSP加速) │
│ │
│ 这些才是真正干活的硬件/软件处理单元,叫做 ChiNode │
└─────────────────────────────────────────────────────────────┘
核心观点:当你听说"在 pipeline 里加一个 node"时,指的通常是第 2 层——在 CamX Pipeline 中加一个 ChiNode。而 Feature Graph 层的"Feature"更像是一个装有 pipeline 的容器。
| CAMX 概念 | 类比 | 说明 |
|---|---|---|
| Usecase | 你决定"用电脑做一件事" | 预览、录像、拍照——使用场景 |
| Feature Graph | 主板上插了哪些板卡及连接方式 | 哪些 Feature 参与、怎么连 |
| Feature | 一块独立的板卡(显卡、声卡) | 一个处理模块,内部有自己的电路 |
| CamX Pipeline | 板卡上的电路走线 | Feature 内部的 Node 连接图 |
| ChiNode | 一个芯片(GPU 核、DSP 核) | 真正的计算单元 |
所以:
CAMX 这样设计是因为:
第 1 层(Feature Graph)解决"功能组合"问题
[RealTime] 把数据推到 display[RealTime] → [B2Y] → [JPEG] 把 sensor raw 转为 JPEG[RealTime] → [HDR] → [B2Y] → [JPEG]第 2 层(CamX Pipeline + ChiNode)解决"真正的计算"问题
总结:Feature Graph 定"谁来干",CamX Pipeline 定"怎么干"。
这是一个很好的问题。既然 ChiNode 才是真正干活的,那为什么不直接往 Pipeline 里加 Node,还要多一层 Feature?
原因在于:ChiNode 只管一帧数据怎么处理,但 Camera 的处理流程远不止"处理一帧数据"。Feature 负责的是 ChiNode 搞不定的那些"脏活累活"。
| 职责 | 举例 | ChiNode 能做吗? |
|---|---|---|
| ① Session 生命周期管理 | 什么时候创建 CamX Session、什么时候销毁。Session 对应 ISP 硬件资源——不能随便创建,也不能一直占着 | ❌ ChiNode 只是一个处理单元,没有 Session 的概念 |
| ② Pipeline 运行时选型 | RealTime Feature 内部有 6 种 Pipeline 变体(ZSL 预览/非 ZSL 预览/带录像/不带录像/第三摄像头…),运行时根据 stream 配置选一个最合适的 | ❌ ChiNode 是固定的,不会根据 stream 配置切换 |
| ③ 请求处理流程编排 | 收到一个 Capture Request 后,先等依赖的 buffer 到齐,然后送进 Pipeline 处理,处理完通知下游。这些调度逻辑都在 Feature 里 | ❌ ChiNode 只管自己这步,不管上下游协调 |
| ④ 多 Pipeline 并发协调 | 有的 Feature(如双摄融合)需要同时管理 2 个 Pipeline,还要同步它们的帧率、做 buffer 对齐 | ❌ ChiNode 属于一个 Pipeline,跨 Pipeline 的事管不了 |
App 打开相机预览
Feature(RealTime 做的事):
1. 看 stream 配置——有 preview + video 两个 stream
2. 从 6 种 Pipeline 变体里选出 "ZSLPreviewRawYUVEIS"(因为带预览+录像+EIS)
3. 创建 CamX Session,把这个 Pipeline 传进去
4. 收到 Preview Request 后,等 ISP 的 buffer 就绪,送进 Pipeline
5. 处理完通知 display 输出
ChiNode(Pipeline 里的 IPE 做的事):
1. 收到输入 buffer(来自 IFE 的 RAW/RDI 数据)
2. 做颜色转换、缩放、锐化
3. 输出 YUV buffer 给下游
* 它不知道自己在被预览还是录像,不管什么时候创建 Session,不关心几个 stream
所以 Feature = Manager(管理人、资源、流程),ChiNode = Worker(只管干好手里的活)。
假设把所有逻辑都堆在 Usecase 里,直接操作 Pipeline 和 ChiNode:
// 没有 Feature 的假想代码——Usecase 里所有细节都要自己管
void StillCaptureUsecase::ProcessRequest(Request* req) {
// 自己创建 Session
m_pSession = CamxSession::Create(..., req->streamConfig);
// 自己选 Pipeline
ChiPipeline* pPipeline = SelectPipeline(req->captureIntent, req->sceneMode);
// 自己把 Node 串起来
pPipeline->AddNode(myBeautyNode, pPipeline->IPE, Position::After);
// 自己等 buffer
WaitForBuffer(req->inputBuffer);
// 自己送 Pipeline 处理
pPipeline->Submit(req->inputBuffer, req->outputBuffer);
// 处理完自己通知 framework
NotifyResult(req);
}
有了 Feature 后:
// BeautyNode 只管处理
void BeautyNode_ProcessRequest(CHINODEPROCESSREQUESTINFO* pInfo) {
MyBeautyAlgorithm(pInfo->pInputBuffers[0], pInfo->pOutputBuffers[0]);
}
// Feature 管流程
void RealTimeFeature::ProcessRequest(FRO* pFRO) {
// Feature 基类 ChiFeature2Base 已经把 Session 创建好了、
// Pipeline 选好了、buffer 依赖等好了
OnExecuteProcessRequest(pFRO); // → 最终走到 BeautyNode_ProcessRequest
}
核心区别一句话:ChiNode 管的是"这帧怎么算",Feature 管的是"整个请求怎么走"——Session 创建、Pipeline 选型、Buffer 调度、Metadata 传递。Feature 把上层流程管理中那些跟 ChiNode 无关的复杂性封装掉了。如果只用 ChiNode,那所有这些管理逻辑都得你自己写。
理解了分层,我们再回头看整个流程:
📊 打开图表:
/api/course-files/camera-fullstack/documents/diagrams/26_graph_selector_flow.png— Feature Graph Selector 选择流程
App 打开相机 → Usecase 创建
│
▼
Feature Graph Selector 根据 stream 配置
选出可用的 Feature Graph 列表
│
▼
App 下发 Capture Request
│
▼
Selector 根据 request 中的 metadata:
- captureIntent (PREVIEW / STILL_CAPTURE / ...)
- sceneMode (HDR / NIGHT / PORTRAIT / ...)
- noiseReduction (OFF / HQ / ...)
- vendorTag (MFNR / Burst / ...)
│
▼
选择匹配的 Feature Graph
│
▼
Feature Graph 里的每个 Feature 创建 CamX Session
│
▼
Session 里创建 CamX Pipeline
│
▼
Pipeline 中的 ChiNode 们开始处理
(IFE → IPE → CVP → ...)
所以回答一个常见问题:正常拍照时,路径是 Usecase → Feature Graph Selector → Feature Graph → Feature → CamX Pipeline → ChiNode。不是 Usecase 直接处理,也不是"默认跑到 feature"——而是 Selector 根据 metadata 动态选择最合适的 Feature Graph。
以 xxx 平台为例,在 chifeature2graphselectoroem.cpp 中:
// 当 App 拍一张普通照片时(StillCapture + 普通场景 + 任何降噪模式)
// Selector 会匹配到这张图:
{ RTBayer2YUVJPEGFeatureGraphDescriptor.pFeatureGraphName,
SINGLE_CAMERA,
{ ControlCaptureIntentStillCapture,
ControlCaptureIntentVideoSnapshot,
ControlCaptureIntentZeroShutterLag,
ControlCaptureIntentManual }, // ← 这些 Capture Intent
{ ControlSceneModeDisabled,
ControlSceneModeFacePriority,
... }, // ← 这些 Scene Mode
{ noiseReductionmodeAll }, // ← 任何降噪模式
{ 0 },
0
},
匹配到的图是:[RealTime] → [Bayer2YUV] → [JPEG]。
📊 打开图表:
/api/course-files/camera-fullstack/documents/diagrams/26_feature_custom_flow.png— 四种定制层次及使用频率
| 层次 | 改动位置 | 典型场景 | 难度 |
|---|---|---|---|
| 1. 加 ChiNode | CamX Pipeline 内部 | 美颜、滤镜、HDR 合成等逐帧算法(本章重点) | ⭐⭐⭐ |
| 2. 改 Feature Graph | Feature Graph 配置 | 重新拼装已有 Feature(不写新代码) | ⭐ |
| 3. 新增 Feature | Feature2 代码 | 复杂多帧算法(SuperNight、MFSR) | ⭐⭐⭐⭐ |
| 4. 新增 Usecase | Usecase 代码 | 全新工作模式(VR、超级慢动作) | ⭐⭐⭐⭐⭐ |
实际项目中:层次 1 和 2 覆盖了 90% 以上的 OEM 定制需求。层次 3 只用于复杂多帧算法,层次 4 极少用到。
下面用两种方式对比,让你感受它们的区别:方式一(ChiNode)用美颜做例子,方式二(Feature)用高通官方 HDRDemo 做例子。
📊 打开图表:
/api/course-files/camera-fullstack/documents/diagrams/26_feature_graph.png— ChiNode 方式:在 Pipeline 中插入 Beauty Node
这是最常用、最高效的方式,适合美颜这种逐帧图像处理算法。
美颜算法通常工作在 YUV 空间(磨皮、美白),所以在 IPE(输出 YUV)之后、display/编码之前最合适。在 RealTime Feature 的 CamX Pipeline 中,典型的节点顺序是:
修改前(一个拍照帧的硬件处理路径):
[Sensor] → [IFE] → [IPE] → [CVP] → [Display/JPEG]
raw raw YUV 直通
修改后(插入美颜 node):
[Sensor] → [IFE] → [IPE] → [Beauty] → [Display/JPEG]
raw raw YUV 美颜YUV
这个 [Beauty] 就是一个自定义 ChiNode。它可以运行在:
添加自定义 ChiNode 有两个步骤:先实现 Node,再串到 Pipeline 拓扑里。下面分开说。
高通为自定义 Node 定义了标准接口(chi-cdk/api/node/chinode.h)。每个自定义 Node 是一个独立的 .so 动态库,通过导出 ChiNodeEntry() 函数向 Chi driver 注册自己。
ChiNodeEntry() 是 Chi driver 加载 Node 时调用的入口。Node 在这里填充 CHINODECALLBACKS 结构体,告诉 Chi driver "我能干什么、怎么调用我":
// 路径: chi-cdk/oem/qcom/node/beauty/chinodebeauty.cpp
// Chi driver 加载 Node .so 后,首先调用这个入口函数
// Node 在这里填充自己的回调函数指针
CDK_VISIBILITY_PUBLIC VOID ChiNodeEntry(CHINODECALLBACKS* pNodeCallbacks) {
pNodeCallbacks->pGetCapabilities = BeautyNode_GetCaps;
pNodeCallbacks->pCreate = BeautyNode_Create;
pNodeCallbacks->pDestroy = BeautyNode_Destroy;
pNodeCallbacks->pQueryBufferInfo = BeautyNode_QueryBufferInfo;
pNodeCallbacks->pSetBufferInfo = BeautyNode_SetBufferInfo;
pNodeCallbacks->pProcessRequest = BeautyNode_ProcessRequest;
pNodeCallbacks->pChiNodeSetNodeInterface = BeautyNode_SetNodeInterface;
pNodeCallbacks->pQueryMetadataPublishList = BeautyNode_QueryMetadataPublishList;
}
各个回调的职责如下:
| 回调 | 作用 |
|---|---|
pGetCapabilities |
返回 Node 的能力(支持哪些格式、缩放等) |
pCreate |
创建 Node 实例,分配资源 |
pDestroy |
销毁 Node 实例,释放资源 |
pQueryBufferInfo |
告诉框架 Node 的输入输出 buffer 需求(宽高、格式等) |
pSetBufferInfo |
框架把实际 buffer 信息告诉 Node |
pProcessRequest |
每帧处理入口——这里放你的美颜算法 |
pChiNodeSetNodeInterface |
框架把 ChiNodeInterface 传给 Node,Node 通过它回调框架 |
pQueryMetadataPublishList |
返回 Node 会发布的 metadata tag 列表 |
核心的处理回调 pProcessRequest 的实现:
// 每帧美颜处理
CDKResult BeautyNode_ProcessRequest(CHINODEPROCESSREQUESTINFO* pRequestInfo) {
// 从请求信息中获取输入/输出 buffer
// 输入:来自 IPE 的 YUV 图像
// 输出:处理后的 YUV 图像,送往 display 或编码器
ChiBufferInfo* pInputBuffer = &pRequestInfo->pInputBuffers[0];
ChiBufferInfo* pOutputBuffer = &pRequestInfo->pOutputBuffers[0];
// 执行美颜算法(磨皮、美白等)
BeautyAlgorithm_Process(pInputBuffer, pOutputBuffer);
// 处理完成,通知 Chi driver
pRequestInfo->pNodeInterface->pProcessRequestDone(pRequestInfo->hNodeSession,
pRequestInfo->pFrameInfo);
return CDKResultSuccess;
}
编译后,生成的 .so(如 libchinodebeauty.so)会放到设备上的 /vendor/lib64/camera/components/ 目录。Chi driver 启动时扫描该目录,按需加载 Node。
注意区分:
CHINODECALLBACKS(Node 实现给 Chi 的回调)和ChiNodeInterface(Chi 提供给 Node 的回调接口)是两个方向相反的结构体。Node 通过ChiNodeInterface回调框架(如报告处理完成pProcessRequestDone),框架通过CHINODECALLBACKS回调 Node(如pProcessRequest)。
Node 实现好了,还要告诉 CamX "这个 Node 应该放在 Pipeline 的哪个位置、跟谁连"。这一步叫串 Node(wiring)。
现代 Qualcomm Camera 平台的 Pipeline 拓扑通过 XML usecase component 文件描述,放在:
chi-cdk/oem/qcom/topology/usecase-components/usecases/<UsecaseName>/camx<UsecaseName>.xml
在 XML 中,你需要做两件事:
① 在 Pipeline 的 Node 列表中加入 Beauty Node
<!-- 在 <Pipeline> 段的 <Nodes> 中加入新节点 -->
<Node>
<NodeId>65537</NodeId> <!-- OEM 自定义 ID,避开高通保留 ID -->
<NodeInstanceId>0</NodeInstanceId>
<NodeName>Beauty</NodeName>
<Property>
<PropertyId>2</PropertyId> <!-- NodePropertyProfileId -->
<PropertyValue>1</PropertyValue>
</Property>
</Node>
高通保留的 Node ID 范围:
| Node ID | 硬件模块 |
|---|---|
| 65536 | IFE(Image Front-End) |
| 65538 | IPE(Image Processing Engine) |
| 65540 | FD(Face Detection) |
| 65543 | CVP(Computer Vision Processor) |
| ≥ 65544 或 < 65536 | OEM 自定义 Node |
② 在 Link 表中把 Beauty Node 串联到 IPE 之后、CVP 之前
<!-- Link 定义:数据从哪个 Node 的哪个 Port 流到哪个 Node 的哪个 Port -->
<!-- IPE 的输出 Port 8(YUV)→ Beauty 的输入 Port 0 -->
<Link>
<SrcNodeId>65538</SrcNodeId>
<SrcNodeInstanceId>0</SrcNodeInstanceId>
<SrcPortId>8</SrcPortId>
<DstNodeId>65537</DstNodeId>
<DstNodeInstanceId>0</DstNodeInstanceId>
<DstPortId>0</DstPortId>
</Link>
<!-- Beauty 的输出 Port 0 → CVP 的输入 Port 0 -->
<Link>
<SrcNodeId>65537</SrcNodeId>
<SrcNodeInstanceId>0</SrcNodeInstanceId>
<SrcPortId>0</SrcPortId>
<DstNodeId>65543</DstNodeId>
<DstNodeInstanceId>0</DstNodeInstanceId>
<DstPortId>0</DstPortId>
</Link>
XML 配置完成后,通过高通的 usecase composer 工具(tools/usecaseconverter/)生成最终的 g_pipelines.h。这个过程叫 topology compilation,具体的编译方法见下面 Step 3。
补充:高通 ISV 指南(
80-p9301-20_c_camera_quickstart_guide_for_isv)中也明确说明了自定义 node 的命名规范为com.<vendor>.node.<algorithm>.so,放在components/目录下。
# 编译 ChiNode 动态库
cd chi-cdk/oem/qcom/node/beauty/
mmm .
# 重新编译 topology XML(如果改了 XML 的话)
cd chi-cdk/oem/qcom/topology/usecase-components/
mmm .
# push 到 components 目录(Chi driver 从这里动态加载 Node)
adb root && adb remount
adb push libchinodebeauty.so /vendor/lib64/camera/components/
adb reboot
# 验证
adb logcat | grep -E "beauty|Beauty|ChiNode|components"
| 优点 | 缺点 |
|---|---|
| 无需额外的 buffer 拷贝,延迟最低 | 需要理解 CamX Pipeline 拓扑 |
| 可以利用硬件加速(DSP/CVP) | 调试复杂,出问题可能影响整个 pipeline |
| 和 ISP tuning 无缝配合 | 对 pipeline 的依赖较强,移植性低 |
📊 打开图表:
/api/course-files/camera-fullstack/documents/diagrams/26_hdrdemo_feature.png— HDRDemo Feature 结构与 ChiNode 对比
注意:美颜这种逐帧 ISP 算法用 ChiNode 就够了,不需要 Feature。本节对应层次 3(新增 Feature),用高通官方 Feature2 文档中的 HDRDemo 来演示什么场景下才需要 Feature。
高通 Feature2 入门文档(kba-210324010650)中带了一个完整的 Feature 实现示例:HDRDemo。
这个 Feature 做的事情:
输入:同一场景、不同曝光的 8 帧 P010 图像
↓
用自己的 Pipeline(SWMFMergeYuv)
做多帧对齐 → 融合 → 色调映射
↓
输出:1 帧 HDR 融合后的 YUV 图像
为什么这个不能用 ChiNode 做?
| 需求 | ChiNode 的方式 | Feature(HDRDemo)的方式 |
|---|---|---|
| 输入 | 每帧来一次 pProcessRequest,一次处理一帧 |
先等 8 帧全部到齐,再一起提交给算法 |
| 处理 Pipeline | 只能利用所在 Pipeline 的已有节点拓扑 | 有自己的 Pipeline SWMFMergeYuv,专为融合设计 |
| 帧同步 | 没有"等 N 帧"的机制,每帧独立 | Feature 基类的 ChiFeature2RequestFlowType::Type1 天然支持多帧依赖 |
关键区别:ChiNode 是"插在别人的 Pipeline 里干活"的,用的是宿主 Pipeline 的数据流。Feature 是"我自己起一个 Pipeline",有完全独立的 Session、Pipeline、Node 拓扑。
HDRDemo 的 Descriptor 定义了 8 个输入端口 + 1 个输出端口:
// 8 帧 P010 输入端口
CDK_VISIBILITY_PUBLIC extern const ChiFeature2PortDescriptor HDRDemoInputPortDescriptors[] = {
{ {0, 0, 0, ChiFeature2PortDirectionType::ExternalInput,
ChiFeature2PortType::ImageBuffer}, "YUV_In0", &HDRDemoInputTargetDescriptors[0] },
{ {0, 0, 1, ChiFeature2PortDirectionType::InternalInput,
ChiFeature2PortType::ImageBuffer}, "YUV_In1", &HDRDemoInputTargetDescriptors[0] },
// ... 直到 YUV_In7
};
// 1 帧输出端口
CDK_VISIBILITY_PUBLIC extern const ChiFeature2PortDescriptor HDRDemoOutputPortDescriptors[] = {
{ {0, 0, 0, ChiFeature2PortDirectionType::ExternalOutput,
ChiFeature2PortType::ImageBuffer}, "YUV_Out", &HDRDemoOutputTargetDescriptors[0] },
};
Pipeline 用的是高通的多帧融合 pipeline SWMFMergeYuv:
static const ChiFeature2PipelineDescriptor HDRDemoPipelineDescriptors[] = {
{ 0, 0, "SWMFMergeYuv", ChiFeature2PipelineType::CHI,
CHX_ARRAY_SIZE(HDRDemoInputPortDescriptors), &HDRDemoInputPortDescriptors[0],
CHX_ARRAY_SIZE(HDRDemoOutputPortDescriptors), &HDRDemoOutputPortDescriptors[0] },
};
Feature 基类提供了三种 request flow,HDRDemo 选择 Type1——先等第一帧(external input),再等剩下的 7 帧(internal input),全部到齐后一起提交:
ChiFeature2RequestFlowType ChiFeature2HDRDemo::OnSelectFlowToExecuteRequest(
ChiFeature2RequestObject* pRequestObject) const {
return ChiFeature2RequestFlowType::Type1;
}
这个逻辑 ChiNode 做不到——ChiNode 每帧来一次 pProcessRequest,没有"等齐 N 帧再处理"的机制。
Feature Graph 中的位置:
[RealTime] → [Bayer2YUV] → [HDRDemo Feature] → [JPEG]
↓
独立的 Session
独立的 Pipeline: SWMFMergeYuv
8 帧 P010 输入 → 1 帧 HDR YUV 输出
"新增一个 Feature"本质上是:在 Feature Graph 中新增一个独立的处理节点,它有自己的 Session/Pipeline,可以处理 ChiNode 无法处理的场景(多帧输入、独立 Pipeline 拓扑、Session 级生命周期管理)。
| 优点 | 缺点 |
|---|---|
| 有独立的 Session/Pipeline,拓扑不受限 | 比 ChiNode 多了跨 Feature 的 buffer 拷贝 |
| 适合多帧处理(等 N 帧 → 处理 → 输出) | 延迟更高(多一次 Feature 调度) |
| Feature Graph 层面可插拔 | 需要更多的内存和资源(独立 Session) |
| 不需要修改 CamX Pipeline XML 拓扑 | 代码量比 ChiNode 大得多 |
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 逐帧处理 ISP Pipeline 内的 YUV/Raw 数据(美颜、滤镜等) | 方式一:ChiNode | 延迟低,利用硬件加速,你之前做的方式就是对的 |
| 算法有外部依赖(第三方库、CPU 密集) | 方式一:ChiNode | 可以用 CVP/DSP 卸载 |
| 多帧输入→单帧输出(HDR 融合、多帧降噪) | 方式二:Feature | 需要管理多帧 buffer 和请求队列,ChiNode 是逐帧的 |
| 需要独立 Pipeline 拓扑(不是 ISP 路径上的处理) | 方式二:Feature | Feature 内部可以定义自己的 Session/Pipeline |
| 不想碰 CamX Pipeline 拓扑 | 方式二:Feature | XML pipeline 配置不改,只在 Feature Graph 层面加节点 |
经验之谈:对于大多数 ISP Pipeline 路径上的图像处理算法(包括美颜),方式一(ChiNode)是更自然的选择——你之前做过的方式就是对的。方式二(Feature)只在算法需要独立 Pipeline 拓扑(如多帧融合、超夜)时才需要考虑。
两种方式的部署略有不同:
# 编译 ChiNode 动态库
cd chi-cdk/oem/qcom/node/beauty/
mmm .
# 推送到 components 目录(ChiNode 的默认加载路径)
adb root && adb remount
adb push libchinodebeauty.so /vendor/lib64/camera/components/
adb reboot
# 验证
adb logcat | grep -E "beauty|Beauty|ChiNode"
# 编译 Feature 动态库
cd chi-cdk/oem/qcom/feature2/hdrdemo/
mmm .
# 推送到 camera 目录
adb root && adb remount
adb push libfeature2hdrdemo.so /vendor/lib64/camera/
adb reboot
# 验证
adb logcat | grep -E "HDRDemo|FeatureGraph|GraphSelector"
不是。"pipeline 里的节点"指的是 CamX Pipeline 中的 ChiNode(硬件/软件处理单元),而"Feature"是 Feature2 框架中的一个逻辑模块。Feature 内部可以承载一整个 CamX Pipeline(包含多个 ChiNode)。
不是。一个 Feature 可以包含多个 Session/Pipeline/Node,它更像一个"处理单元容器"。比如 RealTime Feature 内部有 6 种不同的 pipeline 配置(ZSL 预览、非 ZSL 拍照、第三摄像头等),每种 pipeline 包含 IFE、IPE、CVP 等多个 ChiNode。
不是。ChiNode 接口(chinode.h)是厂商可以实现的扩展接口,Feature2 的 Descriptor 放在 chi-cdk/oem/ 目录下。这些都是高通特意留给 OEM 的定制层。
不是。选择表以 captureIntent + sceneMode + noiseReduction + vendorTag 的组合为条件,每帧都会根据 metadata 重新选择。同一台手机上,拍普通照片和拍 HDR 照片走的是不同的 Feature Graph。
可以结合:一个 Feature 内部的 Pipeline 中可以含有多个 ChiNode。Feature 和 ChiNode 不是互斥的,而是不同层次上的概念。
CAMX 架构的两层 pipeline:
第 1 层(Feature Graph):逻辑功能模块的连接图
第 2 层(CamX Pipeline):真正的硬件/软件处理节点(ChiNode)拓扑
定制层次(从易到难):
层次 1: 在 CamX Pipeline 中加 ChiNode ← 本章重点,适合逐帧算法
层次 2: 改 Feature Graph 连接 ← 不写代码,重新组合
层次 3: 新增 Feature ← 适合复杂/多帧算法
层次 4: 新增 Usecase ← 极少需要
两种方式对比:
方式一(ChiNode):
适用:美颜等逐帧 ISP 算法
做法:实现 ChiNodeEntry → 在 XML 中串到 Pipeline 里
优点:低延迟、硬件加速
缺点:需要理解 CamX Pipeline 拓扑
方式二(Feature):
适用:多帧融合(如 HDRDemo,8帧→1帧)
做法:派生 ChiFeature2Base → 定义 Descriptor → 注册到 Selector
优点:独立 Session/Pipeline,多帧管理
缺点:额外 buffer 拷贝,延迟更高
选择机制一句话:
Usecase 定"模式",Feature Graph Selector 根据 metadata
动态选"图",图里的每个 Feature 创建 CamX Pipeline,
Pipeline 里的 ChiNode 真正干活。
关键源码路径:
ChiNode 接口: chi-cdk/api/node/chinode.h
CamX Pipeline: chi-cdk/core/lib/common/g_pipelines.h
Feature2: chi-cdk/core/chifeature2/
选择器: chi-cdk/oem/qcom/feature2/chifeature2graphselector/
OEM 选择表: chifeature2graphselectoroem.cpp + 平台目录
OEM Graph: chifeature2oemgraphdescriptors.cpp
XML Topology: chi-cdk/oem/qcom/topology/usecase-components/
| 维度 | ChiNode | Feature (ChiFeature2Base) |
|---|---|---|
| 层次 | CamX Pipeline 内部 | Feature2 Graph 节点 |
| 粒度 | 单个处理单元(硬件/软件) | 包含 Session/Pipeline 的容器 |
| 入口函数 | ChiNodeEntry() 填充 CHINODECALLBACKS |
CreateFeature() + 派生虚函数 |
| 处理粒度 | 逐帧 pProcessRequest |
支持多帧依赖的 request flow |
| 输入 | 每帧 1 次回调 | 可配置多帧输入(Type1/Type2) |
| 自己的 Pipeline | ❌ 使用宿主 Pipeline | ✅ 独立 Session + Pipeline |
| buffer 管理 | 框架管理 | 框架管理或自行管理 |
| 适用场景 | 简单的逐帧处理 | 复杂的多帧/多 Session 处理 |
| 所在目录 | chi-cdk/oem/qcom/node/ |
chi-cdk/oem/qcom/feature2/ |