高通相机系统-usecase pipeline的裁剪

目录

基础介绍

1.ChiUsecase数据结构用来概括一个usecase的信息

2.ChiPipelineTargetCreateDescriptor数据结构用来描述一个完整的pipeline

3.ChiPipelineCreateDescriptor数据结构描述一个pipeline的构成

4.ChiTargetPortDescriptorInfo数据结构描述一个pipeline的sink/source缓存信息

裁剪变量

1.裁剪变量的定义

2.usecase描述中的裁剪参数

usecase裁剪的代码实现

举例子 - 裁剪ZSLSnapshotJpeg

1.ZSLSnapshotJpeg pipeline

 2.裁剪node

4.裁剪sourceTarget



usecase裁剪是指根据pruneSettings将一个usecase描述中部分内容剪修的处理过程。

接下来会以ZSLSnapshotJpeg 这条pipeline的裁剪为例子,了解是如何裁剪的。

在开始裁剪一条pipeline前,先看下usecas描述中的几个数据结构,了解可裁剪的内容有哪些(node, target, link)。

基础介绍

1.ChiUsecase数据结构用来概括一个usecase的信息

struct ChiUsecase
{
    const CHAR* pUsecaseName;
    UINT streamConfigMode;
    UINT numTargets;
    ChiTarget** ppChiTargets;
    UINT numPipelines;
    ChiPipelineTargetCreateDescriptor* pPipelineTargetCreateDesc;
    const PruneSettings* pTargetPruneSettings;
    BOOL isOriginalDescriptor;
}

一个usecase的基本信息包括:

  • usecase名字

  • streamConfigMode, 表明该usecase用于什么相机操作模式

  • chiTargets, 该usecase所拥有的target,包括sinkTarget和sourceTarget.

  • chiPipelineTargetCreateDescriptor, 该usecase所拥有的pipeline的描述

  • pruneSettings, 该usecase的裁剪配置

2.ChiPipelineTargetCreateDescriptor数据结构用来描述一个完整的pipeline

struct ChiPipelineTargetCreateDescriptor
{
    const CHAR* pPipelineName;
    ChiPipelineCreateDescriptor pipelineCreateDesc;
    ChiTargetPortDescriptorInfo sinkTarget;
    ChiTargetPortDescriptorInfo sourceTarget;
};

一个Pipeline的完整描述有四个内容:

  • pipeline名,区分pipeline的不同;

  • pipeline的构建描述,描述这条pipeline是什么样的构成

  • sinkTarget下游目标,描述pipeline的输出是什么样的;

  • sourceTarget上游目标,描述pipeline的输入是什么样的。

3.ChiPipelineCreateDescriptor数据结构描述一个pipeline的构成

typedef struct ChiPipelineCreateDescriptor
{
    UINT32 size;
    INT32 numNode;
    CHINODE* pNodes;      
    INT32 nulLinks;
    CHINODELINK* pLinks;
    UINT32 isRealTime;
    UINT numBatchedFrames;
    UINT maxFPSValue;
    UINT32 cameraId;
    CHIMETAHANDLE hPipelineMetadata;
    BOOL HALOutputBufferCombined;
    UINT32 logicalCameraId;
}

pipeline的构成有两个基本构件:

  • CHINODE,说明这条pipeline由哪些节点租出

  • CHINODELINK, 说明够成这条pipeline的节点是如何连接的

对于realtime pipeline是sensor作为开始节点,通过isRealTime和cameraId区分。

3.1用CHINODE数据结构描述一个节点

typedef struct ChiNode
{
    CHINODEPROPERTY* pNodeProperties;
    UINT32 nodeId;
    UINT32 nodeInstanceId;
    CHINODEPORTS nodeAllPorts;
    UINT32 numProperties;
    PruneSettings pruneProperties;
}CHINODE;

3.2用CHINODELINK数据结构描述两个端口的连接

typedef struct ChiNodeLink
{
    CHILINKNODESCRIPTOR srcNode;
    UINT32 numDestNodes;
    CHILINKNODESCRIPTOR pDestNodes;
    CHILINKBUFFERPROPERTIES bufferProperties;
    CHILINKPROPERTIES linkProperties;
}CHINODELINK;

4.ChiTargetPortDescriptorInfo数据结构描述一个pipeline的sink/source缓存信息

struct ChiTargetPortDescriptorInfo
{
    UINT numTargets;
    ChiTargetPortDescriptor* pTargetPortDesc;
}

按照数据的流入/流出方向,端口缓存分为两种:流入端口的缓存称为sinkTarget,从端口流出的缓存sourceTarget。

一个端口的缓存包括两个内容:

  • numTargets缓存的个数,表明该端口缓存有几份组成

  • chiTargetPortDescriptor缓存的端口描述,说明一份缓存是和哪些端口交互、这份缓存的地址是什么

