OpenHarmony Camera组件架构分析以及拍照流程源码解析

系统 OpenHarmony
本文主要描述了OpenHarmony 多媒体子系统Camera组件从应用层到驱动层的架构,按照拍照的流程,分析了与之相关的两个仓库的主要流程的函数代码。

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

本文以OpenHarmony 3.1 Release - multimedia_camera_standard源码为基础进行分析。

概述

相机功能是现在智能设备一个非常重要的功能。OpenHarmony 相机组件支持相机业务的开发,开发者可以通过已开放的接口实现相机硬件的访问、操作和新功能开发,最常见的操作如:预览、拍照和录像等。

基本概念

拍照

此功能用于拍摄采集照片。

预览

此功能用于在开启相机后,在缓冲区内重复采集摄像帧,支持在拍照或录像前进行摄像帧预览显示。

录像

此功能用于在开始录像后和结束录像前的时间段内,在缓冲区内重复采集摄像帧,支持视频录制。

相机组件架构分析

OpenHarmony 相机组件从上层到下层依次可以分为:应用层 - 相机框架服务层 - 系统服务层(驱动框架服务)- 内核驱动。其实 OpenHarmony 的多数组件都和相机组件有同样架构层次。

代码仓库目录结构

Camera Standard 相关代码有两个仓库,相机框架层 ​​OpenHarmony / multimedia_camera_standard​​ 和 系统服务(驱动框架服务层) ​​OpenHarmony / drivers_peripheral / camera​​。

相机框架层

代码结构

/foundation/multimedia/camera_standard     # 相机组件业务代码
├── frameworks # 框架代码
│ ├── js # 外部接口实现
│ │ └── camera_napi # 相机NAPI实现
│ └── native # 内部接口实现
│ ├── camera # 相机框架实现
│ └── metadata # 元数据实现
├── interfaces # 接口代码
│ ├── inner_api # 内部接口
│ └── kits # 外部接口
├── ohos.build # 构建脚本
└── services
├── camera_service # 相机框架服务
│ ├── binder
│ │ ├── base # 相机框架服务结构声明
│ │ ├── client # 相机框架服务客户端
│ │ └── server # 相机框架服务服务端
│ ├── include # 相机服务头文件
│ └── src # 相机服务实现
└── etc # 相机服务配置

相机驱动框架服务层

代码结构:

/drivers/peripheral/camera
├── hal # camera模块的hal层代码(标准系统)
│ ├── adapter # camera hal平台适配层的实现
│ ├── buffer_manager # camera hal统一的Buffer管理
│ ├── device_manager # 提供camera hal层设备管理能力,包括设备枚举、设备能力查询等
│ ├── hdi_impl # camera hal HDI的具体实现
│ ├── include # camera hal层内部的头文件
│ ├── init # camera halHDI接口使用样例实现
│ ├── pipeline_core # camera halpipeline核心代码
│ ├── test # camera hal层测试代码实现
│ └── utils # camera hal层工具类代码,目前提供的是watchdog
├── hal_c # 提供C实现的HAL接口
│ ├── hdi_cif # C实现的HDI接口适配代码
│ └── include # C形式的HDI接口
└── interfaces # camera hal对上层服务提供的驱动能力接口
├── hdi_ipc # IPC模式的HDI实现(标准系统)
│ ├── client # IPC 客户端
│ └── server # IPC 服务端接口
├── hdi_passthrough # 直通模式的HDI实现
└── include # camera hal对外提供的HDI定义

代码调用层级关系

OpenHarmony 应用层一般使用JS或者eTS语言进行开发,相机框架层 最外层对上提供NAPI JS接口,对下通过IPC方式调用其相机框架服务。相机框架服务通过IPC方式调用相机驱动框架服务。相机驱动框架服务调用实际的相机硬件驱动。代码调用层级关系参考下图。

OpenHarmony Camera组件架构分析以及拍照流程源码解析-开源基础软件社区

关于上图的说明:

相机框架层概念和流程

框架层把相机功能抽象为会话管理(管理生命周期、参数配置、输入管理、输出管理)、设备输入(设备查询、设备控制、设备监听)和数据输出(元数据输出、流输出和状态控制),其对应的类名依次为CaptureSession、CameraInput和CaptureOutput。

其中数据输出CaptureOutput根据实际业务功能又细分为预览输出PreviewOutput、拍照输出PhotoOutput和录像输出VideoOutput。以上所有类都由相机管理类CameraManager引出。

其相机功能(预览、拍照和录像)代码流程都可以使用如下伪代码进行描述。

