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
dont les applications sont les consommateurs. Le rappel onFrameAvailable()
avertit 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 à 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
) des manières suivantes :
- Les textures externes restituent les polygones texturés directement à partir des données reçues de
BufferQueue
. - Les rendus de texture externes sont configurés différemment des rendus de texture GLES traditionnels.
- Les textures externes ne peuvent pas effectuer toutes les activités de texture GLES traditionnelles.
Le principal avantage des textures externes est leur capacité à être restituées 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 garantir que les données dans le 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 plus d’informations, consultez la documentation de la classe SurfaceTexture
.
Horodatages et transformations
Les instances SurfaceTexture
incluent la méthode getTimeStamp()
, qui récupère un horodatage, et la méthode getTransformMatrix()
, qui récupère une matrice de transformation. L'appel de updateTexImage()
définit à la fois l'horodatage et la matrice de transformation. Chaque tampon transmis par BufferQueue
comprend des paramètres de transformation et un horodatage.
Les paramètres de transformation sont utiles pour l’efficacité. Dans certains cas, les données sources peuvent être mal orientées pour le consommateur. Au lieu de faire pivoter les données avant de les envoyer au consommateur, envoyez les données 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, minimisant ainsi les frais généraux.
L'horodatage est utile pour les sources 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 image doit avoir un horodatage de présentation à partir du moment où l'image a été capturée, et non à partir du moment où l'application a reçu l'image. Le code de la caméra définit l'horodatage fourni avec le tampon, ce qui donne lieu à une série d'horodatages 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 à afficher ces images à l'écran. Pour enregistrer des images, créez une surface avec la méthode createInputSurface()
de la classe MediaCodec et transmettez la surface à la caméra. Pour afficher des images, créez une instance de SurfaceView
et transmettez la surface à setPreviewDisplay()
. Notez que l'enregistrement d'images et leur affichage en même temps est un processus plus complexe.
L'activité de capture continue affiche la vidéo de la caméra pendant l'enregistrement de la vidéo. Dans ce cas, la vidéo codée est écrite dans un tampon circulaire en mémoire qui peut être enregistrée sur le disque à tout moment.
Ce flux implique trois files d'attente tampon :
-
App
— L'application utilise une instanceSurfaceTexture
pour recevoir des images de la caméra, les convertissant en une texture GLES externe. -
SurfaceFlinger
— L'application déclare une instanceSurfaceView
pour afficher les images. -
MediaServer
— Configurez un encodeurMediaCodec
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 sarcelle, les consommateurs sont verts).
La vidéo codée H.264 est envoyée dans un tampon circulaire dans la RAM au cours du processus d'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 de l'interface utilisateur. La gestion des données codées (gestion d'un tampon circulaire et écriture sur le disque) se fait sur un thread séparé.
SurfaceView
, le rappel surfaceCreated()
crée les instances EGLContext
et EGLSurface
pour l'affichage et l'encodeur vidéo. Lorsqu'une nouvelle image arrive, SurfaceTexture
effectue quatre activités :- Acquiert le cadre.
- Rend le cadre disponible sous forme de texture GLES.
- Rend le cadre avec les commandes GLES.
- Transfère la transformation et l'horodatage pour chaque instance de
EGLSurface
.
Le thread de l'encodeur extrait ensuite la sortie codée de MediaCodec
et la stocke en mémoire.
Lecture vidéo de texture sécurisée
Android prend en charge le post-traitement GPU du contenu vidéo protégé. Cela permet aux applications d'utiliser le GPU pour des effets vidéo complexes et non linéaires (tels que des déformations), en mappant le contenu vidéo protégé sur des textures à utiliser dans des scènes graphiques générales (par exemple, en utilisant GLES) et en réalité virtuelle (VR).
La prise en charge est activée à l'aide des deux extensions suivantes :
- Extension EGL — (
EGL_EXT_protected_content
) Permet la création de contextes et de surfaces GL protégés, qui peuvent tous deux fonctionner sur du contenu protégé. - Extension GLES — (
GL_EXT_protected_textures
) Permet de marquer 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 fait pas la queue vers SurfaceFlinger
et fournit une surface vidéo protégée à utiliser dans un contexte protégé. Cela se fait en définissant le bit consommateur protégé ( GRALLOC_USAGE_PROTECTED
) sur les surfaces créées dans un contexte protégé (vérifié par ACodec).
La lecture vidéo à texture sécurisée constitue la base d'une mise en œuvre solide de DRM dans l'environnement OpenGL ES. Sans une implémentation DRM solide, telle que Widevine Level 1, de nombreux fournisseurs de contenu n'autorisent pas le rendu de leur contenu de grande valeur dans l'environnement OpenGL ES, empêchant ainsi des cas d'utilisation importants de la VR, tels que le visionnage de contenu protégé par DRM en VR.
AOSP inclut un code-cadre pour la lecture vidéo de texture sécurisée. La prise en charge des pilotes appartient aux OEM. Les implémenteurs de périphériques doivent implémenter les extensions EGL_EXT_protected_content
et GL_EXT_protected_textures extensions
. Lorsque vous utilisez votre propre bibliothèque de codecs (pour remplacer libstagefright
), notez les changements dans /frameworks/av/media/libstagefright/SurfaceUtils.cpp
qui permettent aux tampons marqués avec GRALLOC_USAGE_PROTECTED
d'être envoyés à ANativeWindow
(même si ANativeWindow
ne fait pas la queue directement vers le window composer) tant que les bits d'utilisation du consommateur contiennent GRALLOC_USAGE_PROTECTED
. Pour une documentation détaillée sur la mise en œuvre des extensions, reportez-vous aux registres Khronos ( EGL_EXT_protected_content
et GL_EXT_protected_textures
).