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 haben die meisten Geräte nur eine einzige Displayaktualisierungsrate 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 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. Auf Geräten, die keine bessere Übereinstimmung mit der Framerate der App haben, bleibt die aktuelle Displayaktualisierungsrate erhalten.

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 Drop-down-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. Gängige Szenarien sind beispielsweise, wenn die Wiedergabe am Ende des Videos angelangt ist oder der Nutzer die Wiedergabe pausiert. 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 oder ausgeblendet wird, weil der Nutzer zu einer anderen App wechselt. Löschen Sie die Framerate-Einstellung nur, wenn die Oberfläche sichtbar bleibt, ohne verwendet zu werden.

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. Das tritt in der Regel auf Set-Top-Boxen, Fernsehbildschirmen 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 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 Frameraten 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 die App in einem Aufruf von setFrameRate() angibt, schaltet es das Display in einigen Fällen nicht auf diese Aktualisierungsrate um. So kann beispielsweise eine Oberfläche mit höherer Priorität eine andere Framerate haben oder das Gerät befindet sich im Energiesparmodus, wodurch die Bildwiederholrate 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 Bildwiederholrate des Displays ausgeführt zu werden, anstatt bei der bevorzugten Framerate zu bleiben. Die App darf den Wert, den sie an setFrameRate() übergibt, nicht ändern, je nachdem, was die Plattform tut. Sie sollte auf die bevorzugte Framerate der App eingestellt bleiben, unabhängig davon, wie die App Fälle behandelt, in 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:

Mithilfe dieser Zeitstempel verhindert die Plattform, dass ein App-Frame zu früh präsentiert wird, was zu unnötigen Rucklern führen würde. Die korrekte Verwendung von Zeitstempeln für die Frame-Darstellung ist etwas schwierig. 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 in setFrameRate() in der App angegeben ist. Eine App kann beispielsweise setFrameRate() mit 60 Hz aufrufen 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 Zeitstempel für die Frame-Darstellung korrekt festzulegen.

setFrameRate() im Vergleich zu preferredDisplayModeId

WindowManager.LayoutParams.preferredDisplayModeId ist eine weitere Möglichkeit, wie Apps ihre Framerate an die Plattform senden können. Bei einigen Apps soll nur die Displayaktualisierungsrate geändert werden, nicht andere Einstellungen des Displaymodus wie die Displayauflösung. 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 Frameraten ausgeführt werden. Angenommen, auf einem Pixel 4 werden zwei Apps im Splitscreen-Modus ausgeführt. In einer App wird ein 24‑Hz-Video abgespielt und in der anderen eine scrollbare Liste angezeigt. Google Pixel 4 unterstützt zwei Displayaktualisierungsraten: 60 Hz und 90 Hz. Bei Verwendung der preferredDisplayModeId API muss für die Videofläche entweder 60 Hz oder 90 Hz ausgewählt werden. Wenn setFrameRate() mit 24 Hz aufgerufen wird, gibt die Videofläche der Plattform mehr Informationen zur Framerate des Quellvideos. So kann die Plattform 90 Hz für die Displayaktualisierungsrate auswählen, was in diesem Szenario besser ist als 60 Hz.

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

  • Wenn die App die Auflösung oder andere Einstellungen des Anzeigemodus ändern soll, verwenden Sie preferredDisplayModeId.
  • Die Plattform wechselt den Displaymodus nur als Reaktion auf einen Aufruf von setFrameRate(), wenn der Moduswechsel unauffällig ist und für den Nutzer wahrscheinlich nicht wahrnehmbar ist. Wenn die App die Bildwiederholrate bevorzugt ändert, auch wenn ein Wechsel in den Leistungsmodus 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 die bevorzugte Framerate unabhängig von den unterstützten Aktualisierungsraten des Geräts angeben, ähnlich wie bei setFrameRate(), um dem Scheduler einen besseren Hinweis auf die beabsichtigte Framerate der App zu geben.

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 Bildwiederholrate ändern möchten, sollten Sie preferredRefreshRate anstelle von preferredDisplayModeId verwenden.

Vermeiden Sie es, setFrameRate() zu oft aufzurufen.

Der setFrameRate()-Aufruf ist zwar nicht sehr leistungsintensiv, Apps sollten ihn aber nicht in jedem Frame oder mehrmals pro Sekunde aufrufen.setFrameRate() Aufrufe von setFrameRate() führen wahrscheinlich zu einer Änderung der Bildwiederholrate des Displays, was während der Umstellung zu einem Frame-Drop 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, um Schalter für die Bildwiederholrate in Apps zur Videowiedergabe einzubinden:

  1. Legen Sie die changeFrameRateStrategy fest:
    1. Wenn Sie ein langes Video wie einen Film abspielen, 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 die Option für nicht nahtlose Änderungen der Bildwiederholrate aktiviert. Sie können das prüfen, indem Sie sehen, ob DisplayManager.getMatchContentFrameRateUserPreference MATCH_CONTENT_FRAMERATE_ALWAYS zurückgibt.
  4. Wenn die Umstellung nahtlos erfolgen soll, gehen Sie so vor:
    1. Rufe setFrameRate auf und übergebe die Werte 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. UX anzeigen, 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. Das liegt daran, dass die empfohlene Verzögerung bei Displays mit kürzeren Schaltzeiten länger ist als erforderlich.
    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 2 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();
}