أخبار المنتجات

تحسين تشغيل الوسائط: تقديم ميزة التحميل المُسبق باستخدام Media3 - الجزء 1

قراءة لمدة 8 دقائق
Mayuri Khinvasara Khabya
مهندسة علاقات المطوّرين

في التطبيقات التي تركز على الوسائط اليوم، يُعدّ تقديم تجربة تشغيل سلسة وبدون انقطاع أمرًا أساسيًا لتوفير تجربة ممتعة للمستخدمين. ويتوقع المستخدمون أن تبدأ فيديوهاتهم على الفور وأن يتم تشغيلها بسلاسة بدون توقف.

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

تشمل المزايا الرئيسية للتحميل المُسبق ما يلي:

  • 🚀 بدء تشغيل أسرع: تكون الفيديوهات جاهزة للتشغيل، ما يؤدي إلى عمليات انتقال أسرع بين العناصر وبدء تشغيل فوري.
  • 📉 تقليل التخزين المؤقت: من خلال تحميل البيانات بشكل استباقي، يصبح من غير المرجّح أن يتوقف التشغيل، مثلاً بسبب مشاكل في الشبكة.
  • ✨ تجربة مستخدم أكثر سلاسة: يؤدي الجمع بين عمليات البدء الأسرع والتخزين المؤقت الأقل إلى تفاعل أكثر سلاسة وانسيابية للمستخدمين.

في هذه السلسلة المكوّنة من ثلاثة أجزاء، سنقدّم ونشرح بالتفصيل الأدوات القوية في Media3 لتحميل المكوّنات (مسبقًا).

  • في الجزء 1، سنتناول الأساسيات: فهم استراتيجيات التحميل المُسبق المختلفة المتوفّرة في Media3، وتفعيل PreloadConfiguration وإعداد DefaultPreloadManager، ما يتيح لتطبيقك تحميل العناصر مسبقًا. في نهاية هذه المقالة، يجب أن تتمكّن من تحميل عناصر الوسائط وتشغيلها مسبقًا باستخدام الترتيب والمدة اللذين ضبطتهما.
  • في الجزء 2، سنتناول مواضيع أكثر تقدّمًا في DefaultPreloadManager: استخدام المستمعين للإحصاءات، واستكشاف أفضل الممارسات الجاهزة للاستخدام في الإنتاج، مثل نمط النافذة المنزلقة والمكوّنات المخصّصة المشتركة في DefaultPreloadManager وExoPlayer.
  • في الجزء 3، سنتعمّق في التخزين المؤقت على القرص باستخدام DefaultPreloadManager.

التحميل المُسبق لإنقاذ الموقف 🦸‍♀️

الفكرة الأساسية من التحميل المُسبق بسيطة: تحميل محتوى الوسائط قبل الحاجة إليه. عندما ينتقل المستخدم إلى الفيديو التالي، تكون الأجزاء الأولى من الفيديو قد تم تنزيلها وتكون جاهزة للتشغيل الفوري.

يمكنك التفكير في الأمر على أنّه مثل مطعم. لا ينتظر المطبخ المزدحم طلبًا لبدء تقطيع البصل. 🧅 بل يجهّز المكونات مسبقًا. التحميل المُسبق هو تجهيز المكونات لمشغّل الفيديو.

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

في Media3، تتوفّر واجهتا برمجة تطبيقات أساسيتان للتحميل المُسبق، كل منهما مناسبة لحالات استخدام مختلفة. الخطوة الأولى هي اختيار واجهة برمجة التطبيقات المناسبة.

‫1. تحميل عناصر قائمة التشغيل مسبقًا باستخدام PreloadConfiguration

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

لا يبدأ التحميل المُسبق إلا عندما لا يتم تحميل أي وسائط للتشغيل الجاري، ما يمنعه من التنافس على النطاق الترددي مع التشغيل الأساسي.

إذا لم تكن متأكدًا بعد مما إذا كنت بحاجة إلى التحميل المُسبق، فإنّ واجهة برمجة التطبيقات هذه هي خيار رائع وسهل لتجربتها.

player.preloadConfiguration =
    PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)

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

بعد الموافقة، يمكن إيقاف التحميل المُسبق لقائمة التشغيل مرة أخرى باستخدام PreloadConfiguration.DEFAULT لإيقاف التحميل المُسبق لقائمة التشغيل:

player.preloadConfiguration = PreloadConfiguration.DEFAULT

‫2. تحميل القوائم الديناميكية مسبقًا باستخدام PreloadManager

