Framerate

Mit der Framerate API können Apps die Android-Plattform über die beabsichtigte Framerate informieren. Sie ist in Apps verfügbar, die auf Android 11 (API-Level 30) oder höher ausgerichtet sind. Bisher unterstützten die meisten Geräte nur eine einzige Displayaktualisierungsrate, in der Regel 60 Hz. Das ändert sich jedoch. Viele Geräte unterstützen jetzt zusätzliche Aktualisierungsraten wie 90 Hz oder 120 Hz. Einige Geräte unterstützen nahtlose Umstellungen der Bildwiederholrate, während bei anderen kurz ein schwarzer Bildschirm angezeigt wird, der in der Regel eine Sekunde lang anhält.

Der Hauptzweck der API besteht darin, Apps die Möglichkeit zu geben, alle unterstützten Displaywiederholraten besser zu nutzen. Wenn beispielsweise eine App ein 24‑Hz-Video wiedergibt, das setFrameRate() aufruft, kann das Gerät die Displayaktualisierungsrate von 60 Hz auf 120 Hz ändern. Diese neue Bildwiederholrate ermöglicht eine flüssige, ruckelfreie Wiedergabe von 24‑Hz-Videos, ohne dass ein 3:2-Pulldown erforderlich ist, wie es für die Wiedergabe desselben Videos auf einem 60‑Hz-Display erforderlich wäre. Das führt zu einer besseren Nutzerfreundlichkeit.

Grundlegende Verwendung

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 genauso wie die anderen:

Die App muss die tatsächlich unterstützten Displayaktualisierungsraten nicht berücksichtigen. Sie können diese durch Aufrufen von Display.getSupportedModes() abrufen, um setFrameRate() sicher aufzurufen. Auch wenn das Gerät nur 60 Hz unterstützt, rufe setFrameRate() mit der Framerate auf, die für deine App bevorzugt wird. Geräte, die nicht besser an die Framerate der App angepasst sind, behalten die aktuelle Aktualisierungsrate des Displays bei.

Wenn Sie wissen möchten, ob ein Aufruf von setFrameRate() zu einer Änderung der Bildwiederholrate führt, registrieren Sie sich für Benachrichtigungen zu Displayänderungen. Rufen Sie dazu DisplayManager.registerDisplayListener() oder AChoreographer_registerRefreshRateCallback() auf.

Wenn du setFrameRate() aufrufst, solltest du die genaue Framerate angeben, anstatt sie auf eine Ganzzahl zu runden. Wenn Sie beispielsweise ein Video mit 29,97 Hz 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 festgelegt werden, um der Android-Plattform einen zusätzlichen Hinweis darauf zu geben, dass die App sich über das Pulldown-Menü an eine nicht übereinstimmende Displayaktualisierungsrate anpassen wird, was zu Rucklern führt.

In einigen Fällen sendet die Videooberfläche keine Frames mehr, bleibt aber noch einige Zeit auf dem Bildschirm sichtbar. Häufige Szenarien sind, wenn die Wiedergabe das Ende des Videos erreicht oder der Nutzer die Wiedergabe anhält. Rufen Sie in diesen Fällen setFrameRate() mit dem Parameter „Framerate“ auf 0 auf, um die Framerate der Oberfläche auf den Standardwert zurückzusetzen. Das Löschen der Framerate-Einstellung ist nicht erforderlich, wenn die Oberfläche zerstört wird oder wenn die Oberfläche ausgeblendet ist, weil der Nutzer zu einer anderen App wechselt. Löschen Sie die Framerate-Einstellung nur, wenn die Oberfläche sichtbar bleibt, ohne sie zu verwenden.

Framerate-Wechsel ohne automatische Anpassung

Auf einigen Geräten kann es beim Wechsel der Bildwiederholrate zu visuellen Unterbrechungen wie einem schwarzen Bildschirm für ein bis zwei Sekunden kommen. Dies geschieht in der Regel bei Set-Top-Boxen, TV-Panels und ähnlichen Geräten. 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 Ende längerer Videos. So kann die Bildwiederholrate des Displays mit der Framerate des Videos übereinstimmen und Framerate-Konvertierungsartefakte wie Ruckler bei der 3:2-Pulldown-Filmwiedergabe vermieden werden.

Aus diesem Grund können nicht nahtlose Wechsel der Bildwiederholrate aktiviert werden, wenn sowohl der Nutzer als auch die Apps diese Option aktivieren:

Wir empfehlen, für Videos mit langer Laufzeit wie Filme immer CHANGE_FRAME_RATE_ALWAYS zu verwenden. Das liegt daran, dass die Vorteile der Anpassung der Framerate des Videos die Unterbrechung überwiegen, die beim Ändern der Bildwiederholrate auftritt.

Weitere Empfehlungen

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

Mehrere Oberflächen

