TextureView

Klasa TextureView to obiekt widoku, który łączy widok z elementem SurfaceTexture.

Renderowanie za pomocą OpenGL ES

Obiekt TextureView otacza element SurfaceTexture, reagując na wywołania zwrotne i pobierając nowe bufory. Gdy obiekt TextureView pobiera nowe bufory, wysyła żądanie unieważnienia widoku i rysuje, używając zawartości najnowszego bufora jako źródła danych. Renderuje w dowolnym miejscu i w dowolny sposób, zgodnie ze stanem widoku.TextureView

OpenGL ES (GLES) może renderować na obiekcie TextureView przekazując element SurfaceTexture do wywołania tworzenia EGL, ale powoduje to problem. Gdy GLES renderuje na TextureView, producenci i konsumenci BufferQueue znajdują się w tym samym wątku, co może spowodować zawieszenie lub niepowodzenie wywołania zamiany bufora. Jeśli na przykład producent prześle kilka buforów w szybkim tempie z wątku UI, wywołanie zamiany bufora EGL musi usunąć bufor z kolejki BufferQueue. Ponieważ jednak konsument i producent znajdują się w tym samym wątku, nie będą dostępne żadne bufory, a wywołanie zamiany zawiesi się lub zakończy się niepowodzeniem.

Aby zapobiec zawieszaniu się zamiany bufora, BufferQueue zawsze potrzebuje bufora dostępnego do usunięcia z kolejki. W tym celu BufferQueue odrzuca zawartość wcześniej pobranego bufora, gdy do kolejki zostanie dodany nowy bufor. Nakłada też ograniczenia na minimalną i maksymalną liczbę buforów, aby uniemożliwić konsumentowi zużycie wszystkich buforów naraz.

Wybieranie SurfaceView lub TextureView

SurfaceView i TextureView pełnią podobne role i należą do hierarchii widoków. Mają jednak różne implementacje.SurfaceViewTextureView Obiekt SurfaceView przyjmuje te same parametry co inne widoki, ale jego zawartość jest przezroczysta podczas renderowania.SurfaceView

TextureView lepiej obsługuje przezroczystość i obracanie niż SurfaceView, ale SurfaceView ma przewagę pod względem wydajności podczas komponowania elementów interfejsu nakładanych na filmy. Gdy klient renderuje za pomocą obiektu SurfaceView, SurfaceView udostępnia mu oddzielną warstwę kompozycji. SurfaceFlinger komponuje oddzielną warstwę jako nakładkę sprzętową, jeśli jest ona obsługiwana przez urządzenie. Gdy klient renderuje za pomocą TextureView, zestaw narzędzi interfejsu komponuje zawartość obiektu TextureView w hierarchii widoków za pomocą procesora graficznego. Aktualizacje treści mogą powodować ponowne rysowanie innych elementów widoku, na przykład jeśli inne widoki są umieszczone nad TextureView. Po zakończeniu renderowania widoku SurfaceFlinger komponuje warstwę interfejsu aplikacji i wszystkie inne warstwy, dzięki czemu każdy widoczny piksel jest komponowany 2 razy.

Studium przypadku: Grafika's Play Video

Grafika's Play Video zawiera 2 odtwarzacze wideo – jeden zaimplementowany za pomocą TextureView, a drugi za pomocą SurfaceView. Część aktywności związana z dekodowaniem wideo wysyła klatki z MediaCodec do powierzchni zarówno w przypadku TextureView, jak i SurfaceView. Największa różnica między implementacjami polega na krokach wymaganych do wyświetlenia prawidłowego formatu obrazu.

Skalowanie SurfaceView wymaga niestandardowej implementacji FrameLayout. WindowManager musi wysłać do SurfaceFlinger nowe wartości pozycji i rozmiaru okna. Skalowanie elementu SurfaceTexture obiektu TextureView wymaga skonfigurowania macierzy transformacji za pomocą TextureView#setTransform().

Po wyświetleniu prawidłowego formatu obrazu obie implementacje działają tak samo. Gdy SurfaceView lub TextureView tworzy powierzchnię, kod aplikacji włącza odtwarzanie. Gdy użytkownik kliknie Odtwórz, uruchomi się wątek dekodowania wideo, a powierzchnia będzie celem wyjściowym. Następnie kod aplikacji nie robi nic – kompozycją i wyświetlaniem zajmuje się SurfaceFlinger (w przypadku SurfaceView) lub TextureView.

Studium przypadku: Grafika's Double Decode

Grafika's Double Decode pokazuje, jak manipulować elementem SurfaceTexture wewnątrz TextureView.

Grafika's Double Decode używa 2 obiektów TextureView, aby wyświetlać 2 filmy obok siebie, symulując aplikację do wideokonferencji. Gdy orientacja ekranu się zmienia i aktywność się restartuje, dekodery MediaCodec nie zatrzymują się, symulując odtwarzanie strumienia wideo w czasie rzeczywistym. Aby zwiększyć wydajność, klient powinien utrzymywać powierzchnię. Powierzchnia to uchwyt do interfejsu producenta w BufferQueue elementu SurfaceTexture. Ponieważ obiekt TextureView zarządza elementem SurfaceTexture, klient musi utrzymywać element SurfaceTexture, aby utrzymać powierzchnię.

Aby utrzymać element SurfaceTexture, Grafika's Double Decode pobiera odniesienia do SurfaceTexture z obiektów TextureView i zapisuje je w polu statycznym. Następnie Grafika's Double Decode zwraca wartość false z TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed(), aby zapobiec zniszczeniu elementu SurfaceTexture. TextureView przekazuje wtedy element SurfaceTexture do onSurfaceTextureDestroyed(), który można zachować podczas zmiany konfiguracji aktywności. Klient przekazuje go do nowego obiektu TextureView za pomocą setSurfaceTexture().

Każdy dekoder wideo jest obsługiwany przez osobny wątek. Mediaserver wysyła bufory z dekodowanymi danymi wyjściowymi do SurfaceTexture, czyli konsumentów BufferQueue. Obiekty TextureView wykonują renderowanie i działają w wątku UI.

Implementowanie Grafika's Double Decode za pomocą SurfaceView jest trudniejsze niż za pomocą TextureView, ponieważ obiekty SurfaceView niszczą powierzchnie podczas zmian orientacji. Dodatkowo używanie obiektów SurfaceView dodaje 2 warstwy, co nie jest idealne ze względu na ograniczenia dotyczące liczby nakładek dostępnych na sprzęcie.