Liczba klatek

Interfejs API częstotliwości klatek pozwala aplikacjom informować platformę Androida o chcianej częstotliwości klatek. Jest dostępny w aplikacjach kierowanych na Androida 11 (poziom interfejsu API 30) lub nowszego. Tradycyjnie większość urządzeń obsługiwała tylko jedną częstotliwość odświeżania ekranu, zazwyczaj 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 wyświetlają na krótko 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 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 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. W ten sposób zwiększysz wygodę użytkowników.

Podstawowe użycie

Android udostępnia kilka sposobów dostępu do interfejsów i ich kontrolowania, dlatego istnieje kilka wersji interfejsu setFrameRate() API. 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(). Nawet jeśli urządzenie obsługuje tylko 60 Hz, wywołaj funkcję setFrameRate() z liczbą klatek, której preferuje Twoja aplikacja. 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 funkcji setFrameRate() powoduje zmianę częstotliwości odświeżania wyświetlacza, zarejestruj się, aby otrzymywać powiadomienia o zmianach wyświetlacza. W tym celu wybierz 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 należy podać wartość 29,97, a nie zaokrągloną wartość 30.

W przypadku aplikacji wideo parametr compatibility 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 drżenie obrazu).

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 ustawienia częstotliwości klatek 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 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. 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 przewijania w dół w przypadku odtwarzania filmu w formacie 3:2.

Z tego powodu przełączanie się na inną częstotliwość odświeżania bez płynnego przejścia może być włączone, jeśli użytkownik i aplikacje wyrażą na to zgodę:

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 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() 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 wyświetlania 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 klatek aplikacji, to od aplikacji zależy, jak zareaguje. 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. Należy ustawić go na preferowaną przez aplikację liczbę klatek na sekundę, 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 szybkości wyświetlania klatek, 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ć funkcję 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.

Jeśli 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ąć zbędnego trzęsienia. W przypadku gier biblioteka Android Frame Pacing ułatwia prawidłowe ustawianie sygnatur czasowych prezentacji 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ść. 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 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 24 Hz powoduje, że platforma dostaje więcej informacji o częstotliwości wyświetlania klatek filmu źródłowego, co pozwala jej wybrać 90 Hz jako częstotliwość odświeżania wyświetlacza, co jest lepsze 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żyj preferredDisplayModeId.
  • Aplikacje, które nie obsługują wyświetlacza działającego z wielokrotnością częstotliwości odświeżania, co wymaga ustawienia sygnatur czasowych dla każdego klatki, powinny używać 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ć planiście lepszy wskaźnik docelowej częstotliwości klatek.

W przypadku powierzchni, które używają tagu setFrameRate(), parametr preferredRefreshRate jest ignorowany. 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ć 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 są głównym zastosowaniem 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). Dzięki temu urządzenie, które działa domyślnie 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 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 argumentem CHANGE_FRAME_RATE_ALWAYS, jeśli odtwarzanie filmu ma trwać kilka minut lub krócej.

Przykładowa integracja z aplikacjami do odtwarzania filmów

Zalecamy wykonanie tych czynności, aby zintegrować przełącznik częstotliwości odświeżania w aplikacjach do odtwarzania filmów:

  1. Zdecyduj, co changeFrameRateStrategy:
    1. Jeśli odtwarzasz długi film, np. film fabularny, 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 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 oba te fakty są prawdziwe:
    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). Będzie tak, 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 bez płynnego przejścia. 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_SOURCE i changeFrameRateStrategy, 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ć. Pamiętaj, że na etapie 5d zalecamy wdrożenie sposobu, który umożliwi użytkownikowi odrzucenie tej funkcji i pominięcie dodatkowego opóźnienia. Dzieje się tak, ponieważ zalecana przez nas 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. Zaczekaj 2 sekundy, aż przełączenie trybu zostanie zakończone.
    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();
}