Framerate

Mit der Frame-Rate-API können Apps die Android-Plattform über ihre beabsichtigte Frame-Rate informieren. Sie ist für Apps verfügbar, die auf Android 11 (API-Level 30) oder höher ausgerichtet sind. Bisher haben die meisten Geräte nur eine einzige Aktualisierungsrate unterstützt, in der Regel 60 Hz. Das ändert sich jedoch. Viele Geräte unterstützen jetzt zusätzliche Bildwiederholraten wie 90 Hz oder 120 Hz. Einige Geräte unterstützen nahtlose Aktualisierungsratenwechsel, während auf anderen kurz ein schwarzer Bildschirm angezeigt wird, der in der Regel eine Sekunde lang zu sehen ist.

Der Hauptzweck der API besteht darin, Apps in die Lage zu versetzen, alle unterstützten Aktualisierungsraten des Displays besser zu nutzen. Wenn beispielsweise eine App ein 24‑Hz-Video wiedergibt, das setFrameRate() aufruft, kann es sein, dass das Gerät die Aktualisierungsrate des Displays von 60 Hz auf 120 Hz ändert. Diese neue Aktualisierungsrate ermöglicht eine flüssige, ruckelfreie Wiedergabe von 24‑Hz-Videos. Ein 3:2‑Pulldown, wie er für die Wiedergabe desselben Videos auf einem 60‑Hz-Display erforderlich wäre, ist nicht notwendig. Das führt zu einer besseren Nutzererfahrung.

Grundlegende Nutzung

Android bietet mehrere Möglichkeiten, auf Oberflächen zuzugreifen und sie zu steuern. Daher gibt es mehrere Versionen der setFrameRate() API. Jede Version der API verwendet dieselben Parameter und funktioniert wie die anderen:

Die App muss die tatsächlich unterstützten Aktualisierungsraten des Displays nicht berücksichtigen. Diese können durch Aufrufen von Display.getSupportedModes() abgerufen werden, um setFrameRate() sicher aufzurufen. Auch wenn das Gerät beispielsweise nur 60 Hz unterstützt, rufen Sie setFrameRate() mit der von Ihrer App bevorzugten Bildrate auf. Bei Geräten, die keine bessere Übereinstimmung mit der Framerate der App aufweisen, bleibt die aktuelle Aktualisierungsrate des Displays bestehen.

Wenn Sie prüfen möchten, ob ein Aufruf von setFrameRate() zu einer Änderung der Aktualisierungsrate des Displays führt, registrieren Sie sich für Benachrichtigungen über Displayänderungen, indem Sie DisplayManager.registerDisplayListener() oder AChoreographer_registerRefreshRateCallback() aufrufen.

Beim Aufrufen von setFrameRate() sollten Sie die genaue Framerate übergeben, anstatt auf eine Ganzzahl zu runden. Wenn Sie beispielsweise ein mit 29,97 Hz aufgenommenes Video rendern, geben Sie 29,97 an, anstatt auf 30 aufzurunden.

Bei Video-Apps sollte der an setFrameRate() übergebene Kompatibilitätsparameter auf Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE gesetzt werden, um der Android-Plattform einen zusätzlichen Hinweis darauf zu geben, dass die App Pulldown verwendet, um sich an eine nicht übereinstimmende Aktualisierungsrate des Displays anzupassen (was zu Ruckeln führt).

In einigen Fällen werden keine Frames mehr gesendet, die Videooberfläche bleibt aber noch einige Zeit auf dem Bildschirm sichtbar. Das ist z. B. der Fall, wenn die Wiedergabe das Ende des Videos erreicht oder der Nutzer die Wiedergabe pausiert. In diesen Fällen rufen Sie setFrameRate() mit dem auf 0 gesetzten Parameter für die Framerate auf, um die Framerate-Einstellung der Oberfläche auf den Standardwert zurückzusetzen. Das Zurücksetzen der Framerate-Einstellung ist nicht erforderlich, wenn die Oberfläche zerstört wird oder wenn sie ausgeblendet wird, weil der Nutzer zu einer anderen App wechselt. Setzen Sie die Framerate-Einstellung nur zurück, wenn die Oberfläche sichtbar bleibt, ohne verwendet zu werden.

Nicht nahtloser Wechsel der Framerate

Auf einigen Geräten kann es beim Wechseln der Aktualisierungsrate zu visuellen Unterbrechungen kommen, z. B. zu einem schwarzen Bildschirm für ein oder zwei Sekunden. Das tritt in der Regel bei Set-Top-Boxen, TV-Panels und ähnlichen Geräten auf. Standardmäßig wechselt das Android-Framework nicht den Modus, wenn die Surface.setFrameRate()-API aufgerufen wird, um solche visuellen Unterbrechungen zu vermeiden.

