1. 程式人生 > >Android 5.0 Camera系統原始碼分析(4):Camera預覽流程資料流

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。

DisplayThread

那麼影象資料從哪裡來呢,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的地址。

Created with Raphaël 2.1.0Pass1NodeImplPass1NodeImplNormalPipeNormalPipeCamIOPipeCamIOPipeCamPathPass1CamPathPass1ISP_BUF_CTRLISP_BUF_CTRLIspDrvImpIspDrvImpdequeLoopdequedequeOutBufdequeueBufwaitBufReadywaitIrqioctl(ISP_WAIT_IRQ)check if there is already filled bufferdequeueHwBufrtBufCtrlioctl(ISP_BUFFER_CTRL)move FILLED buffer from hw to sw list

到這裡我們已經獲取到了一幀影象,但還是不知道是誰把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); 

CamNode

所以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

getPass2Buffer

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原始碼分析4SkipList

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原始碼分析4DispatcherServlet如何找到正確的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(