OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗留问题

2022年01月13日 阅读数:3
这篇文章主要向大家介绍OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗留问题,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗留问题

新大陆自动识别 郑曦node

Question1 cameraDeviceMap_

HDF 驱动框架简介

OpenHarmony系统 提供了HDF 驱动框架,框架采用 C 语言面向对象编程模型构建,经过平台解耦、内核解耦,来达到兼容不一样内核,统一平台底座的目的,从而帮助开发者实现驱动一次开发,多系统部署的效果。
每一个驱动程序都对应着一个 Driver Entry。Driver Entry 主要完成驱动的初始化和驱动接口绑定功能。引[^1]linux

回到代码

咱们来看下camera相机驱动对应的Driver Entryios

//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-&gt;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基本讲解完毕。markdown

//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。
DeviceManager.png
Hi3516采用的就是MPP的框架,这个框架我也没有仔细研究使用过,目前的开源代码里这部分代码是经过so对外提供的。我的以为做为一个开源的初期平台,更应该选用V4L2这样通用开源的框架来作适配彷佛对学习者会更友好点。不过既然目前用了MPP(包括后面node管理也要用到),这里尝试简单说明一下MPP,若是理解的有问题,还但愿有大神能给些指导。ide

MPP简介 引[^2]

上海海思提供的媒体处理软件平台(Media Process Platform,简称 MPP),可支持应用软件快速开发。该平台对应用软件屏蔽了芯片相关的复杂的底层处理,并对应用软件直接提供 MPI(MPP Program Interface)接口完成相应功能。该平台支持应用软件快速开发如下功能:输入视频捕获、H.265/H.264/JPEG 编码、H.265/H.264/JPEG 解码、视频输出显示、视频图像前处理(包括去噪、加强、锐化)、图像拼接、图像几何矫正、智能、音频捕获及输出、音频编解码等功能。
从下面的结构图理解,MPP是海思为了屏蔽硬件芯片和不一样操做系统(linux、liteos)而设计的多媒体处理平台。其目的是为了统一应用层代码,固然也多是为了保护底层芯片的具体工做方式。
Hi35xx典型系统层次图.pngDeviceManager
来看下MPP的具体工做流程图。本Demo中涉及到的视频流显示和capture采集的流程应该是图中红色标线部分。
视频输入(VI)、视频处理(VPSS)、视频编码(VENC)、视频解码(VDEC)、视频输出(VO)
MPP处理流程图.pngDeviceManager函数

回到代码

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 库里。post

对应这个库的使用没有找到相关的文档和说明,若是有对此了解的朋友但愿能够在评论下给点指导。学习

//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;
}

尝试画个类图表示下:(图有点丑,念书那会导师让咱们画类图,我没好好学,愧对师长--!)
device_mamager_uml_class.pngDeviceManager

剩下的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

想了解更多关于鸿蒙的内容,请访问:

51CTO和华为官方合做共建的鸿蒙技术社区

https://harmonyos.51cto.com/#bkwz

::: hljs-center

21_9.jpg

:::