بالنسبة إلى واجهات المستخدم الديناميكية، مثل الخلاصات العمودية أو لوحات العرض الدوّارة، حيث يتم تحديد العنصر "التالي" من خلال تفاعل المستخدم، تكون واجهة برمجة التطبيقات PreloadManager مناسبة. هذا مكوّن جديد وقوي ومستقل ضمن مكتبة Media3 ExoPlayer مصمّم خصيصًا للتحميل المُسبق بشكل استباقي. يدير هذا المكوّن مجموعة من MediaSources المحتملة، ويمنحها الأولوية استنادًا إلى مدى قربها من موضع المستخدم الحالي، ويوفر تحكّمًا دقيقًا في المحتوى الذي سيتم تحميله مسبقًا، ما يجعله مناسبًا للسيناريوهات المعقدة، مثل الخلاصات الديناميكية للفيديوهات القصيرة.

إعداد PreloadManager

إنّ DefaultPreloadManager هو التنفيذ الأساسي لـ PreloadManager.

يمكن لأداة إنشاء DefaultPreloadManager إنشاء كل من DefaultPreloadManager وأي مثيلات ExoPlayer ستشغّل المحتوى الذي تم تحميله مسبقًا. لإنشاء DefaultPreloadManager، عليك تمرير TargetPreloadStatusControl، الذي يمكن لمدير التحميل المُسبق طلبه لمعرفة مقدار المحتوى الذي سيتم تحميله لعنصر معيّن. سنشرح ونحدّد مثالاً على TargetPreloadStatusControl في القسم أدناه.

val preloadManagerBuilder =
DefaultPreloadManager.Builder(context, targetPreloadStatusControl)
val preloadManager = val preloadManagerBuilder.build()

// Build ExoPlayer with DefaultPreloadManager.Builder
val player = preloadManagerBuilder.buildExoPlayer()

من الضروري استخدام أداة الإنشاء نفسها لكل من ExoPlayer وDefaultPreloadManager، ما يضمن مشاركة المكوّنات التي تعمل تحت غطاء كل منهما بشكل صحيح.

هذا كل ما في الأمر! أصبح لديك الآن مدير جاهز لتلقّي التعليمات.

ضبط المدة والترتيب باستخدام TargetPreloadStatusControl

ماذا لو أردت تحميل 10 ثوانٍ من الفيديو مسبقًا؟ يمكنك تقديم موضع عناصر الوسائط في لوحة العرض الدوّارة، ويمنح DefaultPreloadManager الأولوية لتحميل العناصر استنادًا إلى مدى قربها من العنصر الذي يشغّله المستخدم حاليًا.

إذا أردت التحكّم في مدة العنصر الذي سيتم تحميله مسبقًا، يمكنك إخبار ذلك باستخدام DefaultPreloadManager.PreloadStatus الذي تعرضه.

على سبيل المثال:

  • العنصر "أ" هو الأولوية القصوى، يتم تحميل 5 ثوانٍ من الفيديو.
  • العنصر "ب" هو أولوية متوسطة، ولكن عند الوصول إليه، يتم تحميل 3 ثوانٍ من الفيديو.
  • العنصر "ج" هو أولوية أقل، يتم تحميل المسارات فقط.
  • العنصر "د" هو أولوية أقل، يتم إعداده فقط.
  • أي عناصر أخرى بعيدة، لا يتم تحميل أي شيء مسبقًا.

يمكن أن يساعدك هذا التحكّم الدقيق في تحسين استخدام الموارد، وهو أمر يُنصح به لتشغيل سلس.

import androidx.media3.exoplayer.DefaultPreloadManager.PreloadStatus


class MyTargetPreloadStatusControl(
    currentPlayingIndex: Int = C.INDEX_UNSET
) : TargetPreloadStatusControl<Int,PreloadStatus> {


    // The app is responsible for updating this based on UI state
    override fun getTargetPreloadStatus(index: Int): PreloadStatus? {

        val distance = index - currentPlayingIndex

        // Adjacent items (Next): preload 5 seconds
        if (distance == 1) { 
        // Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED and suggest loading // 5000ms from the default start position
                    return PreloadStatus.specifiedRangeLoaded(5000L)
                } 

        // Adjacent items (Previous): preload 3 seconds
        else if (distance == -1) { 
        // Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED //and suggest loading 3000ms from the default start position
                    return PreloadStatus.specifiedRangeLoaded(3000L)
                } 

        // Items two positions away: just select tracks
        else if (distance) == 2) {
        // Return a PreloadStatus that is labelled by STAGE_TRACKS_SELECTED
                    return PreloadStatus.TRACKS_SELECTED
                } 

        // Items four positions away: just select prepare
        else if (abs(distance) <= 4) {
        // Return a PreloadStatus that is labelled by STAGE_SOURCE_PREPARED
                    return PreloadStatus.SOURCE_PREPARED
                }

             // All other items are too far away
             return null
            }
}

