Die TextureView- Klasse ist ein Ansichtsobjekt, das eine Ansicht mit einer SurfaceTexture kombiniert.
Rendern mit OpenGL ES
Ein TextureView-Objekt umschließt eine SurfaceTexture, reagiert auf Rückrufe und ruft neue Puffer ab. Wenn ein TextureView neue Puffer abruft, gibt ein TextureView eine Anforderung zum Ungültigmachen der Ansicht aus und zeichnet mithilfe des Inhalts des neuesten Puffers als Datenquelle. Dabei wird gerendert, wo und wie auch immer der Ansichtsstatus es 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 auf einer TextureView rendert, befinden sich BufferQueue-Produzenten und -Konsumenten im selben Thread, was dazu führen kann, dass der Buffer-Swap-Aufruf blockiert oder fehlschlägt. Wenn ein Produzent beispielsweise mehrere Puffer schnell hintereinander vom UI-Thread übergibt, muss der EGL-Pufferaustauschaufruf einen Puffer aus der BufferQueue entfernen. Da sich Verbraucher und Produzent jedoch im selben Thread befinden, sind keine Puffer verfügbar und der Swap-Aufruf hängt oder schlägt fehl.
Um sicherzustellen, dass der Pufferaustausch nicht ins Stocken gerät, benötigt BufferQueue immer einen verfügbaren Puffer zum Entfernen aus der Warteschlange. Um dies zu implementieren, verwirft BufferQueue den Inhalt des zuvor erfassten Puffers, wenn ein neuer Puffer in die Warteschlange gestellt wird, und schränkt die minimale und maximale Pufferanzahl ein, um zu verhindern, dass ein Verbraucher alle Puffer auf einmal verbraucht.
Wählen Sie SurfaceView oder TextureView
SurfaceView und TextureView erfüllen ähnliche Rollen und sind beide Bürger der Ansichtshierarchie. Allerdings haben SurfaceView und TextureView unterschiedliche Implementierungen. Ein SurfaceView verwendet dieselben Parameter wie andere Ansichten, aber der SurfaceView-Inhalt ist beim Rendern transparent.
Eine TextureView verfügt über eine bessere Alpha- und Rotationsverarbeitung als eine SurfaceView, aber eine SurfaceView bietet Leistungsvorteile beim Zusammenstellen von über Videos geschichteten UI-Elementen. Wenn ein Client mit einem SurfaceView rendert, stellt das SurfaceView dem Client eine separate Kompositionsebene 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, fügt das UI-Toolkit den Inhalt der TextureView mit der GPU in die Ansichtshierarchie ein. Aktualisierungen des Inhalts können dazu führen, dass andere Ansichtselemente neu gezeichnet werden, beispielsweise wenn die anderen Ansichten über einer TextureView positioniert sind. Nachdem das Rendern der Ansicht abgeschlossen ist, setzt SurfaceFlinger die App-UI-Ebene und alle anderen Ebenen zusammen, sodass jedes sichtbare Pixel zweimal zusammengesetzt wird.
Fallstudie: Grafikas Play-Video
Grafikas Play Video umfasst zwei Videoplayer, einen mit TextureView und einen mit SurfaceView. Der Videodekodierungsteil der Aktivität sendet Frames von MediaCodec an eine Oberfläche für TextureView und SurfaceView. Der größte Unterschied zwischen den Implementierungen besteht in den Schritten, die erforderlich sind, um das richtige Seitenverhältnis darzustellen.
Für die Skalierung von SurfaceView ist eine benutzerdefinierte Implementierung von FrameLayout erforderlich. WindowManager muss eine neue Fensterposition und neue Größenwerte an SurfaceFlinger senden. Das Skalieren der SurfaceTexture einer TextureView erfordert die Konfiguration einer Transformationsmatrix mit TextureView#setTransform()
.
Nach Darstellung des korrekten Seitenverhältnisses folgen beide Implementierungen dem gleichen Muster. Wenn SurfaceView/TextureView die Oberfläche erstellt, ermöglicht der App-Code die Wiedergabe. Wenn ein Benutzer auf „Play“ tippt, wird ein Videodekodierungsthread gestartet, wobei die Oberfläche das Ausgabeziel ist. Danach führt der App-Code keine weiteren Aktionen aus – Komposition und Anzeige werden von SurfaceFlinger (für SurfaceView) oder von TextureView übernommen.
Fallstudie: Grafikas Double Decode
Grafikas Double Decode demonstriert die Manipulation der SurfaceTexture innerhalb einer TextureView.
Double Decode von Grafika verwendet ein Paar TextureView-Objekte, um zwei Videos nebeneinander abzuspielen und so eine Videokonferenz-App zu simulieren. Wenn sich die Ausrichtung des Bildschirms ändert und die Aktivität neu beginnt, stoppen die MediaCodec-Decoder nicht und simulieren die Wiedergabe eines Echtzeit-Videostreams. Um die Effizienz zu verbessern, sollte der Kunde die Oberfläche am Leben halten. Die Oberfläche ist ein Handle für die Producer-Schnittstelle in der BufferQueue der SurfaceTexture. Da TextureView die SurfaceTexture verwaltet, muss der Client die SurfaceTexture am Leben erhalten, um die Oberfläche am Leben zu halten.
Um die SurfaceTexture am Leben zu halten, ruft Grafikas Double Decode Verweise auf SurfaceTextures von den TextureView-Objekten ab und speichert sie in einem statischen Feld. Anschließend gibt Grafikas Double Decode false
von TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed()
zurück, um die Zerstörung der SurfaceTexture zu verhindern. TextureView übergibt dann eine SurfaceTexture an onSurfaceTextureDestroyed()
, die über die Aktivitätskonfigurationsänderung hinweg beibehalten werden kann, die der Client über setSurfaceTexture()
an die neue TextureView übergibt.
Separate Threads steuern jeden Videodecoder. Mediaserver sendet Puffer mit dekodierter Ausgabe an die SurfaceTextures, die BufferQueue-Konsumenten. Die TextureView-Objekte führen das Rendern durch und führen sie im UI-Thread aus.
Die Implementierung von Grafikas Double Decode mit SurfaceView ist schwieriger als die Implementierung mit TextureView, da SurfaceView-Objekte Oberflächen bei Orientierungsänderungen zerstören. Darüber hinaus werden durch die Verwendung von SurfaceView-Objekten zwei Ebenen hinzugefügt, was aufgrund der Einschränkungen hinsichtlich der Anzahl der auf der Hardware verfügbaren Overlays nicht ideal ist.