SurfaceTexture

SurfaceTexture è una combinazione di una superficie e una texture OpenGL ES (GLES). Le istanze SurfaceTexture vengono utilizzate per fornire superfici che generano output per le texture GLES.

SurfaceTexture contiene un'istanza di BufferQueue per cui le app sono il consumatore. Il callback onFrameAvailable() notifica alle app quando il produttore mette in coda un nuovo buffer. Poi, le app chiamano updateTexImage(), che rilascia il buffer precedentemente mantenuto, acquisisce il nuovo buffer dalla coda ed effettua chiamate EGL per rendere il buffer disponibile per GLES come texture esterna.

Texture GLES esterne

Le texture GLES esterne (GL_TEXTURE_EXTERNAL_OES) differiscono dalle texture GLES standard (GL_TEXTURE_2D) nei seguenti modi:

  • Le texture esterne eseguono il rendering di poligoni con texture direttamente dai dati ricevuti da BufferQueue.
  • I renderer di texture esterni sono configurati in modo diverso rispetto ai renderer di texture GLES standard.
  • Le texture esterne non possono eseguire tutte le attività standard delle texture GLES.

Il vantaggio principale delle texture esterne è la loro capacità di eseguire il rendering direttamente dai dati BufferQueue. Le istanze SurfaceTexture impostano i flag di utilizzo dei consumatori su GRALLOC_USAGE_HW_TEXTURE quando creano istanze BufferQueue per le texture esterne per verificare che i dati nel buffer siano riconoscibili da GLES.

Poiché le istanze di SurfaceTexture interagiscono con un contesto EGL, un'app può chiamare i relativi metodi solo se il contesto EGL proprietario della texture è attuale sul thread chiamante. Per saperne di più, consulta la documentazione della classe SurfaceTexture.

Timestamp e trasformazioni

Le istanze SurfaceTexture includono il metodo getTimeStamp(), che recupera un timestamp, e il metodo getTransformMatrix(), che recupera una matrice di trasformazione. La chiamata updateTexImage() imposta sia il timestamp sia la matrice di trasformazione. Ogni buffer che BufferQueue passa include parametri di trasformazione e un timestamp.

I parametri di trasformazione sono utili per l'efficienza. In alcuni casi, i dati di origine potrebbero avere un orientamento errato per il consumatore. Invece di ruotare i dati prima di inviarli al consumatore, inviali nel loro orientamento con una trasformazione che lo corregga. La matrice di trasformazione può essere unita ad altre trasformazioni quando vengono utilizzati i dati, riducendo al minimo l'overhead.

Il timestamp è utile per le origini buffer che dipendono dal tempo. Ad esempio, quando setPreviewTexture() collega l'interfaccia del produttore all'output della videocamera, i frame della videocamera possono essere utilizzati per creare un video. Ogni frame deve avere un timestamp di presentazione del momento in cui è stato acquisito, non del momento in cui l'app lo ha ricevuto. Il codice della videocamera imposta il timestamp fornito con il buffer, ottenendo una serie più coerente di timestamp.

Case study: acquisizione continua di Grafika

L'acquisizione continua di Grafika prevede la registrazione di frame dalla fotocamera di un dispositivo e la visualizzazione di questi frame sullo schermo. Per registrare i frame, crea una superficie con il metodo createInputSurface() della classe MediaCodec e passala alla videocamera. Per visualizzare i frame, crea un'istanza di SurfaceView e passa la superficie a setPreviewDisplay(). Tieni presente che registrare i fotogrammi e visualizzarli contemporaneamente è un processo più complesso.

L'attività acquisizione continua mostra il video della videocamera durante la registrazione. In questo caso, il video codificato viene scritto in un buffer circolare in memoria che può essere salvato su disco in qualsiasi momento.

Questo flusso prevede tre code buffer:

  • App: l'app utilizza un'istanza SurfaceTexture per ricevere i frame dalla videocamera, convertendoli in una texture GLES esterna.
  • SurfaceFlinger: l'app dichiara un'istanza SurfaceView per visualizzare i frame.
  • MediaServer: configura un codificatore MediaCodec con una superficie di input per creare il video.