// 创建相机管理对象
camManagerObj = new CameraManager();
// 创建设备输入
cameraInput = camManagerObj->CreateCameraInput();
// 创建设备输出
captureOutput = camManagerObj->CreateCaptureOutput();
// 创建管理会话
captureSession = camManagerObj->CreateCaptureSession();
// 会话绑定输入
captureSession->AddInput(cameraInput);
// 会话绑定输出
captureSession->AddOutput(photoOutput);
// 使绑定生效
captureSession->CommitConfig();
// 开始预览拍照或录制
captureSession->Start();
// 停止预览或录制
captureSession->Stop();
// 释放资源
Release();

其中创建输入需要指定摄像头设备,创建输出需要配置输出到什么地方。

相机框架层拍照流程解析

上节使用伪代码阐述了相机功能的主要流程,这一节详细讲一讲拍照这一操作在框架层的实际代码实现。

拍照流程图

拍照代码流程顺序如下图所示

OpenHarmony Camera组件架构分析以及拍照流程源码解析-开源基础软件社区

拍照示例代码

  1. 创建缓冲区消费者端监听器(CaptureSurfaceListener)以保存图像。
// 继承IBufferConsumerListener类
class CaptureSurfaceListener : public IBufferConsumerListener {
public:
sptr<Surface> surface_; // 此为传入的拍照用的消费者Surface
// 有数据产生时回调此函数
void OnBufferAvailable() override
{
int32_t flushFence = 0;
int64_t timestamp = 0;
OHOS::Rect damage;
OHOS::sptr<OHOS::SurfaceBuffer> buffer = nullptr;
surface_->AcquireBuffer(buffer, flushFence, timestamp, damage); // 从消费者Surface 队列中获取包含数据的Buffer
if (buffer != nullptr) {
void *addr = buffer->GetVirAddr(); // 获取图像数据的虚拟地址
int32_t size = buffer->GetSize(); // 获取图像数据长度

// write(fd, addr, size) 保存图像数据到文件

surface_->ReleaseBuffer(buffer, -1); // 释放Buffer
}
}
};

获取相机管理器实例并获取相机对象列表。

sptr<CameraManager> camManagerObj = CameraManager::GetInstance();          // 所有的类都由CameraManager创建
std::vector<sptr<CameraInfo>> cameraObjList = camManagerObj->GetCameras(); // 如果有多个摄像头会返回多个

使用相机对象创建相机输入来打开相机。

sptr<CaptureInput> cameraInput = camManagerObj->CreateCameraInput(cameraObjList[0]);  // 使用第一个摄像头创建设备输入

创建采集会话。

sptr<CaptureSession> captureSession = camManagerObj->CreateCaptureSession();

开始配置采集会话。

int32_t result = captureSession->BeginConfig(); // 初始化会话

将相机输入添加到采集会话。

result = captureSession->AddInput(cameraInput);

创建消费者Surface并注册监听器以监听缓冲区更新。

sptr<Surface> photoSurface = Surface::CreateSurfaceAsConsumer(); 
int32_t photoWidth = 1280; // 设置所支持的图片size
int32_t photoHeight = 960;
photoSurface->SetDefaultWidthAndHeight(photoWidth, photoHeight);
photoSurface->SetUserData(CameraManager::surfaceFormat, std::to_string(OHOS_CAMERA_FORMAT_JPEG)); // 当前仅支持JPEG图片格式
sptr<CaptureSurfaceListener> capturelistener = new CaptureSurfaceListener();
capturelistener->surface_ = photoSurface; // 给回调类传递surface
photoSurface->RegisterConsumerListener((sptr<IBufferConsumerListener> &)capturelistener); // 注册回调函数

使用上面创建的 Surface 创建拍照输出。

sptr<CaptureOutput> photoOutput = camManagerObj->CreatePhotoOutput(photoSurface);

将拍照输出添加到采集会话。

result = captureSession->AddOutput(photoOutput);

将配置提交到采集会话。

result = captureSession->CommitConfig(); // 把Input和Output做关联等

拍摄照片。

result = ((sptr<PhotoOutput> &)photoOutput)->Capture(); // 调用最底层的数据接口采集图像

释放采集会话资源。

captureSession->Release();

释放相机输入关闭相机。

cameraInput->Release();

驱动框架层拍照流程源码解析

下图是驱动框架层层次结构图。

OpenHarmony Camera组件架构分析以及拍照流程源码解析-开源基础软件社区

1、与CameraHost通信,获取CameraId

