Liczba klatek

Interfejs Frame Rate API umożliwia aplikacjom informowanie platformy Android o zamierzonej liczbie klatek na sekundę. Jest on 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 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łynną zmianę 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 wyświetlacza. Na przykład aplikacja odtwarzająca film w 24 Hz, która wywołuje setFrameRate(), może spowodować zmianę częstotliwości 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 24 Hz bez drgań i bez konieczności stosowania techniki 3:2 pulldown, która byłaby wymagana do odtwarzania tego samego filmu na wyświetlaczu 60 Hz. Zapewnia to większą wygodę użytkownikom.

Podstawowe użycie

Android udostępnia kilka sposobów uzyskiwania dostępu do ekranów 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 pozostałe:

Aplikacja nie musi uwzględniać rzeczywistych obsługiwanych częstotliwości odświeżania wyświetlacza, które można uzyskać, wywołując Display.getSupportedModes(), aby bezpiecznie wywołać setFrameRate(). Na przykład nawet jeśli urządzenie obsługuje tylko 60 Hz, wywołaj funkcję setFrameRate() z preferowaną przez aplikację liczbą klatek na sekundę. Urządzenia, które nie mają lepszego dopasowania do liczby klatek na sekundę aplikacji, pozostaną przy bieżącej częstotliwości odświeżania wyświetlacza.

Aby sprawdzić, czy połączenie z numerem setFrameRate() powoduje zmianę częstotliwości odświeżania wyświetlacza, zarejestruj się, aby otrzymywać powiadomienia o zmianach wyświetlacza, dzwoniąc pod numer DisplayManager.registerDisplayListener() lub AChoreographer_registerRefreshRateCallback().

Podczas wywoływania funkcji setFrameRate() najlepiej jest przekazywać dokładną liczbę klatek na sekundę, a nie zaokrąglać jej do liczby całkowitej. Na przykład podczas renderowania filmu nagranego w 29, 97 Hz przekaż wartość 29, 97 zamiast zaokrąglać ją do 30.

W przypadku aplikacji wideo parametr zgodności przekazywany do funkcji setFrameRate() powinien mieć wartość Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, aby przekazać platformie Android dodatkową wskazówkę, że aplikacja będzie używać funkcji pull-down do dostosowywania się do niezgodnej częstotliwości odświeżania wyświetlacza (co spowoduje drgania).

W niektórych przypadkach powierzchnia wideo przestanie przesyłać klatki, ale przez pewien czas będzie nadal widoczna na ekranie. Typowe sytuacje 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ść ustawienia liczby klatek powierzchni. Usuwanie ustawienia liczby klatek na sekundę w ten sposób nie jest konieczne podczas niszczenia powierzchni ani gdy jest ona ukryta, ponieważ użytkownik przełącza się na inną aplikację. Usuwaj ustawienie liczby klatek na sekundę 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ć przerwy w wyświetlaniu, np. czarny ekran przez sekundę lub dwie. Zwykle występuje to w przypadku dekoderów, paneli telewizyjnych i podobnych urządzeń. Domyślnie platforma Androida nie przełącza trybów po wywołaniu interfejsu Surface.setFrameRate(), aby uniknąć takich przerw w wyświetlaniu.

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 dopasowana do liczby klatek na sekundę filmu, co pozwoli uniknąć artefaktów związanych z konwersją liczby klatek na sekundę, takich jak drgania spowodowane metodą 3:2 pulldown podczas odtwarzania filmów.

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ę:

W przypadku długich filmów, np. filmów kinowych, zalecamy zawsze używanie CHANGE_FRAME_RATE_ALWAYS. Dzieje się tak, ponieważ 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

Postępuj zgodnie z tymi zaleceniami w typowych scenariuszach.

Różne powierzchnie

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 o różnych liczbach klatek, wywołuj funkcję setFrameRate() z prawidłową liczbą klatek dla każdej powierzchni. Nawet jeśli na urządzeniu działa jednocześnie kilka aplikacji w trybie podzielonego ekranu lub obrazu w obrazie, każda z nich może bezpiecznie wywoływać funkcję setFrameRate() dla własnych powierzchni.

Platforma nie zmienia się na liczbę klatek aplikacji

Nawet jeśli urządzenie obsługuje liczbę klatek na sekundę określoną przez aplikację w wywołaniu funkcji setFrameRate(), w niektórych przypadkach 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 liczby klatek na sekundę lub urządzenie może być w trybie oszczędzania baterii (ustawienie ograniczenia częstotliwości odświeżania wyświetlacza w celu oszczędzania baterii). Aplikacja musi działać prawidłowo, nawet jeśli urządzenie nie przełącza częstotliwości odświeżania ekranu na ustawienie liczby klatek na sekundę w aplikacji, nawet jeśli w normalnych okolicznościach to robi.

