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

تسمح واجهة برمجة التطبيقات لعدد اللقطات في الثانية للتطبيقات بإعلام نظام 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، يتم إجبار مساحة عرض الفيديو على اختيار 60 هرتز أو 90 هرتز. عند استدعاء "setFrameRate()" مع نطاق 24 هرتز، تقدّم مساحة عرض الفيديو للمنصة مزيدًا من المعلومات حول عدد اللقطات في الثانية للفيديو المصدر، ما يتيح للمنصة اختيار 90 هرتز لمعدّل إعادة تحميل الشاشة، وهو أفضل من 60 هرتز في هذا السيناريو.

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

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

setFrameRate() مقابل PreferredRefreshRate

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

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

معدل التحديث المفضّل مقارنةً بمعرّف وضع العرض المفضّل

إذا كانت التطبيقات تريد تغيير معدل التحديث المفضّل فقط، يُفضَّل استخدام 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(fps, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, CHANGE_FRAME_RATE_ALWAYS) عند عرض فيديوهات طويلة مثل الأفلام، حيث يشير fps إلى عدد اللقطات في الثانية للفيديو.
  • ننصحك بشدة بعدم اتصال التطبيقات بـ 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 مختلفَين ولم يكن 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.د. ويعود سبب ذلك إلى أنّ وقت الاستجابة المقترَح أكبر من اللازم على الشاشات التي تُظهر أوقات تبديل أسرع.
    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();
}