Kare hızı

Kare hızı API'si, uygulamaların Android platformunu amaçlanan kare hızları hakkında bilgilendirmesine olanak tanır ve Android 11 (API düzeyi 30) veya sonraki sürümleri hedefleyen uygulamalarda kullanılabilir. Geleneksel olarak çoğu cihaz yalnızca tek bir ekran yenileme hızını (genellikle 60 Hz) destekler. Ancak bu durum değişiyor. Birçok cihaz artık 90 Hz veya 120 Hz gibi ek yenileme hızlarını destekliyor. Bazı cihazlar yenileme hızı geçişlerini kesintisiz olarak desteklerken bazıları genellikle bir saniye süren kısa bir süreliğine siyah ekran gösterir.

API'nin birincil amacı, uygulamaların desteklenen tüm ekran yenileme hızlarından daha iyi yararlanmasını sağlamaktır. Örneğin, setFrameRate() çağrısı yapan ve 24 Hz video oynatan bir uygulama, cihazın ekran yenileme hızını 60 Hz'den 120 Hz'ye değiştirmesine neden olabilir. Bu yeni yenileme hızı, 24 Hz videonun 60 Hz ekranda oynatılması için gereken 3:2 kaydırma işlemine gerek kalmadan sorunsuz ve takılma olmadan oynatılmasını sağlar. Bu, daha iyi bir kullanıcı deneyimi sağlar.

Temel kullanım

Android, yüzeylere erişmek ve bunları kontrol etmek için çeşitli yöntemler sunar. Bu nedenle, setFrameRate() API'nin birkaç sürümü vardır. API'nin her sürümü aynı parametreleri alır ve diğerleriyle aynı şekilde çalışır:

Uygulamanın, setFrameRate()'ı güvenli bir şekilde çağırmak için desteklenen gerçek ekran yenileme hızlarını dikkate alması gerekmez. Bu hızları Display.getSupportedModes() çağrısı yaparak elde edebilirsiniz. Örneğin, cihaz yalnızca 60 Hz'i destekliyorsa setFrameRate() işlevini uygulamanızın tercih ettiği kare hızıyla çağırın. Uygulamanın kare hızıyla daha iyi eşleşmeyen cihazlar mevcut ekran yenileme hızını kullanmaya devam eder.

setFrameRate() çağrısının ekran yenileme hızında bir değişikliğe yol açıp açmadığını görmek için DisplayManager.registerDisplayListener() veya AChoreographer_registerRefreshRateCallback() numaralı telefondan arayarak ekran değişikliği bildirimlerine kaydolun.

setFrameRate() çağrılırken, bir tam sayıya yuvarlamak yerine tam kare hızını geçirmek en iyi seçenektir. Örneğin, 29,97 Hz'de kaydedilen bir videoyu oluştururken 30'a yuvarlamak yerine 29,97 değerini iletin.

Video uygulamalarında, setFrameRate() öğesine iletilen uyumluluk parametresi Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE olarak ayarlanmalıdır. Bu sayede, Android platformuna, uygulamanın eşleşmeyen bir ekran yenileme hızına uyum sağlamak için açılır menüyü kullanacağına dair ek bir ipucu verilir (bu durum titremeye neden olur).

Bazı durumlarda video yüzeyi kare göndermeyi durdurur ancak ekranda bir süre görünür kalır. Oynatma işleminin videonun sonuna ulaşması veya kullanıcının oynatmayı duraklatması gibi durumlar yaygın senaryolardır. Bu gibi durumlarda yüzeyin kare hızı ayarını varsayılan değere sıfırlamak için kare hızı parametresi 0'a ayarlanmış setFrameRate() komutunu çağırın. Yüzeyi ortadan kaldırırken veya kullanıcı farklı bir uygulamaya geçtiği için yüzey gizlenirken kare hızı ayarının bu şekilde temizlenmesi gerekmez. Kare hızı ayarını yalnızca yüzey kullanılmadan görünür kaldığında temizleyin.

Sorunlu kare hızı geçişi

Bazı cihazlarda, yenileme hızı geçişi sırasında bir veya iki saniye boyunca siyah ekran gibi görsel kesintiler yaşanabilir. Bu genellikle set üstü kutularda, TV panellerinde ve benzer cihazlarda görülür. Android çerçevesi, bu tür görsel kesintileri önlemek için varsayılan olarak Surface.setFrameRate() API'si çağrıldığında mod değiştirmez.

