Texture de Surface

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 les consommateurs. Le rappel onFrameAvailable() informe les applications lorsque le producteur met en file d'attente un nouveau tampon. Ensuite, les applications appellent updateTexImage(), qui libère le tampon précédemment détenu, acquiert le nouveau tampon 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 traditionnelles (GL_TEXTURE_2D) comme suit:

  • Les textures externes génèrent des polygones texturés directement à partir des données reçues de BufferQueue.
  • Les moteurs de rendu de texture externes sont configurés différemment des moteurs de rendu de texture GLES traditionnels.
  • Les textures externes ne peuvent pas effectuer toutes les activités de texture GLES traditionnelles.

L'avantage principal des textures externes est leur capacité à effectuer un rendu directement à partir de 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 s'assurer 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 propriétaire de la texture est actuel sur le thread appelant. Pour en savoir plus, consultez la documentation de la classe SurfaceTexture.

Horodatages 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 transmis par BufferQueue inclut des paramètres de transformation et un code temporel.

Les paramètres de transformation sont utiles pour plus d'efficacité. Dans certains cas, l'orientation des données sources peut être 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 réduit les frais généraux.

Le code temporel est utile pour les sources de tampon dépendantes du temps. Par exemple, lorsque setPreviewTexture() connecte l'interface de production à 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 à partir du moment où il a été capturé, et non à partir du moment où l'application l'a reçu. Le code de l'appareil photo définit le code temporel fourni avec le tampon, ce qui génère une série de codes temporels plus cohérente.

Étude de cas: 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 images, créez une surface avec la méthode createInputSurface() de la classe MediaCodec, puis transmettez-la à la caméra. Pour afficher des cadres, créez une instance de SurfaceView et transmettez la surface à setPreviewDisplay(). Notez que l'enregistrement des frames et leur affichage en même temps est un processus plus complexe.

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

Ce flux implique trois files d'attente de tampon:

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

Dans la figure ci-dessous, les flèches indiquent la propagation des données depuis la caméra. Les instances BufferQueue sont en couleur (les producteurs sont bleu-vert, les consommateurs sont verts).

Activité de capture continue Grafika

Figure 1 : Activité de capture continue de Grafika

La vidéo H.264 encodée est envoyée vers un tampon circulaire dans la RAM du 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'un tampon circulaire et écriture sur 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'écran 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 des commandes GLES.
  4. Transmet la transformation et l'horodatage 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 vidéo sécurisée des 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 les déformations), de mapper du contenu vidéo protégé sur des textures à utiliser dans des scènes graphiques générales (par exemple, à l'aide de GLES) et dans la réalité virtuelle (RV).

Lecture vidéo de texture sécurisée

Figure 2. Lecture vidéo de texture sécurisée

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

  • Extension EGL : (EGL_EXT_protected_content) permet de créer des contextes et des surfaces 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 attachements 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 n'est pas mise en file d'attente dans SurfaceFlinger et fournit une surface vidéo protégée à utiliser dans un contexte protégé. Pour ce faire, définissez le bit de consommateur protégé (GRALLOC_USAGE_PROTECTED) sur les surfaces créées dans un contexte protégé (vérifié par ACodec).

La lecture vidéo de texture sécurisée jette les bases d'une implémentation DRM efficace dans l'environnement OpenGL ES. Sans une implémentation DRM efficace, telle que Widevine Level 1, de nombreux fournisseurs de contenu n'autorisent pas le rendu de leur contenu à forte valeur ajoutée dans l'environnement OpenGL ES, ce qui empêche d'importants cas d'utilisation de la VR, comme le visionnage de contenus protégés par DRM en VR.

AOSP inclut du code de framework pour la lecture vidéo de texture sécurisée. La prise en charge des pilotes dépend des OEM. Les implémentateurs d'appareils doivent implémenter EGL_EXT_protected_content et GL_EXT_protected_textures extensions. 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être) 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).