搞定offer-面试知识点

《搞定offer-面试知识点》系列,CAMX Usecase ↔ Pipeline 匹配流程

近期,星球会陆续增加一些新的模块内容。比如目前已经在更新的《Android Camera全栈(Qcom Camx)》系列,目前已经更新到了第18章。
接下来,还会加一个《搞定offer》系列。面试经验、简历这些,是之前就已经有的内容,这次主要是增加“面试知识点”这块的内容。我会结合自己面试经验,总结梳理面试过程中遇到的高频问题,希望能帮助到参加相机相关岗位面试的同学。
今天的文章内容,主要结合代码,讲解下camx里面,usecase和pipeline的匹配规则是怎样的,整个的匹配流程是怎样的?
开始阅读文章之前,大家可以先思考下,这个问题如果让你回答,你会怎么回答?

 1. 全流程总览
2. 关键文件路径

 3. UsecaseId 枚举

定义在 chi-cdk/core/chiusecase/chxdefs.h

enumclassUsecaseId : uint32_t {
    Default         =0,  // → AdvancedCameraUsecase(大部分单摄场景)
    PreviewZSL      =1,  // ZSL 预览
    VideoLiveShot   =2,  // 录像时拍照
    SAT             =3,  // Smooth Zoom / MultiCamera
    RTB             =4,  // Realtime Bokeh
    QCFA            =5,  // Quad CFA (48M/64M binning 模式)
    HighSpeed       =6,  // 高帧率录像 (120fps+)
    VideoEIS        =7,  // 电子防抖
    SuperSlowMoFRC  =8,  // 超级慢动作 FRC
};


4. UsecaseSelector 决策逻辑

文件 chi-cdk/core/chiusecase/chxusecaseutils.cpp → GetMatchingUsecase()

// 输入来自 HAL3 的 configure_streams()
//   - operation_mode: NORMAL / CONSTRAINED_HIGH_SPEED / SAT ...
//   - stream_count: framework 请求的 stream 数量
//   - physical_camera_count: 物理 camera 数量

UsecaseIdGetMatchingUsecase(
    OperationModeop_mode,
    uint32_t      stream_count,
    uint32_t      physical_camera_count)
{
    // 多摄 + 多 stream → 切 MultiCamera (SAT)
    if (physical_camera_count>1&&stream_count>1) {
        returnUsecaseId::SAT;
    }

    // HFR 高帧率模式
    if (op_mode==OperationMode::CONSTRAINED_HIGH_SPEED) {
        returnUsecaseId::HighSpeed;
    }

    // 检测 EIS 是否使能
    if (IsEISEnabled()) {
        returnUsecaseId::VideoEIS;
    }

    // 默认 → AdvancedCameraUsecase(覆盖预览/录像/ZSL拍照等)
    returnUsecaseId::Default;
}

注意: 这一步只确定 UsecaseId,不涉及 XML 节点匹配。大多数单摄场景走 UsecaseId::Default,实际 Usecase 名将在 AdvancedCameraUsecase 内部通过 XML 匹配确定。



 5. UsecaseFactory 分发

文件 chi-cdk/core/chiusecase/chxusecasefactory.cpp → Create()

Usecase*UsecaseFactory::Create(UsecaseIdid) {
    switch (id) {
        caseUsecaseId::Default:
            returnnewAdvancedCameraUsecase();    // ~80% 场景

        caseUsecaseId::SAT:
            returnnewUsecaseMultiCamera();       // 双摄/多摄变焦

        caseUsecaseId::HighSpeed:
            returnnewUsecaseHighSpeed();         // HFR 高帧率

        caseUsecaseId::VideoEIS:
            returnnewUsecaseVideoEIS();

        caseUsecaseId::QCFA:
            returnnewUsecaseQCFA();              // Quad CFA 模式
        ...
    }
}
UsecaseId创建的类典型场景
Default / PreviewZSL / VideoLiveShotAdvancedCameraUsecase单摄预览、录像、ZSL 拍照
SAT / MultiCameraUsecaseMultiCamera双摄变焦(Wide+Tele)
HighSpeedUsecaseHighSpeed高帧率录像(120/240fps)
VideoEISUsecaseVideoEIS电子防抖录像


 6. AdvancedCameraUsecase::Create() 内部三步

文件 chi-cdk/core/chiusecase/advancedcamerausecase.cpp

6-a. SelectUsecaseConfig() — XML Usecase 匹配