Bazı kullanıcılar, uzun videoların başında ve sonunda görsel kesinti olmasını tercih eder. Bu sayede ekranın yenileme hızı video kare hızıyla eşleşebilir ve film oynatma sırasında 3:2 aşağı kaydırma sarsıntısı gibi kare hızı dönüşümü kusurları önlenebilir.

Bu nedenle, hem kullanıcı hem de uygulamalar etkinleştirildiğinde kesintisiz olmayan yenileme hızı anahtarları etkinleştirilebilir:

Film gibi uzun videolar için her zaman CHANGE_FRAME_RATE_ALWAYS kullanmanızı öneririz. Bunun nedeni, video kare hızını eşleştirmenin avantajının, yenileme hızını değiştirirken oluşan kesintinin ağır basmasıdır.

Ek öneriler

Sık karşılaşılan senaryolarda aşağıdaki önerileri uygulayın.

Birden fazla yüzey

Android platformu, farklı kare hızı ayarlarına sahip birden fazla yüzeyin bulunduğu senaryoları doğru şekilde işlemek için tasarlanmıştır. Uygulamanızda farklı kare hızlarına sahip birden fazla yüzey varsa her yüzey için doğru kare hızıyla setFrameRate() işlevini çağırın. Cihaz aynı anda birden fazla uygulamayı (bölünmüş ekran veya pencere içinde pencere modu) çalıştırsa bile her uygulama kendi yüzeyi için setFrameRate() uygulamasını güvenli bir şekilde çağırabilir.

Platform, uygulamanın kare hızına göre değişmez.

Cihaz, uygulamanın setFrameRate() çağrısında belirttiği kare hızını desteklese bile ekranı bu yenileme hızına geçirmeyebilir. Örneğin, daha yüksek öncelikli bir yüzeyin farklı bir kare hızı ayarı olabilir veya cihaz, pil tasarrufu modunda olabilir (pili korumak için ekranın yenileme hızıyla ilgili bir kısıtlama ayarlanır). Cihaz, ekran yenileme hızını uygulamanın kare hızı ayarına geçirmediğinde, cihaz normal koşullar altında değiştirilse bile uygulama doğru şekilde çalışmalıdır.

Ekran yenileme hızı, uygulama kare hızıyla eşleşmediğinde nasıl yanıt verileceğine uygulama karar verir. Video için kare hızı, kaynak videonun kare hızına sabitlenir ve video içeriğini göstermek için açılır liste gerekir. Bir oyun, tercih edilen kare hızında kalmak yerine ekran yenileme hızında çalışmayı deneyebilir. Uygulama, platformun yaptığı işleme göre setFrameRate()'e ilettiği değeri değiştirmemelidir. Platformun, uygulamanın isteğine uygun şekilde ayarlanmadığı durumlarda uygulamanın nasıl davrandığına bakılmaksızın, uygulamanın tercih ettiği kare hızında ayarlanmalıdır. Böylece, cihaz koşulları ek ekran yenileme hızlarının kullanılmasına izin verecek şekilde değişirse platform, uygulamanın tercih ettiği kare hızına geçmek için doğru bilgilere sahip olur.

Uygulamanın ekran yenileme hızında çalışmadığı veya çalışamadığı durumlarda, uygulama, platformun zaman damgası ayarlama mekanizmalarından birini kullanarak her kare için zaman damgası belirtmelidir:

Bu zaman damgalarını kullanmak, platformun uygulama çerçevesini çok erken sunmasını engeller. Bu da gereksiz sarsıntılara neden olur. Çerçeve sunma zaman damgalarının doğru kullanımı biraz karmaşıktır. Oyunlarda titremeden kaçınma hakkında daha fazla bilgi için kare hızı kılavuzumuza göz atın ve Android Frame Pacing kitaplığını kullanmayı düşünün.

Bazı durumlarda platform, uygulamanın setFrameRate() içinde belirttiği kare hızının bir katına geçebilir. Örneğin, bir uygulama 60 Hz ile setFrameRate() numarasını çağırabilir ve cihaz ekranı 120 Hz'e geçirebilir. Bunun nedenlerinden biri, başka bir uygulamanın kare hızı ayarı 24 Hz olan bir yüzeye sahip olmasıdır. Bu durumda, ekranı 120 Hz'de çalıştırmak hem 60 Hz yüzeyin hem de 24 Hz yüzeyin, kaydırma gerekmeden çalışmasına olanak tanır.

