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.