TexturaVer

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

Renderizado con OpenGL ES

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

OpenGL ES (GLES) puede representar en un TextureView pasando SurfaceTexture a la llamada de creación de EGL, pero esto crea un problema. Cuando GLES se procesa en un TextureView, los productores y consumidores de BufferQueue están en el mismo hilo, lo que puede hacer que la llamada de intercambio de búfer se detenga o falle. Por ejemplo, si un productor envía varios búferes en rápida sucesión desde el subproceso de la interfaz de usuario, la llamada de intercambio de búfer de EGL debe sacar un búfer de BufferQueue. Sin embargo, debido a que el consumidor y el productor están en el mismo subproceso, no habrá ningún búfer disponible y la llamada de intercambio se cuelga o falla.

Para asegurarse de que el intercambio de búfer no se detenga, BufferQueue siempre necesita un búfer disponible para ser eliminado. Para implementar esto, BufferQueue descarta el contenido del búfer adquirido previamente cuando se pone en cola un nuevo búfer y establece restricciones en los recuentos de búfer mínimo y máximo para evitar que un consumidor consuma todos los búfer a la vez.

Elegir SurfaceView o TextureView

SurfaceView y TextureView cumplen roles similares y ambos son ciudadanos de la jerarquía de vistas. Sin embargo, SurfaceView y TextureView tienen implementaciones diferentes. Un SurfaceView toma los mismos parámetros que otras vistas, pero los contenidos de SurfaceView son transparentes cuando se representan.

Un TextureView tiene un mejor manejo alfa y de rotación que un SurfaceView, pero un SurfaceView tiene ventajas de rendimiento al componer elementos de la interfaz de usuario en capas sobre videos. Cuando un cliente renderiza con SurfaceView, SurfaceView proporciona al cliente una capa de composición separada. SurfaceFlinger compone la capa separada como una superposición de hardware si es compatible con el dispositivo. Cuando un cliente renderiza con TextureView, el kit de herramientas de la interfaz de usuario combina el contenido de TextureView en la jerarquía de vistas con la GPU. Las actualizaciones del contenido pueden hacer que otros elementos de la vista se vuelvan a dibujar, por ejemplo, si las otras vistas se colocan encima de un TextureView. Una vez que se completa el procesamiento de la vista, SurfaceFlinger compone la capa de la interfaz de usuario de la aplicación y todas las demás capas, de modo que cada píxel visible se compone dos veces.

Estudio de caso: Play Video de Grafika

Play 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 marcos desde MediaCodec a una superficie tanto para TextureView como para SurfaceView. La mayor diferencia entre las implementaciones son los pasos necesarios para presentar la relación de aspecto correcta.

Escalar SurfaceView requiere una implementación personalizada de FrameLayout. WindowManager necesita enviar una nueva posición de ventana y nuevos valores de tamaño a SurfaceFlinger. Escalar una SurfaceTexture de TextureView 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/TextureView crea la superficie, el código de la aplicación permite la reproducción. Cuando un usuario toca reproducir , inicia un hilo de decodificación de video, con la superficie como destino de salida. Después de eso, el código de la aplicación no hace nada: la composición y la visualización están a cargo de SurfaceFlinger (para SurfaceView) o de TextureView.

Estudio de caso: Doble decodificación de Grafika

Double Decode de Grafika demuestra la manipulación de SurfaceTexture dentro de TextureView.

Double Decode de Grafika utiliza un par de objetos TextureView para mostrar dos videos que se reproducen uno al lado del otro, simulando una aplicación de videoconferencia. Cuando la orientación de la pantalla cambia y la actividad se reinicia, los decodificadores de MediaCodec no se detienen, simulando la reproducción de un flujo de video en tiempo real. Para mejorar la eficiencia, el cliente debe mantener viva la superficie. La superficie es un identificador de la interfaz del productor en el BufferQueue de SurfaceTexture. Debido a que TextureView administra SurfaceTexture, el cliente necesita mantener vivo SurfaceTexture para mantener viva la superficie.

Para mantener vivo SurfaceTexture, Double Decode de Grafika obtiene referencias a SurfaceTextures de los objetos TextureView y las guarda en un campo estático. Luego, Double Decode de Grafika devuelve false de TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed() para evitar la destrucción de SurfaceTexture. Luego, TextureView pasa una SurfaceTexture a onSurfaceTextureDestroyed() que se puede mantener durante el cambio de configuración de la actividad, que el cliente pasa a la nueva TextureView a través setSurfaceTexture() .

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

La implementación de Double Decode de Grafika con SurfaceView es más difícil que la implementación con TextureView porque los objetos de 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.