TextureView 類別是檢視區塊物件,可將檢視區塊與 SurfaceTexture 合併。
使用 OpenGL ES 轉譯
TextureView 物件會包裝 SurfaceTexture、回應回呼,並取得新緩衝區。當 TextureView 物件取得新緩衝區時,TextureView 會發出檢視區塊失效要求,並使用最新緩衝區的內容做為資料來源進行繪製,在檢視區塊狀態指出應顯示的位置和方式進行算繪。
OpenGL ES (GLES) 可以透過將 SurfaceTexture 傳遞至 EGL 建立呼叫,在 TextureView 物件上算繪,但這會造成問題。當 GLES 在 TextureView 上算繪時,BufferQueue 的生產者和消費者會位於同一執行緒,這可能會導致緩衝區交換呼叫停滯或失敗。舉例來說,如果產生者從 UI 執行緒快速連續提交多個緩衝區,EGL 緩衝區交換呼叫需要從 BufferQueue 取消佇列緩衝區。不過,由於取用端和生產端位於同一個執行緒,因此不會有任何緩衝區可用,交換呼叫會暫停或失敗。
為避免緩衝區交換停滯,BufferQueue 一律需要可供取消佇列的緩衝區。為此,當新的緩衝區排入佇列時,BufferQueue 會捨棄先前取得的緩衝區內容。此外,系統也會限制緩衝區數量下限和上限,避免消費者一次耗用所有緩衝區。
選擇 SurfaceView 或 TextureView
SurfaceView 和 TextureView 扮演類似的角色,都是檢視區塊階層的成員。不過,SurfaceView 和 TextureView 的實作方式不同。SurfaceView 物件會採用與其他檢視區塊相同的參數,但系統在算繪時會將 SurfaceView 內容設為透明。
TextureView 的 Alpha 和旋轉處理功能比 SurfaceView 更出色,但合成疊加在影片上的 UI 元素時,SurfaceView 的效能更勝一籌。當用戶端使用 SurfaceView 物件算繪時,SurfaceView 會為用戶端提供獨立的組合層。如果裝置支援,SurfaceFlinger 會將個別圖層合成為硬體重疊畫面。當用戶端使用 TextureView 算繪時,UI 工具包會使用 GPU,將 TextureView 物件的內容合成到檢視區塊階層中。更新內容可能會導致其他檢視區塊元素重新繪製,例如其他檢視區塊位於 TextureView 頂端時。檢視區塊完成算繪後,SurfaceFlinger 會合成應用程式 UI 層和所有其他層,因此每個可見像素都會合成兩次。
個案研究:Grafika 的「播放影片」
Grafika 的 Play Video 包含一對影片播放器,其中一個是使用 TextureView 實作,另一個是使用 SurfaceView 實作。活動的影片解碼部分會將 MediaCodec 中的影格傳送至 TextureView 和 SurfaceView 的介面。實作方式之間最大的差異,在於呈現正確顯示比例所需的步驟。
如要縮放 SurfaceView,必須自訂 FrameLayout 的實作方式。
WindowManager 必須將新的視窗位置和大小值傳送至 SurfaceFlinger。如要縮放 TextureView 物件的 SurfaceTexture,請使用 TextureView#setTransform() 設定轉換矩陣。
呈現正確顯示比例後,這兩種實作方式都會遵循相同的模式。當 SurfaceView 或 TextureView 建立介面時,應用程式程式碼會啟用播放功能。使用者輕觸「播放」時,系統會啟動影片解碼執行緒,並以 Surface 做為輸出目標。之後,應用程式程式碼不會執行任何動作,因為合成和顯示作業是由 SurfaceFlinger (適用於 SurfaceView) 或 TextureView 處理。
個案研究:Grafika 的雙重解碼
Grafika 的 Double Decode 示範如何操控 TextureView 內的 SurfaceTexture。
Grafika 的 Double Decode 會使用一對 TextureView 物件並排顯示兩部影片,模擬視訊會議應用程式。當螢幕方向改變且活動重新啟動時,MediaCodec 解碼器不會停止,模擬即時影片串流的播放。為提升效率,用戶端應保持介面運作。Surface 是 SurfaceTexture 的 BufferQueue 中,產生者介面的控制代碼。由於 TextureView 物件會管理 SurfaceTexture,因此用戶端必須讓 SurfaceTexture 維持運作,才能讓 Surface 保持運作。
為保持 SurfaceTexture 存續,Grafika 的 Double Decode 會從 TextureView 物件取得 SurfaceTexture 的參照,並將其儲存在靜態欄位中。接著,Grafika 的 Double Decode 會從 false 回傳 TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed(),避免 SurfaceTexture 遭到毀損。TextureView 接著會將 SurfaceTexture 傳遞至 onSurfaceTextureDestroyed(),該 SurfaceTexture 可在活動設定變更時維持不變,而用戶端會透過 setSurfaceTexture() 將其傳遞至新的 TextureView 物件。
每個影片解碼器都會使用獨立執行緒。Mediaserver 會將含有解碼輸出的緩衝區傳送至 SurfaceTextures (BufferQueue 消費者)。TextureView 物件會在 UI 執行緒上執行轉譯作業。
使用 SurfaceView 實作 Grafika 的雙重解碼比使用 TextureView 實作更困難,因為 SurfaceView 物件會在螢幕方向變更時刪除表面。此外,使用 SurfaceView 物件會新增兩層,但硬體可用的疊加層數量有限,因此這並非理想做法。