Частота кадров

The frame rate API lets apps inform the Android platform of their intended frame rate and is available on apps that target Android 11 (API level 30) or higher. Traditionally, most devices have supported only a single display refresh rate, typically 60Hz, but this has been changing. Many devices now support additional refresh rates such as 90Hz or 120Hz. Some devices support seamless refresh rate switches, while others briefly show a black screen, usually lasting a second.

The primary purpose of the API is to enable apps to better take advantage of all the supported display refresh rates. For example, an app playing a 24Hz video that calls setFrameRate() may result in the device changing the display refresh rate from 60Hz to 120Hz. This new refresh rate enables smooth, judder-free playback of 24Hz video, with no need for 3:2 pulldown as would be required to play the same video on a 60Hz display. This results in a better user experience.

Основное использование

Android предоставляет несколько способов доступа к поверхностям и управления ими, поэтому существует несколько версий API setFrameRate() . Каждая версия API принимает одни и те же параметры и работает так же, как и другие:

The app does not need to consider the actual supported display refresh rates, which can be obtained by calling Display.getSupportedModes() , in order to safely call setFrameRate() . For example, even if the device only supports 60Hz, call setFrameRate() with the frame rate your app prefers. Devices that don't have a better match for the app's frame rate will stay with the current display refresh rate.

Чтобы проверить, приводит ли вызов функции setFrameRate() к изменению частоты обновления экрана, зарегистрируйтесь для получения уведомлений об изменении отображения, вызвав DisplayManager.registerDisplayListener() или AChoreographer_registerRefreshRateCallback() .

При вызове setFrameRate() лучше передавать точную частоту кадров, а не округлять до целого числа. Например, при рендеринге видео, записанного с частотой 29,97 Гц, передавайте 29,97, а не округляйте до 30.

Для видеоприложений параметр совместимости, передаваемый в функцию setFrameRate() следует установить в Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE , чтобы дополнительно указать платформе Android, что приложение будет использовать функцию pulldown для адаптации к несовпадающей частоте обновления дисплея (что приведет к дрожанию изображения).

In some scenarios, the video surface will stop submitting frames but will remain visible on the screen for some time. Common scenarios include when playback reaches the end of the video or when the user pauses playback. In these cases, call setFrameRate() with the frame rate parameter set to 0 to clear the surface's frame rate setting back to the default value. Clearing the frame rate setting like this isn't necessary when destroying the surface, or when the surface is hidden because the user switches to a different app. Clear the frame rate setting only when the surface remains visible without being used.

Неплавное переключение частоты кадров

На некоторых устройствах переключение частоты обновления может сопровождаться визуальными прерываниями, такими как черный экран на секунду-две. Это обычно происходит на телеприставках, телевизионных панелях и подобных устройствах. По умолчанию платформа Android не переключает режимы при вызове API Surface.setFrameRate() , чтобы избежать подобных визуальных прерываний.

Некоторые пользователи предпочитают визуальное прерывание в начале и конце длинных видеороликов. Это позволяет частоте обновления дисплея соответствовать частоте кадров видео и избежать артефактов преобразования частоты кадров, таких как дрожание изображения при воспроизведении фильмов (3:2 pulldown).

По этой причине переключение частоты обновления экрана может быть включено с некоторым интервалом, если на это дадут согласие как пользователь, так и приложения:

Мы рекомендуем всегда использовать CHANGE_FRAME_RATE_ALWAYS для длительных видеороликов, таких как фильмы. Это связано с тем, что преимущество согласования частоты кадров видео перевешивает прерывания, возникающие при изменении частоты обновления.

Дополнительные рекомендации

Следуйте этим рекомендациям для распространенных ситуаций.

Множественные поверхности

The Android platform is designed to correctly handle scenarios where there are multiple surfaces with different frame rate settings. When your app has multiple surfaces with different frame rates, call setFrameRate() with the correct frame rate for each surface. Even if the device is running multiple apps at once, using split screen or picture-in-picture mode, each app can safely call setFrameRate() for their own surfaces.

Платформа не переключается на частоту кадров приложения.

Even if the device supports the frame rate the app specifies in a call to setFrameRate() , there are cases where the device won't switch the display to that refresh rate. For example, a higher priority surface may have a different frame rate setting, or the device may be in battery saver mode (setting a restriction on the display refresh rate to preserve battery). The app must still work correctly when the device doesn't switch the display refresh rate to the app's frame rate setting, even if the device does switch under normal circumstances.