Nella figura seguente, le frecce indicano la propagazione dei dati dalla videocamera. Vengono mostrate BufferQueue istanze, con indicatori visivi che distinguono i produttori (verde acqua) dai consumatori (verde).

Grafika continuous
capture activity

Figura 1. Attività di acquisizione continua di Grafika

Il video codificato in H.264 viene inserito in un buffer circolare nella RAM nel processo dell'app. Quando un utente preme il pulsante di acquisizione, la classe MediaMuxer scrive il video codificato in un file MP4 sul disco.

Tutte le istanze BufferQueue vengono gestite con un unico contesto EGL nell'app, mentre le operazioni GLES vengono eseguite sul thread UI. La gestione dei dati codificati (gestione di un buffer circolare e scrittura su disco) viene eseguita su un thread separato.

Quando utilizzi la classe SurfaceView, il callback surfaceCreated() crea le istanze EGLContext e EGLSurface per il display e il codificatore video. Quando arriva un nuovo frame, SurfaceTexture esegue quattro attività:

  1. Acquisisce il frame.
  2. Rende il frame disponibile come texture GLES.
  3. Esegue il rendering del frame con i comandi GLES.
  4. Inoltra la trasformazione e il timestamp per ogni istanza di EGLSurface.

Il thread dell'encoder estrae quindi l'output codificato da MediaCodec e lo memorizza.

Riproduzione video con texture sicura

Android supporta la post-elaborazione della GPU dei contenuti video protetti. In questo modo, le app possono utilizzare la GPU per effetti video complessi e non lineari (ad esempio, distorsioni), mappare contenuti video protetti su texture da utilizzare in scene grafiche generali (ad esempio, utilizzando GLES) e nella realtà virtuale.

Riproduzione sicura di video con texture

Figura 2. Riproduzione video con texture sicura

Il supporto è abilitato utilizzando le due estensioni seguenti:

  • Estensione EGL: (EGL_EXT_protected_content) consente la creazione di contesti e superfici GL protetti, che possono entrambi operare su contenuti protetti.
  • Estensione GLES: (GL_EXT_protected_textures) Consente di taggare le texture come protette in modo che possano essere utilizzate come allegati delle texture del framebuffer.

Android consente a SurfaceTexture e ACodec (libstagefright.so) di inviare contenuti protetti anche se la superficie della finestra non viene messa in coda a SurfaceFlinger e fornisce una superficie video protetta da utilizzare in un contesto protetto. Ciò avviene impostando il bit di consumatore protetto (GRALLOC_USAGE_PROTECTED) sulle piattaforme create in un contesto protetto (verificato da ACodec).

La riproduzione sicura dei video con texture pone le basi per una solida implementazione del Digital Rights Management (DRM) nell'ambiente OpenGL ES. Senza una solida implementazione di DRM, come Widevine Livello 1, molti fornitori di contenuti non consentono il rendering dei loro contenuti di alto valore nell'ambiente OpenGL ES, impedendo importanti casi d'uso della VR, come la visione di contenuti protetti da DRM in VR.

Android Open Source Project (AOSP) include il codice del framework per la riproduzione sicura di video con texture. Il supporto dei driver dipende dagli OEM. Gli implementatori di dispositivi devono implementare le estensioni EGL_EXT_protected_content e GL_EXT_protected_textures. Quando utilizzi la tua libreria di codec (per sostituire libstagefright), tieni presente le modifiche apportate a /frameworks/av/media/libstagefright/SurfaceUtils.cpp che consentono l'invio dei buffer contrassegnati con GRALLOC_USAGE_PROTECTED a ANativeWindow (anche se ANativeWindow non accoda direttamente al compositore di finestre) purché i bit di utilizzo del consumer contengano GRALLOC_USAGE_PROTECTED. Per la documentazione dettagliata sull'implementazione delle estensioni, consulta i registri Khronos ( EGL_EXT_protected_content) e ( GL_EXT_protected_textures).