一、node的存放位置
node在camx和chi中均会存在,但存在的位置不一样。
Camx中主要位于:android/vendor/qcom/proprietary/camx/src/(hwl/swl),例如:IFE,IPE,BPS,Sensor等。
Chi中主要位于:android/vendor/qcom/proprietary/chi-cdk/oem/qcom/node/,例如:三方EIS,memcpy等。
二、两种node的差异
Camx node是平台已经封装好的节点,一般不需要改动;而Chi node则是提供给vendor自定义实现的节点,用于自定义算法处理。
三、node是如何启用的
一个node会被编译成一个.so文件。在开机过程中系统会加载很多东西,比如各种应用程序、so文件等等,在加载HAL进程时,会一起加载node。

具体实现如下:

probechicomponents代码如下:
CamxResult ProbeChiComponents(
ExternalComponentInfo* pExternalComponentInfo,
UINT* pNumExternalComponent)
//返回so的文件数量
fileCountTypeNode = OsUtils::GetFilesFromPath(ExtCompPath,
FILENAME_MAX,
&soFilesName[0][0],
"*", //VendorName
"node", //CategoryName
,例如com.qti.node.memcpy.so, com.wt.node.sat.so
"*",
"*",
&SharedLibraryExtension[0]);
//循环加载Node节点
while (index < fileCountTypeNode + fileCountTypeStats + fileCountTypeHvx)
{
CamX::OSLIBRARYHANDLE handle = CamX::OsUtils::LibMap(&soFilesName[index][0]);// 1.dlopen的封装方法
if (index < fileCountTypeNode)
{
pNodeEntry = reinterpret_cast<PFCHINODEENTRY>(CamX::OsUtils::LibGetAddr(handle, "ChiNodeEntry")); // 2.dlsys的封装方法
CAMX_ASSERT(NULL != pNodeEntry);
pExternalComponentInfo[index].nodeCallbacks.size = sizeof(ChiNodeCallbacks);
if (NULL != pNodeEntry)
{
pNodeEntry(&pExternalComponentInfo[index].nodeCallbacks);// 3.Node API’s Callbacks
}
if (NULL != pExternalComponentInfo[index].nodeCallbacks.pQueryVendorTag)
{
GetComponentTag(pExternalComponentInfo[index].nodeCallbacks.pQueryVendorTag);
}
pExternalComponentInfo[index].nodeAlgoType = ExternalComponentNodeAlgo::COMPONENTNODE;
}
代码中的核心方法:
1、dlopen
OSLIBRARYHANDLE ChxUtils::LibMap(const CHAR* pLibraryName)
{
OSLIBRARYHANDLE hLibrary = NULL;
const UINT bindFlags = RTLD_NOW | RTLD_LOCAL;
hLibrary = dlopen(pLibraryName, bindFlags);
if (NULL == hLibrary)
{
CHX_LOG_ERROR("Failed to load library %s error %s", pLibraryName, dlerror());
CHX_ASSERT(0 == dlerror());
}
return hLibrary;
}
2、dlsym
// 此方法的作用是获取.so链接库hLibrary里面的名为pProcName的函数
VOID* ChxUtils::LibGetAddr(OSLIBRARYHANDLE hLibrary,
const CHAR* pProcName)
{
VOID* pProcAddr = NULL;
if (hLibrary != NULL)
{
pProcAddr = dlsym(hLibrary, pProcName);
}
return pProcAddr;
}
因此在函数camxchicomponent.cpp里面会获取函数chiNodeEntry 函数,并将地址赋值给pNodeEntry。然后把对应node的回调信息放到&pExternalComponentInfo[index].nodeCallbacks。

首先要知道.so 库文件是一个 ELF 文件,可以通过 readelf -s 命令查看对应的.so文件描述,如下图可以看到其中有一个 Name 属性为 ChiNodeEntry。

获取node方法总体解释: 也就是:CamX通过GetFilesFromPath遍历camera/components目录下的所有so,调用dlopen()来打开目标库获取handle(定位库文件的地址),然后使用dlsym()来得到符号函数名字为"ChiNodeEntry"的地址,因为所有Node都有一个入口方法ChiNodeEntry,这样便可以加载所有的Node。
CDK_VISIBILITY_PUBLIC VOID ChiNodeEntry(
CHINODECALLBACKS* pNodeCallbacks)
{
if (NULL != pNodeCallbacks)
{
if (pNodeCallbacks->majorVersion == ChiNodeMajorVersion &&
pNodeCallbacks->size >= sizeof(CHINODECALLBACKS))
{
pNodeCallbacks->majorVersion = ChiNodeMajorVersion;
pNodeCallbacks->minorVersion = ChiNodeMinorVersion;
pNodeCallbacks->pGetCapabilities = MemCpyNodeGetCaps;
pNodeCallbacks->pQueryVendorTag = MemCpyNodeQueryVendorTag;
pNodeCallbacks->pCreate = MemCpyNodeCreate;
pNodeCallbacks->pDestroy = MemCpyNodeDestroy;
pNodeCallbacks->pQueryBufferInfo = MemCpyNodeQueryBufferInfo;
pNodeCallbacks->pSetBufferInfo = MemCpyNodeSetBufferInfo;
pNodeCallbacks->pProcessRequest = MemCpyNodeProcRequest;
pNodeCallbacks->pChiNodeSetNodeInterface = MemCpyNodeSetNodeInterface;
}
}
加载node之后,如何使用node中的方法呢?
通过调用pNodeEntry对pExternalComponentInfo[index].nodeCallbacks参数进行取地址赋值,这样就可以使用Node中的方法,其他的节点类似就好。

总结:node 的加载是通过编译成.so文件使用,当程序起来的时候,就可以使用.so文件,加载node节点,每个node中存在一个ChiNodeEntry,将chiNodeEntry导入,就可以加载node里面的方法。
四、针对chinodecallbacks分析回调中的各个函数大致分析
CDK_VISIBILITY_PUBLIC VOID ChiNodeEntry(
CHINODECALLBACKS* pNodeCallbacks)
{
if (NULL != pNodeCallbacks)
{
if ((ChiNodeMajorVersion == pNodeCallbacks->majorVersion) &&
(sizeof(CHINODECALLBACKS) <= pNodeCallbacks->size))
{
pNodeCallbacks->majorVersion = ChiNodeMajorVersion;
pNodeCallbacks->minorVersion = ChiNodeMinorVersion;
pNodeCallbacks->pGetCapabilities = GpuNodeGetCaps;
pNodeCallbacks->pQueryVendorTag = GpuNodeQueryVendorTag;
pNodeCallbacks->pCreate = GpuNodeCreate;
pNodeCallbacks->pDestroy = GpuNodeDestroy;
pNodeCallbacks->pQueryBufferInfo = GpuNodeQueryBufferInfo;
pNodeCallbacks->pSetBufferInfo = GpuNodeSetBufferInfo;
pNodeCallbacks->pProcessRequest = GpuNodeProcRequest;
pNodeCallbacks->pChiNodeSetNodeInterface = GpuNodeSetNodeInterface;
pNodeCallbacks->pPostPipelineCreate = GpuNodePostPipelineCreate;
pNodeCallbacks->pFlushRequest = GpuNodeFlushRequest;
pNodeCallbacks->pGetFlushResponse = GpuNodeGetFlushResponse;
}
else
{
LOG_ERROR(CamxLogGroupChi, "Chi API major version doesn't match (%d:%d) vs (%d:%d)",
pNodeCallbacks->majorVersion,
pNodeCallbacks->minorVersion,
ChiNodeMajorVersion,
ChiNodeMinorVersion);
}
}
else
{
LOG_ERROR(CamxLogGroupChi, "Invalid Argument: %p", pNodeCallbacks);
}
}
/// @brief Callback Interface for Chi to call into custom node.
typedef struct ChiNodeCallbacks
{
UINT32 size; ///< Size of this structure
UINT32 majorVersion; ///< Major version
UINT32 minorVersion; ///< Minor version
PFNNODEGETCAPS pGetCapabilities; ///< Get Node Capabilities
PFNCHIQUERYVENDORTAG pQueryVendorTag; ///< Optional function to get the vendor tags
///< supported by node
PFNNODECREATE pCreate; ///< Mandatory function to create an instance of the node
PFNNODEDESTROY pDestroy; ///< Mandatory function to destroy an instance of the node
PFNNODEQUERYBUFFERINFO pQueryBufferInfo; ///< Mandatory function called to query input buffer
///< requirements
PFNNODESETBUFFERINFO pSetBufferInfo; ///< Mandatory function called to set the buffer properties
PFNNODEPROCREQUEST pProcessRequest; ///< Mandatory function to process a request by node
PFCHINODESETNODEINTERFACE pChiNodeSetNodeInterface; ///< Mandatory function to set the node interface functions
PFNPIPELINECREATED pPipelineCreated; ///< Notify pipeline created. Deprecated
PFNPOSTPIPELINECREATE pPostPipelineCreate; ///< Indication of Post pipeline creation
PFNCHIPREPARESTREAMON pPrepareStreamOn; ///< Notify pipeline for preparing stream on
PFNCHIONSTREAMON pOnStreamOn; ///< Notify pipeline stream on
PFNCHIONSTREAMOFF pOnStreamOff; ///< Notify pipeline stream off
PFNNODEQUERYMETADATAPUBLISHLIST pQueryMetadataPublishList; ///< Mandatory function called to query metadata list
///< published by the node
PFNNODEFLUSH pFlushRequest; ///< Free up node resources allocated for a request
PFNNODEFLUSHRESPONSEINFO pGetFlushResponse; ///< Get worst case response time for flush call
PFNNODEFILLPERREQUESTHWDATA pFillHwdata; ///< Fill per request hardware data
} CHINODECALLBACKS;
| 回调函数指针 | 对应实现函数 | 功能与作用 | 调用时机 |
|---|---|---|---|
pGetCapabilities | GpuNodeGetCaps | 获取节点能力 返回节点支持的硬件特性(如旋转/缩放/滤镜等) | 节点初始化时 |
pQueryVendorTag | GpuNodeQueryVendorTag | 查询供应商标签 声明节点自定义的元数据标签(如调试参数/控制参数) | 流水线构建前 |
pCreate | GpuNodeCreate | 创建节点实例 分配节点私有数据,初始化硬件资源 | 每个节点实例化时 |
pDestroy | GpuNodeDestroy | 销毁节点实例 释放资源,清理状态 | 节点不再使用时 |
pQueryBufferInfo | GpuNodeQueryBufferInfo | 查询缓冲区需求 声明节点对输入/输出缓冲区的尺寸/格式/对齐要求 | 流水线缓冲区分配前 |
pSetBufferInfo | GpuNodeSetBufferInfo | 设置缓冲区信息 接收实际分配的缓冲区属性(如 stride/scanline) | 缓冲区分配完成后 |
pProcessRequest | GpuNodeProcRequest | 处理请求 核心函数,执行图像处理(如 GPU 滤镜/旋转/缩放) | 每帧数据处理时 |
pChiNodeSetNodeInterface | GpuNodeSetNodeInterface | 设置节点接口 获取 CHI 框架服务(如日志/元数据访问) | 节点创建后立即调用 |
pPostPipelineCreate | GpuNodePostPipelineCreate | 流水线创建后处理 执行依赖完整流水线的初始化(如跨节点依赖解析) | 整个相机流水线构建完成后 |
pFlushRequest | GpuNodeFlushRequest | 刷新请求 中止正在处理的请求(如相机模式切换时) | 流水线需要重置时 |
pGetFlushResponse | GpuNodeGetFlushResponse | 获取刷新响应 确认所有请求已终止 |
以GPU为例:

1116

被折叠的 条评论
为什么被折叠?



