【Qcom Camera】Camx架构Chi External Node 三部曲之如何加载

文章介绍了Node在Camx/Chi架构中的角色,主要负责数据处理和特殊算法,如EIS和FD。Camx中的Node是预封装的,而Chi中的Node允许厂商自定义。加载流程涉及dlopen和dlsym方法,通过遍历.so文件并调用ChiNodeEntry入口方法来加载Node。文章还以memcpy为例展示了Node的创建和回调函数设置过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Node简单介绍

    较官方的定义:Node节点在Camx/Chi架构中至关重要,主要用于处理pipeline下发的request,最终的数据处理都是通过封装好的Node节点来进行的。

简单来说Node用于数据处理;

同时也可以是一些特殊的算法处理,比如EIS,FD,SW Remosaic等等。

    大概了解下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如何加载

今天讲解的是Chi Node,大概分为三类,主要关注COMPONENT NODE;

enum class ExternalComponentNodeAlgo
{
COMPONENTNODE      = 0,   ///< Node
COMPONENTALGORITHM = 1,   ///< Algorithm
COMPONENTHVX       = 2,   ///< Hvx
};


在开机过程中系统会加载很多东西,比如各种应用程序、so文件等等,在加载HAL进程时同样会一起加载Node,我们共同在以下方法中梳理Node的加载流程:
android/vendor/qcom/proprietary/camx/src/core/camxhwenvironment.cpp
//HW初始化方法
 

CamxResult HwEnvironment::Initialize()
 CAMX_ASSERT(NULL != pExternalComponent);
if ((CamxResultSuccess == result) && (NULL != pExternalComponent))
{
     result = ProbeChiComponents(pExternalComponent, &m_numExternalComponent);
}

//我们要知道External node在编译成功之后存放于以下目录
static const CHAR ExtCompPath[] = "/vendor/lib64/camera/components";
//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

VOID* ChxUtils::LibGetAddr(OSLIBRARYHANDLE hLibrary,
                           const CHAR*     pProcName)
{
    VOID* pProcAddr = NULL;
    if (hLibrary != NULL)
    {
        pProcAddr = dlsym(hLibrary, pProcName);
    }
    return pProcAddr;
}


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

也就是:CamX通过GetFilesFromPath遍历camera/components目录下的所有so,调用dlopen()来打开目标库获取handle(定位库文件的地址),然后使用dlsym()来得到符号名字为"ChiNodeEntry"的地址,因为所有Node都有一个入口方法ChiNodeEntry,这样便可以加载所有的Node。
那么加载之后如何拿到Node中的方法?
1.这里就需要查看"//ProbeChiComponents方法" 中3的pNodeEntry的数据类型
 

PFCHINODEENTRY       pNodeEntry;
typedef VOID (*PFCHINODEENTRY) (CHINODECALLBACKS* pNodeCallbacks);

函数指针类型;
通过调用pNodeEntry对pExternalComponentInfo[index].nodeCallbacks参数进行取地址赋值,这样就可以使用Node中的方法。
2.以memcpy为例看一下具体操作:
android/vendor/qcom/proprietary/chi-cdk/oem/qcom/node/memcpy/camxchinodememcpy.cpp
//Memcpynode 中的ChiNodeEntry方法
 

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如何加载、Camx如何加载Chi、上层如何加载HAL等等,同理。