Aplikacja decyduje, jak ma reagować, gdy częstotliwość odświeżania wyświetlacza nie odpowiada liczbie klatek na sekundę w aplikacji. W przypadku filmów liczba klatek jest stała i odpowiada liczbie klatek filmu źródłowego. Aby wyświetlić treść filmu, konieczne będzie zastosowanie techniki pulldown. Gra może zamiast tego spróbować działać z częstotliwością odświeżania wyświetlacza, a nie z preferowaną liczbą klatek na sekundę. Aplikacja nie powinna zmieniać wartości przekazywanej do setFrameRate() w zależności od działania platformy. Powinna ona pozostać ustawiona na preferowaną liczbę klatek na sekundę aplikacji, niezależnie od tego, jak aplikacja obsługuje przypadki, w których platforma nie dostosowuje się do jej żądania. 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 wyświetlacza, platforma będzie miała prawidłowe informacje, aby przełączyć się na preferowaną przez aplikację liczbę klatek na sekundę.

W przypadku, gdy aplikacja nie może działać z częstotliwością odświeżania wyświetlacza, powinna określać sygnatury czasowe prezentacji dla każdej klatki, korzystając z jednego z mechanizmów platformy do ustawiania sygnatur czasowych prezentacji:

Użycie tych sygnatur czasowych zapobiega zbyt wczesnemu wyświetlaniu przez platformę klatki aplikacji, co mogłoby powodować niepotrzebne drgania. Prawidłowe użycie sygnatur czasowych klatek jest nieco skomplikowane. Więcej informacji o unikaniu drgań znajdziesz w naszym przewodniku po tempie klatek. Możesz też skorzystać z biblioteki Android Frame Pacing.

W niektórych przypadkach platforma może przełączyć się na wielokrotność liczby klatek na sekundę określonej przez aplikację w setFrameRate(). Na przykład aplikacja może wywołać setFrameRate() z częstotliwością 60 Hz, a urządzenie może przełączyć wyświetlacz na 120 Hz. Może się tak zdarzyć, jeśli inna aplikacja ma powierzchnię z ustawieniem liczby klatek na sekundę wynoszącym 24 Hz. W takim przypadku wyświetlanie obrazu z częstotliwością 120 Hz umożliwi wyświetlanie obrazu z częstotliwością 60 Hz i 24 Hz bez konieczności stosowania pulldownu.

Jeśli wyświetlacz działa z częstotliwością odświeżania będącą wielokrotnością liczby klatek aplikacji, aplikacja powinna określać sygnatury czasowe prezentacji dla każdej klatki, aby uniknąć niepotrzebnego drgania. 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ą informować platformę o swojej liczbie klatek na sekundę. Niektóre aplikacje chcą tylko zmienić częstotliwość odświeżania wyświetlacza, a nie inne ustawienia trybu wyświetlania, takie jak rozdzielczość. 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 o określonej liczbie klatek na sekundę.

setFrameRate() daje platformie więcej możliwości wyboru zgodnej liczby klatek na sekundę w sytuacjach, w których na różnych platformach wyświetlanie odbywa się z różną liczbą klatek na sekundę. Rozważmy na przykład sytuację, w której na Pixelu 4 w trybie podzielonego ekranu działają 2 aplikacje: jedna odtwarza film w 24 kl./s, a druga wyświetla użytkownikowi listę z możliwością przewijania. Pixel 4 obsługuje dwie częstotliwości odświeżania wyświetlacza: 60 Hz i 90 Hz. Korzystając z interfejsu preferredDisplayModeId API, powierzchnia wideo jest zmuszona do wyboru 60 Hz lub 90 Hz. Wywołanie funkcji setFrameRate() z częstotliwością 24 Hz sprawia, że powierzchnia wideo przekazuje platformie więcej informacji o liczbie klatek źródłowego filmu. Dzięki temu platforma może wybrać częstotliwość odświeżania wyświetlacza na poziomie 90 Hz, co w tym przypadku jest lepsze niż 60 Hz.

Są jednak sytuacje, w których zamiast setFrameRate() należy użyć preferredDisplayModeId, np.:

  • Jeśli aplikacja chce zmienić rozdzielczość lub inne ustawienia trybu wyświetlania, użyj preferredDisplayModeId.
  • Platforma będzie przełączać tryby wyświetlania tylko w odpowiedzi na wywołanie funkcji setFrameRate(), jeśli przełączenie trybu jest proste i prawdopodobnie niezauważalne dla użytkownika. Jeśli aplikacja woli przełączyć częstotliwość odświeżania wyświetlacza, nawet jeśli wymaga to przełączenia trybu (np. na urządzeniu z Androidem TV), użyj preferredDisplayModeId.
  • Aplikacje, które nie obsługują wyświetlania z częstotliwością będącą wielokrotnością liczby klatek na sekundę aplikacji, co wymaga ustawienia sygnatur czasowych prezentacji w każdej klatce, powinny używać preferredDisplayModeId.

setFrameRate() a preferredRefreshRate

