OpenHarmony视频录制流程介绍

描述

媒体子系统为开发者提供了媒体相关的很多功能,本文针对其中的视频录制功能做个详细的介绍。

首先,我将通过媒体子系统提供的视频录制 Test 代码作为切入点,给大家梳理一下整个录制的流程。

目录

   

foundation/multimedia/camera_framework
├── frameworks
│   ├── js
│   │   └── camera_napi                            #napi实现
│   │       └── src
│   │           ├── input                          #Camera输入
│   │           ├── output                         #Camera输出
│   │           └── session                        #会话管理
│   └── native                                     #native实现
│       └── camera
│           ├── BUILD.gn
│           ├── src
│           │   ├── input                          #Camera输入
│           │   ├── output                         #Camera输出
│           │   └── session                        #会话管理
├── interfaces                                     #接口定义
│   ├── inner_api                                  #内部native实现
│   │   └── native
│   │       ├── camera
│   │       │   └── include
│   │       │       ├── input
│   │       │       ├── output
│   │       │       └── session
│   └── kits                                       #napi接口
│       └── js
│           └── camera_napi
│               ├── BUILD.gn
│               ├── include
│               │   ├── input
│               │   ├── output
│               │   └── session
│               └── @ohos.multimedia.camera.d.ts
└── services                                       #服务端
    └── camera_service
        ├── binder
        │   ├── base
        │   ├── client                             #IPC的客户端
        │   │   └── src
        │   └── server                             #IPC的服务端
        │       └── src
        └── src
 

 

录制的总体流程

    如下图:

媒体

Native 接口使用

    在 OpenHarmony 系统中,多媒体子系统通过 N-API 接口提供给上层 JS 调用,N-API 相当于是 JS 和 Native 之间的桥梁。 在 OpenHarmony 源码中,提供了 C++ 直接调用视频录制功能的例子,foundation/multimedia/camera_framework/interfaces/inner_api/native/test 目录中。

本文章主要参考了 camera_video.cpp 文件中的视频录制流程。

首先根据 camera_video.cpp 的 main 方法,了解下视频录制的主要流程代码:

int main(int argc, char **argv)
{
    ......

    // 创建CameraManager实例
    sptr camManagerObj = CameraManager::GetInstance();

    // 设置回调
    camManagerObj->SetCallback(std::make_shared(testName));

    // 获取支持的相机设备列表
    std::vector> cameraObjList = camManagerObj->GetSupportedCameras();

    // 创建采集会话
    sptr captureSession = camManagerObj->CreateCaptureSession();

    // 开始配置采集会话
    captureSession->BeginConfig();

    // 创建CameraInput
    sptr captureInput = camManagerObj->CreateCameraInput(cameraObjList[0]);
    sptr cameraInput = (sptr &)captureInput;

    // 开启CameraInput
    cameraInput->Open();

    // 设置CameraInput的Error回调
    cameraInput->SetErrorCallback(std::make_shared(testName));

    // 添加CameraInput实例到采集会话中
    ret = captureSession->AddInput(cameraInput);

    sptr videoSurface = nullptr;
    std::shared_ptr recorder = nullptr;

    // 创建Video的Surface
    videoSurface = Surface::CreateSurfaceAsConsumer();

    sptr videoListener = new SurfaceListener("Video", SurfaceType::VIDEO, g_videoFd, videoSurface);

    // 注册Surface的事件监听
    videoSurface->RegisterConsumerListener((sptr &)videoListener);

    // 视频的配置
    VideoProfile videoprofile = VideoProfile(static_cast(videoFormat), videosize, videoframerates);

    // 创建VideoOutput实例
    sptr videoOutput = camManagerObj->CreateVideoOutput(videoprofile, videoSurface);

    // 设置VideoOutput的回调
    ((sptr &)videoOutput)->SetCallback(std::make_shared(testName));

    // 添加videoOutput到采集会话中
    ret = captureSession->AddOutput(videoOutput);

    // 提交会话配置
    ret = captureSession->CommitConfig();

    // 开始录制
    ret = ((sptr &)videoOutput)->Start();


    sleep(videoPauseDuration);
    MEDIA_DEBUG_LOG("Resume video recording");
    // 暂停录制
    ret = ((sptr &)videoOutput)->Resume();

    MEDIA_DEBUG_LOG("Wait for 5 seconds before stop");
    sleep(videoCaptureDuration);
    MEDIA_DEBUG_LOG("Stop video recording");
    // 停止录制
    ret = ((sptr &)videoOutput)->Stop();

    MEDIA_DEBUG_LOG("Closing the session");
    // 停止采集会话
    ret = captureSession->Stop();

    MEDIA_DEBUG_LOG("Releasing the session");
    // 释放会话采集
    captureSession->Release();

    // Close video file
    TestUtils::SaveVideoFile(nullptr, 0, VideoSaveMode::CLOSE, g_videoFd);
    cameraInput->Release();
    camManagerObj->SetCallback(nullptr);
    return 0;
}

 

