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 geram 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 para o GLES como uma textura externa.

Texturas GLES externas

As texturas GLES externas (GL_TEXTURE_EXTERNAL_OES) diferem das texturas GLES padrão (GL_TEXTURE_2D) das seguintes maneiras:

  • 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 padrão.
  • Texturas externas não podem realizar todas as atividades padrão de textura do GLES.

O principal benefício das texturas externas é a capacidade de renderizar diretamente dados do 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 e verificam se os dados no buffer são reconhecíveis pelo GLES.

Como as instâncias SurfaceTexture interagem com um contexto EGL, um app só pode chamar os métodos dele enquanto o contexto EGL proprietário da 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 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 transmitido por BufferQueue inclui parâmetros de transformação e um carimbo de data/hora.

Os parâmetros de transformação são úteis para a 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 original 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 que dependem 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 ele foi capturado, e não de quando o app o recebeu. 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 do Grafika envolve a gravação de frames da câmera de um dispositivo e a exibição deles 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 e mostrar frames 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 a seguir, as setas indicam a propagação de dados da câmera. As instâncias BufferQueue são mostradas com indicadores visuais que distinguem produtores (azul-petróleo) de consumidores (verde).

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 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 UI. 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, o 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.

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

Reprodução segura de vídeos com textura

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

Reprodução segura de vídeo com textura

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

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 marcar texturas como protegidas para que 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 superfícies criadas em um contexto protegido (verificado pelo ACodec).

A reprodução segura de vídeo de textura estabelece a base para uma implementação forte de gerenciamento de direitos digitais (DRM) no ambiente OpenGL ES. Sem uma implementação forte de DRM, como o Widevine Nível 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 Android Open Source Project (AOSP) inclui código de framework para reprodução segura de vídeo com textura. O suporte a drivers depende dos OEMs. Os implementadores de dispositivos precisam implementar as extensões EGL_EXT_protected_content e GL_EXT_protected_textures. 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 coloque em fila diretamente para o compositor de janela), desde que os bits de uso do consumidor contenham GRALLOC_USAGE_PROTECTED. Para documentação detalhada sobre a implementação das extensões, consulte os registros do Khronos ( EGL_EXT_protected_content) e ( GL_EXT_protected_textures).