Frequenza fotogrammi

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:

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:

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 asetFrameRate() 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), utilizza preferredDisplayModeId.
  • 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() con CHANGE_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:

  1. Decidi i changeFrameRateStrategy:
    1. Se riproduci un video di lunga durata, ad esempio un film, utilizza MATCH_CONTENT_FRAMERATE_ALWAYS
    2. Se stai riproducendo un video breve, ad esempio il trailer di un film, utilizza CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
  2. Se changeFrameRateStrategy è CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS , vai al passaggio 4.
  3. Rileva se sta per verificarsi un cambio di frequenza di aggiornamento non continuo controllando che entrambe le cose siano vere:
    1. 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.
    2. L'utente ha attivato le modifiche alla frequenza di aggiornamento non senza interruzioni. Puoi rilevare questo problema controllando se DisplayManager.getMatchContentFrameRateUserPreference restituisce MATCH_CONTENT_FRAMERATE_ALWAYS
  4. Se il passaggio sarà senza interruzioni:
    1. Chiama setFrameRate e passa fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE e changeFrameRateStrategy, dove fps è la frequenza dei fotogrammi del video.
    2. Avvia riproduzione video
  5. Se sta per essere effettuato un cambio di modalità senza soluzione di continuità:
    1. 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.
    2. Chiama setFrameRate e passa fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE e CHANGE_FRAME_RATE_ALWAYS, dove fps è la frequenza dei fotogrammi del video.
    3. Attendi onDisplayChanged o la richiamata.
    4. Attendi 2 secondi per il completamento del passaggio di modalità.
    5. 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();
}