以上是视频录制的整体流程,其过程主要通过 Camera 模块支持的能力来实现,其中涉及几个重要的类:CaptureSession、CameraInput、VideoOutput。

CaptureSession 是整个过程的控制者,CameraInput 和 VideoOutput 相当于是设备的输入和输出。

调用流程

如下图:

媒体

后续主要针对上面的调用流程,梳理具体的调用流程,方便我们对了解视频录制的整理架构有一个更加深入的了解。

①创建 CameraManager 实例

通过 CameraManager::GetInstance() 获取 CameraManager 的实例,后续的一些接口都是通过该实例进行调用的。

GetInstance 使用了单例模式,在 OpenHarmony 代码中这种方式很常见。

sptr &CameraManager::GetInstance()
{
    if (CameraManager::cameraManager_ == nullptr) {
        MEDIA_INFO_LOG("Initializing camera manager for first time!");
        CameraManager::cameraManager_ = new(std::nothrow) CameraManager();
        if (CameraManager::cameraManager_ == nullptr) {
            MEDIA_ERR_LOG("CameraManager::GetInstance failed to new CameraManager");
        }
    }
    return CameraManager::cameraManager_;
}

 

②获取支持的相机设备列表

通过调用 CameraManager 的 GetSupportedCameras() 接口,获取设备支持的 CameraDevice 列表。

跟踪代码可以发现 serviceProxy_->GetCameras 最终会调用到 Camera 服务端的对应接口。

 

std::vector> CameraManager::GetSupportedCameras()
{
    CAMERA_SYNC_TRACE;

    std::lock_guard lock(mutex_);
    std::vector cameraIds;
    std::vector> cameraAbilityList;
    int32_t retCode = -1;
    sptr cameraObj = nullptr;
    int32_t index = 0;

    if (cameraObjList.size() > 0) {
        cameraObjList.clear();
    }
    if (serviceProxy_ == nullptr) {
        MEDIA_ERR_LOG("CameraManager::GetCameras serviceProxy_ is null, returning empty list!");
        return cameraObjList;
    }
    std::vector> supportedCameras;
    retCode = serviceProxy_->GetCameras(cameraIds, cameraAbilityList);
    if (retCode == CAMERA_OK) {
        for (auto& it : cameraIds) {
            cameraObj = new(std::nothrow) CameraDevice(it, cameraAbilityList[index++]);
            if (cameraObj == nullptr) {
                MEDIA_ERR_LOG("CameraManager::GetCameras new CameraDevice failed for id={public}%s", it.c_str());
                continue;
            }
            supportedCameras.emplace_back(cameraObj);
        }
    } else {
        MEDIA_ERR_LOG("CameraManager::GetCameras failed!, retCode: %{public}d", retCode);
    }

    ChooseDeFaultCameras(supportedCameras);
    return cameraObjList;
}
③创建采集会话

下面是比较重要的环节,通过调用 CameraManager 的 CreateCaptureSession 接口创建采集会话。 CameraManager 创建采集会话,是通过 serviceProxy_->CreateCaptureSession 方式进行调用。

 

这里涉及到了 OpenHarmony 中的 IPC 的调用,serviceProxy_ 是远端服务在本地的代理,通过这个代理可以调用到具体的服务端,这里是 HCameraService。

 

