L'API de fréquence d'images permet aux applications d'informer la plate-forme Android de la fréquence d'images qu'elles souhaitent utiliser. Elle est disponible sur les applications qui ciblent Android 11 (niveau d'API 30) ou version ultérieure. Traditionnellement, la plupart des appareils ne prenaient en charge qu'une seule fréquence d'actualisation de l'écran, généralement 60 Hz, mais cela a changé. De nombreux appareils sont désormais compatibles avec des fréquences d'actualisation supplémentaires, telles que 90 Hz ou 120 Hz. Certains appareils permettent de passer d'une fréquence d'actualisation à une autre de manière fluide, tandis que d'autres affichent brièvement un écran noir, généralement pendant une seconde.
L'objectif principal de l'API est de permettre aux applications de mieux tirer parti de toutes les fréquences d'actualisation d'écran compatibles. Par exemple, une application qui lit une vidéo à 24 Hz et appelle setFrameRate()
peut entraîner le passage de la fréquence d'actualisation de l'écran de 60 Hz à 120 Hz. Cette nouvelle fréquence d'actualisation permet une lecture fluide et sans saccades des vidéos à 24 Hz, sans avoir besoin de la conversion 3:2 qui serait nécessaire pour lire la même vidéo sur un écran à 60 Hz. Cela améliore l'expérience utilisateur.
Utilisation de base
Android propose plusieurs façons d'accéder aux surfaces et de les contrôler. Il existe donc plusieurs versions de l'API setFrameRate()
. Chaque version de l'API utilise les mêmes paramètres et fonctionne de la même manière que les autres :
Surface.setFrameRate()
SurfaceControl.Transaction.setFrameRate()
ANativeWindow_setFrameRate()
ASurfaceTransaction_setFrameRate()
L'application n'a pas besoin de tenir compte des fréquences d'actualisation de l'écran réellement prises en charge, qui peuvent être obtenues en appelant Display.getSupportedModes()
, afin d'appeler setFrameRate()
de manière sécurisée. Par exemple, même si l'appareil ne prend en charge que 60 Hz, appelez setFrameRate()
avec la fréquence d'images préférée de votre application.
Les appareils qui ne trouvent pas de meilleure correspondance pour la fréquence d'images de l'application conserveront la fréquence d'actualisation de l'écran actuelle.
Pour savoir si un appel à setFrameRate()
entraîne une modification de la fréquence d'actualisation de l'écran, enregistrez-vous pour recevoir des notifications de modification de l'écran en appelant DisplayManager.registerDisplayListener()
ou AChoreographer_registerRefreshRateCallback()
.
Lorsque vous appelez setFrameRate()
, il est préférable de transmettre la fréquence d'images exacte plutôt que de l'arrondir à un entier. Par exemple, lors du rendu d'une vidéo enregistrée à 29,97 Hz, transmettez 29,97 au lieu d'arrondir à 30.
Pour les applications vidéo, le paramètre de compatibilité transmis à setFrameRate()
doit être défini sur Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
afin de donner une indication supplémentaire à la plate-forme Android selon laquelle l'application utilisera le pull-down pour s'adapter à une fréquence d'actualisation de l'écran non correspondante (ce qui entraînera des saccades).
Dans certains cas, la surface vidéo cesse d'envoyer des frames, mais reste visible à l'écran pendant un certain temps. Les scénarios courants incluent la fin de la lecture de la vidéo ou la mise en pause de la lecture par l'utilisateur. Dans ce cas, appelez setFrameRate()
avec le paramètre de fréquence d'images défini sur 0 pour rétablir la valeur par défaut de la fréquence d'images de la surface. Il n'est pas nécessaire d'effacer le paramètre de fréquence d'images de cette manière lorsque la surface est détruite ou lorsqu'elle est masquée parce que l'utilisateur passe à une autre application. Effacez le paramètre de fréquence d'images uniquement lorsque la surface reste visible sans être utilisée.
Changement de fréquence d'images non fluide
Sur certains appareils, le changement de fréquence d'actualisation peut entraîner des interruptions visuelles, comme un écran noir pendant une seconde ou deux. Cela se produit généralement sur les boîtiers décodeurs, les écrans de télévision et les appareils similaires. Par défaut, le framework Android ne change pas de mode lorsque l'API Surface.setFrameRate()
est appelée, afin d'éviter de telles interruptions visuelles.
Certains utilisateurs préfèrent une interruption visuelle au début et à la fin des vidéos plus longues. Cela permet à la fréquence d'actualisation de l'écran de correspondre à la fréquence d'images de la vidéo et d'éviter les artefacts de conversion de la fréquence d'images, tels que les saccades de la cadence 3:2 pour la lecture de films.
Pour cette raison, les changements de fréquence d'actualisation non fluides peuvent être activés si l'utilisateur et les applications le souhaitent :
- Utilisateurs : pour activer cette fonctionnalité, les utilisateurs peuvent activer le paramètre Adapter la fréquence d'images du contenu.
- Applications : pour activer les benchmarks, les applications peuvent transmettre
CHANGE_FRAME_RATE_ALWAYS
danssetFrameRate()
.
Nous vous recommandons de toujours utiliser CHANGE_FRAME_RATE_ALWAYS
pour les vidéos de longue durée, comme les films. En effet, l'avantage de faire correspondre la fréquence d'images de la vidéo l'emporte sur l'interruption qui se produit lors du changement de fréquence d'actualisation.
Autres recommandations
Suivez ces recommandations pour les scénarios courants.
Plusieurs surfaces
La plate-forme Android est conçue pour gérer correctement les scénarios dans lesquels plusieurs surfaces ont des paramètres de fréquence d'images différents. Lorsque votre application comporte plusieurs surfaces avec des fréquences d'images différentes, appelez setFrameRate()
avec la fréquence d'images appropriée pour chaque surface. Même si l'appareil exécute plusieurs applications à la fois, en mode Écran partagé ou Picture-in-picture, chaque application peut appeler setFrameRate()
en toute sécurité pour ses propres surfaces.
La plate-forme ne passe pas à la fréquence d'images de l'application
Même si l'appareil est compatible avec la fréquence d'images spécifiée par l'application dans un appel à setFrameRate()
, il arrive que l'appareil ne bascule pas l'affichage sur cette fréquence d'actualisation. Par exemple, une surface de priorité plus élevée peut avoir un paramètre de fréquence d'images différent, ou l'appareil peut être en mode Économie de batterie (ce qui limite la fréquence d'actualisation de l'écran pour préserver la batterie). L'application doit toujours fonctionner correctement lorsque l'appareil ne bascule pas la fréquence d'actualisation de l'écran sur le paramètre de fréquence d'images de l'application, même si l'appareil bascule dans des circonstances normales.
Il appartient à l'application de décider comment réagir lorsque la fréquence d'actualisation de l'écran ne correspond pas à la fréquence d'images de l'application. Pour les vidéos, la fréquence d'images est fixe et correspond à celle de la vidéo source. Un pulldown est nécessaire pour afficher le contenu vidéo. Un jeu peut choisir d'essayer de s'exécuter à la fréquence d'actualisation de l'écran plutôt que de conserver sa fréquence d'images préférée. L'application ne doit pas modifier la valeur qu'elle transmet à setFrameRate()
en fonction de ce que fait la plate-forme. Il doit rester défini sur la fréquence d'images préférée de l'application, quelle que soit la façon dont l'application gère les cas où la plate-forme ne s'adapte pas à la demande de l'application. Ainsi, si les conditions de l'appareil changent pour permettre l'utilisation de fréquences d'actualisation supplémentaires, la plate-forme dispose des informations correctes pour passer à la fréquence d'images préférée de l'application.
Si l'application ne peut pas s'exécuter à la fréquence d'actualisation de l'écran, elle doit spécifier des codes temporels de présentation pour chaque frame, en utilisant l'un des mécanismes de la plate-forme pour définir les codes temporels de présentation :
L'utilisation de ces codes temporels empêche la plate-forme de présenter un frame d'application trop tôt, ce qui entraînerait des saccades inutiles. L'utilisation correcte des codes temporels de présentation des frames est un peu délicate. Pour les jeux, consultez notre guide sur le frame pacing pour en savoir plus sur la façon d'éviter les saccades. Vous pouvez également utiliser la bibliothèque Android Frame Pacing.
Dans certains cas, la plate-forme peut passer à un multiple de la fréquence d'images que l'application a spécifiée dans setFrameRate()
. Par exemple, une application peut appeler setFrameRate()
avec 60 Hz et l'appareil peut passer l'affichage à 120 Hz. Cela peut se produire si une autre application possède une surface avec une fréquence d'images de 24 Hz. Dans ce cas, l'exécution de l'affichage à 120 Hz permettra aux surfaces de 60 Hz et de 24 Hz de s'exécuter sans avoir besoin de pulldown.
Lorsque l'affichage s'exécute à un multiple de la fréquence d'images de l'application, celle-ci doit spécifier des codes temporels de présentation pour chaque frame afin d'éviter les saccades inutiles. Pour les jeux, la bibliothèque Android Frame Pacing est utile pour définir correctement les codes temporels de présentation des frames.
setFrameRate() vs preferredDisplayModeId
WindowManager.LayoutParams.preferredDisplayModeId
est une autre façon pour les applications d'indiquer leur fréquence d'images à la plate-forme. Certaines applications ne souhaitent modifier que la fréquence d'actualisation de l'écran, et non d'autres paramètres du mode d'affichage, comme la résolution. En général, utilisez setFrameRate()
au lieu de preferredDisplayModeId
. La fonction setFrameRate()
est plus facile à utiliser, car l'application n'a pas besoin de parcourir la liste des modes d'affichage pour trouver un mode avec une fréquence d'images spécifique.
setFrameRate()
donne à la plate-forme plus de possibilités de choisir une fréquence d'images compatible dans les scénarios où plusieurs surfaces s'exécutent à des fréquences d'images différentes. Prenons l'exemple de deux applications exécutées en mode Écran partagé sur un Pixel 4, où l'une lit une vidéo à 24 Hz et l'autre affiche une liste déroulante à l'utilisateur. Le Pixel 4 est compatible avec deux fréquences d'actualisation de l'écran : 60 Hz et 90 Hz. À l'aide de l'API preferredDisplayModeId
, la surface vidéo est forcée de choisir entre 60 Hz et 90 Hz. En appelant setFrameRate()
avec 24 Hz, la surface vidéo fournit à la plate-forme plus d'informations sur la fréquence d'images de la vidéo source, ce qui lui permet de choisir 90 Hz pour la fréquence d'actualisation de l'écran, ce qui est mieux que 60 Hz dans ce scénario.
Toutefois, dans certains cas, il est préférable d'utiliser preferredDisplayModeId
plutôt que setFrameRate()
. Voici quelques exemples :
- Si l'application souhaite modifier la résolution ou d'autres paramètres du mode d'affichage, utilisez
preferredDisplayModeId
. - La plate-forme ne change de mode d'affichage en réponse à un appel à
setFrameRate()
que si le changement de mode est léger et peu susceptible d'être perceptible par l'utilisateur. Si l'application préfère modifier la fréquence d'actualisation de l'écran même si cela nécessite un changement de mode important (par exemple, sur un appareil Android TV), utilisezpreferredDisplayModeId
. - Les applications qui ne peuvent pas gérer l'affichage à un multiple de la fréquence d'images de l'application, ce qui nécessite de définir des codes temporels de présentation sur chaque frame, doivent utiliser
preferredDisplayModeId
.
setFrameRate() vs preferredRefreshRate
WindowManager.LayoutParams#preferredRefreshRate
définit une fréquence d'images préférée sur la fenêtre de l'application. Cette fréquence s'applique à toutes les surfaces de la fenêtre. L'application doit spécifier sa fréquence d'images préférée, quelles que soient les fréquences d'actualisation acceptées par l'appareil, comme dans setFrameRate()
, pour donner au planificateur une meilleure indication de la fréquence d'images prévue par l'application.
preferredRefreshRate
est ignoré pour les surfaces qui utilisent setFrameRate()
. En général, utilisez setFrameRate()
si possible.
preferredRefreshRate vs preferredDisplayModeId
Si les applications souhaitent uniquement modifier la fréquence d'actualisation préférée, il est préférable d'utiliser preferredRefreshRate
plutôt que preferredDisplayModeId
.
Éviter d'appeler setFrameRate() trop fréquemment
Bien que l'appel setFrameRate()
ne soit pas très coûteux en termes de performances, les applications doivent éviter d'appeler setFrameRate()
à chaque frame ou plusieurs fois par seconde. Les appels à setFrameRate()
sont susceptibles d'entraîner une modification de la fréquence d'actualisation de l'écran, ce qui peut entraîner une perte d'image pendant la transition.
Vous devez déterminer la fréquence d'images correcte à l'avance et appeler setFrameRate()
une seule fois.
Utilisation pour les jeux ou d'autres applications non vidéo
Bien que la vidéo soit le principal cas d'utilisation de l'API setFrameRate()
, elle peut être utilisée pour d'autres applications. Par exemple, un jeu qui ne doit pas s'exécuter à une fréquence supérieure à 60 Hz (pour réduire la consommation d'énergie et obtenir des sessions de jeu plus longues) peut appeler Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
. Ainsi, un appareil qui fonctionne à 90 Hz par défaut fonctionnera à 60 Hz lorsque le jeu sera actif, ce qui évitera les saccades qui se produiraient si le jeu fonctionnait à 60 Hz alors que l'écran fonctionnait à 90 Hz.
Utilisation de FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
n'est destiné qu'aux applications vidéo. Pour une utilisation non vidéo, utilisez FRAME_RATE_COMPATIBILITY_DEFAULT
.
Choisir une stratégie pour modifier la fréquence d'images
- Nous recommandons vivement aux applications d'appeler
setFrameRate(
fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)
lorsque des vidéos de longue durée, comme des films, sont affichées, où fps correspond à la fréquence d'images de la vidéo. - Nous vous déconseillons vivement d'appeler
setFrameRate()
avecCHANGE_FRAME_RATE_ALWAYS
lorsque vous prévoyez que la lecture vidéo durera plusieurs minutes ou moins.
Exemple d'intégration pour les applications de lecture vidéo
Nous vous recommandons de suivre les étapes suivantes pour intégrer les changements de fréquence d'actualisation dans les applications de lecture vidéo :
- Décidez-vous d'ici le
changeFrameRateStrategy
:- Si vous regardez une longue vidéo, comme un film, utilisez
MATCH_CONTENT_FRAMERATE_ALWAYS
. - Si vous regardez une vidéo courte, comme une bande-annonce de film, utilisez
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
.
- Si vous regardez une longue vidéo, comme un film, utilisez
- Si la valeur
changeFrameRateStrategy
estCHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
, passez à l'étape 4. - Détectez si un changement de fréquence d'actualisation non fluide est sur le point de se produire en vérifiant que les deux conditions suivantes sont remplies :
- Il n'est pas possible de passer du taux d'actualisation actuel (C) à la fréquence d'images de la vidéo (V) de manière fluide. Cela se produit si C et V sont différents et que
Display.getMode().getAlternativeRefreshRates
ne contient pas de multiple de V. - L'utilisateur a activé les changements de fréquence d'actualisation non fluides. Pour le détecter, vérifiez si
DisplayManager.getMatchContentFrameRateUserPreference
renvoieMATCH_CONTENT_FRAMERATE_ALWAYS
.
- Il n'est pas possible de passer du taux d'actualisation actuel (C) à la fréquence d'images de la vidéo (V) de manière fluide. Cela se produit si C et V sont différents et que
- Si la transition doit être fluide, procédez comme suit :
- Appelez
setFrameRate
et transmettez-luifps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
etchangeFrameRateStrategy
, oùfps
correspond à la fréquence d'images de la vidéo. - Lancer la lecture d'une vidéo
- Appelez
- Si un changement de mode non fluide est sur le point de se produire, procédez comme suit :
- Afficher l'UX pour avertir l'utilisateur. Notez que nous vous recommandons d'implémenter un moyen pour l'utilisateur de fermer cette UX et d'éviter le délai supplémentaire à l'étape 5.d. En effet, le délai recommandé est plus long que nécessaire sur les écrans qui affichent des temps de commutation plus rapides.
- Appelez
setFrameRate
et transmettez-luifps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
etCHANGE_FRAME_RATE_ALWAYS
, oùfps
correspond à la fréquence d'images de la vidéo. - Attendez le rappel
onDisplayChanged
. - Patientez deux secondes pour que le changement de mode soit effectif.
- Lancer la lecture d'une vidéo
Le pseudo-code pour uniquement prendre en charge le transfert fluide est le suivant :
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();
Le pseudo-code permettant de prendre en charge la commutation fluide et non fluide, comme décrit ci-dessus, est le suivant :
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();
}