前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Camera系统 | ConfigureStreams阶段调用

Camera系统 | ConfigureStreams阶段调用

作者头像
Abalone
发布2022-10-05 15:03:03
1.5K0
发布2022-10-05 15:03:03
举报
文章被收录于专栏:影像技术栈影像技术栈

接下来看configureStreams的流程,

从CameraDeviceSession开始

这个调用是app通过createCaptureSession一路下来的(CameraDeviceClient–>Camera3Device–>CameraDeviceSession)

💡 \hardware\interfaces\camera\device\3.4\default\CameraDeviceSession.cpp

CS
CS
CS
CS

从这里,

即将进入CamX-CHI

对一下结构体名字,没错

CS
CS
CS
CS

又要到camxhal3entry中去找了

💡 \vendor\qcom\proprietary\camx\src\core\hal\camxhal3entry.cpp

CS
CS

下面几步跳转和open类似,不再赘述

CS
CS

💡 \vendor\qcom\proprietary\camx\src\core\hal\camxhal3.cpp

CS
CS
CS
CS
CS
CS

💡 \vendor\qcom\proprietary\camx\src\core\hal\camxhaldevice.cpp

CS
CS
CS
CS

很直观,先获取chi的接口对象,再通过这个对象去到chi实现中

💡 \vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensioninterface.cpp

CS
CS

这个函数有六七百行,大略看一下:

💡 \vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensionmodule.cpp

CS
CS

这个g_chiContextOps应该是CamX提供给CHI的对象,其中函数指针的映射是在ExtensionModule的初始化中完成的过程就不细讲了,可以看这段解释:

“CHI中的ExtensionModule在初始化的时候,其构造方法中也会通过调用dlopen方法加载camera.qcom.so库,并将其入口方法ChiEntry通过dlsym映射出来,之后调用该方法,将g_chiContextOps(ChiContextOps,该结构体中定义了很多指针函数)作为参数传入CamX中,一旦进入CamX中,便会将本地的操作方法地址依次赋值给g_chiContextOps中的每一个函数指针,这样CHI之后就可以通过g_chiContextOps访问到CamX方法。”

ChiEntry方法中的函数指针定义在:

💡 \vendor\qcom\proprietary\camx\src\core\chi\camxchi.cpp

CS
CS

接着往下看,进行了很多StreamConfig的设置

CS
CS

第一个框,看一下做了什么:

CS
CS

把当前camera对应的HalOps映射到m_HALOps本地,pHalOps是更上层传入的

回到InitializeOverrideSession中,第二个框看上去就很重要了,涉及到了Usercase的选择,有了之前的印象,我们可以知道Usecase是camx针对不同的stream建立的对象,用来管理选择feature,并且创建 pipeline以及session。