4.1用ChiTargetPortDescriptor数据结构描述一个端口的一个缓存

struct ChiTargetPortDescriptor
{
    const CHAR* pTargetName;
    ChiTarget* pTarget;
    UINT numNodePorts;
    ChiLinkNodeDescriptor* pNodePort;
}

一份端口缓存包含三个内容:

  • target名字,

  • 指向chiTarget的指针。

  • 和这个target连接的ports信息

struct ChiTarget
{
    ChiStreamType direction;          //target的方向:input/output/bidirectional
    BufferDimension dimension;        //缓存尺寸
    UINT numFormats;                  //该target支持的缓存格式的个数
    ChiBufferFormat* pBufferFormats;  //缓存格式
    ChiStream* pChiStream;            //指向该target使用的chiStream的指针
}

ChiLinkNodeDescriptor数据结构用于描述link中关于node的用于定义两个node间连接的信息:

typedef struct ChiLinkNodeDescriptor
{
    UINT32 nodeId;
    UINT32 nodeInstanceId;
    UINT32 nodePortId;
    UINT32 portSourceTypeId;
    PruneSettings pruneProperties;
}CHILINKNODESCRIPTOR;

从以上数据结构的定义发现具有裁剪属性的有:

  • ChiUsecase,即usecase的描述

  • ChiLinkNodeDescriptor, target中的link描述

  • ChiNode, 节点

裁剪变量

1.裁剪变量的定义

struct PruneSettings {
    UINT numSettings;
    const PruneVariant* pVariants;
}

2.usecase描述中的裁剪参数

在描述usecase的xml中node的裁剪参数如下,

link port的裁剪参数:

usecase裁剪的代码实现

CDKResult UsecaseSelector::PruneUsecaseDescriptor(const ChiUsecase* const pUsecase,
                                         const UINT numPruneVariants,
                                         const PruneVariant* const pPruneVariants,
                                         ChiUsecase** ppPrunedUsecase)
{
    //1.为目的usecase描述分配空间
    ChiUsecase *pPrunedUsecase = 
              static_cast<ChiUsecase*>(CHX_CALLOC(1*sizeof(ChiUsecase)));
    ChiPipelineTargetCreateDescriptor* pPrunedTargetCreateDesc =
        static_cast<ChiPipelineTargetCreateDescriptor*>(
        CHX_CALLOC(pUsecase->numPipelines * sizeof(ChiPipelineTargetCreateDescriptor)));
    ChiTarget** ppPrunedChiTargets = static_cast<ChiTarget**>(
    	CHX_CALLOC(pUsecase->numTargets * sizeof(ChiTarget*)));
    
    pPrunedUsecase->pPipelineTargetCreateDesc = pPrunedTargetCreateDesc;
    pPrunedUsecase->ppChiTargets = ppPrunedChiTargets;
    
    *ppPrunedUsecase = pPrunedUsecase;
    //2.填充目的usecase
    pPrunedUsecase->pUsecaseName = pUsecase->pUsecaseName;
    pPrunedUsecase->streamConfigMode = pUsecase->streamConfigMode;
    pPrunedUsecase->numPipelines = pUsecase->numPipelines;
    //3.组成usecase的pipeline的描述
    for (UINT j = 0; j < pUsecase->numPipelines; j++)
    {
        ChiPipelineTargetCreateDescriptor& rTargetPrunedDesc = 
            					pPrunedUsecase->pPipelineTargetCreateDesc[j];
        ChiPipelineCreateDescriptor& rPrunedDesc = rTargetPrunedDesc.pipelineCreateDesc;
        rTargetPrunedDesc.pPipelineName = rTargetCreateDesc.pPipelineName;
        rTargetPrunedDesc.sinkTarget.pTargetPortDesc = static_cast<ChiTargetPortDescriptor*>(
            CHX_CALLOC(rTargetCreateDesc.sinkTarget.numTargets *
                       sizeof(ChiTargetPortDescriptor)));
        rTargetPrunedDesc.sourceTarget.pTargetPortDesc = static_cast<ChiTargetPortDescriptor*>(
            CHX_CALLOC(rTargetCreateDesc.sourceTarget.numTargets *
                       sizeof(ChiTargetPortDescriptor)));
        rPrunedDesc.isRealTime = rCreateDesc.isRealTime;
        
        rPrunedDesc.pNodes = static_cast<CHINODE*>(
            CHX_CALLOC(rCreateDesc.numNodes * size(CHINODE)));
        rPrunedDesc.pLinks = static_cast<CHINODELINK*>(
            CHX_CALLOC(rCreateDesc.numLinks * sizeof(CHINODELINK)));
        
        const ChiPipelineCreateDescriptor& rCreateDesc = 
            rTargetCreateDesc.pipelineCreateDesc;
        //3.1裁剪node
        //3.2裁剪link
        //3.3裁剪sourceTarget
    }
}

