Velocidad de fotogramas

La API de velocidad de fotogramas permite que las apps informen a la plataforma de Android el fotograma deseado y está disponible en apps orientadas a Android 11 (nivel de API 30) o versiones posteriores. Tradicionalmente, la mayoría de los dispositivos admite solo una frecuencia de actualización de pantalla, normalmente, 60 Hz, pero esto ha ido cambiando. Muchos dispositivos ahora admiten frecuencias de actualización, como 90 Hz o 120 Hz. Algunos dispositivos admiten una frecuencia de actualización fluida interruptores, mientras que otros muestran brevemente una pantalla negra que suele durar un segundo.

El objetivo principal de la API es permitir que las apps aprovechen mejor todas las frecuencias de actualización de pantalla admitidas. Por ejemplo, una app que reproduce un video de 24 Hz que llame a setFrameRate() podría provocar que el dispositivo cambie de pantalla frecuencia de actualización de 60 Hz a 120 Hz. Esta nueva frecuencia de actualización permite Reproducción de video de 24 Hz sin sacudidas, sin necesidad de aplicar despliegue de 3:2. para reproducir el mismo video en una pantalla de 60 Hz. Esto da como resultado una mejor experiencia una experiencia fluida a los desarrolladores.

Uso básico

Android expone varias formas de acceder y controlar superficies, de modo que hay varias versiones de la API de setFrameRate(). Cada versión de la API toma la con los mismos parámetros y funciona igual que los demás:

La app no necesita considerar las frecuencias reales de actualización de pantalla admitidas, que se puede obtener llamando Display.getSupportedModes(): para llamar de forma segura a setFrameRate(). Por ejemplo, aunque el dispositivo solo admite 60 Hz, llama a setFrameRate() con la velocidad de fotogramas que prefiera tu app. Los dispositivos que no tengan una mejor concordancia con la velocidad de fotogramas de la app permanecerán como la frecuencia de actualización actual de la pantalla.

Para ver si una llamada a setFrameRate() genera un cambio en la actualización de la pantalla frecuencia, registra para recibir notificaciones de cambio de pantalla llamando DisplayManager.registerDisplayListener() o AChoreographer_registerRefreshRateCallback().

Cuando se llama a setFrameRate(), es mejor pasar la velocidad de fotogramas exacta en lugar redondear a un número entero. Por ejemplo, si renderizas un video grabado en 29.97 Hz, pasa 29.97 en lugar de redondear a 30.

En el caso de las apps de video, se debe establecer el parámetro de compatibilidad que se pasa a setFrameRate() a Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE para darle una pista adicional a la plataforma de Android que la app usará desplegables para adaptarse a una la frecuencia de actualización de la pantalla (que hará que se sacude).

En algunos casos, la plataforma del video dejará de enviar fotogramas, pero permanecerá visible en la pantalla durante un tiempo. Las situaciones comunes incluyen cuando la reproducción Llega al final del video o cuando el usuario pausa la reproducción. En estos casos, llama a setFrameRate() con el parámetro de velocidad de fotogramas establecido en 0 para borrar la latencia velocidad de fotogramas al valor predeterminado. Borrando la configuración de velocidad de fotogramas de esta manera no es necesaria cuando se destruye la superficie, oculto porque el usuario cambia a otra aplicación. Borra la velocidad de fotogramas configuración solo cuando la superficie permanece visible sin que se use.

Interruptor de velocidad de fotogramas ininterrumpido

En algunos dispositivos, el cambio de frecuencia de actualización puede tener interrupciones visuales, como una notificación pantalla durante uno o dos segundos. Esto suele ocurrir en decodificadores, paneles de TV, y dispositivos similares. De forma predeterminada, el framework de Android no cambia de modo cuando el elemento Surface.setFrameRate() para evitar estas interrupciones visuales.

Algunos usuarios prefieren una interrupción visual al principio y el final de los videos más largos. Esto permite que coincida la frecuencia de actualización de la pantalla la velocidad de fotogramas del video y evitar artefactos de conversión de velocidad de fotogramas como 3:2 sacudida desplegable para reproducir la película.

Por esta razón, los interruptores de frecuencia de actualización que no sean fluidos pueden habilitarse si tanto aceptación del usuario y las apps:

Te recomendamos que siempre uses CHANGE_FRAME_RATE_ALWAYS para videos de larga duración, como películas. Esto se debe a que el beneficio de hacer coincidir la velocidad de fotogramas supera la interrupción que se produce al cambiar la frecuencia de actualización.

Recomendaciones adicionales

Sigue estas recomendaciones para situaciones comunes.

Varias superficies

La plataforma de Android se diseñó para manejar correctamente situaciones en las que hay en varias plataformas con diferentes configuraciones de velocidad de fotogramas. Cuando tu app tiene varias plataformas con distintas velocidades de fotogramas, llama a setFrameRate() con la la velocidad de fotogramas de cada superficie. Aunque el dispositivo ejecute varias apps en una vez, con el modo de pantalla dividida o pantalla en pantalla, cada app puede llamar setFrameRate() para sus propias plataformas

La plataforma no cambia a la velocidad de fotogramas de la app

