OpenHarmony Camera组件架构分析以及拍照流程源码解析 原创 精华
作者:邵黎明
本文以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 hal层HDI接口使用样例实现
│ ├── pipeline_core # camera hal层pipeline核心代码
│ ├── 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 / multimedia_camera_standard,紫色部分代码在 OpenHarmony / drivers_peripheral / camera。
- 框中文字为其主要的C++类的名称。
- 相机组件整个流程中存在两次IPC调用,代码中IPC客户端类名为proxy,IPC服务端接口类名为stub。
相机框架层概念和流程
框架层把相机功能抽象为会话管理(管理生命周期、参数配置、输入管理、输出管理)、设备输入(设备查询、设备控制、设备监听)和数据输出(元数据输出、流输出和状态控制),其对应的类名依次为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();
其中创建输入需要指定摄像头设备,创建输出需要配置输出到什么地方。
相机框架层拍照流程解析
上节使用伪代码阐述了相机功能的主要流程,这一节详细讲一讲拍照这一操作在框架层的实际代码实现。
拍照流程图
拍照代码流程顺序如下图所示
拍照示例代码
-
创建缓冲区消费者端监听器(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();
驱动框架层拍照流程源码解析
下图是驱动框架层层次结构图
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()
函数,此函数根据参数区分是单次捕获还是连续捕获。
更多原创内容请关注:深开鸿技术团队
入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。
感谢老师详细的讲解
不错不错,自己顶^_^
有camera设备驱动的skeleton驱动示例吗,如ISP、image sensor需要实现哪些接口,这部分接口的基本功能等