Framerate

Mithilfe der Framerate API können Apps die Android-Plattform über die gewünschte 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 Aktualisierungsrate von einem Bildschirm unterstützt, üblicherweise 60 Hz. Dies hat sich jedoch geändert. Viele Geräte unterstützen jetzt zusätzliche Aktualisierungsraten wie 90 Hz oder 120 Hz. Einige Geräte unterstützen nahtlose Wechsel der Aktualisierungsrate, während auf anderen kurz ein schwarzer Bildschirm angezeigt wird, der in der Regel eine Sekunde dauert.

Der Hauptzweck der API besteht darin, Apps die Möglichkeit zu geben, alle unterstützten Display-Aktualisierungsraten besser zu nutzen. Wenn eine App beispielsweise ein 24-Hz-Video abspielt und setFrameRate() aufruft, kann das dazu führen, dass das Gerät die Aktualisierungsrate des Displays von 60 Hz auf 120 Hz ändert. Diese neue Aktualisierungsrate ermöglicht eine problemlose, ruckelfreie Wiedergabe von 24-Hz-Videos ohne 3:2-Pulldown, da dies für die Wiedergabe desselben Videos auf einem 60-Hz-Display erforderlich wäre. Dies führt zu einer besseren User Experience.

Grundlegende Nutzung

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

Die App muss nicht die tatsächlich unterstützten Displayaktualisierungsraten berücksichtigen, die durch Aufrufen von Display.getSupportedModes() abgerufen werden können, um setFrameRate() sicher aufzurufen. Auch wenn das Gerät beispielsweise nur 60 Hz unterstützt, kannst du setFrameRate() mit der von deiner App bevorzugten Framerate aufrufen. 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 Aktualisierungsrate der Anzeige führt, registrieren Sie sich für Benachrichtigungen zu Displayänderungen. Rufen Sie dazu DisplayManager.registerDisplayListener() oder AChoreographer_registerRefreshRateCallback() auf.

Wenn Sie setFrameRate() aufrufen, sollten Sie die genaue Framerate übergeben, anstatt auf eine Ganzzahl zu runden. Wenn Sie beispielsweise ein Video rendern, das mit 29,97 Hz aufgezeichnet wurde, geben Sie 29,97 an und runden Sie nicht auf 30.

Bei Video-Apps sollte der an setFrameRate() übergebene Kompatibilitätsparameter auf Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE gesetzt werden. So erhält die Android-Plattform einen zusätzlichen Hinweis, dass die App das Drop-down-Menü verwendet, um sich an eine nicht übereinstimmende Aktualisierungsrate des Displays anzupassen. Das führt zu Ruckeln.

In einigen Szenarien werden von der Videooberfläche keine Frames mehr gesendet, sie 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() auf und setzen Sie den Framerate-Parameter auf 0, 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.

Nicht nahtloser Wechsel der Framerate

Auf einigen Geräten kann es beim Umschalten der Aktualisierungsrate zu visuellen Unterbrechungen wie etwa einem schwarzen Bildschirm für ein oder zwei Sekunden kommen. Dies geschieht in der Regel bei Set-Top-Boxen, TV-Panels und ähnlichen Geräten. Standardmäßig wechselt das Android-Framework beim Aufrufen der Surface.setFrameRate() API nicht den Modus, um solche visuellen Unterbrechungen zu vermeiden.

Manche Nutzer bevorzugen eine visuelle Unterbrechung am Anfang und am Ende von längeren Videos. Dadurch kann die Aktualisierungsrate der Anzeige der Framerate des Videos entsprechen und es werden Artefakte zur Framerate-Konvertierung wie 3:2-Pulldown bei der Wiedergabe von Filmen vermieden.

Aus diesem Grund können Schalter für die nicht nahtlose Aktualisierungsrate aktiviert werden, wenn sowohl der Nutzer als auch die Apps zustimmen:

Wir empfehlen, für Videos mit langer Laufzeit, z. B. bei Filmen, immer CHANGE_FRAME_RATE_ALWAYS zu verwenden. Das liegt daran, dass der Vorteil der Anpassung der Video-Framerate die Unterbrechung überwiegt, die beim Ändern der Aktualisierungsrate auftritt.

Weitere Empfehlungen

Beachten Sie bei häufigen Szenarien die folgenden Empfehlungen.

Mehrere Oberflächen

Die Android-Plattform ist auf Szenarien mit mehreren Oberflächen mit unterschiedlichen Frame-Rate-Einstellungen ausgelegt. 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 im Splitscreen- oder Bild-im-Bild-Modus ausgeführt werden, kann jede App setFrameRate() sicher für ihre eigene Oberfläche aufrufen.

Die Plattform ändert sich nicht an der 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 weiterhin ordnungsgemäß funktionieren, wenn das Gerät die Aktualisierungsrate des Displays nicht auf die Framerate der App umstellt, auch wenn das Gerät unter normalen Umständen die Aktualisierung durchführt.

Die App entscheidet selbst, wie sie reagieren soll, wenn die Aktualisierungsrate des Displays nicht mit der Framerate der App übereinstimmt. Bei Videos ist die Framerate auf die des Quellvideos festgelegt und der Videoinhalt muss per Pull-down-Menü angezeigt werden. Ein Spiel kann stattdessen versuchen, mit der Aktualisierungsrate des Displays zu laufen, anstatt seine bevorzugte Framerate beizubehalten. Der Wert, den die App an setFrameRate() übergibt, sollte je nach Funktion der Plattform nicht geändert werden. 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. Wenn sich die Gerätebedingungen ändern, sodass zusätzliche Aktualisierungsraten des Bildschirms verwendet werden können, hat die Plattform die richtigen Informationen, um zur bevorzugten Framerate der App zu wechseln.

