Android 5.0 Camera系統原始碼分析(4):Camera預覽流程資料流
1. 前言
上一篇講了怎麼讓Camera進入預覽模式,提到了DisplayClient負責顯示影象資料,而CamAdapter負責提供影象資料,這裡主要記錄了CamAdapter怎麼獲取影象,然後DisplayClient怎麼將影象顯示在螢幕上。
2. DisplayClient
上一篇提到在setPreviewWindow的時候會構造並初始化DisplayClient,之前沒有仔細分析,現在來看看
bool
DisplayClient::
init()
{
bool ret = false;
ret = createDisplayThread()
&& createImgBufQueue();
return ret;
}
建立了一個顯示執行緒和一個ImgBuf佇列,看下這兩個函式的具體實現
bool
DisplayClient::
createDisplayThread()
{
bool ret = false;
status_t status = OK;
mpDisplayThread = IDisplayThread::createInstance(this);
if ( mpDisplayThread == 0 || OK != (status = mpDisplayThread->run()) )
{
... ...
}
ret = true;
lbExit:
return ret;
}
bool
DisplayClient::
createImgBufQueue()
{
bool ret = false;
mpImgBufQueue = new ImgBufQueue(IImgBufProvider::eID_DISPLAY, "[email protected]");
if ( mpImgBufQueue == 0 )
{
MY_LOGE("Fail to new ImgBufQueue" );
goto lbExit;
}
......
ret = true;
lbExit:
MY_LOGD("-");
return ret;
}
ImgBufQueue暫時放一邊,createDisplayThread建立了DisplayThread,作為執行緒關注的重點當然是threadLoop,所以接著看DisplayThread的threadLoop函式
bool
DisplayThread::
threadLoop()
{
Command cmd;
if ( getCommand(cmd) )
{
switch (cmd.eId)
{
case Command::eID_EXIT:
MY_LOGD("Command::%s", cmd.name());
break;
case Command::eID_WAKEUP:
default:
if ( mpThreadHandler != 0 )
{
mpThreadHandler->onThreadLoop(cmd);
}
break;
}
}
return true;
}
DisplayThread將接收WAKEUP命令,然後做出響應。那麼由誰來發這個WAKEUP命令呢,就在上一篇提到的enableDisplayClient函式裡面傳送。這裡的mpThreadHandler 指的是DisplayClient,也就是在接收到WAKEUP命令後,將回調DisplayClient的onThreadLoop函式
bool
DisplayClient::
onThreadLoop(Command const& rCmd)
{
// (0) lock Processor.
sp<IImgBufQueue> pImgBufQueue;
{
Mutex::Autolock _l(mModuleMtx);
pImgBufQueue = mpImgBufQueue;
if ( pImgBufQueue == 0 || ! isDisplayEnabled() )
{
MY_LOGW("pImgBufQueue.get(%p), isDisplayEnabled(%d)", pImgBufQueue.get(), isDisplayEnabled());
return true;
}
}
// (1) Prepare all TODO buffers.
if ( ! prepareAllTodoBuffers(pImgBufQueue) )
{
return true;
}
// (2) Start
if ( ! pImgBufQueue->startProcessor() )
{
return true;
}
// (3) Do until disabled.
while(1)
{
// (.1)
waitAndHandleReturnBuffers(pImgBufQueue);
// (.2) break if disabled.
if ( ! isDisplayEnabled() )
{
MY_LOGI("Display disabled");
break;
}
// (.3) re-prepare all TODO buffers, if possible,
// since some DONE/CANCEL buffers return.
prepareAllTodoBuffers(pImgBufQueue);
}
......
return true;
}
先分析步驟(1)準備好接收資料的buffers
/******************************************************************************
* dequePrvOps() -> enqueProcessor() & enque Buf List
*******************************************************************************/
bool
DisplayClient::
prepareAllTodoBuffers(sp<IImgBufQueue>const& rpBufQueue)
{
bool ret = false;
while ( mStreamBufList.size() < (size_t)mi4MaxImgBufCount )
{
if ( ! prepareOneTodoBuffer(rpBufQueue) )
{
break;
}
}
return ret;
}
bool
DisplayClient::
prepareOneTodoBuffer(sp<IImgBufQueue>const& rpBufQueue)
{
bool ret = false;
......
// (2) deque it from PrvOps
sp<StreamImgBuf> pStreamImgBuf;
if ( ! dequePrvOps(pStreamImgBuf) )
{
goto lbExit;
}
// (3) enque it into Processor
ret = rpBufQueue->enqueProcessor(
ImgBufQueNode(pStreamImgBuf, ImgBufQueNode::eSTATUS_TODO)
);
// (4) enque it into List & increment the list size.
mStreamBufList.push_back(pStreamImgBuf);
ret = true;
lbExit:
MY_LOGD_IF((2<=miLogLevel), "- ret(%d)", ret);
return ret;
}
這裡的ImgBufQueue就是DisplayClient初始化的時候建立的那個ImgBufQueue,裡有兩個Buf佇列,mTodoImgBufQue和mDoneImgBufQue。prepareOneTodoBuffer函式做的事情就是從dequePrvOps 函式deque出StreamImgBuf,並用它生成ImgBufQueNode,把ImgBufQueNode的標誌位設eSTATUS_TODO後呼叫ImgBufQueue的enqueProcessor函式把所有的ImgBufQueNode都放入到mTodoImgBufQue做接收資料的準備。看下dequePrvOps和enqueProcessor的實現
bool
DisplayClient::
dequePrvOps(sp<StreamImgBuf>& rpImgBuf)
{
// [1] dequeue_buffer
err = mpStreamOps->dequeue_buffer(mpStreamOps, &phBuffer, &stride);
// [2] lock buffers
err = mpStreamOps->lock_buffer(mpStreamOps, phBuffer);
......
// [5] Setup the output to return.
rpImgBuf = new StreamImgBuf(mpStreamImgInfo, stride, address, phBuffer, fdIon);
ret = true;
lbExit:
return ret;
}
值得一提的是mpStreamOps,它就是上一篇不斷提到的mHalPreviewWindow.nw,呼叫它的dequeue_buffer函式就相當於從Surface中dequeue一個buffer出來,將buffer填滿後通過呼叫enqueue_buffer函式將buffer傳給Surface,這樣影象就得以顯示。
bool
ImgBufQueue::
enqueProcessor(ImgBufQueNode const& rNode)
{
......
mTodoImgBufQue.push_back(rNode);
return true;
}
把所有的ImgBufQueNode都放入到mTodoImgBufQue做接收資料的準備。
回到onThreadLoop函式,步驟(3)進入死迴圈,不斷呼叫waitAndHandleReturnBuffers函式來接收處理buffer,同時呼叫 prepareAllTodoBuffers函式來將處理完的buffer重新放回 mTodoImgBufQue,接著看如何接收處理buffer
bool
DisplayClient::
waitAndHandleReturnBuffers(sp<IImgBufQueue>const& rpBufQueue)
{
bool ret = false;
Vector<ImgBufQueNode> vQueNode;
......
// (1) deque buffers from processor.
rpBufQueue->dequeProcessor(vQueNode);
// (2) handle buffers dequed from processor.
ret = handleReturnBuffers(vQueNode);
lbExit:
return ret;
}
在此處呼叫ImgBufQueue的dequeProcessor()等待通知並接收資料。然後再呼叫handleReturnBuffers函式將資料發給Surface
ImgBufQueue::
dequeProcessor(Vector<ImgBufQueNode>& rvNode)
{
bool ret = false;
while ( mDoneImgBufQue.empty() && mbIsProcessorRunning )
{
status_t status = mDoneImgBufQueCond.wait(mDoneImgBufQueMtx);
}
if ( ! mDoneImgBufQue.empty() )
{
// If the queue is not empty, deque all buffers from the queue.
ret = true;
rvNode = mDoneImgBufQue;
mDoneImgBufQue.clear();
}
return ret;
}
通過mDoneImgBufQueCond.wait(mDoneImgBufQueMtx)等待通知,收到通知後,從mDoneImgBufQue取出所有的ImgBufQueNode,這時候ImgBufQueNode裡面已經包含了影象資料。
bool
DisplayClient::
handleReturnBuffers(Vector<ImgBufQueNode>const& rvQueNode)
{
for (int32_t i = 0; i < queSize; i++)
{
sp<IImgBuf>const& rpQueImgBuf = rvQueNode[i].getImgBuf(); // ImgBuf in Queue.
sp<StreamImgBuf>const pStreamImgBuf = *mStreamBufList.begin(); // ImgBuf in List.
// (.1) Check valid pointers to image buffers in Queue & List
if ( rpQueImgBuf == 0 || pStreamImgBuf == 0 )
{
......
continue;
}
// (.2) Check the equality of image buffers between Queue & List.
if ( rpQueImgBuf->getVirAddr() != pStreamImgBuf->getVirAddr() )
{
......
continue;
}
// (.3) Every check is ok. Now remove the node from the list.
mStreamBufList.erase(mStreamBufList.begin());
// (.4) enquePrvOps/cancelPrvOps
if ( i == idxToDisp ) {
......
enquePrvOps(pStreamImgBuf);
}
}
return true;
}
for迴圈裡面通過 enquePrvOps函式將一個個StreamImgBuf發給Surface
void
DisplayClient::
enquePrvOps(sp<StreamImgBuf>const& rpImgBuf)
{
......
// [5] unlocks and post the buffer to display.
err = mpStreamOps->enqueue_buffer(mpStreamOps, rpImgBuf->getBufHndlPtr());
}
就如前面提到的,將buffer填滿後需要呼叫enqueue_buffer函式,這樣影象就已經發往Surface。
那麼影象資料從哪裡來呢,DisplayClient一共維護了兩個佇列 mTodoImgBufQue和mDoneImgBufQue,也就是說肯定在某個地方有人從mTodoImgBufQue deque了一個ImgBufQueNode,將它填滿後enque到mDoneImgBufQue裡面併發送通知告訴DisplayClient資料已經準備好
3. Pass1Node
上一篇提到由CamAdapter提供影象資料給DisplayClient。它的大部分工作分別由各個CamNode完成,其中Pass1Node負責和sensor driver打交道,最初的影象資料就是由它來獲取,之前已經看過它的onInit和onStart函式,現在來看它的threadLoopUpdate函式
MBOOL
Pass1NodeImpl::
threadLoopUpdate()
{
MBOOL ret = MTRUE;
//
if( keepLooping() )
{
// deque
ret = dequeLoop();
// try to keep ring buffer running
enqueBuffer();
}
else
{
ret = stopHw();
syncWithThread();
}
//
return ret;
}
這裡值得關注的只有dequeLoop函式
MBOOL
Pass1NodeImpl::
dequeLoop()
{
//FUNC_START;
MBOOL ret = MTRUE;
//prepare to deque
QBufInfo dequeBufInfo;
dequeBufInfo.mvOut.reserve(2);
if( mpRingImgo ) {
BufInfo OutBuf(mpRingImgo->getPortID(), 0);
dequeBufInfo.mvOut.push_back(OutBuf);
}
if( mpRingRrzo ) {
BufInfo OutBuf(mpRingRrzo->getPortID(), 0);
dequeBufInfo.mvOut.push_back(OutBuf);
}
for(MUINT32 i=0; i<2; i++)
{
MY_LOGD("frame %d: deque+", muFrameCnt);
ret = mpCamIO->deque(dequeBufInfo);
MY_LOGD("frame %d: deque-,%d", muFrameCnt, ret);
......
}
......
handleNotify(
PASS1_EOF,
newMagicNum,
muSensorDelay == 0 ? dequeMagicNum : MAGIC_NUM_INVALID);
configFrame(newMagicNum);
......
vector<BufInfo>::const_iterator iter;
for( iter = dequeBufInfo.mvOut.begin(); iter != dequeBufInfo.mvOut.end(); iter++ )
{
mpIspSyncCtrlHw->addPass1Info(
iter->mMetaData.mMagicNum_hal,
iter->mBuffer,
iter->mMetaData,
iter->mPortID == PORT_RRZO);
......
ret = ret && handlePostBuffer( mapToNodeDataType(iter->mPortID, bIsDynamicPureRaw), (MUINTPTR)iter->mBuffer, iter->mMetaData.mMagicNum_hal);
}
//FUNC_END;
return ret;
}
第23行,通過mpCamIO->deque取出一幀資料
第30-33行,傳送 PASS1_EOF訊息,其它的CamNode接收到訊息後做相應的處理,例如更新3A
第35行, configFrame不知道它在做什麼,有待研究
第42-46行,將Pass1的deque資訊加入到IspSyncCtrl
第48行,將deque到的資料post到Pass2Node(其實是post到DefaultCtrlNode,再由它post給Pass2Node)
這裡CamIO指的是NormalPipe,在Pass1Node的onInit函式裡建立,看下它如何deque資料
MBOOL
NormalPipe::deque(QBufInfo& rQBuf, MUINT32 u4TimeoutMs)
{
for (MUINT32 ii=0 ; ii<port_cnt ; ii++ ) {
if (MFALSE == mpCamIOPipe->dequeOutBuf(portID, rQTSBufInfo) ) {
......
}
if ( rQTSBufInfo.vBufInfo.size() >= 1 ) {
......
buff.mPortID = rQBuf.mvOut.at(ii).mPortID;
buff.mBuffer = pframe;
buff.mMetaData = result;
buff.mSize = rQTSBufInfo.vBufInfo.at(idx).u4BufSize[0];
buff.mVa = rQTSBufInfo.vBufInfo.at(idx).u4BufVA[0];
buff.mPa = rQTSBufInfo.vBufInfo.at(idx).u4BufPA[0];
rQBuf.mvOut.at(ii) = buff;
}
}
return ret;
}
MBOOL
CamIOPipe::
dequeOutBuf(PortID const portID, QTimeStampBufInfo& rQBufInfo, MUINT32 const u4TimeoutMs /*= 0xFFFFFFFF*/)
{
MUINT32 dmaChannel = 0;
stISP_FILLED_BUF_LIST bufInfo;
ISP_BUF_INFO_L bufList;
......
bufInfo.pBufList = &bufList;
if ( 0 != this->m_CamPathPass1.dequeueBuf( dmaChannel,bufInfo) ) {
......
}
rQBufInfo.vBufInfo.resize(bufList.size());
for ( MINT32 i = 0; i < (MINT32)rQBufInfo.vBufInfo.size() ; i++) {
rQBufInfo.vBufInfo[i].memID[0] = bufList.front().memID;
rQBufInfo.vBufInfo[i].u4BufSize[0] = bufList.front().size;
rQBufInfo.vBufInfo[i].u4BufVA[0] = bufList.front().base_vAddr;
rQBufInfo.vBufInfo[i].u4BufPA[0] = bufList.front().base_pAddr;
rQBufInfo.vBufInfo[i].i4TimeStamp_sec = bufList.front().timeStampS;
rQBufInfo.vBufInfo[i].i4TimeStamp_us = bufList.front().timeStampUs;
rQBufInfo.vBufInfo[i].img_w = bufList.front().img_w;
rQBufInfo.vBufInfo[i].img_h = bufList.front().img_h;
rQBufInfo.vBufInfo[i].img_stride = bufList.front().img_stride;
rQBufInfo.vBufInfo[i].img_fmt = bufList.front().img_fmt;
......
bufList.pop_front();
}
return MTRUE;
}
int CamPathPass1::dequeueBuf( MUINT32 dmaChannel ,stISP_FILLED_BUF_LIST& bufInfo )
{
int ret = 0;
Mutex *_localVar;
//check if there is already filled buffer
if ( MFALSE == this->ispBufCtrl.waitBufReady(dmaChannel) ) {
......
}
//move FILLED buffer from hw to sw list
if ( eIspRetStatus_Success != this->ispBufCtrl.dequeueHwBuf( dmaChannel, bufInfo ) ) {
......
}
return ret;
}
第7行,當buffer準備好時ISP會產生一箇中斷,而這裡將通過ioctl去等待獲取這個中斷
第11行,從底層獲取已經填滿的buffer
EIspRetStatus
ISP_BUF_CTRL::
dequeueHwBuf( MUINT32 dmaChannel, stISP_FILLED_BUF_LIST& bufList )
{
if ( ISP_PASS1 == this->path || \
ISP_PASS1_D == this->path_D || \
ISP_PASS1_CAMSV == this->path || \
ISP_PASS1_CAMSV_D == this->path_D
) {
//deque filled buffer
buf_ctrl.ctrl = ISP_RT_BUF_CTRL_DEQUE;
buf_ctrl.buf_id = (_isp_dma_enum_)rt_dma;
buf_ctrl.data_ptr = 0;
buf_ctrl.pExtend = (unsigned char*)&deque_buf;
if ( MTRUE != this->m_pIspDrvShell->m_pPhyIspDrv_bak->rtBufCtrl((void*)&buf_ctrl) ) {
ISP_FUNC_ERR("ERROR:rtBufCtrl");
ret = eIspRetStatus_Failed;
goto EXIT;
}
......
} else { // Pass2
......
}
EXIT:
return ret;
}
MBOOL IspDrvImp::rtBufCtrl(void *pBuf_ctrl)
{
MINT32 Ret;
Ret = ioctl(mFd,ISP_BUFFER_CTRL,pBuf_ctrl);
return MTRUE;
}
mFd是通過open(“/dev/camera-isp”, O_RDWR)得到的,而這裡通過ioctl獲取到已經填滿的buffer的地址。
到這裡我們已經獲取到了一幀影象,但還是不知道是誰把buffer放到DisplayClient的mDoneImgBufQue裡面去
4. Pass2Node
回到Pass1Node的dequeLoop函式,最後一個步驟handlePostBuffer函式。上一篇提到在CamAdapter的onHandleStartPreview函式裡面,通過connectData把Pass1Node和DefaultCtrlNode連線起來,把Pass2Node和DefaultCtrlNode連線起來
mpCamGraph->connectData(PASS1_RESIZEDRAW,CONTROL_RESIZEDRAW,mpPass1Node,mpDefaultCtrlNode);
mpCamGraph->connectData(CONTROL_PRV_SRC,PASS2_PRV_SRC,mpDefaultCtrlNode,mpPass2Node);
所以Pass1Node的handlePostBuffer函式會先把buffer post到DefaultCtrlNode,DefaultCtrlNode接收到之後再將它post給Pass2Node,這裡直接看Pass2Node的onPostBuffer函式
4.1 onPostBuffer函式分析
MBOOL
Pass2NodeImpl::
onPostBuffer(MUINT32 const data, MUINTPTR const buf, MUINT32 const ext)
{
if( pushBuf(data, (IImageBuffer*)buf, ext) )
{
// no thing
}
}
MBOOL
Pass2NodeImpl::
pushBuf(MUINT32 const data, IImageBuffer* const buf, MUINT32 const ext)
{
PostBufInfo postBufData = {data, buf, ext};
mlPostBufData.push_back(postBufData);
muPostFrameCnt++;
if( isReadyToEnque() )
{
triggerLoop();
}
return MTRUE;
}
儲存好buffer之後呼叫triggerLoop函式,triggerLoop會給自身的執行緒傳送update命令,然後Pass2Node的threadLoopUpdate函式就會被呼叫
MBOOL
Pass2NodeImpl::
threadLoopUpdate()
{
MBOOL ret = MTRUE;
ret = enquePass2(MTRUE);
return ret;
}
MBOOL
Pass2NodeImpl::
enquePass2(MBOOL const doCallback)
{
QParams enqueParams;
vector<p2data> vP2data;
if( !getPass2Buffer(vP2data) )
{
// no dst buffers
return MTRUE;
}
......
configFeature();
if( !mpIspSyncCtrlHw->lockHw(IspSyncControlHw::HW_PASS2) )
{
......
}
enqueParams.mpfnCallback = pass2CbFunc;
enqueParams.mpCookie = this;
if( !mpPostProcPipe->enque(enqueParams) )
{
......
}
return MTRUE;
}
第8行,通過DefaultBufHandler從mTodoImgBufQue取出buffer
第23行,enque目標buffer到IHalPostProcPipe,至於IHalPostProcPipe做了些什麼事情我也不知道,可能是影象縮放之類的工作,有待研究。IHalPostProcPipe處理完之後會回撥pass2CbFunc函式。Pass2CbFunc會把處理過的buffer通過DefaultBufHandler放回mDoneImgBufQue裡面。
DefaultBufHandler的作用是管理所有的CamClient的buffer佇列,例如DisplayClient。DisplayClient需要呼叫setImgBufProviderClient函式來把它的ImgBufQueue加入到ImgBufProvidersManager的IImgBufProvider列表中。從類圖可以看到,Pass2Node只要獲取到DefaultBufHandler,就能拿到DisplayClient的ImgBufQueue
4.2 getPass2Buffer函式分析
MBOOL
PrvPass2::
getPass2Buffer(vector<p2data>& vP2data)
{
MBOOL haveDst = MFALSE;
// src
{
Mutex::Autolock lock(mLock);
p2data one;
MUINT32 count = 0;
//
if( mlPostBufData.size() < muMultiFrameNum )
{
......
}
//
list<PostBufInfo>::iterator iter = mlPostBufData.begin();
while( iter != mlPostBufData.end() )
{
one.src = *iter;
iter = mlPostBufData.erase(iter);
//
vP2data.push_back(one);
count++;
//
if(count == muMultiFrameNum)
{
break;
}
}
}
// dst
{
MBOOL bDequeDisplay = MTRUE;
vector<p2data>::iterator pData = vP2data.begin();
while( pData != vP2data.end() )
{
for(MUINT32 i = 0; i < MAX_DST_PORT_NUM; i++)
{
MBOOL ret;
ImgRequest outRequest;
//
......
//
ret = getDstBuffer(
muDequeOrder[i],
&outRequest);
//
if(ret)
{
haveDst = MTRUE;
if(muDequeOrder[i] == PASS2_PRV_DST_0)
{
bDequeDisplay = MFALSE;
}
//
pData->vDstReq.push_back(outRequest);
pData->vDstData.push_back(muDequeOrder[i]);
}
}
......
}
}
}
第6-31行,把之前儲存在mlPostBufData裡的影象資料取出來並儲存在p2data中
第46-48行,從DefaultBufHandler取出所有CamClient的buffer
第50-61行,把從DefaultBufHandler獲取到的目標buffer一起放到p2data中
MBOOL
Pass2NodeImpl::
getDstBuffer(
MUINT32 nodeData,
ImgRequest* pImgReq)
{
MBOOL ret = MFALSE;
ICamBufHandler* pBufHdl = getBufferHandler(nodeData);
if(pBufHdl && pBufHdl->dequeBuffer(nodeData, pImgReq))
{
ret = MTRUE;
}
return ret;
}
MBOOL
DefaultBufHandlerImpl::
dequeBuffer(MUINT32 const data, ImgRequest * pImgReq)
{
......
sp<IImgBufProvider> bufProvider = NULL;
switch((*iterMapPort).bufType)
{
case eBuf_Disp:
{
bufProvider = mspImgBufProvidersMgr->getDisplayPvdr();
pImgReq->mUsage = NSIoPipe::EPortCapbility_Disp;
break;
}
......
}
......
if(bufProvider->dequeProvider(node))
{
node.setCookieDE((*iterMapPort).bufType);
mvBufQueNode[bufQueIdx].push_back(node);
isDequeProvider = MTRUE;
break;
}
......
}
第11行,這裡獲取到的就是DisplayClient的ImgBufQueue,它繼承了IImgBufProvider類
第18行,獲取ImgBufQueNode
bool
ImgBufQueue::
dequeProvider(ImgBufQueNode& rNode)
{
bool ret = false;
//
Mutex::Autolock _lock(mTodoImgBufQueMtx);
//
if ( ! mTodoImgBufQue.empty() )
{
// If the queue is not empty, take the first buffer from the queue.
ret = true;
rNode = *mTodoImgBufQue.begin();
mTodoImgBufQue.erase(mTodoImgBufQue.begin());
}
//
return ret;
}
第13行,可以看到deque最後是從mTodoImgBufQue裡面取出buffer
4.3 pass2CbFunc函式分析
之前提到過IHalPostProcPipe處理完之後會回撥pass2CbFunc函式。
MVOID
Pass2NodeImpl::
pass2CbFunc(QParams& rParams)
{
Pass2NodeImpl* pPass2NodeImpl = (Pass2NodeImpl*)(rParams.mpCookie);
pPass2NodeImpl->handleP2Done(rParams);
}
MBOOL
Pass2NodeImpl::
handleP2Done(QParams& rParams)
{
......
//
if( !mpIspSyncCtrlHw->unlockHw(IspSyncControlHw::HW_PASS2) )
{
MY_LOGE("isp sync unlock pass2 failed");
goto lbExit;
}
for( iterIn = rParams.mvIn.begin() ; iterIn != rParams.mvIn.end() ; iterIn++ )
{
MUINT32 nodeDataType = mapToNodeDataType( iterIn->mPortID );
handleReturnBuffer( nodeDataType, (MUINTPTR)iterIn->mBuffer, 0 );
}
vpDstBufAddr.clear();
for( iterOut = rParams.mvOut.begin() ; iterOut != rParams.mvOut.end() ; iterOut++ )
{
MBOOL bFind = MFALSE;
......
if(!bFind)
{
MUINT32 nodeDataType = mapToNodeDataType( iterOut->mPortID );
handlePostBuffer( nodeDataType, (MUINTPTR)iterOut->mBuffer, 0 );
vpDstBufAddr.push_back(iterOut->mBuffer);
}
}
return ret;
}
第13-17行,之前Pass1Node呼叫handlePostBuffer把buffer傳到Pass2Node,而現在呼叫handleReturnBuffer則會把buffer返回給Pass1Node,由Pass1Node的onReturnBuffer函式接收處理
第29行,再次呼叫handlePostBuffer函式,這裡由於沒有連線其它的CamNode,所以會回撥Pass2Node的onReturnBuffer函式
Pass1Node的onReturnBuffer函式就是把處理完的buffer放回ring buffer裡面,這裡不再分析,來看看Pass2Node的onReturnBuffer函式
MBOOL
Pass2NodeImpl::
onReturnBuffer(MUINT32 const data, MUINTPTR const buf, MUINT32 const ext)
{
ICamBufHandler* pBufHdl = getBufferHandler(data);
......
MBOOL ret = pBufHdl->enqueBuffer(data, (IImageBuffer*)buf);
......
return MTRUE;
}
MBOOL
DefaultBufHandlerImpl::
enqueBuffer(MUINT32 const data, IImageBuffer const * pImageBuffer)
{
......
switch(keepImgBufQueNode.getCookieDE())
{
case eBuf_Disp:
{
bufProvider = mspImgBufProvidersMgr->getDisplayPvdr();
break;
}
......
}
......
bufProvider->enqueProvider(keepImgBufQueNode);
......
}
bool
ImgBufQueue::
enqueProvider(ImgBufQueNode const& rNode)
{
......
Mutex::Autolock _lock(mDoneImgBufQueMtx);
mDoneImgBufQue.push_back(rNode);
mDoneImgBufQueCond.broadcast();
return true;
}
第8行,流程上和之前的deque差不多,可以看到enque最終將buffer放到mDoneImgBufQue裡面
第9行,準備好之後傳送廣播通知DisplayClient
5. 總結
DisplayClient準備好buffer放到mTodoImgBufQue裡面。
Pass1Node從底層deque一幀資料,然後將資料post給DefaultCtrlNode,DefaultCtrlNode又將資料post給Pass2Node。
Pass2Node儲存好buffer之後會觸發threadLoopUpdate,threadLoopUpdate通過DefaultBufHandler從mTodoImgBufQue取出buffer,再將buffer交給IHalPostProcPipe處理,當IHalPostProcPipe處理完之後會回撥Pass2CbFunc函式,Pass2CbFunc通過DefaultBufHandler把buffer放回mDoneImgBufQue裡面。
最後DisplayClient不斷從mDoneImgBufQue裡面取出已經處理好的buffer送到Surface裡面
相關推薦
Android 5.0 Camera系統原始碼分析(4):Camera預覽流程資料流
1. 前言 上一篇講了怎麼讓Camera進入預覽模式,提到了DisplayClient負責顯示影象資料,而CamAdapter負責提供影象資料,這裡主要記錄了CamAdapter怎麼獲取影象,然後DisplayClient怎麼將影象顯示在螢幕上。 2.
Android 5.0 Camera系統原始碼分析(2):Camera開啟流程
1. 前言 本文將分析android系統原始碼,從frameworks層到hal層,暫不涉及app層和kernel層。由於某些函式比較複雜,在貼出程式碼時會適當對其進行簡化。本文屬於自己對原始碼的總結,僅僅是貫穿程式碼流程,不會深入分析各個細節。歡迎聯絡討論,QQ:1026
Android 5.0 Camera系統原始碼分析(3):Camera預覽流程控制流
1. 前言 本文分析的是Android系統原始碼,從frameworks層到hal層,記錄了Camera進入預覽模式的重點程式碼,主要為控制流程的程式碼,有關影象buffer的傳遞暫不涉及,硬體平臺基於mt6735。由於某些函式比較複雜,在貼出程式碼時會適當對
Android 7.0 Gallery相簿原始碼分析4
上篇文章講了初始化View時會例項化一個SlotView並監聽其事件,至於它是怎麼實現的,用的是Android自帶的GestureDetector。 GestureDetector是Android自帶的用來監聽各種使用者手勢的的一個類,比如監聽單擊、雙擊和
Android 5.0 Usb除錯攔截分析及修改
當我們除錯安卓機器時,第一次插上usb線,會彈出一個授權的對話方塊,(前提是打開了usb除錯功能)點選確認,才會允許除錯. 如果我們想機器預設就可以除錯該怎麼做呢? 如果我們想動態攔截,需要使用者輸入帳號密碼,才確認是否可以除錯,該怎麼做呢?或者只是單純的想改變這個不好看
android 5.0 64bit系統載入庫檔案失敗問題淺析
最近公司的一個專案使用android 5.0 64 bit平臺,相對以前版本,除了android 5.0 有很大變動之外,64 bit系統和32 bit系統也存在很多差異性。 目前碰到的問題就是以前在32位上的so庫檔案,到64 位系統上不能載入的問題。首先來看一下相關lo
Android 7.0 Gallery相簿原始碼分析8
在Android 7.0 Gallery相簿原始碼分析3 - 資料載入及顯示流程一文最後講了AlbumSetSlidingWindow的onContentChanged方法,專輯縮圖和縮圖下面的label的載入就是在此方法中完成的 public
React Native之Android 5.0以下系統WebView訪問https頁面變成空白頁
在我們的React Native專案中,需要開發一個tab頁面專門配置三方h5連結,供使用者瀏覽。自動化測試:Android 5.0以下系統此tab頁面為空白頁面。看效果: 而我們去檢視這個三方的
android 5.0以下系統Intent傳遞序列化物件的bug
專案中使用外掛框架,當外掛在Intent中傳遞Serializable物件時,在android 5.0以下系統上會出現 E/InstrumentationHacker(25176): Parcelable encounteredClassNotFoundExce
Android 5.0核心和原始碼學習(3)——SystemServer啟動了什麼服務?
/**入口 * The main entry point from zygote. */ public static void main(String[] args) { new SystemServer().run(); } /**
SDL2原始碼分析4:紋理(SDL_Texture)
=====================================================SDL原始碼分析系列文章列表:=====================================================上一篇文章分析了SDL中建立渲染器
leveldb原始碼分析4:SkipList
skiplist思想可以具體參考這: // Create a new SkipList object that will use "cmp" for comparing keys, // and will allocate memory using "*ar
Linux-0.11核心原始碼分析系列:記憶體管理get_free_page()函式分析
Linux-0.11記憶體管理模組是原始碼中比較難以理解的部分,現在把筆者個人的理解發表 先發Linux-0.11核心記憶體管理get_free_page()函式分析有時間再寫其他函式或者檔案的:) /* *Author : DavidLin *Date :
Hbase-0.98.6原始碼分析--Put寫操作Client端流程
客戶端程式寫資料通過HTable和Put進行操作,我們從客戶端程式碼開始分析寫資料的流程: 可以看到,客戶端寫資料最終的呼叫了HTableInterface的put()方法,因為HTableInterface只是一個介面,所以最終呼叫的是它的
XBMC原始碼分析 4:視訊播放器(dvdplayer)-解碼器(以ffmpeg為例)
XBMC分析系列文章: 本文我們分析XBMC中視訊播放器(dvdplayer)中的解碼器部分。由於解碼器種類很多,不可能一一分析,因此以ffmpeg解碼器為例進行分析。 XBMC解碼器部分檔案目錄如下圖所示: 解碼器分為音訊解碼器和視訊解碼器。在這裡我們看一下視訊
華為5.0以上系統一鍵激活Xposed框架的流程
搜索 折騰 自動 需要 感覺 sta log 重新 type 對於喜歡研究手機的機友而言,大多時候會使用到Xposed框架以及各類功能牛逼的模塊,對於5.0以下的系統版本,只要手機能獲得ROOT權限,安裝和激活Xposed框架是非常輕易的,但隨著系統版本的叠代,5.0以後的
SpringMVC原始碼分析4:DispatcherServlet如何找到正確的Controller
SpringMVC是目前主流的Web MVC框架之一。 我們使用瀏覽器通過地址 http://ip:port/contextPath/path進行訪問,SpringMVC是如何得知使用者到底是訪問哪個Controller中的方法,這期間到底發生了什麼。 本文將分析SpringMVC是如何處理請求與
(一)Android camera2 實現相機預覽及獲取預覽幀資料流
一、本文重點說明 本文基於 android camera2 實現視訊預覽,暫未相容 camera1 API,基礎實現可以參考 googlesample Camera2 例子 android-Camera2Basic ,本文以工具類形式實現一步呼叫。 谷歌例子中沒有具體指
Android4.4之Camera2預覽流程(從APP到Driver)
1.APP呼叫 packages/apps/Camera2/src/com/android/camera/PhotoModule.java private void startPreview() { Log.v(TAG, "startPrev
Android Camera 系統架構原始碼分析(4)---->Camera的資料來源及Camera的管理
Camera的資料來源及Camera的管理 我們接著第3篇,再返回Cam1DeviceBase::startPreview()的(4) mpCamAdapter->startPreview()。在講(4)前我們先來看看(1)onStartPreview(