Einige Nutzer bevorzugen eine visuelle Unterbrechung am Anfang und am Ende längerer Videos. So kann die Bildwiederholrate des Displays an die Videobildrate angepasst werden, um Artefakte durch die Bildratenkonvertierung wie 3:2-Pulldown-Ruckeln bei der Filmwiedergabe zu vermeiden.

Aus diesem Grund können nicht nahtlose Aktualisierungsratenwechsel aktiviert werden, wenn sowohl der Nutzer als auch die Apps dies zulassen:

Wir empfehlen, für lange Videos wie Filme immer CHANGE_FRAME_RATE_ALWAYS zu verwenden. Das liegt daran, dass der Vorteil der Anpassung der Videoframerate die Unterbrechung überwiegt, die beim Ändern der Aktualisierungsrate auftritt.

Weitere Empfehlungen

Beachten Sie die folgenden Empfehlungen für häufige Szenarien.

Mehrere Oberflächen

Die Android-Plattform ist so konzipiert, dass sie Szenarien mit mehreren Oberflächen mit unterschiedlichen Einstellungen für die Bildrate korrekt verarbeitet. Wenn Ihre App mehrere Oberflächen mit unterschiedlichen Framerates hat, rufen Sie setFrameRate() mit der richtigen Framerate für jede Oberfläche auf. Auch wenn auf dem Gerät mehrere Apps gleichzeitig ausgeführt werden, z. B. im Splitscreen- oder Bild-im-Bild-Modus, kann jede App setFrameRate() für ihre eigenen Oberflächen aufrufen.

Die Plattform ändert sich nicht an die Framerate der App an

Auch wenn das Gerät die von der App in einem Aufruf von setFrameRate() angegebene Bildrate unterstützt, gibt es Fälle, in denen das Gerät die Anzeige nicht auf diese Aktualisierungsrate umstellt. Beispielsweise kann eine Oberfläche mit höherer Priorität eine andere Einstellung für die Bildrate haben oder das Gerät befindet sich im Energiesparmodus, wodurch die Aktualisierungsrate des Displays begrenzt wird, um den Akku zu schonen. Die App muss auch dann korrekt funktionieren, wenn das Gerät die Aktualisierungsrate des Displays nicht an die Framerate-Einstellung der App anpasst, auch wenn das Gerät unter normalen Umständen die Aktualisierungsrate anpasst.

Es liegt an der App, wie sie reagiert, wenn die Aktualisierungsrate des Displays nicht mit der Framerate der App übereinstimmt. Bei Videos ist die Framerate auf die des Quellvideos festgelegt. Für die Wiedergabe der Videoinhalte ist ein Pulldown erforderlich. Ein Spiel kann stattdessen versuchen, mit der Aktualisierungsrate des Displays zu laufen, anstatt bei seiner bevorzugten Framerate zu bleiben. Die App sollte den Wert, den sie an setFrameRate() übergibt, nicht basierend auf dem Verhalten der Plattform ändern. Sie sollte auf die bevorzugte Framerate der App eingestellt bleiben, unabhängig davon, wie die App Fälle behandelt, in denen die Plattform die Anfrage der App nicht berücksichtigt. Wenn sich die Gerätebedingungen ändern, sodass zusätzliche Aktualisierungsraten für das Display verwendet werden können, hat die Plattform so die richtigen Informationen, um zur bevorzugten Framerate der App zu wechseln.

Wenn die App nicht mit der Aktualisierungsrate des Displays ausgeführt werden kann, sollte sie für jeden Frame Präsentationszeitstempel angeben. Dazu kann sie einen der folgenden Plattformmechanismen verwenden:

Durch die Verwendung dieser Zeitstempel wird verhindert, dass die Plattform einen App-Frame zu früh präsentiert, was zu unnötigen Rucklern führen würde. Die korrekte Verwendung von Zeitstempeln für die Frame-Präsentation ist etwas kompliziert. Weitere Informationen zum Vermeiden von Ruckeln bei Spielen finden Sie in unserem Leitfaden zum Frame Pacing. Erwägen Sie außerdem die Verwendung der Android Frame Pacing-Bibliothek.

In einigen Fällen wechselt die Plattform möglicherweise zu einem Vielfachen der Framerate, die in setFrameRate() angegeben ist. Beispiel: Eine App ruft setFrameRate() mit 60 Hz auf und das Gerät schaltet das Display auf 120 Hz um. Ein möglicher Grund dafür ist, dass eine andere App eine Oberfläche mit einer Bildrate von 24 Hz hat. In diesem Fall können sowohl die 60‑Hz- als auch die 24‑Hz-Oberfläche ohne Pulldown auf dem Display mit 120 Hz wiedergegeben werden.

