Interfejs API częstotliwości klatek pozwala aplikacjom informować platformę Androida o zamierzonej częstotliwości klatek. Jest dostępny w aplikacjach kierowanych na Androida 11 (poziom API 30) lub nowszego. Tradycyjnie większość urządzeń obsługiwała tylko jedną częstotliwość odświeżania ekranu, zazwyczaj 60 Hz, ale sytuacja się zmienia. Wiele urządzeń obsługuje teraz dodatkowe częstotliwości odświeżania, takie jak 90 Hz lub 120 Hz. Niektóre urządzenia obsługują płynną zmianę częstotliwości odświeżania, podczas gdy inne na krótko wyświetlają czarny ekran (zwykle trwa to około sekundy).
Głównym celem interfejsu API jest umożliwienie aplikacjom lepszego korzystania ze wszystkich obsługiwanych częstotliwości odświeżania wyświetlacza. Na przykład aplikacja odtwarzająca film w standardzie 24 Hz, która wywołuje funkcję setFrameRate()
, może spowodować, że urządzenie zmieni częstotliwość odświeżania wyświetlacza z 60 Hz na 120 Hz. Ta nowa częstotliwość odświeżania umożliwia płynne odtwarzanie filmów w standardzie 24 Hz bez efektu trzęsienia obrazu. Nie trzeba już stosować funkcji pulldown 3:2, która jest wymagana do odtwarzania tego samego filmu na wyświetlaczu 60 Hz. W efekcie użytkownicy będą mieli lepsze wrażenia.
Podstawowe użycie
Android udostępnia kilka sposobów na uzyskiwanie dostępu do interfejsów i ich kontrolowanie, dlatego istnieje kilka wersji interfejsu setFrameRate()
API. Każda wersja interfejsu API przyjmuje te same parametry i działa w ten sam sposób:
Surface.setFrameRate()
SurfaceControl.Transaction.setFrameRate()
ANativeWindow_setFrameRate()
ASurfaceTransaction_setFrameRate()
Aplikacja nie musi uwzględniać rzeczywistych obsługiwanych częstotliwości odświeżania wyświetlacza, których można użyć, wywołując funkcję Display.getSupportedModes()
, aby bezpiecznie wywołać funkcję setFrameRate()
. Na przykład nawet wtedy, gdy urządzenie obsługuje tylko 60 Hz, wywołanie setFrameRate()
będzie miało liczbę klatek na sekundę preferowaną przez Twoją aplikację.
Urządzenia, które nie mają lepszego dopasowania do częstotliwości wyświetlania aplikacji, pozostaną przy bieżącej częstotliwości odświeżania.
Aby sprawdzić, czy wywołanie funkcji setFrameRate()
powoduje zmianę częstotliwości odświeżania wyświetlacza, zarejestruj się, aby otrzymywać powiadomienia o zmianach wyświetlacza. Aby to zrobić, wywołaj funkcję DisplayManager.registerDisplayListener()
lub AChoreographer_registerRefreshRateCallback()
.
Gdy wywołujesz funkcję setFrameRate()
, najlepiej przekazać dokładną liczbę klatek na sekundę zamiast zaokrąglenia do liczby całkowitej. Na przykład podczas renderowania filmu nagranego z częstotliwością 29,97 Hz podaj wartość 29,97, a nie zaokrągloną wartość 30.
W przypadku aplikacji wideo parametr zgodności przekazywany do setFrameRate()
powinien mieć wartość Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
, aby dać platformie Android dodatkową wskazówkę, że aplikacja będzie używać funkcji pulldown, aby dostosować się do niepasującej częstotliwości odświeżania wyświetlacza (co spowoduje zjawisko judder).
W niektórych przypadkach powierzchnia wideo przestanie przesyłać klatki, ale przez jakiś czas będzie widoczna na ekranie. Typowe scenariusze to zakończenie odtwarzania filmu lub wstrzymanie odtwarzania przez użytkownika. W takich przypadkach wywołaj funkcję setFrameRate()
, ustawiając parametr częstotliwości klatek na 0, aby przywrócić ustawienie częstotliwości klatek na powierzchni do wartości domyślnej. Wyczyszczenie tego ustawienia nie jest konieczne, gdy usuwasz powierzchnię lub gdy jest ona ukryta, ponieważ użytkownik przełączył się na inną aplikację. Wyczyść ustawienie częstotliwości klatek tylko wtedy, gdy powierzchnia pozostaje widoczna bez użycia.
Przełączanie liczby klatek bez płynnego przejścia
Na niektórych urządzeniach przełączanie częstotliwości odświeżania może powodować przerwy w wyświetlaniu obrazu, np. przez sekundę może być czarny ekran. Zwykle dzieje się tak w przypadku dekoderów, paneli telewizyjnych i podobnych urządzeń. Domyślnie framework Androida nie przełącza trybów podczas wywołania interfejsu API Surface.setFrameRate()
, aby uniknąć takich wizualnych przerw.
Niektórzy użytkownicy wolą wizualne przerwy na początku i na końcu dłuższych filmów. Dzięki temu częstotliwość odświeżania wyświetlacza będzie odpowiadać częstotliwości odświeżania filmu, co pozwoli uniknąć artefaktów związanych z konwersją częstotliwości odświeżania, takich jak efekt juddera w przypadku obrazu 3:2 podczas odtwarzania filmu.
Z tego powodu przełączanie częstotliwości odświeżania bez płynnego przejścia może być włączone, jeśli użytkownik i aplikacje wyrażą na to zgodę:
- Użytkownicy: aby włączyć tę funkcję, użytkownicy mogą włączyć ustawienie Dopasuj częstotliwość klatek treści.
- Aplikacje: aby włączyć tę opcję, aplikacje mogą przesyłać dane
CHANGE_FRAME_RATE_ALWAYS
dosetFrameRate()
.
Zalecamy, aby w przypadku długich filmów, takich jak filmy fabularne, zawsze używać tagu CHANGE_FRAME_RATE_ALWAYS
. Dzieje się tak, ponieważ korzyści z dopasowania liczby klatek filmu do częstotliwości odświeżania przewyższają przerwę, która występuje podczas zmiany częstotliwości odświeżania.
Dodatkowe zalecenia
W przypadku typowych scenariuszy postępuj zgodnie z tymi zaleceniami.
Wiele powierzchni
Platforma Android została zaprojektowana tak, aby prawidłowo obsługiwać scenariusze, w których występuje wiele powierzchni z różnymi ustawieniami liczby klatek na sekundę. Jeśli aplikacja ma wiele powierzchni z różnymi częstotliwościami klatek, wywołaj funkcję setFrameRate()
z prawidłową częstotliwością klatek dla każdej z nich. Nawet jeśli na urządzeniu działa kilka aplikacji jednocześnie, korzystając z podziału ekranu lub trybu obrazu w obrazie, każda z nich może bezpiecznie wywoływać setFrameRate()
dla swoich powierzchni.
Platforma nie zmienia liczby klatek aplikacji
Nawet jeśli urządzenie obsługuje częstotliwość klatek określoną przez aplikację w wywołaniu setFrameRate()
, w niektórych przypadkach urządzenie nie przełączy wyświetlacza na tę częstotliwość odświeżania. Na przykład powierzchnia o wyższym priorytecie może mieć inne ustawienie częstotliwości klatek lub urządzenie może być w trybie oszczędzania baterii (co powoduje ograniczenie częstotliwości odświeżania wyświetlacza w celu oszczędzania baterii). Aplikacja musi działać prawidłowo, gdy urządzenie nie przełączy częstotliwości odświeżania ekranu na ustawienie częstotliwości klatek aplikacji, nawet jeśli urządzenie przełączy się w normalnych okolicznościach.
Gdy częstotliwość odświeżania wyświetlacza nie pasuje do częstotliwości wyświetlania klatek aplikacji, aplikacja sama decyduje, jak zareagować. W przypadku filmów liczba klatek jest stała i odpowiada liczbie klatek filmu źródłowego. Aby wyświetlić treści wideo, trzeba będzie przewinąć film. Zamiast tego gra może próbować działać z częstotliwością odświeżania wyświetlacza zamiast z preferowaną liczbą klatek. Aplikacja nie powinna zmieniać wartości przekazywanej do setFrameRate()
w zależności od tego, co robi platforma. Ustawienie powinno odpowiadać preferowanej częstotliwości odświeżania aplikacji niezależnie od tego, jak aplikacja radzi sobie w przypadkach, gdy platforma nie dostosowuje się do jej żądania. Dzięki temu, jeśli warunki na urządzeniu zmienią się w taki sposób, że będzie można użyć dodatkowych częstotliwości odświeżania ekranu, platforma będzie mieć prawidłowe informacje, aby przełączyć się na preferowaną częstotliwość klatek aplikacji.
Jeśli aplikacja nie będzie działać z częstotliwością odświeżania ekranu lub nie będzie mogła działać z taką częstotliwością, powinna określić sygnatury czasowe wyświetlania dla każdego obrazu za pomocą jednego z tych mechanizmów platformy do ustawiania sygnatur czasowych wyświetlania:
Dzięki tym sygnaturom czasowym platforma nie wyświetla ramki aplikacji zbyt wcześnie, co mogłoby spowodować niepotrzebne zacinanie. Prawidłowe używanie sygnatur czasowych wyświetlania klatek jest nieco kłopotliwe. W przypadku gier zapoznaj się z przewodnikiem po ustawianiu odstępu między klatkami, aby dowiedzieć się więcej o unikaniu zacięć. Możesz też skorzystać z biblioteki Android Frame Pacing.
W niektórych przypadkach platforma może przełączyć się na wielokrotność częstotliwości klatek aplikacji określonej w setFrameRate()
. Aplikacja może na przykład wywołać setFrameRate()
z częstotliwością 60 Hz, a urządzenie może przełączyć wyświetlacz na częstotliwość 120 Hz. Może się tak zdarzyć, jeśli inna aplikacja ma powierzchnię z ustawieniem częstotliwości klatek 24 Hz. W takim przypadku wyświetlanie z częstotliwością 120 Hz pozwoli na wyświetlanie powierzchni 60 Hz i 24 Hz bez konieczności przewijania.
Gdy wyświetlacz działa z wielokrotnością częstotliwości odświeżania aplikacji, aplikacja powinna określać sygnatury czasowe wyświetlania dla każdej klatki, aby uniknąć niepotrzebnego zacinania. W przypadku gier biblioteka Android Frame Pacing pomaga prawidłowo ustawiać sygnatury czasowe wyświetlania klatek.
setFrameRate() a preferredDisplayModeId
WindowManager.LayoutParams.preferredDisplayModeId
to kolejny sposób, w jaki aplikacje mogą wskazać platformie swoją częstotliwość klatek. Niektóre aplikacje chcą zmienić tylko częstotliwość odświeżania ekranu, a nie inne ustawienia trybu wyświetlania, takie jak rozdzielczość. Ogólnie zalecamy używanie wartości setFrameRate()
zamiast preferredDisplayModeId
. Funkcja setFrameRate()
jest łatwiejsza w użyciu, ponieważ aplikacja nie musi przeszukiwać listy trybów wyświetlania w celu znalezienia trybu o określonej liczbie klatek na sekundę.
setFrameRate()
daje platformie więcej możliwości wyboru zgodnej częstotliwości klatek w sytuacjach, gdy występuje wiele powierzchni z różnymi częstotliwościami klatek. Rozważmy na przykład scenariusz, w którym 2 aplikacje działają w trybie podzielonego ekranu na Pixelu 4. Jedna z nich odtwarza film w standardzie 24 Hz, a druga wyświetla użytkownikowi listę, którą można przewijać. Pixel 4 obsługuje 2 częstotliwości odświeżania: 60 Hz i 90 Hz. Za pomocą interfejsu API preferredDisplayModeId
platforma wideo jest zmuszona do wybrania częstotliwości 60 Hz lub 90 Hz. Wywołanie setFrameRate()
z częstotliwością 24 Hz powoduje, że platforma otrzymuje więcej informacji o częstotliwości wyświetlania obrazu w źródłowym filmie, co pozwala jej wybrać częstotliwość odświeżania wyświetlacza 90 Hz, która jest lepsza niż 60 Hz w tym scenariuszu.
Są jednak sytuacje, w których należy użyć preferredDisplayModeId
zamiast setFrameRate()
, na przykład:
- Jeśli aplikacja chce zmienić rozdzielczość lub inne ustawienia trybu wyświetlania, użyj
preferredDisplayModeId
. - Platforma przełączy tryby wyświetlania tylko w odpowiedzi na wywołanie funkcji
setFrameRate()
, jeśli przełączenie trybu jest lekkie i nie powinno być zauważalne dla użytkownika. Jeśli aplikacja preferuje zmianę częstotliwości odświeżania wyświetlacza, nawet jeśli wymaga to przełączenia do trybu ciężkiego (np. na urządzeniu z Androidem TV), użyjpreferredDisplayModeId
. - Aplikacje, które nie obsługują wyświetlacza działającego z wielokrotnością częstotliwości odświeżania, co wymaga ustawiania sygnatur czasowych dla każdego klatki, powinny używać
preferredDisplayModeId
.
setFrameRate() a preferredRefreshRate
WindowManager.LayoutParams#preferredRefreshRate
ustawia preferowaną częstotliwość klatek w oknie aplikacji. Częstotliwość ta jest stosowana do wszystkich powierzchni w oknie. Aplikacja powinna określić preferowaną częstotliwość klatek niezależnie od obsługiwanych częstotliwości odświeżania urządzenia (podobnie jak w przypadku setFrameRate()
), aby podać planującemu lepszy wskaźnik docelowej częstotliwości klatek.
Wartość preferredRefreshRate
jest ignorowana w przypadku powierzchni, które korzystają z setFrameRate()
. W ogóle, jeśli to możliwe, używaj setFrameRate()
.
preferredRefreshRate vs preferredDisplayModeId
Jeśli aplikacje chcą zmienić tylko preferowaną częstotliwość odświeżania, zaleca się użycie wartości preferredRefreshRate
zamiast preferredDisplayModeId
.
Unikaj zbyt częstego wywoływania funkcji setFrameRate().
Mimo że wywołanie setFrameRate()
nie jest bardzo kosztowne pod względem wydajności, aplikacje powinny unikać wywoływania setFrameRate()
w każdej klatce lub wielokrotnie na sekundę. Wywołania funkcji setFrameRate()
prawdopodobnie spowodują zmianę częstotliwości odświeżania wyświetlacza, co może spowodować utratę klatek podczas przejścia.
Należy wcześniej określić prawidłową liczbę klatek na sekundę i raz wywołać funkcję setFrameRate()
.
Używanie w przypadku gier lub innych aplikacji nieobejmujących filmów
Chociaż filmy to główne zastosowanie interfejsu API setFrameRate()
, 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ć czas rozgrywki), 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. Pozwoli to uniknąć zacięć, które wystąpiłyby, gdyby gra działała z częstotliwością 60 Hz, a wyświetlacz z częstotliwością 90 Hz.
Użycie parametru FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
jest przeznaczona tylko do aplikacji wideo. W przypadku innych zastosowań niż wideo użyj FRAME_RATE_COMPATIBILITY_DEFAULT
.
Wybór strategii zmiany liczby klatek
- Zalecamy, aby aplikacje wyświetlające długie filmy, takie jak filmy fabularne, wywoływały funkcję
setFrameRate(
fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)
, gdzie fps to liczba klatek na sekundę filmu. - Zdecydowanie odradzamy wywoływanie przez aplikacje funkcji
setFrameRate()
z parametramiCHANGE_FRAME_RATE_ALWAYS
, jeśli odtwarzanie filmu ma trwać kilka minut lub krócej.
Przykładowa integracja z aplikacjami do odtwarzania filmów
Aby zintegrować przełącznik częstotliwości odświeżania w aplikacjach do odtwarzania filmów, wykonaj te czynności:
- Zdecyduj, co
changeFrameRateStrategy
:- Jeśli odtwarzasz długi film, np. film fabularny, użyj
MATCH_CONTENT_FRAMERATE_ALWAYS
. - Jeśli odtwarzasz krótki film, np. zwiastun filmu, użyj
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
.
- Jeśli odtwarzasz długi film, np. film fabularny, użyj
- Jeśli
changeFrameRateStrategy
toCHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
, przejdź do kroku 4. - Aby wykryć, czy nastąpi zmiana częstotliwości odświeżania, która nie będzie płynna, sprawdź, czy są spełnione oba te warunki:
- Nie można płynnie przełączyć się z bieżącej częstotliwości odświeżania (nazwijmy ją C) na częstotliwość klatek filmu (nazwijmy ją V). Tak się stanie, jeśli C i V są różne, a
Display.getMode().getAlternativeRefreshRates
nie zawiera wielokrotności V. - Użytkownik zgodził się na zmiany częstotliwości odświeżania, które nie są płynne. Możesz to sprawdzić, sprawdzając, czy
DisplayManager.getMatchContentFrameRateUserPreference
zwracaMATCH_CONTENT_FRAMERATE_ALWAYS
- Nie można płynnie przełączyć się z bieżącej częstotliwości odświeżania (nazwijmy ją C) na częstotliwość klatek filmu (nazwijmy ją V). Tak się stanie, jeśli C i V są różne, a
- Aby przejście było płynne, wykonaj te czynności:
- wywołać funkcję
setFrameRate
i przekazać jej argumentfps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
ichangeFrameRateStrategy
, gdziefps
to liczba klatek na sekundę w filmie. - Rozpocznij odtwarzanie filmu
- wywołać funkcję
- Jeśli ma nastąpić zmiana trybu bez przełączania, wykonaj te czynności:
- Wyświetl interfejs użytkownika, aby go powiadomić. Pamiętaj, że na etapie 5d zalecamy wdrożenie sposobu, który pozwoli użytkownikowi zamknąć ten interfejs i pominąć dodatkowy czas oczekiwania. Dzieje się tak, ponieważ zalecana wartość opóźnienia jest większa niż wymagana w przypadku wyświetlaczy o krótszym czasie przełączania.
- wywołać funkcję
setFrameRate
, a potem przekazać jej argumentyfps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
iCHANGE_FRAME_RATE_ALWAYS
, gdziefps
to liczba klatek na sekundę w filmie. - Poczekaj na oddzwonienie z numeru
onDisplayChanged
. - Zaczekaj 2 sekundy, aż przełączenie trybu zostanie zakończone.
- Rozpocznij odtwarzanie filmu
Pseudokod, który tylko obsługuje 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, jak opisano powyżej:
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();
}