TextureView

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

Renderowanie za pomocą OpenGL ES

Obiekt TextureView otacza obiekt SurfaceTexture, reagując na wywołania zwrotne i uzyskując nowe bufory. Gdy TextureView uzyskuje nowe bufory, wysyła żądanie unieważnienia widoku i rysuje, używając zawartości najnowszego bufora jako źródła danych, renderując w miejscu i w sposób wskazany przez stan widoku.

OpenGL ES (GLES) może renderować na obiekcie TextureView, przekazując 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 przesyła kilka buforów w szybkiej kolejności z wątku interfejsu, wywołanie zamiany bufora EGL musi usunąć bufor z kolejki buforów. Jednak ponieważ 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 niepowodzeniem.

Aby zapobiec wstrzymaniu zamiany bufora, BufferQueue zawsze potrzebuje bufora dostępnego do usunięcia z kolejki. W tym celu BufferQueue odrzuca zawartość wcześniej uzyskanego bufora, gdy w kolejce pojawi się nowy bufor. Nakłada też ograniczenia na minimalną i maksymalną liczbę buforów, aby zapobiec zużyciu wszystkich buforów przez konsumenta naraz.

Wybór SurfaceView lub TextureView

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

Obiekt TextureView lepiej obsługuje przezroczystość alfa i obracanie niż obiekt SurfaceView, ale ten drugi ma przewagę pod względem wydajności podczas łączenia elementów interfejsu nakładanych na filmy. Gdy klient renderuje za pomocą SurfaceView, SurfaceView udostępnia klientowi oddzielną warstwę kompozycji. SurfaceFlinger tworzy oddzielną warstwę jako nakładkę sprzętową, jeśli jest to obsługiwane przez urządzenie. Gdy klient renderuje za pomocą elementu TextureView, zestaw narzędzi interfejsu użytkownika łączy zawartość elementu 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 elementem TextureView. Po zakończeniu renderowania widoku SurfaceFlinger łączy warstwę interfejsu aplikacji i wszystkie inne warstwy, dzięki czemu każdy widoczny piksel jest łączony 2 razy.

Studium przypadku: Grafika's Play Video

Grafika's Play Video zawiera parę odtwarzaczy wideo, z których jeden jest zaimplementowany za pomocą TextureView, a drugi za pomocą SurfaceView. Część aktywności związana z dekodowaniem wideo wysyła klatki z MediaCodec na powierzchnię zarówno w przypadku TextureView, jak i SurfaceView. Największą różnicą między tymi implementacjami są czynności wymagane do wyświetlenia prawidłowego formatu obrazu.

Skalowanie SurfaceView wymaga niestandardowej implementacji FrameLayout. Menedżer okien musi wysłać do SurfaceFlingera nowe wartości położenia i rozmiaru okna. Skalowanie elementu SurfaceTexture w obiekcie TextureView wymaga skonfigurowania macierzy przekształcenia za pomocą funkcji TextureView#setTransform().

Po przedstawieniu prawidłowego formatu obie implementacje działają w ten sam sposób. Gdy SurfaceView/TextureView utworzy powierzchnię, kod aplikacji włączy odtwarzanie. Gdy użytkownik kliknie odtwarzanie, rozpocznie się wątek dekodowania filmu, a powierzchnia będzie miejscem docelowym danych wyjściowych. Potem kod aplikacji nie robi już nic – kompozycją i wyświetlaniem zajmuje się SurfaceFlinger (w przypadku SurfaceView) lub TextureView.

Studium przypadku: podwójne dekodowanie Grafiki

Grafika's Double Decode pokazuje manipulację elementem SurfaceTexture w obiekcie TextureView.

Przykład Grafika's Double Decode wykorzystuje parę obiektów TextureView do wyświetlania dwóch filmów odtwarzanych obok siebie, symulując aplikację do obsługi rozmów wideo. Gdy orientacja ekranu się zmieni i aktywność zostanie ponownie uruchomiona, dekodery MediaCodec nie zatrzymają się, symulując odtwarzanie strumienia wideo w czasie rzeczywistym. Aby zwiększyć wydajność, klient powinien utrzymywać aktywność powierzchni. Powierzchnia to uchwyt do interfejsu producenta w obiekcie BufferQueue interfejsu SurfaceTexture. Ponieważ TextureView zarządza SurfaceTexture, klient musi utrzymywać SurfaceTexture, aby utrzymać powierzchnię.

Aby utrzymać SurfaceTexture, funkcja Double Decode w Grafice pobiera odwołania do SurfaceTexture z obiektów TextureView i zapisuje je w polu statycznym. Następnie funkcja Double Decode w bibliotece Grafika zwraca wartość false z wartości TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed(), aby zapobiec zniszczeniu obiektu SurfaceTexture. Następnie TextureView przekazuje SurfaceTexture do onSurfaceTextureDestroyed(), który może być zachowany 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 zdekodowanymi danymi wyjściowymi do SurfaceTexture, czyli odbiorców BufferQueue. Obiekty TextureView wykonują renderowanie i działają w wątku interfejsu.

Wdrożenie podwójnego dekodowania Grafiki za pomocą SurfaceView jest trudniejsze niż w przypadku 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 liczby nakładek dostępnych na sprzęcie.