轉(zhuǎn)自:https://source.android.com/devices/graphics/?hl=zh-cn
TextureView
我們在 Android 4.0 中引入了 TextureView 類雕崩,它結(jié)合了 View 與 SurfaceTexture掠手,是我們在此討論的最復(fù)雜的 View 對象。
使用 GLES 呈現(xiàn)
我們已經(jīng)知道,SurfaceTexture 是一個“GL 消費者”,它會占用圖形數(shù)據(jù)的緩沖區(qū)寿烟,并將它們作為紋理進行提供。TextureView 會對 SurfaceTexture 進行封裝辛燥,并接管對回調(diào)做出響應(yīng)以及獲取新緩沖區(qū)的責任筛武。新緩沖區(qū)的就位會導(dǎo)致 TextureView 發(fā)出 View 失效請求。當被要求進行繪圖時挎塌,TextureView 會使用最近收到的緩沖區(qū)的內(nèi)容作為數(shù)據(jù)源徘六,并根據(jù) View 狀態(tài)的指示,以相應(yīng)的方式在相應(yīng)的位置進行呈現(xiàn)榴都。
您可以使用 GLES 在 TextureView 上呈現(xiàn)內(nèi)容待锈,就像在 SurfaceView 上一樣。只需將 SurfaceTexture 傳遞到 EGL 窗口創(chuàng)建調(diào)用即可嘴高。不過竿音,這樣做會導(dǎo)致潛在問題和屎。
在我們看到的大部分內(nèi)容中,BufferQueue 是在不同進程之間傳遞緩沖區(qū)春瞬。當使用 GLES 呈現(xiàn)到 TextureView 時柴信,生產(chǎn)者和消費者處于同一進程中,它們甚至可能會在單個線程上得到處理宽气。假設(shè)我們以快速連續(xù)的方式從界面線程提交多個緩沖區(qū)随常。EGL 緩沖區(qū)交換調(diào)用需要使一個緩沖區(qū)從 BufferQueue 出列,而在有可用的緩沖區(qū)之前萄涯,它將處于暫停狀態(tài)绪氛。只有當消費者獲取一個緩沖區(qū)用于呈現(xiàn)時才會有可用的緩沖區(qū)程梦,但是這一過程也會發(fā)生在界面線程上…因此我們陷入了困境慎玖。
解決方案是讓 BufferQueue 確保始終有一個可用的緩沖區(qū)能夠出列,以使緩沖區(qū)交換始終不會暫停蒸眠。要保證能夠?qū)崿F(xiàn)這一點袄琳,一種方法是讓 BufferQueue 在新緩沖區(qū)加入隊列時舍棄之前加入隊列的緩沖區(qū)的內(nèi)容询件,并對最小緩沖區(qū)計數(shù)和最大獲取緩沖區(qū)計數(shù)施加限制(如果您的隊列有三個緩沖區(qū)燃乍,而所有這三個緩沖區(qū)均被消費者獲取唆樊,那么就沒有可以出列的緩沖區(qū),緩沖區(qū)交換調(diào)用必然會暫涂绦罚或失敗逗旁。因此我們需要防止消費者一次獲取兩個以上的緩沖區(qū))。丟棄緩沖區(qū)通常是不可取的舆瘪,因此僅允許在特定情況下發(fā)生片效,例如生產(chǎn)者和消費者處于同一進程中時。
SurfaceView 還是 TextureView英古?
SurfaceView 和 TextureView 扮演的角色類似淀衣,但是擁有截然不同的實現(xiàn)。要作出最合適的選擇召调,則需要了解它們各自的利弊膨桥。
因為 TextureView 是 View 層次結(jié)構(gòu)的固有成員,所以其行為與其他所有 View 一樣唠叛,可以與其他元素相互疊加只嚣。您可以執(zhí)行任意轉(zhuǎn)換,并通過簡單的 API 調(diào)用將內(nèi)容檢索為位圖艺沼。
影響 TextureView 的主要因素是合成步驟的表現(xiàn)册舞。使用 SurfaceView 時,內(nèi)容可以寫到 SurfaceFlinger(理想情況下使用疊加層)合成的獨立分層中障般。使用 TextureView 時调鲸,View 合成往往使用 GLES 執(zhí)行盛杰,并且對其內(nèi)容進行的更新也可能會導(dǎo)致其他 View 元素重繪(例如,如果它們位于 TextureView 上方)藐石。View 呈現(xiàn)完成后饶唤,應(yīng)用界面層必須由 SurfaceFlinger 與其他分層合成,以便您可以高效地將每個可見像素合成兩次贯钩。對于全屏視頻播放器募狂,或任何其他相當于位于視頻上方的界面元素的應(yīng)用,SurfaceView 可以帶來更好的效果角雷。
如之前所述祸穷,受 DRM 保護的視頻只能在疊加平面上呈現(xiàn)。支持受保護內(nèi)容的視頻播放器必須使用 SurfaceView 進行實現(xiàn)勺三。
案例研究:Grafika 的視頻播放 (TextureView)
Grafika 包括一對視頻播放器雷滚,一個用 TextureView 實現(xiàn),另一個用 SurfaceView 實現(xiàn)吗坚。對于這兩個視頻播放器來說祈远,僅將幀從 MediaCodec 發(fā)送到 Surface 的視頻解碼部分是一樣的。這兩種實現(xiàn)之間最有趣的區(qū)別是呈現(xiàn)正確寬高比所需的步驟商源。
SurfaceView 需要 FrameLayout 的自定義實現(xiàn)车份,而要重新調(diào)整 SurfaceTexture 的大小,只需使用TextureView#setTransform()配置轉(zhuǎn)換矩陣即可牡彻。對于前者扫沼,您會通過 WindowManager 向 SurfaceFlinger 發(fā)送新的窗口位置和大小值;對于后者庄吼,您僅僅是在以不同的方式呈現(xiàn)它缎除。
否則,兩種實現(xiàn)均遵循相同的模式总寻。創(chuàng)建 Surface 后器罐,系統(tǒng)會啟用播放。點擊“播放”時渐行,系統(tǒng)會啟動視頻解碼線程轰坊,并將 Surface 作為輸出目標。之后殊轴,應(yīng)用代碼不需要執(zhí)行任何操作衰倦,SurfaceFlinger(適用于 SurfaceView)或 TextureView 會處理合成和顯示。
案例研究:Grafika 的雙重解碼
此操作組件演示了在 TextureView 中對 SurfaceTexture 的操控旁理。
此操作組件的基本結(jié)構(gòu)是一對顯示兩個并排播放的不同視頻的 TextureView樊零。為了模擬視頻會議應(yīng)用的需求,我們希望在操作組件因屏幕方向發(fā)生變化而暫停和恢復(fù)時,MediaCodec 解碼器能保持活動狀態(tài)驻襟。原因在于夺艰,如果不對 MediaCodec 解碼器使用的 Surface 進行完全重新配置,就無法更改它沉衣,而這是成本相當高的操作郁副;因此我們希望 Surface 保持活動狀態(tài)。Surface 只是 SurfaceTexture 的 BufferQueue 中生產(chǎn)者界面的句柄豌习,而 SurfaceTexture 由 TextureView 管理存谎;因此我們還需要 SurfaceTexture 保持活動狀態(tài)。那么我們?nèi)绾翁幚?TextureView 被關(guān)閉的情況呢肥隆?
TextureView 提供的setSurfaceTexture()調(diào)用正好能夠滿足我們的需求既荚。我們從 TextureView 獲取對 SurfaceTexture 的引用,并將它們保存在靜態(tài)字段中栋艳。當操作組件被關(guān)閉時恰聘,我們從onSurfaceTextureDestroyed()回調(diào)返回“false”,以防止 SurfaceTexture 被銷毀吸占。當操作組件重新啟動時晴叨,我們將原來的 SurfaceTexture 填充到新的 TextureView 中。TextureView 類負責創(chuàng)建和破壞 EGL 上下文矾屯。
每個視頻解碼器都是從單獨的線程驅(qū)動的兼蕊。乍一看,我們似乎需要每個線程的本地 EGL 上下文问拘;但請注意遍略,具有解碼輸出的緩沖區(qū)實際上是從 mediaserver 發(fā)送給我們的 BufferQueue 消費者 (SurfaceTexture)惧所。TextureView 會為我們處理呈現(xiàn)骤坐,并在界面線程上執(zhí)行。
使用 SurfaceView 實現(xiàn)該操作組件可能較為困難下愈。我們不能只創(chuàng)建一對 SurfaceView 并將輸出引導(dǎo)至它們纽绍,因為 Surface 在屏幕方向改變期間會被銷毀。此外势似,這樣做會增加兩個層拌夏,而由于可用疊加層的數(shù)量限制,我們不得不盡量將層數(shù)量減到最少履因。與上述方法不同障簿,我們希望創(chuàng)建一對 SurfaceTexture,以從視頻解碼器接收輸出栅迄,然后在應(yīng)用中執(zhí)行呈現(xiàn)站故,使用 GLES 將兩個紋理間隙呈現(xiàn)到 SurfaceView 的 Surface。
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 3.0 License, and code samples are licensed under theApache 2.0 License. For details, see ourSite Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 九月 6, 2017.