Oberflächenstruktur

SurfaceTexture ist eine Kombination aus einer Oberfläche und einer OpenGL ES (GLES)-Textur. SurfaceTexture-Instanzen werden verwendet, um Oberflächen bereitzustellen, die in GLES-Texturen ausgegeben werden.

SurfaceTexture enthält eine Instanz von BufferQueue, für die Apps der Verbraucher sind. Der onFrameAvailable()-Callback benachrichtigt Apps, wenn der Produzent einen neuen Puffer in die Warteschlange stellt. Anschließend rufen Apps updateTexImage() auf, wodurch der zuvor gehaltene Puffer freigegeben, der neue Puffer aus der Warteschlange abgerufen und EGL-Aufrufe ausgeführt werden, um den Puffer GLES als externe Textur zur Verfügung zu stellen.

Externe GLES-Texturen

Externe GLES-Texturen (GL_TEXTURE_EXTERNAL_OES) unterscheiden sich in folgenden Punkten von herkömmlichen GLES-Texturen (GL_TEXTURE_2D):

  • Bei externen Texturen werden texturierte Polygone direkt aus Daten gerendert, die von BufferQueue empfangen werden.
  • Externe Textur-Renderer werden anders konfiguriert als herkömmliche GLES-Textur-Renderer.
  • Mit externen Texturen können nicht alle traditionellen GLES-Texturaktivitäten ausgeführt werden.

Der Hauptvorteil externer Texturen besteht darin, dass sie direkt aus BufferQueue-Daten gerendert werden können. SurfaceTexture-Instanzen setzen die Flags für die Nutzernutzung auf GRALLOC_USAGE_HW_TEXTURE, wenn sie BufferQueue-Instanzen für externe Texturen erstellen, damit die Daten im Buffer von GLES erkannt werden können.

Da SurfaceTexture-Instanzen mit einem EGL-Kontext interagieren, kann eine App ihre Methoden nur aufrufen, wenn der EGL-Kontext, zu dem die Textur gehört, im aufrufenden Thread aktuell ist. Weitere Informationen finden Sie in der Klassendokumentation zu SurfaceTexture.

Zeitstempel und Transformationen

Zu den SurfaceTexture-Instanzen gehören die Methode getTimeStamp(), mit der ein Zeitstempel abgerufen wird, und die Methode getTransformMatrix(), mit der eine Transformationsmatrix abgerufen wird. Durch den Aufruf von updateTexImage() werden sowohl der Zeitstempel als auch die Transformationsmatrix festgelegt. Jeder Puffer, der von BufferQueue übergeben wird, enthält Transformationsparameter und einen Zeitstempel.

Transformationsparameter sind hilfreich, um die Effizienz zu steigern. In einigen Fällen sind die Quelldaten für den Nutzer möglicherweise nicht richtig ausgerichtet. Anstatt die Daten vor dem Senden an den Verbraucher zu drehen, senden Sie sie in ihrer Ausrichtung mit einer Transformation, die sie korrigiert. Die Transformationsmatrix kann mit anderen Transformationen zusammengeführt werden, wenn die Daten verwendet werden, um den Overhead zu minimieren.

Der Zeitstempel ist nützlich für Pufferquellen, die zeitabhängig sind. Wenn setPreviewTexture() beispielsweise die Producer-Oberfläche mit dem Ausgang der Kamera verbindet, können Frames von der Kamera verwendet werden, um ein Video zu erstellen. Jeder Frame muss einen Zeitstempel für die Präsentation haben, der angibt, wann der Frame erfasst wurde, nicht wann die App ihn empfangen hat. Der Kameracode legt den mit dem Puffer bereitgestellten Zeitstempel fest, was zu einer einheitlicheren Reihe von Zeitstempeln führt.

Fallstudie: Kontinuierliche Erfassung mit Grafika

Bei der kontinuierlichen Aufzeichnung von Grafika werden Frames von der Kamera eines Geräts aufgenommen und auf dem Bildschirm angezeigt. Wenn Sie Frames aufnehmen möchten, erstellen Sie mit der Methode createInputSurface() der Klasse MediaCodec eine Oberfläche und übergeben Sie sie an die Kamera. Wenn Sie Frames anzeigen möchten, erstellen Sie eine Instanz von SurfaceView und übergeben Sie die Oberfläche an setPreviewDisplay(). Das Aufzeichnen von Frames und deren gleichzeitige Anzeige ist ein etwas aufwändigerer Vorgang.

Bei der Aktivität Videoaufzeichnung rund um die Uhr wird das Video der Kamera während der Aufzeichnung angezeigt. In diesem Fall wird das codierte Video in einen zyklischen Puffer im Arbeitsspeicher geschrieben, der jederzeit auf die Festplatte gespeichert werden kann.