Die Android-Plattform ist so konzipiert, dass Szenarien mit mehreren Oberflächen mit unterschiedlichen Framerateneinstellungen korrekt verarbeitet werden. Wenn Ihre App mehrere Oberflächen mit unterschiedlichen Frameraten 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 zur Framerate der App

Auch wenn das Gerät die Framerate unterstützt, die von der App in einem Aufruf von setFrameRate() angegeben wird, kann es vorkommen, dass das Display das Display nicht auf diese Aktualisierungsrate umschaltet. Beispielsweise kann eine Oberfläche mit höherer Priorität eine andere Einstellung für die Framerate haben oder das Gerät befindet sich im Energiesparmodus, indem die Aktualisierungsrate des Displays eingeschränkt wird, um den Akku zu schonen. Die App muss auch dann ordnungsgemäß funktionieren, wenn das Gerät die Displayaktualisierungsrate nicht auf die Framerate-Einstellung der App umstellt, auch wenn das Gerät unter normalen Umständen die Umstellung vornimmt.

Die App entscheidet, wie sie reagieren soll, wenn die Displayaktualisierungsrate nicht mit der Framerate der App übereinstimmt. Bei Videos ist die Framerate auf die des Quellvideos festgelegt. Für die Wiedergabe des Videoinhalts ist ein Drop-down-Menü erforderlich. Ein Spiel kann stattdessen versuchen, mit der Aktualisierungsrate des Displays zu laufen, anstatt seine bevorzugte Framerate beizubehalten. Die App darf den Wert, den sie an setFrameRate() übergibt, nicht basierend auf der Funktionsweise der Plattform ändern. Sie sollte auf die bevorzugte Framerate der App festgelegt bleiben, unabhängig davon, wie die App mit Fällen umgeht, bei denen sich die Plattform nicht an die Anfrage der App anpasst. So hat die Plattform die richtigen Informationen, um bei einer Änderung der Gerätebedingungen zur Verwendung zusätzlicher Displayaktualisierungsraten zur bevorzugten Framerate der App zu wechseln.

Wenn die App nicht oder nicht mit der Bildwiederholrate des Displays ausgeführt wird, sollte sie für jeden Frame Präsentationszeitstempel angeben. Dazu kann einer der Mechanismen der Plattform zum Festlegen von Präsentationszeitstempeln verwendet werden:

Durch die Verwendung dieser Zeitstempel wird verhindert, dass die Plattform einen App-Frame zu früh präsentiert, was zu unnötigem Ruckeln führen würde. Die richtige Verwendung von Zeitstempeln für die Frame-Präsentation ist etwas kompliziert. Weitere Informationen dazu, wie Sie Ruckler bei Spielen vermeiden, finden Sie in unserem Leitfaden zum Frame-Pacing. Außerdem können Sie die Android Frame Pacing-Bibliothek verwenden.

In einigen Fällen wechselt die Plattform möglicherweise zu einem Vielfachen der Framerate, die die App in setFrameRate() angegeben hat. Eine App ruft beispielsweise setFrameRate() mit 60 Hz auf und das Gerät schaltet das Display auf 120 Hz um. Das kann beispielsweise passieren, wenn eine andere App eine Oberfläche mit einer Framerate von 24 Hz hat. In diesem Fall können sowohl die 60‑Hz- als auch die 24‑Hz-Oberfläche ohne Pulldown ausgeführt werden, wenn das Display mit 120 Hz betrieben wird.

Wenn das Display mit einer Framerate ausgeführt wird, die ein Vielfaches der Framerate der App ist, sollte die App für jeden Frame Präsentationszeitstempel angeben, um unnötiges Ruckeln zu vermeiden. Bei Spielen ist die Android Frame Pacing-Bibliothek hilfreich, um die Zeitstempel der Frame-Präsentation richtig festzulegen.

setFrameRate() im Vergleich zu preferredDisplayModeId

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

setFrameRate() bietet der Plattform mehr Möglichkeiten, eine kompatible Framerate auszuwählen, wenn mehrere Oberflächen mit unterschiedlichen Framerates ausgeführt werden. Stellen Sie sich beispielsweise ein Szenario vor, bei dem zwei Apps auf einem Pixel 4 im Splitscreen-Modus ausgeführt werden, wobei eine App ein 24-Hz-Video abspielt und die andere dem Nutzer eine scrollbare Liste präsentiert. Pixel 4 unterstützt zwei Display-Aktualisierungsraten: 60 Hz und 90 Hz. Bei Verwendung der preferredDisplayModeId API wird die Videooberfläche gezwungen, entweder 60 Hz oder 90 Hz auszuwählen. Wenn setFrameRate() mit 24 Hz aufgerufen wird, liefert die Videooberfläche der Plattform mehr Informationen über die Framerate des Quellvideos. So kann die Plattform 90 Hz für die Aktualisierungsrate des Displays 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.:

  • Wenn die App die Auflösung oder andere Einstellungen für den Anzeigemodus ändern möchte, verwende preferredDisplayModeId.
  • Die Plattform wechselt als Reaktion auf einen Aufruf von setFrameRate() nur dann den Anzeigemodus, wenn der Moduswechsel unkompliziert ist und für den Nutzer nicht erkennbar ist. Wenn die App die Bildwiederholrate bevorzugt ändert, auch wenn ein Wechsel in den Heavy-Modus erforderlich ist (z. B. auf einem Android TV-Gerät), verwenden Sie preferredDisplayModeId.
  • Bei Apps, die das Display nicht mit einer Framerate ausführen können, die ein Vielfaches der Framerate der App ist, müssen für jeden Frame Präsentationszeitstempel festgelegt werden. In diesen Fällen sollte preferredDisplayModeId verwendet werden.