It's up to the app to decide how to respond when the display refresh rate doesn't match the app frame rate. For video, the frame rate is fixed to that of the source video, and pulldown will be required to show the video content. A game may instead choose to try to run at the display refresh rate rather than staying with its preferred frame rate. The app shouldn't change the value it passes to setFrameRate() based on what the platform does. It should stay set to the app's preferred frame rate, regardless of how the app handles cases where the platform doesn't adjust to match the app's request. That way, if device conditions change to allow additional display refresh rates to be used, the platform has the correct information to switch to the app's preferred frame rate.

В случаях, когда приложение не может или не может работать с частотой обновления экрана, оно должно указывать временные метки отображения для каждого кадра, используя один из механизмов платформы для установки временных меток отображения:

Использование этих временных меток предотвращает преждевременное отображение кадра приложения платформой, что может привести к ненужным рывкам. Правильное использование временных меток отображения кадров — довольно сложная задача. Для игр см. наше руководство по настройке частоты кадров для получения дополнительной информации о предотвращении рывков и рассмотрите возможность использования библиотеки Android Frame Pacing .

In some cases, the platform may switch to a multiple of the frame rate the app specified in setFrameRate() . For example, an app may call setFrameRate() with 60Hz and the device may switch the display to 120Hz. One reason this might happen is if another app has a surface with a frame rate setting of 24Hz. In that case, running the display at 120Hz will allow both the 60Hz surface and 24Hz surface to run with no pulldown required.

Когда частота кадров дисплея кратна частоте кадров приложения, приложение должно указывать временные метки отображения для каждого кадра, чтобы избежать ненужных рывков. Для игр библиотека Android Frame Pacing полезна для правильной установки временных меток отображения кадров.

setFrameRate() против preferredDisplayModeId

WindowManager.LayoutParams.preferredDisplayModeId is another way that apps can indicate their frame rate to the platform. Some apps only want to change the display refresh rate rather than changing other display mode settings, like the display resolution. In general, use setFrameRate() instead of preferredDisplayModeId . The setFrameRate() function is easier to use because the app doesn't need to search through the list of display modes to find a mode with a specific frame rate.

setFrameRate() gives the platform more opportunities to pick a compatible frame rate in scenarios where there are multiple surfaces that are running at different frame rates. For example, consider a scenario where two apps are running in split-screen mode on a Pixel 4, where one app is playing a 24Hz video and the other is showing the user a scrollable list. Pixel 4 supports two display refresh rates: 60Hz and 90Hz. Using the preferredDisplayModeId API, the video surface is forced to pick either 60Hz or 90Hz. By calling setFrameRate() with 24Hz, the video surface gives the platform more information about the frame rate of the source video, enabling the platform to choose 90Hz for the display refresh rate, which is better than 60Hz in this scenario.

Однако существуют сценарии, в которых вместо setFrameRate() следует использовать preferredDisplayModeId , например, следующие:

  • Если приложение хочет изменить разрешение или другие параметры режима отображения, используйте preferredDisplayModeId .
  • Платформа будет переключать режимы отображения в ответ на вызов setFrameRate() только в том случае, если переключение режима является незначительным и вряд ли будет заметно пользователю. Если приложение предпочитает переключать частоту обновления экрана, даже если это требует значительного переключения режима (например, на устройстве Android TV), используйте preferredDisplayModeId .
  • Приложениям, которые не могут обрабатывать отображение с частотой, кратной частоте кадров приложения, что требует установки временных меток отображения для каждого кадра, следует использовать preferredDisplayModeId .

setFrameRate() против preferredRefreshRate

WindowManager.LayoutParams#preferredRefreshRate устанавливает предпочтительную частоту кадров для окна приложения, и эта частота применяется ко всем поверхностям внутри окна. Приложение должно указывать предпочтительную частоту кадров независимо от поддерживаемой устройством частоты обновления, аналогично методу setFrameRate() , чтобы дать планировщику более точное представление о предполагаемой частоте кадров приложения.

preferredRefreshRate игнорируется для поверхностей, использующих setFrameRate() . В целом, по возможности используйте setFrameRate() .

