当前位置: 首页 > 科技观察

OH-v3.0-LTSCameraDriverFramework(L2)Analysis5-ImageAcquisition

时间:2023-03-13 14:01:26 科技观察

更多内容请访问:与华为官方共建的鸿蒙技术社区https://harmonyos.51cto.com应用层代码mainDemo->CaptureON(STREAM_ID_PREVIEW,CAPTURE_ID_PREVIEW,CAPTURE_PREVIEW);RetCodeHos3516Demo::CaptureON(constintstreamId,constintcaptureId,CaptureModemode){CAMERA_LOGD("demoON测试:Cap进入模式==%{public}d",mode);std::shared_ptrcaptureInfo=std::make_shared();captureInfo->streamIds_={streamId};captureInfo->captureSetting_=ability_;captureInfo->enableShutterCallback_=false;intrc=streamOperator_->Capture(captureId,captureInfo,true);......CAMERA_LOGD("演示测试:CaptureON退出");返回RC_OK;}一个。StreamOperator::Capture创建CaptureRequest请求,调用AddRequest将请求添加到对应的Stream//drivers/peripheral/camera/hal/hdi_impl/src/stream_operator/stream_operator.cppCamRetCodeStreamOperator::Capture(intcaptureId,conststd::shared_ptr&captureInfo,boolisStreaming){...CaptureSettingsetting=captureInfo->captureSetting_;自动请求=std::make_shared(captureId,captureInfo->streamIds_.size(),setting,captureInfo->enableShutterCallback_,isStreaming);for(autoid:captureInfo->streamIds_){RetCoderc=streamMap_[id]->AddRequest(request);如果(rc!=RC_OK){返回DEVICE_ERROR;}}...returnNO_ERROR;}StreamBase::AddRequest()在第一次请求时先调用StartStream()来启动“流”//drivers/peripheral/camera/hal/hdi_impl/src/stream_operator/stream_base.cppRetCodeStreamBase::AddRequest(std::shared_ptr&request){......if(isFirstRequest){......RetCoderc=StartStream();if(rc!=RC_OK){CAMERA_LOGE("启动流[id:%{public}d]失败",streamId_);关于转RC_ERROR;}请求->SetFirstRequest(true);isFirstRequest=false;}{std::unique_lockl(wtLock_);waitingList_.emplace_back(请求);cv_.notify_one();}返回RC_OK;}1。pipeline_->Prepare和Start会分别调用之前创建的流节点列表中每个Node的init和Start接口2.创建线程处理HandleRequest()//drivers/peripheral/camera/hal/hdi_impl/src/stream_operator/stream_base.cppRetCodeStreamBase::StartStream(){...RetCoderc=pipeline_->Prepare({streamId_});...state_=STREAM_STATE_BUSY;std::stringthreadName=g_avaliableStreamType[static_cast(streamType_)]+"#"+std::to_string(streamId_);handler_=std::make_unique([this,&threadName]{prctl(PR_SET_NAME,threadName.c_str());while(state_==STREAM_STATE_BUSY){HandleRequest();}});rc=pipeline_->Start({streamId_});...返回rnRC_OK;}StartSteam()会在完成必要的启动过程后,通过HandleRequest()线程继续处理当前Request的Process()函数。CaptureRequest的Process启动下面StramBase的Capture()1,pipeline_->Config和Capture会分别调用之前创建的流节点列表中每个Node的Config和Capture接口2,SendMessage负责发送启动消息3给应用程序层,最后启动缓冲区分配函数DeliverBuffer(),开始图像数据的传输。//drivers/peripheral/camera/hal/hdi_impl/src/stream_operator/stream_base.cppRetCodeStreamBase::Capture(conststd::shared_ptr&request){...rc=pipeline_->Config({streamId_},请求->GetCaptureSetting());...rc=pipeline_->Capture({streamId_},request->GetCaptureId());...if(request->IsFirstOne()){if(messenger_==nullptr){CAMERA_LOGE("stream[id:%{public}d]无法发送消息,messenger_为null",streamId_);返回RC_ERROR;}std::shared_ptrstartMessage=std::make_shared(streamId_,request->GetCaptureId(),request->GetBeginTime(),request->GetOwnerCount());messenger_->SendMessage(startMessage);请求->SetFirstRequest(false);}//DeliverBuffer必须在Capture之后调用,否则这个捕获请求将错过一个缓冲区。做{rc=DeliverBuffer();}while(rc!=RC_OK&&state_==STREAM_STATE_BUSY);returnRC_OK;}至此,Capture请求处理完毕,剩下的就是Stream负责让数据流在Node中旋转,并通过Surface图形缓冲区接口向应用层传递数据。整个数据轮换涉及多个线程。下面将对关键代码进行整理。对细节感兴趣的同学可以自行深入。2、Buffer的申请和传递StreamBase::DeliverBuffer()tunnel_->GetBuffer()会向生产Surface申请一个Buffer,并创建一个SurfaceBuffer对应的CameraBuffer(IBuffer类型)。检索到的IBuffer加入到bufferPool缓冲池中进行管理。RetCodeStreamBase::DeliverBuffer(){CHECK_IF_PTR_NULL_RETURN_VALUE(tunnel_,RC_ERROR);CHECK_IF_PTR_NULL_RETURN_VALUE(bufferPool_,RC_ERROR);std::shared_ptrbuffer=tunnel_->GetBuffer();CHECK_IF_PTR_NULL_RETURN_VALUE(缓冲区,RC_ERROR);buffer->SetEncodeType(streamConfig_.encodeType);缓冲区->SetStreamId(streamId_);bufferPool_->AddBuffer(缓冲区);CAMERA_LOGI("stream[id:%{public}d]enqueuebufferindex:%{public}d",streamId_,buffer->GetIndex());returnRC_OK;}bufferPool中有有效的buffer后,会将IBuffer转为frameSpec格式发送给SDKnode->ProvideBuffers(frameSpec),Vpss节点上也有实现!//drivers/peripheral/camera/hal/pipeline_core/nodes/src/source_node/source_node.cppvoidSourceNode::PortHandler::CollectBuffers(){CHECK_IF_PTR_NULL_RETURN_VOID(池);std::shared_ptrbuffer=pool->AcquireBuffer(-1);CHECK_IF_PTR_NULL_RETURN_VOID(池);缓冲);PortFormat格式={};端口->GetFor垫子(格式);std::shared_ptrframeSpec=std::make_shared();frameSpec->bufferPoolId_=format.bufferPoolId_;frameSpec->bufferCount_=format.bufferCount_;frameSpec->buffer_=缓冲区;自动节点=端口->GetNode();CHECK_IF_PTR_NULL_RETURN_VOID(节点);RetCoderc=node->ProvideBuffers(frameSpec);if(rc==RC_ERROR){CAMERA_LOGE("提供缓冲区失败。");}}//drivers/peripheral/camera/hal/adapter/chipset/hispark_taurus/src/pipeline_core/nodes/vpss_node/vpss_node.cppRetCodeVpssNode::ProvideBuffers(std::shared_ptrframeSpec){if(deviceManager_->SendFrameBuffer(frameSpec)==RC_OK){返回RC_OK;}CAMERA_LOGE("提供缓冲区失败。");returnRC_ERROR;}3.两个重要的Node回调接口3.1VpssNode::SetBufferCallback()前面pipeline_->Start()中一个重要的节点是VpssNode(VpssNode是-一个SourceNode)这里又启动了另外两个线程来收集Buffer并分发缓冲。另见SetBufferCallback()Hi3516底层SDK设置重要回调接口接口最后调用的是SourceNode::PortHandler::OnBuffer函数。//drivers/peripheral/camera/hal/pipeline_core/nodes/src/source_node/source_node.cppRetCodeSourceNode::Start(constint32_tstreamId){...SetBufferCallback();...RetCoderc=handler_[streamId]->StartCollectBuffers();CHECK_IF_NOT_EQUAL_RETURN_VALUE(rc,RC_OK,RC_ERROR);rc=handler_[streamId]->StartDistributeBuffers();CHECK_IF_NOT_EQUAL_RETURN_VALUE(rc,RC_OK,RC_ERROR);芯片组/hispark_taurus/src/pipeline_core/nodes/vpss_node/vpss_node.cppvoidVpssNode::SetBufferCallback(){deviceManager_->SetNodeCallBack([&](std::shared_ptrframeSpec){OnPackBuffer(frameSpec);});return;}3.2SinkNode::SetCallBack()另一个重要的Node是ViNode(ViNode是一个SinkNode)。这个回调的设置要追溯到前面几章的代码。从下面的代码片段中,您可以看到调用了SinkNode的CallBack()。它是函数HandleResult()。//drivers\peripheral\camera\hal\hdi_impl\src\stream_operator\stream_base.cppRetCodeStreamBase::CommitStream(){...RetCoderc=hostStreamMgr_->CreateHostStream(info,[this](std::shared_ptr缓冲区){HandleResult(缓冲区);返回;});...}std::shared_ptrStreamPipelineBuilder::Build(conststd::shared_ptr&pipelineSpec){...std::optionaltypeId=GetTypeId(it.type_,G_STREAM_TABLE_PTR,G_STREAM_TABLE_SIZE);如果(typeId){newNode->SetCallBack(hostStreamMgr_->GetBufferCb(it.streamId_));}...}4.摄像头拍摄时上传摄像头图像对于一帧数据,SDK会通过VpssNode的回调通知CameraHDI层。这个时候我们就来到了上面VpssNode::SetBufferCallback()设置的SourceNode::PortHandler::OnBuffer()函数。OnBuffer通知分发线程将接收到的buffers数据分发给实现了DeliverBuffer()接口的Node。本示例代码中SinkNode的DeliverBuffer()//drivers/peripheral/camera/hal/pipeline_core/nodes/src/sink_node/sink_node.cppvoidSinkNode::DeliverBuffer(std::shared_ptr&buffer){cb_(buffer);return;}SinkNode的回调StreamBase::HandleResult()最终会来到StreamBase::OnFrame()调用函数StreamBase::ReceiveBuffer();ReceiveBuffer先将缓冲区返回到bufferPool缓冲池,然后调用StreamTunnel::PutBuffer//drivers/peripheral/camera/hal/hdi_impl/src/stream_operator/stream_base.cppRetCodeStreamBase::ReceiveBuffer(std::shared_ptr&buffer){CHECK_IF_PTR_NULL_RETURN_VALUE(缓冲区,RC_ERROR);CHECK_IF_PTR_NULL_RETURN_VALUE(tunnel_,RC_ERROR);CHECK_IF_PTR_NULL_RETURN_VALUE(bufferPool_,RC_ERROR);bufferPool_->ReturnBuffer(缓冲区);隧道_->PutBuffer(缓冲区);returnRC_OK;}StreamTunnel会调用Surface的FlushBuffer接口返回一个生成的SurfaceBuffer对象,并携带一些信息。这里上层应用的consumerSurface可以拿到这张图片的数据。RetCodeStreamTunnel::PutBuffer(conststd::shared_ptr&buffer){...if(buffer->GetBufferStatus()==CAMERA_BUFFER_STATUS_OK){int32_tfence=0;EsFrmaeInfoesInfo=buffer->GetEsFrameInfo();如果(esInfo.size!=-1&&esInfo.timestamp!=-1){sb->ExtraSet("dataSize",esInfo.size);sb->ExtraSet("isKeyFrame",esInfo.isKey);sb->ExtraSet("timeStamp",esInfo.timestamp);sb->ExtraSet("frameNum",esInfo.frameNum);}bufferQueue_->FlushBuffer(sb,fence,flushConfig_);帧数_++;}else{bufferQueue_->CancelBuffer(sb);}...returnRC_OK;}5.总结整个图像采集上发送的代码涉及到很多线程之间的协作,代码阅读难度比较大,所以需要多点耐心,抓住几个重要的节点进行梳理代码。一个有效的pipeLine应该包含一个SourceNode(VpssNode)和一个SinkNode(VoNode)。本章结束后介绍了cameraHDI层的整体框架和工作流程。感谢耐心阅读系列章节的同学们。更多信息请访问:与华为官方共建的鸿蒙技术社区https://harmonyos.51cto.com