sptr CameraManager::CreateCaptureSession()
{
    CAMERA_SYNC_TRACE;
    sptr captureSession = nullptr;
    sptr result = nullptr;
    int32_t retCode = CAMERA_OK;

    if (serviceProxy_ == nullptr) {
        MEDIA_ERR_LOG("CameraManager::CreateCaptureSession serviceProxy_ is null");
        return nullptr;
    }
    retCode = serviceProxy_->CreateCaptureSession(captureSession);
    if (retCode == CAMERA_OK && captureSession != nullptr) {
        result = new(std::nothrow) CaptureSession(captureSession);
        if (result == nullptr) {
            MEDIA_ERR_LOG("Failed to new CaptureSession");
        }
    } else {
        MEDIA_ERR_LOG("Failed to get capture session object from hcamera service!, %{public}d", retCode);
    }
    return result;
}
代码最终来到 HCameraService::CreateCaptureSession 中,该方法中 new 了一个 HCaptureSession 对象,并且将该对象传递给了参数 session。

 

所以前面的 captureSession 对象就是这里 new 出来的 HCaptureSession,前面的 CameraManager 的 CreateCaptureSession() 方法中将 captureSession 封装成 CaptureSession 对象返回给应用层使用。

 

int32_t HCameraService::CreateCaptureSession(sptr &session)
{
    CAMERA_SYNC_TRACE;
    sptr captureSession;
    if (streamOperatorCallback_ == nullptr) {
        streamOperatorCallback_ = new(std::nothrow) StreamOperatorCallback();
        if (streamOperatorCallback_ == nullptr) {
            MEDIA_ERR_LOG("HCameraService::CreateCaptureSession streamOperatorCallback_ allocation failed");
            return CAMERA_ALLOC_ERROR;
        }
    }

    std::lock_guard lock(mutex_);
    OHOS::AccessTokenID callerToken = IPCSkeleton::GetCallingTokenID();
    captureSession = new(std::nothrow) HCaptureSession(cameraHostManager_, streamOperatorCallback_, callerToken);
    if (captureSession == nullptr) {
        MEDIA_ERR_LOG("HCameraService::CreateCaptureSession HCaptureSession allocation failed");
        return CAMERA_ALLOC_ERROR;
    }
    session = captureSession;
    return CAMERA_OK;
}

 

④开始配置采集会话

调用 CaptureSession 的 BeginConfig 进行采集会话的配置工作。这个工作最终调用到被封装的 HCaptureSession 中。

int32_t HCaptureSession::BeginConfig()
{
    CAMERA_SYNC_TRACE;
    if (curState_ == CaptureSessionState::SESSION_CONFIG_INPROGRESS) {
        MEDIA_ERR_LOG("HCaptureSession::BeginConfig Already in config inprogress state!");
        return CAMERA_INVALID_STATE;
    }
    std::lock_guard lock(sessionLock_);
    prevState_ = curState_;
    curState_ = CaptureSessionState::SESSION_CONFIG_INPROGRESS;
    tempCameraDevices_.clear();
    tempStreams_.clear();
    deletedStreamIds_.clear();
    return CAMERA_OK;
}

 

⑤创建 CameraInput

应用层通过 camManagerObj->CreateCameraInput(cameraObjList[0]) 的方式进行 CameraInput 的创建,cameraObjList[0] 就是前面获取支持设备的第一个。根据 CameraDevice 创建对应的 CameraInput 对象。

 

sptr CameraManager::CreateCameraInput(sptr &camera)
{
    CAMERA_SYNC_TRACE;
    sptr cameraInput = nullptr;
    sptr deviceObj = nullptr;

    if (camera != nullptr) {
        deviceObj = CreateCameraDevice(camera->GetID());
        if (deviceObj != nullptr) {
            cameraInput = new(std::nothrow) CameraInput(deviceObj, camera);
            if (cameraInput == nullptr) {
                MEDIA_ERR_LOG("failed to new CameraInput Returning null in CreateCameraInput");
                return cameraInput;
            }
        } else {
            MEDIA_ERR_LOG("Returning null in CreateCameraInput");
        }
    } else {
        MEDIA_ERR_LOG("CameraManager: Camera object is null");
    }
    return cameraInput;
}
⑥开启 CameraInput

 

调用了 CameraInput 的 Open 方法,进行输入设备的启动打开。

 

void CameraInput::Open()
{
    int32_t retCode = deviceObj_->Open();
    if (retCode != CAMERA_OK) {
        MEDIA_ERR_LOG("Failed to open Camera Input, retCode: %{public}d", retCode);
    }
}

 

⑦添加 CameraInput 实例到采集会话中

通过调用 captureSession 的 AddInput 方法,将创建的 CameraInput 对象添加到采集会话的输入中,这样采集会话就知道采集输入的设备。

