L'API Frame Rate consente alle app di comunicare alla piattaforma Android il frame rate previsto ed è disponibile nelle app che hanno come target Android 11 (livello API 30) o versioni successive. Tradizionalmente, la maggior parte dei dispositivi supporta una sola frequenza di aggiornamento del display, in genere 60 Hz, ma la situazione sta cambiando. Molti dispositivi ora supportano frequenze di aggiornamento aggiuntive, come 90 Hz o 120 Hz. Alcuni dispositivi supportano il cambio della frequenza di aggiornamento senza interruzioni, mentre altri mostrano brevemente una schermata nera, di solito per un secondo.
Lo scopo principale dell'API è consentire alle app di sfruttare al meglio tutte
le frequenze di aggiornamento del display supportate. Ad esempio, un'app che riproduce un video a 24 Hz
che chiama setFrameRate()
potrebbe comportare 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 scatti dei video a 24 Hz, senza la necessità di un pulldown 3:2 come sarebbe necessario per riprodurre lo stesso video su un display a 60 Hz. Ciò si traduce in una migliore esperienza utente.
Utilizzo di base
Android espone diversi modi per accedere e controllare le superfici, quindi esistono
diverse versioni dell'API setFrameRate()
. Ogni versione dell'API accetta gli stessi parametri e funziona allo stesso modo delle altre:
Surface.setFrameRate()
SurfaceControl.Transaction.setFrameRate()
ANativeWindow_setFrameRate()
ASurfaceTransaction_setFrameRate()
L'app non deve considerare le frequenze di aggiornamento del display supportate effettive,
che possono essere ottenute chiamando
Display.getSupportedModes()
,
per chiamare in sicurezza setFrameRate()
. Ad esempio, anche se il dispositivo supporta solo 60 Hz, chiama setFrameRate()
con il frame rate preferito dalla tua app.
I dispositivi che non hanno una corrispondenza migliore per la frequenza fotogrammi dell'app manterranno
l'attuale frequenza di aggiornamento del display.
Per verificare se una chiamata a setFrameRate()
comporta una modifica della frequenza di aggiornamento del display, registrati per ricevere le notifiche di modifica del display chiamando
DisplayManager.registerDisplayListener()
o AChoreographer_registerRefreshRateCallback()
.
Quando chiami setFrameRate()
, è meglio passare la frequenza dei fotogrammi esatta
anziché arrotondarla a un numero intero. Ad esempio, durante il rendering di un video registrato a
29,97 Hz, inserisci 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 suggerimento aggiuntivo alla
piattaforma Android che l'app utilizzerà il pull-down per adattarsi a una frequenza di aggiornamento
del display non corrispondente (che comporterà un effetto judder).
In alcuni scenari, la superficie video smetterà di inviare frame, ma rimarrà
visibile sullo schermo per un po' di tempo. Gli scenari comuni includono la riproduzione
che raggiunge la fine del video o quando l'utente mette in pausa la riproduzione. In questi casi,
chiama setFrameRate()
con il parametro frame rate impostato su 0 per ripristinare
l'impostazione del frame rate della superficie al valore predefinito. La cancellazione dell'impostazione del frame rate
in questo modo non è necessaria quando la superficie viene eliminata o quando la superficie è
nascosta perché l'utente passa a un'altra app. Cancella l'impostazione
del frame rate solo quando la superficie rimane visibile senza essere utilizzata.
Cambio della frequenza fotogrammi non fluido
Su alcuni dispositivi, il cambio della frequenza di aggiornamento potrebbe causare interruzioni visive, ad esempio una schermata nera per uno o due secondi. Ciò si verifica in genere su set-top box, pannelli TV
e dispositivi simili. Per impostazione predefinita, il framework Android non cambia modalità quando viene chiamata l'API Surface.setFrameRate()
, per evitare interruzioni visive.
Alcuni utenti preferiscono un'interruzione visiva all'inizio e alla fine dei video più lunghi. In questo modo, la frequenza di aggiornamento del display corrisponde alla frequenza dei fotogrammi del video ed evita artefatti di conversione della frequenza dei fotogrammi, come il judder di pulldown 3:2 per la riproduzione di film.
Per questo motivo, i cambi di frequenza di aggiornamento non fluidi possono essere abilitati se sia l'utente che le app acconsentono:
- Utenti: per attivare questa funzionalità, gli utenti possono abilitare l'impostazione utente Corrispondenza frame rate dei contenuti.
- App: per attivare la funzionalità, 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 di abbinare
la frequenza dei fotogrammi del video supera l'interruzione che si verifica quando si modifica la
frequenza di aggiornamento.
Altri consigli
Segui questi consigli per gli scenari comuni.
Più superfici
La piattaforma Android è progettata per gestire correttamente gli scenari in cui sono presenti
più superfici con impostazioni di frame rate diverse. Quando la tua app ha più
superfici con frame rate diversi, chiama setFrameRate()
con il
frame rate corretto per ogni superficie. Anche se il dispositivo esegue più app contemporaneamente, utilizzando la modalità schermo diviso o Picture in picture, ogni app può chiamare in sicurezza
setFrameRate()
per le proprie piattaforme.
La piattaforma non passa alla frequenza fotogrammi dell'app
Anche se il dispositivo supporta il frame rate specificato dall'app in una chiamata a
setFrameRate()
, in alcuni casi il dispositivo non imposta la frequenza di aggiornamento
del display. Ad esempio, una superficie con priorità più alta potrebbe avere un'impostazione
di frame rate diversa oppure il dispositivo potrebbe essere in modalità Risparmio energetico (impostando
una limitazione alla frequenza di aggiornamento del display per risparmiare batteria). L'app deve comunque
funzionare correttamente quando il dispositivo non imposta la frequenza di aggiornamento del display sull'impostazione della frequenza dei fotogrammi dell'app, anche se il dispositivo lo fa in circostanze normali.
Spetta all'app decidere come rispondere quando la frequenza di aggiornamento del display
non corrisponde alla frequenza fotogrammi dell'app. Per i video, la frequenza fotogrammi è fissa a quella del video sorgente e sarà necessario il pulldown per mostrare i contenuti video. Un gioco potrebbe invece scegliere di provare a essere eseguito alla frequenza di aggiornamento del display anziché mantenere la frequenza fotogrammi preferita. L'app non deve modificare il valore che
trasmette a setFrameRate()
in base alle azioni della piattaforma. Deve rimanere impostato
sulla frequenza dei fotogrammi preferita dell'app, indipendentemente da come l'app gestisce i casi in cui
la piattaforma non si adegua alla richiesta dell'app. In questo modo, se le condizioni del dispositivo cambiano per consentire l'utilizzo di frequenze di aggiornamento del display aggiuntive, la piattaforma dispone delle informazioni corrette per passare alla frequenza dei fotogrammi preferita dell'app.
Nei casi in cui l'app non viene eseguita o non può essere eseguita alla frequenza di aggiornamento del display, l'app deve specificare i timestamp di presentazione per ogni frame, utilizzando uno dei meccanismi della piattaforma per impostare i timestamp di presentazione:
L'utilizzo di questi timestamp impedisce alla piattaforma di presentare un frame dell'app troppo presto, il che comporterebbe un judder non necessario. L'utilizzo corretto dei timestamp di presentazione dei frame è un po' complicato. Per i giochi, consulta la nostra guida al frame pacing per saperne di più su come evitare il judder e valuta la possibilità di utilizzare la libreria Android Frame Pacing.
In alcuni casi, la piattaforma potrebbe passare a un multiplo del frame rate specificato dall'app in setFrameRate()
. Ad esempio, un'app potrebbe chiamare setFrameRate()
con 60 Hz e il dispositivo potrebbe impostare il display a 120 Hz. Uno dei motivi per cui ciò potrebbe
accadere è che un'altra app ha una superficie con un'impostazione della frequenza dei fotogrammi di 24 Hz. In
questo caso, l'esecuzione del display a 120 Hz consentirà sia alla superficie a 60 Hz sia
a quella a 24 Hz di funzionare senza pulldown.
Quando la visualizzazione viene eseguita a un multiplo del frame rate dell'app, l'app deve specificare i timestamp di presentazione per ogni frame per evitare un judder non necessario. Per i giochi, la libreria Android Frame Pacing è utile per impostare correttamente i timestamp di presentazione dei frame.
setFrameRate() e preferredDisplayModeId
WindowManager.LayoutParams.preferredDisplayModeId
è un altro modo in cui le app possono indicare la frequenza dei fotogrammi alla piattaforma. Alcune
app vogliono solo modificare la frequenza di aggiornamento del display anziché altre
impostazioni della modalità di visualizzazione, come la risoluzione del display. In generale, utilizza
setFrameRate()
al posto di 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 dei fotogrammi specifica.
setFrameRate()
offre alla piattaforma maggiori opportunità di scegliere una frequenza dei fotogrammi compatibile
in scenari in cui sono presenti più piattaforme che vengono eseguite a
frequenze dei fotogrammi diverse. Ad esempio, considera uno scenario in cui due app
vengono eseguite in modalità schermo diviso su Pixel 4, una riproduce un video a 24 Hz
e l'altra mostra all'utente un elenco scorrevole. Pixel 4 supporta due
frequenze di aggiornamento del display: 60 Hz e 90 Hz. Utilizzando l'API preferredDisplayModeId
,
la superficie video è costretta a scegliere 60 Hz o 90 Hz. Chiamando
setFrameRate()
con 24 Hz, la superficie video fornisce alla piattaforma maggiori
informazioni sul frame rate del video sorgente, consentendole di
scegliere 90 Hz per la frequenza di aggiornamento del display, che è migliore di 60 Hz in questo
scenario.
Tuttavia, esistono scenari in cui è necessario utilizzare preferredDisplayModeId
anziché setFrameRate()
, ad esempio i seguenti:
- Se l'app vuole modificare la risoluzione o altre impostazioni della modalità di visualizzazione,
usa
preferredDisplayModeId
. - La piattaforma cambierà modalità di visualizzazione solo in risposta a una chiamata a
setFrameRate()
se il cambio di modalità è leggero e difficilmente percepibile 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
. - Le app che non riescono a gestire la visualizzazione a un multiplo del frame
rate dell'app, il che richiede l'impostazione dei timestamp di presentazione su ogni frame, devono
utilizzare
preferredDisplayModeId
.
setFrameRate() e preferredRefreshRate
WindowManager.LayoutParams#preferredRefreshRate
imposta una frequenza dei fotogrammi preferita nella finestra dell'app e la frequenza è applicabile
a tutte le superfici all'interno della finestra. L'app deve specificare la frequenza dei fotogrammi preferita
indipendentemente dalle frequenze di aggiornamento supportate dal dispositivo, in modo simile a
setFrameRate()
, per fornire allo scheduler un suggerimento migliore sulla frequenza dei fotogrammi
prevista dall'app.
preferredRefreshRate
viene ignorato per le piattaforme che utilizzano setFrameRate()
. In
generale, utilizza setFrameRate()
, se possibile.
preferredRefreshRate vs preferredDisplayModeId
Se le app vogliono modificare solo la frequenza di aggiornamento preferita, è preferibile utilizzare
preferredRefreshRate
anziché preferredDisplayModeId
.
Evitare di chiamare setFrameRate() troppo spesso
Sebbene la chiamata setFrameRate()
non sia molto costosa in termini di prestazioni,
le app devono evitare di chiamare setFrameRate()
ogni frame o più volte al
secondo. Le chiamate a setFrameRate()
probabilmente comporteranno una modifica della
frequenza di aggiornamento del display, che potrebbe causare un calo di frame durante la transizione.
Dovresti calcolare in anticipo il frame rate corretto e chiamare
setFrameRate()
una sola volta.
Utilizzo per giochi o altre app non video
Sebbene il video sia il caso d'uso principale dell'API setFrameRate()
, può essere
utilizzata per altre app. Ad esempio, un gioco che non intende superare i 60 Hz (per ridurre il consumo energetico e ottenere sessioni di gioco più lunghe) può chiamare Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
. In questo
modo, un dispositivo che funziona a 90 Hz per impostazione predefinita funzionerà invece a 60 Hz mentre
il gioco è attivo, il che eviterà il judder che si verificherebbe altrimenti se il
gioco funzionasse a 60 Hz mentre il display funziona a 90 Hz.
Utilizzo di FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
è destinato solo alle app video. Per
l'utilizzo non video, utilizza FRAME_RATE_COMPATIBILITY_DEFAULT
.
Scegliere una strategia per modificare la frequenza fotogrammi
- Consigliamo vivamente alle app, quando visualizzano video di lunga durata come
film, di chiamare
setFrameRate(
fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS)
dove fps è il frame rate del video. - Sconsigliamo vivamente alle app di chiamare
setFrameRate()
conCHANGE_FRAME_RATE_ALWAYS
quando prevedi che la riproduzione video duri diversi minuti o meno.
Esempio di integrazione per app di riproduzione video
Ti consigliamo di seguire questi passaggi per integrare i cambi di frequenza di aggiornamento nelle app di riproduzione video:
- Decidi il
changeFrameRateStrategy
:- Se riproduci un video di lunga durata, ad esempio un film, utilizza
MATCH_CONTENT_FRAMERATE_ALWAYS
. - Se riproduci 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 uniforme controllando
che entrambi i seguenti fatti siano veri:
- Il cambio di modalità senza interruzioni non è possibile dalla frequenza di aggiornamento attuale (chiamiamola C) alla frequenza dei fotogrammi del video (chiamiamola V). Ciò si verifica se C e V sono diversi e
Display.getMode().getAlternativeRefreshRates
non contiene un multiplo di V. - L'utente ha attivato le modifiche non fluide della frequenza di aggiornamento. Puoi rilevarlo
controllando se
DisplayManager.getMatchContentFrameRateUserPreference
restituisceMATCH_CONTENT_FRAMERATE_ALWAYS
- Il cambio di modalità senza interruzioni non è possibile dalla frequenza di aggiornamento attuale (chiamiamola C) alla frequenza dei fotogrammi del video (chiamiamola V). Ciò si verifica se C e V sono diversi e
- Se il passaggio deve essere senza interruzioni, procedi nel seguente modo:
- Chiama
setFrameRate
e passalo afps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
, echangeFrameRateStrategy
, dovefps
è il frame rate del video. - Avvia la riproduzione video
- Chiama
- Se sta per verificarsi un cambio di modalità non continuo, procedi nel seguente modo:
- Mostra l'UX per notificare l'utente. Tieni presente che ti consigliamo di implementare un modo per consentire all'utente di chiudere questa UX e saltare l'ulteriore ritardo nel passaggio 5.d. Questo perché il ritardo consigliato è maggiore del necessario sui display che mostrano tempi di commutazione più rapidi.
- Chiama
setFrameRate
e passalo afps
,FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
, eCHANGE_FRAME_RATE_ALWAYS
, dovefps
è il frame rate del video. - Attendi la richiamata
di
onDisplayChanged
. - Attendi 2 secondi per il completamento del cambio di modalità.
- Avvia la riproduzione video
Lo pseudocodice per supportare solo il cambio di rete 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 cambio fluido e non fluido 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();
}