Wenn das Display mit einem Vielfachen der Framerate der App läuft, sollte die App für jeden Frame Präsentationszeitstempel angeben, um unnötige Ruckler zu vermeiden. Für Spiele ist die Android Frame Pacing-Bibliothek hilfreich, um die Zeitstempel für die Frame-Präsentation richtig festzulegen.

setFrameRate() im Vergleich zu preferredDisplayModeId

WindowManager.LayoutParams.preferredDisplayModeId ist eine weitere Möglichkeit für Apps, der Plattform ihre Framerate mitzuteilen. Einige Apps möchten nur die Aktualisierungsrate des Displays ändern, nicht aber andere Einstellungen des Anzeigemodus wie die Displayauflösung. Verwenden Sie im Allgemeinen setFrameRate() anstelle von preferredDisplayModeId. Die setFrameRate()-Funktion ist einfacher zu verwenden, da die App nicht die Liste der Anzeigemodi durchsuchen muss, um einen Modus mit einer bestimmten Bildrate zu finden.

setFrameRate() bietet der Plattform mehr Möglichkeiten, eine kompatible Framerate in Szenarien auszuwählen, in denen mehrere Oberflächen mit unterschiedlichen Frameraten ausgeführt werden. Angenommen, auf einem Pixel 4 werden zwei Apps im Splitscreen-Modus ausgeführt. In der einen App wird ein Video mit 24 Hz wiedergegeben und in der anderen wird dem Nutzer eine scrollbare Liste angezeigt. Das Pixel 4 unterstützt zwei Aktualisierungsraten für das Display: 60 Hz und 90 Hz. Bei Verwendung der preferredDisplayModeId API wird die Videooberfläche gezwungen, entweder 60 Hz oder 90 Hz auszuwählen. Durch Aufrufen von setFrameRate() mit 24 Hz erhält die Plattform über die Videooberfläche mehr Informationen zur Bildrate des Quellvideos. So kann die Plattform für die Aktualisierungsrate des Displays 90 Hz auswählen, was in diesem Szenario besser als 60 Hz ist.

Es gibt jedoch Szenarien, in denen preferredDisplayModeId anstelle von setFrameRate() verwendet werden sollte, z. B. in den folgenden Fällen:

  • Wenn die App die Auflösung oder andere Einstellungen für den Anzeigemodus ändern möchte, verwenden Sie preferredDisplayModeId.
  • Die Plattform wechselt nur als Reaktion auf einen Aufruf von setFrameRate() den Anzeigemodus, wenn der Moduswechsel schnell und für den Nutzer wahrscheinlich nicht wahrnehmbar ist. Wenn die App die Aktualisierungsrate des Displays auch dann lieber ändern möchte, wenn ein schwerwiegender Moduswechsel erforderlich ist (z. B. auf einem Android TV-Gerät), verwenden Sie preferredDisplayModeId.
  • Apps, die nicht mit der Anzeige bei einem Vielfachen der Framerate der App umgehen können, was das Festlegen von Präsentationszeitstempeln für jeden Frame erfordert, sollten preferredDisplayModeId verwenden.

setFrameRate() im Vergleich zu preferredRefreshRate

Mit WindowManager.LayoutParams#preferredRefreshRate wird eine bevorzugte Framerate für das Fenster der App festgelegt. Die Rate gilt für alle Oberflächen im Fenster. Die App sollte ihre bevorzugte Framerate unabhängig von den unterstützten Aktualisierungsraten des Geräts angeben, ähnlich wie setFrameRate(), damit der Scheduler einen besseren Hinweis auf die beabsichtigte Framerate der App erhält.

preferredRefreshRate wird für Plattformen ignoriert, die setFrameRate() verwenden. Verwenden Sie nach Möglichkeit immer setFrameRate().

preferredRefreshRate im Vergleich zu preferredDisplayModeId

Wenn Apps nur die bevorzugte Aktualisierungsrate ändern möchten, sollte preferredRefreshRate anstelle von preferredDisplayModeId verwendet werden.

setFrameRate() nicht zu häufig aufrufen

Obwohl der setFrameRate()-Aufruf in Bezug auf die Leistung nicht sehr kostspielig ist, sollten Apps ihn nicht bei jedem Frame oder mehrmals pro Sekunde aufrufen.setFrameRate() Aufrufe von setFrameRate() führen wahrscheinlich zu einer Änderung der Aktualisierungsrate des Displays, was während des Übergangs zu einem Frame-Drop führen kann. Sie sollten die richtige Framerate im Voraus ermitteln und setFrameRate() einmal aufrufen.