int32_t HCameraHostManager::GetCameras(std::vector<std::string>& cameraIds){
...
// 第一次时候 初始化 CameraHostInfo,CameraHostInfo中获取CameraHost
AddCameraHost("camera_service");
// 返回从底层获取的 CameraId
cameraHost->GetCameras(cameraIds);
return CAMERA_OK;
}
void HCameraHostManager::AddCameraHost(const std::string& svcName){
// 创建一个 CameraHostInfo
sptr<HCameraHostManager::CameraHostInfo> cameraHost = new HCameraHostManager::CameraHostInfo(this, svcName);
// 初始化时获取 CameraHost的Proxy
cameraHost->Init();
// CameraHostInfo中维护CameraHost
cameraHostInfos_.push_back(cameraHost);
std::vector<std::string> cameraIds;
...
}
bool HCameraHostManager::CameraHostInfo::Init()
{
// 获取驱动框架服务层的 CameraHost的Proxy代理
cameraHostProxy_ = Camera::ICameraHost::Get(name_.c_str());
// 通过cameraHostProxy 查询物理设备对应的cameraIds
Camera::CamRetCode ret = cameraHostProxy_->GetCameraIds(cameraIds_);
...
return true;
}

2、根据CameraId打开摄像头获取CameraDevice

// 根据CameraId 创建 CameraInput 的时候创建 CameraDevice
sptr<CameraInput> CameraManager::CreateCameraInput(sptr<CameraInfo> &camera){
CreateCameraDevice(camera->GetID());
...
cameraInput = new CameraInput(deviceObj, camera);
}
int32_t HCameraService::CreateCameraDevice(std::string cameraId, sptr<ICameraDeviceService> &device){
sptr<HCameraDevice> cameraDevice;
cameraDeviceCallback_ = new CameraDeviceCallback();
// 创建 HCameraDevice
cameraDevice = new HCameraDevice(cameraHostManager_, cameraDeviceCallback_, cameraId);
devices_.insert(std::make_pair(cameraId, cameraDevice));
device = cameraDevice;
return CAMERA_OK;
}
// 把 CameraInput 加入到 CaptureSession 中
int32_t HCaptureSession::AddInput(sptr<ICameraDeviceService> cameraDevice){
sptr<HCameraDevice> localCameraDevice = nullptr;
localCameraDevice = static_cast<HCameraDevice*>(cameraDevice.GetRefPtr());
// 保存在 tempCameraDevices_ 中
tempCameraDevices_.emplace_back(localCameraDevice);
return CAMERA_OK;
}
// CommitConfig 提交配置使其生效的时候会打开 CameraDevice
int32_t HCaptureSession::CommitConfig(){
GetCameraDevice(device);
...
}
int32_t HCaptureSession::GetCameraDevice(sptr<HCameraDevice> &device) {
sptr<HCameraDevice> camDevice;
camDevice = tempCameraDevices_[0]; // 从中取出上一步存储的cameraDevice
// 打开 CameraDevice
rc = camDevice->Open();
// 获取streamOperator,第一次会创建一个streamOperator与cameraDevice进行绑定
// 后面的流程中会再次获取这个streamOperator
rc = camDevice->GetStreamOperator(streamOperatorCallback_, streamOperator);
device = camDevice;
return rc;
}

3、根据CameraDevice获取流操作器 StreamOperator

int32_t HCaptureSession::CommitConfig(){
...
HandleCaptureOuputsConfig(device);
}
int32_t HCaptureSession::HandleCaptureOuputsConfig(sptr<HCameraDevice> &device){
...
rc = CheckAndCommitStreams(device, settings, allStreamInfos, newStreamInfos);
}
int32_t HCaptureSession::CheckAndCommitStreams(sptr<HCameraDevice> &device,
std::shared_ptr<CameraMetadata> &deviceSettings,
std::vector<std::shared_ptr<Camera::StreamInfo>> &allStreamInfos,
std::vector<std::shared_ptr<Camera::StreamInfo>> &newStreamInfos) {
...
return CreateAndCommitStreams(device, deviceSettings, newStreamInfos);
}
int32_t HCaptureSession::CreateAndCommitStreams(sptr<HCameraDevice> &device,
std::shared_ptr<CameraMetadata> &deviceSettings,
std::vector<std::shared_ptr<Camera::StreamInfo>> &streamInfos)
{
...
streamOperator = device->GetStreamOperator(); // 获取StreamOperator
...
streamOperator->CreateStreams(streamInfos); // 通过配置信息 创建流
}

