Camera ConfigureStreams 调用流程

dff4db1bc5943267f4db450e9bd91a36.gif

和你一起终身学习,这里是程序员Android

经典好文推荐,通过阅读本文,您将收获以下知识点:

从CameraDeviceSession开始

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

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

11e9244a2a67a31b7f65851931778205.jpeg

65d5dfea5d9cb1c889f8ba5ee7d628be.jpeg

从这里,

即将进入CamX-CHI

对一下结构体名字,没错

3fbafb7732b39b67be8bbac365f626d1.jpeg

1e62eedb8840411e777fefce0d23ce24.jpeg

又要到camxhal3entry中去找了

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

b4cab32d3bfd9b7f6baa64986ac1e059.jpeg

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

796c11091de3d812cf737e3f52de7960.jpeg

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

71aaf038cd66a5d6002f59cbebb8e110.jpeg

c7313ab2dff1849e62ad737f4be7d4fa.jpeg

da21623a5e449a0f693f7a4cbf5da3b8.jpeg

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

9af05854a4c50006c41a574c38af8270.jpeg

90677cd997a9aa6e7cdf8066f46bd9c3.jpeg

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

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

b4d50598564f9d9fdd6e49428f0394ca.jpeg

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

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

1cc49eaa267c5907e81568e97b9867a9.jpeg

这个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

a0c04f40a79e5a0d86091ae310c93280.jpeg

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

b5dedb21af45ecb3587da5d1d0bb2b9b.jpeg

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

91fe84b5401a136947bd0dae5285518c.jpeg

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

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

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

ac4df07155163a0ccf1be8ab13274c7c.jpeg

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

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

1a3f409db50e6bd50407e0174a6cca0c.jpeg

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

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

d99130821c491dc759da314f0f32ea4e.jpeg

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

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

fcf76365195c7fcd87dbefb1fbc1235d.jpeg

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

50f920dfcceae75cc465da085f1cf182.jpeg

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

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

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

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

20dc044793ed57a950d4310faf53f39d.jpeg

693e281dc3081ba9b1a1c5d081b696c5.jpeg

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

2c4df0014a9add697911973895206305.jpeg

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

245773a6394b510b884d376d2e7734ad.jpeg

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

回到Initialize:

f07154d3010a69ce9b5e6378e1fb7f19.jpeg

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

e932afa37b4a8624585768a1773e297a.jpeg

aa07862967cdcf706f295f0c221fc65c.jpeg

55801c45303cd0774f234a162898860b.jpeg

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

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

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

8de5f7fe5b4f32ca678950fd8f3ca5c6.jpeg

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

c105407c9665da93ac8ba8158e9ee58a.jpeg

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

6e71badb2ec6adb7c5d75fef6d89d8b8.jpeg

c7b113357c25649232dffe5c6cc358a8.jpeg

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

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

aa6e3e6de1a2f4c82d3f73e25a257b95.jpeg

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

395474381f123854969937d7b507e14c.jpeg

image.png

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

05b3e349cc636ad6257078656f59d0b3.jpeg

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

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

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

2b36d569e77a8d7b82156a33f6bbaaa8.jpeg

b8659a4ec06b167e9ed4b90bb1ec1591.jpeg

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

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

e9bfaeb4b02c9ac347b0af754d9e29b8.jpeg

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

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

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

77edb29e8165d478861ffeabc06b4078.jpeg

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

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

ff525cee2dfb6c1eaa66a9c785675c05.jpeg

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

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

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

4fced3760cffce488c0055d5f5bf07a0.jpeg

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

接着往下看:

193254b29be0f406568cea0de8e30ae4.jpeg

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

6133e41c1cd5b5a333ec2366bd53bd2b.jpeg

69f9edf12695c46fb8fcb38a27cc90c1.jpeg

继续看Pipeline::Create

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

114e97181ff4bd3635e738d491eccb28.jpeg

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

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

e4b1e86bdea7c7b23dde0f0c80a88401.jpeg

接着先看chxpipeline中的Initialize

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

88826ae328c5826030b51c8ab4b4b2f7.jpeg

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

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