Ekran, uygulamanın kare hızının iki katı kadar hızda çalışıyorsa uygulama, gereksiz titremeyi önlemek amacıyla her kare için sunum zaman damgaları belirtmelidir. Android Frame Pacing kitaplığı, oyunlarda kare sunma zaman damgalarını doğru şekilde ayarlamak için faydalıdır.

setFrameRate() ve preferredDisplayModeId

WindowManager.LayoutParams.preferredDisplayModeId uygulamaların kare hızlarını platforma belirtmesinin bir başka yoludur. Bazı uygulamalar, ekran çözünürlüğü gibi diğer ekran modu ayarlarını değiştirmek yerine yalnızca ekran yenileme hızını değiştirmek ister. Genel olarak preferredDisplayModeId yerine setFrameRate() kullanın. Uygulamanın belirli bir kare hızına sahip bir mod bulmak için görüntü modu listesinde arama yapması gerekmediğinden setFrameRate() işlevi daha kolay kullanılır.

setFrameRate(), farklı kare hızlarında çalışan birden fazla yüzeyin olduğu senaryolarda platforma uyumlu bir kare hızı seçmek için daha fazla fırsat sunar. Örneğin, Pixel 4'te iki uygulamanın bölünmüş ekran modunda çalıştığı bir senaryo düşünün. Bu senaryodaki uygulamalardan biri 24 Hz video oynatırken diğeri kullanıcıya kaydırılabilir bir liste gösteriyor. Pixel 4, iki ekran yenileme hızını destekler: 60 Hz ve 90 Hz. preferredDisplayModeId API kullanılarak video yüzeyi 60 Hz veya 90 Hz seçimi yapmaya zorlanır. Video yüzeyi, setFrameRate() işlevini 24 Hz ile çağırarak platforma kaynak videonun kare hızı hakkında daha fazla bilgi verir. Bu sayede platform, görüntü yenileme hızı için 90 Hz'i seçebilir. Bu, söz konusu senaryoda 60 Hz'den daha iyidir.

Ancak aşağıdaki gibi durumlarda setFrameRate() yerine preferredDisplayModeId kullanılması gerekir:

  • Uygulama, çözünürlük veya diğer görüntü modu ayarlarını değiştirmek isterse preferredDisplayModeId özelliğini kullanın.
  • Platform, yalnızca mod geçişi hafif ve kullanıcının fark etmesi olası değilse setFrameRate() çağrısına yanıt olarak ekran modlarını değiştirir. Uygulama, ağır mod geçişi gerektirse bile ekran yenileme hızını değiştirmeyi tercih ediyorsa (ör. Android TV cihazında) preferredDisplayModeId değerini kullanın.
  • Görüntünün, uygulamanın kare hızının bir katı hızda çalışmasını kaldıramayan uygulamalar (bu durumda her karede zaman damgası ayarlaması gerekir) preferredDisplayModeId değerini kullanmalıdır.

setFrameRate() ve preferredRefreshRate

WindowManager.LayoutParams#preferredRefreshRate, uygulamanın penceresinde tercih edilen kare hızı belirler ve bu hız pencere içindeki tüm yüzeyler için geçerli olur. Uygulama, planlayıcıya uygulamanın amaçlanan kare hızıyla ilgili daha iyi bir ipucu vermek için setFrameRate()'e benzer şekilde, cihazın desteklediği yenileme hızlarından bağımsız olarak tercih ettiği kare hızını belirtmelidir.

preferredRefreshRate, setFrameRate() kullanan Surface'ler için yoksayılır. Mümkünse genel olarak setFrameRate() kullanın.

preferredRefreshRate ve preferredDisplayModeId

Uygulamalar yalnızca tercih edilen yenileme hızını değiştirmek istiyorsa preferredDisplayModeId yerine preferredRefreshRate kullanılması tercih edilir.

setFrameRate() işlevini çok sık çağırmaktan kaçının

setFrameRate() çağrısı performans açısından çok maliyetli olmasa da uygulamaların setFrameRate() çağrısını her karede veya saniyede birden çok kez çağırmaktan kaçınması gerekir. setFrameRate() çağrıları, ekran yenileme hızında bir değişikliğe neden olabilir. Bu da geçiş sırasında kare atlamasına yol açabilir. Doğru kare hızını önceden belirlemeniz ve setFrameRate() yöntemini bir kez çağırmanız gerekir.