preferredRefreshRate против preferredDisplayModeId

Если приложениям нужно изменить только предпочтительную частоту обновления экрана, лучше использовать preferredRefreshRate , а не preferredDisplayModeId .

Избегайте слишком частого вызова функции setFrameRate().

Although the setFrameRate() call isn't very costly in terms of performance, apps should avoid calling setFrameRate() every frame or multiple times per second. Calls to setFrameRate() are likely to result in a change to the display refresh rate, which may result in a frame drop during the transition. You should figure out the correct frame rate ahead of time and call setFrameRate() once.

Использование для игр или других приложений, не связанных с видео.

Although video is the primary use case for the setFrameRate() API, it can be used for other apps. For example, a game that intends to not run higher than 60Hz (to reduce power usage and achieve longer play sessions) can call Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) . In this way, a device that runs at 90Hz by default will instead run at 60Hz while the game is active, which will avoid the judder that would otherwise occur if the game ran at 60Hz while the display ran at 90Hz.

Использование параметра FRAME_RATE_COMPATIBILITY_FIXED_SOURCE

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE предназначен только для видеоприложений. Для приложений, не связанных с видео, используйте FRAME_RATE_COMPATIBILITY_DEFAULT .

Выбор стратегии для изменения частоты кадров

  • Мы настоятельно рекомендуем приложениям при отображении длительных видеороликов, таких как фильмы, вызывать функцию setFrameRate( fps , FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) , где fps — частота кадров видео.
  • Мы настоятельно не рекомендуем приложениям вызывать setFrameRate() с CHANGE_FRAME_RATE_ALWAYS если вы ожидаете, что воспроизведение видео продлится несколько минут или меньше.

Пример интеграции для приложений воспроизведения видео.

Для интеграции переключателей частоты обновления в приложения для воспроизведения видео мы рекомендуем следующие шаги:

  1. Определите стратегию changeFrameRateStrategy :
    1. При воспроизведении длинного видео, например, фильма, используйте MATCH_CONTENT_FRAMERATE_ALWAYS
    2. При воспроизведении короткого видеоролика, например, трейлера к фильму, используйте CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
  2. Если параметр changeFrameRateStrategy имеет значение CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS , перейдите к шагу 4.
  3. Чтобы определить, произойдет ли неплавное переключение частоты обновления, убедитесь, что оба этих факта верны:
    1. Плавное переключение режимов с текущей частоты обновления (назовем ее C) на частоту кадров видео (назовем ее V) невозможно. Это произойдет, если C и V разные, и Display.getMode().getAlternativeRefreshRates не содержит значения, кратного V.
    2. Пользователь дал согласие на изменение частоты обновления экрана с нарушением плавности. Это можно определить, проверив, возвращает ли DisplayManager.getMatchContentFrameRateUserPreference MATCH_CONTENT_FRAMERATE_ALWAYS
  4. Чтобы переход прошел без сбоев, выполните следующие действия:
    1. Вызовите setFrameRate и передайте ей значения fps , FRAME_RATE_COMPATIBILITY_FIXED_SOURCE и changeFrameRateStrategy , где fps — частота кадров видео.
    2. Начать воспроизведение видео
  5. Если планируется переключение в неплавный режим, выполните следующие действия:
    1. Покажите пользователю всплывающее окно, чтобы уведомить его. Обратите внимание, что мы рекомендуем предусмотреть возможность закрытия этого всплывающего окна и пропуска дополнительной задержки на шаге 5.d. Это связано с тем, что рекомендуемая нами задержка больше, чем необходимо на экранах с более быстрым переключением.
    2. Вызовите setFrameRate и передайте ей значения fps , FRAME_RATE_COMPATIBILITY_FIXED_SOURCE и CHANGE_FRAME_RATE_ALWAYS , где fps — частота кадров видео.
    3. Дождитесь вызова функции обратного вызова onDisplayChanged .
    4. Подождите 2 секунды, пока завершится переключение режимов.
    5. Начать воспроизведение видео

Псевдокод, поддерживающий только бесшовное переключение, выглядит следующим образом:

SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
    contentFrameRate,
    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
    CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();

Псевдокод для поддержки бесшовного и не бесшовного переключения, как описано выше, выглядит следующим образом:

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();
}