int32_t CaptureSession::AddInput(sptr &input)
{
    CAMERA_SYNC_TRACE;
    if (input == nullptr) {
        MEDIA_ERR_LOG("CaptureSession::AddInput input is null");
        return CAMERA_INVALID_ARG;
    }
    input->SetSession(this);
    inputDevice_ = input;
    return captureSession_->AddInput(((sptr &)input)->GetCameraDevice());
}

 

最终调用到 HCaptureSession 的 AddInput 方法,该方法中核心的代码是 tempCameraDevices_.emplace_back(localCameraDevice),将需要添加的 CameraDevice 插入到 tempCameraDevices_ 容器中。

 

int32_t HCaptureSession::AddInput(sptr cameraDevice)
{
    CAMERA_SYNC_TRACE;
    sptr localCameraDevice = nullptr;

    if (cameraDevice == nullptr) {
        MEDIA_ERR_LOG("HCaptureSession::AddInput cameraDevice is null");
        return CAMERA_INVALID_ARG;
    }
    if (curState_ != CaptureSessionState::SESSION_CONFIG_INPROGRESS) {
        MEDIA_ERR_LOG("HCaptureSession::AddInput Need to call BeginConfig before adding input");
        return CAMERA_INVALID_STATE;
    }
    if (!tempCameraDevices_.empty() || (cameraDevice_ != nullptr && !cameraDevice_->IsReleaseCameraDevice())) {
        MEDIA_ERR_LOG("HCaptureSession::AddInput Only one input is supported");
        return CAMERA_INVALID_SESSION_CFG;
    }
    localCameraDevice = static_cast(cameraDevice.GetRefPtr());
    if (cameraDevice_ == localCameraDevice) {
        cameraDevice_->SetReleaseCameraDevice(false);
    } else {
        tempCameraDevices_.emplace_back(localCameraDevice);
        CAMERA_SYSEVENT_STATISTIC(CreateMsg("CaptureSession::AddInput"));
    }

    sptr streamOperator;
    int32_t rc = localCameraDevice->GetStreamOperator(streamOperatorCallback_, streamOperator);
    if (rc != CAMERA_OK) {
        MEDIA_ERR_LOG("HCaptureSession::GetCameraDevice GetStreamOperator returned %{public}d", rc);
        localCameraDevice->Close();
        return rc;
    }
    return CAMERA_OK;
}
⑧创建 Video 的 Surface

 

通过 Surface::CreateSurfaceAsConsumer 创建 Surface。

 

sptr Surface::CreateSurfaceAsConsumer(std::string name, bool isShared)
{
    sptr surf = new ConsumerSurface(name, isShared);
    GSError ret = surf->Init();
    if (ret != GSERROR_OK) {
        BLOGE("Failure, Reason: consumer surf init failed");
        return nullptr;
    }
    return surf;
}
⑨创建 VideoOutput 实例

 

通过调用 CameraManager 的 CreateVideoOutput 来创建 VideoOutput 实例。

 

sptr CameraManager::CreateVideoOutput(VideoProfile &profile, sptr &surface)
{
    CAMERA_SYNC_TRACE;
    sptr streamRepeat = nullptr;
    sptr result = nullptr;
    int32_t retCode = CAMERA_OK;
    camera_format_t metaFormat;

    metaFormat = GetCameraMetadataFormat(profile.GetCameraFormat());
    retCode = serviceProxy_->CreateVideoOutput(surface->GetProducer(), metaFormat,
                                               profile.GetSize().width, profile.GetSize().height, streamRepeat);
    if (retCode == CAMERA_OK) {
        result = new(std::nothrow) VideoOutput(streamRepeat);
        if (result == nullptr) {
            MEDIA_ERR_LOG("Failed to new VideoOutput");
        } else {
            std::vector videoFrameRates = profile.GetFrameRates();
            if (videoFrameRates.size() >= 2) { // vaild frame rate range length is 2
                result->SetFrameRateRange(videoFrameRates[0], videoFrameRates[1]);
            }
            POWERMGR_SYSEVENT_CAMERA_CONFIG(VIDEO,
                                            profile.GetSize().width,
                                            profile.GetSize().height);
        }
    } else {
        MEDIA_ERR_LOG("VideoOutpout: Failed to get stream repeat object from hcamera service! %{public}d", retCode);
    }
    return result;
}

 

