Liczba klatek

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:

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

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 funkcji preferredDisplayModeId.
  • 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:

  1. Określ wartość changeFrameRateStrategy:
    1. Jeśli odtwarzasz długi film, np. film, użyj wartości MATCH_CONTENT_FRAMERATE_ALWAYS
    2. Jeśli odtwarzasz krótki film, np. zwiastun filmu, użyj wartości CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS.
  2. Jeśli wartość changeFrameRateStrategy to CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS , przejdź do kroku 4.
  3. Sprawdź, czy ma nastąpić niepłynne przełączenie częstotliwości odświeżania, sprawdzając, czy oba te warunki są spełnione:
    1. 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().getAlternativeRefreshRates nie zawiera wielokrotności wartości V.
    2. Użytkownik wyraził zgodę na niepłynne zmiany częstotliwości odświeżania. Możesz to wykryć sprawdzając, czy DisplayManager.getMatchContentFrameRateUserPreference zwraca MATCH_CONTENT_FRAMERATE_ALWAYS
  4. Jeśli przełączenie będzie 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 filmu.
    2. Rozpocznij odtwarzanie filmu.
  5. Jeśli ma nastąpić niepłynna zmiana trybu, wykonaj te czynności:
    1. 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ą.
    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 filmu.
    3. Poczekaj na wywołanie zwrotne onDisplayChanged.
    4. Poczekaj 2 sekundy na zakończenie przełączania trybu.
    5. 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();
}