Die Klasse TextureView ist ein Ansichtsobjekt, das eine Ansicht mit einer SurfaceTexture kombiniert.
Mit OpenGL ES rendern
Ein TextureView-Objekt umschließt eine SurfaceTexture, reagiert auf Callbacks und ruft neue Puffer ab. Wenn ein TextureView-Objekt neue Puffer erhält, sendet TextureView eine Anfrage zum Ungültigmachen der Ansicht und zeichnet die Inhalte des neuesten Puffers als Datenquelle. Die Darstellung erfolgt überall und so, wie es der Ansichtsstatus vorgibt.
OpenGL ES (GLES) kann auf einem TextureView-Objekt gerendert werden, indem die SurfaceTexture an den EGL-Erstellungsaufruf übergeben wird. Dadurch entsteht jedoch ein Problem. Wenn GLES auf einem TextureView gerendert wird, befinden sich BufferQueue-Producer und -Consumer im selben Thread. Dies kann dazu führen, dass der Aufruf zum Pufferwechsel verzögert wird oder fehlschlägt. Wenn ein Producer beispielsweise mehrere Puffer in schneller Folge über den UI-Thread sendet, muss beim EGL-Puffertauschaufruf ein Puffer aus der BufferQueue in die Warteschlange gestellt werden. Da sich Consumer und Producer jedoch im selben Thread befinden, sind keine Puffer verfügbar und der Swap-Aufruf bleibt hängen oder schlägt fehl.
Um Puffer-Swap-Stalls zu vermeiden, benötigt BufferQueue immer einen Puffer, der aus der Warteschlange entfernt werden kann. Dazu verwirft BufferQueue den Inhalt des zuvor abgerufenen Puffers, wenn ein neuer Puffer in die Warteschlange gestellt wird. Außerdem werden Einschränkungen für die Mindest- und Höchstanzahl von Puffern festgelegt, um zu verhindern, dass ein Consumer alle Puffer gleichzeitig verwendet.
SurfaceView oder TextureView auswählen
SurfaceView und TextureView haben ähnliche Funktionen und sind beide Bestandteile der Ansichtshierarchie. SurfaceView und TextureView werden jedoch unterschiedlich implementiert. Ein SurfaceView-Objekt verwendet dieselben Parameter wie andere Ansichten, aber SurfaceView-Inhalte sind beim Rendern transparent.
TextureView bietet eine bessere Alpha- und Rotationsverarbeitung als SurfaceView, aber SurfaceView hat Leistungsvorteile beim Compositing von UI-Elementen, die über Videos gelegt werden. Wenn ein Client mit einem SurfaceView-Objekt gerendert wird, stellt das SurfaceView dem Client eine separate Kompositionsebene zur Verfügung. SurfaceFlinger stellt die einzelnen Ebenen als Hardware-Overlays zusammen, sofern das Gerät dies unterstützt. Wenn ein Client mit TextureView rendert, fügt das UI-Toolkit den Inhalt des TextureView-Objekts mit der GPU in die Ansichtshierarchie ein. Aktualisierungen des Inhalts können dazu führen, dass andere Ansichtselemente neu gezeichnet werden, z. B. wenn die anderen Ansichten über TextureView positioniert sind. Nachdem das Rendern der Ansicht abgeschlossen ist, fügt SurfaceFlinger die UI-Ebene der App und alle anderen Ebenen zusammen, sodass jedes sichtbare Pixel zweimal zusammengesetzt wird.
Fallstudie: Grafika – Play Video
Grafika's Play Video enthält zwei Videoplayer, einen mit TextureView und einen mit SurfaceView. Beim Teil der Aktivität, der für die Videodecodierung zuständig ist, werden Frames von MediaCodec an eine Oberfläche für TextureView und SurfaceView gesendet. Der größte Unterschied zwischen den Implementierungen sind die Schritte, die erforderlich sind, um das richtige Seitenverhältnis zu präsentieren.
Für die Skalierung von SurfaceView ist eine benutzerdefinierte Implementierung von FrameLayout erforderlich.
WindowManager muss SurfaceFlinger neue Werte für die Fensterposition und ‑größe senden. Wenn Sie die SurfaceTexture eines TextureView-Objekts skalieren möchten, müssen Sie eine Transformationsmatrix mit TextureView#setTransform() konfigurieren.
Nachdem das richtige Seitenverhältnis angezeigt wurde, folgen beide Implementierungen demselben Muster. Wenn SurfaceView oder TextureView die Oberfläche erstellt, wird die Wiedergabe durch den App-Code aktiviert. Wenn ein Nutzer auf Wiedergabe tippt, wird ein Thread für die Videodecodierung gestartet, wobei die Oberfläche das Ausgabeziel ist. Danach führt der App-Code keine Aktionen mehr aus. Das Zusammensetzen und Anzeigen wird von SurfaceFlinger (für SurfaceView) oder von TextureView übernommen.
Fallstudie: Grafika's Double Decode
Grafika's Double Decode zeigt die Bearbeitung der SurfaceTexture in TextureView.
Bei Grafikas Double Decode werden zwei TextureView-Objekte verwendet, um zwei Videos nebeneinander abzuspielen und so eine Videokonferenz-App zu simulieren. Wenn sich die Ausrichtung des Bildschirms ändert und die Aktivität neu gestartet wird, werden die MediaCodec-Decoder nicht beendet, wodurch die Wiedergabe eines Echtzeit-Videostreams simuliert wird. Um die Effizienz zu steigern, sollte der Client die Oberfläche aktiv halten. Die Oberfläche ist ein Handle für die Producer-Schnittstelle in der BufferQueue von SurfaceTexture. Da das TextureView-Objekt die SurfaceTexture verwaltet, muss der Client die SurfaceTexture aktiv halten, damit die Oberfläche aktiv bleibt.
Damit die SurfaceTexture aktiv bleibt, ruft Grafika Double Decode Referenzen zu SurfaceTextures aus den TextureView-Objekten ab und speichert sie in einem statischen Feld.
Grafika's Double Decode gibt dann false von TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed() zurück, um die Zerstörung der SurfaceTexture zu verhindern. TextureView übergibt dann eine SurfaceTexture an onSurfaceTextureDestroyed(), die bei der Änderung der Aktivitätskonfiguration beibehalten werden kann. Der Client übergibt sie über setSurfaceTexture() an das neue TextureView-Objekt.
Jeder Videodecoder wird durch separate Threads gesteuert. Der Mediaserver sendet Puffer mit decodierter Ausgabe an die SurfaceTextures, die BufferQueue-Consumer. Die TextureView-Objekte führen das Rendern aus und werden im UI-Thread ausgeführt.
Die Implementierung von Grafikas Double Decode mit SurfaceView ist schwieriger als mit TextureView, da SurfaceView-Objekte Oberflächen bei Änderungen der Ausrichtung zerstören. Außerdem werden durch die Verwendung von SurfaceView-Objekten zwei Ebenen hinzugefügt, was aufgrund der Einschränkungen bei der Anzahl der Overlays, die auf der Hardware verfügbar sind, nicht ideal ist.