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

API частоты кадров позволяет приложениям информировать платформу Android о предполагаемой частоте кадров и доступен в приложениях, ориентированных на Android 11 (уровень API 30) или выше. Традиционно большинство устройств поддерживали только одну частоту обновления дисплея, обычно 60 Гц, но ситуация меняется. Многие устройства теперь поддерживают дополнительные частоты обновления, например 90 Гц или 120 Гц. Некоторые устройства поддерживают плавное переключение частоты обновления, в то время как другие кратковременно отображают черный экран, обычно длящийся секунду.

Основная цель API — позволить приложениям лучше использовать все поддерживаемые частоты обновления дисплея. Например, приложение, воспроизводящее видео с частотой 24 Гц и вызывающее функцию setFrameRate() может привести к тому, что устройство изменит частоту обновления экрана с 60 Гц на 120 Гц. Эта новая частота обновления обеспечивает плавное воспроизведение видео с частотой 24 Гц без дрожания без необходимости преобразования 3:2, которое требуется для воспроизведения того же видео на дисплее с частотой 60 Гц. Это приводит к улучшению пользовательского опыта.

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

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

Приложению не нужно учитывать фактическую поддерживаемую частоту обновления дисплея, которую можно получить, вызвав Display.getSupportedModes() , чтобы безопасно вызвать setFrameRate() . Например, даже если устройство поддерживает только частоту 60 Гц, вызовите setFrameRate() с частотой кадров, которую предпочитает ваше приложение. Устройства, частота кадров которых не соответствует частоте кадров приложения, останутся с текущей частотой обновления экрана.

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

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

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

В некоторых сценариях поверхность видео перестанет отправлять кадры, но останется видимой на экране в течение некоторого времени. Общие сценарии включают ситуацию, когда воспроизведение достигает конца видео или когда пользователь приостанавливает воспроизведение. В этих случаях вызовите setFrameRate() с параметром частоты кадров, установленным в 0, чтобы сбросить настройку частоты кадров поверхности до значения по умолчанию. Очистка настройки частоты кадров подобным образом не требуется при разрушении поверхности или когда поверхность скрыта из-за того, что пользователь переключается на другое приложение. Сбрасывайте настройку частоты кадров только в том случае, если поверхность остается видимой, но не используется.

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

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

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

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

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

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

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

Несколько поверхностей

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

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

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

Приложение должно решить, как реагировать, когда частота обновления дисплея не соответствует частоте кадров приложения. Для видео частота кадров фиксируется на частоте исходного видео, и для отображения видеоконтента потребуется раскрывающееся меню. Вместо этого игра может попытаться запуститься с частотой обновления экрана, а не оставаться с предпочтительной частотой кадров. Приложение не должно изменять значение, которое оно передает в setFrameRate() в зависимости от того, что делает платформа. Он должен оставаться установленным на предпочитаемую частоту кадров приложения, независимо от того, как приложение обрабатывает случаи, когда платформа не настраивается в соответствии с запросом приложения. Таким образом, если условия устройства изменятся, что позволит использовать дополнительную частоту обновления дисплея, платформа будет иметь правильную информацию для переключения на предпочтительную частоту кадров приложения.

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

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

В некоторых случаях платформа может переключиться на частоту кадров, кратную частоте кадров, указанной приложением в setFrameRate() . Например, приложение может вызвать setFrameRate() с частотой 60 Гц, а устройство может переключить дисплей на частоту 120 Гц. Одна из причин, по которой это может произойти, заключается в том, что другое приложение имеет поверхность с настройкой частоты кадров 24 Гц. В этом случае работа дисплея с частотой 120 Гц позволит работать как с поверхностной частотой 60 Гц, так и с поверхностной частотой 24 Гц без необходимости понижения напряжения.

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

setFrameRate() против предпочтительногоDisplayModeId

WindowManager.LayoutParams.preferredDisplayModeId — это еще один способ, с помощью которого приложения могут сообщать платформе свою частоту кадров. Некоторые приложения хотят изменить только частоту обновления экрана, а не изменять другие настройки режима отображения, например разрешение экрана. В общем, используйте setFrameRate() вместо preferredDisplayModeId . Функцию setFrameRate() проще использовать, поскольку приложению не нужно искать в списке режимов отображения режим с определенной частотой кадров.

