一、chi和camx之间如何通信:
-
Chi对Camx的操作,需要通过
ExtensionModule
进行操作,因此,CamX对外提供的接口扩展需要通过ExtensionModule进行,里面一个重要的变量就是g_chiContextOps
。 -
Camx对Chi的操作,是通过
HAL3Module
接口的m_ChiAppCallbacks
进行的,因此chi里面释放的接口,都会在m_ChiAppCallbacks
里面体现。 -
具体可以参考链接:
-
高通CamX关键流程
(一定要先看此链接再看后续的)
二、 为什么存在camx架构
为了更精细化控制底层硬件(Sensor/ISP等关键硬件),同时方便手机厂商自定义一些功能,现在提出了CamX-CHI架构. 它将一些高度统一的功能性接口抽离出来放到CamX中,将可定制化的部分放在CHI中供不同厂商进行修改,实现各自独有的特色功能.
这样设计的好处显而易见,那便是即便开发者对于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的具体实现流程:
3.1 open
该方法是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中。
3.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中。
针对上一段,结合图来解释:
CHI有数据后调用本地ProcessCaptureResult
然后在该方法中获取到HALDevice的成员变量m_pCamera3CbOps,进而调用m_pCamera3CbOps中的process_capture_result方法,即camxhal3entry.cpp中定义的process_capture_result方法
VOID HALDevice::ProcessCaptureResult(const camera3_device_t* pCamera3Device,const camera3_capture_result_t* pCamera3_CaptureResult)
{const Camera3CaptureResult* pCamera3CaptureResult = reinterpret_cast<const Camera3CaptureResult*>(pCamera3_CaptureResult);HALDevice* pHALDevice = static_cast<HALDevice*>(pCamera3Device->priv);CamxResult result = CamxResultSuccess;const StaticSettings* pSettings = HwEnvironment::GetInstance()->GetStaticSettings();BOOL updateFramework = TRUE;// Keep track of information related to request for error conditionsFrameworkRequestData* pFrameworkRequest = pHALDevice->GetFrameworkRequestData(pCamera3CaptureResult->frameworkFrameNum, FALSE);// ...
// ...pHALDevice->GetCallbackOps()->process_capture_result(pHALDevice->GetCallbackOps(), pCamera3_CaptureResult);
下图为camxhal3entry.cpp中的 m_pCamera3CbOps中的process_capture_result方法绑定camxhal3entry.cpp里面的process_capture_result
然后这个方法中会去调用JumpTableHAL3.process_capture_result方法
综上:当hal中有数据产生时,调用process_capture_request最终会调用到g_jumpTableHAL3里面的process_capture_request函数。
四、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
4.1 选择UsecaseId
不同的UsecaseId分别对应的不同的应用场景,该阶段是通过调用UsecaseSelector::GetMatchingUsecase()方法来实现的,该函数中通过传入的operation_mode、num_streams配置数据流数量以及当前使用的Sensor个数来选择相应的UsecaseId,比如当numPhysicalCameras值大于1同时配置的数据流数量num_streams大于1时选择的就是UsecaseId::MultiCamera,表示当前采用的是双摄场景。
4.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方法,进行一些常规初始化工作
接下来我们就这几个阶段逐一进行分析:
4.2.1 获取XML文件中Usecase配置信息
这一部分主要通过调用CameraUsecaseBase::GetXMLUsecaseByName方法进行实现。
该方法的主要操作是从PerNumTargetUsecases数组中找到匹配到给定的usecaseName的Usecase,并作为返回值返回给调用者,其中这里我们以”UsecaseZSL“为例进行分析,PerNumTargetUsecases的定义是在g_pipeline.h中,该文件是在编译过程中通过usecaseconverter.pl脚本将定义在个平台目录下的common_usecase.xml中的内容转换生成g_pipeline.h。
4.2.2 创建Feature
如果当前场景选取了Feature,则调用FeatureSetup来完成创建工作。
该方法主要是通过诸如operation_mode、camera数量以及UsecaseId等信息来决定需要选择哪些Feature,具体逻辑比较清晰,一旦决定需要使用哪一个Feature之后,便调用相应的Feature的Create()方法进行初始化操作。
4.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进行关联。
4.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中