OpenHarmony 源码解析之多媒体子系统(camera) 原创 精华

深开鸿
发布于 2021-9-3 08:49
浏览
14收藏

作者:郭岳峰

1 简介

媒体子系统为开发者提供一套接口,方便开发者使用系统的媒体资源,本文主要介绍多媒体下的Camera模块。

1.1 OpenHarmony 架构图

OpenHarmony 源码解析之多媒体子系统(camera)-鸿蒙开发者社区

1.2 camera子系统

相关功能接口:相机拍照、相机预览、相机录像

OpenHarmony 源码解析之多媒体子系统(camera)-鸿蒙开发者社区

1.3 原理图

OpenHarmony 源码解析之多媒体子系统(camera)-鸿蒙开发者社区

1.4 搭建HarmonyOS环境

我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。

  • 安装DevEco Studio,详情请参考下载和安装软件

  • 设置DevEco Studio开发环境,DevEco Studio开发环境需要依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:

    1.如果可以直接访问Internet,只需进行下载OpenHarmony SDK操作。

    2.如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境

  • 开发者可以参考以下链接,完成设备调试的相关配置:

    1.使用真机进行调试

    2.使用模拟器进行调试

2 基础知识

2.1 代码结构

OpenHarmony 源码解析之多媒体子系统(camera)-鸿蒙开发者社区

2.2 相机模块核心类

OpenHarmony 源码解析之多媒体子系统(camera)-鸿蒙开发者社区

3 源码解析

3.1 上层初始化示例

该文件的main方法执行了创建camera的整个流程,并且在创建初始化好了camera后,根据用户输入的字符,对camera进行拍照,录像,预览功能的执行。

int main()
{
    cout << "Camera sample begin." << endl;
    SampleHelp();
    CameraKit *camKit = CameraKit::GetInstance();
    if (camKit == nullptr) {
        cout << "Can not get CameraKit instance" << endl;
        return 0;
    }
    list<string> camList = camKit->GetCameraIds();
    string camId;
    for (auto &cam : camList) {
        camId = cam;
    break;
    }

    if (camId.empty()) {
        cout << "No available camera.(1080p wanted)" << endl;
        return 0;
    }

    EventHandler eventHdlr; // Create a thread to handle callback events
    SampleCameraStateMng CamStateMng(eventHdlr);

    camKit->CreateCamera(camId, CamStateMng, eventHdlr);

    char input;
    while (cin >> input) {
        switch (input) {
            case '1':
                CamStateMng.Capture();
                break;
            case '2':
                CamStateMng.StartRecord();
                break;
            case '3':
                CamStateMng.StartPreview();
                break;
            case 's':
                CamStateMng.Stop();
                break;
            case 'q':
                CamStateMng.Stop();
                goto EXIT;
            default:
                SampleHelp();
                break;
        }
    }
    EXIT:
    cout << "Camera sample end." << endl;
    return 0;
}

Camkit 是CameraKit类的全局的变量,这个主要提供上层的调用,这个类里面实际是通过cameraManager_对象进行调用相关的方法。图中主要的流程分为以下几个步骤:

  1. 获取CameraKit实例

  2. 调用camKit的GetCameraIds方法获取摄像头的id列表,根据id列表找到1080P分辨率的摄像头id,如果没有找到提示错误信息,直接return。

  3. 调用camKit的CreateCamera方法,这个方法是camera初始化的核心

根据这三个步骤,分析每一个步骤的流程,获取CameraKit实例这一步,其实一层层调用,实现camera初始化的操作,以下是相关的调用流程

(1)获取CameraKit流程

OpenHarmony 源码解析之多媒体子系统(camera)-鸿蒙开发者社区

(2)GetCameraIds流程

list<string> CameraKit::GetCameraIds()
{
    return cameraManager_->GetCameraIds();
}

CameraKit调用实际上是通过cameraManager_进行调用

list<string> GetCameraIds() override
{
    list<string> cameraList;
    for (auto &i : cameraMapCache_) {
        MEDIA_DEBUG_LOG("%s", i.first.c_str());
        cameraList.emplace_back(i.first);
    }
    return cameraList;
}

CameraService初始化完成后,通过device和ability创建的CameraImpl实例存入cameraMapCache_变量中, GetCameraIds方法通过遍历cameraMapCache_来获取可用的cameraId列表。

(3)CreateCamera流程

void CameraKit::CreateCamera(const string &cameraId, CameraStateCallback &callback, EventHandler &handler)
{
    g_cameraManager->CreateCamera(cameraId, callback, handler);
}

CameraKit实际调用了CameraManagerImpl的CreateCamera方法

