عدد اللقطات في الثانية

من خلال واجهة برمجة التطبيقات الخاصة بعدد اللقطات في الثانية، يمكن للتطبيقات إعلام نظام Android الأساسي بعدد اللقطات في الثانية المطلوب، وهي متوفّرة على التطبيقات التي تستهدف الإصدار Android 11 (المستوى 30 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث. عادةً ما كانت معظم الأجهزة متوافقة مع معدل تحديث شاشة واحد فقط، عادةً ما يكون 60 هرتز، إلا أنّ هذا الأمر كان يتغيّر. توفِّر العديد من الأجهزة الآن معدّلات تحديث إضافية، مثل 90 أو 120 هرتز. تتيح بعض الأجهزة استخدام مفاتيح سلسة لمعدل التحديث، بينما يعرض البعض الآخر شاشة سوداء لفترة وجيزة، عادةً ما تستمر لمدة ثانية.

إنّ الغرض الأساسي من واجهة برمجة التطبيقات هو تفعيل إمكانية استفادة التطبيقات من جميع معدّلات تحديث الشاشة المتوافقة بشكل أفضل. على سبيل المثال، إذا كان هناك تطبيق يشغِّل فيديو بنطاق 24 هرتز يستدعي setFrameRate()، قد يغيّر الجهاز معدّل تحديث الشاشة من 60 هرتز إلى 120 هرتز. يتيح معدّل التحديث الجديد هذا تشغيل الفيديو بتردد 24 هرتز بسلاسة وبدون اهتزاز، وبدون الحاجة إلى عرض سحب بنسبة عرض إلى ارتفاع 3:2، كما هو مطلوب لتشغيل الفيديو نفسه على شاشة بتردد 60 هرتز. يؤدي ذلك إلى تجربة مستخدم أفضل.

الاستخدام الأساسي

يوفّر Android طرقًا متعدّدة للوصول إلى مساحات العرض والتحكّم فيها، وتتوفّر إصدارات متعدّدة من setFrameRate() API. يأخذ كل إصدار من واجهة برمجة التطبيقات المعلمات نفسها ويعمل بنفس الطريقة مثل العناصر الأخرى:

لا يحتاج التطبيق إلى مراعاة معدّلات إعادة تحميل الشاشة الفعلية المتوافقة، والتي يمكن الحصول عليها من خلال طلب الرمز Display.getSupportedModes()، للاتصال بـ setFrameRate() بأمان. على سبيل المثال، حتى إذا كان الجهاز يتيح استخدام 60 هرتز فقط، يمكنك استدعاء setFrameRate() مع تحديد عدد اللقطات في الثانية الذي يفضّله تطبيقك. الأجهزة التي لا تتوافق بشكل أفضل مع عدد اللقطات في الثانية في التطبيق ستظل مع معدّل تحديث الشاشة الحالي.

لمعرفة ما إذا كانت المكالمة مع "setFrameRate()" أدّت إلى تغيير في معدّل إعادة تحميل الشاشة، يمكنك التسجيل لتلقّي إشعارات تغيير العرض من خلال الاتصال على DisplayManager.registerDisplayListener() أو AChoreographer_registerRefreshRateCallback().

عند استدعاء setFrameRate()، من الأفضل تمرير عدد اللقطات في الثانية الدقيق بدلاً من التقريب إلى عدد صحيح. على سبيل المثال، عند عرض فيديو مسجَّلاً بتردد 29.97 هرتز، مرِّر 29.97 بدلاً من تقريبه إلى 30.

بالنسبة إلى تطبيقات الفيديو، يجب ضبط معلَمة التوافق التي يتم تمريرها إلى setFrameRate() على Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE لتقديم تلميح إضافي لنظام Android الأساسي بأنّ التطبيق سيستخدم القائمة المنسدلة للتكيّف مع معدّل تحديث الشاشة غير المطابق (ما سيؤدي إلى حدوث اهتزاز).

في بعض السيناريوهات، سيتوقف سطح الفيديو عن إرسال اللقطات ولكنه سيظل مرئيًا على الشاشة لبعض الوقت. تشمل السيناريوهات الشائعة وصول التشغيل إلى نهاية الفيديو أو إيقاف المستخدم مؤقتًا للتشغيل. في هذه الحالات، عليك استدعاء setFrameRate() مع ضبط معلَمة عدد اللقطات في الثانية على 0 لإزالة ضبط عدد اللقطات في الثانية على القيمة التلقائية. إنّ إزالة إعدادات عدد اللقطات في الثانية ليس ضروريًا عند تلف السطح أو عندما يكون السطح مخفيًا بسبب انتقال المستخدم إلى تطبيق مختلف. وننصح بعدم محو إعدادات عدد اللقطات في الثانية إلا عندما يظل السطح مرئيًا بدون استخدامه.

التبديل بين عدد اللقطات في الثانية غير السلس

على بعض الأجهزة، قد يؤدي التبديل بين معدل التحديث إلى انقطاعات مرئية مثل ظهور شاشة سوداء لمدة ثانية أو اثنتين. ويحدث هذا عادةً على أجهزة الاستقبال ولوحات التلفزيون والأجهزة المشابهة. لا يبدِّل إطار عمل Android الأوضاع تلقائيًا عند استدعاء واجهة برمجة التطبيقات Surface.setFrameRate() لتجنُّب مثل هذه الانقطاعات المرئية.

يفضل بعض المستخدمين مقاطعة مرئية في بداية مقاطع الفيديو الأطول ونهايتها. ويتيح ذلك لمعدل تحديث الشاشة أن يتوافق مع عدد اللقطات في الثانية للفيديو، وتجنب أدوات تحويل معدل اللقطات في الثانية، مثل أداة سحب السحب للأسفل بنسبة 3:2 لتشغيل الفيلم.

ولهذا السبب، يمكن تفعيل التبديلات غير السلسة لمعدّل التحديث في حال موافقة كل من المستخدم والتطبيقات على ذلك:

ننصحك دائمًا باستخدام علامة CHANGE_FRAME_RATE_ALWAYS لالفيديوهات الطويلة الأمد، مثل الأفلام. وذلك لأنّ فائدة مطابقة عدد اللقطات في الثانية للفيديو تفوق الفاصل الذي يحدث عند تغيير معدّل التحديث.

اقتراحات إضافية

اتّبِع هذه الاقتراحات للسيناريوهات الشائعة.

مساحات عرض متعدّدة

تم تصميم نظام Android الأساسي للتعامل بشكل صحيح مع حالات ظهور مساحات عرض متعددة بإعدادات مختلفة لعدد اللقطات في الثانية. عندما يحتوي التطبيق على أسطح متعدّدة لها عدد لقطات مختلف في الثانية، يجب استدعاء setFrameRate() بعدد اللقطات الصحيح في كل مساحة عرض. حتى إذا كان الجهاز يشغّل تطبيقات متعدّدة في آنٍ واحد، وباستخدام وضع "تقسيم الشاشة" أو وضع "نافذة ضمن النافذة"، يمكن لكل تطبيق الاتصال بأمان setFrameRate() لعرض مساحات العرض الخاصة به.

عدم تغيّر النظام الأساسي لعدد اللقطات في الثانية في التطبيق

حتى إذا كان الجهاز متوافقًا مع عدد اللقطات في الثانية الذي يحدّده التطبيق في طلب setFrameRate()، قد لا يبدّل الجهاز الشاشة إلى معدّل التحديث هذا. على سبيل المثال، قد يكون هناك إعداد مختلف لعدد اللقطات في الثانية على الأسطح ذات الأولوية الأعلى، أو قد يكون الجهاز في وضع توفير شحن البطارية (مع ضبط تقييد على معدّل تحديث الشاشة للحفاظ على طاقة البطارية). يجب أن يستمر التطبيق في العمل بشكل صحيح عندما لا يبدّل الجهاز معدل تحديث الشاشة إلى إعداد عدد اللقطات في الثانية للتطبيق، حتى إذا كان تبديل الجهاز في الظروف العادية.

الأمر متروك للتطبيق لتحديد كيفية الاستجابة في حالة عدم تطابق معدل تحديث الشاشة مع عدد اللقطات في الثانية للتطبيق. بالنسبة إلى الفيديو، يتم تثبيت عدد اللقطات في الثانية على عدد اللقطات في الفيديو المصدر، ويجب السحب للأسفل لعرض محتوى الفيديو. وقد تختار اللعبة تشغيلها بمعدّل تحديث شاشة العرض بدلاً من الالتزام بعدد اللقطات المفضّل في الثانية. يجب ألا يغير التطبيق القيمة التي يمرّرها إلى setFrameRate() بناءً على ما يفعله النظام الأساسي. يجب أن يظل مضبوطًا على عدد اللقطات المفضّل في التطبيق، بغض النظر عن كيفية تعامل التطبيق مع الحالات التي لا يتم فيها تعديل النظام الأساسي للتوافق مع طلب التطبيق. وبهذه الطريقة، إذا تغيرت شروط الجهاز للسماح باستخدام معدلات تحديث الشاشة الإضافية، فإن النظام الأساسي يتوفر لديه المعلومات الصحيحة للتبديل إلى معدل عرض الإطارات المفضل للتطبيق.

في الحالات التي يتعذّر فيها تشغيل التطبيق أو يتعذّر تشغيله في معدّل تحديث العرض، يجب أن يحدِّد التطبيق الطوابع الزمنية للعرض التقديمي لكل إطار، وذلك باستخدام إحدى آليات النظام الأساسي لضبط الطوابع الزمنية للعروض التقديمية:

يؤدي استخدام هذه الطوابع الزمنية إلى منع المنصة من تقديم إطار التطبيق في وقت مبكّر جدًا، ما قد يؤدي إلى اهتزاز غير ضروري. يعد الاستخدام الصحيح للطوابع الزمنية للعرض التقديمي للإطار أمرًا صعبًا بعض الشيء. بالنسبة إلى الألعاب، يمكنك الاطّلاع على دليل سرعة الإطارات للحصول على مزيد من المعلومات حول تجنُّب الاهتزاز، ويمكنك أيضًا استخدام مكتبة "وتيرة سرعة الإطارات" في جهاز Android.

في بعض الحالات، قد يبدِّل النظام الأساسي إلى مضاعف لعدد اللقطات في الثانية المحدَّد للتطبيق في setFrameRate(). على سبيل المثال، قد يتصل أحد التطبيقات بـ setFrameRate() مع 60 هرتز، وقد يبدّل الجهاز الشاشة إلى 120 هرتز. أحد الأسباب المحتملة لحدوث ذلك هو احتواء تطبيق آخر على سطح تم ضبط إعدادات عدد اللقطات في الثانية على 24 هرتز. في هذه الحالة، سيسمح تشغيل الشاشة على 120 هرتز بتشغيل سطح 60 هرتز وسطح 24 هرتز بدون الحاجة إلى السحب.

عند تشغيل الشاشة بمضاعفات عدد اللقطات في الثانية في التطبيق، يجب أن يحدّد التطبيق الطوابع الزمنية للعرض التقديمي لكل إطار لتجنُّب الاهتزاز غير الضروري. بالنسبة إلى الألعاب، تُعد مكتبة Android Frame Pacing مفيدة في ضبط الطوابع الزمنية للعروض التقديمية للإطارات بشكل صحيح.

setFrameRate() مقابل PreferredDisplayModeId

WindowManager.LayoutParams.preferredDisplayModeId هي طريقة أخرى يمكن للتطبيقات من خلالها الإشارة إلى عدد اللقطات في الثانية على النظام الأساسي. تريد بعض التطبيقات تغيير معدّل تحديث الشاشة فقط بدلاً من تغيير إعدادات وضع العرض الأخرى، مثل درجة دقة العرض. بشكل عام، استخدِم setFrameRate() بدلاً من preferredDisplayModeId. إنّ استخدام الوظيفة setFrameRate() أسهل لأنّ التطبيق لا يحتاج إلى البحث في قائمة أوضاع العرض للعثور على وضع بعدد لقطات محدّد في الثانية.

تتيح علامة setFrameRate() للنظام الأساسي مزيدًا من الفرص لاختيار عدد لقطات متوافق في الثانية في الحالات التي يتم فيها عرض مساحات عرض متعددة بمعدلات لقطات مختلفة في الثانية. على سبيل المثال، لنأخذ في الاعتبار سيناريو يتم فيه تشغيل تطبيقَين في وضع تقسيم الشاشة على هاتف Pixel 4، حيث يشغّل أحدهما فيديو بنطاق 24 هرتز والتطبيق الآخر يعرض للمستخدم قائمة قابلة للتمرير. يدعم Pixel 4 معدّلَي تحديث الشاشة: 60 هرتز و90 هرتز. باستخدام preferredDisplayModeId API، يُطلب من سطح الفيديو اختيار 60 هرتز أو 90 هرتز. من خلال استدعاء setFrameRate() مع 24 هرتز، تمنح سطح الفيديو النظام الأساسي مزيدًا من المعلومات حول عدد اللقطات في الثانية للفيديو المصدر، ما يتيح للمنصة اختيار 90 هرتز لمعدل تحديث الشاشة، وهو أفضل من 60 هرتز في هذا السيناريو.

مع ذلك، هناك سيناريوهات يجب فيها استخدام preferredDisplayModeId بدلاً من setFrameRate()، مثل ما يلي:

  • إذا أراد التطبيق تغيير درجة الدقة أو إعدادات وضع العرض الأخرى، استخدِم preferredDisplayModeId.
  • ولن يبدّل النظام الأساسي أوضاع العرض إلا استجابةً للاستدعاء الموجَّه إلى setFrameRate() إذا كان مفتاح الوضع خفيفًا ومن غير المرجّح أن يلاحظه المستخدم. إذا كان التطبيق يفضّل تبديل معدّل تحديث الشاشة حتى إذا كان يتطلّب مفتاح تبديل الوضع الثقيل (على سبيل المثال، على جهاز Android TV )، استخدِم preferredDisplayModeId.
  • ويجب استخدام preferredDisplayModeId في التطبيقات التي لا يمكنها التعامل مع الشاشة التي تعمل بمعدّلات مضاعفة لعدد اللقطات في الثانية في التطبيق، ما يتطلب ضبط الطوابع الزمنية للعرض التقديمي على كل إطار.

setFrameRate() مقابل الموافقة على PreferredrefreshRate

WindowManager.LayoutParams#preferredRefreshRate يضبط هذا المعدّل عدد اللقطات المفضّل في نافذة التطبيق، وينطبق هذا المعدّل على كل مساحات العرض في النافذة. يجب أن يحدِّد التطبيق عدد اللقطات في الثانية المفضّل بغض النظر عن معدّلات إعادة التحميل المتوافقة مع الجهاز، والتي تشبه setFrameRate()، وذلك لمنح أداة جدولة المهام تلميحًا أفضل عن معدّل اللقطات في الثانية المطلوب في التطبيق.

يتم تجاهل السمة preferredRefreshRate لمساحات العرض التي تستخدم السمة setFrameRate(). وبشكل عام، استخدِم setFrameRate() إذا أمكن.

نسبة إعادة التحميل المفضَّلة مقارنةً بـ PreferredDisplayModeId

إذا كانت التطبيقات تريد تغيير معدّل التحديث المفضّل فقط، من المفضّل استخدام preferredRefreshRate بدلاً من preferredDisplayModeId.

تجنب استدعاء setFrameRate() بشكل متكرر جدًا

على الرغم من أنّ طلب setFrameRate() ليس مكلفًا جدًا من حيث الأداء، يجب أن تتجنّب التطبيقات استدعاء setFrameRate() كل إطار أو عدة مرات في الثانية. من المرجَّح أن تؤدي المكالمات التي يتم إجراؤها إلى setFrameRate() إلى تغيير معدل تحديث الشاشة، ما قد يؤدي إلى انخفاض الإطار أثناء عملية الانتقال. يجب معرفة العدد الصحيح للقطات في الثانية مسبقًا والاتصال بـ setFrameRate() مرة واحدة.

استخدام الألعاب أو التطبيقات الأخرى بخلاف الفيديو

مع أنّ الفيديو هو حالة الاستخدام الأساسية لواجهة برمجة التطبيقات setFrameRate()، إلا أنّه يمكن استخدامه في تطبيقات أخرى. على سبيل المثال، يمكن للعبة إذا تم إنشاؤها بمعدّل أعلى من 60 هرتز (لتقليل استخدام الطاقة وتحقيق جلسات تشغيل أطول) طلب الرمز Surface.setFrameRate(60, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT). بهذه الطريقة، سيتم تشغيل الجهاز الذي يعمل بسرعة 90 هرتز بدلاً من 60 هرتز عندما تكون اللعبة نشطة، ما سيؤدي إلى تجنُّب الاهتزاز الذي قد يحدث في حال تشغيل اللعبة على 60 هرتز بينما تعمل الشاشة على 90 هرتز.

استخدام Frame_RATE_COMPATIBILITY_FIXED_SOURCE

FRAME_RATE_COMPATIBILITY_FIXED_SOURCE مخصَّص لتطبيقات الفيديو فقط. استخدِم FRAME_RATE_COMPATIBILITY_DEFAULT للاستخدام من غير الفيديوهات.

اختيار استراتيجية لتغيير عدد اللقطات في الثانية

  • عند عرض الفيديوهات الطويلة لمدة طويلة، مثل الأفلام، ننصح بشدّة باستخدام setFrameRate(لقطات في الثانية, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) حيث يكون عدد اللقطات في الثانية هو عدد اللقطات في الثانية للفيديو.
  • ننصحك بشدة بعدم استخدام التطبيقات التي تتصل بـ setFrameRate() باستخدام CHANGE_FRAME_RATE_ALWAYS عندما تتوقع أن يستمر تشغيل الفيديو عدة دقائق أو أقل.

مثال على عملية دمج لتطبيقات تشغيل الفيديو

ننصحك باتّباع الخطوات التالية لدمج مفاتيح تبديل معدّل التحديث في تطبيقات تشغيل الفيديو:

  1. حدِّد changeFrameRateStrategy:
    1. في حال تشغيل فيديو طويل الأمد مثل فيلم، استخدِم MATCH_CONTENT_FRAMERATE_ALWAYS.
    2. في حال تشغيل فيديو قصير مثل مقطع دعائي لحركة، استخدِم الرمز CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS.
  2. إذا كانت قيمة changeFrameRateStrategy هي CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS، انتقِل إلى الخطوة 4.
  3. لمعرفة ما إذا كانت عملية التبديل بين معدّل التحديث غير السلس على وشك الحدوث من خلال التحقّق من صحة هاتَين الحالتَين:
    1. لا يمكن التبديل بين الوضع السلس بدءًا من معدل التحديث الحالي (لنسميه C) إلى عدد اللقطات في الثانية للفيديو (لنطلق عليه V). سيكون ذلك إذا اختلف الرمز C وV في حين أنّ السمة Display.getMode().getAlternativeRefreshRates لا تحتوي على مضاعف V.
    2. فعَّل المستخدم إجراء تغييرات غير سلسة في معدّل التحديث. يمكنك رصد ذلك من خلال التحقّق ممّا إذا كان DisplayManager.getMatchContentFrameRateUserPreference يعرض MATCH_CONTENT_FRAMERATE_ALWAYS.
  4. إذا كان التبديل سيتم بسلاسة، فقم بما يلي:
    1. يمكنك الاتصال بالرقم setFrameRate وتمريره إلى fps وFRAME_RATE_COMPATIBILITY_FIXED_SOURCE وchangeFrameRateStrategy حيث يشير fps إلى عدد اللقطات في الثانية للفيديو.
    2. بدء تشغيل الفيديو
  5. إذا كنت على وشك أن يتم تغيير الوضع غير السلس، يُرجى اتّباع الخطوات التالية:
    1. اعرض تجربة المستخدم لإعلام المستخدم. لاحظ أننا نوصي بتنفيذ طريقة للمستخدم لرفض تجربة المستخدم هذه وتخطي التأخير الإضافي في الخطوة 5.d. ويرجع ذلك إلى أنّ التأخير الذي ننصح به يكون أكبر من اللازم على الشاشات التي تعرض مُدد تبديل أسرع.
    2. يمكنك الاتصال بالرقم setFrameRate وتمريره إلى fps وFRAME_RATE_COMPATIBILITY_FIXED_SOURCE وCHANGE_FRAME_RATE_ALWAYS، حيث يكون fps هو عدد اللقطات في الثانية للفيديو.
    3. انتظر معاودة الاتصال بـ onDisplayChanged.
    4. انتظِر لمدة ثانيتين حتى يكتمل مفتاح الوضع.
    5. بدء تشغيل الفيديو

في ما يلي الرمز الزائف الذي يتيح إمكانية التبديل السلس فقط:

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

في ما يلي الرمز البرمجي الزائف لإتاحة التبديل السلس وغير السلس كما هو موضّح أعلاه:

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