bcc8f2fdd5535dd7385a2dfba01af14e.jpeg

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

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

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

CreatePipeline中接着往下

e535c5a9a8c05cf6e0fe0bb4bdab2a61.jpeg

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

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

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

2691fbd9ebc76fecf610474e038a69d5.jpeg

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

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

cf5394f8670bd0f29a934a0befd35032.jpeg

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

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

继续跟着CreatePipelineDescriptor往下追踪:

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

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

30d4ba8dc93aacc66edaf47123ff220a.jpeg

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

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

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

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

e82b612f932d88632dc45a6d3e9a2b9b.jpeg

这里就相当冗长了

9f30de8a82d323bd74d580681bc2637f.jpeg

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

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

752755090a671b70250b09e55fb6fe08.jpeg

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

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

b608c8a144518de0b1eb7274bbfaa627.jpeg

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

同样,追着result往下看:

1f5c8778dab435b788c41a91d6eba8b8.jpeg

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

e11e8075f58d2aa872dea67769b4829c.jpeg

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

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

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

11fab728940068d3e1316c1381e48291.jpeg

m_pHwFactory是怎么创造出来的呢:

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

b97175053dd7291e6a086ac22e04ce13.jpeg

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

c4f4b973c0d535383160d6116027c3a8.jpeg

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

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

a160cd7e1626fec033531da4cae2767b.jpeg

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

d27c0e5dc418c3330ed65a0948f81378.jpeg

回到Node::Create中

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

02f0a8432a0153cf8931a66288f5c1e7.jpeg

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

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

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

77392e63203b25d486f0cac9163fa783.jpeg

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

一直回到这里:

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

7037e8bfb86414201ddfa1ee5c7aeb6e.jpeg

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

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

89843d8e2db36d261ce938577433c0b0.jpeg

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

接着往下:

a8ed4519c2fc9bdcea17aed15b7c514d.jpeg

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

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

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

Initialize中:

45b7051b61860545b6a9e325da2a2bb0.jpeg

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

回到CreatePipelineDescriptor

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

e449774d77408450b0d2ebe0952757f7.jpeg

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

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

73271ad8cab86dfc141dafc3fb00f47b.jpeg

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

bb9706ecce5ab14ced829c3560e0bcc0.jpeg

依然向上返回到CreateDescriptor中

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

b98bc004227f8b7b0c6529022ba07f68.jpeg

创建出来的descriptor保存到m_hPipelineHandle

69259da757a06efe54943c641989b38e.jpeg

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

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

135fe0f9772b897f6bf7670dbd5378c2.jpeg

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

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

159aac642a74ef1e7f87afea18373895.jpeg

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

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

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

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

204eba69e2dc7aba96c1789eae47fd80.jpeg

把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

acb60e24fc3493e907d2bc5a3ebcff08.jpeg

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

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

2090e2f4fff10624f8bf956423cd2b3e.jpeg

122d4172ecde3f0b3873d8f52071a880.jpeg

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

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

b730cc2c80691de6978c1e1f0e65873e.jpeg

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

e76e27d62a1215194022a64b17f12030.jpeg

向上:

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

4818398ddb9c52cf9b2dd2c4cb9a62a7.jpeg

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

a6231825fd4af0e1b3c84929880bbca0.jpeg

之后就会一路回到cameraservice了

ConfigureStream整体流程参考:

df9f69387b52be2981692602e17f3f9a.jpeg

来源: 影像技术栈
文章作者: Abalone
文章链接: https://camerastacker.com/2022/092522604.html

参考文献:

【腾讯文档】Camera学习知识库
https://docs.qq.com/doc/DSWZ6dUlNemtUWndv

至此,本篇已结束。转载网络的文章,小编觉得很优秀,欢迎点击阅读原文,支持原创作者,如有侵权,恳请联系小编删除,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

9a22d9f66cfa12ca506b6ed8d6246558.jpeg

点击阅读原文,为大佬点赞!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员Android

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

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

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

打赏作者

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

抵扣说明:

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

余额充值