[文章]OpenHarmony相机用户态驱动框架

阅读量0
0
4
作者:润和软件 郭新星
相机作为智能手机上少有的成长空间不错的,能够做出差异化的功能,每年都能成为各大Android手机厂商争相宣传的亮点。众所周知Android采用Linux 作为其内核,而Linux采用的开源协议具有传染性[1],导致Android HAL[2]成为了手机厂商们竞争的重要战场。随着OpenHarmony 3.1[3]的发布,相机模块也逐渐完善起来,目前提供了基础预览和拍照的能力。OpenHarmony中,相机用户态驱动框架承担了和Android Camera HAL一样的角色,这部分位于OpenHarmony的HDF[4]中,对上实现相机HDI[5]接口,对下实现相机Pipeline模型,管理相机各个硬件设备。

相机用户态驱动框架(下图的CameraHost 部分)总体可以分为三层,HDI实现层,实现相机标准南向接口;框架层,对接HDI实现层的控制、流的转发,实现数据通路的搭建、管理相机各个硬件设备等功能;适配层,屏蔽底层芯片和OS差异,支持多平台适配。

模块介绍
HDI Implementation:对上实现HDI接口,向下调用框架层的接口,完成HDI接口任务的转发。
Buffer Manager : 屏蔽不同内存管理的差异,为子系统提供统一的操作接口,同时提供buffer轮转的功能。
Pipeline Core :解析HCS配置完成pipeline的搭建,调度pipeline中的各个node完成流的处理
Device Manager:通过调用底层硬件适配层接口,实现查询控制底层设备、枚举监听底层设备的功能
Platform Adaption :屏蔽硬件差异,为Device Manager提供统一的操作底层硬件的能力
•目录结构
  1. Shell
  2. drivers/peripheral/camera

  3. |-- README_zh.md

  4. |-- bundle.json

  5. |-- figures

  6. |   `-- logic-view-of-modules-related-to-this-repository_zh.png

  7. |-- hal

  8. |   |-- BUILD.gn

  9. |   |-- adapter

  10. |   |-- buffer_manager

  11. |   |-- camera.gni

  12. |   |-- device_manager

  13. |   |-- hdi_impl

  14. |   |-- include

  15. |   |-- init

  16. |   |-- pipeline_core

  17. |   |-- test

  18. |   `-- utils

  19. |-- hal_c

  20. |   |-- BUILD.gn

  21. |   |-- camera.gni

  22. |   |-- hdi_cif

  23. |   `-- include

  24. `-- interfaces

  25.     |-- hdi_ipc

  26.     |-- hdi_passthrough

  27.     `-- include

复制代码
HDI Implementation中的预览流程
接下来我们通过已经发布的OpenHarmony 3.1开源代码,来看看预览是怎么完成的吧
drivers/peripheral/camera/hal/test/v4l2/src /preview_test.cpp存放了针对v4l2的预览测试代码,入口如下
  1. C++
  2. TEST_F(UtestPreviewTest, camera_preview_0001)

  3. {

  4.     std::cout << "==========[test log] Preview stream, expected success." << std::endl;

  5.     // Get the stream manager

  6.     display_->AchieveStreamOperator(); // 获取stream operator

  7.     // start stream

  8.     display_->intents = {Camera::PREVIEW}; // 预览流

  9.     display_->StartStream(display_->intents); // 起流

  10.     // Get preview

  11.     display_->StartCapture(display_->streamId_preview, display_->captureId_preview, false, true);

  12.     // release stream

  13.     display_->captureIds = {display_->captureId_preview};

  14.     display_->streamIds = {display_->streamId_preview};

  15.     display_->StopStream(display_->captureIds, display_->streamIds);

  16. }

复制代码
先获取stream operator实例

  1. C++
  2. void testdisplay::achievestreamoperator()

  3. {

  4.     // create and get streamoperator information

  5.     std::shared_ptr<ohos::camera::istreamoperatorcallback> streamoperatorcallback =

  6.         std::make_shared<ohos::camera::istreamoperatorcallback>();

  7.     rc = cameradevice->getstreamoperator(streamoperatorcallback, streamoperator);

  8.                                         // ........

  9. }

复制代码
通过前文的streamOperator创建流

  1. C++
  2. void TestDisplay::StartStream(std::vector<OHOS::Camera::StreamIntent> intents)

  3. {

  4.     // ..............................

  5.     for (auto& intent : intents) {

  6.         if (intent == 0) {

  7.             std::shared_ptr<IBufferProducer> producer = IBufferProducer::CreateBufferQueue();

  8.             producer->SetQueueSize(8); // 创建buffer的生产端,并和相应的流进行绑定

  9.             auto callback = [this](std::shared_ptr<SurfaceBuffer> Prebuffer) {

  10.                 BufferCallback(Prebuffer, preview_mode);

  11.                 return;

  12.             };

  13.             producer->SetCallback(callback);

  14.             streamInfo->streamId_ = streamId_preview;

  15.             streamInfo->width_ = 640; // 640:picture width

  16.             streamInfo->height_ = 480; // 480:picture height

  17.             streamInfo->format_ = CAMERA_FORMAT_YUYV_422_PKG;

  18.             streamInfo->datasapce_ = 8; // 8:picture datasapce

  19.             streamInfo->intent_ = intent;

  20.             streamInfo->tunneledMode_ = 5; // 5:tunnel mode

  21.             streamInfo->bufferQueue_ = producer;

  22.             streamInfos.push_back(streamInfo);

  23.         } else if (intent == 1) {

  24.       // .......................

  25.     }

  26.     rc = streamOperator->CreateStreams(streamInfos); // 创建流

  27.     // ................................

  28.     rc = streamOperator->CommitStreams(Camera::NORMAL, ability); // 提交流

  29.     // .................................

  30. }

复制代码
下面我们正式进入到hal的源代码中看看是怎么创建流的吧

  1. C++
  2. CamRetCode StreamOperator::CreateStreams(const std::vector<std::shared_ptr<StreamInfo>>& streamInfos)

  3. {

  4. // .....

  5.     for (auto it : streamInfos) {

  6. //....

  7.         std::shared_ptr<IStream> stream = StreamFactory::Instance().CreateShared(

  8.             IStream::g_availableStreamType[it->intent_], it->streamId_, it->intent_, pipelineCore_, messenger_); // 创建流实例

  9. // ...

  10.         StreamConfiguration scg;

  11.         scg.id = it->streamId_;

  12.         scg.type = it->intent_;

  13.         scg.width = it->width_;

  14.         scg.height = it->height_;

  15.         PixelFormat pf = static_cast<PixelFormat>(it->format_);

  16.         scg.format = BufferAdapter::PixelFormatToCameraFormat(pf);

  17.         scg.dataspace = it->datasapce_;

  18.         scg.tunnelMode = it->tunneledMode_;

  19.         scg.minFrameDuration = it->minFrameDuration_;

  20.         scg.encodeType = it->encodeType_;



  21.         RetCode rc = stream->ConfigStream(scg); // 依据上文的流信息配置流

  22. // ...

  23.         if (it->bufferQueue_ != nullptr) {  // 绑定前文的生产端

  24.             auto tunnel = std::make_shared<StreamTunnel>();

  25.             CHECK_IF_PTR_NULL_RETURN_VALUE(tunnel, INSUFFICIENT_RESOURCES);

  26.             RetCode rc = tunnel->AttachBufferQueue(it->bufferQueue_);

  27.             CHECK_IF_NOT_EQUAL_RETURN_VALUE(rc, RC_OK, INVALID_ARGUMENT);

  28.             if (stream->AttachStreamTunnel(tunnel) != RC_OK) {

  29.                 CAMERA_LOGE("attach buffer queue to stream [id = %{public}d] failed", it->streamId_);

  30.                 return INVALID_ARGUMENT;

  31.             }

  32.         }

  33.         {

  34.             std::lock_guard<std::mutex> l(streamLock_);

  35.             streamMap_[stream->GetStreamId()] = stream; // 保存流实例

  36.         }

  37. // ...

  38. }

复制代码

  1. C++
  2. RetCode StreamBase::CommitStream()

  3. {

  4. // ...

  5.     hostStreamMgr_ = pipelineCore_->GetHostStreamMgr(); //从pipelinecore获取hoststreamanager

  6.     CHECK_IF_PTR_NULL_RETURN_VALUE(hostStreamMgr_, RC_ERROR);

  7. // ...

  8.         info.bufferPoolId_ = poolId_;

  9.         info.bufferCount_ = GetBufferCount();

  10.     //  初始化 bufferpool

  11.         RetCode rc = bufferPool_->Init(streamConfig_.width, streamConfig_.height, streamConfig_.usage,

  12.                                        streamConfig_.format, GetBufferCount(), CAMERA_BUFFER_SOURCE_TYPE_EXTERNAL);

  13.         if (rc != RC_OK) {

  14.             CAMERA_LOGE("stream [id:%{public}d] initialize buffer pool failed.", streamId_);

  15.             return RC_ERROR;

  16.         }

  17.     }

  18. // stream传递到pipelinecore 并进行绑定

  19.     RetCode rc = hostStreamMgr_->CreateHostStream(info, [this](std::shared_ptr<IBuffer> buffer) {

  20.         HandleResult(buffer);

  21.         return;

  22.     });

  23. // ....

  24.     return RC_OK;

  25. }

复制代码
CreateStream 和CommitStream结束之后便是Capture,这里包含了起流的动作,关键实现如下

  1. C++
  2. CamRetCode StreamOperator::Capture(int captureId, const std::shared_ptr<CaptureInfo>& captureInfo, bool isStreaming)

  3. {

  4. // ...

  5. //  captureId 捕获请求的id; captureInfo 预览/拍照/录像的参数;isStreaming 连续捕获还是单次捕获(拍照)

  6.     CaptureSetting setting = captureInfo->captureSetting_;

  7.     auto request =

  8.         std::make_shared<CaptureRequest>(captureId, captureInfo->streamIds_.size(), setting,

  9.                                          captureInfo->enableShutterCallback_, isStreaming);

  10.     for (auto id : captureInfo->streamIds_) {

  11.         // 创建捕获请求,并传递给前文创建的流

  12.         RetCode rc = streamMap_[id]->AddRequest(request);

  13.         if (rc != RC_OK) {

  14.             return DEVICE_ERROR;

  15.         }

  16.     }

  17. // ...

  18. }

复制代码

  1. C++
  2. CamRetCode StreamOperator::Capture(int captureId, const std::shared_ptr<CaptureInfo>& captureInfo, bool isStreaming)

  3. {

  4. // ...

  5. //  captureId 捕获请求的id; captureInfo 预览/拍照/录像的参数;isStreaming 连续捕获还是单次捕获(拍照)

  6.     CaptureSetting setting = captureInfo->captureSetting_;

  7.     auto request =

  8.         std::make_shared<CaptureRequest>(captureId, captureInfo->streamIds_.size(), setting,

  9.                                          captureInfo->enableShutterCallback_, isStreaming);

  10.     for (auto id : captureInfo->streamIds_) {

  11.         // 创建捕获请求,并传递给前文创建的流

  12.         RetCode rc = streamMap_[id]->AddRequest(request);

  13.         if (rc != RC_OK) {

  14.             return DEVICE_ERROR;

  15.         }

  16.     }

  17. // ...

  18. }

复制代码
从上面的代码可知预览、拍照、录像都是通过捕获请求触发,单次拍照则为单次捕获请求,预览和录像则是连续捕获请求。

  1. C++
  2. RetCode StreamBase::AddRequest(std::shared_ptr<CaptureRequest>& request)

  3. {

  4.     CHECK_IF_PTR_NULL_RETURN_VALUE(request, RC_ERROR);

  5.     request->AddOwner(shared_from_this());



  6.     request->SetFirstRequest(false);

  7.     if (isFirstRequest) {

  8.         RetCode rc = StartStream(); // 起流

  9.         if (rc != RC_OK) {

  10.             CAMERA_LOGE("start stream [id:%{public}d] failed", streamId_);

  11.             return RC_ERROR;

  12.         }

  13.         request->SetFirstRequest(true);

  14.         isFirstRequest = false;

  15.     }



  16.     {

  17.         std::unique_lock<std::mutex> l(wtLock_);

  18.         waitingList_.emplace_back(request); // 捕获请求添加到waitingList

  19.         cv_.notify_one();

  20.     }



  21.     return RC_OK;

  22. }

复制代码
看看StreamStream是怎么实现的吧

  1. C++
  2. RetCode StreamBase::StartStream()

  3. {

  4. // ...

  5.     RetCode rc = pipeline_->Prepare({streamId_}); //  pipeline先完成一些准备工作

  6. // ...



  7.     state_ = STREAM_STATE_BUSY;

  8.     std::string threadName =

  9.         g_availableStreamType[static_cast<StreamIntent>(streamType_)] + "#" + std::to_string(streamId_);

  10.     handler_ = std::make_unique<std::thread>([this, &threadName] {// 创建轮转线程

  11.         prctl(PR_SET_NAME, threadName.c_str());

  12.         while (state_ == STREAM_STATE_BUSY) {

  13.             HandleRequest(); // 处理捕获请求

  14.         }

  15.     });

  16. // ...

  17.     rc = pipeline_->Start({streamId_}); // 通知pipeline和底层硬件可以开始出帧了

  18. // ...

  19.     return RC_OK;

  20. }

复制代码
  1. C++
  2. void StreamBase::HandleRequest()

  3. {

  4.     // 如果有 捕获请求下发,则退出等待状态

  5.     if (waitingList_.empty()) {

  6.         std::unique_lock<std::mutex> l(wtLock_);

  7.         if (waitingList_.empty()) {

  8.             cv_.wait(l, [this] { return !(state_ == STREAM_STATE_BUSY && waitingList_.empty()); });

  9.         }

  10.     }

  11. // ...

  12.         request = waitingList_.front();

  13.         CHECK_IF_PTR_NULL_RETURN_VOID(request);

  14.         if (!request->IsContinous()) { // 如果是连续捕获,则保留一份拷贝在waitinglist

  15.             waitingList_.pop_front();

  16.         }

  17.     }

  18. // 处理捕获请求

  19.     request->Process(streamId_);// 最终调用下面的Capture接口



  20.     return;

  21. }

复制代码
  1. C++
  2. RetCode StreamBase::Capture(const std::shared_ptr<CaptureRequest>& request)

  3. {

  4.     CHECK_IF_PTR_NULL_RETURN_VALUE(request, RC_ERROR);

  5.     CHECK_IF_PTR_NULL_RETURN_VALUE(pipeline_, RC_ERROR);



  6.     RetCode rc = RC_ERROR;

  7.     if (request->IsFirstOne() && !request->IsContinous()) {

  8.         uint32_t n = GetBufferCount();

  9.         for (uint32_t i = 0; i < n; i++) {

  10.             DeliverBuffer();// 单次捕获一次性下发所有的buffer

  11.         }

  12.     } else {

  13.         do {

  14.             rc = DeliverBuffer();// 连续捕获每次下发一个buffer

  15.         } while (rc != RC_OK && state_ == STREAM_STATE_BUSY);

  16.     }



  17.     if (request->NeedCancel()) {// 被取消的捕获则退出

  18.         CAMERA_LOGE("StreamBase::Capture stream [id:%{public}d] request->NeedCancel", streamId_);

  19.         return RC_OK;

  20.     }



  21.     rc = pipeline_->Config({streamId_}, request->GetCaptureSetting());// 通知pipeline配置

  22.     if (rc != RC_OK) {

  23.         CAMERA_LOGE("stream [id:%{public}d] config pipeline failed.", streamId_);

  24.         return RC_ERROR;

  25.     }



  26.     rc = pipeline_->Capture({streamId_}, request->GetCaptureId());//  这里的capture指的是pipeline中的source node开始回buffer



  27.     {

  28.         std::unique_lock<std::mutex> l(tsLock_);

  29.         inTransitList_.emplace_back(request);// 处理过的捕获请求存放在inTransitList

  30.     }



  31.     return RC_OK;

  32. }

复制代码
到这起流的流程就结束了,pipeline回上来的帧通过OnFrame接口处理

  1. C++
  2. RetCode StreamBase::OnFrame(const std::shared_ptr<CaptureRequest>& request)

  3. {

  4. // ...



  5.     bool isEnded = false;

  6.     if (!request->IsContinous()) {

  7.         isEnded = true;

  8.     } else if (request->NeedCancel()) {

  9.         isEnded = true;

  10.     }



  11.     {

  12.         // inTransitList_ may has multiple copies of continious-capture request, we just need erase one of them.

  13.         std::unique_lock<std::mutex> l(tsLock_);

  14.         for (auto it = inTransitList_.begin(); it != inTransitList_.end(); it++) {

  15.             if ((*it) == request) {

  16.                 inTransitList_.erase(it);// 已经回帧的请求,从inTransitList删除

  17.                 break;

  18.             }

  19.         }



  20.         if (isEnded) {

  21.             // if this is the last request of capture, send CaptureEndedMessage.

  22.             auto it = std::find(inTransitList_.begin(), inTransitList_.end(), request);

  23.             if (it == inTransitList_.end()) {

  24.                 std::shared_ptr<ICaptureMessage> endMessage =

  25.                     std::make_shared<CaptureEndedMessage>(streamId_, request->GetCaptureId(), request->GetEndTime(),

  26.                                                           request->GetOwnerCount(), tunnel_->GetFrameCount());

  27.                 CAMERA_LOGV("end of stream [%d], ready to send end message, capture id = %d",

  28.                     streamId_, request->GetCaptureId());

  29.                 messenger_->SendMessage(endMessage);

  30.                 pipeline_->CancelCapture({streamId_});// 如果此次捕获结束,则取消捕获

  31.             }

  32.         }

  33.     }
  34.     ReceiveBuffer(buffer);// 底层返回的buffer送还到生产端,最终帧数据送到消费端

  35.     return RC_OK;

  36. }

复制代码
附录


  • linux和Android的关系 - 知乎 (zhihu.com) ↩︎
  • HAL Subsystem | Android Open Source Project (google.cn) ↩︎
  • zh-cn/release-notes/OpenHarmony-v3.1-release.md · OpenHarmony/docs - Gitee.com ↩︎
  • OpenHarmony HDF 驱动框架介绍和驱动加载过程分析-OpenHarmony技术社区-↩︎
  • OpenHarmony HDF HDI基础能力分析与使用- ↩︎


回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友