void CreateCamera(const string &cameraId, CameraStateCallback &callback, EventHandler &handler) override
{
    auto p = cameraMapCache_.find(cameraId);
    if (p == cameraMapCache_.end()) {
        MEDIA_ERR_LOG("Camera impl not found.");
        handler.Post([&callback, &cameraId] { callback.OnCreateFailed(cameraId, MEDIA_ERR); });
        return;
    }
    p->second->RegistCb(callback, handler);
    cameraService_->CreateCamera(cameraId);
}

首先根据cameraId在cameraMapCache_中找到CameraImpl的位置,将CameraStateCallback回调注册到CamerImpl中,CameraStateCallback是从应用层通过参数传入进来,所以后续CameraImpl中的Configure,Release,OnCreate,OnCreateFailed等方法会通过回调,调用应用层的接口。最后调用CameraService的CreateCamera方法进行创建camera。

void CameraService::CreateCamera(string cameraId)
{
    if (cameraId != g_mainCamera) {
        MEDIA_ERR_LOG("This camera does not exist. (cameraId=%s)", cameraId.c_str());
    }
    cameraServiceCb_->OnCameraStatusChange(cameraId, CameraServiceCallback::CAMERA_STATUS_CREATED, *device_);
}

CameraService中主要通过回调OnCameraStatusChange方法,这个是在CameraManagerImpl类中实现的,CameraManagerImpl通过调用CameraService的Initialize(*this)实现回调接口注册。

void CameraService::Initialize(CameraServiceCallback &callback)
{
    MEDIA_DEBUG_LOG("Camera service initializing");
    InitCameraDevices();

    list<string> cameraList = {g_mainCamera};
    cameraServiceCb_->OnCameraServiceInitialized(cameraList);
}

接下来看一下CameraManagerImpl中的回调实现

void OnCameraStatusChange(string &cameraId, CameraStauts status) override
{
    auto p = cameraMapCache_.find(cameraId);
    switch (status) {
        case CAMERA_STATUS_UNAVAIL:
            if (p != cameraMapCache_.end()) {
                for (auto &i : deviceCbList_) {
                    i.second->Post(
                        [i, cameraId]() { i.first->OnCameraStatus(cameraId, CAMERA_DEVICE_STATE_UNAVAILABLE); });
                }
                // User may still using icamera now
                delete p->second;
                cameraMapCache_.erase(p);
            }
            break;
        case CAMERA_STATUS_AVAIL:
            InitCameraAbility(cameraId);
            if (p == cameraMapCache_.end()) {
                for (auto &i : deviceCbList_) {
                    i.second->Post(
                        [i, cameraId]() { i.first->OnCameraStatus(cameraId, CAMERA_DEVICE_STATE_AVAILABLE); });
                }
            }
            break;
        case CAMERA_STATUS_CREATED:
            if (p != cameraMapCache_.end()) {
                p->second->OnCreate(cameraId);
            }
            break;
        case CAMERA_STATUS_CREATE_FAILED:
            if (p != cameraMapCache_.end()) {
                p->second->OnCreateFailed();
            }
            break;
        default:
            break;
    }
}

调用cameraMapCache_中对应cameraId的OnCreate方法,其中device参数是CameraService中创建并传给CameraImpl的,接下来看一下CameraImpl的OnCreate方法

void CameraImpl::OnCreate(CameraDevice &device)
{
    device_ = &device;
    if (stateCb_ == nullptr || handler_ == nullptr) {
        return;
    }
    handler_->Post([this] { this->stateCb->OnCreated(*this); });
}

首先在CameraImpl中保存一份device_, 其次在EventHandler中通过之前注册的CameraStateCallback回调OnCreate方法,该回调是上层应用实现的,具体如下图所示

class SampleCameraStateMng : public CameraStateCallback {
public:
    SampleCameraStateMng() = delete;
    SampleCameraStateMng(EventHandler &eventHdlr) : eventHdlr_(eventHdlr) {}
    ~SampleCameraStateMng()
    {
        CloseRecorder();
    }
    void OnCreated(Camera &c) override
    {
        cout << "Sample recv OnCreate camera." << endl;
        auto config = CameraConfig::CreateCameraConfig();
        config->SetFrameStateCallback(&fsCb_, &eventHdlr_);
        c.Configure(*config);
        cam_ = &c;
    }
    void OnCreateFailed(const std::string cameraId, int32_t errorCode) override {}
    void OnReleased(Camera &c) override {}

主要配置了FrameStateCallback回调,以及设置了EventHandler,然后保存CameraImpl,应用层拿到了CameraImpl的对象,并且赋值给cam_对象,后续的camera操作都是通过cam_进行的。

3.2 上层调用相机功能

char input;
while (cin >> input) {
    switch (input) {
        case '1':
            CamStateMng.Capture();
            break;
        case '2':
            CamStateMng.StartRecord();
            break;
        case '3':
            CamStateMng.StartPreview();
            break;
        case 's':
            CamStateMng.Stop();
            break;
        case 'q':
            CamStateMng.Stop();
            goto EXIT;
        default:
            SampleHelp();
            break;
    }
}

应用层根据用户输入的字符进行拍照,开始录像,开始预览以及停止的操作,以下是拍照,录像和预览的时序图

拍照流程

OpenHarmony 源码解析之多媒体子系统(camera)-鸿蒙开发者社区

录像流程

-OpenHarmony 源码解析之多媒体子系统(camera)-鸿蒙开发者社区

总结

通过本文的学习可以大致了解Camera的初始化流程,明白Camera的创建过程,并且对Camera的拍照,录像等功能有详细的了解。

更多原创内容请关注:开鸿 HarmonyOS 学院

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-11-19 17:11:06修改
18
收藏 14
回复
举报
21条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

楼主对Camera讲解可以说是非常详细了。

回复
2021-9-3 10:18:12
强哥1999
强哥1999

对于初学者来说这个真的太有用了

回复
2021-9-3 13:47:50
ㄩ

你们对鸿蒙源码都研究的这么细啊... 厉害...

回复
2021-9-3 13:52:37
flyzhyg
flyzhyg

对源码讲解的好详细呀,给楼主点赞

回复
2021-9-3 13:56:27
李祥志
李祥志

写的很详细,能快速的熟悉Camera模块,点赞~

回复
2021-9-3 13:58:07
萨瓦迪迪
萨瓦迪迪

仰望大佬~小菜鸡求带~

回复
2021-9-3 14:06:50
wx60b7765540463
wx60b7765540463

此文在手 玩转鸿蒙Camera不愁 棒~棒~棒~ 你真棒!

回复
2021-9-3 14:08:16
轩辕筱柒
轩辕筱柒

源码分析很到位,对我帮助很大,让我对多媒体子系统有更深入的了解

回复
2021-9-3 14:08:32
wx60b25071502d0
wx60b25071502d0

流程代码很细致,仰望楼主

回复
2021-9-3 14:33:16
mb6096018f96945
mb6096018f96945

代码分析的很透彻,层次清晰

回复
2021-9-3 14:38:04
执着的刀斯林
执着的刀斯林

nb了老哥

回复
2021-9-3 16:23:14
深开鸿
深开鸿 回复了 红叶亦知秋
楼主对Camera讲解可以说是非常详细了。

谢谢认可,可以关注专栏,下期还有更多源码解析

回复
2021-9-3 17:02:18
深开鸿
深开鸿 回复了 强哥1999
对于初学者来说这个真的太有用了

谢谢认可,可以关注专栏,下期还有更多源码解析

回复
2021-9-3 17:02:24
深开鸿
深开鸿 回复了
你们对鸿蒙源码都研究的这么细啊... 厉害...

谢谢认可,可以关注专栏,下期还有更多源码解析

回复
2021-9-3 17:02:29
深开鸿
深开鸿 回复了 flyzhyg
对源码讲解的好详细呀,给楼主点赞

谢谢认可,可以关注专栏,下期还有更多源码解析

回复
2021-9-3 17:02:35
深开鸿
深开鸿 回复了 李祥志
写的很详细,能快速的熟悉Camera模块,点赞~

谢谢认可,可以关注专栏,下期还有更多源码解析

回复
2021-9-3 17:02:41
深开鸿
深开鸿 回复了 轩辕筱柒
源码分析很到位,对我帮助很大,让我对多媒体子系统有更深入的了解

谢谢认可,可以关注专栏,下期还有更多源码解析

回复
2021-9-3 17:02:52
深开鸿
深开鸿 回复了 轩辕筱柒
源码分析很到位,对我帮助很大,让我对多媒体子系统有更深入的了解

谢谢认可,可以关注专栏,下期还有更多源码解析

回复
2021-9-3 17:03:02
朱伟ISRC
朱伟ISRC

为什么一定找1080P的摄像头呢~ 我看openharmony3.0 没有找1080P摄像头这段代码呀

1
回复
2021-11-3 09:35:14
蓝色的皮卡
蓝色的皮卡

为什么openharmony3.0的demo代码中,把hevc改成h264就不行呢,代码编码没问题,但烧录之后ohos运行代码录制就出错

回复
2022-2-16 16:00:43
回复
    相关推荐