<think>好的,我现在需要解决用户在Qcom CamX设备上遇到的屏幕旋转270度时照片预览与保存方向不一致的问题。首先,我得了解这个问题的背景和可能的成因。用户提到当屏幕旋转270度时,预览和保存的照片方向不同,这通常涉及到图像方向的处理逻辑在CamX中的配置或代码实现有误。 首先,我应该回顾CamX架构中关于图像方向处理的部分。根据引用[1]提到的camxoverridesettings.txt文件,这个文件是用来覆盖默认相机设置的,可能包含方向相关的参数。可能需要检查或修改这个文件中的相关配置。例如,是否存在控制预览方向或JPEG保存方向的参数,比如PreviewOrientation或JPEGOrientation。如果这些参数没有正确设置为270度,就可能导致不一致的问题。 接下来,根据引用[2],AdvancedCameraUsecase的ExecuteCaptureRequest中有两个主要分支处理不同的请求。这里可能需要检查在处理捕获请求时,是否正确地处理了设备旋转信息。例如,在设置预览和拍照的元数据时,是否都应用了相同的旋转角度。如果预览使用了传感器的方向,而保存的照片没有应用设备旋转的补偿,就会导致方向不一致。 然后,我需要考虑CamX中的方向处理流程。通常,相机子系统会根据设备方向调整预览的方向,并在保存照片时应用相应的EXIF方向标签。但如果在旋转270度时,逻辑处理出现错误,比如没有正确计算旋转角度或应用旋转矩阵,就会导致问题。可能需要检查CamX中处理旋转的相关模块,如Sensor或Post Processing模块,确认旋转参数是否正确传递和应用。 此外,硬件抽象层(HAL)的设置也可能影响方向处理。需要确认HAL层是否正确配置了sensor的安装方向,例如mountAngle是否为270度,这会影响后续的方向计算。如果sensor的mountAngle设置不正确,系统无法正确补偿设备旋转,导致预览和保存方向不一致。 可能的解决方案步骤包括: 1. 检查camxoverridesettings.txt中的方向参数,确保预览和JPEG方向设置为270度。 2. 在CamX的Use Case或Node中对旋转逻辑进行调整,确保预览和拍照应用相同的旋转处理。 3. 验证HAL层中的sensor安装方向配置是否正确。 4. 通过adb命令测试不同旋转角度的表现,确认修改后的效果。 最后,需要生成相关问题,帮助用户进一步理解或排查其他可能的问题,例如如何验证方向参数是否生效,或者CamX中的旋转处理机制等。</think>针对Qcom CamX设备中屏幕旋转270度时照片预览与保存方向不一致的问题,以下是分步解决方案: ### 1. **检查CamX配置覆盖文件** 在`camxoverridesettings.txt`中添加或修改以下参数: ```text overrideForcePreviewUpsideDown=1 # 强制预览方向翻转 overrideJpegOrientation=270 # 设置JPEG保存方向为270度 ``` 将此文件推送到`vendor/etc/camera/`目录并重启设备[^1]。此操作直接覆盖系统默认方向配置。 ### 2. **修改CamX旋转处理逻辑** 在`AdvancedCameraUsecase`的`ExecuteCaptureRequest`中,确保预览与拍照分支均调用`SetMetaData`设置相同方向参数: ```cpp // 在预览和拍照请求处理分支中添加 VOID* pData[] = { &rotationAngle }; UpdateMetadata(pRequest, RotationKey, pData); // RotationKey=270 ``` 需确保两个分支均通过`UpdateMetadata`同步旋转参数[^2]。 ### 3. **调整Sensor Mount Angle配置** 在HAL层`sensor_library.xml`中,确认sensor安装方向参数: ```xml <mountAngle>270</mountAngle> <!-- 若sensor物理安装为270度 --> ``` 错误的值会导致系统旋转补偿失效,需与硬件实际安装方向一致。 ### 4. **验证方向补偿逻辑** 在CamX的`Feature`或`Node`中(如`PostProcNode`),添加旋转矩阵计算逻辑: ```cpp // 计算270度旋转矩阵 mat3 rotationMatrix = { {0, 1, 0}, {-1, 0, 0}, {0, 0, 1} }; SetRotationMatrix(rotationMatrix); // 应用到预览和拍照流水线 ``` 此矩阵强制所有输出图像应用270度旋转补偿。 ### 5. **ADB命令验证** 通过以下命令动态测试方向参数: ```bash adb shell setprop persist.vendor.camera.override.rotation 270 adb reboot ``` 重启后拍摄照片并检查EXIF方向标签是否为`Rotate 270 CW`。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cam_Rxy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值