SurfaceTexture

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 例項以顏色顯示 (生產者為藍綠色,消費者為綠色)。

Grafika 持續擷取活動

圖 1. Grafika 的持續擷取活動

經過編碼的 H.264 影片會傳送至應用程式程序中 RAM 的循環緩衝區。當使用者按下擷取按鈕時,MediaMuxer 類別會將已編碼的影片寫入磁碟上的 MP4 檔案。

當 GLES 作業在 UI 執行緒上執行時,所有 BufferQueue 例項都會使用應用程式中的單一 EGL 上下文進行處理。在個別執行緒中處理編碼資料 (管理循環緩衝區並將其寫入磁碟)。

使用 SurfaceView 類別時,surfaceCreated() 回呼會為顯示器和影片編碼器建立 EGLContextEGLSurface 例項。收到新影格時,SurfaceTexture 會執行四項活動:
  1. 取得影格。
  2. 將影格做為 GLES 紋理提供。
  3. 使用 GLES 指令算繪影格。
  4. 將每個 EGLSurface 例項的轉換和時間戳記轉發。

接著,編碼器執行緒會從 MediaCodec 提取已編碼的輸出內容,並將其儲存在記憶體中。

安全的紋理影片播放

Android 支援保護影片內容的 GPU 後製處理。這項功能可讓應用程式使用 GPU 處理複雜的非線性影片效果 (例如扭曲),將受保護的影片內容對應至紋理,以便在一般圖像場景 (例如使用 GLES) 和虛擬實境 (VR) 中使用。

安全的紋理影片播放

圖 2. 安全的紋理影片播放

支援功能是透過下列兩個擴充功能啟用:

  • EGL 擴充功能:(EGL_EXT_protected_content) 可用來建立受保護的 GL 情境和介面,兩者皆可在受保護的內容上運作。
  • GLES 擴充功能 - (GL_EXT_protected_textures) 可將紋理標記為受保護,以便做為 framebuffer 紋理附件使用。

Android 可讓 SurfaceTexture 和 ACodec (libstagefright.so) 傳送受保護的內容,即使視窗的表面未排入 SurfaceFlinger 的佇列,也能提供受保護的影片表面,供受保護的內容中使用。方法是在受保護的背景 (由 ACodec 驗證) 中建立的表面上,設定受保護的消費者位元 (GRALLOC_USAGE_PROTECTED)。

安全的紋理影片播放功能可為 OpenGL ES 環境中強大的 DRM 實作奠定基礎。如果沒有強大的 DRM 實作 (例如 Widevine Level 1),許多內容供應商都不會允許在 OpenGL ES 環境中算繪高價值內容,導致無法在 VR 中觀看受 DRM 保護的內容等重要 VR 用途。

Android 開放原始碼計畫包含用於安全紋理影片播放的架構程式碼。驅動程式支援功能由原始設備製造商 (OEM) 提供。裝置實作者必須實作 EGL_EXT_protected_contentGL_EXT_protected_textures extensions。使用自訂編解碼器程式庫 (用於取代 libstagefright) 時,請注意 /frameworks/av/media/libstagefright/SurfaceUtils.cpp 中的變更,以便將標示為 GRALLOC_USAGE_PROTECTED 的緩衝區傳送至 ANativeWindow (即使 ANativeWindow 未直接排入視窗編寫器的佇列),只要使用者用途位元包含 GRALLOC_USAGE_PROTECTED 即可。如需實作擴充功能的詳細說明文件,請參閱 Khronos 登錄檔 (EGL_EXT_protected_contentGL_EXT_protected_textures)。