SurfaceTexture

SurfaceTexture é uma combinação de uma superfície e uma textura OpenGL ES (GLES). As instâncias SurfaceTexture são usadas para fornecer superfícies que são enviadas para texturas GLES.

SurfaceTexture contém uma instância de BufferQueue para a qual os apps são o consumidor. O callback onFrameAvailable() notifica os apps quando o produtor enfileira um novo buffer. Em seguida, os apps chamam updateTexImage(), que libera o buffer mantido anteriormente, adquire o novo buffer da fila e faz chamadas EGL para disponibilizar o buffer ao GLES como uma textura externa.

Texturas GLES externas

As texturas GLES externas (GL_TEXTURE_EXTERNAL_OES) diferem das texturas GLES tradicionais (GL_TEXTURE_2D) das seguintes maneiras:

  • As texturas externas renderizam polígonos texturizados diretamente dos dados recebidos de BufferQueue.
  • Os renderizadores de textura externos são configurados de maneira diferente dos renderizadores de textura GLES tradicionais.
  • Texturas externas não podem realizar todas as atividades tradicionais de textura do GLES.

O principal benefício das texturas externas é a capacidade de renderização diretamente a partir de dados BufferQueue. As instâncias SurfaceTexture definem as flags de uso do consumidor como GRALLOC_USAGE_HW_TEXTURE quando criam instâncias BufferQueue para texturas externas, para garantir que os dados no buffer sejam reconhecíveis pelo GLES.

Como as instâncias de SurfaceTexture interagem com um contexto EGL, um app só pode chamar os métodos enquanto o contexto EGL que possui a textura estiver atual na linha de execução de chamada. Para mais informações, consulte a documentação da classe SurfaceTexture.

Carimbos de data/hora e transformações

As instâncias de SurfaceTexture incluem o método getTimeStamp(), que recupera um carimbo de data/hora, e o método getTransformMatrix(), que recupera uma matriz de transformação. A chamada de updateTexImage() define o carimbo de data/hora e a matriz de transformação. Cada buffer que BufferQueue transmite inclui parâmetros de transformação e um carimbo de data/hora.

Os parâmetros de transformação são úteis para eficiência. Em alguns casos, os dados de origem podem estar na orientação incorreta para o consumidor. Em vez de girar os dados antes de enviá-los ao consumidor, envie os dados na orientação com uma transformação que os corrija. A matriz de transformação pode ser mesclada com outras transformações quando os dados são usados, minimizando a sobrecarga.

O carimbo de data/hora é útil para fontes de buffer dependentes do tempo. Por exemplo, quando setPreviewTexture() conecta a interface do produtor à saída da câmera, os frames da câmera podem ser usados para criar um vídeo. Cada frame precisa ter um carimbo de data/hora de apresentação do momento em que o frame foi capturado, e não do momento em que o app recebeu o frame. O código da câmera define o carimbo de data/hora fornecido com o buffer, resultando em uma série mais consistente de carimbos de data/hora.

Estudo de caso: captura contínua da Grafika

A captura contínua da Grafika envolve gravar frames da câmera de um dispositivo e exibir esses frames na tela. Para gravar frames, crie uma superfície com o método createInputSurface() da classe MediaCodec e transmita a superfície para a câmera. Para mostrar frames, crie uma instância de SurfaceView e transmita a superfície para setPreviewDisplay(). Gravar frames e exibi-los ao mesmo tempo é um processo mais complexo.

A atividade de captura contínua mostra o vídeo da câmera enquanto ele está sendo gravado. Nesse caso, o vídeo codificado é gravado em um buffer circular na memória que pode ser salvo no disco a qualquer momento.

Esse fluxo envolve três filas de buffer:

  • App: o app usa uma instância SurfaceTexture para receber frames da câmera, convertendo-os em uma textura GLES externa.
  • SurfaceFlinger: o app declara uma instância SurfaceView para mostrar os frames.
  • MediaServer: configure um codificador MediaCodec com uma superfície de entrada para criar o vídeo.

