本文介绍CamX关键流程.
文章目录
- 1 Camera Provider 初始化
- 2 Open Camera
- 3 Configure Streams
- 4 ProcessCaptureRequest
- 5 ProcessCaptureResult
一、Camera Provider 初始化
当系统启动的时候,Camera Provider主程序会被运行,在整个程序初始化的过程中会通过获取到的camera_module_t调用其get_number_of_camera接口获取底层支持的camera数量,由于是第一次获取,所以在CamX-CHI中会伴随着很多初始化动作,具体操作见下图:
主要流程如下:
通过
HAL3Module::GetInstance()
静态方法实例化了HAL3Module对象,在其构造方法里面通过HwEnvironment::GetInstance()
静态方法又实例化了HwEnvironment对象,在其构造方法中,实例化了SettingsManager对象,然后又在它构造方法中通过OverrideSettingsFile对象获取了位于/vendor/etc/camera/camoverridesettings.txt
文件中的平台相关的配置信息(通过这种Override机制方便平台厂商加入自定义配置),该配置文件中,可以加入平台特定的配置项,比如可以通过设置multiCameraEnable的值来表示当前平台是否支持多摄,或者通过设置overrideLogLevels设置项来配置CamX-CHI部分的Log输出等级等等。同时在HwEnvironment构造方法中会调用其Initialize方法,在该方法中实例化了CSLModeManager对象,并通过CSLModeManager提供的接口,获取了所有底层支持的硬件设备信息,其中包括了Camera Request Manager、CAPS模块(该驱动模块主要用于CSL获取Camera平台驱动信息,以及IPE/BPS模块的电源控制)以及Sensor/IPE/Flash等硬件模块,并且通过调用CSLHwInternalProbeSensorHW方法获取了当前设备安装的Sensor模组信息,并且将获取的信息暂存起来,等待后续阶段使用,总得来说在HwEnvironment初始化的过程中,通过探测方法获取了所有底层的硬件驱动模块,并将其信息存储下来供后续阶段使用。
之后通过调用HwEnvironment对象中的ProbeChiCompoents方法在
/vendor/lib64/camera/components
路径下找寻各个Node生成的so库,并获取Node提供的标准对外接口,这些Node不但包括CHI部分用户自定义的模块,还包括了CamX部分实现的硬件模块,并最后都将其都存入ExternalComponentInfo对象中,等待后续阶段使用。
另外在初始化阶段还有一个比较重要的操作就是CamX 与CHI是通过互相dlopen对方的So库,获取了对方的入口方法,最后通过彼此的入口方法获取了对方操作方法集合,之后再通过这些操作方法与对方进行通讯,其主要流程见下图:
从上图不难看出,在HAL3Module构造方法中会去通过dlopen方法加载com.qti.chi.override.so
库,并通过dlsym映射出CHI部分的入口方法chi_hal_override_entry
,并调用该方法将HAL3Module对像中的成员变量m_ChiAppCallbacks(CHIAppCallbacks)传入CHI中,其中包含了很多函数指针,这些函数指针分别对应着CHI部分的操作方法集中的方法,一旦进入到CHI中,就会将CHI本地的操作方法集合中的函数地址依次赋值给m_ChiAppCallbacks,这样CamX后续就可以通过这个成员变量调用到CHI中方法,从而保持了与CHI的通讯。
同样地,CHI中的ExtensionModule在初始化的时候,其构造方法中也会通过调用dlopen方法加载camera.qcom.so
库,并将其入口方法ChiEntry
通过dlsym映射出来,之后调用该方法,将g_chiContextOps(ChiContextOps,该结构体中定义了很多指针函数)作为参数传入CamX中,一旦进入CamX中,便会将本地的操作方法地址依次赋值给g_chiContextOps中的每一个函数指针,这样CHI之后就可以通过g_chiContextOps访问到CamX方法。
二、Open Camera
一旦用户打开了相机应用,App中便会去调用CameraManager的openCamera方法,该方法之后会最终调用到Camera Service中的CameraService::connectDevice方法,然后通过ICameraDevice::open()这一个HIDL接口通知Provider,然后在Provider内部又通过调用之前获取的camera_module_t中methods的open方法来获取一个Camera 设备.
对应于HAL中的camera3_device_t结构体,紧接着,在Provider中会继续调用获取到的camera3_device_t的initialize方法进行初始化动作。接下来我们便来详细分析下CamX-CHI对于open以及initialize的具体实现流程:
2.1open
该方法是camera_module_t的标准方法,主要用来获取camera3_device_t设备结构体的,CamX-CHI对其进行了实现,open方法中完成的工作主要有以下几个:
- 将当前camera id传入CHI中进行remap操作,当然这个remap操作逻辑完全是根据CHI中用户需求来的,用户可以根据自己的需要在CHI中加入自定义remap逻辑。
- 实例化HALDevice对象,其构造函数中调用Initialize方法,该方法会填充CamX中自定义的Camera3Device结构体。
- 将m_HALCallbacks.process_capture_result指向了本地方法ProcessCaptureResult以及m_HALCallbacks.notify_result指向了本地方法Notify(之后会在配置数据流的过程中,将m_HALCallbacks注册到CHI中, 一旦当CHI数据处理完成之后,便会通过这两个回调方法将数据或者事件回传给CamX)。
- 最后将HALDevice 中的Camera3Device成员变量作为返回值给到Provider中的CameraCaptureSession中。
Camera3Device 其实重定义了camera3_device_t,其中HwDevice对应于camera3_device_t中的hw_device_t,Camera3DeviceOps对应于camera3_device_ops_t,而在HALDevice的初始化过程中,会将CamX实现的HAL3接口的结构体g_camera3DeviceOps赋值给Camera3DeviceOps中。
2.2 initialize
该方法在调用open后紧接着被调用,主要用于将上层的回调接口传入HAL中,一旦有数据或者事件产生,CamX便会通过这些回调接口将数据或者事件上传至调用者,其内部的实现较为简单。
initialize方法中有两个参数,分别是之前通过open方法获取的camera3_device_t结构体和实现了camera3_callback_ops_t的CameraDevice,很显然camera3_device_t结构体并不是重点,所以该方法的主要工作是将camera3_callback_ops_t与CamX关联上,一旦数据准备完成便通过这里camera3_callback_ops_t中回调方法将数据回传到Camera Provider中的CameraDevice中,基本流程可以总结为以下几点:
- 实例化了一个Camera3CbOpsRedirect对象并将其加入了
g_HAL3Entry.m_cbOpsList
队列中,这样方便之后需要的时候能够顺利拿到该对象。 - 将本地的process_capture_result以及notify方法地址分别赋值给Camera3CbOpsRedirect.cbOps中的process_capture_result以及notify函数指针。
- 将上层传入的回调方法结构体指针pCamera3CbOpsAPI赋值给Camera3CbOpsRedirect.pCbOpsAPI,并将Camera3CbOpsRedirect.cbOps赋值给pCamera3CbOpsAPI,通过JumpTableHal3的initialize方法将pCamera3CbOpsAPI传给HALDevice中的m_pCamera3CbOps成员变量,这样HALDevice中的m_pCamera3CbOps就指向了CamX中本地方法process_capture_result以及notify。
经过这样的一番操作之后,一旦CHI有数据传入便会首先进入到本地方法ProcessCaptureResult,然后在该方法中获取到HALDevice的成员变量m_pCamera3CbOps,进而调用m_pCamera3CbOps中的process_capture_result方法,即camxhal3entry.cpp中定义的process_capture_result方法,然后这个方法中会去调用JumpTableHAL3.process_capture_result方法,该方法最终会去调用Camera3CbOpsRedirect.pCbOpsAPI中的process_capture_result方法,这样就调到从Provider传入的回调方法,将数据顺利给到了CameraCaptureSession中。
三、Configure Streams
在打开相机应用过程中,App在获取并打开相机设备之后,会调用CameraDevice.createCaptureSession来获取CameraDeviceSession,并且通过Camera api v2标准接口,通知Camera Service,调用其CameraDeviceClient.endConfigure方法,在该方法内部又会去通过HIDL接口ICameraDeviceSession::configureStreams_3_4通知Provider开始处理此次配置需求,在Provider内部,会去通过在调用open流程中获取的camera3_device_t结构体的configure_streams方法来将数据流的配置传入CamX-CHI中,之后由CamX-CHI完成对数据流的配置工作.
接下来我们来详细分析下CamX-CHI对于该标准HAL3接口 configure_streams的具体实现,配置数据流是整个CamX-CHI流程比较重要的一环,其中主要包括两个阶段:
- 选择UsecaseId
- 根据选择的UsecaseId创建Usecase
3.1 选择UsecaseId
不同的UsecaseId分别对应的不同的应用场景,该阶段是通过调用UsecaseSelector::GetMatchingUsecase()方法来实现的,该函数中通过传入的operation_mode、num_streams配置数据流数量以及当前使用的Sensor个数来选择相应的UsecaseId,比如当numPhysicalCameras值大于1同时配置的数据流数量num_streams大于1时选择的就是UsecaseId::MultiCamera,表示当前采用的是双摄场景。
3.2 创建Usecase
根据之前选择的UsecaseId,通过UsecaseFactory来创建相应的Usecase,
其中Class Usecase是所有Usecase的基类,其中定义并实现了一些通用接口,CameraUsecaseBase继承于Usecase,并扩展了部分功能。AdvancedCameraUsecase又继承于CameraUsecaseBase,作为主要负责大部分场景的Usecase实现类,另外对于多摄场景,现提供了继承于AdvancedCameraUsecase的UsecaseMultiCamera来负责实现。
除了双摄场景,其它大部分场景使用的都是AdvancedCameraUsecase类来管理各项资源的,接下来我们重点梳理下AdvancedCameraUsecase::Create()方法。
在AdvancedCameraUsecase::Create方法中做了很多初始化操作,其中包括了以下几个阶段:
- 获取XML文件中Usecase配置信息
- 创建Feature
- 保存数据流,重建Usecase的配置信息
- 调用父类CameraUsecaseBase的initialize方法,进行一些常规初始化工作
接下来我们就这几个阶段逐一进行分析:
3.2.1 获取XML文件中Usecase配置信息
这一部分主要通过调用CameraUsecaseBase::GetXMLUsecaseByName方法进行实现。
该方法的主要操作是从PerNumTargetUsecases数组中找到匹配到给定的usecaseName的Usecase,并作为返回值返回给调用者,其中这里我们以”UsecaseZSL“为例进行分析,PerNumTargetUsecases的定义是在g_pipeline.h中,该文件是在编译过程中通过usecaseconverter.pl脚本将定义在个平台目录下的common_usecase.xml中的内容转换生成g_pipeline.h。
3.2.2 创建Feature
如果当前场景选取了Feature,则调用FeatureSetup来完成创建工作。
该方法主要是通过诸如operation_mode、camera数量以及UsecaseId等信息来决定需要选择哪些Feature,具体逻辑比较清晰,一旦决定需要使用哪一个Feature之后,便调用相应的Feature的Create()方法进行初始化操作。
3.2.3 保存数据流,重建Usecase的配置信息
从Camera Service 传入的数据流,需要将其存储下来,供后续使用,同时高通针对Usecase也加入了Override机制,根据需要可以选择性地扩展Usecase,这两个步骤的实现主要是通过SelectUsecaseConfig方法来实现。
其中主要是调用以下两个方法来实现的:
- ConfigureStream: 该方法将从上层配置的数据流指针存入AdvancedCameraUsecase中,其中包括了用于预览的m_pPreviewStream以及用于拍照的m_pSnapshotStream。
- BuildUsecase: 这个方法用来重新在原有的Usecase上面加入了Feature中所需要的pipeline,并创建了一个新的Usecase,并将其存入AdvancedCameraUsecase中的m_pChiUsecase成员变量中,紧接着通过SetPipelineToSessionMapping方法将pipeline与Session进行关联。
3.2.4 调用父类CameraUsecaseBase的initialize方法
调用父类CameraUsecaseBase的initialize方法,进行一些常规初始化工作. 该方法中的操作主要有以下三个:
- 设置Session回调
- 创建Pipeline
- 创建Session
设置Session回调
该方法有两个参数,第二个是缺省的,第一个是ChiCallBacks,该参数是作为创建的每一条Session的回调方法,当Session中的pipeline全部跑完之后,会回调该方法将数据投递到CHI中。
创建Pipeline
根据之前获取的pipeline信息开始创建每一条pipeline,通过调用CreatePipeline()方法实现。
创建Session
创建Session,通过CreateSession()方法实现,此时会将AdvancedCameraUsecase端的回调函数注册到Session中,一旦Session中数据处理完成,便会调用回调将数据回传给AdvancedCameraUsecase。
综上,整个configure_stream过程,基本可以概括为以下几点:
- 根据operation_mode、camera 个数以及stream的配置信息选取了对应的UsecaseId
- 根据所选取的UsecaseId,使用UsecaseFactory简单工厂类创建了用于管理整个场景下所有资源的AdvancedCameraUsecase对象。
- 创建AdvancedCameraUsecase对象是通过调用其Create()方法完成,该方法中获取了common_usecase.xml定义的关于Usecase的配置信息,之后又根据需要创建了Feature并选取了Feature所需的pipeline,并通过Override机制将Feature中所需要的Pipeline加入重建后的Usecase中。
- 最后通过调用CameraUsecaseBaese的initialize方法依次创建了各个pipeline以及Session,并且将AdvancedCameraUsecase的成员方法注册到Session,用于Session将数据返回给Usecase中
四、ProcessCaptureRequest
当用户打开相机应用进行预览或者点击一次拍照操作的时候,便触发了一次拍照请求,该动作首先通过CameraDeviceSession的capture或者setRepeatingRequest方法将请求通过Camera api v2接口下发到Camera Service中,然后在Camera Service内部将此次请求发送到CameraDevice::RequestThread线程中进行处理,一旦进入到该线程之后,便会最终通过HIDL接口ICameraCaptureSession:processCaptureRequest_3_4将请求发送至Provider中,之后当Provider收到请求之后,会调用camera3_device_t结构体的process_capture_request开始了HAL针对此次Request的处理,而该处理是由CamX-CHI来负责实现,现在我们就来看下CamX-CHI是如何实现该方法的:
首先CamX中会将此次request转发到HALDevice中,再通过HALDevice对象调用之前初始化的时候获取的CHI部分的回调接口m_ChiAppCallbacks.chi_override_process_request方法(chi_override_process_request方法的定义位于chxextensioninterface.cpp中)将request发送到CHI部分。
在chi_override_process_request方法中会去获取ExtensionModule对象,并将request发送到ExtensionModule对象中,该对象中存储了之前创建的Usecase对象,然后经过层层调用,最终会调用AdvancedCameraUsecase的ExecuteCaptureRequest方法,该方法负责处理此次Request,具体流程如下:
在AdvancedCameraUsecase的ExecuteCaptureRequest中会有两个主要的分支来分别处理:
- 如果当前并没有任何Feature需要实现,此时便会走默认流程,根据上面的流程图所示,这里会调用CameraUsecaseBase::ExecuteCaptureRequest方法,在该方法中,首先会将request取出,重新封装成CHICAPTUREREQUEST,然后调用CheckAndActivatePipeline方法唤醒pipeline,这一操作到最后会调到Session的StreamOn方法,在唤醒了pipeline之后,继续往下执行,再将封装后的Request发送到CamX中,最终调用到相应的Session::ProcessCaptureRequest方法,此时Request就进入到了Session内部进行流转了。
- 如果当前场景需要实现某个Feature,则直接调用Feature的ExecuteProcessRequest方法将此次request送入Feature中处理,最后依然会调用到Session::StreamOn以及Session::ProcessCaptureRequest方法来分别完成唤醒pipeline以及下发request的到Session的操作。
该流程最终都会调用到两个比较关键的方法Session::StreamOn以及Session::ProcessCaptureRequest,接下来针对这两个方法重点介绍下:
4.1 Session::StreamOn
从方法名称基本可以知道该方法主要用于开始硬件的数据输出,具体点儿就是进行配置Sensor寄存器,让其开始出图,并且将当前的Session的状态告知每一Node,让它们在自己内部也做好处理数据的准备,所以之后的相关Request的流转都是以该方法为前提进行的,所以该方法重要性可见一斑,其操作流程见下图:
Session的StreamOn方法中主要做了如下两个工作:
- 调用FinalizeDeferPipeline()方法,如果当前pipeline并未初始化,则会调用pipeline的FinalizePipeline方法,这里方法里面会去针对每一个从属于当前pipeline的Node依次做FinalizeInitialization、CreateBufferManager、NotifyPipelineCreated以及PrepareNodeStreamOn操作,FinalizeInitialization用于完成Node的初始化动作,NotifyPipelineCreated用于通知Node当前Pipeline的状态,此时Node内部可以根据自身的需要作相应的操作,PrepareNodeStreamOn方法的主要是完成Sensor以及IFE等Node的控制硬件模块出图前的配置,其中包括了曝光的参数的设置,CreateBufferManagers方法涉及到CamX-CHI中的一个非常重要的Buffer管理机制,用于Node的ImageBufferManager的创建,而该类用于管理Node中的output port的buffer申请/流转/释放等操作。
- 调用Pipeline的StreamOn方法,里面会进一步通知CSL部分开启数据流,并且调用每一个Node的OnNodeStreamOn方法,该方法会去调用ImageBufferManager的Activate(),该方法里面会去真正分配用于装载图像数据的buffer,之后会去调用CHI部分实现的用户自定义的Nod的pOnStreamOn方法,用户可以在该方法中做一些自定义的操作。
4.2 Session::ProcessCaptureRequest
针对每一次的Request的流转,都是以该方法为入口开始的,具体流程见下图:
上述流程可以总结为以下几个步骤:
- 通过调用Session的ProcessCaptureRequest方法进入到Session,然后调用Pipeline中的ProcessRequest方法通知Pipeline开始处理此次Request。
- 在Pipeline中,会先去调用内部的每一个Node的SetupRequest方法分别设置该Node的Output Port以及Input Port,之后通过调用DRQ(DeferredRequestQueue)的AddDeferredNode方法将所有的Node加入到DRQ中,其中DRQ中有两个队列分别是用于保存没有依赖项的Node的m_readyNodes以及保存处于等待依赖关系满足的Node的m_deferredNodes,当调用DRQ的DispatchReadyNodes方法后,会开始从m_readyNodes队列中取出Node调用其ProcessRequest开始进入Node内部处理本次request,在处理过程中会更新meta data数据,并更新至DRQ中,当该Node处理完成之后,会将处于m_deferredNodes中的已无依赖关系的Node移到m_readyNodes中,并再次调用DispatchReadyNodes方法从m_readyNodes取出Node进行处理。
- 与此过程中,当Node的数据处理完成之后会通过CSLFenceCallback通知到Pipeline,此时Pipeline会判断当前Node的Output port 是否是Sink Port(输出到CHI),如果不是,则会更新依赖项到DRQ中,并且将不存在依赖项的Node移到m_readyNodes队列中,然后调用DispatchReadyNdoes继续进入到DRQ中流转,如果是Sink Port,则表示此Node是整个Pipeline的最末端,调用sinkPortFenceSignaled将数据给到Session中,最后通过调用Session中的NotifyResult将结果发送到CHI中。
4.3 DeferredRequestQueue
上述流程里面中涉及到DeferredRequestQueue这个概念,这里简单介绍下:
DeferredRequestQueue继承于IPropertyPoolObserver,实现了OnPropertyUpdate/OnMetadataUpdate/OnPropertyFailure/OnMetadataFailure接口,这几个接口用于接收Meta Data以及Property的更新,另外,DRQ主要包含了以下几个主要方法:
- Create()
该方法用于创建DRQ,其中创建了用于存储依赖信息的m_pDependencyMap,并将自己注册到MetadataPool中,一旦有meta data或者property更新便会通过类中实现的几个接口通知到DRQ。 - DispatchReadyNodes()
该方法主要用于将处于m_readyNodes队列的Node取出,将其投递到m_hDeferredWorker线程中进行处理。 - AddDeferredNode()
该方法主要用于添加依赖项到m_pDependencyMap中。 - FenceSignaledCallback()
当Node内部针对某次request处理完成之后,会通过一系列回调通知到DRQ,而其调用的方法便是该方法,在该方法中,会首先调用UpdateDependency更新依赖项,然后调用DispatchReadyNodes触发开始对处于ready状态的Node开始进行处理 - OnPropertyUpdate()
该方法是定义于IPropertyPoolObserver接口,DRQ实现了它,主要用于接收Property更新的通知,并在内部调用UpdateDependency更新依赖项。 - OnMetadataUpdate()
该方法是定义于IPropertyPoolObserver接口,DRQ实现了它,主要用于接收Meta data更新的通知,并在内部调用UpdateDependency更新依赖项。 - UpdateDependency()
该方法用于更新Node的依赖项信息,并且将没有依赖的Node从m_deferredNodes队列中移到m_readyNodes,这样该Node就可以在之后的某次DispatchReadyNodes调用之后投入运行。 - DeferredWorkerWrapper()
该方法是m_hDeferredWorker线程的处理函数,主要用于处理需要下发request的Node,同时再次更新依赖项,最后会再次调用DispatchReadyNodes开始处理。
其中需要注意的是,Pipeline首次针对每一个Node通过调用AddDeferredNode方法加入到DRQ中,此时所有的Node都会加入到m_readyNodes中,然后通过调用dispatchReadyNodes方法,触发DRQ开始进行整个内部处理流程,基本流程可以参见下图,接下来就以该图进行深入梳理下:
- 当调用了DRQ的dispatchReadyNodes方法后,会从m_readyNodes链表里面依次取出Dependency,将其投递到DeferredWorkerWrapper线程中,在该线程会从Dependency取出Node调用其ProcessRequest方法开始在Node内部处理本次request,处理完成之后如果当前Node依然存在依赖项,则调用AddDeferredNode方法将Node再次加入到m_deferredNodes链表中,并且加入新的依赖项,存入m_pDependencyMap hash表中。
- 在Node处理request的过程中,会持续更新meta data以及property,此时会通过调用MetadataSlot的PublishMetadata方法更新到MetadataPool中,此时MetadataPool会调用之前在DRQ初始化时候注册的几个回调方法OnPropertyUpdate以及OnMetadataUpdate方法通知DRQ,此时有新的meta data 和property更新,接下来会在这两个方法中调用UpdateDependency方法,去更新meta data 和property到m_pDependencyMap中,并且将没有任何依赖项的Node从m_deferredNodes取出加入到m_readyNodes,等待处理。
- 与此同时,Node的处理结果也会通过ProcessFenceCallback方法通知pipeline,并且调用pipeline的NonSinkPortFenceSignaled方法,在该方法内部又会去调用DRQ的FenceSignaledCallback方法,而该方法又会调用UpdateDependency更新依赖,并将依赖项都满足的Node从m_deferredNodes取出加入到m_readyNodes,然后调用dispatchReadyNodes继续进行处理。
五、ProcessCaptureResult
在用户开启了相机应用,相机框架收到某次Request请求之后会开始对其进行处理,一旦有图像数据产生便会通过层层回调最终返回到应用层进行显示,这里我们针对CamX-CHI部分对于拍照结果的上传流程进行一个简单的梳理:
每一个Request对应了三个Result,分别是partial metadata、metadata以及image data,对于每一个Result,上传过程可以大致分为以下两个阶段:
- Session内部完成图像数据的处理,将结果发送至Usecase中
- Usecase接收到来自Session的数据,并将其上传至Provider
首先来看下Session内部完成图像数据的处理后是如何将结果发送至Usecase的:
在整个requets流转的过程中,一旦Node中有Partial Meta Data产生,便会调用Node的ProcessPartialMetadataDone方法去通知从属的Pipeline,其内部又调用了pipeline的NotifyNodePartialMetadataDone方法。每次调用Pipeline的NotifyNodePartialMetadataDone方法都会去将pPerRequestInfo→numNodesPartialMetadataDone加一并且判断当前值是否等于pipeline中的Node数量,一旦相等,便说明当前所有的Node都完成了partial meta data的更新动作,此时,便会调用ProcessPartialMetadataRequestIdDone方法,里面会去取出partial meta data,并且重新封装成ResultsData结构体,将其作为参数通过Session的NotifyResult方法传入Session中,之后在Session中经过层层调用最终会调用到内部成员变量m_chiCallBacks的ChiProcessPartialCaptureResult方法,该方法正是创建Session的时候,传入Session中的Usecase的方法(AdvancedCameraUsecase::ProcessDriverPartialCaptureResultCb),通过该方法就将meta data返回到了CHI中。
同样地,Meta data的逻辑和Partial Meta Data很相似,每个Node在处理request的过程中,会调用ProcessMetadataDone方法将数据发送到Pipeline中,一旦所有的Node的meta data否发送完成了,pipeline会调用NotifyNodeMetadataDone方法,将最终的结果发送至Session中,最后经过层层调用,会调用Session 中成员变量m_chiCallBacks的ChiProcessCaptureResult方法,将结果发送到CHI中Usecase中。
图像数据的流转和前两个meta data的流转有点儿差异,一旦Node内部图像数据处理完成后便会调用其ProcessFenceCallback方法,在该方法中会去检查当前输出是否是SInk Buffer,如果是则会调用Pipeline的SinkPortFenceSignaled方法将数据发送到Pipeline中,在该方法中Pipeline又会将数据发送至Session中,最后经过层层调用,会调用Session 中成员变量m_chiCallBacks的ChiProcessCaptureResult方法,将结果发送到CHI中Usecase中。
接下来我们来看下一旦Usecase接收到Session的数据,是如何发送至Provider的:
我们以常用的AdvancedCameraUsecase为例进行代码的梳理:
如上图所示,整个result的流转逻辑还是比较清晰的,CamX通过回调方法将结果回传给CHI中,而在CHI中,首先判断是否需要发送到具体的Feature的, 如果需要,则调用相应Feature的ProcessDriverPartialCaptureResult或者ProcessResult方法将结果发送到具体的Feature中,一旦处理完成,便会调用调用CameraUsecaseBase的ProcessAndReturnPartialMetadataFinishedResults以及ProcessAndReturnFinishedResults方法将结果发送到Usecase中,如果当前不需要发送到Feature进行处理,就在AdvancedCameraUsecase中调用CameraUsecaseBase的SessionCbPartialCaptureResult以及SessionCbCaptureResult方法,然后通过Usecase::ReturnFrameResult方法将结果发送到ExtensionModule中,之后调用ExtensionModule中存储的CamX中的回调函数process_capture_result将结果发送到CamX中的HALDevice中,之后HALDevice又通过之前存储的上层传入的回调方法,将结果最终发送到CameraDeviceSession中。
通过以上的梳理,可以发现,整个CamX-CHI框架设计的很不错,目录结构清晰明确,框架简单高效,流程控制逻辑分明,比如针对某一图像请求,整个流程经过Usecase、Feature、Session、Pipeline并且给到具体的Node中进行处理,最终输出结果。另外,相比较之前的QCamera & Mm-Camera框架的针对某个算法的扩展需要在整个流程代码中嵌入自定义的修改做法而言,CamX-CHI通过将自定义实现的放入CHI中,提高了其扩展性,降低了开发门槛,使得平台厂商在并不是很熟悉CamX框架的情况下也可以通过小规模的修改成功添加新功能。但是人无完人,框架也是一样,该框架异步化处理太多,加大了定位问题以及解决问题的难度,给开发者带来了不小的压力。另外,框架对于内存的要求较高,所以在一些低端机型尤其是低内存机型上,整个框架的运行效率可能会受到一定的限制,进而导致相机效率低于预期。
转载来源:https://deepinout.com/qcom-camx-chi/qcom-camx-main-scenario-flow.html
*本人从事Android Camera相关开发已有5年,
*目前在深圳上班,
*小伙伴记得点我头像,看【个人介绍】进行关注哦,希望和更多的小伙伴一起交流 ~
-----2021.05.02 深圳 13:56