SurfaceTexture

SurfaceTexture est une combinaison d'une surface et d'une texture OpenGL ES (GLES). Les instances SurfaceTexture sont utilisées pour fournir des surfaces qui génèrent des textures GLES.

SurfaceTexture contient une instance de BufferQueue pour laquelle les applications sont le consommateur. Le rappel onFrameAvailable() avertit les applications lorsque le producteur met en file d'attente un nouveau tampon. Ensuite, les applications appellent updateTexImage(), ce qui libère le tampon précédemment détenu, acquiert le nouveau tampon à partir de la file d'attente et effectue des appels EGL pour rendre le tampon disponible pour GLES en tant que texture externe.

Textures GLES externes

Les textures GLES externes (GL_TEXTURE_EXTERNAL_OES) diffèrent des textures GLES standards (GL_TEXTURE_2D) sur les points suivants :

  • Les textures externes affichent les polygones texturés directement à partir des données reçues de BufferQueue.
  • Les renderers de texture externes sont configurés différemment des renderers de texture GLES standards.
  • Les textures externes ne peuvent pas effectuer toutes les activités de texture GLES standards.

L'avantage principal des textures externes est leur capacité à s'afficher directement à partir des données BufferQueue. Les instances SurfaceTexture définissent les indicateurs d'utilisation du consommateur sur GRALLOC_USAGE_HW_TEXTURE lorsqu'elles créent des instances BufferQueue pour les textures externes afin de vérifier que les données du tampon sont reconnaissables par GLES.

Étant donné que les instances SurfaceTexture interagissent avec un contexte EGL, une application ne peut appeler ses méthodes que lorsque le contexte EGL qui possède la texture est actuel sur le thread appelant. Pour en savoir plus, consultez la documentation sur la classe SurfaceTexture.

Codes temporels et transformations

Les instances SurfaceTexture incluent la méthode getTimeStamp(), qui récupère un code temporel, et la méthode getTransformMatrix(), qui récupère une matrice de transformation. L'appel de updateTexImage() définit à la fois le code temporel et la matrice de transformation. Chaque tampon que BufferQueue transmet inclut des paramètres de transformation et un code temporel.

Les paramètres de transformation sont utiles pour l'efficacité. Dans certains cas, les données sources peuvent être orientées de manière incorrecte pour le consommateur. Au lieu de faire pivoter les données avant de les envoyer au consommateur, envoyez-les dans leur orientation avec une transformation qui les corrige. La matrice de transformation peut être fusionnée avec d'autres transformations lorsque les données sont utilisées, ce qui minimise la surcharge.

L'horodatage est utile pour les sources de tampon qui dépendent du temps. Par exemple, lorsque setPreviewTexture() connecte l'interface du producteur à la sortie de la caméra, les images de la caméra peuvent être utilisées pour créer une vidéo. Chaque frame doit comporter un code temporel de présentation indiquant le moment où il a été capturé, et non celui où l'application l'a reçu. Le code de la caméra définit le code temporel fourni avec le tampon, ce qui permet d'obtenir une série de codes temporels plus cohérente.

Étude de cas : la capture continue de Grafika

La capture continue de Grafika consiste à enregistrer des images à partir de la caméra d'un appareil et à les afficher à l'écran. Pour enregistrer des frames, créez une surface avec la méthode createInputSurface() de la classe MediaCodec et transmettez la surface à la caméra. Pour afficher des frames, créez une instance de SurfaceView et transmettez la surface à setPreviewDisplay(). Notez que l'enregistrement et l'affichage simultanés des frames sont plus complexes.

L'activité Capture en continu affiche la vidéo de la caméra pendant l'enregistrement. Dans ce cas, la vidéo encodée est écrite dans une mémoire tampon circulaire en mémoire, qui peut être enregistrée sur le disque à tout moment.

Ce flux comporte trois files d'attente de mémoire tampon :

  • App : l'application utilise une instance SurfaceTexture pour recevoir les frames de la caméra et les convertir en texture GLES externe.
  • SurfaceFlinger : l'application déclare une instance SurfaceView pour afficher les frames.
  • MediaServer : configurez un encodeur MediaCodec avec une surface d'entrée pour créer la vidéo.

