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

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

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

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

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

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

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

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

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

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

‫setFrameRate()‎ مقابل preferredDisplayModeId

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

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

إذا كانت التطبيقات تريد تغيير معدل التحديث المفضّل فقط، يُفضّل استخدام 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();
}