Visualização de textura

A classe TextureView é um objeto de visualização que combina uma visualização com um SurfaceTexture.

Renderizando com OpenGL ES

Um objeto TextureView envolve um SurfaceTexture, respondendo a retornos de chamada e adquirindo novos buffers. Quando um TextureView adquire novos buffers, um TextureView emite uma solicitação de invalidação de visualização e desenha usando o conteúdo do buffer mais recente como sua fonte de dados, renderizando onde e como o estado de visualização indicar que deveria.

OpenGL ES (GLES) pode renderizar em um TextureView passando SurfaceTexture para a chamada de criação do EGL, mas isso cria um problema. Quando o GLES é renderizado em um TextureView, os produtores e consumidores do BufferQueue estão no mesmo thread, o que pode fazer com que a chamada de troca de buffer seja interrompida ou falhe. Por exemplo, se um produtor enviar vários buffers em rápida sucessão a partir do thread de UI, a chamada de troca de buffer EGL precisará retirar da fila um buffer do BufferQueue. No entanto, como o consumidor e o produtor estão no mesmo thread, não haverá buffers disponíveis e a chamada de troca trava ou falha.

Para garantir que a troca de buffer não pare, BufferQueue sempre precisa de um buffer disponível para ser retirado da fila. Para implementar isso, BufferQueue descarta o conteúdo do buffer adquirido anteriormente quando um novo buffer é enfileirado e coloca restrições nas contagens mínima e máxima de buffer para evitar que um consumidor consuma todos os buffers de uma só vez.

Escolhendo SurfaceView ou TextureView

SurfaceView e TextureView preenchem funções semelhantes e são cidadãos da hierarquia de visualização. No entanto, SurfaceView e TextureView têm implementações diferentes. Um SurfaceView usa os mesmos parâmetros que outras visualizações, mas o conteúdo do SurfaceView é transparente quando renderizado.

Um TextureView tem melhor manipulação de alfa e rotação do que um SurfaceView, mas um SurfaceView tem vantagens de desempenho ao compor elementos de UI em camadas sobre vídeos. Quando um cliente é renderizado com um SurfaceView, o SurfaceView fornece ao cliente uma camada de composição separada. SurfaceFlinger compõe a camada separada como uma sobreposição de hardware, se for compatível com o dispositivo. Quando um cliente é renderizado com um TextureView, o kit de ferramentas da UI compõe o conteúdo do TextureView na hierarquia de visualização com a GPU. Atualizações no conteúdo podem fazer com que outros elementos da visualização sejam redesenhados, por exemplo, se as outras visualizações estiverem posicionadas sobre um TextureView. Após a conclusão da renderização da visualização, o SurfaceFlinger compõe a camada da interface do usuário do aplicativo e todas as outras camadas, de modo que cada pixel visível seja composto duas vezes.

Estudo de caso: Reproduzir vídeo da Grafika

O Play Video da Grafika inclui um par de players de vídeo, um implementado com TextureView e outro implementado com SurfaceView. A parte de decodificação de vídeo da atividade envia quadros do MediaCodec para uma superfície para TextureView e SurfaceView. A maior diferença entre as implementações são as etapas necessárias para apresentar a proporção correta.

O dimensionamento do SurfaceView requer uma implementação personalizada do FrameLayout. WindowManager precisa enviar uma nova posição de janela e novos valores de tamanho para SurfaceFlinger. Dimensionar o SurfaceTexture de um TextureView requer a configuração de uma matriz de transformação com TextureView#setTransform() .

Após apresentar a proporção correta, ambas as implementações seguem o mesmo padrão. Quando SurfaceView/TextureView cria a superfície, o código do aplicativo permite a reprodução. Quando um usuário toca em play , ele inicia um thread de decodificação de vídeo, com a superfície como alvo de saída. Depois disso, o código do aplicativo não faz nada – a composição e a exibição são tratadas pelo SurfaceFlinger (para o SurfaceView) ou pelo TextureView.

Estudo de caso: decodificação dupla da Grafika

O Double Decode do Grafika demonstra a manipulação do SurfaceTexture dentro de um TextureView.

O Double Decode da Grafika usa um par de objetos TextureView para mostrar dois vídeos sendo reproduzidos lado a lado, simulando um aplicativo de videoconferência. Quando a orientação da tela muda e a atividade é reiniciada, os decodificadores MediaCodec não param, simulando a reprodução de um stream de vídeo em tempo real. Para melhorar a eficiência, o cliente deve manter a superfície viva. A superfície é um identificador para a interface do produtor no BufferQueue do SurfaceTexture. Como o TextureView gerencia o SurfaceTexture, o cliente precisa manter o SurfaceTexture ativo para manter a superfície viva.

Para manter o SurfaceTexture vivo, o Double Decode do Grafika obtém referências a SurfaceTextures dos objetos TextureView e os salva em um campo estático. Então, o Double Decode do Grafika retorna false de TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed() para evitar a destruição do SurfaceTexture. TextureView então passa um SurfaceTexture para onSurfaceTextureDestroyed() que pode ser mantido durante a alteração da configuração da atividade, que o cliente passa para o novo TextureView por meio de setSurfaceTexture() .

Threads separados conduzem cada decodificador de vídeo. Mediaserver envia buffers com saída decodificada para SurfaceTextures, os consumidores BufferQueue. Os objetos TextureView realizam renderização e são executados no thread de UI.

Implementar o Double Decode do Grafika com SurfaceView é mais difícil do que implementar com TextureView porque os objetos SurfaceView destroem superfícies durante as mudanças de orientação. Além disso, o uso de objetos SurfaceView adiciona duas camadas, o que não é ideal devido às limitações no número de sobreposições disponíveis no hardware.