该方法中通过 IPC 的调用最终调用到了 HCameraService 的:

 

CreateVideoOutput(surface->GetProducer(), format, streamRepeat)

int32_t HCameraService::CreateVideoOutput(const sptr &producer, int32_t format,
                                          int32_t width, int32_t height,
                                          sptr &videoOutput)
{
    CAMERA_SYNC_TRACE;
    sptr streamRepeatVideo;

    if ((producer == nullptr) || (width == 0) || (height == 0)) {
        MEDIA_ERR_LOG("HCameraService::CreateVideoOutput producer is null");
        return CAMERA_INVALID_ARG;
    }
    streamRepeatVideo = new(std::nothrow) HStreamRepeat(producer, format, width, height, true);
    if (streamRepeatVideo == nullptr) {
        MEDIA_ERR_LOG("HCameraService::CreateVideoOutput HStreamRepeat allocation failed");
        return CAMERA_ALLOC_ERROR;
    }
    POWERMGR_SYSEVENT_CAMERA_CONFIG(VIDEO, producer->GetDefaultWidth(),
                                    producer->GetDefaultHeight());
    videoOutput = streamRepeatVideo;
    return CAMERA_OK;
}

 

HCameraService 的 CreateVideoOutput 方法中主要创建了 HStreamRepeat,并且通过参数传递给前面的 CameraManager 使用,CameraManager 通过传递的 HStreamRepeat 对象,进行封装,创建出 VideoOutput 对象。

⑩添加 videoOutput 到采集会话中,并且提交采集会话 该步骤类似添加 CameraInput 到采集会话的过程,可以参考前面的流程。 ⑪开始录制

通过调用 VideoOutput 的 Start 进行录制的操作。

 

int32_t VideoOutput::Start()
{
    return static_cast(GetStream().GetRefPtr())->Start();
}

 

该方法中会调用到 HStreamRepeat 的 Start 方法。

 

int32_t HStreamRepeat::Start()
{
    CAMERA_SYNC_TRACE;

    if (streamOperator_ == nullptr) {
        return CAMERA_INVALID_STATE;
    }
    if (curCaptureID_ != 0) {
        MEDIA_ERR_LOG("HStreamRepeat::Start, Already started with captureID: %{public}d", curCaptureID_);
        return CAMERA_INVALID_STATE;
    }
    int32_t ret = AllocateCaptureId(curCaptureID_);
    if (ret != CAMERA_OK) {
        MEDIA_ERR_LOG("HStreamRepeat::Start Failed to allocate a captureId");
        return ret;
    }
    std::vector ability;
    OHOS::ConvertMetadataToVec(cameraAbility_, ability);
    CaptureInfo captureInfo;
    captureInfo.streamIds_ = {streamId_};
    captureInfo.captureSetting_ = ability;
    captureInfo.enableShutterCallback_ = false;
    MEDIA_INFO_LOG("HStreamRepeat::Start Starting with capture ID: %{public}d", curCaptureID_);
    CamRetCode rc = (CamRetCode)(streamOperator_->Capture(curCaptureID_, captureInfo, true));
    if (rc != HDI::NO_ERROR) {
        ReleaseCaptureId(curCaptureID_);
        curCaptureID_ = 0;
        MEDIA_ERR_LOG("HStreamRepeat::Start Failed with error Code:%{public}d", rc);
        ret = HdiToServiceError(rc);
    }
    return ret;
}
核心的代码是 streamOperator_->Capture,其中最后一个参数 true,表示采集连续数据。 ⑫录制结束,保存录制文件  

 

总结

    本文主要对 OpenHarmony 3.2 Beta 多媒体子系统的视频录制进行介绍,首先梳理了整体的录制流程,然后对录制过程中的主要步骤进行了详细地分析。

视频录制主要分为以下几个步骤:

获取 CameraManager 实例。

创建采集会话 CaptureSession。

创建 CameraInput 实例,并且将输入设备添加到 CaptureSession 中。

创建 Video 录制需要的 Surface。

创建 VideoOutput 实例,并且将输出添加到 CaptureSession 中。

提交采集会话的配置。

调用 VideoOutput 的 Start 方法,进行视频的录制。

录制结束,保存录制的文件。

作者:巴延兴

  审核编辑:汤梓红

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分