Liczba klatek

Interfejs API częstotliwości klatek pozwala aplikacjom informować platformę Androida o zamierzonej częstotliwości klatek. Jest on dostępny w aplikacjach kierowanych na Androida 11 (poziom API 30) lub nowszego. Zazwyczaj większość urządzeń obsługiwała tylko jedną częstotliwość odświeżania wyświetlacza, zwykle 60 Hz, ale to 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łynne przełączanie częstotliwości odświeżania, a inne tylko na chwilę pojawiają się czarny ekran, zwykle przez sekundę.

Głównym celem interfejsu API jest umożliwienie aplikacjom lepszego korzystania ze wszystkich obsługiwanych częstotliwości odświeżania ekranu. 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. Nie jest już potrzebne przeciąganie w proporcjach 3:2, które jest wymagane do odtwarzania tego samego filmu na wyświetlaczu 60 Hz. Dzięki temu użytkownicy stają się wygodniejsi.

Podstawowe użycie

Android udostępnia kilka sposobów dostępu do platform i sterowania nimi, dlatego istnieje kilka wersji interfejsu API setFrameRate(). Każda wersja interfejsu API przyjmuje te same parametry i działa tak samo jak inne:

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 jeśli urządzenie obsługuje tylko częstotliwość 60 Hz, wywołaj funkcję setFrameRate() z preferowaną przez aplikację liczbą klatek. Urządzenia, które nie mają lepszego dopasowania do częstotliwości klatek aplikacji, pozostaną przy bieżącej częstotliwości odświeżania wyświetlacza.

Aby sprawdzić, czy wywołanie metody setFrameRate() spowoduje zmianę częstotliwości odświeżania wyświetlacza, zarejestruj się w celu otrzymywania powiadomień o zmianie sposobu wyświetlania, wywołując 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 zamiast zaokrąglenia do 30.

W przypadku aplikacji wideo parametr zgodności przekazany do funkcji setFrameRate() powinien mieć wartość Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE. Będzie to dla platformy Androida dodatkową wskazówkę, że aplikacja będzie używać menu rozwijanego, aby dostosować się do niezgodnej częstotliwości odświeżania wyświetlacza (co spowoduje dyskomfort).

W niektórych przypadkach powierzchnia wideo przestanie przesyłać klatki, ale przez pewien czas będzie nadal widoczna na ekranie. Typowe scenariusze to zakończenie odtwarzania filmu lub wstrzymanie odtwarzania przez użytkownika. W takich przypadkach wywołaj funkcję setFrameRate() z parametrem liczby klatek ustawionym na 0, aby przywrócić domyślną wartość liczby klatek na powierzchni. 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ływania 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 podczas odtwarzania filmów w formacie 3:2.

Dlatego niepłynne przełączniki częstotliwości odświeżania mogą być włączone, jeśli wyrażą na to zgodę zarówno użytkownik, jak i aplikacje:

W przypadku długotrwałych filmów, takich jak filmy, zalecamy zawsze używać CHANGE_FRAME_RATE_ALWAYS. Dzieje się tak, ponieważ korzyści z dopasowania liczby klatek na sekundę filmu przeważają 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(), podając odpowiednią częstotliwość klatek dla każdej z nich. Nawet jeśli na urządzeniu działa wiele aplikacji jednocześnie, korzystając z podzielonego ekranu lub trybu obrazu w obrazie, każda z nich może bezpiecznie wywołać setFrameRate() we własnych platformach.

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 ekran o wyższym priorytecie może mieć inne ustawienie liczby klatek lub urządzenie może być w trybie oszczędzania baterii (przez ustawienie ograniczenia częstotliwości odświeżania wyświetlacza, by oszczędzać baterię). 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 klatek aplikacji, to od niej zależy, jak zareaguje. W przypadku wideo liczba klatek jest stała, więc do wyświetlenia treści wideo wymagane jest menu rozwijane. 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. Powinno ono pozostawać ustawione na preferowaną liczbę klatek przez aplikację niezależnie od tego, jak aplikacja obsługuje przypadki, gdy platforma nie dostosowuje się do żądania aplikacji. W ten sposób, jeśli warunki na urządzeniu ulegną zmianie i umożliwią użycie dodatkowych częstotliwości odświeżania ekranu, platforma będzie mieć prawidłowe informacje, aby przełączyć się na preferowaną częstotliwość wyświetlania 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:

Użycie tych sygnatur czasowych powstrzymuje platformę przed zbyt wczesnym wyświetlaniem ramki aplikacji, co mogłoby powodować niepotrzebne przestraszenie. 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 wyświetlania klatek aplikacji, aplikacja powinna określać sygnatury czasowe wyświetlania dla każdej klatki, aby uniknąć niepotrzebnego zacinania. W przypadku gier biblioteka Android Frame Pacing może pomóc w prawidłowym ustawianiu sygnatur czasowych prezentacji klatek.