Oyunlar veya video dışı diğer uygulamalar için kullanım

setFrameRate() API'nin birincil kullanım alanı video olsa da diğer uygulamalar için de kullanılabilir. Örneğin, 60 Hz'den yüksek çalışmaması (güç kullanımını azaltmak ve daha uzun oynama oturumları sağlamak için) bir oyun Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) çağırabilir. Bu sayede, varsayılan olarak 90 Hz'de çalışan bir cihaz, oyun etkinken 60 Hz'de çalışır. Böylece, oyun 60 Hz'de çalışırken ekran 90 Hz'de çalışıyorsa oluşabilecek takılmalar önlenir.

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE kullanımı

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE yalnızca video uygulamaları için tasarlanmıştır. Video dışındaki kullanımlar için FRAME_RATE_COMPATIBILITY_DEFAULT değerini kullanın.

Kare hızını değiştirmek için strateji seçme

  • Uygulamalarda filmler gibi uzun süreli videolar görüntülerken setFrameRate(fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) çağrısının kullanılmasını önemle tavsiye ederiz. Burada fps, videonun kare hızıdır.
  • Video oynatma işleminin birkaç dakika veya daha kısa sürmesini beklediğiniz durumlarda setFrameRate() işlevini CHANGE_FRAME_RATE_ALWAYS ile çağıran uygulamaları kullanmamanızı önemle tavsiye ederiz.

Video oynatma uygulamaları için örnek entegrasyon

Yenileme hızı anahtarlarını video oynatma uygulamalarına entegre etmek için aşağıdaki adımları uygulamanızı öneririz:

  1. changeFrameRateStrategy değerine karar verin:
    1. Film gibi uzun süre devam eden bir video oynatıyorsanız MATCH_CONTENT_FRAMERATE_ALWAYS kullanın.
    2. Film fragmanı gibi kısa bir videoyu oynatıyorsanız CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS simgesini kullanın.
  2. changeFrameRateStrategy CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS ise 4. adıma geçin.
  3. Aşağıdakilerin her ikisinin de doğru olup olmadığını kontrol ederek sorunsuz olmayan bir yenileme hızı geçişinin gerçekleşip gerçekleşmeyeceğini algılayın:
    1. Mevcut yenileme hızından (C olarak adlandıralım) videonun kare hızına (V olarak adlandıralım) sorunsuz geçiş yapılamaz. C ve V farklıysa ve Display.getMode().getAlternativeRefreshRates, V'nin katlarını içermiyorsa bu durum geçerli olur.
    2. Kullanıcı, yenileme hızı değişikliklerinin sorunsuz olmayan sürümünü etkinleştirmişse. DisplayManager.getMatchContentFrameRateUserPreference işlevinin MATCH_CONTENT_FRAMERATE_ALWAYS değerini döndürüp döndürmediğini kontrol ederek bunu tespit edebilirsiniz.
  4. Geçiş sorunsuz olacaksa aşağıdakileri yapın:
    1. setFrameRate işlevini çağırın ve fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE ve changeFrameRateStrategy parametrelerini iletin. fps, videonun kare hızıdır.
    2. Video oynatmayı başlatma
  5. Sorunsuz olmayan bir mod değişikliği gerçekleşmek üzereyse aşağıdakileri yapın:
    1. Kullanıcıyı bilgilendirmek için kullanıcı deneyimini gösterin. Kullanıcının bu kullanıcı deneyimini kapatıp 5.d adımında ek gecikmeyi atlayabileceği bir yöntem uygulamanızı öneririz. Bunun nedeni, önerilen gecikmenin daha hızlı geçiş süreleri gösteren ekranlarda gerekenden daha uzun olmasıdır.
    2. setFrameRate işlevini çağırın ve fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE ve CHANGE_FRAME_RATE_ALWAYS parametrelerini iletin. fps, videonun kare hızıdır.
    3. onDisplayChanged geri aramasını bekleyin.
    4. Mod geçişinin tamamlanması için 2 saniye bekleyin.
    5. Video oynatmayı başlatma

Sorunsuz geçişi yalnızca destekleyen sözde kod şu şekildedir:

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

Yukarıda açıklandığı gibi sorunsuz ve sorunsuz olmayan geçişleri destekleyen sözde kod şu şekildedir:

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