(更详细的介绍可以看https://blog.csdn.net/u012596975/article/details/107138576)

CS
CS

如此看来,接下来的m_pUsecaseSelector->GetMatchingUsecase这一步调用重要性可见一斑,看看具体做了什么:

💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxusecaseutils.cpp

CS
CS

果然如同上述介绍所说,这里根据不同的使用场景,选择了不同的usecaseId,usecaseId结构的定义在:

💡 \vendor\qcom\proprietary\chi-cdk\core\chiutils\chxdefs.h

CS
CS

最后把选择的usecaseId返回InitializeOverrideSession中,这里就要真正地创造usecase对象了:

💡 \vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensionmodule.cpp

CS
CS

💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxusecaseutils.cpp

CS
CS

可以看到,根据不同的usecaseId调用不同的Create方法向上返回usecase对象。并且通过这些case的名字来判断,预览应该走的是PreviewZSL的case,也就是创建了一个AdvancedCameraUsecase的对象,这个AdvancedCameraUsecase类也是使用最多的usecase。

至此,爽快直接,逻辑通顺,看来跟踪的流程是正确的

接着看AdvancedCameraUsecase::Create具体做了什么:

💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp

CS
CS
CS
CS

可以注意到,调用了一个从XML中获取usecase的方法GetXMLUsecaseByName,参数定义如下:

CS
CS

这里很容易联想到第一篇中定义了pipeline、node的那个xml,进去看一下:

CS
CS

这里并没有预想中打开特定xml之后读取的动作,看了一下高亮的这个变量应该是存储了所有的usecase,不幸的是里面涉及的数据结构比较复杂,这里先不深入了

回到Initialize:

CS
CS

又出现了关键词feature,跟进去看一下:

CS
CS
CS
CS
CS
CS

很长,但是可以理解。通过一系列设置,整了一个featureWrapper对象,最后把选好的feature设置到pStreamConfig里去

回到Initialize,看看用这个StreamConfig又干嘛了:

💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp

CS
CS

可以看到,FeatureSetup中把pStreamConfig设置好之后,就丢进了SelectUsecaseConfig进行下一步处理

CS
CS

这下,出现了两个很惊人的函数调用,首先看ConfigureStream,没想到是在这里调用

CS
CS
CS
CS

根据不同的usecaseId,把pStreamConfig中带下来的stream们保存到不同的本地stream变量里

这里大概能看出预览和拍照应该分别使用的是m_pPreviewStream和m_pSnapshotStream

CS
CS

确认这两个stream保存下来之后,计算了一个纵横比保存下来,最后调用了ConfigFdStream

CS
CS

看名字以为和设备有关,看上去也只是把一些参数设置到m_pFdStream变量中去,看一下这个m_pFdStream的定义:

CS
CS

看不出什么端倪,等等看之后的流程会不会用到吧

configStream算完事了,接下来回到SelectUsecaseConfig看BuildUsecase做了什么

💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp

CS
CS
CS
CS

主要填充了这两个Pipelines的数组

并且从6945行开始的两层循环可以看出来,每个物理摄像头可能对应多个feature,然后会把feature里的每个pipeline都保存下来

CS
CS

接着,usercase相关的信息也会保存到m_pClonedUsecase中

这样,SelectUsecaseConfig完成了需要关注的事务,回到AdvancedCameraUsecase::Initialize中

💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp

CS
CS

从SelectUsecaseConfig出来之后对m_pCallbacks做了一系列赋值操作,看看这个callback具体是callback什么,定义在

💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.h

CS
CS

可以看出,主要就是result的回调,那看来还挺重要的。

第一个框里可以看出,每一个pipeline都有其callback(也可以从初始化的时候,给m_pCallbacks分配空间时候看出,5194行,用pipeline的num来申请空间)

第二个框,调用了CameraUsecaseBase::Initialize,把刚映射完成的callback传进去

CS
CS

框里的注释可以看出来,session和pipeline是一对一的关系,数量是一样的

接着往下看:

CS
CS

这里,终于正式开始创建pipeline,从循环次数和CreatePipeline接收的参数可以看出,这里也是把每个pipeline都创建出来,接着进入CreatePipeline中,注意一下第二个参数PipelineType::Default,值为5

CS
CS
CS
CS

继续看Pipeline::Create

💡 \vendor\qcom\proprietary\chi-cdk\core\chiframework\chxpipeline.cpp

CS
CS

代码中能看到的Pipeline类有两个,chxpipeline,和camxpipeline。

虽然从参数来看可以知道这个Initialize调用到的是chxpipeline,但是camxpipeline是什么作用呢?它和chxpipeline有类似的接口设置,又有何用意?先把这个问题保留,对不上号的camxpipeline的参数是input/output两个data,留个印象:

CS
CS

接着先看chxpipeline中的Initialize

💡 \vendor\qcom\proprietary\chi-cdk\core\chiframework\chxpipeline.cpp

CS
CS

为pipeline中的一系列成员变量赋值,注意445行,这里应该是不进if的,记得刚才提到传入的type吗,是Default而不是OfflinePreview

接下来可以回到CameraUsecaseBase::CreatePipeline中继续往下看了

CS
CS

可以看到对outputbuffer和inputbuffer中所有的sink和src进行了配置

sink和src又是什么呢,看它们和output/input的对应关系加上百度了一些相关知识,大概了解到sink一般对应一个模块的输出部分,src当然就是输入部分

那么推测这部分就是把pipeline中输入输出部分的buffer、node和port配置了一下

CreatePipeline中接着往下

CS
CS

配置好的pSinkTarget和pSrcTarget统一set到pPipelineData中,这些set方法没什么好说的,就是把传入的参数都保存到对应的成员变量里去。

再看看最后一个框中的调用:

💡 \vendor\qcom\proprietary\chi-cdk\core\chiframework\chxpipeline.cpp

CS
CS

用所有保存好的信息,创建pipelineCreateData对象,然后传到ExtensionModule中的CreatePipelineDescriptor

💡 \vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensionmodule.cpp

CS
CS

通过老朋友g_chiContextOps继续,这里就是向camx的调用了,不记得这个对象就看这里的函数映射关系

💡 vendor\qcom\proprietary\camx\src\core\chi\camxchi.cpp

https://www.notion.so

继续跟着CreatePipelineDescriptor往下追踪:

💡 \vendor\qcom\proprietary\camx\src\core\chi\camxchicontext.cpp

https://www.notion.so

主要关注这个函数中,对pPipelineDescriptor这个对象做了什么,毕竟是要返回上去的,上图只能看到一些常规的赋值,接着往下看:

CS
CS

到这里,第一个框,大概知道根据pPipelineDescriptor中的信息,对pipelineCreateInputData和pipelineCreateOutputData做了一些设置。

而第二个框就有意思了,这两个参数唤起了沉睡的记忆,刚在initialize的时候遇到了两个pipeline类的问题,这里传入Pipeline::Create的是camxpipeline中的函数接收的input/output参数,也就可以回答上面提到的camxpipeline类是用来做什么的了。

解决了之前留下的问题,于是这里的Create调用到:

💡 \vendor\qcom\proprietary\camx\src\core\camxpipeline.cpp

CS
CS

这里就相当冗长了

CS
CS

创建了一系列lock,后面也是很多从函数名看不出端倪的调用

追流程要抓关键,这个函数最终会返回result,那么很有理由相信result会作为关键调用的返回值被赋值,有了这个思想之后看看哪些地方修改了result的值

CS
CS

果然,看到了这个想看到的函数,进入看看:

💡 \vendor\qcom\proprietary\camx\src\core\camxpipeline.cpp

CS
CS

可以看到,node数量和node信息之类的值都是pCreateInputData->pPipelineDescriptor带下来的,也就是说pipeline中就已经决定了使用哪些node

同样,追着result往下看:

CS
CS

💡 \vendor\qcom\proprietary\camx\src\core\camxnode.cpp

CS
CS

这里创建了一个pFactory对象,这个对象赋值的时候同时把HwEnviroment中的HwFactory单例创建出来了。

这部分流程看上去不太重要,但是可以说明白pFactory->CreateNode调用的位置,不想看可以跳过,这里大致列一下调用流程:

💡 \vendor\qcom\proprietary\camx\src\core\camxhwenvironment.h

CS
CS

m_pHwFactory是怎么创造出来的呢:

💡 \vendor\qcom\proprietary\camx\src\core\camxhwenvironment.cpp

CS
CS

💡 \vendor\qcom\proprietary\camx\src\core\camxhwenvironment.cpp

CS
CS

要素察觉,看着又是一系列函数指针定向

💡 \vendor\qcom\proprietary\camx\src\hwl\titan17x\camxtitan17xhwl.cpp

CS
CS

💡 \vendor\qcom\proprietary\camx\src\hwl\titan17x\camxtitan17xfactory.cpp

CS
CS

回到Node::Create中

💡 \vendor\qcom\proprietary\camx\src\core\camxnode.cpp

CS
CS

通过上面支线解析可以知道,pFactory是HwFactory的子类Titan17xFactory的一个对象

所以CreateNode的调用位置就很好知道了:

💡 \vendor\qcom\proprietary\camx\src\core\camxhwfactory.cpp 💡 \vendor\qcom\proprietary\camx\src\hwl\titan17x\camxtitan17xfactory.cpp

CS
CS

终于看到了这些node最后被创造的地方,向上返回不同类型的Create创建返回的nodes

一直回到这里:

💡 \vendor\qcom\proprietary\camx\src\core\camxnode.cpp

CS
CS

可以看到pNode还会调用一个Initialize函数,但是注意pNode创建的时候是由不同类型的Create创建返回的,所以这里不去关注这些node的初始化过程,接着向上返回

回到\vendor\qcom\proprietary\camx\src\core\camxpipeline.cpp:

CS
CS

看上去node创建出来之后,还对这个node的各种属性进行赋值

接着往下:

CS
CS

如注释所说,创建node的连接关系,输入输出port、link等等

这样一直到最后,完成CreateNodes的工作,回到

💡 \vendor\qcom\proprietary\camx\src\core\camxpipeline.cpp

Initialize中:

CS
CS

接下来也没有什么值得说道的,Pipeline::Initialize结束,标记这个pipeline已经初始化后,向上返回

回到CreatePipelineDescriptor

💡 \vendor\qcom\proprietary\camx\src\core\chi\camxchicontext.cpp

CS
CS

pPipelineDescriptor创建好了,继续向上返回到ChiCreatePipelineDescriptor

💡 \vendor\qcom\proprietary\camx\src\core\chi\camxchi.cpp

CS
CS

💡 \vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensionmodule.cpp

CS
CS

依然向上返回到CreateDescriptor中

💡 \vendor\qcom\proprietary\chi-cdk\core\chiframework\chxpipeline.cpp

CS
CS

创建出来的descriptor保存到m_hPipelineHandle

CS
CS

再把信息保存到m_pipelineInfo.hPipelineDescriptor,继续向上返回到CreatePipeline中

💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp

CS
CS

CreatePipeline也就这样结束,看看Initialize中还做了什么

💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp

CS
CS

除了pipeline,看样子还创造了session,omfg以为已经结束了

先不管这个session的创建过程,有机会再细看吧

这样看完了CameraUsecaseBase::Initialize的过程,调用这个Initialize的上层是AdvancedCameraUsecase::Initialize

💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp

CS
CS

把usecase创建完成的事post一下,主要是把feature

AdvancedCameraUsecase::Initialize中,重要的调用(

GetXMLUsecaseByName、FeatureSetup、SelectUsecaseConfig、CameraUsecaseBase::Initialize)执行完毕后,也继续向上返回

💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp 💡 \vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxusecaseutils.cpp

CS
CS

继续一路返回到ExtensionModule::InitializeOverrideSession中:

💡 \vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensionmodule.cpp

CS
CS
CS
CS

至此,一个logicalCamera对应的usecase就创建出来,可以看到创建好之后对这个对象的其他成员也都纷纷赋值或者新建,然后就继续向上返回

\vendor\qcom\proprietary\camx\src\core\hal\camxhaldevice.cpp

CS
CS

成功创建则向上返回true赋值状态

CS
CS

向上:

💡 \vendor\qcom\proprietary\camx\src\core\hal\camxhal3.cpp

CS
CS

confige完毕之后,把stream的信息打印了一滩,之后也是向上返回

CS
CS

之后就会一路回到cameraservice了

ConfigureStream整体流程参考:

CS
CS
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-09-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从CameraDeviceSession开始
  • 即将进入CamX-CHI
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档