WindowManager.LayoutParams#preferredRefreshRate ustawia preferowaną liczbę klatek na sekundę w oknie aplikacji, która obowiązuje na wszystkich powierzchniach w tym oknie. Aplikacja powinna określać preferowaną liczbę klatek na sekundę niezależnie od obsługiwanych częstotliwości odświeżania urządzenia, podobnie jak setFrameRate(), aby dać harmonogramowi lepsze wskazówki dotyczące zamierzonej liczby klatek na sekundę.

W przypadku platform korzystających z setFrameRate() parametr preferredRefreshRate jest ignorowany. W miarę możliwości używaj setFrameRate().

preferredRefreshRate vs preferredDisplayModeId

Jeśli aplikacje chcą tylko zmienić preferowaną częstotliwość odświeżania, zalecamy użycie parametru preferredRefreshRate zamiast preferredDisplayModeId.

Unikanie zbyt częstego wywoływania funkcji setFrameRate()

Chociaż wywołanie setFrameRate() nie jest zbyt kosztowne pod względem wydajności, aplikacje powinny unikać wywoływania setFrameRate() w każdej klatce lub wiele razy na sekundę. Wywołania funkcji setFrameRate() prawdopodobnie spowodują zmianę częstotliwości odświeżania wyświetlacza, co może skutkować utratą klatki podczas przejścia. Prawidłową liczbę klatek na sekundę należy określić z wyprzedzeniem i wywołać funkcję setFrameRate() tylko raz.

Użycie w przypadku gier lub innych aplikacji niebędących aplikacjami wideo

Chociaż interfejs setFrameRate() API jest przeznaczony głównie do filmów, można go używać w innych aplikacjach. Na przykład gra, która nie ma działać z częstotliwością wyższą niż 60 Hz (aby zmniejszyć zużycie energii i wydłużyć sesje 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 w 90 Hz, będzie działać w 60 Hz, gdy gra jest aktywna. Pozwoli to uniknąć drgań, które wystąpiłyby, gdyby gra działała w 60 Hz, a wyświetlacz w 90 Hz.

Korzystanie z FRAME_RATE_COMPATIBILITY_FIXED_SOURCE

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE jest przeznaczony tylko dla aplikacji wideo. W przypadku wykorzystania niezwiązanego z wideo użyj FRAME_RATE_COMPATIBILITY_DEFAULT.

Wybieranie strategii zmiany liczby klatek

  • Zdecydowanie zalecamy, aby aplikacje wyświetlające długie filmy, takie jak filmy kinowe, 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 funkcji setFrameRate() z parametrem CHANGE_FRAME_RATE_ALWAYS w przypadku, gdy odtwarzanie filmu ma trwać kilka minut lub krócej.

Przykładowa integracja w przypadku aplikacji do odtwarzania filmów

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

  1. Określ changeFrameRateStrategy:
    1. Jeśli odtwarzasz długi film, np. pełnometrażowy, użyj MATCH_CONTENT_FRAMERATE_ALWAYS.
    2. Jeśli odtwarzasz krótki film, np. zwiastun filmu, użyj CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS.
  2. Jeśli changeFrameRateStrategy ma wartość CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS , przejdź do kroku 4.
  3. Wykryj, czy ma nastąpić przełączenie częstotliwości odświeżania bez płynnego przejścia, sprawdzając, czy oba te warunki są spełnione:
    1. Płynne przełączanie trybów nie jest możliwe z bieżącej częstotliwości odświeżania (nazwijmy ją C) na liczbę klatek na sekundę filmu (nazwijmy ją V). Tak będzie, jeśli C i V są różne, a Display.getMode().getAlternativeRefreshRates nie zawiera wielokrotności V.
    2. Użytkownik wyraził zgodę na zmiany częstotliwości odświeżania bez efektu przejścia. Możesz to sprawdzić, weryfikując, czy funkcja DisplayManager.getMatchContentFrameRateUserPreference zwraca wartość MATCH_CONTENT_FRAMERATE_ALWAYS.
  4. Jeśli przełączanie ma być płynne, wykonaj te czynności:
    1. Wywołaj funkcję setFrameRate, przekazując jej wartości fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE i changeFrameRateStrategy, gdzie fps to liczba klatek na sekundę filmu.
    2. Rozpocznij odtwarzanie filmu
  5. Jeśli ma nastąpić zmiana trybu bez płynnego przejścia, wykonaj te czynności:
    1. Wyświetl interfejs, aby powiadomić użytkownika. Pamiętaj, że zalecamy wdrożenie sposobu, w jaki użytkownik może zamknąć ten interfejs i pominąć dodatkowe opóźnienie w kroku 5.d. Dzieje się tak, ponieważ zalecane przez nas opóźnienie jest większe niż to konieczne w przypadku wyświetlaczy, które charakteryzują się krótszym czasem przełączania.
    2. Wywołaj funkcję setFrameRate, przekazując jej wartości fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE i CHANGE_FRAME_RATE_ALWAYS, gdzie fps to liczba klatek na sekundę filmu.
    3. Poczekaj na wywołanie zwrotne onDisplayChanged.
    4. Poczekaj 2 sekundy, aż przełączanie trybu się zakończy.
    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 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();
}