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里面已经包含了图像数据。