تتيح واجهة برمجة التطبيقات frame rate API للتطبيقات إبلاغ نظام Android الأساسي بعدد اللقطات في الثانية، وهي متاحة للتطبيقات التي تستهدف الإصدار Android 11 (مستوى واجهة برمجة التطبيقات 30) أو إصدار أحدث. في العادة، كانت معظم الأجهزة لا تتيح سوى معدّل تحديث واحد للشاشة، عادةً ما يكون 60 هرتز، ولكن هذا الأمر بدأ يتغيّر. تتيح العديد من الأجهزة الآن معدّلات تحديث إضافية، مثل 90 هرتز أو 120 هرتز. تتيح بعض الأجهزة التبديل السلس بين معدّلات التحديث، بينما يعرض البعض الآخر شاشة سوداء لفترة وجيزة، عادةً ما تكون ثانية واحدة.
الغرض الأساسي من واجهة برمجة التطبيقات هو السماح للتطبيقات بالاستفادة بشكل أفضل من جميع معدّلات تحديث الشاشة المتاحة. على سبيل المثال، قد يؤدي تشغيل تطبيق لفيديو بمعدّل 24 هرتز واستدعاء الدالة setFrameRate() إلى تغيير الجهاز لمعدّل تحديث الشاشة من 60 هرتز إلى 120 هرتز. يتيح معدّل التحديث الجديد تشغيل الفيديو بمعدّل 24 هرتز بسلاسة وبدون اهتزاز، بدون الحاجة إلى تقنية 3:2 pulldown كما هو مطلوب لتشغيل الفيديو نفسه على شاشة بمعدّل 60 هرتز. يساهم هذا الإجراء في تحسين تجربة المستخدم.
الاستخدام الأساسي
يتيح Android عدة طرق للوصول إلى مساحات العرض والتحكّم فيها، لذا تتوفّر عدة إصدارات من واجهة برمجة التطبيقات setFrameRate(). يأخذ كل إصدار من واجهة برمجة التطبيقات المَعلمات نفسها ويعمل بالطريقة نفسها:
Surface.setFrameRate()SurfaceControl.Transaction.setFrameRate()ANativeWindow_setFrameRate()ASurfaceTransaction_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 الأساسي بأنّ التطبيق سيستخدم تقنية pulldown للتكيّف مع معدّل تحديث الشاشة غير المتطابق (ما سيؤدي إلى اهتزاز).
في بعض السيناريوهات، ستتوقف مساحة عرض الفيديو عن إرسال اللقطات ولكن ستظل مرئية على الشاشة لبعض الوقت. تشمل السيناريوهات الشائعة وصول التشغيل إلى نهاية الفيديو أو إيقاف المستخدم للتشغيل مؤقتًا. في هذه الحالات، استدعِ الدالة setFrameRate() مع ضبط مَعلمة عدد اللقطات على 0 لمحو إعداد عدد اللقطات في الثانية لمساحة العرض وإعادته إلى القيمة التلقائية. ليس من الضروري محو إعداد عدد اللقطات في الثانية بهذه الطريقة عند إتلاف مساحة العرض، أو عندما تكون مساحة العرض مخفية لأنّ المستخدم ينتقل إلى تطبيق مختلف. امحُ إعداد عدد اللقطات في الثانية فقط عندما تظل مساحة العرض مرئية بدون استخدامها.
التبديل غير السلس بين عدد اللقطات في الثانية
في بعض الأجهزة، قد يؤدي التبديل بين معدّلات التحديث إلى حدوث انقطاعات مرئية، مثل ظهور شاشة سوداء لثانية أو ثانيتَين. يحدث ذلك عادةً على أجهزة فك التشفير ولوحات التلفزيون والأجهزة المماثلة. تلقائيًا، لا يغيّر إطار عمل Android الأوضاع
عند استدعاء واجهة برمجة التطبيقات Surface.setFrameRate()
، وذلك لتجنُّب حدوث مثل هذه الانقطاعات المرئية.
يفضّل بعض المستخدمين حدوث انقطاع مرئي في بداية الفيديوهات الأطول ونهايتها. يسمح ذلك بمطابقة معدّل تحديث الشاشة مع عدد اللقطات في الثانية للفيديو، وتجنُّب العناصر غير المرغوب فيها الناتجة عن تحويل عدد اللقطات في الثانية، مثل اهتزاز تقنية 3:2 pulldown عند تشغيل الأفلام.
لهذا السبب، يمكن تفعيل التبديل غير السلس بين معدّلات التحديث إذا وافق كل من المستخدمين والتطبيقات على ذلك:
- المستخدمون: للموافقة، يمكن للمستخدمين تفعيل إعداد المستخدم "مطابقة عدد اللقطات في الثانية للمحتوى" .
- التطبيقات: للموافقة، يمكن للتطبيقات تمرير
CHANGE_FRAME_RATE_ALWAYSإلىsetFrameRate().
ننصحك دائمًا باستخدام CHANGE_FRAME_RATE_ALWAYS
للفيديوهات الطويلة، مثل الأفلام. يعود ذلك إلى أنّ فائدة مطابقة عدد اللقطات في الثانية للفيديو تفوق الانقطاع الذي يحدث عند تغيير معدّل التحديث.
اقتراحات إضافية
اتّبِع هذه الاقتراحات للسيناريوهات الشائعة.
مساحات عرض متعددة
تم تصميم نظام Android الأساسي للتعامل بشكل صحيح مع السيناريوهات التي تتضمّن مساحات عرض متعددة بإعدادات مختلفة لعدد اللقطات في الثانية. عندما يكون لتطبيقك مساحات عرض متعددة بأعداد مختلفة للقطات في الثانية، استدعِ الدالة setFrameRate() مع عدد اللقطات الصحيح لكل مساحة عرض. حتى إذا كان الجهاز يشغّل تطبيقات متعددة في آنٍ واحد، باستخدام ميزة "تقسيم الشاشة" أو وضع "صورة داخل صورة"، يمكن لكل تطبيق استدعاء setFrameRate() بأمان لمساحات العرض الخاصة به.
لا يتم تغيير المنصة إلى عدد اللقطات في الثانية الذي يفضّله التطبيق
حتى إذا كان الجهاز يتيح عدد اللقطات في الثانية الذي يحدّده التطبيق في استدعاء setFrameRate()، هناك حالات لن يغيّر فيها الجهاز معدّل التحديث إلى ذلك المعدّل. على سبيل المثال، قد يكون لمساحة عرض ذات أولوية أعلى إعداد مختلف لعدد اللقطات في الثانية، أو قد يكون الجهاز في وضع توفير شحن البطارية (ما يفرض قيودًا على معدّل تحديث الشاشة للحفاظ على البطارية). يجب أن يظل التطبيق يعمل بشكل صحيح عندما لا يغيّر الجهاز معدّل تحديث الشاشة إلى إعداد عدد اللقطات في الثانية الذي يفضّله التطبيق، حتى إذا كان الجهاز يغيّر معدّل التحديث في الظروف العادية.
يعود إلى التطبيق تحديد كيفية الاستجابة عندما لا يتطابق معدّل تحديث الشاشة مع عدد اللقطات في الثانية الذي يفضّله التطبيق. بالنسبة إلى الفيديو، يتم تثبيت عدد اللقطات في الثانية على عدد اللقطات في الثانية للفيديو المصدر، وسيكون من الضروري استخدام تقنية pulldown لعرض محتوى الفيديو. بدلاً من ذلك، قد تختار اللعبة محاولة التشغيل بمعدّل تحديث الشاشة بدلاً من البقاء على عدد اللقطات في الثانية الذي تفضّله. يجب ألا يغيّر التطبيق القيمة التي يمرّرها إلى setFrameRate() استنادًا إلى ما تفعله المنصة. يجب أن تظل القيمة مضبوطة على عدد اللقطات في الثانية الذي يفضّله التطبيق، بغض النظر عن كيفية تعامل التطبيق مع الحالات التي لا تتكيّف فيها المنصة لتلبية طلب التطبيق. بهذه الطريقة، إذا تغيّرت ظروف الجهاز للسماح باستخدام معدّلات تحديث إضافية للشاشة، ستحصل المنصة على المعلومات الصحيحة للتبديل إلى عدد اللقطات في الثانية الذي يفضّله التطبيق.
في الحالات التي لن يتم فيها تشغيل التطبيق بمعدّل تحديث الشاشة أو لا يمكن تشغيله، يجب أن يحدّد التطبيق الطوابع الزمنية للعرض لكل لقطة، باستخدام إحدى آليات المنصة لضبط الطوابع الزمنية للعرض:
يمنع استخدام هذه الطوابع الزمنية المنصة من عرض لقطة تطبيق في وقت مبكر جدًا، ما سيؤدي إلى اهتزاز غير ضروري. إنّ الاستخدام الصحيح للطوابع الزمنية لعرض اللقطات أمر معقّد بعض الشيء. بالنسبة إلى الألعاب، يمكنك الاطّلاع على دليل وتيرة اللقطات لمزيد من المعلومات حول تجنُّب الاهتزاز، وننصحك باستخدام مكتبة Android Frame Pacing.
في بعض الحالات، قد تنتقل المنصة إلى مضاعف عدد اللقطات في الثانية الذي حدّده التطبيق في setFrameRate(). على سبيل المثال، قد يستدعي التطبيق setFrameRate() مع 60 هرتز وقد يغيّر الجهاز الشاشة إلى 120 هرتز. أحد الأسباب المحتملة لحدوث ذلك هو إذا كان لتطبيق آخر مساحة عرض بإعداد عدد اللقطات في الثانية يبلغ 24 هرتز. في هذه الحالة، سيسمح تشغيل الشاشة بمعدّل 120 هرتز بتشغيل كل من مساحة العرض بمعدّل 60 هرتز ومساحة العرض بمعدّل 24 هرتز بدون الحاجة إلى تقنية pulldown.
عندما يتم تشغيل الشاشة بمضاعف عدد اللقطات في الثانية الذي يفضّله التطبيق، يجب أن يحدّد التطبيق الطوابع الزمنية للعرض لكل لقطة لتجنُّب الاهتزاز غير الضروري. بالنسبة إلى الألعاب، تكون مكتبة 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
إذا كانت التطبيقات تريد تغيير معدّل التحديث المفضّل فقط، من الأفضل استخدام 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عندما تتوقع أن يستمر تشغيل الفيديو لعدة دقائق أو أقل.
مثال على عملية الدمج لتطبيقات تشغيل الفيديو
ننصحك باتّباع الخطوات التالية لدمج عمليات التبديل بين معدّلات التحديث في تطبيقات تشغيل الفيديو:
- حدِّد
changeFrameRateStrategy:- إذا كنت تشغّل فيديو طويلاً مثل فيلم، استخدِم
MATCH_CONTENT_FRAMERATE_ALWAYS - إذا كنت تشغّل فيديو قصيرًا مثل إعلان فيلم، استخدِم
CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
- إذا كنت تشغّل فيديو طويلاً مثل فيلم، استخدِم
- إذا كانت
changeFrameRateStrategyهيCHANGE_FRAME_RATE_ONLY_IF_SEAMLESS، انتقِل إلى الخطوة 4. - اكتشِف ما إذا كان سيحدث تبديل غير سلس بين معدّلات التحديث من خلال التأكّد من صحة كل من الحقائق التالية:
- لا يمكن تبديل الوضع بسلاسة من معدّل التحديث الحالي (لنسمِّه C) إلى عدد اللقطات في الثانية للفيديو (لنسمِّه V). سيحدث ذلك إذا كان C وV مختلفَين ولا يحتوي
Display.getMode().getAlternativeRefreshRatesعلى مضاعف لـ V. - وافق المستخدم على التغييرات غير السلسة في معدّل التحديث. يمكنك اكتشاف
ذلك من خلال التحقّق مما إذا كانت الدالة
DisplayManager.getMatchContentFrameRateUserPreferenceتعرضMATCH_CONTENT_FRAMERATE_ALWAYS
- لا يمكن تبديل الوضع بسلاسة من معدّل التحديث الحالي (لنسمِّه C) إلى عدد اللقطات في الثانية للفيديو (لنسمِّه V). سيحدث ذلك إذا كان C وV مختلفَين ولا يحتوي
- إذا كان التبديل سلسًا، اتّبِع الخطوات التالية:
- استدعِ الدالة
setFrameRateومرِّر إليهاfpsوFRAME_RATE_COMPATIBILITY_FIXED_SOURCEوchangeFrameRateStrategy، حيث يمثّلfpsعدد اللقطات في الثانية للفيديو. - ابدأ تشغيل الفيديو
- استدعِ الدالة
- إذا كان سيحدث تغيير غير سلس في الوضع، اتّبِع الخطوات التالية:
- اعرض تجربة مستخدم لإعلام المستخدم. يُرجى العِلم بأنّنا ننصحك بتنفيذ طريقة تسمح للمستخدم برفض تجربة المستخدم هذه وتخطّي التأخير الإضافي في الخطوة 5.d. يعود ذلك إلى أنّ التأخير الذي ننصح به أكبر من اللازم على الشاشات التي تعرض أوقات تبديل أسرع.
- استدعِ الدالة
setFrameRateومرِّر إليهاfpsوFRAME_RATE_COMPATIBILITY_FIXED_SOURCEوCHANGE_FRAME_RATE_ALWAYS، حيث يمثّلfpsعدد اللقطات في الثانية للفيديو. - انتظِر
onDisplayChangedمعاودة الاتصال. - انتظِر ثانيتَين حتى يكتمل تبديل الوضع.
- ابدأ تشغيل الفيديو
في ما يلي الرمز الزائف لدعم التبديل السلس فقط:
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();
}