setFrameRate() дает платформе больше возможностей для выбора совместимой частоты кадров в сценариях, где несколько поверхностей работают с разной частотой кадров. Например, рассмотрим сценарий, в котором два приложения работают в режиме разделенного экрана на Pixel 4, где одно приложение воспроизводит видео с частотой 24 Гц, а другое показывает пользователю прокручиваемый список. Pixel 4 поддерживает две частоты обновления дисплея: 60 Гц и 90 Гц. Используя API- preferredDisplayModeId , поверхность видео принудительно выбирает частоту 60 Гц или 90 Гц. Вызывая setFrameRate() с частотой 24 Гц, видеоповерхность предоставляет платформе дополнительную информацию о частоте кадров исходного видео, позволяя платформе выбирать 90 Гц для частоты обновления дисплея, что в этом сценарии лучше, чем 60 Гц.

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

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

setFrameRate() против предпочтительногоRefreshRate

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

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

привилегированныйRefreshRate и привилегированныйDisplayModeId

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

Как избежать слишком частого вызова setFrameRate()

Хотя вызов setFrameRate() не требует больших затрат с точки зрения производительности, приложениям следует избегать вызова setFrameRate() каждый кадр или несколько раз в секунду. Вызовы setFrameRate() могут привести к изменению частоты обновления дисплея, что может привести к падению кадров во время перехода. Вам следует заранее определить правильную частоту кадров и один раз вызвать setFrameRate() .

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

Хотя видео является основным вариантом использования API setFrameRate() , его можно использовать и для других приложений. Например, игра, которая не планирует работать с частотой выше 60 Гц (чтобы снизить энергопотребление и продлить игровые сеансы), может вызвать Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) . Таким образом, устройство, которое по умолчанию работает с частотой 90 Гц, вместо этого будет работать с частотой 60 Гц, пока игра активна, что позволит избежать дрожания, которое в противном случае могло бы возникнуть, если бы игра работала с частотой 60 Гц, а дисплей работал с частотой 90 Гц.

Использование 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. Покажите UX, чтобы уведомить пользователя. Обратите внимание, что мы рекомендуем вам реализовать возможность отклонения пользователем этого пользовательского интерфейса и пропуска дополнительной задержки на шаге 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();
}
,

API частоты кадров позволяет приложениям информировать платформу Android о предполагаемой частоте кадров и доступен в приложениях, предназначенных для Android 11 (уровень API 30) или выше. Традиционно большинство устройств поддерживали только одну частоту обновления дисплея, обычно 60 Гц, но ситуация меняется. Многие устройства теперь поддерживают дополнительные частоты обновления, например 90 Гц или 120 Гц. Некоторые устройства поддерживают плавное переключение частоты обновления, в то время как другие кратковременно отображают черный экран, обычно длящийся секунду.

Основная цель API — позволить приложениям лучше использовать все поддерживаемые частоты обновления дисплея. Например, приложение, воспроизводящее видео с частотой 24 Гц и вызывающее функцию setFrameRate() может привести к тому, что устройство изменит частоту обновления экрана с 60 Гц на 120 Гц. Эта новая частота обновления обеспечивает плавное воспроизведение видео с частотой 24 Гц без дрожания без необходимости преобразования 3:2, которое требуется для воспроизведения того же видео на дисплее с частотой 60 Гц. Это приводит к улучшению пользовательского опыта.

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

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

Приложению не нужно учитывать фактическую поддерживаемую частоту обновления дисплея, которую можно получить, вызвав Display.getSupportedModes() , чтобы безопасно вызвать setFrameRate() . Например, даже если устройство поддерживает только частоту 60 Гц, вызовите setFrameRate() с частотой кадров, которую предпочитает ваше приложение. Устройства, частота кадров которых не соответствует частоте кадров приложения, останутся с текущей частотой обновления экрана.

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

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

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

В некоторых сценариях поверхность видео перестанет отправлять кадры, но останется видимой на экране в течение некоторого времени. Общие сценарии включают ситуацию, когда воспроизведение достигает конца видео или когда пользователь приостанавливает воспроизведение. В этих случаях вызовите setFrameRate() с параметром частоты кадров, установленным в 0, чтобы сбросить настройку частоты кадров поверхности до значения по умолчанию. Очистка настройки частоты кадров подобным образом не требуется при разрушении поверхности или когда поверхность скрыта из-за того, что пользователь переключается на другое приложение. Сбрасывайте настройку частоты кадров только в том случае, если поверхность остается видимой, но не используется.

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

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

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

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

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

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

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

