SurfaceTexture
是表面和OpenGL ES (GLES)紋理的組合。 SurfaceTexture
實例用於提供輸出到 GLES 紋理的表面。
SurfaceTexture
包含應用程序作為消費者的BufferQueue
實例。當生產者排隊一個新的緩衝區時, onFrameAvailable()
回調會通知應用程序。然後,應用程序調用updateTexImage()
,它釋放先前持有的緩衝區,從隊列中獲取新緩衝區,並進行EGL調用以使 GLES 可以將緩衝區作為外部紋理使用。
外部 GLES 紋理
外部 GLES 紋理 ( GL_TEXTURE_EXTERNAL_OES
) 在以下方面與傳統 GLES 紋理 ( GL_TEXTURE_2D
) 不同:
- 外部紋理直接根據從
BufferQueue
接收的數據渲染紋理多邊形。 - 外部紋理渲染器的配置與傳統 GLES 紋理渲染器不同。
- 外部紋理不能執行所有傳統的 GLES 紋理活動。
外部紋理的主要好處是它們能夠直接從BufferQueue
數據進行渲染。 SurfaceTexture
實例在為外部紋理創建BufferQueue
實例時將使用者使用標誌設置為GRALLOC_USAGE_HW_TEXTURE
,以確保 GLES 可識別緩衝區中的數據。
因為SurfaceTexture
實例與 EGL 上下文交互,所以應用程序只能在擁有紋理的 EGL 上下文在調用線程上時調用其方法。有關詳細信息,請參閱SurfaceTexture
類文檔。
時間戳和轉換
SurfaceTexture
實例包括檢索時間戳的getTimeStamp()
方法和檢索變換矩陣的getTransformMatrix()
方法。調用updateTexImage()
設置時間戳和轉換矩陣。 BufferQueue
傳遞的每個緩衝區都包含轉換參數和時間戳。
轉換參數對效率很有用。在某些情況下,源數據可能對消費者來說方向不正確。與其在將數據發送給消費者之前旋轉數據,不如將數據按其方向發送,並進行更正的轉換。使用數據時,轉換矩陣可以與其他轉換合併,從而最大限度地減少開銷。
時間戳對於時間相關的緩衝區源很有用。例如,當setPreviewTexture()
將生產者接口連接到相機的輸出時,來自相機的幀可用於創建視頻。每個幀都需要有一個從捕獲幀時開始的演示時間戳,而不是從應用程序接收到幀時的時間戳。相機代碼設置緩衝區提供的時間戳,從而產生更一致的時間戳系列。
案例研究:Grafika 的連續捕獲
Grafika 的連續捕捉包括從設備的攝像頭記錄幀並將這些幀顯示在屏幕上。要記錄幀,請使用MediaCodec類的createInputSurface()
方法創建一個表面並將該表面傳遞給相機。要顯示幀,請創建SurfaceView
的實例並將表面傳遞給setPreviewDisplay()
。請注意,記錄幀並同時顯示它們是一個更複雜的過程。
在錄製視頻時,連續捕獲活動會顯示來自攝像機的視頻。在這種情況下,編碼的視頻被寫入內存中的循環緩衝區,可以隨時保存到磁盤。
此流程涉及三個緩衝區隊列:
-
App
— 應用程序使用SurfaceTexture
實例從相機接收幀,將它們轉換為外部 GLES 紋理。 -
SurfaceFlinger
— 應用聲明一個SurfaceView
實例來顯示幀。 -
MediaServer
— 使用輸入表面配置MediaCodec
編碼器以創建視頻。
在下圖中,箭頭表示來自相機的數據傳播。 BufferQueue
實例是彩色的(生產者是藍綠色的,消費者是綠色的)。
編碼的 H.264 視頻在應用程序進程中進入 RAM 中的循環緩衝區。當用戶按下捕獲按鈕時, MediaMuxer
類將編碼的視頻寫入磁盤上的 MP4 文件。
所有BufferQueue
實例都在應用程序中使用單個 EGL 上下文進行處理,而 GLES 操作在 UI 線程上執行。編碼數據的處理(管理循環緩衝區並將其寫入磁盤)在單獨的線程上完成。
SurfaceView
類時, surfaceCreated()
回調會為顯示器和視頻編碼器創建EGLContext
和EGLSurface
實例。當新幀到達時, SurfaceTexture
執行四個活動:- 獲取幀。
- 使框架可用作 GLES 紋理。
- 使用 GLES 命令渲染框架。
- 轉發每個
EGLSurface
實例的轉換和時間戳。
然後,編碼器線程從MediaCodec
提取編碼輸出並將其存儲在內存中。
安全紋理視頻播放
Android 支持受保護視頻內容的 GPU 後處理。這讓應用程序可以使用 GPU 實現複雜的非線性視頻效果(例如扭曲),將受保護的視頻內容映射到紋理上以用於一般圖形場景(例如,使用 GLES)和虛擬現實 (VR)。
使用以下兩個擴展啟用支持:
- EGL 擴展— (
EGL_EXT_protected_content
) 允許創建受保護的 GL 上下文和表面,它們都可以對受保護的內容進行操作。 - GLES 擴展- (
GL_EXT_protected_textures
) 啟用將紋理標記為受保護,以便它們可以用作幀緩衝區紋理附件。
Android 允許SurfaceTexture
和 ACodec ( libstagefright.so
) 發送受保護的內容,即使窗口的表面沒有排隊到SurfaceFlinger
並提供受保護的視頻表面以在受保護的上下文中使用。這是通過在受保護的上下文(由 ACodec 驗證)中創建的表面上設置受保護的消費者位 ( GRALLOC_USAGE_PROTECTED
) 來完成的。
安全的紋理視頻播放為在 OpenGL ES 環境中實現強大的 DRM 奠定了基礎。如果沒有強大的 DRM 實施,例如 Widevine Level 1,許多內容提供商不允許在 OpenGL ES 環境中渲染他們的高價值內容,從而阻止重要的 VR 用例,例如在 VR 中觀看受 DRM 保護的內容。
AOSP 包括用於安全紋理視頻播放的框架代碼。驅動程序支持取決於 OEM。設備實現者必須實現EGL_EXT_protected_content
和GL_EXT_protected_textures extensions
。使用您自己的編解碼器庫(替換libstagefright
)時,請注意/frameworks/av/media/libstagefright/SurfaceUtils.cpp
中的更改允許將標記為GRALLOC_USAGE_PROTECTED
的緩衝區發送到ANativeWindow
(即使ANativeWindow
不直接排隊到窗口作曲家)只要消費者使用位包含GRALLOC_USAGE_PROTECTED
。有關實現擴展的詳細文檔,請參閱 Khronos 註冊表( EGL_EXT_protected_content
和GL_EXT_protected_textures
)。