Incluso si el dispositivo admite la velocidad de fotogramas que la app especifica en una llamada a setFrameRate(), hay casos en los que el dispositivo no cambia la pantalla a esa frecuencia de actualización. Por ejemplo, una superficie de mayor prioridad puede tener un valor velocidad de fotogramas o que el dispositivo esté en modo ahorro de batería (configurar una en la frecuencia de actualización de la pantalla para conservar la batería). La aplicación debe seguir funcionará correctamente cuando el dispositivo no cambie la frecuencia de actualización de la pantalla al la velocidad de fotogramas de la app, incluso si el dispositivo cambia según lo normal a las circunstancias.

Depende de la app decidir cómo responder cuando la frecuencia de actualización de la pantalla no coincide con la velocidad de fotogramas de la app. Para los videos, la velocidad de fotogramas se fija en la del el video de origen y se solicitará un menú desplegable para mostrar el contenido del video. R el juego puede optar por intentar ejecutarse a la frecuencia de actualización de la pantalla en lugar de y mantenga su velocidad de fotogramas preferida. La app no debería cambiar su valor pase a setFrameRate() según lo que haga la plataforma. Debería permanecer fija según la velocidad de fotogramas preferida de la app, sin importar cómo la app maneja los casos en los que la plataforma no se ajusta para coincidir con la solicitud de la app. De esta manera, si el dispositivo las condiciones cambian para permitir que se usen frecuencias de actualización de pantalla adicionales, el plataforma tenga la información correcta para cambiar al fotograma preferido de la app tasa.

En los casos en que la app no se ejecute o no pueda ejecutarse a la frecuencia de actualización de la pantalla, la app deberías especificar marcas de tiempo de presentación para cada marco mecanismos de la plataforma para establecer marcas de tiempo de presentación:

Si usas estas marcas de tiempo, la plataforma también dejará de presentar un fotograma de la app. tempranamente, lo que generaría una sacudida innecesaria. Uso correcto del marco las marcas de tiempo de presentación es un poco complicada. Para juegos, consulta nuestra guía de ritmo de fotogramas para obtener más información sobre cómo evitar que te sacudas y considera utilizar el Biblioteca de Android Frame Pacing

En algunos casos, la plataforma puede cambiar a un múltiplo de la velocidad de fotogramas de la app especificadas en setFrameRate(). Por ejemplo, una app puede llamar a setFrameRate() con 60 Hz, y el dispositivo puede cambiar la pantalla a 120 Hz. Una de las razones por las que esto podría sucede cuando otra app tiene una superficie con una configuración de velocidad de fotogramas de 24 Hz. En en ese caso, ejecutar la pantalla a 120 Hz permitirá que tanto la superficie de Se trata de una superficie de 24 Hz que se ejecuta sin necesidad de bajar el brazo.

Cuando la pantalla se ejecuta a un múltiplo de la velocidad de fotogramas de la app, la app debes especificar marcas de tiempo de presentación en cada fotograma para evitar sacudir. En el caso de los juegos, la biblioteca de Android Frame Pacing es útil para la configuración de marcas de tiempo de presentación de marcos.

setFrameRate() frente a PreferredDisplayModeId

WindowManager.LayoutParams.preferredDisplayModeId es otra forma en que las apps pueden indicar su velocidad de fotogramas a la plataforma. Algunos las apps solo quieren cambiar la frecuencia de actualización de la pantalla en lugar de cambiar otras del modo de visualización, como la resolución. En general, usa setFrameRate() en lugar de preferredDisplayModeId. El setFrameRate() es más fácil de usar porque la app no necesita buscar en la de modos de visualización para encontrar un modo con una velocidad de fotogramas específica.

setFrameRate() le da a la plataforma más oportunidades de elegir una velocidad de fotogramas en situaciones en las que hay varias plataformas que se ejecutan en a diferentes velocidades de fotogramas. Por ejemplo, imagina una situación en la que dos aplicaciones si se ejecuta en el modo de pantalla dividida en un Pixel 4, en el que una app reproduce un video de 24 Hz y la otra le muestra al usuario una lista desplazable. El Pixel 4 admite dos frecuencias de actualización de la pantalla: 60 Hz y 90 Hz. Con la API de preferredDisplayModeId, la superficie del video selecciona 60 Hz o 90 Hz. Llamando setFrameRate() con 24 Hz, la superficie de video le brinda más a la plataforma información sobre la velocidad de fotogramas del video de origen, lo que permite que la plataforma elige 90 Hz para la frecuencia de actualización de la pantalla, que es mejor que 60 Hz en este situación.

Sin embargo, hay situaciones en las que se debe usar preferredDisplayModeId. en lugar de setFrameRate(), como se muestra a continuación:

  • Si la app quiere cambiar la resolución o alguna otra configuración del modo de visualización, haz lo siguiente: usa preferredDisplayModeId.
  • La plataforma solo cambiará los modos de visualización en respuesta a una llamada a setFrameRate() si el interruptor de modo es ligero y es poco probable que lo sea notorio para el usuario. Si la app prefiere cambiar la actualización de la pantalla incluso si requiere un cambio de modo pesado (por ejemplo, en un dispositivo Android TV dispositivo), usa preferredDisplayModeId.
  • Apps que no pueden controlar la pantalla ejecutándose en un múltiplo del fotograma de la app de presentación, que requiere configurar marcas de tiempo de presentación en cada fotograma, usa preferredDisplayModeId.

