Die Klasse TextureView ist ein Ansichtsobjekt, das eine Ansicht mit einer SurfaceTexture kombiniert.
Rendering mit OpenGL ES
Ein TextureView-Objekt umschließt eine SurfaceTexture, reagiert auf Rückrufe und ruft neue Buffers ab. Wenn eine TextureView neue Buffers abruft, sendet sie eine Anfrage zum Ungültigmachen der Ansicht und zeichnet mit dem Inhalt des neuesten Buffers als Datenquelle. Das Rendering erfolgt dann an der Stelle und so, wie es der Ansichtsstatus vorgibt.
OpenGL ES (GLES) kann auf einer TextureView rendern, indem die SurfaceTexture an den EGL-Erstellungsaufruf übergeben wird. Dies führt jedoch zu einem Problem. Wenn GLES in einer TextureView rendert, befinden sich die BufferQueue-Ersteller und ‑Nutzer im selben Thread. Dies kann dazu führen, dass der Buffer-Swap-Aufruf ins Stocken gerät oder fehlschlägt. Wenn ein Produzent beispielsweise mehrere Buffers in schneller Folge aus dem UI-Thread einreicht, muss der EGL-Buffer-Swap-Aufruf einen Buffer aus der BufferQueue entfernen. Da sich Verbraucher und Erzeuger jedoch im selben Thread befinden, sind keine Puffer verfügbar und der Swap-Aufruf hängt oder schlägt fehl.
Damit der Puffertausch nicht ins Stocken gerät, muss in der BufferQueue immer ein Puffer verfügbar sein, der aus der Warteschlange entfernt werden kann. Dazu verwirft BufferQueue den Inhalt des zuvor abgerufenen Buffers, wenn ein neuer Buffer in die Warteschlange gestellt wird. Außerdem werden Einschränkungen für die Mindest- und Höchstzahl der Buffers festgelegt, um zu verhindern, dass ein Verbraucher alle Buffers gleichzeitig verbraucht.
SurfaceView oder TextureView auswählen
SurfaceView und TextureView haben ähnliche Rollen und sind beide Teil der Ansichtshierarchie. SurfaceView und TextureView haben jedoch unterschiedliche Implementierungen. Für eine SurfaceView gelten dieselben Parameter wie für andere Ansichten. Der Inhalt einer SurfaceView ist beim Rendern jedoch transparent.
Eine TextureView bietet eine bessere Alpha- und Drehung als eine SurfaceView. Eine SurfaceView bietet jedoch Leistungsvorteile beim Zusammensetzen von UI-Elementen, die über Videos gelegt werden. Wenn ein Client mit einer SurfaceView rendert, stellt die SurfaceView dem Client eine separate Kompositionebene zur Verfügung. SurfaceFlinger stellt die separate Ebene als Hardware-Overlay zusammen, sofern dies vom Gerät unterstützt wird. Wenn ein Client mit einer TextureView rendert, kombiniert das UI-Toolkit den Inhalt der TextureView mit der GPU in der Ansichtshierarchie. Aktualisierungen der Inhalte können dazu führen, dass andere Ansichtselemente neu gezeichnet werden, z. B. wenn sich die anderen Ansichten über einer TextureView befinden. Nach Abschluss des Ansichtsrenderings kombiniert SurfaceFlinger die App-UI-Ebene und alle anderen Ebenen, sodass jedes sichtbare Pixel zweimal kombiniert wird.
Fallstudie: Play-Video von Grafika
Play Video von Grafika enthält zwei Videoplayer, einen mit TextureView und einen mit SurfaceView. Der Teil der Aktivität, der für die Videodekodierung zuständig ist, sendet Frames von MediaCodec sowohl an TextureView als auch an SurfaceView. Der größte Unterschied zwischen den Implementierungen besteht in den Schritten, die zum Darstellen des richtigen Seitenverhältnisses erforderlich sind.
Zum Skalieren von SurfaceView ist eine benutzerdefinierte Implementierung von FrameLayout erforderlich.
WindowManager muss eine neue Fensterposition und neue Größenwerte an SurfaceFlinger senden. Zum Skalieren der SurfaceTexture einer TextureView muss eine Transformationsmatrix mit TextureView#setTransform()
konfiguriert werden.
Nach der Darstellung des richtigen Seitenverhältnisses folgen beide Implementierungen demselben Muster. Wenn SurfaceView/TextureView die Oberfläche erstellt, aktiviert der App-Code die Wiedergabe. Wenn ein Nutzer auf Wiedergabe tippt, wird ein Videodekodierungs-Thread gestartet, wobei die Oberfläche als Ausgabeziel dient. Danach führt der App-Code keine weiteren Aktionen aus. Die Zusammensetzung und Anzeige werden von SurfaceFlinger (für SurfaceView) oder von TextureView übernommen.
Fallstudie: Double Decode von Grafika
Double Decode von Grafika zeigt die Manipulation der SurfaceTexture in einer TextureView.
Bei der doppelten Dekodierung von Grafika werden zwei TextureView-Objekte verwendet, um zwei nebeneinander abgespielte Videos zu zeigen und so eine Videokonferenz-App zu simulieren. Wenn sich die Bildschirmausrichtung ändert und die Aktivität neu gestartet wird, werden die MediaCodec-Decoder nicht angehalten, wodurch die Wiedergabe eines Echtzeit-Videostreams simuliert wird. Zur Steigerung der Effizienz sollte der Kunde die Oberfläche aktiv halten. Die Oberfläche ist ein Handle für die Produzentenschnittstelle in der BufferQueue der SurfaceTexture. Da die SurfaceTexture von der TextureView verwaltet wird, muss der Client die SurfaceTexture aktiv halten, damit die Oberfläche aktiv bleibt.
Damit die SurfaceTexture aktiv bleibt, ruft die Double Decode-Funktion von Grafika Verweise auf SurfaceTextures aus den TextureView-Objekten ab und speichert sie in einem statischen Feld.
Anschließend gibt die Double Decode-Funktion von Grafika 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. Diese wird vom Client über setSurfaceTexture()
an die neue TextureView übergeben.
Für jeden Videodecoder werden separate Threads verwendet. Der Mediaserver sendet Puffer mit decodierter Ausgabe an die SurfaceTextures, die BufferQueue-Abnehmer. Die TextureView-Objekte führen das Rendering aus und werden im UI-Thread ausgeführt.
Die Implementierung der doppelten Dekodierung von Grafika mit SurfaceView ist schwieriger als mit TextureView, da SurfaceView-Objekte Oberflächen bei Richtungsänderungen 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 auf der Hardware nicht ideal ist.