Dieser Ablauf umfasst drei Pufferwarteschlangen:

  • App – Die App verwendet eine SurfaceTexture-Instanz, um Frames von der Kamera zu empfangen und in eine externe GLES-Textur umzuwandeln.
  • SurfaceFlinger: Die App deklariert eine SurfaceView-Instanz, um die Frames anzuzeigen.
  • MediaServer – Konfiguriere einen MediaCodec-Encoder mit einer Eingabeoberfläche, um das Video zu erstellen.

In der Abbildung unten zeigen die Pfeile die Datenübertragung von der Kamera. BufferQueue Instanzen sind farblich gekennzeichnet (Erzeuger sind teal, Verbraucher sind grün).

Grafika-Aktivitäten mit kontinuierlicher Erfassung

Abbildung 1: Kontinuierliche Aufnahmeaktivität von Grafika

Das codierte H.264-Video wird in einen zyklischen Puffer im RAM des App-Prozesses übertragen. Wenn ein Nutzer die Aufnahmeschaltfläche drückt, schreibt die MediaMuxer-Klasse das codierte Video in eine MP4-Datei auf die Festplatte.

Alle BufferQueue-Instanzen werden in der App mit einem einzigen EGL-Kontext verwaltet, während die GLES-Vorgänge im UI-Thread ausgeführt werden. Die Verarbeitung codierter Daten (Verwaltung eines zyklischen Puffers und Schreiben auf die Festplatte) erfolgt in einem separaten Thread.

Bei Verwendung der SurfaceView-Klasse werden über den surfaceCreated()-Callback die EGLContext- und EGLSurface-Instanzen für den Display- und den Videoencoder erstellt. Wenn ein neuer Frame eintrifft, führt SurfaceTexture vier Aktivitäten aus:
  1. Erfasst den Frame.
  2. Stellt den Frame als GLES-Textur zur Verfügung.
  3. Rendert den Frame mit GLES-Befehlen.
  4. Die Transformation und der Zeitstempel werden für jede Instanz von EGLSurface weitergeleitet.

Der Encoder-Thread ruft dann die codierte Ausgabe aus MediaCodec ab und speichert sie im Arbeitsspeicher.

Sichere Videowiedergabe mit Texturen

Android unterstützt die GPU-Nachbearbeitung geschützter Videoinhalte. So können Apps die GPU für komplexe, nichtlineare Videoeffekte (z. B. Verzerrungen) verwenden und geschützte Videoinhalte auf Texturen für die Verwendung in allgemeinen Grafikszenen (z. B. mit GLES) und Virtual Reality (VR) abbilden.

Sichere Videowiedergabe mit Texturen

Abbildung 2: Sichere Videowiedergabe mit Texturen

Der Support wird mit den folgenden beiden Erweiterungen aktiviert:

  • EGL-Erweiterung: (EGL_EXT_protected_content) Ermöglicht das Erstellen geschützter GL-Kontexte und ‑Oberflächen, die beide mit geschützten Inhalten arbeiten können.
  • GLES-Erweiterung – (GL_EXT_protected_textures) Ermöglicht das Tagging von Texturen als geschützt, damit sie als Framebuffer-Texturanhang verwendet werden können.

Android ermöglicht es SurfaceTexture und ACodec (libstagefright.so), geschützte Inhalte zu senden, auch wenn die Oberfläche des Fensters nicht in SurfaceFlinger eingereiht wird, und bietet eine geschützte Videooberfläche für die Verwendung in einem geschützten Kontext. Dazu wird das Bit „Protected Consumer“ (GRALLOC_USAGE_PROTECTED) auf Oberflächen festgelegt, die in einem geschützten Kontext erstellt wurden (von ACodec bestätigt).

Die sichere Videowiedergabe von Texturen bildet die Grundlage für eine starke DRM-Implementierung in der OpenGL ES-Umgebung. Ohne eine strikte DRM-Implementierung wie Widevine Level 1 erlauben viele Inhaltsanbieter das Rendern ihrer hochwertigen Inhalte nicht in der OpenGL ES-Umgebung. Das verhindert wichtige VR-Anwendungsfälle wie das Ansehen von DRM-geschützten Inhalten in VR.

AOSP enthält Framework-Code für die sichere Wiedergabe von Videotexturen. Der Treibersupport obliegt den OEMs. Geräteimplementierer müssen EGL_EXT_protected_content und GL_EXT_protected_textures extensions implementieren. Wenn du deine eigene Codec-Bibliothek verwendest, um libstagefright zu ersetzen, beachte die Änderungen an /frameworks/av/media/libstagefright/SurfaceUtils.cpp, die es ermöglichen, mit GRALLOC_USAGE_PROTECTED gekennzeichnete Puffer an ANativeWindow zu senden (auch wenn ANativeWindow nicht direkt in die Warteschlange des Fenster-Composers gestellt wird), solange die Bits zur Nutzernutzung GRALLOC_USAGE_PROTECTED enthalten. Ausführliche Informationen zur Implementierung der Erweiterungen finden Sie in den Khronos-Registries (EGL_EXT_protected_content und GL_EXT_protected_textures).