首先 BufferSlot 是 Buffer 的封裝鸥诽, BufferQueueCore 使用 mSlots 來管理所有Buffer, mSlots 是一個 BufferSlot 數(shù)組谦絮, 包含了所有的BufferSlot. BufferSlot里面最重要的兩個成員是: mGraphicBuffer 和 mBufferState
BufferItem是 mQueue 中的元素征椒, mQueue是 BufferQueueCore中另一個重要的成員梧奢, 是一個FIFO隊列忧陪。 生產(chǎn)者調(diào)用queueBuffer的時候惨险, 會把這個Buffer封裝成BufferItem搞糕,然后放入到 mQueue中。 消費者調(diào)用acquireBuffer的時候會取出來mQueue中的第一個元素進(jìn)行消費蜂科。
我們知道Buffer有四種狀態(tài): Dequeued, Queued, Acquired, Free. 這種狀態(tài)使用 BufferState來封裝顽决, 就是我們上面提到的 BufferSlot中的 mBufferState.
整個BufferQueue機(jī)制是通過 BufferQueue.cpp 的 createBufferQueue來創(chuàng)建的, 會創(chuàng)建三個角色: BufferQueueCore, BufferQueueProducer, BufferQueueConsusmer. 其中 BufferQueueProducer 和 BufferQueueConsumer都是Binder导匣, 可以跨進(jìn)程調(diào)用才菠。
但是我們一般都是在消費者進(jìn)程創(chuàng)建的BufferQ, 所以一般只要把 BufferQueueProducer傳遞給遠(yuǎn)端就可以了。
BufferQueueProducer和BufferQueueConsumer都可以直接訪問 BufferQueueCore, 其中 BufferQueueProducer中還包含了BufferQueueCore中的mSlots的一個 引用贡定, 這樣會方便 BufferQueueProducer更快捷的操作 mSlots.
BufferQueueConsumer 和 BufferQueueProducer 的回調(diào) 都是在各自的 connect 方法中執(zhí)行的赋访。 會向 BufferQueueCore中分別注冊自己的回調(diào): ProxyConsumerListener 和 IProducerListener 這兩個回調(diào), 因為 Producer一般在遠(yuǎn)端, 所以 IProducerListener這個回調(diào)里面還包含了Binder死亡回調(diào)的實現(xiàn)蚓耽。
BufferItem中定義了索引值mSlot, 這個是 mSlots這個數(shù)組的下標(biāo)渠牲, 這樣通過BufferItem就可以得到具體的BufferSlot了。
BufferSlot的狀態(tài)分為: Active狀態(tài)和Free狀態(tài)步悠, Active狀態(tài)代表了正在被生產(chǎn)者或者消費者使用的狀態(tài)嘱兼, Free狀態(tài)代表了空閑的BufferSlot, 而Free狀態(tài)又被分為了: FreeBuffer 和 FreeSlot, FreeBuffer代表了已經(jīng)擁有Buffer的BufferSlot, FreeSlot代表還沒有擁有Buffer的BufferSlot.
Active狀態(tài)的BufferSlot的下標(biāo) 被放在了 mActiveBuffers 這個 vector中贤徒。
FreeBuffer狀態(tài)的BufferSlot的下標(biāo) 被放在了 mFreeBuffers 這個 vector 中芹壕。
FreeSlot狀態(tài)的BufferSlot的下標(biāo)被放在了mFreeSlot這個vector中。
BufferSlot沒有實現(xiàn)任何Binder或者Flattenable接口接奈, 不能跨進(jìn)程傳遞踢涌, 所以只能在BufferQueueCore中接受管理, 同時 BufferQueueProducer 作為bn端序宦, 和BufferQueueCore在同一個進(jìn)程中睁壁, 也保存了 mSlots 的一個引用。
BufferItem和GraphicBuffer都實現(xiàn)了Flattenable互捌, 可以跨進(jìn)程傳遞潘明。
生產(chǎn)和消費過程
首先看一下生產(chǎn)者最常見的操作: dequeueBuffer 和 queueBuffer
dequeueBuffer的主要工作:
- 調(diào)用 waitForFreeSlotThenRelock 方法, 這個方法的作用就是: 如果是dequeue, 就優(yōu)先嘗試從 mFreeBuffers 里面找slot, 然后嘗試從 mFreeSlots里面找slot. 如果是attach, 就優(yōu)先嘗試從mFreeSlots里面找slot, 然后嘗試從mFreeBuffers里面找slot. waitForFreeSlotThenRelock 這個方法只是返回slot, 并不會真的去分配GraphicBuffer.
這里面有一些細(xì)節(jié)秕噪, 比如如果開啟了BufferManager, 那么 waitForFreeSlotThenRelock 就只會返回 FreeBuffer了钳降, 但是如果這個FreeBuffer需要重新分配空間來滿足Buffer的一些屬性, 那么這個FreeBuffer就不能使用腌巾, 我們就要把這個FreeBuffer清空遂填, 放入到FreeSlot中, 然后再次去循環(huán)調(diào)用 waitForFreeSlotThenRelock, 知道找到合適的slot澈蝙。 - 把找到的slot放入到 mActiveBuffer中吓坚, 并更改BufferState狀態(tài)為 Dequeue狀態(tài)。
并看一下是不是需要分配GraphicBuffer, 如果需要分配就分配新的GraphicBuffer. 當(dāng)GraphicBuffer好了之后灯荧,通知 ConsumerListener的onFrameDequeued
方法礁击, 這個方法傳入的參數(shù)是分配好的GraphicBuffer。
如果是Consumer通過 attachBuffer 添加的一個Buffer到BQ逗载, 那么生產(chǎn)者在 dequeueBuffer的時候遇到了這樣的Buffer, 肯定需要重新分配一遍哆窿, 因為消費者attach進(jìn)來的Buffer是不符合生產(chǎn)者要求的。
dequeueBuffer
這個方法會返回一個可用的GraphicBuffer, 而waitForFreeSlotThenRelock只是會返回可用的slot.
requestBuffer
這個方法就是根據(jù)傳入的slot, 把這個slot對應(yīng)的GraphicBuffer放入到指定的buffer指針中撕贞, 這個方法并不會分配新的Buffer. 也就是說requestBuffer得到的Buffer也有可能是空的更耻。
queueBuffer的流程:
- 創(chuàng)建 BufferItem, 并且更改BufferState狀態(tài)為 Queued, 并且把BufferItem關(guān)聯(lián)到GraphicBuffer测垛, 然后嘗試放入FIFO中捏膨, 之所以嘗試放入, 是因為要看FIFO中最后一個元素是否需要被Replace. 然后會把 BufferItem關(guān)聯(lián)的GraphicBuffer的引用給清空, 也就是說BufferItem應(yīng)該只包含slot号涯, 并不包含GraphicBuffer的引用目胡。
- 然后根據(jù)是否被Replace了, 來調(diào)用 ConsumerListener的 onFrameAvailable 或者 onFrameRepleaced 方法链快。
queueBuffer的流程是比較簡單的誉己。
attachBuffer的流程
attachBuffer就是把本來不屬于該BQ的Buffer放入該BQ中讓該BQ管理。
- 首先調(diào)用 waitForFreeSlotThenRelock方法域蜗, 只不過這一次是優(yōu)先從 mFreeSlots 中尋找 slot.
- 找到這個slot之后 把它放入到 mActiveBuffers中管理巨双, 并且把狀態(tài)改成Dequeued, 也就是說 生產(chǎn)者調(diào)用的 attachBuffer, 相當(dāng)于是Dequeue出去一個Buffer.
detachBuffer的流程
- 調(diào)用 ConsumerListener 的
onFrameDetached
方法 - 更改BufferStatus狀態(tài)為Dequeue--, 然后把BufferSlot 從mActiveBuffers中移除霉祸,放入到 mFreeSlots中筑累, 并且清空這個BufferSlot.
- 調(diào)用 ConsumerListener 的
onBuffersReleased
方法。
這里會調(diào)用兩個ConsumerListener的回調(diào)丝蹭。
也就是說 Producer 的 attach 和 detach方法慢宗, 都不會往FIFO中放入BufferItem, 僅僅是把Buffer放入了BufferQueueCore中進(jìn)行管理。 attachBuffer之后奔穿, 這個Buffer的狀態(tài)是Dequeued, 是活躍的镜沽。 detachBuffer之后, 這個buffer的狀態(tài)是Queued--的贱田, 是FreeSlot狀態(tài)缅茉。
從上面的分析可以知道, 生產(chǎn)者直接attach一個buffer到BQ中是不能被消費的男摧, 必須 attach+queue才可以宾舅。