OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗 原创 精华

NL_AIDC_Ryanzx
发布于 2022-1-6 13:59
浏览
1收藏

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。
OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗-鸿蒙开发者社区
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)而设计的多媒体处理平台。其目的是为了统一应用层代码,当然也可能是为了保护底层芯片的具体工作方式。
OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗-鸿蒙开发者社区OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗-鸿蒙开发者社区
来看下MPP的具体工作流程图。本Demo中涉及到的视频流显示和capture采集的流程应该是图中红色标线部分。
视频输入(VI)、视频处理(VPSS)、视频编码(VENC)、视频解码(VDEC)、视频输出(VO)
OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗-鸿蒙开发者社区OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗-鸿蒙开发者社区

回到代码

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

尝试画个类图表示下:(图有点丑,念书那会导师让我们画类图,我没好好学,愧对师长–!)
OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗-鸿蒙开发者社区OpenHarmony-v3.0-LTS Camera相机驱动框架(L2)解析2_解决两个遗-鸿蒙开发者社区

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

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
1
收藏 1
回复
举报
回复
    相关推荐