L'API Frame Rate consente alle app di comunicare alla piattaforma Android il frame previsto ed è disponibile su app destinate ad Android 11 (livello API 30) o versioni successive. In genere, la maggior parte dei dispositivi supporta una sola frequenza di aggiornamento del display, tipicamente a 60 Hz, ma la situazione è cambiata. Molti dispositivi ora supportano frequenze di aggiornamento aggiuntive, ad esempio 90 Hz o 120 Hz. Alcuni dispositivi supportano una frequenza di aggiornamento continua mentre altri mostrano brevemente uno schermo nero, che in genere dura un secondo.
Lo scopo principale dell'API è consentire alle app di sfruttare meglio tutti
i tassi di aggiornamento del display supportati. Ad esempio, un'app che riproduce un video a 24 Hz
che chiama setFrameRate()
potrebbe causare la modifica della frequenza di aggiornamento
del display da 60 Hz a 120 Hz. Questa nuova frequenza di aggiornamento consente una riproduzione fluida e senza sfarfallio dei video a 24 Hz, senza bisogno del pulldown 3:2 necessario per riprodurre lo stesso video su un display a 60 Hz. Ciò si traduce in un miglioramento dell'esperienza utente
un'esperienza senza intervento manuale.
Utilizzo di base
Android presenta diversi modi per accedere alle piattaforme e controllarle, quindi ci sono
diverse versioni dell'API setFrameRate()
. Ogni versione dell'API accetta gli stessi parametri e funziona come le altre:
Surface.setFrameRate()
SurfaceControl.Transaction.setFrameRate()
ANativeWindow_setFrameRate()
ASurfaceTransaction_setFrameRate()
L'app non deve considerare le frequenze di aggiornamento effettive supportate dal display,
che possono essere ottenute chiamando
Display.getSupportedModes()
,
per chiamare in sicurezza setFrameRate()
. Ad esempio, anche se il dispositivo
supporta 60 Hz, chiama setFrameRate()
con la frequenza fotogrammi preferita dalla tua app.
I dispositivi che non hanno una corrispondenza migliore per la frequenza fotogrammi dell'app rimarranno con la frequenza di aggiornamento del display corrente.
Per verificare se una chiamata a setFrameRate()
determina una modifica dell'aggiornamento della visualizzazione
, registrati per ricevere le notifiche di modifica del display chiamando
DisplayManager.registerDisplayListener()
o AChoreographer_registerRefreshRateCallback()
.
Quando chiami il numero setFrameRate()
, è meglio passare la frequenza fotogrammi esatta anziché
rispetto all'arrotondamento a un numero intero. Ad esempio, quando esegui il rendering di un video registrato
29,97 Hz, passa a 29,97 anziché arrotondare a 30.
Per le app video, il parametro di compatibilità passato a setFrameRate()
deve essere impostato su Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
per fornire un ulteriore suggerimento alla piattaforma Android che l'app utilizzerà il pulldown per adattarsi a una frequenza di aggiornamento del display non corrispondente (con conseguente judder).
In alcuni scenari, la piattaforma video interromperà l'invio dei frame, ma rimarrà
visibile sullo schermo per un po' di tempo. Gli scenari più comuni sono quando la riproduzione
arriva alla fine del video o quando l'utente mette in pausa la riproduzione. In questi casi,
richiama setFrameRate()
con il parametro della frequenza fotogrammi impostato su 0 per cancellare il valore
ripristinare la frequenza fotogrammi al valore predefinito. Cancellare l'impostazione della frequenza frame
in questo modo non è necessario quando la superficie viene distrutta o nascosta perché l'utente passa a un'altra app. Cancella l'impostazione della frequenza frame
solo quando la superficie rimane visibile senza essere utilizzata.
Cambio di frequenza fotogrammi non continuo
Su alcuni dispositivi, il passaggio da una frequenza di aggiornamento all'altra potrebbe comportare interruzioni visive, ad esempio un'oscuramento dello schermo per uno o due secondi. Questo solitamente si verifica su decoder, pannelli TV,
e dispositivi simili. Per impostazione predefinita, il framework Android non cambia modalità
quando Surface.setFrameRate()
viene chiamata l'API per evitare queste interruzioni visive.
Alcuni utenti preferiscono un'interruzione visiva all'inizio alla fine dei video più lunghi. In questo modo, la frequenza di aggiornamento del display corrisponde alla frequenza fotogrammi del video ed è possibile evitare artefatti di conversione della frequenza fotogrammi, come il judder del pulldown 3:2 per la riproduzione di film.
Per questo motivo, è possibile attivare i sensori della frequenza di aggiornamento non continui se sono attivazione di utenti e app:
- Utenti: per attivare questa opzione, gli utenti possono attivare l'opzione Adatta alla frequenza fotogrammi dei contenuti. dell'ambientazione.
- App: per attivarle, le app possono passare
CHANGE_FRAME_RATE_ALWAYS
: insetFrameRate()
.
Ti consigliamo di utilizzare sempre CHANGE_FRAME_RATE_ALWAYS
per i video di lunga durata come i film. Questo perché il vantaggio della corrispondenza
la frequenza fotogrammi del video supera l'interruzione che si verifica quando si cambia il
e la frequenza di aggiornamento.
Altri consigli
Segui questi consigli per gli scenari più comuni.
Più superfici
La piattaforma Android è progettata per gestire correttamente scenari in cui
più piattaforme con impostazioni
di frequenza fotogrammi diverse. Quando la tua app ha più
superfici con frequenze frame diverse, chiama setFrameRate()
con la frequenza frame corretta per ogni superficie. Anche se sul dispositivo sono in esecuzione più app contemporaneamente, utilizzando la modalità schermo diviso o Picture in picture, ogni app può chiamare in sicurezzasetFrameRate()
per le proprie piattaforme.
La piattaforma non cambia la frequenza fotogrammi dell'app
Anche se il dispositivo supporta la frequenza fotogrammi specificata dall'app in una chiamata
setFrameRate()
, ci sono casi in cui il dispositivo non esegue il passaggio del display a
che la frequenza di aggiornamento. Ad esempio, una superficie con priorità più elevata potrebbe avere un'impostazione diversa della frequenza frame o il dispositivo potrebbe essere in modalità Risparmio energetico (impostando una limitazione della frequenza di aggiornamento del display per preservare la batteria). L'app deve comunque funzionare correttamente quando il dispositivo non imposta la frequenza di aggiornamento del display sull'impostazione della frequenza frame dell'app, anche se il dispositivo esegue il passaggio in circostanze normali.
Sta all'app decidere come rispondere al momento della frequenza di aggiornamento del display.
non corrisponde alla frequenza fotogrammi dell'app. Per i video, la frequenza fotogrammi è fissata su quella
il video sorgente e il menu a discesa sarà necessario per mostrare i contenuti del video. R
potrebbe invece scegliere di eseguire un test alla frequenza di aggiornamento del display anziché
con la sua frequenza fotogrammi preferita. L'app non deve modificarne il valore
trasmette a setFrameRate()
in base alle azioni compiute dalla piattaforma. Dovrebbe rimanere impostato
alla frequenza fotogrammi preferita dell'app, indipendentemente dal modo in cui l'app gestisce i casi in cui
la piattaforma non si adatta alla richiesta dell'app. In questo modo, se le condizioni del dispositivo cambiano in modo da consentire l'utilizzo di frequenze di aggiornamento del display aggiuntive, la piattaforma dispone delle informazioni corrette per passare alla frequenza di frame preferita dell'app.
Nei casi in cui l'app non può essere eseguita o non può essere eseguita con la frequenza di aggiornamento del display, devi specificare i timestamp della presentazione per ogni frame, utilizzando uno dei dei meccanismi della piattaforma per impostare timestamp delle presentazioni:
L'uso di questi timestamp impedisce alla piattaforma di presentare anche un frame dell'app in anticipo, il che provocherebbe urti inutili. L'utilizzo corretto dei timestamp di presentazione dei frame è un po' complicato. Per i giochi, consulta la nostra guida alla sincronizzazione dei fotogrammi per ulteriori informazioni su come evitare il tremolio e valuta la possibilità di utilizzare la libreria Android Frame Pacing.
In alcuni casi, la piattaforma può passare a un multiplo della frequenza fotogrammi dell'app
specificato in setFrameRate()
. Ad esempio, un'app potrebbe chiamare setFrameRate()
a 60 Hz e il dispositivo può impostare il display a 120 Hz. Uno dei motivi per cui
succede se un'altra app ha una piattaforma con una frequenza fotogrammi di 24 Hz. In questo caso, l'esecuzione del display a 120 Hz consentirà di utilizzare sia la superficie a 60 Hz sia la superficie a 24 Hz senza necessità di eseguire il pulldown.
Quando il display funziona a un multiplo della frequenza frame dell'app, l'app deve specificare i timestamp di presentazione per ogni frame per evitare tremolii non necessari. Per i giochi, la libreria del pacing del frame Android è utile per impostare i timestamp di presentazione del frame.
setFrameRate() e preferredDisplayModeId
WindowManager.LayoutParams.preferredDisplayModeId
è un altro modo con cui le app possono indicare la frequenza fotogrammi alla piattaforma. Alcune app vogliono modificare solo la frequenza di aggiornamento del display anziché altre impostazioni della modalità di visualizzazione, come la risoluzione del display. In generale, utilizza
setFrameRate()
anziché preferredDisplayModeId
. La funzione setFrameRate()
è più facile da usare perché l'app non deve cercare nell'elenco delle modalità di visualizzazione per trovare una modalità con una frequenza frame specifica.
setFrameRate()
offre alla piattaforma più opportunità di scegliere una frequenza frame compatibile in scenari in cui sono presenti più piattaforme che funzionano a frequenze frame diverse. Ad esempio, considera uno scenario in cui due app vengono
in modalità schermo diviso su Pixel 4, su cui un'app riproduce un video a 24 Hz
e l'altra mostra all'utente un elenco scorrevole. Pixel 4 supporta due
frequenza di aggiornamento del display: 60 Hz e 90 Hz. Se utilizzi l'API preferredDisplayModeId
, la superficie video è costretta a scegliere 60 Hz o 90 Hz. Chiamando
setFrameRate()
a 24 Hz, la superficie video offre alla piattaforma più
informazioni sulla frequenza fotogrammi del video sorgente, consentendo alla piattaforma
scegliere 90 Hz per la frequenza di aggiornamento del display, migliore di 60 Hz
in questo scenario.
Tuttavia, esistono casi in cui è necessario utilizzare preferredDisplayModeId
anziché setFrameRate()
, come ad esempio:
- Se l'app vuole modificare la risoluzione o altre impostazioni della modalità di visualizzazione,
usa
preferredDisplayModeId
. - La piattaforma cambierà le modalità di visualizzazione solo in risposta a una chiamata a
setFrameRate()
se il passaggio di modalità è leggero e difficilmente rilevabile dall'utente. Se l'app preferisce cambiare la frequenza di aggiornamento del display anche se richiede un cambio di modalità pesante (ad esempio su un dispositivo Android TV), utilizzapreferredDisplayModeId
. - App che non possono gestire il display in esecuzione in più frame dell'app
rapida, che richiede l'impostazione dei timestamp della presentazione su ogni frame,
usa
preferredDisplayModeId
.
setFrameRate() e preferredRefreshRate
WindowManager.LayoutParams#preferredRefreshRate
imposta una frequenza fotogrammi preferita nella finestra dell'app e la frequenza è applicabile a tutte le piattaforme all'interno della finestra. L'app deve specificare la sua
frequenza fotogrammi indipendentemente dalle frequenze di aggiornamento supportate dal dispositivo, simile a
setFrameRate()
, per dare allo scheduler un suggerimento migliore sulle esigenze dell'app
frequenza fotogrammi.
preferredRefreshRate
viene ignorato per le piattaforme che utilizzano setFrameRate()
. Nella
uso generico di setFrameRate()
, se possibile.
preferredRefreshRate e preferredDisplayModeId
Se le app vogliono solo modificare la frequenza di aggiornamento preferita, è preferibile utilizzare
preferredRefreshRate
anziché preferredDisplayModeId
.
Evitare di chiamare setFrameRate() troppo spesso
Anche se la chiamata a setFrameRate()
non è molto costosa in termini di prestazioni,
le app dovrebbero evitare di chiamare setFrameRate()
a ogni frame o più volte
secondo. Le chiamate a setFrameRate()
potrebbero comportare una modifica della frequenza di aggiornamento del display, che potrebbe causare una perdita di frame durante la transizione.
Devi calcolare in anticipo la frequenza fotogrammi corretta e chiamare
setFrameRate()
una volta.
Utilizzo di giochi e altre app non video
Sebbene i video siano il caso d'uso principale dell'API setFrameRate()
, possono essere utilizzati anche per altre app. Ad esempio, un gioco che non intende essere eseguito più in alto di
60 Hz (per ridurre il consumo di energia e ottenere sessioni di riproduzione più lunghe) può chiamare
Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
. In questo
un dispositivo che funziona a 90 Hz per impostazione predefinita userà invece a 60 Hz mentre
che il gioco sia attivo, per evitare sbalzi che altrimenti si verificherebbero se
del gioco funzionava a 60 Hz, mentre il display a 90 Hz.
Utilizzo di FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
è destinato solo alle app video. Per
per utilizzo non video, usa FRAME_RATE_COMPATIBILITY_DEFAULT
.
Scelta di una strategia per modificare la frequenza fotogrammi
- Consigliamo vivamente alle app, quando mostrano video di lunga durata come
film, chiamata a
setFrameRate(
f/s, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)
dove f/s è la frequenza fotogrammi del video. - Sconsigliamo vivamente di app che chiamano
setFrameRate()
conCHANGE_FRAME_RATE_ALWAYS
quando si prevede che la riproduzione del video duri diversi minuti o anche meno.
Integrazione di esempio per le app di riproduzione video
Per integrare i pulsanti di attivazione/disattivazione della frequenza di aggiornamento nelle app di riproduzione video, consigliamo i seguenti passaggi:
- Decidi i
changeFrameRateStrategy
:- Se riproduci un video di lunga durata, ad esempio un film, utilizza
MATCH_CONTENT_FRAMERATE_ALWAYS
- Se stai riproducendo un video breve, ad esempio il trailer di un film, utilizza
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
- Se riproduci un video di lunga durata, ad esempio un film, utilizza
- Se
changeFrameRateStrategy
èCHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
, vai al passaggio 4. - Rileva se sta per verificarsi un cambio di frequenza di aggiornamento non continuo controllando
che entrambe le cose siano vere:
- Non è possibile passare dalla frequenza di aggiornamento corrente (chiamiamola C) alla frequenza frame del video (chiamiamola V) in modalità Seamless. Questo accade se C e V sono diversi e
Display.getMode().getAlternativeRefreshRates
non contiene un multiplo di V. - L'utente ha attivato le modifiche alla frequenza di aggiornamento non senza interruzioni. Puoi rilevare questo problema controllando se
DisplayManager.getMatchContentFrameRateUserPreference
restituisceMATCH_CONTENT_FRAMERATE_ALWAYS
- Non è possibile passare dalla frequenza di aggiornamento corrente (chiamiamola C) alla frequenza frame del video (chiamiamola V) in modalità Seamless. Questo accade se C e V sono diversi e
- Se il passaggio sarà senza interruzioni:
- Chiama
setFrameRate
e passafps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
echangeFrameRateStrategy
, dovefps
è la frequenza dei fotogrammi del video. - Avvia riproduzione video
- Chiama
- Se sta per essere effettuato un cambio di modalità senza soluzione di continuità:
- Mostra UX per inviare una notifica all'utente. Ti consigliamo di implementare un metodo all'utente di ignorare questa UX e saltare l'ulteriore ritardo nel passaggio 5.d. Questo accade perché il ritardo consigliato è superiore a quello necessario sui display con tempi di commutazione più rapidi.
- Chiama
setFrameRate
e passafps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
eCHANGE_FRAME_RATE_ALWAYS
, dovefps
è la frequenza dei fotogrammi del video. - Attendi
onDisplayChanged
o la richiamata. - Attendi 2 secondi per il completamento del passaggio di modalità.
- Avvia riproduzione video
Lo pseudo-codice per supportare solo il passaggio senza interruzioni è il seguente:
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
contentFrameRate,
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();
Lo pseudo-codice per supportare il passaggio senza interruzioni e con interruzioni come descritto sopra è il seguente:
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();
}