Несколько поверхностей

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

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

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

Приложение должно решить, как реагировать, когда частота обновления дисплея не соответствует частоте кадров приложения. Для видео частота кадров фиксируется на частоте исходного видео, и для отображения видеоконтента потребуется раскрывающееся меню. Вместо этого игра может попытаться запуститься с частотой обновления экрана, а не оставаться с предпочтительной частотой кадров. Приложение не должно изменять значение, которое оно передает в setFrameRate() в зависимости от того, что делает платформа. Он должен оставаться установленным на предпочитаемую частоту кадров приложения, независимо от того, как приложение обрабатывает случаи, когда платформа не настраивается в соответствии с запросом приложения. Таким образом, если условия устройства изменятся, что позволит использовать дополнительную частоту обновления дисплея, платформа будет иметь правильную информацию для переключения на предпочтительную частоту кадров приложения.

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

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

В некоторых случаях платформа может переключиться на частоту кадров, кратную частоте кадров, указанной приложением в setFrameRate() . Например, приложение может вызвать setFrameRate() с частотой 60 Гц, а устройство может переключить дисплей на частоту 120 Гц. Одна из причин, по которой это может произойти, заключается в том, что другое приложение имеет поверхность с настройкой частоты кадров 24 Гц. В этом случае работа дисплея с частотой 120 Гц позволит работать как с поверхностной частотой 60 Гц, так и с поверхностной частотой 24 Гц без необходимости понижения напряжения.

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

setFrameRate() против предпочтительногоDisplayModeId

WindowManager.LayoutParams.preferredDisplayModeId — это еще один способ, с помощью которого приложения могут сообщать платформе свою частоту кадров. Некоторые приложения хотят изменить только частоту обновления экрана, а не изменять другие настройки режима отображения, например разрешение экрана. В общем, используйте setFrameRate() вместо preferredDisplayModeId . Функцию setFrameRate() проще использовать, поскольку приложению не нужно искать в списке режимов отображения режим с определенной частотой кадров.

setFrameRate() дает платформе больше возможностей для выбора совместимой частоты кадров в сценариях, где несколько поверхностей работают с разной частотой кадров. Например, рассмотрим сценарий, в котором два приложения работают в режиме разделенного экрана на Pixel 4, где одно приложение воспроизводит видео с частотой 24 Гц, а другое показывает пользователю прокручиваемый список. Pixel 4 поддерживает две частоты обновления дисплея: 60 Гц и 90 Гц. Используя API- preferredDisplayModeId , поверхность видео принудительно выбирает частоту 60 Гц или 90 Гц. Вызывая setFrameRate() с частотой 24 Гц, видеоповерхность предоставляет платформе дополнительную информацию о частоте кадров исходного видео, позволяя платформе выбирать 90 Гц для частоты обновления дисплея, что в этом сценарии лучше, чем 60 Гц.

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

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

setFrameRate() против предпочтительногоRefreshRate

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

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

привилегированныйRefreshRate и привилегированныйDisplayModeId

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

Как избежать слишком частого вызова setFrameRate()

Хотя вызов setFrameRate() не требует больших затрат с точки зрения производительности, приложениям следует избегать вызова setFrameRate() каждый кадр или несколько раз в секунду. Вызовы setFrameRate() могут привести к изменению частоты обновления дисплея, что может привести к падению кадров во время перехода. Вам следует заранее определить правильную частоту кадров и один раз вызвать setFrameRate() .

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

Хотя видео является основным вариантом использования API setFrameRate() , его можно использовать и для других приложений. Например, игра, которая не планирует работать с частотой выше 60 Гц (чтобы снизить энергопотребление и продлить игровые сеансы), может вызвать Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) . Таким образом, устройство, которое по умолчанию работает с частотой 90 Гц, вместо этого будет работать с частотой 60 Гц, пока игра активна, что позволит избежать дрожания, которое в противном случае могло бы возникнуть, если бы игра работала с частотой 60 Гц, а дисплей работал с частотой 90 Гц.

Использование 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. Покажите UX, чтобы уведомить пользователя. Обратите внимание, что мы рекомендуем вам реализовать возможность отклонения пользователем этого пользовательского интерфейса и пропуска дополнительной задержки на шаге 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();
}
,

