Liczba klatek

Interfejs API częstotliwości odświeżania umożliwia aplikacjom informowanie platformy Android o zamierzonej częstotliwości odświeżania. 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 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 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 liczbą klatek na sekundę, która jest preferowana przez Twoją aplikację. 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, a nie zaokrągloną 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 pulldown 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 pozostanie widoczna na ekranie przez pewien czas. Typowe sytuacje to zakończenie odtwarzania filmu lub wstrzymanie go 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 Android nie przełącza trybów po wywołaniu interfejsu Surface.setFrameRate(), aby uniknąć takich przerw w wyświetlaniu.

Niektórzy użytkownicy wolą, aby na początku i na końcu dłuższych filmów pojawiały się przerywniki wizualne. Dzięki temu częstotliwość odświeżania wyświetlacza jest dopasowana do liczby klatek na sekundę w filmie, co pozwala uniknąć artefaktów związanych z konwersją liczby klatek na sekundę, takich jak drgania spowodowane konwersją 3:2 podczas odtwarzania filmów.

Z tego powodu przełączanie częstotliwości odświeżania bez efektu migotania można włączyć, jeśli użytkownik i aplikacje wyrażą na to zgodę:

W przypadku długich filmów, np. filmów fabularnych, zalecamy 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 częstotliwościach odświeżania, wywołuj funkcję setFrameRate() z odpowiednią częstotliwością odświeżania 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() na własnych powierzchniach.

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 (ustawiając ograniczenie 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 aplikacji, nawet jeśli w normalnych okolicznościach urządzenie to robi.

Aplikacja decyduje, jak reagować, gdy częstotliwość odświeżania wyświetlacza nie odpowiada liczbie klatek na sekundę w aplikacji. W przypadku wideo liczba klatek jest stała i odpowiada liczbie klatek filmu źródłowego. Aby wyświetlić treści wideo, konieczne będzie zastosowanie techniki pulldown. Gra może spróbować działać z częstotliwością odświeżania wyświetlacza zamiast utrzymywać 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 prezentacji jest nieco skomplikowane. Więcej informacji o unikaniu drgań znajdziesz w naszym przewodniku po synchronizacji 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(). 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 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ą klatek będącą wielokrotnością częstotliwości 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 występuje wiele platform działających 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 Hz, a druga wyświetla użytkownikowi listę z możliwością przewijania. Pixel 4 obsługuje 2 częstotliwości odświeżania wyświetlacza: 60 Hz i 90 Hz. Za pomocą preferredDisplayModeId interfejsu API można wymusić wybór częstotliwości 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 częstotliwości klatek filmu źródłowego. Dzięki temu platforma może wybrać częstotliwość odświeżania wyświetlacza 90 Hz, która w tym scenariuszu jest lepsza 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łączanie 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ę, co wymaga ustawienia sygnatur czasowych prezentacji dla każdej klatki, 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 w przypadku 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 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łanie funkcji setFrameRate() prawdopodobnie spowoduje 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.

Wybór 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 sytuacjach, 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 tagu CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS.
  2. Jeśli changeFrameRateStrategy ma wartość CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS , przejdź do kroku 4.
  3. Wykrywaj, 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 płynnego przejścia. Możesz to sprawdzić, sprawdzając, czy DisplayManager.getMatchContentFrameRateUserPreference zwraca MATCH_CONTENT_FRAMERATE_ALWAYS.
  4. Jeśli przejście ma być płynne, wykonaj te czynności:
    1. Wywołaj funkcję setFrameRate i przekaż 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 UX, 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 i przekaż 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 oddzwonienie 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();
}