L'API de fréquence d'images permet aux applications d'informer la plate-forme Android de leur fréquence d'images prévue. Elle est disponible sur les applications qui ciblent Android 11 (niveau d'API 30) ou version ultérieure. Traditionnellement, la plupart des appareils n'acceptaient qu'une seule fréquence d'actualisation de l'écran, généralement 60 Hz, mais cela a changé. De nombreux appareils acceptent désormais des fréquences d'actualisation supplémentaires, telles que 90 Hz ou 120 Hz. Certains appareils sont compatibles avec les changements de fréquence d'actualisation, tandis que d'autres affichent brièvement un écran noir, qui dure généralement une seconde.
L'objectif principal de l'API est de permettre aux applications de mieux exploiter toutes les fréquences d'actualisation d'écran compatibles. Par exemple, une application qui lit une vidéo à 24 Hz qui appelle setFrameRate()
peut entraîner la modification de la fréquence d'actualisation de l'écran de 60 Hz à 120 Hz. Ce nouveau taux d'actualisation permet de lire des vidéos 24 Hz de manière fluide et sans saccades, sans avoir besoin d'un pulldown 3:2, comme cela 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 et de contrôler les surfaces. 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 prendre en compte les fréquences d'actualisation d'écran compatibles réelles, 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 n'est compatible qu'avec la fréquence de 60 Hz, appelez setFrameRate()
avec la fréquence d'images préférée de votre application.
Les appareils qui ne correspondent pas à la fréquence d'images de l'application conservent la fréquence d'actualisation actuelle de l'affichage.
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 d'arrondir à un entier. Par exemple, lors de l'affichage 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 d'indiquer à la plate-forme Android que l'application utilisera le pulldown pour s'adapter à un taux de rafraîchissement d'affichage non correspondant (ce qui entraînera un scintillement).
Dans certains cas, la surface vidéo cesse d'envoyer des images, mais reste visible à l'écran pendant un certain temps. Il peut s'agir, par exemple, de la fin de la vidéo ou de la mise en pause de la lecture. 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 du paramètre de 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 vous supprimez la surface ou qu'elle est masquée parce que l'utilisateur passe à une autre application. N'effacez le paramètre de fréquence d'images que lorsque la surface reste visible sans être utilisée.
Changement dynamique de la fréquence d'images
Sur certains appareils, le changement de la fréquence d'actualisation peut entraîner des interruptions visuelles, comme l'affichage d'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 de faire correspondre la fréquence d'actualisation de l'écran à la fréquence d'images vidéo et d'éviter les artefacts de conversion de fréquence d'images tels que le scintillement de la mise à l'échelle 3:2 pour la lecture de films.
Pour cette raison, les boutons de changement de fréquence d'actualisation non fluides peuvent être activés si l'utilisateur et les applications l'activent:
- Utilisateurs: pour activer cette fonctionnalité, les utilisateurs peuvent activer le paramètre Adapter la fréquence d'images au contenu.
- Applications : pour activer cette fonctionnalité, les applications peuvent transmettre
CHANGE_FRAME_RATE_ALWAYS
àsetFrameRate()
.
Nous vous recommandons de toujours utiliser CHANGE_FRAME_RATE_ALWAYS
pour les vidéos longues, comme les films. En effet, l'avantage de faire correspondre la fréquence d'images vidéo l'emporte sur l'interruption qui se produit lors du changement de la 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 cas où plusieurs surfaces présentent 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, à l'aide du 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 peut arriver que l'appareil ne bascule pas l'écran 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 économiseur 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 définit 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 le fait dans des conditions normales.
Il appartient à l'application de décider comment réagir lorsque la fréquence d'actualisation de l'affichage ne correspond pas à la fréquence d'images de l'application. Pour les vidéos, la fréquence d'images est fixée à celle de la vidéo source. Un défilement vers le bas 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 le taux de trames préféré de l'application, quel que soit le mode de gestion des cas où la plate-forme ne s'ajuste pas à la demande de l'application. De cette façon, si les conditions de l'appareil changent pour permettre l'utilisation de fréquences d'actualisation d'écran 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 s'exécute pas ou 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, à l'aide de 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 un à-coup inutile. 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 l'évitement des à-coups et envisagez d'utiliser la bibliothèque Android Frame Pacing.
Dans certains cas, la plate-forme peut passer à un multiple de la fréquence d'images spécifiée par l'application 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 dispose d'une surface avec un paramètre de fréquence d'images de 24 Hz. Dans ce cas, une fréquence de 120 Hz permet aux surfaces de 60 Hz et de 24 Hz de fonctionner sans qu'il soit nécessaire d'effectuer un défilement.
Lorsque l'écran 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 à-coups 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() par rapport à preferredDisplayModeId
WindowManager.LayoutParams.preferredDisplayModeId
est un autre moyen permettant aux applications d'indiquer leur fréquence d'images à la plate-forme. Certaines applications ne souhaitent modifier que la fréquence d'actualisation de l'affichage plutôt que de modifier d'autres paramètres de mode d'affichage, tels que la résolution de l'écran. En règle générale, utilisez setFrameRate()
au lieu de preferredDisplayModeId
. La fonction setFrameRate()
est plus facile à utiliser, car l'application n'a pas besoin de rechercher dans la liste des modes d'affichage pour trouver un mode avec une fréquence d'images spécifique.
setFrameRate()
offre à la plate-forme plus d'occasions 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. Imaginons, par exemple, que deux applications s'exécutent en mode Écran partagé sur un Pixel 4, l'une diffusant une vidéo à 24 Hz et l'autre affichant une liste à faire défiler. 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 sélectionner 60 Hz ou 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 meilleur que 60 Hz dans ce scénario.
Toutefois, dans certains cas, preferredDisplayModeId
doit être utilisé à la place de setFrameRate()
, par exemple:
- 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 qu'en réponse à un appel à
setFrameRate()
si le changement de mode est léger et peu susceptible d'être remarqué par l'utilisateur. Si l'application préfère modifier la fréquence d'actualisation de l'écran, même si elle nécessite un changement de mode lourd (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() par rapport à preferredRefreshRate
WindowManager.LayoutParams#preferredRefreshRate
définit un taux de rafraîchissement préféré sur la fenêtre de l'application. Ce taux est applicable à toutes les surfaces de la fenêtre. L'application doit spécifier sa fréquence d'images préférée, quelle que soit la fréquence d'actualisation prise en charge par l'appareil, comme pour setFrameRate()
, afin de donner au planificateur une meilleure indication de la fréquence d'images prévue de l'application.
preferredRefreshRate
est ignoré pour les surfaces qui utilisent setFrameRate()
. En règle générale, utilisez setFrameRate()
si possible.
preferredRefreshRate par rapport à preferredDisplayModeId
Si les applications ne souhaitent modifier que 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 de modifier la fréquence d'actualisation de l'affichage, ce qui peut entraîner une perte de frames pendant la transition.
Vous devez déterminer à l'avance la fréquence d'images correcte et appeler setFrameRate()
une seule fois.
Utilisation pour des jeux ou d'autres applications non vidéo
Bien que la vidéo soit le cas d'utilisation principal de l'API setFrameRate()
, elle peut être utilisée pour d'autres applications. Par exemple, un jeu qui ne doit pas fonctionner à plus de 60 Hz (pour réduire la consommation d'énergie et prolonger les sessions de jeu) 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 est actif, ce qui évitera les à-coups 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 autre que vidéo, utilisez FRAME_RATE_COMPATIBILITY_DEFAULT
.
Choisir une stratégie pour modifier la fréquence d'images
- Lorsque des applications affichent des vidéos longues, telles que des films, nous vous recommandons vivement d'appeler
setFrameRate(
fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)
, 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 pensez que la lecture des vidéos durera plusieurs minutes ou moins.
Exemple d'intégration pour les applications de lecture de vidéos
Nous vous recommandons de procéder comme suit pour intégrer des boutons de fréquence d'actualisation dans les applications de lecture vidéo:
- Déterminez
changeFrameRateStrategy
:- Si vous lisez une vidéo longue, comme un film, utilisez
MATCH_CONTENT_FRAMERATE_ALWAYS
. - Si vous lisez une courte vidéo, comme une bande-annonce de film, utilisez
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
.
- Si vous lisez une vidéo longue, comme un film, utilisez
- Si
changeFrameRateStrategy
correspond àCHANGE_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 :
- Le passage en mode fluide n'est pas possible du taux de rafraîchissement actuel (appelons-le C) au taux de trames de la vidéo (appelons-le V). Cela se produira si C et V sont différents et que
Display.getMode().getAlternativeRefreshRates
ne contient pas de multiple de V. - L'utilisateur a activé les modifications du taux de rafraîchissement non fluides. Pour le détecter, vérifiez si
DisplayManager.getMatchContentFrameRateUserPreference
renvoieMATCH_CONTENT_FRAMERATE_ALWAYS
.
- Le passage en mode fluide n'est pas possible du taux de rafraîchissement actuel (appelons-le C) au taux de trames de la vidéo (appelons-le V). Cela se produira si C et V sont différents et que
- Si le transfert est 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 de la vidéo
- Appelez
- Si un changement de mode non fluide est sur le point de se produire, procédez comme suit :
- Affichez l'interface utilisateur pour informer l'utilisateur. Notez que nous vous recommandons d'implémenter un moyen pour l'utilisateur de fermer cette expérience utilisateur et d'ignorer 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 que le changement de mode soit effectué.
- Lancer la lecture de la vidéo
Le pseudo-code permettant de n'autoriser que le changement de profil en douceur 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 transparente et non transparente, 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();
}