API частоты кадров позволяет приложениям информировать платформу Android о предполагаемой частоте кадров и доступен в приложениях, ориентированных на Android 11 (уровень API 30) или выше. Традиционно большинство устройств поддерживали только одну частоту обновления дисплея, обычно 60 Гц, но ситуация меняется. Многие устройства теперь поддерживают дополнительные частоты обновления, например 90 Гц или 120 Гц. Некоторые устройства поддерживают плавное переключение частоты обновления, в то время как другие кратковременно отображают черный экран, обычно длящийся секунду.

Основная цель API — позволить приложениям лучше использовать все поддерживаемые частоты обновления дисплея. Например, приложение, воспроизводящее видео с частотой 24 Гц и вызывающее функцию setFrameRate() может привести к тому, что устройство изменит частоту обновления экрана с 60 Гц на 120 Гц. Эта новая частота обновления обеспечивает плавное воспроизведение видео с частотой 24 Гц без дрожания без необходимости преобразования 3:2, которое требуется для воспроизведения того же видео на дисплее с частотой 60 Гц. Это приводит к улучшению пользовательского опыта.

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

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

Приложению не нужно учитывать фактическую поддерживаемую частоту обновления дисплея, которую можно получить, вызвав Display.getSupportedModes() , чтобы безопасно вызвать setFrameRate() . Например, даже если устройство поддерживает только частоту 60 Гц, вызовите setFrameRate() с частотой кадров, которую предпочитает ваше приложение. Устройства, частота кадров которых не соответствует частоте кадров приложения, останутся с текущей частотой обновления экрана.

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

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

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

В некоторых сценариях поверхность видео перестанет отправлять кадры, но останется видимой на экране в течение некоторого времени. Общие сценарии включают ситуацию, когда воспроизведение достигает конца видео или когда пользователь приостанавливает воспроизведение. В этих случаях вызовите setFrameRate() с параметром частоты кадров, установленным в 0, чтобы сбросить настройку частоты кадров поверхности до значения по умолчанию. Очистка настройки частоты кадров подобным образом не требуется при разрушении поверхности или когда поверхность скрыта из-за того, что пользователь переключается на другое приложение. Сбрасывайте настройку частоты кадров только в том случае, если поверхность остается видимой, но не используется.

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

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

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

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

  • Пользователи : Чтобы выбрать, пользователи могут включить настройку пользователя Content Content Content .
  • Приложения : Чтобы выбрать, приложения могут передавать CHANGE_FRAME_RATE_ALWAYS в setFrameRate() .

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

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

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

Несколько поверхностей

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

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

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

Приложение зависит от приложения, чтобы решить, как ответить, когда частота обновления дисплея не соответствует частоте кадров приложения. Для видео частота кадров приспособлена к частоте исходного видео, и для отображения видеоконтента потребуется протяжение. Вместо этого игра может попытаться работать на частоте обновления дисплея, а не оставаться с предпочтительной частотой кадров. Приложение не должно изменять значение, которое оно передает на setFrameRate() в зависимости от того, что делает платформа. Он должен оставаться настроенным на предпочтительную частоту кадров приложения, независимо от того, как приложение обрабатывает случаи, когда платформа не настраивается в соответствии с запросом приложения. Таким образом, если условия устройства изменяются, чтобы позволить использовать дополнительные показатели обновления дисплея, платформа имеет правильную информацию для перехода на предпочтительную частоту кадров приложения.

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

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

В некоторых случаях платформа может переключаться на кратную частоту кадров, указанную в setFrameRate() . Например, приложение может вызовать setFrameRate() с 60 Гц, а устройство может переключить дисплей на 120 Гц. Одна из причин, по которой это может произойти, заключается в том, что в другом приложении есть поверхность с настройкой частоты кадров 24 Гц. В этом случае запуск дисплея при 120 Гц позволит поверхности 60 Гц и поверхности 24 Гц работать без необходимости протяжения.

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

setFramerate () против PreferredDisplayModeid

WindowManager.LayoutParams.preferredDisplayModeId - это еще один способ, которым приложения могут указывать свою частоту кадров на платформе. Некоторые приложения хотят только изменить частоту обновления дисплея, а не изменять другие настройки режима отображения, такие как разрешение дисплея. В общем, используйте setFrameRate() вместо preferredDisplayModeId . Функцию setFrameRate() проще в использовании, потому что приложение не нужно искать в списке режимов отображения, чтобы найти режим с определенной частотой кадров.