setFrameRate() im Vergleich zu PreferredRefreshRate

Mit WindowManager.LayoutParams#preferredRefreshRate wird eine bevorzugte Framerate für das Fenster der App festgelegt. Diese 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 bei setFrameRate(). So erhält der Planer einen besseren Hinweis auf die beabsichtigte Framerate der App.

preferredRefreshRate wird für Oberflächen ignoriert, die setFrameRate() verwenden. Verwenden Sie nach Möglichkeit setFrameRate().

preferredRefreshRate im Vergleich zu preferredDisplayModeId

Wenn Apps nur die bevorzugte Aktualisierungsrate ändern möchten, sollten Sie preferredRefreshRate anstelle von preferredDisplayModeId verwenden.

Vermeiden Sie es, setFrameRate() zu oft aufzurufen.

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

Verwendung in Spielen oder anderen Apps, die keine Videos sind

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

Verwendung von FRAME_RATE_COMPATIBILITY_FIXED_SOURCE

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE ist nur für Video-Apps vorgesehen. Verwenden Sie FRAME_RATE_COMPATIBILITY_DEFAULT, wenn Sie die Inhalte nicht für Videos verwenden.

Strategie für die Änderung der Framerate auswählen

  • Wir empfehlen dringend, dass Apps, die lang laufende Videos wie Filme anzeigen, setFrameRate(fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) aufrufen, wobei „fps“ die Framerate des Videos ist.
  • Wir raten dringend davon ab, in Apps setFrameRate() mit CHANGE_FRAME_RATE_ALWAYS aufzurufen, wenn die Videowiedergabe voraussichtlich nur wenige Minuten dauert.

Beispiel für die Einbindung in Apps für die Videowiedergabe

Wir empfehlen die folgenden Schritte für die Integration von Schaltern für die Bildwiederholrate in Apps zur Videowiedergabe:

  1. Legen Sie die changeFrameRateStrategy fest:
    1. Wenn Sie ein Video mit langer Laufzeit, z. B. einen Film, abspielen möchten, verwenden Sie MATCH_CONTENT_FRAMERATE_ALWAYS.
    2. Wenn ein kurzes Video wie ein Filmtrailer wiedergegeben wird, 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. Prüfen Sie, ob eine nicht nahtlose Umstellung der Bildwiederholrate bevorsteht. Dazu müssen beide der folgenden Bedingungen erfüllt sein:
    1. Ein nahtloser Moduswechsel von der aktuellen Bildwiederholrate (nennen wir sie C) zur Framerate des Videos (nennen wir sie V) ist 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 der nahtlosen Änderung der Aktualisierungsrate zugestimmt. Dazu prüfen Sie, ob DisplayManager.getMatchContentFrameRateUserPreference den Wert MATCH_CONTENT_FRAMERATE_ALWAYS zurückgibt.
  4. Wenn die Umstellung nahtlos erfolgen soll, gehen Sie so vor:
    1. Rufe setFrameRate auf und übergebe fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE und changeFrameRateStrategy. Dabei ist fps die Framerate des Videos.
    2. Videowiedergabe starten
  5. Wenn eine nicht nahtlose Modusänderung bevorsteht, gehen Sie so vor:
    1. Zeigen Sie die UX, um den Nutzer zu benachrichtigen. Wir empfehlen Ihnen, in Schritt 5.d eine Möglichkeit zu implementieren, mit der Nutzer diese UX schließen und die zusätzliche Verzögerung überspringen können. Der Grund dafür ist, dass die von uns empfohlene Verzögerung bei Displays mit kürzeren Wechselzeiten größer als erforderlich ist.
    2. Rufe setFrameRate auf und übergebe fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE und CHANGE_FRAME_RATE_ALWAYS. Dabei ist fps die Framerate des Videos.
    3. Warten Sie auf den onDisplayChanged-Callback.
    4. Warten Sie zwei Sekunden, bis der Moduswechsel abgeschlossen ist.
    5. Videowiedergabe starten

Der Pseudocode für die nur Unterstützung des nahtlosen Wechsels sieht so aus:

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 zur Unterstützung des reibungslosen und nicht reibungslosen Wechsels wie oben beschrieben lautet:

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