setFrameRate() frente a PreferredRefreshRate

WindowManager.LayoutParams#preferredRefreshRate establece una velocidad de fotogramas preferida en la ventana de la app y la velocidad es aplicable a todas las superficies dentro de la ventana. La app debe especificar su preferencia velocidad de fotogramas, independientemente de la frecuencia de actualización que admita el dispositivo, similar a setFrameRate(), para darle al programador una mejor pista de la app velocidad de fotogramas.

preferredRefreshRate se ignora para plataformas que usan setFrameRate(). En usa setFrameRate() si es posible.

PreferredRefreshRate frente a PreferredDisplayModeId

Si las apps solo quieren cambiar la frecuencia de actualización preferida, es preferible usar preferredRefreshRate, en lugar de preferredDisplayModeId.

Cómo evitar llamar a setFrameRate() con demasiada frecuencia

Aunque la llamada a setFrameRate() no es muy costosa en términos de rendimiento, Las apps deben evitar llamar a setFrameRate() cada fotograma o varias veces por por segundo. Es probable que las llamadas a setFrameRate() generen un cambio en el la frecuencia de actualización de la pantalla, lo que puede provocar una caída de fotogramas durante la transición. Debes determinar la velocidad de fotogramas correcta de antemano y llamar setFrameRate() una vez.

Uso para juegos y otras apps que no son de video

Si bien el video es el caso de uso principal de la API de setFrameRate(), se puede para otras apps. Por ejemplo, para un juego cuya ejecución no se debe superar 60 Hz (para reducir el consumo de energía y lograr sesiones de juego más largas) puede llamar Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) En este Por eso, un dispositivo que funciona a 90 Hz de forma predeterminada lo hará a 60 Hz, mientras que el juego está activo, lo que evitará el sacudón que se generaría si el el juego se ejecutó a 60 Hz y la pantalla a 90 Hz.

Uso de FRAME_RATE_COMPATIBILITY_FIXED_SOURCE

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE está diseñado únicamente para apps de video. Para para uso que no sea de video, usa FRAME_RATE_COMPATIBILITY_DEFAULT.

Cómo elegir una estrategia para cambiar la velocidad de fotogramas

  • Recomendamos que las aplicaciones, al mostrar videos de larga duración, como películas, llama a setFrameRate(FPS, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) donde fps es la velocidad de fotogramas del video.
  • Te recomendamos que no uses las apps que llamen a setFrameRate() con CHANGE_FRAME_RATE_ALWAYS cuando esperas que la reproducción de un video dure varios minutos o menos.

Ejemplo de integración para apps de reproducción de video

Te recomendamos que sigas estos pasos para integrar los cambios de frecuencia de actualización en las apps de reproducción de video:

  1. Decide el changeFrameRateStrategy:
    1. Si reproduces un video de larga duración, como una película, usa MATCH_CONTENT_FRAMERATE_ALWAYS.
    2. Si reproduces un video corto, como un avance en movimiento, usa CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS.
  2. Si el changeFrameRateStrategy es CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS , ve al paso 4.
  3. Detecta si está a punto de ocurrir un cambio de frecuencia de actualización no fluido mediante la que los dos hechos son verdaderos:
    1. El cambio de modo fluido no es posible desde la frecuencia de actualización actual (veamos C) a la velocidad de fotogramas del video (queremos V). Si confirmas esta acción, sería el caso si C y V son diferentes y Display.getMode().getAlternativeRefreshRates no contiene un múltiplo de V.
    2. El usuario habilitó los cambios de frecuencia de actualización constantes. Puedes detectar esto verificando si DisplayManager.getMatchContentFrameRateUserPreference muestra MATCH_CONTENT_FRAMERATE_ALWAYS
  4. Si el cambio va a ser fluido, haz lo siguiente:
    1. Llama a setFrameRate y pasarlo fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, y changeFrameRateStrategy, donde fps es la velocidad de fotogramas del video.
    2. Iniciar reproducción de video
  5. Si está por ocurrir un cambio de modo continuo, haz lo siguiente:
    1. Mostrar UX para notificar al usuario Ten en cuenta que recomendamos implementar una forma de que el usuario descarte esta UX y omita el retraso adicional en el paso 5.d. Este es porque el retraso recomendado es mayor que el necesario en pantallas que presentan tiempos de cambio más rápidos.
    2. Llama a setFrameRate y pasarlo fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, y CHANGE_FRAME_RATE_ALWAYS, donde fps es la velocidad de fotogramas del video.
    3. Espera a que aparezca onDisplayChanged. devolución de llamada.
    4. Espera 2 segundos para que se complete el cambio de modo.
    5. Iniciar reproducción de video

El seudocódigo que solo admite el cambio fluido es el siguiente:

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

El seudocódigo que admite el cambio fluido y fluido, como se describió anteriormente, es el siguiente:

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