Android 5.0 Camera系统源码分析(4):Camera预览流程数据流
2017-11-18 23:46
696 查看
http://blog.csdn.net/eternity9255/article/details/52838696
上一篇讲了怎么让Camera进入预览模式,提到了DisplayClient负责显示图像数据,而CamAdapter负责提供图像数据,这里主要记录了CamAdapter怎么获取图像,然后DisplayClient怎么将图像显示在屏幕上。
上一篇提到在setPreviewWindow的时候会构造并初始化DisplayClient,之前没有仔细分析,现在来看看
2
3
4
5
6
7
8
9
10
11
创建了一个显示线程和一个ImgBuf队列,看下这两个函数的具体实现
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ImgBufQueue暂时放一边,createDisplayThread创建了DisplayThread,作为线程关注的重点当然是threadLoop,所以接着看DisplayThread的threadLoop函数
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
DisplayThread将接收WAKEUP命令,然后做出响应。那么由谁来发这个WAKEUP命令呢,就在上一篇提到的enableDisplayClient函数里面发送。这里的mpThreadHandler 指的是DisplayClient,也就是在接收到WAKEUP命令后,将回调DisplayClient的onThreadLoop函数
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
先分析步骤(1)准备好接收数据的buffers
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
这里的ImgBufQueue就是DisplayClient初始化的时候创建的那个ImgBufQueue,里有两个Buf队列,mTodoImgBufQue和mDoneImgBufQue。prepareOneTodoBuffer函数做的事情就是从dequePrvOps 函数deque出StreamImgBuf,并用它生成ImgBufQueNode,把ImgBufQueNode的标志位设eSTATUS_TODO后调用ImgBufQueue的enqueProcessor函数把所有的ImgBufQueNode都放入到mTodoImgBufQue做接收数据的准备。看下dequePrvOps和enqueProcessor的实现
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
值得一提的是mpStreamOps,它就是上一篇不断提到的mHalPreviewWindow.nw,调用它的dequeue_buffer函数就相当于从Surface中dequeue一个buffer出来,将buffer填满后通过调用enqueue_buffer函数将buffer传给Surface,这样图像就得以显示。
2
3
4
5
6
7
8
把所有的ImgBufQueNode都放入到mTodoImgBufQue做接收数据的准备。
回到onThreadLoop函数,步骤(3)进入死循环,不断调用waitAndHandleReturnBuffers函数来接收处理buffer,同时调用 prepareAllTodoBuffers函数来将处理完的buffer重新放回 mTodoImgBufQue,接着看如何接收处理buffer
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在此处调用ImgBufQueue的dequeProcessor()等待通知并接收数据。然后再调用handleReturnBuffers函数将数据发给Surface
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
通过mDoneImgBufQueCond.wait(mDoneImgBufQueMtx)等待通知,收到通知后,从mDoneImgBufQue取出所有的ImgBufQueNode,这时候ImgBufQueNode里面已经包含了图像数据。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
for循环里面通过 enquePrvOps函数将一个个StreamImgBuf发给Surface
2
3
4
5
6
7
8
就如前面提到的,将buffer填满后需要调用enqueue_buffer函数,这样图像就已经发往Surface。
那么图像数据从哪里来呢,DisplayClient一共维护了两个队列 mTodoImgBufQue和mDoneImgBufQue,也就是说肯定在某个地方有人从mTodoImgBufQue deque了一个ImgBufQueNode,将它填满后enque到mDoneImgBufQue里面并发送通知告诉DisplayClient数据已经准备好
上一篇提到由CamAdapter提供图像数据给DisplayClient。它的大部分工作分别由各个CamNode完成,其中Pass1Node负责和sensor driver打交道,最初的图像数据就是由它来获取,之前已经看过它的onInit和onStart函数,现在来看它的threadLoopUpdate函数
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
这里值得关注的只有dequeLoop函数
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
第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数据
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
第7行,当buffer准备好时ISP会产生一个中断,而这里将通过ioctl去等待获取这个中断
第11行,从底层获取已经填满的buffer
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
mFd是通过open(“/dev/camera-isp”, O_RDWR)得到的,而这里通过ioctl获取到已经填满的buffer的地址。
Pass1NodeImplPass1NodeImplNormalPipeNormalPipeCamIOPipeCamIOPipeCamPathPass1CamPathPass1ISP_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里面去
回到Pass1Node的dequeLoop函数,最后一个步骤handlePostBuffer函数。上一篇提到在CamAdapter的onHandleStartPreview函数里面,通过connectData把Pass1Node和DefaultCtrlNode连接起来,把Pass2Node和DefaultCtrlNode连接起来
2
所以Pass1Node的handlePostBuffer函数会先把buffer post到DefaultCtrlNode,DefaultCtrlNode接收到之后再将它post给Pass2Node,这里直接看Pass2Node的onPostBuffer函数
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
保存好buffer之后调用triggerLoop函数,triggerLoop会给自身的线程发送update命令,然后Pass2Node的threadLoopUpdate函数就会被调用
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
第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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
第6-31行,把之前保存在mlPostBufData里的图像数据取出来并保存在p2data中
第46-48行,从DefaultBufHandler取出所有CamClient的buffer
第50-61行,把从DefaultBufHandler获取到的目标buffer一起放到p2data中
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
第11行,这里获取到的就是DisplayClient的ImgBufQueue,它继承了IImgBufProvider类
第18行,获取ImgBufQueNode
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
第13行,可以看到deque最后是从mTodoImgBufQue里面取出buffer
之前提到过IHalPostProcPipe处理完之后会回调pass2CbFunc函数。
2
3
4
5
6
7
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
第13-17行,之前Pass1Node调用handlePostBuffer把buffer传到Pass2Node,而现在调用handleReturnBuffer则会把buffer返回给Pass1Node,由Pass1Node的onReturnBuffer函数接收处理
第29行,再次调用handlePostBuffer函数,这里由于没有连接其它的CamNode,所以会回调Pass2Node的onReturnBuffer函数
Pass1Node的onReturnBuffer函数就是把处理完的buffer放回ring buffer里面,这里不再分析,来看看Pass2Node的onReturnBuffer函数
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
第8行,流程上和之前的deque差不多,可以看到enque最终将buffer放到mDoneImgBufQue里面
第9行,准备好之后发送广播通知DisplayClient
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里面
1. 前言
上一篇讲了怎么让Camera进入预览模式,提到了DisplayClient负责显示图像数据,而CamAdapter负责提供图像数据,这里主要记录了CamAdapter怎么获取图像,然后DisplayClient怎么将图像显示在屏幕上。
2. DisplayClient
上一篇提到在setPreviewWindow的时候会构造并初始化DisplayClient,之前没有仔细分析,现在来看看bool DisplayClient:: init() { bool ret = false; ret = createDisplayThread() && createImgBufQueue(); return ret; }1
2
3
4
5
6
7
8
9
10
11
创建了一个显示线程和一个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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
先分析步骤(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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
这里的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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
值得一提的是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; }1
2
3
4
5
6
7
8
把所有的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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在此处调用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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
通过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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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()); }1
2
3
4
5
6
7
8
就如前面提到的,将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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
这里值得关注的只有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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
第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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
第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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
MBOOL IspDrvImp::rtBufCtrl(void *pBuf_ctrl) { MINT32 Ret; Ret = ioctl(mFd,ISP_BUFFER_CTRL,pBuf_ctrl); return MTRUE; }1
2
3
4
5
6
7
8
mFd是通过open(“/dev/camera-isp”, O_RDWR)得到的,而这里通过ioctl获取到已经填满的buffer的地址。
Pass1NodeImplPass1NodeImplNormalPipeNormalPipeCamIOPipeCamIOPipeCamPathPass1CamPathPass1ISP_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);1
2
所以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 } }1
2
3
4
5
6
7
8
9
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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
保存好buffer之后调用triggerLoop函数,triggerLoop会给自身的线程发送update命令,然后Pass2Node的threadLoopUpdate函数就会被调用
MBOOL Pass2NodeImpl:: threadLoopUpdate() { MBOOL ret = MTRUE; ret = enquePass2(MTRUE); return ret; }1
2
3
4
5
6
7
8
9
10
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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
第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]); } } ...... } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
第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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
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; } ...... }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
第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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
第13行,可以看到deque最后是从mTodoImgBufQue里面取出buffer
4.3 pass2CbFunc函数分析
之前提到过IHalPostProcPipe处理完之后会回调pass2CbFunc函数。MVOID Pass2NodeImpl:: pass2CbFunc(QParams& rParams) { Pass2NodeImpl* pPass2NodeImpl = (Pass2NodeImpl*)(rParams.mpCookie); pPass2NodeImpl->handleP2Done(rParams); }1
2
3
4
5
6
7
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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
第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; }1
2
3
4
5
6
7
8
9
10
11
MBOOL DefaultBufHandlerImpl:: enqueBuffer(MUINT32 const data, IImageBuffer const * pImageBuffer) { ...... switch(keepImgBufQueNode.getCookieDE()) { case eBuf_Disp: { bufProvider = mspImgBufProvidersMgr->getDisplayPvdr(); break; } ...... } ...... bufProvider->enqueProvider(keepImgBufQueNode); ...... }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
bool ImgBufQueue:: enqueProvider(ImgBufQueNode const& rNode) { ...... Mutex::Autolock _lock(mDoneImgBufQueMtx); mDoneImgBufQue.push_back(rNode); mDoneImgBufQueCond.broadcast(); return true; }1
2
3
4
5
6
7
8
9
10
11
12
第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预览流程数据流
- Android 5.0 Camera系统源码分析(4):Camera预览流程数据流
- Android 5.0 Camera系统源码分析(5):Camera预览3A流程
- Android 5.0 Camera系统源码分析(3):Camera预览流程控制流
- Android 5.0 Camera系统源码分析(3):Camera预览流程控制流
- Android 5.0 Camera系统源码分析(5):Camera预览3A流程
- Android 5.0 Camera系统源码分析(2):Camera打开流程
- Android 5.0 Camera系统源码分析(1):CameraService启动流程
- Android 5.0 Camera系统源码分析(1):CameraService启动流程
- Android 5.0 Camera系统源码分析(2):Camera打开流程
- Android 5.0 Camera系统源码分析(1):CameraService启动流程
- Android Camera 系统架构源码分析(3)---->Camera的显示流程
- Android Camera 系统架构源码分析(3)---->Camera的显示流程
- 源码级分析Android系统启动流程
- Android View系统源码分析(一)——概述&触摸事件总体处理流程
- Android系统启动流程源码分析
- android6.0源码分析之Camera API2.0下的Preview(预览)流程分析
- android开发之源码级分析(系统启动流程 & Handler消息机制 & AsyncTask机制)
- Android Camera 系统架构源码分析(1)---->Camera的初始化
- Android源码分析---系统开机流程