4、通过StreamOperator创建Stream

获取StreamOperator后会接着CreateStreams,这一步主要分析创建Stream之前的参数配置。

int32_t HCaptureSession::CommitConfig(){
HandleCaptureOuputsConfig(device);
}
int32_t HCaptureSession::HandleCaptureOuputsConfig(sptr<HCameraDevice> &device) {
curStreamInfo = std::make_shared<Camera::StreamInfo>();
// 配置流的信息
curStreamCapture->SetStreamInfo(curStreamInfo);
}
// 配置流的信息
void HStreamCapture::SetStreamInfo(std::shared_ptr<Camera::StreamInfo> streamInfoPhoto)
{
int32_t pixelFormat;
#ifdef RK_CAMERA
pixelFormat = PIXEL_FMT_RGBA_8888;
#else
pixelFormat = PIXEL_FMT_YCRCB_420_SP;
#endif
streamInfoPhoto->streamId_ = photoStreamId_;
streamInfoPhoto->width_ = producer_->GetDefaultWidth(); // 分辨率
streamInfoPhoto->height_ = producer_->GetDefaultHeight();
streamInfoPhoto->format_ = pixelFormat; // 像素格式
streamInfoPhoto->datasapce_ = CAMERA_PHOTO_COLOR_SPACE;
streamInfoPhoto->intent_ = Camera::STILL_CAPTURE;
streamInfoPhoto->tunneledMode_ = true;
streamInfoPhoto->bufferQueue_ = producer_;
streamInfoPhoto->encodeType_ = Camera::ENCODE_TYPE_JPEG; // 图片编码格式
}
// 最后走到这个函数创建流
int32_t HCaptureSession::CreateAndCommitStreams(sptr<HCameraDevice> &device,
std::shared_ptr<CameraMetadata> &deviceSettings,
std::vector<std::shared_ptr<Camera::StreamInfo>> &streamInfos)
{
streamOperator = device->GetStreamOperator(); // 获取StreamOperator
...
streamOperator->CreateStreams(streamInfos); // 创建流
}
CamRetCode StreamOperator::CreateStreams(const std::vector<std::shared_ptr<StreamInfo>>& streamInfos){
...
StreamConfiguration scg;
// 把streamInfos转换为scg格式

// 创建 StreamBase
std::shared_ptr<IStream> stream = StreamFactory::Instance().CreateShared(
IStream::g_availableStreamType[it->intent_], it->streamId_, it->intent_, pipelineCore_, messenger_);
// 配置 StreamBase
RetCode rc = stream->ConfigStream(scg);
}

5、通过StreamOperator提交Stream,并设置数据流回调函数

int32_t HCaptureSession::CreateAndCommitStreams(sptr<HCameraDevice> &device,
std::shared_ptr<CameraMetadata> &deviceSettings,
std::vector<std::shared_ptr<Camera::StreamInfo>> &streamInfos)
{
streamOperator = device->GetStreamOperator(); // 获取StreamOperator
...
streamOperator->CreateStreams(streamInfos); // 创建流
...
hdiRc = streamOperator->CommitStreams(Camera::NORMAL, deviceSettings);
}
CamRetCode StreamOperator::CommitStreams(OperationMode mode,
const std::shared_ptr<CameraStandard::CameraMetadata>& modeSetting)
{
...
// 调用 StreamBase类的CommitStream函数
stream->CommitStream();
...
}
// 提交Stream,并设置数据流的回调函数
RetCode StreamBase::CommitStream()
{
...
hostStreamMgr_ = pipelineCore_->GetHostStreamMgr();
// 创建HostStream,并设置数据流的回调函数HandleResult()
RetCode rc = hostStreamMgr_->CreateHostStream(info, [this](std::shared_ptr<IBuffer> buffer) {
HandleResult(buffer);
return;
});
}

6、拍一张照片

CommitConfig中环节中,会给HStreamCapture 传递一个StreamOperator。

// CommitConfig 提交配置
int32_t HCaptureSession::CommitConfig(){
...
HandleCaptureOuputsConfig(device);
}
// 设置 streamOperator 与 HStreamCapture 的绑定关系
int32_t HCaptureSession::HandleCaptureOuputsConfig(sptr<HCameraDevice> &device){
...
streamOperator = device->GetStreamOperator();
curStreamCapture->LinkInput(streamOperator, settings, streamId);
...
}
// 保存streamOperator
int32_t HStreamCapture::LinkInput(sptr<Camera::IStreamOperator> streamOperator,
std::shared_ptr<CameraMetadata> cameraAbility, int32_t streamId){
streamOperator_ = streamOperator;
photoStreamId_ = streamId;
cameraAbility_ = cameraAbility;
return CAMERA_OK;
}

