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, "CameraDisplay@ImgBufQue"); 
    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里面已经包含了图像数据。

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值