// 匹配算法:遍历编译进来的所有 XML Usecase,寻找匹配配置
voidSelectUsecaseConfig(constStreamConfig&config) {
    for (auto&xmlUsecase : GetXMLUsecaseList()) {

        // 条件①:StreamConfigMode 必须一致
        if (xmlUsecase.streamConfigMode!=config.op_mode)
            continue;

        // 条件②:framework 的每个 stream 都必须在 XML Targets 里有匹配
        boolmatch=true;
        for (auto&stream : config.GetStreams()) {
            boolfound=false;
            for (auto&target : xmlUsecase.GetTargets()) {
                // 匹配三要素:Format 类型 + Direction + Resolution 范围
                if (stream.GetFormat()    ==target.GetFormat()     &&
                    stream.GetDirection() ==target.GetDirection()  &&
                    stream.GetWidth()     <=target.GetMaxW()       &&
                    stream.GetHeight()    <=target.GetMaxH()) {
                    found=true;
                    break;
                }
            }
            if (!found) { match=falsebreak; }
        }

        if (match) {
            m_pXmlUsecase=&xmlUsecase;  // 采用匹配到的 Usecase
            return;
        }
    }
    // 无完全匹配 → fallback 到默认 Usecase
    m_pXmlUsecase=GetDefaultXMLUsecase();
}

6-b. FeatureSetup() — Feature 确定

voidFeatureSetup() {
    for (auto&stream : m_frameworkStreams) {
        if (stream.GetType() ==StreamType::Snapshot)
            AddFeature(FeatureType::MFNR);  // 多帧降噪

        if (IsHDRRequested())
            AddFeature(FeatureType::HDR);

        if (IsVideoSnapshotEnabled())
            AddFeature(FeatureType::LiveShot);  // 录像时拍照
    }
}

6-c. BuildUsecase() — 插入 Feature Pipelines

voidBuildUsecase() {
    // 1. 克隆 XML 定义的 base topology
    m_pClonedUsecase=CloneXMLUsecase(m_pXmlUsecase);

    // 2. 根据 Feature 插入额外的 pipeline / 修改属性
    for (auto&ft : m_features) {
        switch (ft) {
            caseFeatureType::MFNR:
                m_pClonedUsecase->AddPipeline("ReprocessJpeg");
                break;
            caseFeatureType::LiveShot:
                m_pClonedUsecase->AddPipeline("ReprocessYuv2Streams");
                break;
            caseFeatureType::HDR:
                // HDR 一般不改 pipeline 拓扑,通过 IFE 属性控制
                m_pClonedUsecase->SetNodeProperty("IFE""HDRMerge"1);
                break;
        }
    }
}
Feature插入的 Pipeline用途
MFNRReprocessJpeg多帧降噪 / 夜景 / HDR+
HDR不改拓扑,设 IFE 属性HDR 合并模式
LiveShotReprocessYuv2Streams录像时拍照


 7. Pipeline & Session 创建

文件 chi-cdk/core/chiusecase/camerausecasebase.cpp → Initialize()

CamxResultCameraUsecaseBase::Initialize() {
    // 遍历 BuildUsecase 后确定的 pipeline 列表
    for (uint32_ti=0i<m_pClonedUsecase->GetPipelineCount(); i++) {

        constauto&pipeDef=m_pClonedUsecase->GetPipelineDef(i);

        // Pipeline::Create() — 解析 XML 拓扑,创建节点和 DRQ 链路
        Pipeline*pPipeline=Pipeline::Create(
            pipeDef.GetName(),
            pipeDef.GetNodeList(),
            pipeDef.GetPortLinkages(),
            pipeDef.GetAllocProperties());

        // Session::Create() — 绑定硬件资源
        Session*pSession=Session::Create(pPipelinem_hCameraHandle);
        pSession->RegisterResultCb(ProcessResultCb);

        m_pPipelines.push_back(pPipeline);
        m_pSessions.push_back(pSession);
    }
    returnCamxResult::Success;
}

camx/src/core/camxpipeline.cpp → Pipeline::Create() 核心逻辑:

// camxpipeline.cpp — 核心解析逻辑
Pipeline*Pipeline::Create(
    constchar*      name,
    constNodeList&  nodeList,
    constLinkList&  linkList,
    ...)
{
    Pipeline*pPipeline=newPipeline(name);

    // ① 创建所有 node(Sensor / IFE / IPE / BPS / JPEG ...)
    for (auto&nodeDef : nodeList) {
        Node*pNode=HwFactory::CreateNode(
            nodeDef.GetNodeId(),
            nodeDef.GetInstanceId(),
            nodeDef.GetProperties());
        pPipeline->GetNodeMap()[nodeDef.GetKey()] =pNode;
    }

    // ② 建�� DRQ 链路(= 拓扑图中的边)
    for (auto&linkDef : linkList) {
        DRQ*pDrq=DRQ::Create(
            pPipeline->FindNode(linkDef.GetSrcKey()),
            linkDef.GetSrcPort(),
            pPipeline->FindNode(linkDef.GetDstKey()),
            linkDef.GetDstPort(),
            linkDef.GetBufferProperties());
        pPipeline->GetDrqLinks().push_back(pDrq);
    }

    // ③ 分配硬件资源(IFE/IPE/BPS 时间片、时钟、总线带宽)
    pPipeline->AllocateHWResources();

    returnpPipeline;
}

关键理解: Pipeline 是一个有向无环图(DAG)。Node = 顶点,DRQ Link = 边,数据从 Sensor 节点流向 SinkBuffer 节点。



 8. 完整示例:双摄 Preview + Video + Snapshot

以 kona 平台的 UsecaseDualCamera 为例:

// config: op_mode=NORMAL, streams=3 (Preview+Video+Snapshot), 2 cameras (Wide+Tele)

// Step 1: UsecaseSelector
physical_camera_count=2stream_count=3    UsecaseId::SAT

// Step 2: UsecaseFactory
newUsecaseMultiCamera()

// Step 3: UsecaseMultiCamera::Create()
// ③-a XML 匹配
kona_usecase.xml<UsecaseName>UsecaseDualCamera</UsecaseName>
  Target匹配:
    Preview(1080pNV12)   TARGET_BUFFER_PREVIEW0 (max5488×4112)  
    Video(4KNV12)        TARGET_BUFFER_VIDEO0    (max5488×4112)  
    Snapshot(12MJPEG)    TARGET_BUFFER_SNAPSHOT0 (format=JPEG)     

// ③-b FeatureSetup: 有 Snapshot → MFNR
// ③-c BuildUsecase: 插入 MFNR 的 ReprocessJpeg pipeline

// Step 4: Initialize()
// 最终 pipelines:
  Pipeline[0="Realtime0"      // Wide:  Sensor0 → IFE → IPE → Sink
  Pipeline[1="Realtime1"      // Tele:  Sensor1 → IFE → IPE → Sink
  Pipeline[2="ReprocessJpeg"  // MFNR:  IPE → JPEG → Aggregator → Sink

  Session[0=Session::Create(Pipeline[0])
  Session[1=Session::Create(Pipeline[1])
  Session[2=Session::Create(Pipeline[2])

  3Pipelines3SessionsReady

⚠️ 注意: 如果 UsecaseDualCamera 的 Targets 不包含 Video stream,传了 Video stream 下来匹配不上 → fallback 到默认 Usecase。这就是为什么有的双摄平台录像时会被降级到单摄。



9. 匹配规则总结

维度匹配规则说明
StreamConfigMode精确相等NORMAL / CONSTRAINED_HIGH_SPEED / SAT 必须一致
Format相等(OR 关系)一个 Target 可能支持多个 Format(如 NV12 + UBWC)
Direction相等TargetOutput(对应 framework 的 output stream)
Resolution≤ MaxW/MaxH只要 ≤ 上限就算匹配,不等精度匹配
FeatureBuildUsecase 时插入不改 Usecase 匹配结果,只影响 pipeline 数量

图片

《更多交流,欢迎加入知识星球》

推荐阅读:

关于博主 

采用v4l2loopback来实现 虚拟Camera

Camera基础及一些基本概念

Android Camera 学习路线 | 个人推荐

Android Camera开发系列(干货满满)

Camera Hal|如何学习一个新平台

一篇文章带你了解Android 最新Camera框架

学习完Camera入门课程视频,可以去找工作了?



欢迎扫码关注「小驰行动派」公众号 10 年Camera开发 | Camera技术干货 | 行业洞察 | Camera实战分享
小驰行动派公众号 扫一扫关注
分享到: 复制链接
← 上一篇 Android Camera 全栈课程(Qcom Camx) — 大纲 下一篇 → Android Camera 全栈课程(Qcom Camx):Camera2 实战 — 预览/拍照/录像

推荐课程

想系统学习 Camera 开发?看看这些课程

评论 (0)

暂无评论,快来抢沙发吧