ppPrunedUsecase是裁剪后的usecase,pUsecase是裁剪的初始,pPruneVariants是裁剪参数。

举例子 - 裁剪ZSLSnapshotJpeg

工程中对usecase的裁剪基于xml中描述的usecase,通过裁剪参数将clone的usecase裁剪成业务需要的usecase。根据configure_streams阶段用户设置的相机参数和使用的相机设备会生成一组裁剪参数。

这里使用的用户裁剪参数:EIS :Disabled 、Snapshot:JPEG、FDIN:Disabled

1.ZSLSnapshotJpeg pipeline

xml中对ZSLSnapshotJpeg pipeline的描述如下,

 2.裁剪node

检索Jpeg node的裁剪参数group=Snapshot,然后查找到用户设置的裁剪参数中有group=Snapshot,比较Jpeg node和用户设置是一致的Jpeg,则当前Jpeg node不需要被剪掉。JPEG Node的描述信息将添加到目的pipeline描述的节点列表中。  

link根据连接的内容有三种情况:

  • sourceTarget-->dstNode

  • srcNode-->dstNode

  • srcNode-->sinkTarget sinkTarget又有两种情况(sink buffer和sink no buffer)

对一条link裁剪主要是三个步骤:

  1. [更新link的start]对于srcNode--> 这种部分,首先需要更新srcNode的端口信息

  2. [更新link的end]对于-->dstNode这部分,如果不被剪掉添加dstNode信息;对于-->sinkTarget这部分,如果不被剪掉更新sinkTarget

  3. [更新link]对于srcNode bypass nextNode这种情况,需要根据srcNode的bypass属性更新link的dstPort(修改link.dstPort dstNode.inputPortA为dstNode.outputPortB,绕过dstNode)

裁剪发生在第二步,根据dstNode或者sinkTarget的裁剪信息确定是不是要被剪掉,不被剪掉的dstNode或者sinkTarget被加到目的usecase.pipeline中。

情况一:sourceTarget-->dstNode

以BPS0[0].BPSOutputPortFull-->IFE0[0].IPEInputPortFull这条link为例子,

步骤1:因为BPS0 node没被剪掉,所以直接更新pipeline.node[BPS0].nodeAllPorts.pOutputPorts[1]的端口信息

步骤2:对于[1]BPSOutputPortFull-->[0]IPEInputPortFull这条link,dstNode是IPE0, 根据节点裁剪逻辑,IPE0是不被剪掉的,所以添加节点描述到目的pipeline.node[]

步骤3:对于[1]BPSOutputPortFull-->[0]IPEInputPortFull这条link,srcNode BPS0没有设置bypass属性,所以IPE0不需要绕过。

 情况二:target-->dstNode

对于ZSLSnapshotJpeg pipeline的输入TARGET_BUFFER_RAW-->[0]BPSInputPort1这条link,裁剪过程:

步骤1:因为是sourceTarget输入,所以不存在port要更新

步骤2:是-->dstNode这种情况,dstNode是BPS, 根据BSP的裁剪参数,BPS0 Node不需要剪掉,所以添加节点到目的pipeline.node[]

步骤3:因为是sourceTarget输入,无bypass逻辑。

情况三:srcNode-->target

对于[1]JPEGAggregatorOutputPort0-->TARGET_BUFFER_BLOB这条link,裁剪过程:

步骤1:srcNode是JPEGAggregator,更新目的pipeline.node[JPEGAggregator0].nodeAllPorts.pOutputPorts[1]的端口信息

步骤2:是-->sinkTarget这种情况,TARGET_BUFFER_BLOB是sink buffer,根据target裁剪逻辑需要被剪掉(轻颜相机没有配置heic拍照)

步骤3:srcNode是JPEGAggregator,没有设置bypass,所以不存在bypass处理。

如果[1]JPEGAggregatorOutputPort0-->TARGET_BUFFER_BLOB JPEGAggregator有设置bypass属性处理逻辑是什么样的?这就不得不说下srcNode bypass处理,有两种情况:

  1. srcNode-->dstNode, 这时更新srcNode link到对应的dstNode.outputPort

  2. srcNode-->sinkTarget(也就是当前假设这种情况),这时由于sinkTarget不是dstNode所以不存在port这个概念,也就不存在bypass概念。

4.裁剪sourceTarget

 

TARGET_BUFFER_RAW这个sourceTarget不需要剪掉通过两个条件判断:

  • TARGET_BUFFER_RAW有接收

  • TARGET_BUFFER_RAW有接收,且接收不被剪掉。

所以裁剪sourceTarget可以分为两步:裁剪接收sourceTarget的node和裁剪sourceTarget和dstNode的连接。

猜你喜欢

转载自blog.csdn.net/hongyeying/article/details/128318445
今日推荐