Dans la figure suivante, les flèches indiquent la propagation des données depuis la caméra. Les instances BufferQueue sont affichées, avec des indicateurs visuels permettant de distinguer les producteurs (bleu turquoise) des consommateurs (vert).

Activité de capture continue Grafika

Figure 1 : Activité de capture continue de Grafika

La vidéo H.264 encodée est placée dans une mémoire tampon circulaire de la RAM dans le processus de l'application. Lorsqu'un utilisateur appuie sur le bouton de capture, la classe MediaMuxer écrit la vidéo encodée dans un fichier MP4 sur le disque.

Toutes les instances BufferQueue sont gérées avec un seul contexte EGL dans l'application, tandis que les opérations GLES sont effectuées sur le thread d'UI. La gestion des données encodées (gestion d'une mémoire tampon circulaire et écriture sur le disque) est effectuée sur un thread distinct.

Lorsque vous utilisez la classe SurfaceView, le rappel surfaceCreated() crée les instances EGLContext et EGLSurface pour l'affichage et l'encodeur vidéo. Lorsqu'un nouveau frame arrive, SurfaceTexture effectue quatre activités :

  1. Acquiert le frame.
  2. Rend le frame disponible en tant que texture GLES.
  3. Affiche le frame avec les commandes GLES.
  4. Transmet la transformation et le code temporel pour chaque instance de EGLSurface.

Le thread de l'encodeur extrait ensuite la sortie encodée de MediaCodec et la stocke en mémoire.

Lecture sécurisée des vidéos de textures

Android est compatible avec le post-traitement GPU des contenus vidéo protégés. Cela permet aux applications d'utiliser le GPU pour des effets vidéo complexes et non linéaires (tels que des déformations), de mapper du contenu vidéo protégé sur des textures à utiliser dans des scènes graphiques générales (par exemple, en utilisant GLES) et de la réalité virtuelle (RV).

Lecture sécurisée de vidéos de texture

Figure 2. Lecture sécurisée des vidéos de textures

La compatibilité est activée à l'aide des deux extensions suivantes :

  • Extension EGL — (EGL_EXT_protected_content) Permet de créer des surfaces et des contextes GL protégés, qui peuvent tous deux fonctionner sur du contenu protégé.
  • Extension GLES : (GL_EXT_protected_textures) permet de taguer les textures comme protégées afin qu'elles puissent être utilisées comme pièces jointes de texture de framebuffer.

Android permet à SurfaceTexture et ACodec(libstagefright.so) d'envoyer du contenu protégé même si la surface de la fenêtre ne met pas en file d'attente SurfaceFlinger et fournit une surface vidéo protégée à utiliser dans un contexte protégé. Pour ce faire, le bit de consommateur protégé (GRALLOC_USAGE_PROTECTED) est défini sur les surfaces créées dans un contexte protégé (vérifié par ACodec).

La lecture sécurisée de vidéos de textures constitue la base d'une solide implémentation de la gestion des droits numériques (DRM) dans l'environnement OpenGL ES. Sans une implémentation DRM solide, telle que Widevine niveau 1, de nombreux fournisseurs de contenu n'autorisent pas le rendu de leurs contenus à forte valeur ajoutée dans l'environnement OpenGL ES, ce qui empêche d'importants cas d'utilisation de la VR, comme regarder des contenus protégés par DRM en VR.

Le projet Android Open Source (AOSP) inclut le code du framework pour la lecture sécurisée de vidéos avec texture. La compatibilité des pilotes dépend des OEM. Les implémenteurs d'appareils doivent implémenter les extensions EGL_EXT_protected_content et GL_EXT_protected_textures. Lorsque vous utilisez votre propre bibliothèque de codecs (pour remplacer libstagefright), notez les modifications apportées à /frameworks/av/media/libstagefright/SurfaceUtils.cpp qui permettent d'envoyer les tampons marqués avec GRALLOC_USAGE_PROTECTED à ANativeWindow (même si ANativeWindow ne met pas directement en file d'attente le compositeur de fenêtres) tant que les bits d'utilisation du consommateur contiennent GRALLOC_USAGE_PROTECTED. Pour obtenir une documentation détaillée sur l'implémentation des extensions, consultez les registres Khronos ( EGL_EXT_protected_content et GL_EXT_protected_textures).