1

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

 2 years ago
source link: https://blog.51cto.com/harmonyos/5370753
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

作者:邵黎明

本文以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 Camera组件架构分析以及拍照流程源码解析_Camera拍照流程

关于上图的说明:

相机框架层概念和流程

框架层把相机功能抽象为会话管理(管理生命周期、参数配置、输入管理、输出管理)、设备输入(设备查询、设备控制、设备监听)和数据输出(元数据输出、流输出和状态控制),其对应的类名依次为CaptureSessionCameraInputCaptureOutput
其中数据输出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组件架构分析以及拍照流程源码解析_Camera组件_02

拍照示例代码

  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
            }
        }
    };
    
  2. 获取相机管理器实例并获取相机对象列表

    sptr<CameraManager> camManagerObj = CameraManager::GetInstance();          // 所有的类都由CameraManager创建
    std::vector<sptr<CameraInfo>> cameraObjList = camManagerObj->GetCameras(); // 如果有多个摄像头会返回多个
    
  3. 使用相机对象创建相机输入来打开相机

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

    sptr<CaptureSession> captureSession = camManagerObj->CreateCaptureSession();
    
  5. 开始配置采集会话

    int32_t result = captureSession->BeginConfig(); // 初始化会话
    
  6. 将相机输入添加到采集会话

    result = captureSession->AddInput(cameraInput);
    
  7. 创建消费者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); // 注册回调函数
    
  8. 使用上面创建的 Surface 创建拍照输出

    sptr<CaptureOutput> photoOutput = camManagerObj->CreatePhotoOutput(photoSurface);
    
  9. 将拍照输出添加到采集会话

    result = captureSession->AddOutput(photoOutput);
    
  10. 将配置提交到采集会话。

    result = captureSession->CommitConfig(); // 把Input和Output做关联等
    
  11. 拍摄照片。

    result = ((sptr<PhotoOutput> &)photoOutput)->Capture(); // 调用最底层的数据接口采集图像
    
  12. 释放采集会话资源。

    captureSession->Release();
    
  13. 释放相机输入关闭相机。

    cameraInput->Release();
    

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

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK