Visualização de textura

A classe TextureView é um objeto de exibição que combina uma exibiçã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.

O OpenGL ES (GLES) pode renderizar em um TextureView passando o 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 encadeamento, o que pode fazer com que a chamada de troca de buffer pare ou falhe. Por exemplo, se um produtor enviar vários buffers em rápida sucessão a partir do encadeamento de UI, a chamada de troca de buffer EGL precisará desenfileirar um buffer do BufferQueue. No entanto, como o consumidor e o produtor estão no mesmo encadeamento, 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 desenfileirado. Para implementar isso, o BufferQueue descarta o conteúdo do buffer adquirido anteriormente quando um novo buffer é enfileirado e impõe restrições nas contagens mínima e máxima de buffer para evitar que um consumidor consuma todos os buffers de uma vez.

Escolhendo SurfaceView ou TextureView

SurfaceView e TextureView preenchem funções semelhantes e são ambos 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 alfa e manipulação de rotação do que um SurfaceView, mas um SurfaceView tem vantagens de desempenho ao compor elementos de interface do usuário em camadas sobre vídeos. Quando um cliente renderiza com um SurfaceView, o SurfaceView fornece ao cliente uma camada de composição separada. O SurfaceFlinger compõe a camada separada como uma sobreposição de hardware, se suportada pelo dispositivo. Quando um cliente renderiza com um TextureView, o kit de ferramentas da interface do usuário 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 de vista sejam redesenhados, por exemplo, se as outras vistas estiverem posicionadas em cima de um TextureView. Após a conclusão da renderização da exibição, o SurfaceFlinger compõe a camada da interface do usuário do aplicativo e todas as outras camadas, para que cada pixel visível seja composto duas vezes.

Estudo de caso: vídeo de reprodução 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 os passos necessários 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 a 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 habilita 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 destino 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 reinicia, os decodificadores MediaCodec não param, simulando a reprodução de um fluxo 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 ativa.

Para manter a SurfaceTexture viva, o Double Decode do Grafika obtém referências a SurfaceTextures dos objetos TextureView e as 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 mudança de configuração da atividade, que o cliente passa para o novo TextureView por meio de setSurfaceTexture() .

Threads separados conduzem cada decodificador de vídeo. O Mediaserver envia buffers com saída decodificada para SurfaceTextures, os consumidores BufferQueue. Os objetos TextureView executam a renderização e são executados no thread da interface do usuário.

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 do número de sobreposições disponíveis no hardware.