In Fällen, in denen die App nicht mit der Aktualisierungsrate des Bildschirms ausgeführt werden kann oder nicht, sollte sie für jeden Frame einen Präsentationszeitstempel angeben. Dazu wird einer der Mechanismen der Plattform zum Festlegen von Präsentationszeitstempeln verwendet:

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 zur Vermeidung von Ruckeln bei Spielen finden Sie in unserer Anleitung zur Frame-Taktung. Sie können auch 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 kann beispielsweise setFrameRate() mit 60 Hz aufrufen und das Gerät schaltet die Anzeige auf 120 Hz um. Dies kann unter anderem der Fall sein, wenn eine andere App eine Oberfläche mit einer Framerate von 24 Hz hat. Wenn das Display bei 120 Hz läuft, können sowohl die 60-Hz-Oberfläche als auch die 24-Hz-Oberfläche ohne Pulldown ausgeführt werden.

Wenn die Anzeige mit einem Vielfachen der Framerate der App ausgeführt wird, sollte die App für jeden Frame einen 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 der Plattform ihre Framerate anzeigen 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 Aktualisierungsrate des Displays selbst dann ändern möchte, wenn ein intensiver Moduswechsel erforderlich ist (z. B. auf einem Android TV-Gerät), verwende preferredDisplayModeId.
  • Anwendungen, die die Anzeige nicht verarbeiten können, wenn sie mit einem Vielfachen der Framerate der App ausgeführt wird, wofür für jeden Frame ein Präsentationszeitstempel festgelegt werden muss, sollte preferredDisplayModeId verwenden.

setFrameRate() im Vergleich zu PreferredRefreshRate

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

preferredRefreshRate wird bei Oberflächen, die setFrameRate() verwenden, ignoriert. Verwende nach Möglichkeit im Allgemeinen setFrameRate().

bevorzugte Aktualisierungsrate im Vergleich zu PreferredDisplayModeId

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

Zu häufiges Aufrufen von setFrameRate() vermeiden

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. Du solltest die richtige Framerate vorab ermitteln und setFrameRate() einmal aufrufen.

Nutzung für Spiele und andere Apps ohne Video

Video ist zwar der primäre Anwendungsfall für die setFrameRate() API, kann aber auch für andere Anwendungen verwendet werden. Beispiel: Ein Spiel, das nicht mehr als 60 Hz ausführen soll (um den Stromverbrauch zu reduzieren und längere Spielsitzungen zu erzielen) kann Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) aufrufen. Auf diese Weise wird ein Gerät, das standardmäßig mit 90 Hz läuft, stattdessen bei 60 Hz ausgeführt, während das Spiel aktiv ist. Dadurch wird das Ruckeln vermieden, das sonst auftreten würde, wenn das Spiel mit 60 Hz, das Display mit 90 Hz läuft.

Verwendung von FRAME_RATE_COMPATIBILITY_FIXED_SOURCE

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE ist nur für Video-Apps vorgesehen. Verwende FRAME_RATE_COMPATIBILITY_DEFAULT für die Nutzung ohne Videos.

Strategie zum Ändern der Framerate auswählen

  • Wenn Apps, die Videos mit langer Ausführungszeit wiedergeben, z. B. Filme, sollten sie unbedingt setFrameRate(fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) aufrufen, wobei fps für die Framerate des Videos steht.
  • Wir raten dringend davon ab, dass Apps setFrameRate() mit CHANGE_FRAME_RATE_ALWAYS aufrufen, wenn die Videowiedergabe einige Minuten oder weniger dauern soll.

Beispielintegration für Apps zur Videowiedergabe

Wir empfehlen die folgenden Schritte zum Integrieren von Schaltern für die Aktualisierungsrate 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. Verwende CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, wenn du ein kurzes Video abspielst, z. B. einen Move-Trailer
  2. Wenn changeFrameRateStrategy CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS ist, fahren Sie mit Schritt 4 fort.
  3. Prüfen Sie, ob die beiden folgenden Voraussetzungen erfüllt sind, um festzustellen, ob ein Wechsel der Aktualisierungsrate ohne nahtlose Übergang stattfindet:
    1. Ein nahtloser Moduswechsel ist nicht von der aktuellen Aktualisierungsrate (nennen wir C) bis zur Framerate des Videos (nennen wir es V) möglich. Dies 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 der Wechsel reibungslos verläuft, gehen Sie so vor:
    1. Rufe setFrameRate auf und übergib fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE und changeFrameRateStrategy, wobei fps die Framerate des Videos ist.
    2. Videowiedergabe starten
  5. Wenn eine Änderung des nicht nahtlosen Modus bevorsteht, gehen Sie so vor:
    1. Zeigen Sie die UX, um den Nutzer zu benachrichtigen. Wir empfehlen, eine Möglichkeit zu implementieren, mit der Nutzer diese UX schließen und die zusätzliche Verzögerung in Schritt 5.d ü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 übergib 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. Warten Sie zwei Sekunden, bis der Moduswechsel abgeschlossen ist.
    5. Videowiedergabe starten

Der Pseudocode, der ausschließlich den nahtlosen Wechsel unterstützt, 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 oben beschriebene Pseudocode zur Unterstützung des nahtlosen und nahtlosen Wechsels lautet wie folgt:

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