Die Klasse TextureView ist ein Ansichtsobjekt, das eine Ansicht mit einer SurfaceTexture kombiniert.
Rendern mit OpenGL ES
Ein TextureView-Objekt umschließt eine SurfaceTexture, reagiert auf Callbacks und ruft neue Puffer ab. Wenn eine TextureView neue Puffer erhält, wird eine Anfrage zum Ungültigmachen der Ansicht ausgegeben und die Ansicht wird mit dem Inhalt des neuesten Puffers als Datenquelle gezeichnet. Die Darstellung erfolgt überall und so, wie es der Ansichtsstatus vorgibt.
OpenGL ES (GLES) kann auf einem TextureView 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-Ersteller und ‑Leser im selben Thread, was dazu führen kann, dass der Puffer-Swap-Aufruf 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 entfernt 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, muss in BufferQueue immer ein Puffer zum Dequeuing verfügbar sein. 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 nutzt.
SurfaceView oder TextureView auswählen
SurfaceView und TextureView haben ähnliche Funktionen und sind beide Teil der Ansichtshierarchie. SurfaceView und TextureView haben jedoch unterschiedliche Implementierungen. Eine SurfaceView verwendet dieselben Parameter wie andere Ansichten, aber der Inhalt von SurfaceView ist beim Rendern transparent.
Eine TextureView bietet eine bessere Alpha- und Rotationsverarbeitung als eine SurfaceView. Eine SurfaceView hat jedoch Leistungsvorteile beim Compositing von UI-Elementen, die über Videos gelegt werden. Wenn ein Client mit einer SurfaceView rendert, stellt die SurfaceView dem Client eine separate Kompositionsebene zur Verfügung. SurfaceFlinger stellt die separate Ebene als Hardware-Overlay zusammen, sofern das Gerät dies unterstützt. Wenn ein Client mit einer TextureView gerendert wird, fügt das UI-Toolkit den Inhalt der TextureView mit der GPU in die Ansichtshierarchie ein. Durch Aktualisierungen der Inhalte werden möglicherweise andere Ansichtselemente neu gezeichnet, z. B. wenn die anderen Ansichten über einer TextureView positioniert sind. Nachdem das Rendern der Ansicht abgeschlossen ist, führt SurfaceFlinger die App-UI-Ebene und alle anderen Ebenen zusammen, sodass jedes sichtbare Pixel zweimal zusammengeführt wird.
Fallstudie: Grafika's Play Video
Grafika's Play Video enthält zwei Videoplayer, einen mit TextureView und einen mit SurfaceView. Beim Videodecodieren werden Frames von MediaCodec an eine Oberfläche für TextureView und SurfaceView gesendet. Der größte Unterschied zwischen den Implementierungen besteht in den Schritten, die erforderlich sind, um das richtige Seitenverhältnis zu präsentieren.
Zum Skalieren 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 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/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 Erstellen und Anzeigen wird von SurfaceFlinger (für die SurfaceView) oder von der TextureView übernommen.
Fallstudie: Grafika – Double Decode
Grafika's Double Decode zeigt die Bearbeitung der SurfaceTexture in einer 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 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 Grafikas 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 einer Änderung der Aktivitätskonfiguration beibehalten werden kann. Der Client übergibt sie über setSurfaceTexture()
an die neue TextureView.
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 Rendering 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 Beschränkungen der Anzahl der auf der Hardware verfügbaren Overlays nicht ideal ist.