ANDROID CAMERA 学习-configureStreams 在 QCOM CAMX架构中的调用
系列文章目录:
- openCamera
- configureStreams
- request流转
接下来看configureStreams的流程,从CameraDeviceSession开始,这个调用是app通过createCaptureSession一路下来的(CameraDeviceClient–>Camera3Device–>CameraDeviceSession)
\hardware\interfaces\camera\device\3.4\default\CameraDeviceSession.cpp
从这里,即将进入CamX-CHI,
对一下结构体名字,没错
又要到camxhal3entry中去找了
\vendor\qcom\proprietary\camx\src\core\hal\camxhal3entry.cpp
下面几步跳转和open类似,不再赘述
\vendor\qcom\proprietary\camx\src\core\hal\camxhal3.cpp
\vendor\qcom\proprietary\camx\src\core\hal\camxhaldevice.cpp
很直观,先获取chi的接口对象,再通过这个对象去到chi实现中
\vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensioninterface.cpp
这个函数有六七百行,大略看一下:
\vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensionmodule.cpp
这个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
接着往下看,进行了很多StreamConfig的设置
第一个框,看一下做了什么:
把当前camera对应的HalOps映射到m_HALOps本地,pHalOps是更上层传入的
回到InitializeOverrideSession中,第二个框看上去就很重要了,涉及到了Usercase的选择,有了之前的印象,我们可以知道Usecase是camx针对不同的stream建立的对象,用来管理选择feature,并且创建 pipeline以及session。
(更详细的介绍可以看https://blog.csdn.net/u012596975/article/details/107138576)
如此看来,接下来的m_pUsecaseSelector->GetMatchingUsecase这一步调用重要性可见一斑,看看具体做了什么:
\vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxusecaseutils.cpp
果然如同上述介绍所说,这里根据不同的使用场景,选择了不同的usecaseId,usecaseId结构的定义在:
\vendor\qcom\proprietary\chi-cdk\core\chiutils\chxdefs.h
最后把选择的usecaseId返回InitializeOverrideSession中,这里就要真正地创造usecase对象了:
\vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensionmodule.cpp
\vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxusecaseutils.cpp
可以看到,根据不同的usecaseId调用不同的Create方法向上返回usecase对象。并且通过这些case的名字来判断,预览应该走的是PreviewZSL的case,也就是创建了一个AdvancedCameraUsecase的对象,这个AdvancedCameraUsecase类也是使用最多的usecase。
至此,爽快直接,逻辑通顺,看来跟踪的流程是正确的
接着看AdvancedCameraUsecase::Create具体做了什么:
\vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp
可以注意到,调用了一个从XML中获取usecase的方法GetXMLUsecaseByName,参数定义如下:
这里很容易联想到第一篇中定义了pipeline、node的那个xml,进去看一下:
这里并没有预想中打开特定xml之后读取的动作,看了一下高亮的这个变量应该是存储了所有的usecase,不幸的是里面涉及的数据结构比较复杂,这里先不深入了
回到Initialize:
又出现了关键词feature,跟进去看一下:
很长,但是可以理解。通过一系列设置,整了一个featureWrapper对象,最后把选好的feature设置到pStreamConfig里去
回到Initialize,看看用这个StreamConfig又干嘛了:
\vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp
可以看到,FeatureSetup中把pStreamConfig设置好之后,就丢进了SelectUsecaseConfig进行下一步处理
这下,出现了两个很惊人的函数调用,首先看ConfigureStream,没想到是在这里调用
根据不同的usecaseId,把pStreamConfig中带下来的stream们保存到不同的本地stream变量里
这里大概能看出预览和拍照应该分别使用的是m_pPreviewStream和m_pSnapshotStream
确认这两个stream保存下来之后,计算了一个纵横比保存下来,最后调用了ConfigFdStream
看名字以为和设备有关,看上去也只是把一些参数设置到m_pFdStream变量中去,看一下这个m_pFdStream的定义:
看不出什么端倪,等等看之后的流程会不会用到吧
configStream算完事了,接下来回到SelectUsecaseConfig看BuildUsecase做了什么
\vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp
主要填充了这两个Pipelines的数组
并且从6945行开始的两层循环可以看出来,每个物理摄像头可能对应多个feature,然后会把feature里的每个pipeline都保存下来
接着,usercase相关的信息也会保存到m_pClonedUsecase中
这样,SelectUsecaseConfig完成了需要关注的事务,回到AdvancedCameraUsecase::Initialize中
\vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp
从SelectUsecaseConfig出来之后对m_pCallbacks做了一系列赋值操作,看看这个callback具体是callback什么
定义在\vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.h
可以看出,主要就是result的回调,那看来还挺重要的。
第一个框里可以看出,每一个pipeline都有其callback(也可以从初始化的时候,给m_pCallbacks分配空间时候看出,5194行,用pipeline的num来申请空间)
第二个框,调用了CameraUsecaseBase::Initialize,把刚映射完成的callback传进去
框里的注释可以看出来,session和pipeline是一对一的关系,数量是一样的
接着往下看:
这里,终于正式开始创建pipeline,从循环次数和CreatePipeline接收的参数可以看出,这里也是把每个pipeline都创建出来,接着进入CreatePipeline中,注意一下第二个参数PipelineType::Default,值为5
继续看Pipeline::Create
\vendor\qcom\proprietary\chi-cdk\core\chiframework\chxpipeline.cpp
代码中能看到的Pipeline类有两个,chxpipeline,和camxpipeline。
虽然从参数来看可以知道这个Initialize调用到的是chxpipeline,但是camxpipeline是什么作用呢?它和chxpipeline有类似的接口设置,又有何用意?先把这个问题保留,对不上号的camxpipeline的参数是input/output两个data,留个印象:
接着先看chxpipeline中的Initialize
\vendor\qcom\proprietary\chi-cdk\core\chiframework\chxpipeline.cpp
为pipeline中的一系列成员变量赋值,注意445行,这里应该是不进if的,记得刚才提到传入的type吗,是Default而不是OfflinePreview
接下来可以回到CameraUsecaseBase::CreatePipeline中继续往下看了
可以看到对outputbuffer和inputbuffer中所有的sink和src进行了配置
sink和src又是什么呢,看它们和output/input的对应关系加上百度了一些相关知识,大概了解到sink一般对应一个模块的输出部分,src当然就是输入部分
那么推测这部分就是把pipeline中输入输出部分的buffer、node和port配置了一下
CreatePipeline中接着往下
配置好的pSinkTarget和pSrcTarget统一set到pPipelineData中,这些set方法没什么好说的,就是把传入的参数都保存到对应的成员变量里去。
再看看最后一个框中的调用:
\vendor\qcom\proprietary\chi-cdk\core\chiframework\chxpipeline.cpp
用所有保存好的信息,创建pipelineCreateData对象,然后传到ExtensionModule中的CreatePipelineDescriptor
\vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensionmodule.cpp
通过老朋友g_chiContextOps继续,这里就是向camx的调用了,不记得这个对象就看这里的函数映射关系
S:\virtualCam\vendor\qcom\proprietary\camx\src\core\chi\camxchi.cpp
继续跟着CreatePipelineDescriptor往下追踪:
\vendor\qcom\proprietary\camx\src\core\chi\camxchicontext.cpp
主要关注这个函数中,对pPipelineDescriptor这个对象做了什么,毕竟是要返回上去的,上图只能看到一些常规的赋值,接着往下看:
到这里,第一个框,大概知道根据pPipelineDescriptor中的信息,对pipelineCreateInputData和pipelineCreateOutputData做了一些设置。
而第二个框就有意思了,这两个参数唤起了沉睡的记忆,刚在initialize的时候遇到了两个pipeline类的问题,这里传入Pipeline::Create的是camxpipeline中的函数接收的input/output参数,也就可以回答上面提到的camxpipeline类是用来做什么的了。
解决了之前留下的问题,于是这里的Create调用到:
\vendor\qcom\proprietary\camx\src\core\camxpipeline.cpp
这里就相当冗长了
创建了一系列lock,后面也是很多从函数名看不出端倪的调用
追流程要抓关键,这个函数最终会返回result,那么很有理由相信result会作为关键调用的返回值被赋值,有了这个思想之后看看哪些地方修改了result的值
果然,看到了这个想看到的函数,进入看看:
\vendor\qcom\proprietary\camx\src\core\camxpipeline.cpp
可以看到,node数量和node信息之类的值都是pCreateInputData->pPipelineDescriptor带下来的,也就是说pipeline中就已经决定了使用哪些node
同样,追着result往下看:
\vendor\qcom\proprietary\camx\src\core\camxnode.cpp
这里创建了一个pFactory对象,这个对象赋值的时候同时把HwEnviroment中的HwFactory单例创建出来了。
这部分流程看上去不太重要,但是可以说明白pFactory->CreateNode调用的位置,不想看可以跳过,这里大致列一下调用流程:
\vendor\qcom\proprietary\camx\src\core\camxhwenvironment.h
m_pHwFactory是怎么创造出来的呢:
\vendor\qcom\proprietary\camx\src\core\camxhwenvironment.cpp
\vendor\qcom\proprietary\camx\src\core\camxhwenvironment.cpp
要素察觉,看着又是一系列函数指针定向
\vendor\qcom\proprietary\camx\src\hwl\titan17x\camxtitan17xhwl.cpp
\vendor\qcom\proprietary\camx\src\hwl\titan17x\camxtitan17xfactory.cpp
回到Node::Create中
\vendor\qcom\proprietary\camx\src\core\camxnode.cpp
通过上面支线解析可以知道,pFactory是HwFactory的子类Titan17xFactory的一个对象
所以CreateNode的调用位置就很好知道了:
\vendor\qcom\proprietary\camx\src\core\camxhwfactory.cpp
\vendor\qcom\proprietary\camx\src\hwl\titan17x\camxtitan17xfactory.cpp
终于看到了这些node最后被创造的地方,向上返回不同类型的Create创建返回的nodes
一直回到这里:
\vendor\qcom\proprietary\camx\src\core\camxnode.cpp
可以看到pNode还会调用一个Initialize函数,但是注意pNode创建的时候是由不同类型的Create创建返回的,所以这里不去关注这些node的初始化过程,接着向上返回
回到\vendor\qcom\proprietary\camx\src\core\camxpipeline.cpp:
看上去node创建出来之后,还对这个node的各种属性进行赋值
接着往下:
如注释所说,创建node的连接关系,输入输出port、link等等
这样一直到最后,完成CreateNodes的工作,回到
\vendor\qcom\proprietary\camx\src\core\camxpipeline.cpp
Initialize中:
接下来也没有什么值得说道的,Pipeline::Initialize结束,标记这个pipeline已经初始化后,向上返回
回到CreatePipelineDescriptor
\vendor\qcom\proprietary\camx\src\core\chi\camxchicontext.cpp
pPipelineDescriptor创建好了,继续向上返回到ChiCreatePipelineDescriptor
\vendor\qcom\proprietary\camx\src\core\chi\camxchi.cpp
\vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensionmodule.cpp
依然向上返回到CreateDescriptor中
\vendor\qcom\proprietary\chi-cdk\core\chiframework\chxpipeline.cpp
创建出来的descriptor保存到m_hPipelineHandle
再把信息保存到m_pipelineInfo.hPipelineDescriptor,继续向上返回到CreatePipeline中
\vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp
CreatePipeline也就这样结束,看看Initialize中还做了什么
\vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp
除了pipeline,看样子还创造了session,omfg以为已经结束了
先不管这个session的创建过程,有机会再细看吧
这样看完了CameraUsecaseBase::Initialize的过程,调用这个Initialize的上层是AdvancedCameraUsecase::Initialize
\vendor\qcom\proprietary\chi-cdk\core\chiusecase\chxadvancedcamerausecase.cpp
把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
继续一路返回到ExtensionModule::InitializeOverrideSession中:
\vendor\qcom\proprietary\chi-cdk\core\chiframework\chxextensionmodule.cpp
至此,一个logicalCamera对应的usecase就创建出来,可以看到创建好之后对这个对象的其他成员也都纷纷赋值或者新建,然后就继续向上返回
\vendor\qcom\proprietary\camx\src\core\hal\camxhaldevice.cpp
成功创建则向上返回true赋值状态
向上:
\vendor\qcom\proprietary\camx\src\core\hal\camxhal3.cpp
confige完毕之后,把stream的信息打印了一滩,之后也是向上返回
之后就会一路回到cameraservice了
ConfigureStream整体流程参考:
1 对 “ANDROID CAMERA 学习-configureStreams 在 QCOM CAMX架构中的调用”的想法;