Klasa TextureView to obiekt widoku, który łączy widok z SurfaceTexture.
Renderowanie za pomocą OpenGL ES
Obiekt TextureView otacza obiekt SurfaceTexture, odpowiada na wywołania zwrotne i pobiera nowe bufory. Gdy TextureView pozyska nowe bufory, wysyła żądanie unieważnienia widoku i rysuje za pomocą zawartości najnowszego bufora jako źródła danych, renderując w dowolnym miejscu i w dowolny sposób, zgodnie ze stanem widoku.
OpenGL ES (GLES) może renderować na TextureView, przekazując SurfaceTexture do wywołania EGL, ale powoduje to problem. Gdy GLES renderuje na TextureView, producenci i konsumenci kolejki buforów znajdują się w tym samym wątku, co może spowodować zatrzymanie się lub niepowodzenie wywołania zamiany bufora. Jeśli na przykład producent przesyła kilka buforów z krótkim odstępem z wątku interfejsu użytkownika, wywołanie wymiany bufora EGL musi usunąć z kolejki bufor z kolejki buforów. Ponieważ jednak konsument i producent znajdują się w tym samym wątku, nie będzie dostępnych żadnych buforów, a wywołanie swap zostanie zawieszone lub zakończy się niepowodzeniem.
Aby zapewnić, że wymiana bufora nie będzie opóźniona, kolejka buforowa musi mieć zawsze dostępny bufor, który można usunąć z kolejki. Aby to zaimplementować, BufferQueue odrzuca zawartość wcześniej pozyskanego bufora, gdy nowy bufor jest umieszczany w kolejce, oraz nakłada ograniczenia na minimalną i maksymalną liczbę buforów, aby uniemożliwić konsumentowi wykorzystanie wszystkich buforów jednocześnie.
Wybieranie widoku SurfaceView lub TextureView
SurfaceView i TextureView pełnią podobne role i oba należą do hierarchii widoku. Jednak SurfaceView i TextureView mają różne implementacje. Widok SurfaceView przyjmuje te same parametry co inne widoki, ale jego zawartość jest przezroczysta podczas renderowania.
Widżet TextureView lepiej obsługuje przezroczystość i obrót niż SurfaceView, ale SurfaceView ma lepsze osiągi podczas łączenia elementów interfejsu nałożonych na filmy. Gdy klient renderuje SurfaceView, SurfaceView udostępnia klientowi oddzielną warstwę kompozycji. Jeśli urządzenie obsługuje tę funkcję, SurfaceFlinger tworzy oddzielną warstwę jako nakładkę sprzętową. Gdy klient renderuje obraz za pomocą TextureView, zestaw narzędzi interfejsu użytkownika łączy zawartość TextureView z hierarchią widoku za pomocą procesora graficznego. Zmiany w treści mogą powodować ponowne rysowanie innych elementów widoku, na przykład gdy inne widoki są umieszczone na wierzchu widoku TextureView. Po zakończeniu renderowania widoku SurfaceFlinger łączy warstwę interfejsu aplikacji i wszystkie inne warstwy, tak aby każdy widoczny piksel został złożony dwukrotnie.
Studium przypadku: film z gry Grafika
Grafika odtwarzania filmu zawiera parę odtwarzaczy wideo: jeden z TextureView, a drugi z SurfaceView. Część aktywności odpowiedzialna za dekodowanie wideo wysyła klatki z MediaCodec do 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.
OknoWindowManager musi wysłać do SurfaceFlinger nowe wartości położenia i rozmiaru okna. Pomiar obrazu SurfaceTexture elementu TextureView wymaga skonfigurowania macierzy transformacji za pomocą TextureView#setTransform()
.
Po przedstawieniu prawidłowego formatu obrazu obie implementacje będą się stosować do tego samego wzoru. Gdy SurfaceView lub TextureView utworzy powierzchnię, kod aplikacji włączy odtwarzanie. Gdy użytkownik kliknie odtwórz, rozpocznie się dekodowanie wątku wideo, a Surface będzie stanowiło docelowe urządzenie wyjściowe. Po tym kod aplikacji nie robi już nic – kompozycja i wyświetlanie są obsługiwane przez SurfaceFlinger (w przypadku SurfaceView) lub przez TextureView.
Studium przypadku: podwójne dekodowanie w Grafice
Grafika's Double Decode pokazuje manipulowanie SurfaceTexture wewnątrz TextureView.
Grafika Double Decode używa pary obiektów TextureView do wyświetlania dwóch filmów odtwarzanych obok siebie, symulując aplikację do rozmów wideo. Gdy orientacja ekranu się zmienia i czynność jest ponownie uruchamiana, dekodery MediaCodec nie przestają działać, symulując odtwarzanie strumienia wideo w czasie rzeczywistym. Aby zwiększyć wydajność, klient powinien utrzymywać powierzchnię w stanie aktywnym. Powierzchnia jest uchwytem interfejsu producenta w kole buforowej SurfaceTexture. Ponieważ TextureView zarządza SurfaceTexture, klient musi zachować SurfaceTexture, aby zachować powierzchnię.
Aby zachować SurfaceTexture, metoda DoubleDecode w klasie Grafika uzyskuje odwołania do SurfaceTexture z obiektów TextureView i zapisuje je w polu statycznym.
Następnie metoda DoubleDecode w klasie Grafika zwraca wartość false
z TextureView.SurfaceTextureListener#onSurfaceTextureDestroyed()
, aby zapobiec zniszczeniu obiektu SurfaceTexture. Następnie TextureView przekazuje SurfaceTexture do onSurfaceTextureDestroyed()
, co pozwala zachować zmiany konfiguracji aktywności, które klient przekazuje do nowego TextureView przez setSurfaceTexture()
.
Oddzielne wątki obsługują poszczególne dekodery wideo. Serwer multimediów wysyła bufory z dekodowanym wyjściem do SurfaceTextures, czyli do odbiorców kolejki buforowej. Obiekty TextureView wykonują renderowanie i wykonanie na wątku interfejsu użytkownika.
Zastosowanie funkcji podwójnego dekodowania Grafika w SurfaceView jest trudniejsze niż w TextureView, ponieważ obiekty SurfaceView usuwają powierzchnie podczas zmiany orientacji. Ponadto używanie obiektów SurfaceView powoduje dodanie 2 warstw, co nie jest idealne ze względu na ograniczenia dotyczące liczby nakładek dostępnych na sprzęcie.