Interfejs Frame Rate API umożliwia aplikacjom informowanie platformy Android o docelowej liczbie klatek. Jest dostępny w aplikacjach kierowanych na Androida 11 (poziom 30 interfejsu API) lub nowszego. Tradycyjnie większość urządzeń obsługiwała tylko jedną częstotliwość odświeżania ekranu, zwykle 60 Hz, ale to się zmienia. Wiele urządzeń obsługuje teraz dodatkowe częstotliwości odświeżania, takie jak 90 Hz czy 120 Hz. Niektóre urządzenia obsługują płynne przełączanie częstotliwości odświeżania, a inne na krótko wyświetlają czarny ekran, zwykle przez sekundę.
Głównym celem interfejsu API jest umożliwienie aplikacjom lepszego wykorzystania wszystkich obsługiwanych częstotliwości odświeżania ekranu. Na przykład aplikacja odtwarzająca film w 24 Hz, która wywołuje funkcję setFrameRate(), może spowodować zmianę częstotliwości odświeżania ekranu z 60 Hz na 120 Hz. Ta nowa częstotliwość odświeżania umożliwia płynne odtwarzanie filmów w 24 Hz bez zacinania się i bez konieczności stosowania techniki 3:2 pulldown, która byłaby wymagana do odtwarzania tego samego filmu na ekranie 60 Hz. Zapewnia to lepsze wrażenia użytkownika.
Podstawowe użycie
Android udostępnia kilka sposobów uzyskiwania dostępu do powierzchni i sterowania nimi, dlatego istnieje kilka wersji interfejsu setFrameRate() API. Każda wersja interfejsu API przyjmuje te same parametry i działa tak samo jak inne:
Surface.setFrameRate()SurfaceControl.Transaction.setFrameRate()ANativeWindow_setFrameRate()ASurfaceTransaction_setFrameRate()
Aby bezpiecznie wywołać funkcję setFrameRate(), aplikacja nie musi brać pod uwagę rzeczywistych obsługiwanych częstotliwości odświeżania ekranu,
które można uzyskać, wywołując funkcję
Display.getSupportedModes(). Na przykład nawet jeśli urządzenie obsługuje tylko 60 Hz, wywołaj funkcję setFrameRate() z preferowaną przez aplikację liczbą klatek.
Urządzenia, które nie mają lepszego dopasowania do liczby klatek aplikacji, pozostaną przy bieżącej częstotliwości odświeżania ekranu.
Aby sprawdzić, czy wywołanie funkcji setFrameRate() powoduje zmianę częstotliwości odświeżania ekranu, zarejestruj się, aby otrzymywać powiadomienia o zmianie ekranu, wywołując funkcję DisplayManager.registerDisplayListener()lub AChoreographer_registerRefreshRateCallback().
Podczas wywoływania funkcji setFrameRate() najlepiej jest przekazywać dokładną liczbę klatek, a nie zaokrąglać jej do liczby całkowitej. Na przykład podczas renderowania filmu nagranego w 29,97 Hz przekaż wartość 29,97, a nie zaokrąglaj jej do 30.
W przypadku aplikacji wideo parametr zgodności przekazywany do funkcji setFrameRate() powinien być ustawiony na Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, aby dodatkowo poinformować platformę Android, że aplikacja będzie używać techniki pulldown do dostosowania się do niezgodnej częstotliwości odświeżania ekranu (co spowoduje zacinanie się obrazu).
W niektórych przypadkach powierzchnia wideo przestanie przesyłać klatki, ale przez pewien czas pozostanie widoczna na ekranie. Typowe scenariusze to sytuacje, gdy odtwarzanie dobiega końca lub gdy użytkownik wstrzymuje odtwarzanie. W takich przypadkach wywołaj funkcję setFrameRate() z parametrem liczby klatek ustawionym na 0, aby wyczyścić ustawienie liczby klatek powierzchni i przywrócić wartość domyślną. Wyczyszczenie ustawienia liczby klatek w ten sposób nie jest konieczne podczas niszczenia powierzchni ani gdy powierzchnia jest ukryta, ponieważ użytkownik przełącza się na inną aplikację. Ustawienie liczby klatek należy wyczyścić tylko wtedy, gdy powierzchnia pozostaje widoczna, ale nie jest używana.
Niepłynne przełączanie liczby klatek
Na niektórych urządzeniach przełączanie częstotliwości odświeżania może powodować wizualne przerwy, takie jak czarny ekran przez sekundę lub dwie. Zwykle występuje to w przypadku dekoderów, paneli telewizyjnych i podobnych urządzeń. Domyślnie platforma Android nie przełącza trybów
gdy wywoływany jest interfejs Surface.setFrameRate()
API, aby uniknąć takich wizualnych przerw.
Niektórzy użytkownicy wolą wizualne przerwy na początku i na końcu dłuższych filmów. Umożliwia to dopasowanie częstotliwości odświeżania ekranu do liczby klatek filmu i uniknięcie artefaktów konwersji liczby klatek, takich jak zacinanie się obrazu spowodowane techniką 3:2 pulldown podczas odtwarzania filmu.
Z tego powodu niepłynne przełączanie częstotliwości odświeżania można włączyć, jeśli użytkownik i aplikacje wyrażą na to zgodę:
- Użytkownicy: aby wyrazić zgodę, użytkownicy mogą włączyć ustawienie użytkownika Dopasuj liczbę klatek treści.
- Aplikacje: aby wyrazić zgodę, aplikacje mogą przekazać wartość
CHANGE_FRAME_RATE_ALWAYSdo funkcjisetFrameRate().
W przypadku długich filmów, takich jak filmy, zawsze zalecamy używanie wartości CHANGE_FRAME_RATE_ALWAYS. Wynika to z tego, że korzyści z dopasowania liczby klatek filmu przeważają nad przerwą, która występuje podczas zmiany częstotliwości odświeżania.
Dodatkowe zalecenia
W typowych scenariuszach postępuj zgodnie z tymi zaleceniami.
Wiele powierzchni
Platforma Android jest zaprojektowana tak, aby prawidłowo obsługiwać scenariusze, w których występuje wiele powierzchni z różnymi ustawieniami liczby klatek. Jeśli aplikacja ma wiele powierzchni z różnymi liczbami klatek, wywołaj funkcję setFrameRate() z prawidłową liczbą klatek dla każdej powierzchni. Nawet jeśli na urządzeniu działa jednocześnie kilka aplikacji, korzystając z trybu podzielonego ekranu lub obrazu w obrazie, każda aplikacja może bezpiecznie wywołać funkcję setFrameRate() dla swoich powierzchni.
Platforma nie zmienia liczby klatek na liczbę klatek aplikacji
Nawet jeśli urządzenie obsługuje liczbę klatek określoną przez aplikację w wywołaniu funkcji setFrameRate(), w niektórych przypadkach urządzenie nie przełączy ekranu na tę częstotliwość odświeżania. Na przykład powierzchnia o wyższym priorytecie może mieć inne ustawienie liczby klatek lub urządzenie może być w trybie oszczędzania baterii (ograniczającym częstotliwość odświeżania ekranu w celu oszczędzania baterii). Aplikacja musi nadal działać prawidłowo, gdy urządzenie nie przełącza częstotliwości odświeżania ekranu na ustawienie liczby klatek aplikacji, nawet jeśli w normalnych warunkach urządzenie przełącza się.
To aplikacja decyduje, jak reagować, gdy częstotliwość odświeżania ekranu nie odpowiada liczbie klatek aplikacji. W przypadku wideo liczba klatek jest stała i odpowiada liczbie klatek źródłowego filmu, a do wyświetlenia treści wideo będzie wymagana technika pulldown. Gra może zamiast tego spróbować działać z częstotliwością odświeżania ekranu, a nie z preferowaną liczbą klatek. Aplikacja nie powinna zmieniać wartości przekazywanej do funkcji setFrameRate() na podstawie tego, co robi platforma. Powinna ona pozostać ustawiona na preferowaną liczbę klatek aplikacji, niezależnie od tego, jak aplikacja obsługuje przypadki, w których platforma nie dostosowuje się do żądania aplikacji. Dzięki temu, jeśli warunki urządzenia zmienią się tak, że będzie można używać dodatkowych częstotliwości odświeżania ekranu, platforma będzie mieć prawidłowe informacje, aby przełączyć się na preferowaną liczbę klatek aplikacji.
W przypadkach, gdy aplikacja nie może lub nie chce działać z częstotliwością odświeżania ekranu, powinna określić sygnatury czasowe prezentacji dla każdej klatki, używając jednego z mechanizmów platformy do ustawiania sygnatur czasowych prezentacji:
Używanie tych sygnatur czasowych uniemożliwia platformie zbyt wczesne wyświetlenie klatki aplikacji, co spowodowałoby niepotrzebne zacinanie się obrazu. Prawidłowe używanie sygnatur czasowych prezentacji klatek jest nieco skomplikowane. W przypadku gier więcej informacji o unikaniu zacinania się obrazu znajdziesz w naszym przewodniku po tempie klatek . Rozważ też użycie biblioteki Android Frame Pacing.
W niektórych przypadkach platforma może przełączyć się na wielokrotność liczby klatek określonej przez aplikację w funkcji setFrameRate(). Na przykład aplikacja może wywołać funkcję setFrameRate() z wartością 60 Hz, a urządzenie może przełączyć ekran na 120 Hz. Jednym z powodów może być to, że inna aplikacja ma powierzchnię z ustawieniem liczby klatek na 24 Hz. W takim przypadku wyświetlanie z częstotliwością 120 Hz umożliwi działanie zarówno powierzchni 60 Hz, jak i 24 Hz bez konieczności stosowania techniki pulldown.
Gdy ekran działa z wielokrotnością liczby klatek aplikacji, aplikacja powinna określić sygnatury czasowe prezentacji dla każdej klatki, aby uniknąć niepotrzebnego zacinania się obrazu. W przypadku gier biblioteka Android Frame Pacing pomaga prawidłowo ustawiać sygnatury czasowe prezentacji klatek.
setFrameRate() a preferredDisplayModeId
WindowManager.LayoutParams.preferredDisplayModeId
to kolejny sposób, w jaki aplikacje mogą wskazywać platformie liczbę klatek. Niektóre aplikacje chcą tylko zmienić częstotliwość odświeżania ekranu, a nie inne ustawienia trybu wyświetlania, takie jak rozdzielczość ekranu. Ogólnie rzecz biorąc, używaj funkcji setFrameRate() zamiast preferredDisplayModeId. Funkcja setFrameRate() jest łatwiejsza w użyciu, ponieważ aplikacja nie musi przeszukiwać listy trybów wyświetlania, aby znaleźć tryb z określoną liczbą klatek.
Funkcja setFrameRate() daje platformie więcej możliwości wyboru zgodnej liczby klatek w scenariuszach, w których występuje wiele powierzchni działających z różnymi liczbami klatek. Rozważ na przykład scenariusz, w którym 2 aplikacje działają w trybie podzielonego ekranu na Pixelu 4. Jedna aplikacja odtwarza film w 24 Hz, a druga wyświetla użytkownikowi listę z możliwością przewijania. Pixel 4 obsługuje 2 częstotliwości odświeżania ekranu: 60 Hz i 90 Hz. Korzystając z interfejsu preferredDisplayModeId API, powierzchnia wideo jest zmuszona do wybrania 60 Hz lub 90 Hz. Wywołując funkcję setFrameRate() z wartością 24 Hz, powierzchnia wideo przekazuje platformie więcej informacji o liczbie klatek źródłowego filmu, co umożliwia platformie wybranie 90 Hz jako częstotliwości odświeżania ekranu, która w tym scenariuszu jest lepsza niż 60 Hz.
Istnieją jednak scenariusze, w których zamiast funkcji setFrameRate() należy użyć funkcji preferredDisplayModeId, np.:
- Jeśli aplikacja chce zmienić rozdzielczość lub inne ustawienia trybu wyświetlania, użyj funkcji
preferredDisplayModeId. - Platforma przełączy tryby wyświetlania tylko w odpowiedzi na wywołanie funkcji
setFrameRate(), jeśli przełączenie trybu jest lekkie i prawdopodobnie niezauważalne dla użytkownika. Jeśli aplikacja woli przełączyć częstotliwość odświeżania ekranu, nawet jeśli wymaga to ciężkiego przełączenia trybu (np. na urządzeniu z Androidem TV), użyj funkcjipreferredDisplayModeId. - Aplikacje, które nie mogą obsługiwać ekranu działającego z wielokrotnością liczby klatek aplikacji, co wymaga ustawienia sygnatur czasowych prezentacji dla każdej klatki, powinny używać funkcji
preferredDisplayModeId.
setFrameRate() a preferredRefreshRate
WindowManager.LayoutParams#preferredRefreshRate
ustawia preferowaną liczbę klatek w oknie aplikacji, a ta liczba klatek ma zastosowanie do wszystkich powierzchni w oknie. Aplikacja powinna określić preferowaną liczbę klatek niezależnie od obsługiwanych przez urządzenie częstotliwości odświeżania, podobnie jak w przypadku funkcji setFrameRate(), aby dać harmonogramowi lepszą wskazówkę co do docelowej liczby klatek aplikacji.
W przypadku powierzchni, które używają funkcji setFrameRate(), funkcja preferredRefreshRate jest ignorowana. Ogólnie rzecz biorąc, jeśli to możliwe, używaj funkcji setFrameRate().
preferredRefreshRate a preferredDisplayModeId
Jeśli aplikacje chcą tylko zmienić preferowaną częstotliwość odświeżania, lepiej jest użyć funkcji preferredRefreshRate niż preferredDisplayModeId.
Unikanie zbyt częstego wywoływania funkcji setFrameRate()
Chociaż wywołanie funkcji setFrameRate() nie jest zbyt kosztowne pod względem wydajności, aplikacje powinny unikać wywoływania funkcji setFrameRate() co klatkę lub kilka razy na sekundę. Wywołania funkcji setFrameRate() prawdopodobnie spowodują zmianę częstotliwości odświeżania ekranu, co może spowodować utratę klatki podczas przejścia.
Z góry określ prawidłową liczbę klatek i wywołaj funkcję setFrameRate() tylko raz.
Użycie w przypadku gier lub innych aplikacji niebędących aplikacjami wideo
Chociaż wideo jest głównym przypadkiem użycia interfejsu setFrameRate() API, można go używać w innych aplikacjach. Na przykład gra, która nie ma działać z częstotliwością większą niż 60 Hz (aby zmniejszyć zużycie energii i wydłużyć sesje gry), może wywołać funkcję Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT). W ten sposób urządzenie, które domyślnie działa z częstotliwością 90 Hz, będzie działać z częstotliwością 60 Hz, gdy gra jest aktywna, co pozwoli uniknąć zacinania się obrazu, które wystąpiłoby, gdyby gra działała z częstotliwością 60 Hz, a ekran z częstotliwością 90 Hz.
Użycie funkcji FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
Funkcja FRAME_RATE_COMPATIBILITY_FIXED_SOURCE jest przeznaczona tylko dla aplikacji wideo. W przypadku użycia innego niż wideo użyj funkcji FRAME_RATE_COMPATIBILITY_DEFAULT.
Wybieranie strategii zmiany liczby klatek
- Zdecydowanie zalecamy, aby aplikacje podczas wyświetlania długich filmów, takich jak filmy, wywoływały funkcję
setFrameRate(fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS), gdzie fps to liczba klatek filmu. - Zdecydowanie odradzamy aplikacjom wywoływanie funkcji
setFrameRate()z wartościąCHANGE_FRAME_RATE_ALWAYS, gdy spodziewasz się, że odtwarzanie filmu będzie trwało kilka minut lub krócej.
Przykładowa integracja w przypadku aplikacji do odtwarzania wideo
W przypadku integracji przełączania częstotliwości odświeżania w aplikacjach do odtwarzania wideo zalecamy wykonanie tych czynności:
- Określ wartość
changeFrameRateStrategy:- Jeśli odtwarzasz długi film, np. film, użyj wartości
MATCH_CONTENT_FRAMERATE_ALWAYS - Jeśli odtwarzasz krótki film, np. zwiastun filmu, użyj wartości
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS.
- Jeśli odtwarzasz długi film, np. film, użyj wartości
- Jeśli wartość
changeFrameRateStrategytoCHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, przejdź do kroku 4. - Sprawdź, czy ma nastąpić niepłynne przełączenie częstotliwości odświeżania, sprawdzając, czy oba te warunki są spełnione:
- Przełączenie trybu płynnego z bieżącej częstotliwości odświeżania (nazwijmy ją C) na liczbę klatek filmu (nazwijmy ją V) nie jest możliwe. Będzie tak, jeśli wartości C i V są różne, a funkcja
Display.getMode().getAlternativeRefreshRatesnie zawiera wielokrotności wartości V. - Użytkownik wyraził zgodę na niepłynne zmiany częstotliwości odświeżania. Możesz to wykryć
sprawdzając, czy
DisplayManager.getMatchContentFrameRateUserPreferencezwracaMATCH_CONTENT_FRAMERATE_ALWAYS
- Przełączenie trybu płynnego z bieżącej częstotliwości odświeżania (nazwijmy ją C) na liczbę klatek filmu (nazwijmy ją V) nie jest możliwe. Będzie tak, jeśli wartości C i V są różne, a funkcja
- Jeśli przełączenie będzie płynne, wykonaj te czynności:
- Wywołaj funkcję
setFrameRatei przekaż jej wartościfps,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, ichangeFrameRateStrategy, gdziefpsto liczba klatek filmu. - Rozpocznij odtwarzanie filmu.
- Wywołaj funkcję
- Jeśli ma nastąpić niepłynna zmiana trybu, wykonaj te czynności:
- Wyświetl interfejs użytkownika, aby powiadomić użytkownika. Pamiętaj, że zalecamy zaimplementowanie sposobu, w jaki użytkownik może zamknąć ten interfejs i pominąć dodatkowe opóźnienie w kroku 5.d. Wynika to z tego, że zalecane przez nas opóźnienie jest większe niż konieczne w przypadku ekranów, które szybciej się przełączają.
- Wywołaj funkcję
setFrameRatei przekaż jej wartościfps,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, iCHANGE_FRAME_RATE_ALWAYS, gdziefpsto liczba klatek filmu. - Poczekaj na wywołanie zwrotne
onDisplayChanged. - Poczekaj 2 sekundy na zakończenie przełączania trybu.
- Rozpocznij odtwarzanie filmu.
Pseudokod obsługujący tylko płynne przełączanie wygląda tak:
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();
Pseudokod obsługujący płynne i niepłynne przełączanie opisane powyżej wygląda tak:
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
if (isSeamlessSwitch(contentFrameRate)) {
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();
} else if (displayManager.getMatchContentFrameRateUserPreference()
== MATCH_CONTENT_FRAMERATE_ALWAYS) {
showRefreshRateSwitchUI();
sleep(shortDelaySoUserSeesUi);
displayManager.registerDisplayListener(…);
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ALWAYS);
transaction.apply();
waitForOnDisplayChanged();
sleep(twoSeconds);
hideRefreshRateSwitchUI();
beginPlayback();
}