本文以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开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。
想了解更多关于开源的内容,请访问:
51CTO 开源基础软件社区
https://ost.51cto.com/#bkwz