Na figura abaixo, as setas indicam a propagação de dados da câmera. As instâncias de BufferQueue são coloridas (os produtores são azul-esverdeado, e os consumidores são verdes).

Atividade de captura
contínua do Grafika

Figura 1. Atividade de captura contínua da Grafika

O vídeo H.264 codificado vai para um buffer circular na RAM no processo do app. Quando um usuário pressiona o botão de captura, a classe MediaMuxer grava o vídeo codificado em um arquivo MP4 no disco.

Todas as instâncias de BufferQueue são processadas com um único contexto EGL no app, enquanto as operações GLES são realizadas na linha de execução da interface. O processamento de dados codificados (gerenciamento de um buffer circular e gravação em disco) é feito em uma linha de execução separada.

Ao usar a classe SurfaceView, o callback surfaceCreated() cria as instâncias EGLContext e EGLSurface para a tela e o codificador de vídeo. Quando um novo frame chega, SurfaceTexture realiza quatro atividades:
  1. Adquire o frame.
  2. Disponibiliza o frame como uma textura GLES.
  3. Renderiza o frame com comandos GLES.
  4. Encaminha a transformação e o carimbo de data/hora para cada instância de EGLSurface.

A linha de execução do codificador extrai a saída codificada de MediaCodec e a armazena na memória.

Reprodução de vídeo de textura segura

O Android oferece suporte ao pós-processamento de conteúdo de vídeo protegido por GPU. Isso permite que os apps usem a GPU para efeitos de vídeo complexos e não lineares (como deformações), mapeando conteúdo de vídeo protegido para texturas para uso em cenas gráficas gerais (por exemplo, usando GLES) e realidade virtual (RV).

Reprodução de vídeo de textura segura

Figura 2. Reprodução de vídeo de textura segura

O suporte é ativado usando as duas extensões a seguir:

  • Extensão EGL: (EGL_EXT_protected_content) permite a criação de contextos e plataformas GL protegidos, que podem operar em conteúdo protegido.
  • Extensão GLES: (GL_EXT_protected_textures) permite a inclusão de tags em texturas como protegidas para que elas possam ser usadas como anexos de textura de framebuffer.

O Android permite que SurfaceTexture e ACodec (libstagefright.so) enviem conteúdo protegido, mesmo que a superfície da janela não seja enfileirada para SurfaceFlinger e forneça uma superfície de vídeo protegida para uso em um contexto protegido. Isso é feito definindo o bit de consumidor protegido (GRALLOC_USAGE_PROTECTED) em plataformas criadas em um contexto protegido (verificado pelo ACodec).

A reprodução segura de vídeos de textura estabelece a base para uma implementação robusta de DRM no ambiente OpenGL ES. Sem uma implementação forte de DRM, como o Widevine Level 1, muitos provedores de conteúdo não permitem a renderização do conteúdo de alto valor no ambiente OpenGL ES, impedindo casos de uso importantes de RV, como assistir conteúdo protegido por DRM em RV.

O AOSP inclui o código do framework para reprodução segura de vídeos de textura. O suporte a drivers é de responsabilidade dos OEMs. Os implementadores de dispositivos precisam implementar EGL_EXT_protected_content e GL_EXT_protected_textures extensions. Ao usar sua própria biblioteca de codec (para substituir libstagefright), observe as mudanças em /frameworks/av/media/libstagefright/SurfaceUtils.cpp que permitem que buffers marcados com GRALLOC_USAGE_PROTECTED sejam enviados para ANativeWindow, mesmo que ANativeWindow não seja enfileirado diretamente no compilador de janelas, desde que os bits de uso do consumidor contenham GRALLOC_USAGE_PROTECTED. Para conferir uma documentação detalhada sobre a implementação das extensões, consulte os registros do Khronos (EGL_EXT_protected_content e GL_EXT_protected_textures).