Verwendung für Spiele oder andere Apps, die keine Videos enthalten

Video ist zwar der primäre Anwendungsfall für die setFrameRate() API, sie kann aber auch für andere Apps verwendet werden. Ein Spiel, das nicht mit mehr als 60 Hz ausgeführt werden soll, um den Stromverbrauch zu senken und längere Spielsitzungen zu ermöglichen, kann beispielsweise Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) aufrufen. Auf diese Weise wird ein Gerät, das standardmäßig mit 90 Hz läuft, während des Spiels mit 60 Hz betrieben. So wird das Ruckeln vermieden, das sonst auftreten würde, wenn das Spiel mit 60 Hz und das Display mit 90 Hz laufen.

Verwendung von FRAME_RATE_COMPATIBILITY_FIXED_SOURCE

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE ist nur für Video-Apps vorgesehen. Verwenden Sie für die nicht videobezogene Nutzung FRAME_RATE_COMPATIBILITY_DEFAULT.

Strategie zum Ändern der Framerate auswählen

  • Wir empfehlen dringend, dass Apps beim Anzeigen von Videos mit langer Laufzeit, z. B. Filmen, setFrameRate(fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) aufrufen, wobei „fps“ die Framerate des Videos ist.
  • Wir raten dringend davon ab, dass Apps setFrameRate() mit CHANGE_FRAME_RATE_ALWAYS aufrufen, wenn die Videowiedergabe voraussichtlich mehrere Minuten oder weniger dauert.

Beispiel für die Integration von Apps für die Videowiedergabe

Wir empfehlen die folgenden Schritte für die Integration von Aktualisierungsratenschaltern in Videowiedergabe-Apps:

  1. Entscheiden Sie sich für die changeFrameRateStrategy:
    1. Wenn Sie ein langes Video wie einen Film abspielen, verwenden Sie MATCH_CONTENT_FRAMERATE_ALWAYS.
    2. Wenn Sie ein kurzes Video wie einen Filmtrailer abspielen, verwenden Sie CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS.
  2. Wenn changeFrameRateStrategy CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS ist, fahren Sie mit Schritt 4 fort.
  3. Sie können erkennen, ob ein nicht nahtloser Wechsel der Aktualisierungsrate bevorsteht, indem Sie prüfen, ob beide der folgenden Bedingungen erfüllt sind:
    1. Ein nahtloser Moduswechsel ist von der aktuellen Aktualisierungsrate (C) zur Framerate des Videos (V) nicht möglich. Das ist der Fall, wenn C und V unterschiedlich sind und Display.getMode().getAlternativeRefreshRates kein Vielfaches von V enthält.
    2. Der Nutzer hat sich für nicht nahtlose Änderungen der Aktualisierungsrate entschieden. Sie können dies erkennen, indem Sie prüfen, ob DisplayManager.getMatchContentFrameRateUserPreference MATCH_CONTENT_FRAMERATE_ALWAYS zurückgibt.
  4. Wenn der Wechsel nahtlos erfolgen soll, gehen Sie so vor:
    1. Rufen Sie setFrameRate auf und übergeben Sie fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE und changeFrameRateStrategy. Dabei ist fps die Framerate des Videos.
    2. Videowiedergabe starten
  5. Wenn ein nicht nahtloser Moduswechsel bevorsteht, gehen Sie so vor:
    1. UX anzeigen, um den Nutzer zu benachrichtigen Wir empfehlen, dass Sie dem Nutzer die Möglichkeit geben, diese Benutzeroberfläche zu schließen und die zusätzliche Verzögerung in Schritt 5.d zu überspringen. Das liegt daran, dass die von uns empfohlene Verzögerung bei Displays mit schnelleren Schaltzeiten größer als nötig ist.
    2. Rufen Sie setFrameRate auf und übergeben Sie fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE und CHANGE_FRAME_RATE_ALWAYS, wobei fps die Framerate des Videos ist.
    3. Warten Sie auf den onDisplayChanged-Callback.
    4. Warte 2 Sekunden, bis der Moduswechsel abgeschlossen ist.
    5. Videowiedergabe starten

Der Pseudocode für die ausschließliche Unterstützung des nahtlosen Wechsels lautet wie folgt:

SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
transaction.setFrameRate(surfaceControl,
    contentFrameRate,
    FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
    CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS);
transaction.apply();
beginPlayback();

Der Pseudocode für den nahtlosen und nicht nahtlosen Wechsel wie oben beschrieben lautet so:

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();
}