TextureView
TextureView 類是在 Android 4.0 中引入的,且是這里討論的最復(fù)雜的 View 對(duì)象,它結(jié)合了 View 和 SurfaceTexture。
用 GLES 渲染
回憶一下,SurfaceTexture 是一個(gè) "GL 消費(fèi)者",消費(fèi)圖形數(shù)據(jù)緩沖區(qū)并使其可用作紋理。TextureView 封裝了一個(gè) SurfaceTexture,并接管了響應(yīng)回調(diào)和獲取新緩沖區(qū)的職責(zé)。新緩沖區(qū)的到達(dá)會(huì)導(dǎo)致 TextureView 發(fā)出 View invalidate 請(qǐng)求。當(dāng)被請(qǐng)求繪制時(shí),TextureView 使用最近接收到的緩沖區(qū)的內(nèi)容作為它的數(shù)據(jù)源,無(wú)論何時(shí)何地在 View 狀態(tài)表示它應(yīng)該渲染時(shí)渲染。
你可以像使用 SurfaceView 那樣通過 GLES 在 TextureView 上渲染。僅僅將 SurfaceTexture 傳遞給 EGL 窗口創(chuàng)建調(diào)用。然而,這樣做暴露了一個(gè)潛在的問題。在大部分我們研究的內(nèi)容中,BufferQueue 已經(jīng)在不同進(jìn)程間傳遞了緩沖區(qū)。當(dāng)用 GLES 向 TextureView 渲染時(shí),生產(chǎn)者和消費(fèi)者在相同的進(jìn)程中,且它們甚至可能在同一個(gè)線程中處理。假設(shè)我們從 UI 線程快速連續(xù)地提交了一些緩沖區(qū)。EGL 緩沖區(qū)交換調(diào)用將需要從 BufferQueue 獲取一塊緩沖區(qū),并且它將停止,直到有可用的緩沖區(qū)。在消費(fèi)者獲取一個(gè)緩沖區(qū)用于渲染之前,將不會(huì)有任何可用的緩沖區(qū),但那也會(huì)發(fā)生在 UI 線程 . . . 所以我們被卡住了。
解決方案是使 BufferQueue 總有一塊可用的緩沖區(qū)用于獲取,因此緩沖區(qū)交換從來(lái)不會(huì)停止。保證這一點(diǎn)的一種方式是使 BufferQueue 在新緩沖區(qū)入隊(duì)時(shí),丟棄之前入隊(duì)的緩沖區(qū)的內(nèi)容,并對(duì)最小緩沖區(qū)計(jì)數(shù)和獲取的最大緩沖區(qū)計(jì)數(shù)進(jìn)行限制。(如果你的隊(duì)列已經(jīng)有了三塊緩沖區(qū),且所有的三塊緩沖區(qū)都由消費(fèi)者獲取了,則沒有東西可以獲取了,且緩沖區(qū)交換調(diào)用必須掛起或失敗。因此我們需要阻止消費(fèi)者一次獲取多于兩塊緩沖區(qū)。)丟棄緩沖區(qū)通常是不好的,因此它僅在特定情況下啟用,例如生產(chǎn)者和消費(fèi)者處于相同進(jìn)程中。
SurfaceView 或者 TextureView?
SurfaceView 和 TextureView 扮演類似的角色,但實(shí)現(xiàn)卻差別巨大。為了確定哪個(gè)最好,需要對(duì)其中的折中有一個(gè)了解。
由于 TextureView 是 View 層次體系的一個(gè)良好公民,它的行為就像任何其它的 View,且可以覆蓋其它元素或被其它元素覆蓋。你可以執(zhí)行任意轉(zhuǎn)換,并可以通過簡(jiǎn)單的 API 調(diào)用將其內(nèi)容提取為 bitmap。
TextureView 的主要弱點(diǎn)是合成步驟的性能。通過 SurfaceView,內(nèi)容被寫入 SurfaceFlinger 組合的單獨(dú)的 layer,理想情況下通過一個(gè) overlay。通過 TextureView,View 合成總是由 GLES 執(zhí)行,且更新它的內(nèi)容也可能導(dǎo)致其它 View 元素重繪(如果它們的位置在 TextureView 之上的話)。在 View 渲染完成之后,應(yīng)用程序 UI layer 必須由 SurfaceFlinger 與其它 layers 合成,因此你在有效地合成每個(gè)可見的像素兩次。對(duì)于一個(gè)全屏視頻播放器,或任何其他實(shí)際上只是 UI 元素 layer 在視頻之上的應(yīng)用程序,SurfaceView 提供更好的性能。
如前所述,DRM保護(hù)的視頻只能在 overlay 平面上呈現(xiàn)。 支持受保護(hù)內(nèi)容的視頻播放器必須使用 SurfaceView 實(shí)現(xiàn)。
案例研究:Grafika 的播放視頻 (TextureView)
Grafika 包含了一對(duì)視頻播放器,一個(gè)用 TextureView 實(shí)現(xiàn),另一個(gè)用 SurfaceView。視頻解碼的部分,其僅僅將幀從 MediaCodec 發(fā)送到 Surface,兩者是相同的。實(shí)現(xiàn)之間大部分有趣的差異在請(qǐng)求以正確的長(zhǎng)寬比顯示的步驟。
盡管 SurfaceView 請(qǐng)求一個(gè)定制的 FrameLayout 實(shí)現(xiàn),改變 SurfaceTexture 的大小是一個(gè)通過 TextureView#setTransform() 配置一個(gè)轉(zhuǎn)換矩陣 的簡(jiǎn)單問題。對(duì)于前者,你在通過 WindowManager 向 SurfaceFlinger 發(fā)送新的窗口位置和大小值;對(duì)于后者,你只是在做不同的渲染。
此外,兩種實(shí)現(xiàn)按照相同的模式。一旦 Surface 創(chuàng)建好了,播放被啟用。當(dāng)按下 “播放”,視頻解碼線程被啟動(dòng),且以 Surface 作為輸出目標(biāo)。之后,應(yīng)用程序代碼不需要做任何事情 -- 合成和顯示將由 SurfaceFlinger(對(duì)于SurfaceView)或 TextureView 處理。
案例研究:Grafika 的雙重解碼
這個(gè) Activity 演示了 TextureView 內(nèi)部的 SurfaceTexture 管理。
這個(gè) Activity 的基本結(jié)構(gòu)是一對(duì) TextureViews,它們一邊一個(gè)展示了兩個(gè)不同的視頻播放。為了模擬視頻會(huì)議應(yīng)用的需求,我們想要在 Activity 由于方向改變而 paused 和 resumed 時(shí)保持 MediaCodec 解碼器存活。技巧在于你不能在不完全重新配置它的情況下改變 MediaCodec 解碼器使用的 Surface ,那是一個(gè)相當(dāng)昂貴的操作;因此我們想要保持 Surface 存活。Surface 只是指向SurfaceTexture 的 BufferQueue 中的生產(chǎn)者接口的句柄,且 SurfaceTexture 由 TextureView 管理;因此我們還需要保持 SurfaceTexture 存活。那么我們要如何處理 TextureView 的銷毀呢?
恰好 TextureView 提供的 setSurfaceTexture() 調(diào)用正是我們想要的。我們從 TextureViews 獲得 SurfaceTextures 的引用并在靜態(tài)成員中保存它們。當(dāng) Activity 被關(guān)閉時(shí),我們從 onSurfaceTextureDestroyed() 回調(diào)中返回 "false" 來(lái)阻止 SurfaceTexture 的銷毀。當(dāng) Activity 被重啟時(shí),我們把老的 SurfaceTexture 放進(jìn)新的 TextureView 中。TextureView 負(fù)責(zé)創(chuàng)建并銷毀 EGL contexts。
每個(gè)視頻解碼器都是從單獨(dú)的線程驅(qū)動(dòng)的。乍看起來(lái),似乎我們需要每個(gè)線程本地的 EGL contexts;但請(qǐng)記住擁有解碼的輸出的緩沖區(qū)實(shí)際上是從 mediaserver 發(fā)送到我們的 BufferQueue 消費(fèi)者(SurfaceTextures)的。TextureViews 負(fù)責(zé)為我們渲染,且它們?cè)?UI 線程執(zhí)行。
以 SurfaceView 實(shí)現(xiàn)這個(gè) Activity 更難一點(diǎn)。我們不能僅僅創(chuàng)建一對(duì) SurfaceViews 并把輸出導(dǎo)向它們,由于 Surfaces 將在屏幕方向轉(zhuǎn)變期間被銷毀。此外,那將添加兩個(gè) layers,關(guān)于可用的 overlays 的數(shù)量上的限制強(qiáng)烈地激勵(lì)我們保持 layers 的數(shù)量為最小值。相反,我們想要?jiǎng)?chuàng)建一對(duì) SurfaceTextures 來(lái)接收來(lái)自于視頻解碼器的輸出,然后在應(yīng)用中執(zhí)行渲染,使用 GLES 將兩個(gè)紋理四邊形渲染到 SurfaceView 的 Surface 上。
原文
總結(jié)
以上是生活随笔為你收集整理的TextureView的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SurfaceTexture
- 下一篇: 游戏循环