谷歌提供一個(gè)機(jī)制扰她, 就是 OutputConfiguration 的 enableSurfaceSharing
, 這個(gè)功能打開(kāi)之后犹赖, 該 OutputConfiguration 之內(nèi)的 若干個(gè) Surface, 就可以使用同一個(gè) Buffer 進(jìn)行顯示, 也就是說(shuō)這幾個(gè) Surface 會(huì)顯示相同的內(nèi)容董瞻。
這個(gè)機(jī)制是怎么實(shí)現(xiàn)的呢寞蚌? 就是通過(guò) Camera3SharedOutputStream
來(lái)實(shí)現(xiàn)的。 我們來(lái)看一下具體流程。
首先有幾個(gè)概念需要牢記:
- 每個(gè) OutputConfiguration 都會(huì) 對(duì)應(yīng)一路流睬澡, 對(duì)應(yīng)一個(gè) StreamId, 不論這個(gè) OutputConfiguration里面包含多少個(gè)Surface, 對(duì)應(yīng)的都是同一個(gè)StreamId.
也就是說(shuō)同一個(gè)StreamId 會(huì)對(duì)應(yīng)多個(gè) SurfaceId, 這種對(duì)應(yīng)關(guān)系在createStream
的時(shí)候會(huì)被保存起來(lái)固额。 另外在getBufferLocked
和returnBufferLocked
的時(shí)候也會(huì)攜帶上這種對(duì)應(yīng)關(guān)系。
首先在 Camera3Device
的 createStream
方法中煞聪, 在創(chuàng)建各種 OutputStream 的時(shí)候會(huì)進(jìn)行判斷斗躏, 如果打開(kāi)了 isSharing
, 就會(huì)創(chuàng)建 Camera3SharedOutputStream
, 默認(rèn)情況下會(huì)創(chuàng)建 Camera3OutputStream
.
Camera3SharedOutputStream
是 Camera3OutputStream
的子類(lèi)昔脯, 只重寫(xiě)了幾個(gè)關(guān)鍵方法, 最重要的就是這兩個(gè)方法:getBufferLocked
, queueBufferToConsumer
, 一個(gè)是獲取Buffer交給Provider生產(chǎn)啄糙, 一個(gè)是把生產(chǎn)好的Buffer返回給BufferQueue.
Camera3SharedOutputStream 的主要工作都是交給 Camera3StreamSplitter
來(lái)做的。
總體的思路是: 在 Camera3StreamSpliter 里面新建一個(gè) BQ, 從這個(gè)BQ中取出 Buffer 交給Provider生產(chǎn)云稚。 在生產(chǎn)好之后隧饼, 把這個(gè)Buffer 給 attach 到所有的 surface_ids
中。
為了方便静陈, 把 新建的 BQ 成為 Inner BQ, 把 OutputConfiguration 中的 BQ 稱為 Outter BQ.
下面講一下詳細(xì)流程(假設(shè)使用了Hal Buffer Manager)
- Camera3SharedOutputStream 在
configureQueueLocked
中調(diào)用connectStreamSplitterLocked
, 最終調(diào)用到 Camera3StreamSplitter 的connect
方法燕雁。 -
Camera3StreamSplitter
的connect
方法會(huì) 創(chuàng)建一個(gè)新的 BQ, 并且保存起來(lái) BQ 的生產(chǎn)端 mProducer 和 消費(fèi)端 mConsumer. 并且會(huì)把 mProducer 生成 sp<Surface> 賦值給Camera3OutputStream 的mConsumer
變量。 這里我們一定要記住的是mConsumer
是 Camera3OutputStream 里面非常重要的一個(gè)變量鲸拥, Camera3OutputStream 使用這個(gè)變量來(lái)dequeueBuffer
和queueBuffer
.
由于我們把Inner BQ 的 mProducer 賦值給了 Camera3OutputStream 的 mConsumer 變量拐格, 所以 在getBufferLocked
就會(huì)從 Inner BQ 中獲取 Buffer. - 接下來(lái)在 Camera3SharedOutputStream 中 的
getBufferLocked
方法中, 會(huì)從 Inner BQ中申請(qǐng)出來(lái)一個(gè) Buffer, 交給 Provider 去生產(chǎn)。 - 接下來(lái)就是在
queueBufferToConsumer
中刑赶, 調(diào)用attachBufferToSplitterLocked
方法捏浊, 然后調(diào)用到Camera3StreamSplitter的attachBufferToOutputs
, 這個(gè)方法的作用就是 把 這個(gè) Buffer 給 attach 到每一個(gè) Outter BQ 中。
我們先詳細(xì)講一下attachBufferToOutputs
的流程, Splitter 里面有幾個(gè)數(shù)據(jù)結(jié)構(gòu)要注意:
- BufferTracker, 記錄 Buffer 和 Outter Surface的對(duì)應(yīng)關(guān)系
- mOutputs 記錄 surfaceId 和 Outter Surface的對(duì)應(yīng)關(guān)系
- mOutputSlots 記錄 Outter Surface 和 占用的 Slot(這是個(gè)Vector) 的對(duì)應(yīng)關(guān)系
- mBuffers: 記錄 Buffer 和 BufferTracker 的對(duì)應(yīng)關(guān)系
- mDetachedBuffers 記錄所有需要Detach的Buffer.
- mInputSlots 記錄Buffer Id 和 BufferItem 的對(duì)應(yīng)關(guān)系
記下來(lái)看attachBufferToOutputs
的流程:
4.1 生成一個(gè) BufferTrakcer, 記錄了該 Buffer 以及需要該Buffer的所有 Outter Surface.
4.2 遍歷每一個(gè)Outter Surface 調(diào)用getSlotForOutputLocked
, 該方法的作用是:看一下 mOutputSlots 中 該 Outter Surface 是否保存過(guò)該 Buffer, 如果保存過(guò)該Buffer, 就返回對(duì)應(yīng)的Vector下標(biāo)撞叨, 否則就返回 INVALID_BUFFER_SLOT.
4.3 如果Outter Surface已經(jīng)保存過(guò)這個(gè)Buffer了金踪, 那么就遍歷下一個(gè)Outter Surface(這說(shuō)明該Buffer已經(jīng)在到 Outter BQ中了)。 如果沒(méi)有保存過(guò)牵敷, 就把該 Buffer 給 attachBuffer
到 Outter BQ 中胡岔, 然后會(huì)返回一個(gè) slot, 這個(gè)slot是 Outter Buffer 中對(duì)應(yīng)的槽位。
4.4 看一下返回的這個(gè)槽位 在 Outter Surface 對(duì)應(yīng)的 Vector 中是否有 GB, 如果有GB, 說(shuō)明什么劣领? 說(shuō)明原來(lái) Vector 對(duì)應(yīng)的 槽位上是一個(gè)舊Buffer, 調(diào)用 decrementBufRefCountLocked
(這個(gè)方法稍后會(huì)講)去清理一下和舊Buffer相關(guān)的數(shù)據(jù)結(jié)構(gòu)姐军, 然后把這個(gè)槽位上更新為新的Buffer.
4.5 然后把這個(gè) buffer 和對(duì)應(yīng)的 BufferTracker 保存到 mBuffers 中铁材。
我們?cè)诳匆幌?code>decrementBufRefCountLocked尖淘, 這個(gè)方法的主要作用是:
4.6 首先調(diào)用對(duì)應(yīng) BufferTracker 的 decrementReferenceCountLocked
方法, 這個(gè)方法的主要作用 就是 BufferTracker 中移除這個(gè)SurfaceId, 并降低計(jì)數(shù)著觉。
BufferTracker
的作用 就是 記錄同一個(gè)Buffer, 還沒(méi)有被哪些 Outter Surface消費(fèi)村生。 每個(gè) Outter Surface消費(fèi)完都需要從 BufferTracker 中清除自己。
4.7 如果 BufferTracker
的計(jì)數(shù)已經(jīng)到 0 了饼丘,說(shuō)明這個(gè) Buffer 已經(jīng)被所有的 OutterSurface 消費(fèi)完畢了趁桃, 這個(gè) BufferTracker 要從 mBuffers
中移除。
4.8 看一下 mInputSlots 中是否包含這個(gè) buffer id。 如果沒(méi)有包含就說(shuō)明這個(gè)Buffer 應(yīng)該是一個(gè)新的Buffer, 啥都不需要做卫病。 如果包含了油啤, 就說(shuō)明這個(gè) Buffer 和前面某一次Provider傳過(guò)來(lái)的Buffer是同一個(gè), 這個(gè)時(shí)候就需要做一些清理工作了: 看一下 mDetachedBuffers 中是否包含這個(gè)Buffer, 如果包含(如果一個(gè)Shared Surface被移除了蟀苛,就會(huì)把這個(gè)Surface對(duì)應(yīng)的 mOutputSlots里面的所有Buffer都用Outter Surface給 detachBuffer掉益咬, 如果detachBuffer失敗, 就放入mDetachedBuffers中)帜平, 就把 這個(gè)Buffer 從 mDetachedBuffers 和 mInputSlots 中移除幽告,并且 調(diào)用 Inner Consumer 的 detachBuffer
(如果mDetachedBuffers中包含)或者releaseBuffer
方法。
這里就是問(wèn)題了裆甩, 如果 Inner Consumer調(diào)用了 detachBuffer, 并且我們重寫(xiě)了onBufferDetached, 把 Inner Surface中的引用清空冗锁, 會(huì)出現(xiàn)什么結(jié)果?
主要思想就是: 當(dāng)有新的Buffer從Provider過(guò)來(lái)的時(shí)候嗤栓, 嘗試去往每個(gè) Outter Surface中attach,
如果之前這個(gè)Buffer沒(méi)有被Outter Surface attach過(guò)冻河, 就可以成功。 如果attach過(guò)(根據(jù)mOutputSlots來(lái)判斷)茉帅, 就不會(huì)再次attach(這個(gè)時(shí)候Outter BQ中已經(jīng)有這個(gè)Buffer了芋绸, Buffer的內(nèi)容肯定被更新成最新的了。為啥以前一個(gè)Buffer被attach到OutterBQ中担敌,但直到新的同一個(gè)Buffer到來(lái)摔敛, 同一個(gè)Buffer還一直在 mOutputSlots中呢?)全封。 attach成功之后马昙, 看一下這個(gè) 槽位 是不是已經(jīng)在 mOutputSlots 里面了, 如果在的話刹悴, 說(shuō)明復(fù)用了原來(lái)的槽位(可能Outter BQ已經(jīng)消費(fèi)過(guò)這個(gè) 槽位了行楞, 又重新復(fù)用了這個(gè)槽位。 但是為啥消費(fèi)過(guò)了土匀, 這個(gè)槽位還在mOutputSlots中呢子房?)
這樣就把 所有和舊Buffer相關(guān)的數(shù)據(jù)結(jié)構(gòu)的內(nèi)容的清理了(mInputSlots
和 mDetachedBuffers
), 或者給更新成新內(nèi)容了(mOutputSlots
, mBuffers
)
- 然后 Inner Surface 會(huì)調(diào)用
queueBuffer
把 該 Buffer 放入 Inner BQ 中, 這個(gè)時(shí)候Camera3StreamSplitter
的onFrameAvailable
就會(huì)被調(diào)用就轧。 然后 Inner Consumer會(huì)調(diào)用acquireBuffer
獲取到這個(gè) Buffer, 也就是 BufferItem. 然后把這個(gè) BufferItem 放入 mInputSlots 中证杭。 - 對(duì)于每個(gè) Outter Surface, 調(diào)用
outputBufferLocked
方法妒御, 下面詳細(xì)講一下outputBufferLocked
方法
6.1 先調(diào)用getSlotForOutputLocked
得到 Slot, 還記得我們?cè)?4.2
中attachBuffer
之前會(huì)先得到一個(gè)Slot嗎解愤, 這個(gè)Slot 是 Outter BQ 中的槽點(diǎn)。
6.2 調(diào)用 Outter Producer 的queueBuffer
乎莉, 將 Buffer 放入對(duì)應(yīng)的槽點(diǎn)送讲。如果失敗了就調(diào)用decrementBufRefCountLocked
看一下要不要清理
這樣每一個(gè) Outter Consusmer 都可以消費(fèi)這個(gè) Buffer了奸笤。
看一下消費(fèi)完這個(gè) Buffer 之后的處理。
每一個(gè) Outter Consumer 消費(fèi)完之后哼鬓, 會(huì)回調(diào)到Camera3StreamSplitter
中的 OutputListener
的 onBufferReleased
方法中监右, 最終調(diào)用到 onBufferReleasedByOutput
.
接下來(lái)是很詭異的一個(gè)操作, 百思不得其解异希。它會(huì)先 調(diào)用 Outter Producer 的 dequeueBuffer
出來(lái)秸侣, 得到這個(gè)Buffer的Slot. 為什么要先 dequeue 一個(gè)槽位? 由于 Outter BQ不會(huì)去開(kāi)辟Buffer, 這個(gè) dequeueBuffer
僅僅是把一個(gè)槽位設(shè)置為 Dequeued
狀態(tài), 而且這個(gè)槽位 肯定是從 mFreeBuffers
中來(lái)的
然后調(diào)用returnOutputBufferLocked
, 我們看一下這個(gè)方法
7.1 首先從 mOutputSlots
中取出對(duì)應(yīng) Outter Producer所有的 Buffer. 還記的這些Buffer是從哪里來(lái)的嗎宠互? 是 Inner Consumer在調(diào)用 attachBufferToOutputs
中放入的味榛, 也就是說(shuō)這些Buffer其實(shí)都是從 Inner BQ來(lái)的, 是被填充過(guò)的予跌。
這里沒(méi)有對(duì)這個(gè)Buffer進(jìn)行任何非空判斷搏色, 就直接使用了, 這是為什么呢券册?
我們?cè)谑崂硪幌铝鞒蹋?在 Outter Consumer 調(diào)用了release方法之后频轿, 這個(gè) Buffer 會(huì)被放到 Outter BQ的 mFreeBuffers
中, 而dequeueBuffer
會(huì)先從 mFreeBuffers
中取出來(lái)Buffer, 所以會(huì)取到烁焙。
那會(huì)不會(huì)出現(xiàn)航邢, dequeueBuffer
從mFreeSlots
中取值的情況呢?
因?yàn)?對(duì)于 Outter BQ來(lái)說(shuō)骄蝇, 采用的是attach&queue
的方式膳殷,dequeueBuffer
是在release
的時(shí)候才會(huì)調(diào)用, 所以 dequeueBuffer
的調(diào)用肯定是小于 attach&queue
的九火, 不可能出現(xiàn) attach&queue
一次赚窃, 但是dequeueBuffer
兩次的情況。
7.2 從mOutputSlots
中取出來(lái) Buffer之后岔激, 然后繼續(xù)從 mBuffers
中取出來(lái) BufferTrack
.
還記的我們?cè)?code>attach&queue流程中更新了兩個(gè)數(shù)據(jù)結(jié)構(gòu)勒极,就是mBuffers
和mOutputSlots
,
接下來(lái)就是要對(duì)這兩個(gè)進(jìn)行清理了。
7.3 如果 mDetachedBuffers 中包含了這個(gè) Buffer, 說(shuō)明這個(gè) Outter Surface被從 OutputConfiguration中移除了虑鼎, 那么我們就 調(diào)用 Outter Producer的 detachBuffer
, 然后清空一下mOutputSlots
.
為啥這種情況下會(huì)調(diào)用一下detachBuffer
呢辱匿? 因?yàn)?code>detachBuffer會(huì)把 Buffer 放到 mFreeSlots
中, 并且清空這個(gè)Buffer.
7.4 調(diào)用 decrementBufferRefCountLocked
, 這個(gè)前面已經(jīng)講過(guò)了炫彩。
這里最后為什么要dequeueBuffer
一下呢匾七? 這樣可以確保 這個(gè) Buffer 不會(huì)被 Outter BQ復(fù)用,防止在 Buffer 的生命周期旗艦媒楼, 某一個(gè) Outter BQ 亂用這個(gè) Buffer, 導(dǎo)致出問(wèn)題乐尊。
這樣會(huì)導(dǎo)致 Outter BQ 中多出一個(gè)處于 Dequeued
狀態(tài)的 Buffer, 是的戚丸, 拿什么時(shí)候多出的這個(gè)Dequeue 狀態(tài)的Buffer會(huì)被清除呢划址?
當(dāng)所有的 Outter BQ 都用完這個(gè) Buffer 之后? 具體流程沒(méi)有找到扔嵌。。夺颤。痢缎。應(yīng)該是 4.3 那一步有關(guān)系。
從上面的解析來(lái)看世澜, enableSurfaceSharing是完全不考慮幀率的...