setFrameRate() дает платформе больше возможностей для выбора совместимой частоты кадров в сценариях, где существует несколько поверхностей, которые работают с разными частотами кадров. Например, рассмотрим сценарий, в котором два приложения работают в режиме разделенного экрана в Pixel 4, где одно приложение воспроизводит видео 24 Гц, а другое показывает пользователю прокрученный список. Pixel 4 поддерживает две показатели обновления дисплея: 60 Гц и 90 Гц. Используя API preferredDisplayModeId , поверхность видео вынуждена выбирать либо 60 Гц, либо 90 Гц. Позвонив setFrameRate() с 24 Гц, поверхность видео дает платформе больше информации о частоте кадров исходного видео, что позволяет платформе выбирать 90 Гц для частоты обновления дисплея, что лучше, чем 60 Гц в этом сценарии.

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

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

setFramerate () против PreferredRefreshrate

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

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

PreferredRefreshrate vs PreferredDisplayModeid

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

Избегать вызова setFramerate () слишком часто

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

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

Хотя видео является основным вариантом использования для API setFrameRate() , его можно использовать для других приложений. Например, игра, которая намерена не работать выше 60 Гц (для уменьшения использования мощности и достижения более длинных игровых сеансов) может вызовать Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) . Таким образом, устройство, которое работает на 90 Гц по умолчанию, вместо этого будет работать на 60 Гц, в то время как игра активна, что будет избежать Джаддера, который в противном случае произойдет, если игра будет работать в 60 Гц, в то время как дисплей работает на 90 Гц.

Использование frame_rate_compatibility_fixed_source

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

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

  • Мы настоятельно рекомендуем, чтобы приложения, отображая многолетние видео, такие как фильмы, вызов setFrameRate( FPS , FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) где FPS является частотой кадров видео.
  • Мы настоятельно рекомендуем против приложений Calling 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. Показать UX, чтобы уведомить пользователя. Обратите внимание, что мы рекомендуем вам реализовать способ для пользователя отклонить этот UX и пропустить дополнительную задержку в шаге 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();
}
,

API частоты кадров позволяет приложениям информировать платформу Android о их предполагаемой частоте кадров и доступна в приложениях, которые нацелены на Android 11 (API -уровень 30) или выше. Традиционно, большинство устройств поддерживают только одну частоту обновления дисплея, как правило, 60 Гц, но это изменилось. Многие устройства теперь поддерживают дополнительные показатели обновления, такие как 90 Гц или 120 Гц. Некоторые устройства поддерживают бесшовные переключатели скорости обновления, в то время как другие кратко показывают черный экран, обычно длится секунда.

Основная цель API - позволить приложениям лучше воспользоваться всеми поддерживаемыми показателями обновления дисплея. Например, приложение, воспроизводимое видео 24 Гц, которое вызывает setFrameRate() может привести к изменению частоты обновления дисплея с 60 Гц на 120 Гц. Эта новая частота обновления позволяет плавно воспроизводить видео 24 Гц, без необходимости, без необходимости в 3: 2, как потребовалось бы, чтобы воспроизводить то же видео на дисплее 60 Гц. Это приводит к лучшему пользовательскому опыту.

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

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

Приложению не нужно учитывать фактическую поддерживаемую частоту обновления дисплея, которые можно получить, вызовов Display.getSupportedModes() , чтобы безопасно вызовать setFrameRate() . Например, даже если устройство поддерживает только 60 Гц, вызовите setFrameRate() с частотой кадров, которые предпочитает ваше приложение. Устройства, которые не имеют лучшего соответствия для частоты кадров приложения, останутся с текущей частотой обновления дисплея.

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

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

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

В некоторых сценариях поверхность видео перестанет отправлять рамки, но в течение некоторого времени останется видимой на экране. Общие сценарии включают в себя, когда воспроизведение достигает конца видео или когда пользователь делает паузу воспроизведения. В этих случаях вызов setFrameRate() с параметром скорости кадров, установленным на 0, чтобы очистить настройку частоты кадров поверхности до значения по умолчанию. Очистка настройки частоты кадров, подобная этой, не требуется при уничтожении поверхности или когда поверхность скрыта, потому что пользователь переключается на другое приложение. Очистить настройку частоты кадров только тогда, когда поверхность остается видимой без использования.

