OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗 原创 精华
OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗留问题
新大陆自动识别 郑曦
Question1 cameraDeviceMap_
HDF 驱动框架简介
OpenHarmony系统 提供了HDF 驱动框架,框架采用 C 语言面向对象编程模型构建,通过平台解耦、内核解耦,来达到兼容不同内核,统一平台底座的目的,从而帮助开发者实现驱动一次开发,多系统部署的效果。
每个驱动程序都对应着一个 Driver Entry。Driver Entry 主要完成驱动的初始化和驱动接口绑定功能。引[^1]
回到代码
我们来看下camera相机驱动对应的Driver Entry
//drivers\peripheral\camera\interfaces\include\server\camera_host_driver.cpp
static int32_t CameraServiceDispatch(struct HdfDeviceIoClient *client, int cmdId,
struct HdfSBuf *data, struct HdfSBuf *reply)
{
HdfCameraService *hdfCameraService = CONTAINER_OF(client->device->service, HdfCameraService, ioservice);
return CameraHostServiceOnRemoteRequest(hdfCameraService->instance, cmdId, data, reply);
}
int HdfCameraHostDriverBind(HdfDeviceObject *deviceObject)
{
HDF_LOGI("HdfCameraHostDriverBind enter!");
if (deviceObject == nullptr) {
HDF_LOGE("HdfCameraHostDriverBind: HdfDeviceObject is NULL !");
return HDF_FAILURE;
}
HdfCameraService *hdfCameraService =
reinterpret_cast<HdfCameraService *>(OsalMemAlloc(sizeof(HdfCameraService)));
if (hdfCameraService == nullptr) {
HDF_LOGE("HdfCameraHostDriverBind OsalMemAlloc HdfCameraService failed!");
return HDF_FAILURE;
}
hdfCameraService->ioservice.Dispatch = CameraServiceDispatch;
hdfCameraService->ioservice.Open = nullptr;
hdfCameraService->ioservice.Release = nullptr;
hdfCameraService->instance = CameraHostStubInstance();
deviceObject->service = &hdfCameraService->ioservice;
return HDF_SUCCESS;
}
struct HdfDriverEntry g_cameraHostDriverEntry = {
.moduleVersion = 1,
.moduleName = "camera_service",
.Bind = HdfCameraHostDriverBind,
.Init = HdfCameraHostDriverInit,
.Release = HdfCameraHostDriverRelease,
};
CameraServiceDispatch()
接口提供了之前CameraHost IPC 消息的一个分发入口。
主要看下HdfCameraHostDriverBind()
里面的hdfCameraService->instance = CameraHostStubInstance();
HDF驱动框架在Bind camera HDI驱动接口的时候会调用CameraHost::CreateCameraHost()
创建CameraHost的实例对象,最终会调用CameraHostImpl的Init()函数完成初始化。
//drivers\peripheral\camera\interfaces\include\server\camera_host_service_stub.cpp
void *CameraHostStubInstance()
{
OHOS::Camera::CameraHostStub *stub =
new (std::nothrow) OHOS::Camera::CameraHostStub();
if (stub == nullptr) {
HDF_LOGE("%s: camera host stub create failed.", __func__);
return nullptr;
}
OHOS::Camera::RetCode ret = stub->Init();
if (ret != OHOS::Camera::RC_OK) {
delete stub;
stub = nullptr;
return nullptr;
}
return reinterpret_cast<void*>(stub);
}
RetCode CameraHostStub::Init()
{
cameraHost_ = CameraHost::CreateCameraHost();
if (cameraHost_ == nullptr) {
HDF_LOGE("%s: camera host service start failed", __func__);
return RC_ERROR;
}
return RC_OK;
}
来看下CameraHostImpl::Init()
先获取deviceManager
并调用初始化函数,deviceManager
是device Manager相关的设备管理实例,留在后续的CameraPowerUp()再说明
再通过cameraId 创建CameraDevice实例,CameraDevice::CreateCameraDevice(cameraId)
。把创建好的CameraDevice对象和cameraId关联保存在cameraDeviceMap_里。
至此上篇中的OpenCamera()中打开的相机对象Map基本讲解完毕。
//drivers\peripheral\camera\hal\hdi_impl\src\camera_host\camera_host_impl.cpp
CamRetCode CameraHostImpl::Init()
{
std::shared_ptr<IDeviceManager> deviceManager =
IDeviceManager::GetInstance();
if (deviceManager == nullptr) {
return INVALID_ARGUMENT;
}
RetCode ret = RC_OK;
ret = deviceManager->Init();
if (ret == RC_ERROR) {
return INVALID_ARGUMENT;
}
CameraHostConfig *config = CameraHostConfig::GetInstance();
if (config == nullptr) {
return INVALID_ARGUMENT;
}
std::vector<std::string> cameraIds;
RetCode rc = config->GetCameraIds(cameraIds);
if (rc != RC_OK) {
CAMERA_LOGE("host get camera id failed.");
return INVALID_ARGUMENT;
}
for (auto &cameraId : cameraIds) {
std::vector<std::string> phyCameraIds;
rc = config->GetPhysicCameraIds(cameraId, phyCameraIds);
if (rc != RC_OK) {
continue;
}
std::shared_ptr<CameraDevice> cameraDevice =
CameraDevice::CreateCameraDevice(cameraId);
if (cameraDevice != nullptr) {
cameraDeviceMap_.insert(std::make_pair(cameraId, cameraDevice));
} else {
CAMERA_LOGW("host implement new device failed [cameraid = %{public}s].", cameraId.c_str());
}
}
return NO_ERROR;
}
Question2 CameraPowerUp()
回头再看下第二个问题 CameraHostImpl::OpenCamera()中调用的CameraPowerUp()
先调用deviceManager的接口关闭补光灯,并通过回调接口通知上层补光状态的变化
在调用deviceManager的PoweUp给camera外设上电。
//drivers\peripheral\camera\hal\hdi_impl\src\camera_host\camera_host_impl.cpp
RetCode CameraHostImpl::CameraPowerUp(const std::string &cameraId,
const std::vector<std::string> &phyCameraIds)
{
FlashlightStatus flashlightStatus = FLASHLIGHT_UNAVAILABLE;
RetCode rc = SetFlashlight(phyCameraIds, false, flashlightStatus);
if (rc != RC_OK) {
CAMERA_LOGW("flash light close failed. [cameraId = %{public}s]", cameraId.c_str());
}
if (cameraHostCallback_ != nullptr) {
cameraHostCallback_->OnFlashlightStatus(cameraId, flashlightStatus);
}
std::shared_ptr<IDeviceManager> deviceManager = IDeviceManager::GetInstance();
if (deviceManager == nullptr) {
CAMERA_LOGW("device manager is null [dm name MpiDeviceManager].");
return RC_ERROR;
}
for (auto &phyCameraId : phyCameraIds) {
auto itr = CameraHostConfig::enumCameraIdMap_.find(phyCameraId);
if (itr == CameraHostConfig::enumCameraIdMap_.end()) {
CAMERA_LOGW("config phyCameraId undefined in device manager.");
continue;
}
rc = deviceManager->PowerUp(itr->second);
if (rc != RC_OK) {
CAMERA_LOGE("physic camera powerup failed [phyCameraId = %{public}s].", phyCameraId.c_str());
return RC_ERROR;
}
}
CAMERA_LOGD("camera powerup success.");
return RC_OK;
}
DeviceManager 是不同平台适配层对上的封装管理接口。从下图可以看出设置DeviceManager这一层的初衷是希望对上可以统一接口,对下可以支持不同的视频采集框架。比如标准linux支持的V4L2 或者 海思系列的MPP。
Hi3516采用的就是MPP的框架,这个框架我也没有仔细研究使用过,目前的开源代码里这部分代码是通过so对外提供的。个人觉得作为一个开源的初期平台,更应该选用V4L2这样通用开源的框架来做适配似乎对学习者会更友好点。不过既然目前用了MPP(包括后面node管理也要用到),这里尝试简单说明一下MPP,如果理解的有问题,还希望有大神能给些指导。
MPP简介 引[^2]
上海海思提供的媒体处理软件平台(Media Process Platform,简称 MPP),可支持应用软件快速开发。该平台对应用软件屏蔽了芯片相关的复杂的底层处理,并对应用软件直接提供 MPI(MPP Program Interface)接口完成相应功能。该平台支持应用软件快速开发以下功能:输入视频捕获、H.265/H.264/JPEG 编码、H.265/H.264/JPEG 解码、视频输出显示、视频图像前处理(包括去噪、增强、锐化)、图像拼接、图像几何矫正、智能、音频捕获及输出、音频编解码等功能。
从下面的结构图理解,MPP是海思为了屏蔽硬件芯片和不同操作系统(linux、liteos)而设计的多媒体处理平台。其目的是为了统一应用层代码,当然也可能是为了保护底层芯片的具体工作方式。
来看下MPP的具体工作流程图。本Demo中涉及到的视频流显示和capture采集的流程应该是图中红色标线部分。
视频输入(VI)、视频处理(VPSS)、视频编码(VENC)、视频解码(VDEC)、视频输出(VO)
回到代码
std::shared_ptr<IDeviceManager> deviceManager = IDeviceManager::GetInstance();
deviceManager 对象实例是通过device_manager框架层中提供的类工厂的方式动态创建,最终获得的是在平台适配层定义的MpiDeviceManager对象实例
先调用Init函数把定义好的每一个硬件通过CreateManager()创建对应的controller和manager并成vetor list。
这里调用的CreateSysObject()和VI、VO、VPSS对应的CreateXXObject()具体的实现都封装在了
//device/hisilicon/hardware/media/hal/camera/libs/hispark_taurus/libdriver_adapter.z.so
库里。
对应这个库的使用没有找到相关的文档和说明,如果有对此了解的朋友希望可以在评论下给点指导。
//drivers\peripheral\camera\hal\adapter\chipset\hispark_taurus\src\device_manager\mpi_device_manager.cpp
RetCode MpiDeviceManager::Init()
{
RetCode rc = RC_ERROR;
std::vector<HardwareConfiguration> hardware = {
{CAMERA_FIRST, DM_M_VI, DM_C_VI, (std::string) "vi"},
{CAMERA_FIRST, DM_M_VO, DM_C_VO, (std::string) "vo"},
{CAMERA_FIRST, DM_M_VI, DM_C_SENSOR, (std::string) "Imx335"},
{CAMERA_FIRST, DM_M_VPSS, DM_C_VPSS, (std::string) "vpss"},
{CAMERA_SECOND, DM_M_VI, DM_C_SENSOR, (std::string) "Imx600"},
{CAMERA_SECOND, DM_M_VO, DM_C_VO, (std::string) "vo"},
{CAMERA_SECOND, DM_M_VI, DM_C_VI, (std::string) "vi"}
};
for (auto iter = hardware.cbegin(); iter != hardware.cend(); iter++) {
hardwareList_.push_back(*iter);
}
rc = CreateManager();
if (rc != RC_OK) {
CAMERA_LOGE("CreateManager fail");
return rc;
}
sysObject_ = ISysObject::CreateSysObject();
if (sysObject_ == nullptr) {
CAMERA_LOGE("Create SysObject fail");
return RC_ERROR;
}
rc = sysObject_->InitSys();
if (rc != RC_OK) {
CAMERA_LOGE("InitSys fail");
}
return rc;
}
尝试画个类图表示下:(图有点丑,念书那会导师让我们画类图,我没好好学,愧对师长–!)
剩下的PowerUp就简单了。调用每个硬件对应的PowerUp实现 完成上电的动作。
//drivers\peripheral\camera\hal\adapter\chipset\hispark_taurus\src\device_manager\mpi_device_manager.cpp
RetCode MpiDeviceManager::PowerUp(CameraId cameraId)
{
......
RetCode rc = RC_OK;
for (auto iter = managerList_.cbegin(); iter != managerList_.cend(); iter++) {
rc = (*iter)->PowerUp(cameraId);
if (rc == RC_ERROR) {
return RC_ERROR;
}
}
return rc;
}
简单看下其中一个PowerUp代码。Vi部分只是设置了一个上电状态标识。
//drivers\peripheral\camera\hal\adapter\chipset\hispark_taurus\src\device_manager\vi_manager.cpp
RetCode ViManager::PowerUp(CameraId cameraId)
{
return vi_->PowerUp(cameraId);
}
//drivers\peripheral\camera\hal\adapter\chipset\hispark_taurus\src\device_manager\vi_controller.cpp
RetCode ViController::PowerUp(CameraId cameraId)
{
RetCode rc = RC_OK;
if (GetPowerOnState() == false) {
SetPowerOnState(true);
CAMERA_LOGI("%{public}s Vi Powerup", __FUNCTION__);
return rc;
}
return rc;
}
总结
Camera相机驱动框架的初始化过程是通过实现驱动程序 (Driver Entry)入口,交由HDF驱动框架在启动过程中完成的,并提供了一套server stub的消息应答接口。上层通过获取对应的client proxy通过IPC 实现对下层驱动功能的调用。
在后续的文章会回到demo程序继续往下看看流的创建过程。
同时欢迎OpenHarmony各位朋友加入我司可以发简历到chid@nlscan.com
参考文章
[^1]: OpenHarmony HDF 驱动框架介绍和驱动加载过程分析
[^2]: Hi3516C V500 SDK