ملاحظة: يمكن أن يحمّل PreloadManager كلاً من العنصرَين السابق والتالي مسبقًا، بينما يبحث PreloadConfiguration عن العناصر التالية فقط.

إدارة العناصر التي يتم تحميلها مسبقًا

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

‫1. إضافة عناصر الوسائط

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

val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
    preloadManager.add(
        initialMediaItems.get(index),index)
    )
}

سيبدأ المدير الآن في جلب البيانات لهذا MediaItem في الخلفية.

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

preloadManager.invalidate()

‫2. استرداد عنصر وتشغيله

هنا يأتي منطق التشغيل الرئيسي. عندما يقرّر المستخدم تشغيل هذا الفيديو، ليس عليك إنشاء MediaSource جديد. بدلاً من ذلك، يمكنك طلب العنصر الذي أعدّه PreloadManager مسبقًا. يمكنك استرداد MediaSource من PreloadManager باستخدام MediaItem.

إذا كان العنصر الذي تم استرداده من PreloadManager فارغًا، يعني ذلك أنّه لم يتم تحميل mediaItem مسبقًا بعد أو لم تتم إضافته إلى PreloadMamager، لذا يمكنك اختيار ضبط mediaItem مباشرةً.

// When a media item is about to displ​​ay on the screen
val mediaSource = preloadManager.getMediaSource(mediaItem)
if (mediaSource!= null) {
  player.setMediaSource(mediaSource)
} else {
  // If mediaSource is null, that mediaItem hasn't been added yet.
  // So, send it directly to the player.
  player.setMediaItem(mediaItem)
}
player.prepare()
// When the media item is displaying at the center of the screen
player.play()

من خلال إعداد MediaSource الذي تم استرداده من PreloadManager، يمكنك الانتقال بسلاسة من التحميل المُسبق إلى التشغيل، باستخدام البيانات الموجودة في الذاكرة. هذا ما يجعل وقت البدء أسرع.

‫3. مواصلة مزامنة الفهرس الحالي مع واجهة المستخدم

بما أنّ خلاصتنا أو قائمتنا قد تكون ديناميكية، من المهم إبلاغ PreloadManager بفهرس التشغيل الحالي حتى يتمكّن دائمًا من منح الأولوية للعناصر الأقرب إلى الفهرس الحالي للتحميل المُسبق.

preloadManager.setCurrentPlayingIndex(currentIndex)
// Need to call invalidate() to update the priorities
preloadManager.invalidate()

‫4. إزالة عنصر

للحفاظ على كفاءة المدير، عليك إزالة العناصر التي لم يعُد بحاجة إلى تتبّعها، مثل العناصر البعيدة عن موضع المستخدم الحالي.

// When an item is too far from the current playing index
preloadManager.remove(mediaItem)

إذا كنت بحاجة إلى محو جميع العناصر مرة واحدة، يمكنك استدعاء preloadManager.reset().

‫5. إيقاف المدير

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

// In your Activity's onDestroy() or Composable's onDispose
preloadManager.release()

تقديم العرض التوضيحي

لنطّلِع على طريقة استخدام هذه الميزة عمليًا 👍

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

Demo-PreloadManager_2.webp

الخطوات التالية:

وهذا ختام الجزء الأول! أصبحت لديك الآن الأدوات اللازمة لإنشاء نظام تحميل مُسبق ديناميكي. يمكنك إما استخدام PreloadConfiguration لتحميل العنصر التالي من قائمة تشغيل مسبقًا في ExoPlayer أو إعداد DefaultPreloadManager وإضافة العناصر وإزالتها أثناء التنقل، وضبط حالة التحميل المُسبق المستهدَفة، واسترداد المحتوى الذي تم تحميله مسبقًا بشكل صحيح للتشغيل.

في الجزء 2، سنتعمّق أكثر في DefaultPreloadManager. سنستكشف كيفية الاستماع إلى أحداث التحميل المُسبق، ونناقش أفضل الممارسات، مثل استخدام نافذة منزلقة لتجنُّب مشاكل الذاكرة، ونلقي نظرة على المكوّنات المخصّصة المشتركة في ExoPlayer وDefaultPreloadManager.

هل لديك أي ملاحظات لمشاركتها؟ نحن حريصون على سماع رأيك.

ترقَّبوا المزيد، وابدأوا في تسريع تطبيقاتكم. 🚀

تمت كتابة المقالة بواسطة:

متابعة القراءة