setFrameRate() a preferredDisplayModeId

WindowManager.LayoutParams.preferredDisplayModeId to kolejny sposób, w jaki aplikacje mogą określać liczbę klatek na platformie. Niektóre aplikacje chcą zmienić tylko częstotliwość odświeżania ekranu, a nie inne ustawienia trybu wyświetlania, takie jak rozdzielczość. Zwykle zamiast preferredDisplayModeId używaj setFrameRate(). 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.

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łując funkcję setFrameRate() z częstotliwością 24 Hz, powierzchnia wideo dostarcza platformie więcej informacji o liczbie klatek filmu źródłowego. Dzięki temu platforma może wybrać częstotliwość odświeżania obrazu 90 Hz, która w tym scenariuszu jest lepsza niż 60 Hz.

W niektórych sytuacjach zamiast setFrameRate() należy jednak używać właściwości preferredDisplayModeId. Na przykład:

  • Jeśli aplikacja chce zmienić rozdzielczość lub inne ustawienia trybu wyświetlania, użyj preferredDisplayModeId.
  • Platforma przełącza tryby wyświetlania w odpowiedzi na wywołanie funkcji setFrameRate() tylko wtedy, gdy przełącznik trybu jest niewielki i mało prawdopodobne, że będzie widoczny 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żyj preferredDisplayModeId.
  • Aplikacje, które nie potrafią obsłużyć wyświetlacza działającego z wieloma szybkością klatek, co wymaga ustawienia sygnatur czasowych prezentacji w każdej klatce, powinny używać właściwości preferredDisplayModeId.

setFrameRate() a PreferredRefreshRate

WindowManager.LayoutParams#preferredRefreshRate ustawia preferowaną liczbę klatek na sekundę w oknie aplikacji. Ta liczba 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 miarę możliwości należy używać właściwości setFrameRate().

preferowany częstotliwość odświeżania a 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 setFrameRate() prawdopodobnie spowodują zmianę częstotliwości odświeżania wyświetlacza, co może spowodować spadek liczby 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órej nie zamierzasz używać więcej niż 60 Hz (w celu zmniejszenia zużycia energii i zwiększenia dłuższych sesji gry), może wywołać Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT). Dzięki temu 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.

Wykorzystanie FRAME_RATE_COMPATIBILITY_FIXED_SOURCE

Aplikacja FRAME_RATE_COMPATIBILITY_FIXED_SOURCE jest przeznaczona tylko dla aplikacji wideo. W przypadku innych niż wideo używaj 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 używanie aplikacji wywołujących setFrameRate() z użyciem CHANGE_FRAME_RATE_ALWAYS, jeśli spodziewasz się, że odtwarzanie filmu może potrwać kilka minut.

Przykładowa integracja z aplikacjami do odtwarzania filmów

Aby zintegrować przełączniki częstotliwości odświeżania w aplikacjach do odtwarzania filmów, wykonaj te czynności:

  1. Zdecyduj, co changeFrameRateStrategy:
    1. Jeśli odtwarzasz długotrwały film, na przykład film, użyj funkcji MATCH_CONTENT_FRAMERATE_ALWAYS
    2. Jeśli odtwarzasz krótki film, na przykład zwiastun z przenoszeniem, użyj aplikacji CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
  2. Jeśli changeFrameRateStrategy to CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, przejdź do kroku 4.
  3. Aby wykryć, czy nastąpiła zmiana częstotliwości odświeżania, która nie jest płynna, sprawdź, czy są spełnione oba te warunki:
    1. 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.
    2. 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 zwraca MATCH_CONTENT_FRAMERATE_ALWAYS
  4. Aby przejście było płynne, wykonaj te czynności:
    1. Wywołaj funkcję setFrameRate i przekaż jej argumenty fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCEchangeFrameRateStrategy, gdzie fps to liczba klatek na sekundę w filmie.
    2. Rozpocznij odtwarzanie filmu
  5. Jeśli ma nastąpić zmiana trybu bez przełączania, wykonaj te czynności:
    1. Wyświetl interfejs użytkownika, aby go powiadomić. Zalecamy zaimplementowanie sposobu, który umożliwi użytkownikowi zamknięcie tego interfejsu i pominięcie dodatkowego opóźnienia opisanego w kroku 5.d. Dzieje się tak, ponieważ zalecana wartość opóźnienia jest większa niż wymagana w przypadku wyświetlaczy o krótszym czasie przełączania.
    2. wywołać funkcję setFrameRate, a potem przekazać jej argumenty fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE i CHANGE_FRAME_RATE_ALWAYS, gdzie fps to liczba klatek na sekundę w filmie.
    3. Poczekaj na oddzwonienie z numeru onDisplayChanged.
    4. Poczekaj 2 sekundy na zakończenie przełączania trybu.
    5. 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();
}