Бесполезный переключатель частоты кадров

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

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

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

  • Пользователи : Чтобы выбрать, пользователи могут включить настройку пользователя Content Content Content .
  • Приложения : Чтобы выбрать, приложения могут передавать CHANGE_FRAME_RATE_ALWAYS в setFrameRate() .

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

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

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

Несколько поверхностей

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

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

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

Приложение зависит от приложения, чтобы решить, как ответить, когда частота обновления дисплея не соответствует частоте кадров приложения. Для видео частота кадров приспособлена к частоте исходного видео, и для отображения видеоконтента потребуется протяжение. Вместо этого игра может попытаться работать на частоте обновления дисплея, а не оставаться с предпочтительной частотой кадров. Приложение не должно изменять значение, которое оно передает на setFrameRate() в зависимости от того, что делает платформа. Он должен оставаться настроенным на предпочтительную частоту кадров приложения, независимо от того, как приложение обрабатывает случаи, когда платформа не настраивается в соответствии с запросом приложения. Таким образом, если условия устройства изменяются, чтобы позволить использовать дополнительные показатели обновления дисплея, платформа имеет правильную информацию для перехода на предпочтительную частоту кадров приложения.

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

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

В некоторых случаях платформа может переключаться на кратную частоту кадров, указанную в setFrameRate() . Например, приложение может вызовать setFrameRate() с 60 Гц, а устройство может переключить дисплей на 120 Гц. Одна из причин, по которой это может произойти, заключается в том, что в другом приложении есть поверхность с настройкой частоты кадров 24 Гц. В этом случае запуск дисплея при 120 Гц позволит поверхности 60 Гц и поверхности 24 Гц работать без необходимости протяжения.

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

setFramerate () против PreferredDisplayModeid

WindowManager.LayoutParams.preferredDisplayModeId - это еще один способ, которым приложения могут указывать свою частоту кадров на платформе. Некоторые приложения хотят только изменить частоту обновления дисплея, а не изменять другие настройки режима отображения, такие как разрешение дисплея. В общем, используйте setFrameRate() вместо preferredDisplayModeId . Функцию setFrameRate() проще в использовании, потому что приложение не нужно искать в списке режимов отображения, чтобы найти режим с определенной частотой кадров.

setFrameRate() дает платформе больше возможностей для выбора совместимой частоты кадров в сценариях, где существует несколько поверхностей, которые работают с разными частотами кадров. Например, рассмотрим сценарий, в котором два приложения работают в режиме разделенного экрана в Pixel 4, где одно приложение воспроизводит видео 24 Гц, а другое показывает пользователю прокрученный список. Pixel 4 поддерживает две показатели обновления дисплея: 60 Гц и 90 Гц. Используя API preferredDisplayModeId , поверхность видео вынуждена выбирать либо 60 Гц, либо 90 Гц. Позвонив setFrameRate() с 24 Гц, поверхность видео дает платформе больше информации о частоте кадров исходного видео, что позволяет платформе выбирать 90 Гц для частоты обновления дисплея, что лучше, чем 60 Гц в этом сценарии.

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

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

setFramerate () против PreferredRefreshrate

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

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

PreferredRefreshrate vs PreferredDisplayModeid

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

Избегать вызова setFramerate () слишком часто

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

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

Хотя видео является основным вариантом использования для API setFrameRate() , его можно использовать для других приложений. Например, игра, которая намерена не работать выше 60 Гц (для уменьшения использования мощности и достижения более длинных игровых сеансов) может вызовать Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) . Таким образом, устройство, которое работает на 90 Гц по умолчанию, вместо этого будет работать на 60 Гц, в то время как игра активна, что будет избежать Джаддера, который в противном случае произойдет, если игра будет работать при 60 Гц, в то время как дисплей работает на 90 Гц.

Использование frame_rate_compatibility_fixed_source

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

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

  • Мы настоятельно рекомендуем, чтобы приложения, отображая многолетние видео, такие как фильмы, вызов setFrameRate( FPS , FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) где FPS является частотой кадров видео.
  • Мы настоятельно рекомендуем против приложений Calling 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. Показать UX, чтобы уведомить пользователя. Обратите внимание, что мы рекомендуем вам реализовать способ для пользователя отклонить этот UX и пропустить дополнительную задержку в шаге 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();
}