TextureView

La TextureView clase es un objeto de vista que combina una vista con una SurfaceTexture.

Renderiza con OpenGL ES

Un objeto TextureView encapsula una SurfaceTexture, responde a las devoluciones de llamada y adquiere búferes nuevos. Cuando un objeto TextureView adquiere búferes nuevos, TextureView emite una solicitud de invalidación de vista y dibuja con el contenido del búfer más reciente como su fuente de datos, renderizando donde y como lo indique el estado de la vista.

OpenGL ES (GLES) puede renderizar en un objeto TextureView pasando la SurfaceTexture a la llamada de creación de EGL, pero esto crea un problema. Cuando GLES renderiza en un TextureView, los productores y consumidores de BufferQueue están en el mismo subproceso, lo que puede provocar que la llamada de intercambio de búferes se detenga o falle. Por ejemplo, si un productor envía varios búferes en rápida sucesión desde el subproceso de IU, la llamada de intercambio de búferes de EGL debe quitar un búfer de la BufferQueue. Sin embargo, como el consumidor y el productor están en el mismo subproceso, no habrá búferes disponibles y la llamada de intercambio se bloqueará o fallará.

Para evitar que se detenga el intercambio de búferes, BufferQueue siempre necesita un búfer disponible para quitar de la cola. Para ello, BufferQueue descarta el contenido del búfer adquirido anteriormente cuando se pone en cola un búfer nuevo. También establece restricciones en las cantidades mínimas y máximas de búferes para evitar que un consumidor consuma todos los búferes a la vez.

Elige SurfaceView o TextureView

SurfaceView y TextureView cumplen funciones similares y son ciudadanos de la jerarquía de vistas. Sin embargo, SurfaceView y TextureView tienen implementaciones diferentes. Un objeto SurfaceView toma los mismos parámetros que otras vistas, pero el contenido de SurfaceView es transparente cuando se renderiza.

TextureView tiene un mejor manejo de la rotación y el canal alfa que SurfaceView, pero SurfaceView tiene ventajas de rendimiento cuando se componen elementos de la IU superpuestos sobre videos. Cuando un cliente renderiza con un objeto SurfaceView, SurfaceView le proporciona al cliente una capa de composición separada. SurfaceFlinger compone la capa separada como una superposición de hardware si el dispositivo lo admite. Cuando un cliente renderiza con TextureView, el kit de herramientas de la IU compone el contenido del objeto TextureView en la jerarquía de vistas con la GPU. Las actualizaciones del contenido pueden hacer que se vuelvan a dibujar otros elementos de la vista, por ejemplo, si las otras vistas se colocan sobre TextureView. Una vez que se completa la renderización de la vista, SurfaceFlinger compone la capa de la IU de la app y todas las demás capas, de modo que cada píxel visible se compone dos veces.

Caso de éxito: Reproducir video de Grafika

Reproducir video de Grafika incluye un par de reproductores de video, uno implementado con TextureView y otro implementado con SurfaceView. La parte de decodificación de video de la actividad envía fotogramas de MediaCodec a una superficie para TextureView y SurfaceView. La mayor diferencia entre las implementaciones son los pasos necesarios para presentar la relación de aspecto correcta.

El escalamiento de SurfaceView requiere una implementación personalizada de FrameLayout. WindowManager debe enviar una nueva posición de ventana y valores de tamaño nuevos a SurfaceFlinger. Para escalar la SurfaceTexture de un objeto TextureView, se requiere configurar una matriz de transformación con TextureView#setTransform().

Después de presentar la relación de aspecto correcta, ambas implementaciones siguen el mismo patrón. Cuando SurfaceView o TextureView crea la superficie, el código de la app habilita la reproducción. Cuando un usuario presiona Reproducir, se inicia un subproceso de decodificación de video, con la superficie como destino de salida. Después de eso, el código de la app no hace nada: SurfaceFlinger (para SurfaceView) o TextureView controlan la composición y la visualización.

Caso de éxito: Decodificación doble de Grafika

Decodificación doble de Grafika muestra la manipulación de SurfaceTexture dentro de TextureView.

Decodificación doble de Grafika usa un par de objetos TextureView para mostrar dos videos que se reproducen uno al lado del otro, simulando una app de videoconferencias. Cuando cambia la orientación de la pantalla y se reinicia la actividad, los decodificadores de MediaCodec no se detienen, lo que simula la reproducción de una transmisión de video en tiempo real. Para mejorar la eficiencia, el cliente debe mantener activa la superficie. La superficie es un controlador de la interfaz del productor en la BufferQueue de SurfaceTexture. Como el objeto TextureView administra la SurfaceTexture, el cliente debe mantener activa la SurfaceTexture para mantener activa la superficie.

Para mantener activa la SurfaceTexture, Decodificación doble de Grafika obtiene referencias a SurfaceTextures de los objetos TextureView y las guarda en un campo estático. Luego, Decodificación doble de Grafika muestra false desde TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed() para evitar la destrucción de la SurfaceTexture. Luego, TextureView pasa una SurfaceTexture a onSurfaceTextureDestroyed() que se puede mantener en el cambio de configuración de la actividad, que el cliente pasa al nuevo objeto TextureView a través de setSurfaceTexture().

Los subprocesos separados controlan cada decodificador de video. Mediaserver envía búferes con salida decodificada a las SurfaceTextures, los consumidores de BufferQueue. Los objetos TextureView realizan la renderización y se ejecutan en el subproceso de IU.

Implementar Decodificación doble de Grafika con SurfaceView es más difícil que implementar con TextureView porque los objetos SurfaceView destruyen las superficies durante los cambios de orientación. Además, el uso de objetos SurfaceView agrega dos capas, lo que no es ideal debido a las limitaciones en la cantidad de superposiciones disponibles en el hardware.