BufferQueue原創(chuàng) - enableSurfaceSharing

谷歌提供一個(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è)概念需要牢記:

  1. 每個(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)固额。 另外在getBufferLockedreturnBufferLocked的時(shí)候也會(huì)攜帶上這種對(duì)應(yīng)關(guān)系。

首先在 Camera3DevicecreateStream 方法中煞聪, 在創(chuàng)建各種 OutputStream 的時(shí)候會(huì)進(jìn)行判斷斗躏, 如果打開(kāi)了 isSharing, 就會(huì)創(chuàng)建 Camera3SharedOutputStream, 默認(rèn)情況下會(huì)創(chuàng)建 Camera3OutputStream.

Camera3SharedOutputStreamCamera3OutputStream 的子類(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)

  1. Camera3SharedOutputStream 在 configureQueueLocked 中調(diào)用 connectStreamSplitterLocked, 最終調(diào)用到 Camera3StreamSplitter 的 connect 方法燕雁。
  2. Camera3StreamSplitterconnect 方法會(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)dequeueBufferqueueBuffer.
    由于我們把Inner BQ 的 mProducer 賦值給了 Camera3OutputStream 的 mConsumer 變量拐格, 所以 在 getBufferLocked 就會(huì)從 Inner BQ 中獲取 Buffer.
  3. 接下來(lái)在 Camera3SharedOutputStream 中 的 getBufferLocked方法中, 會(huì)從 Inner BQ中申請(qǐng)出來(lái)一個(gè) Buffer, 交給 Provider 去生產(chǎn)。
  4. 接下來(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)容的清理了(mInputSlotsmDetachedBuffers), 或者給更新成新內(nèi)容了(mOutputSlots, mBuffers)

  1. 然后 Inner Surface 會(huì)調(diào)用 queueBuffer 把 該 Buffer 放入 Inner BQ 中, 這個(gè)時(shí)候 Camera3StreamSplitteronFrameAvailable就會(huì)被調(diào)用就轧。 然后 Inner Consumer會(huì)調(diào)用acquireBuffer 獲取到這個(gè) Buffer, 也就是 BufferItem. 然后把這個(gè) BufferItem 放入 mInputSlots 中证杭。
  2. 對(duì)于每個(gè) Outter Surface, 調(diào)用outputBufferLocked 方法妒御, 下面詳細(xì)講一下outputBufferLocked方法
    6.1 先調(diào)用 getSlotForOutputLocked 得到 Slot, 還記得我們?cè)?4.2attachBuffer之前會(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中的 OutputListeneronBufferReleased方法中监右, 最終調(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)航邢, dequeueBuffermFreeSlots中取值的情況呢?
因?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)勒极,就是mBuffersmOutputSlots,
接下來(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是完全不考慮幀率的...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末独旷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子寥裂,更是在濱河造成了極大的恐慌嵌洼,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件封恰,死亡現(xiàn)場(chǎng)離奇詭異麻养,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)诺舔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)鳖昌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人低飒,你說(shuō)我怎么就攤上這事许昨。” “怎么了褥赊?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵糕档,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拌喉,道長(zhǎng)翼岁,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任司光,我火速辦了婚禮琅坡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘残家。我一直安慰自己榆俺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布坞淮。 她就那樣靜靜地躺著茴晋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪回窘。 梳的紋絲不亂的頭發(fā)上诺擅,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音啡直,去河邊找鬼烁涌。 笑死苍碟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撮执。 我是一名探鬼主播微峰,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼抒钱!你這毒婦竟也來(lái)了蜓肆?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤谋币,失蹤者是張志新(化名)和其女友劉穎仗扬,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蕾额,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡厉颤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凡简。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逼友。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖秤涩,靈堂內(nèi)的尸體忽然破棺而出帜乞,到底是詐尸還是另有隱情,我是刑警寧澤筐眷,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布黎烈,位于F島的核電站,受9級(jí)特大地震影響匀谣,放射性物質(zhì)發(fā)生泄漏照棋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一武翎、第九天 我趴在偏房一處隱蔽的房頂上張望烈炭。 院中可真熱鬧,春花似錦宝恶、人聲如沸符隙。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)霹疫。三九已至,卻和暖如春综芥,著一層夾襖步出監(jiān)牢的瞬間丽蝎,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工膀藐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留屠阻,地道東北人红省。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像栏笆,于是被迫代替她去往敵國(guó)和親类腮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子臊泰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容