A classe TextureView
é um objeto de visualização que combina uma visualização com uma SurfaceTexture.
Renderizar com OpenGL ES
Um objeto TextureView encapsula uma SurfaceTexture, respondendo a callbacks e
adquirindo novos buffers. Quando um objeto TextureView adquire novos buffers, TextureView
emite uma solicitação de invalidação de visualização e desenha usando o conteúdo do buffer
mais recente como fonte de dados, renderizando onde e como o estado da visualização
indica que deve ser.
O OpenGL ES (GLES) pode
renderizar em um objeto TextureView transmitindo o
SurfaceTexture para a chamada de criação do EGL, mas isso cria um problema. Quando o GLES
renderiza em um TextureView, os produtores e consumidores de BufferQueue estão na mesma
thread, 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 da linha de execução de interface, a
chamada de troca de buffer EGL precisará remover um buffer da BufferQueue. No entanto, como o consumidor e o produtor estão na mesma linha de execução, não haverá buffers disponíveis, e a chamada de troca vai ficar pendente ou falhar.
Para evitar interrupções na troca de buffers, o BufferQueue sempre precisa de um buffer disponível para remoção da fila. Para isso, a BufferQueue descarta o conteúdo do buffer adquirido anteriormente quando um novo buffer é enfileirado. Ele também impõe restrições às contagens mínimas e máximas de buffer para evitar que um consumidor use todos os buffers de uma vez.
Escolher SurfaceView ou TextureView
SurfaceView e TextureView têm funções semelhantes e são elementos da hierarquia de visualização. No entanto, SurfaceView e TextureView têm implementações diferentes. Um objeto SurfaceView usa os mesmos parâmetros que outras visualizações, mas
o conteúdo SurfaceView é transparente quando renderizado.
O TextureView tem melhor processamento de alfa e rotação do que o SurfaceView, mas o SurfaceView tem vantagens de desempenho ao compor elementos da interface em camadas sobre vídeos. Quando um cliente renderiza com um objeto 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 o dispositivo for compatível. Quando um cliente
renderiza com TextureView, o kit de ferramentas de UI combina o conteúdo do objeto TextureView
na hierarquia de visualização com a GPU. As atualizações no conteúdo podem fazer com que outros elementos de visualização sejam renderizados novamente, por exemplo, se as outras visualizações estiverem posicionadas acima de TextureView. Depois que a renderização da visualização é concluída, o SurfaceFlinger compõe a camada da interface do app 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 dois players de vídeo, um
implementado com TextureView e outro com SurfaceView. A parte de decodificação de vídeo da atividade envia frames 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 escalonamento de SurfaceView requer uma implementação personalizada de FrameLayout.
O WindowManager precisa enviar uma nova posição e novos valores de tamanho para o
SurfaceFlinger. Para dimensionar a SurfaceTexture de um objeto TextureView, é necessário configurar uma matriz de transformação com TextureView#setTransform().
Depois de apresentar a proporção correta, as duas implementações seguem o mesmo padrão. Quando SurfaceView ou TextureView cria a superfície, o código do app
ativa a reprodução. Quando um usuário toca em play, uma linha de execução de decodificação de vídeo é iniciada, com a superfície como destino de saída. Depois disso, o código
do app não faz nada. A composição e a exibição são processadas pelo
SurfaceFlinger (para SurfaceView) ou pelo TextureView.
Estudo de caso: decodificação dupla da Grafika
O Double Decode do Grafika demonstra a manipulação da
SurfaceTexture dentro de TextureView.
A decodificação dupla do Grafika usa um par de objetos TextureView para mostrar dois vídeos
reproduzidos lado a lado, simulando um app 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 precisa manter a superfície ativa. A superfície é um identificador para a interface
do produtor na BufferQueue da SurfaceTexture. Como o objeto TextureView gerencia a SurfaceTexture, o cliente precisa manter a SurfaceTexture ativa para manter a superfície ativa.
Para manter a SurfaceTexture ativa, o Double Decode do Grafika recebe referências
a SurfaceTextures dos objetos TextureView e as salva em um campo estático.
Em seguida, o Double Decode do Grafika retorna false de
TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed() para
evitar a destruição da SurfaceTexture. Em seguida, TextureView transmite uma SurfaceTexture para onSurfaceTextureDestroyed(), que pode ser mantida durante a mudança de configuração da atividade. O cliente transmite isso para o novo objeto TextureView usando setSurfaceTexture().
Threads separados acionam cada decodificador de vídeo. O mediaserver envia buffers com saída decodificada para as SurfaceTextures, os consumidores do BufferQueue. Os objetos TextureView realizam a renderização e são executados na linha de execução de interface.
Implementar a decodificação dupla do Grafika com SurfaceView é mais difícil do que
implementar com TextureView porque os objetos SurfaceView destroem superfícies
durante 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.