操作streamOperator进行拍照。

int32_t HStreamCapture::Capture(const std::shared_ptr<CameraMetadata> &captureSettings){
// 拍照会调用 streamOperator 的捕获图像函数
rc = streamOperator_->Capture(CurCaptureId, captureInfoPhoto, false);
}

7、采集到的图像数据回调流程

系统通过 V4L2 或者 mpp 采集到的图像数据,通过 SourceNode 等一系列流程,最终到达 CommitStream() 环节中设置的回调函数 StreamBase::HandleResult() 的位置,以下是数据流转到Surface Buffer的过程。

void StreamBase::HandleResult(std::shared_ptr<IBuffer>& buffer){
...
request->AttachBuffer(buffer);
request->OnResult(streamId_);
...
}
RetCode CaptureRequest::OnResult(const int32_t id){
...
return stream->OnFrame(shared_from_this());
}
RetCode StreamBase::OnFrame(const std::shared_ptr<CaptureRequest>& request){
...
ReceiveBuffer(buffer);
...
}
RetCode StreamBase::ReceiveBuffer(std::shared_ptr<IBuffer>& buffer){
...
bufferPool_->ReturnBuffer(buffer);
tunnel_->PutBuffer(buffer);
...
}
RetCode StreamTunnel::PutBuffer(const std::shared_ptr<IBuffer>& buffer){
...
// 数据最终流入到 Surface 的Buffer中
bufferQueue_->FlushBuffer(sb, fence, flushConfig_);
}

数据流入Surface 的BufferQueue中,最终在 FlushBuffer 函数中会调用最上层设置的 surface的数据监听 listener。

GSError BufferQueue::FlushBuffer(int32_t sequence, const BufferExtraData &bedata,
int32_t fence, const BufferFlushConfig &config)
{
...
// 这个回调函数中可以获取BufferQueue中媒体数据并保存成文件
listener_->OnBufferAvailable();
...
}
// 下面是设置surface消费者回调函数的流程
GSError ConsumerSurface::RegisterConsumerListener(sptr<IBufferConsumerListener>& listener)
{
return consumer_->RegisterConsumerListener(listener);
}
GSError BufferQueueConsumer::RegisterConsumerListener(sptr<IBufferConsumerListener>& listener)
{
return bufferQueue_->RegisterConsumerListener(listener);
}
GSError BufferQueue::RegisterConsumerListener(sptr<IBufferConsumerListener> &listener)
{
listener_ = listener;
return GSERROR_OK;
}

总结

本文主要描述了OpenHarmony 多媒体子系统Camera组件从应用层到驱动层的架构,按照拍照的流程,分析了与之相关的两个仓库的主要流程的函数代码。

预览、录像与拍照的区别为前两者捕获的是连续的图像(按固定的帧率间隔,定时捕获1帧数据),拍照仅捕获一帧图像。

在相机框架层中,拍照使用的是HStreamCapture进行单次捕获图像,预览和录像使用HStreamRepeat类进行连续捕获。这两个类最终都是调用驱动层的StreamOperator::Capture()函数,此函数根据参数区分是单次捕获还是连续捕获。

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​​。

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-07-27 14:30:15

分布式数据鸿蒙

2021-09-13 15:15:18

2021-12-17 16:42:09

2022-05-11 15:08:52

驱动开发系统移植

2022-04-20 20:28:40

HDF 驱动框架鸿蒙操作系统

2022-04-21 11:26:31

鸿蒙操作系统

2022-03-04 15:43:36

文件管理模块Harmony鸿蒙

2021-11-08 15:04:47

2022-04-29 14:56:40

通话应用源码剖析

2022-04-06 14:55:45

Harmony同步机制鸿蒙

2021-11-25 09:54:54

2021-11-10 16:10:18

2022-01-06 16:16:21

2022-02-25 15:08:06

DevEco开发OpenHarmon鸿蒙

2022-03-14 15:26:59

Hi3516Ark子系统鸿蒙

2020-11-12 11:50:20

OpenHarmony

2022-07-04 16:26:07

鸿蒙屏幕截图

2021-09-07 15:48:28

2022-04-18 10:47:55

UI框架鸿蒙操作系统

2022-03-29 10:04:44

APIHarmony文件管理
点赞
收藏

51CTO技术栈公众号