اخبار محصول

ارتقای بازپخش رسانه: بررسی عمیق PreloadManager در Media3 - بخش ۲

مدت‌زمان مطالعه: ۹ دقیقه
مشاهده نمایه Mayuri Khinvasara Khabya
Mayuri Khinvasara Khabya مهندس روابط توسعه‌دهندگان

به بخش دوم از مجموعه سه‌بخشی ما درباره پیش‌بارگیری رسانه با Media3 خوش آمدید. این مجموعه برای راهنمایی شما در فرایند ساخت تجربه‌های رسانه‌ای با تأخیر کم و پاسخ‌دهی بالا در برنامه‌های Android طراحی شده است.

  • بخش ۱: معرفی پیش‌بارگیری با Media3 اصول اولیه را پوشش داد. تفاوت بین PreloadConfiguration برای فهرست‌های پخش ساده و DefaultPreloadManager قدرتمندتر برای میاناهای کاربر پویا را بررسی کردیم. با نحوه پیاده‌سازی چرخه حیات API پایه آشنا شدید: افزودن رسانه با()add، بازیابی MediaSource آماده‌شده با()getMediaSource، مدیریت اولویت‌ها با()setCurrentPlayingIndex و()invalidate، و آزاد کردن منابع با()remove و()release.
  • بخش ۲ (این پست): در این وبلاگ، قابلیت‌های پیشرفته DefaultPreloadManager را بررسی می‌کنیم. ما نحوه به‌دست آوردن اطلاعات آماری با PreloadManagerListener، پیاده‌سازی بهترین روال‌های مطلوب آماده تولید مانند هم‌رسانی مؤلفه‌های اصلی با ExoPlayer، و تسلط بر الگوی پنجره لغزشی برای مدیریت مؤثر حافظه را پوشش می‌دهیم.
  • بخش ۳: بخش نهایی این مجموعه به ادغام PreloadManager با حافظه نهان دیسک پایدار می‌پردازد و به شما امکان می‌دهد مصرف داده را با مدیریت منابع کاهش دهید و تجربه‌ای یکپارچه ارائه دهید.

اگر با پیش‌بارگیری در Media3 آشنا نیستید، اکیداً توصیه می‌کنیم قبل‌از ادامه دادن، بخش ۱ را بخوانید. برای کسانی که آماده‌اند فراتر از اصول اولیه بروند، بیایید نحوه ارتقای پیاده‌سازی بازپخش رسانه را بررسی کنیم.

گوش دادن: واکشی تجزیه‌وتحلیل با PreloadManagerListener

وقتی می‌خواهید ویژگی‌ای را در تولید راه‌اندازی کنید، به‌عنوان توسعه‌دهنده برنامه می‌خواهید تجزیه‌وتحلیل‌های پشت آن را نیز درک و ثبت کنید. چگونه می‌توانید مطمئن شوید که استراتژی پیش‌بارگذاری شما در محیط واقعی مؤثر است؟ پاسخ به این پرسش به داده‌های مربوط به نرخ موفقیت، شکست، و عملکرد نیاز دارد. میانای PreloadManagerListener سازوکار اصلی برای جمع‌آوری این داده‌ها است.

‫PreloadManagerListener دو فراخوانی برگشتی ضروری ارائه می‌دهد که اطلاعات آماری مهمی درباره فرایند و وضعیت پیش‌بارگیری ارائه می‌دهند.

  • onCompleted(MediaItem mediaItem): این فراخوانی برگشتی پس‌از تکمیل موفقیت‌آمیز درخواست پیش‌بارگیری، همان‌طور که توسط TargetPreloadStatusControl شما تعریف شده است، فراخوانی می‌شود.
  • onError(PreloadException error): این فراخوانی برگشتی می‌تواند برای اشکال‌زدایی و نظارت مفید باشد. وقتی پیش‌بارگیری ناموفق باشد، این روش فراخوانی می‌شود و استثنای مربوطه را ارائه می‌دهد.

می‌توانید شنونده‌ای را با یک فراخوانی روش ثبت کنید، همان‌طور که در کد نمونه زیر نشان داده شده است:

val preloadManagerListener = object : PreloadManagerListener {
    override fun onCompleted(mediaItem: MediaItem) {
        // Log success for analytics. 
        Log.d("PreloadAnalytics", "Preload completed for $mediaItem")
    }

    override fun onError( preloadError: PreloadException) {
        // Log the specific error for debugging and monitoring.
        Log.e("PreloadAnalytics", "Preload error ", preloadError)
    }
}

preloadManager.addListener(preloadManagerListener)

درحال استخراج اطلاعات آماری از شنونده 

این فراخوانی‌های شنونده را می‌توان به خط لوله تجزیه‌وتحلیل شما متصل کرد. با بازارسال این رویدادها به موتور تجزیه‌وتحلیل خود، می‌توانید به سؤالات کلیدی مانند موارد زیر پاسخ دهید:

  • نرخ موفقیت پیش‌بارگیری ما چقدر است؟ (نسبت رویدادهای onCompleted به کل تلاش‌های پیش‌بارکردن)
  • کدام CDNها یا قالب‌های ویدیو بالاترین نرخ خطا را نشان می‌دهند؟ (با تجزیه کردن استثنائات از onError)
  • نرخ خطای پیش‌بارگیری ما چقدر است؟ (نسبت رویدادهای onError به کل تلاش‌های پیش‌بارکردن)

این داده‌ها می‌توانند بازخورد کمی درباره استراتژی پیش‌بارگیری شما ارائه دهند و امکان آزمایش A/B و بهبودهای مبتنی بر داده را در تجربه کاربری شما فراهم کنند. این داده‌ها همچنین می‌توانند به شما کمک کنند تا مدت‌زمان پیش‌بارگیری و تعداد ویدیوهایی را که می‌خواهید پیش‌بارگیری کنید، و همچنین میان‌گیرهایی را که اختصاص می‌دهید، به‌طور هوشمندانه تنظیم دقیق کنید.

فراتر از اشکال‌زدایی: استفاده از onError برای جایگزینی مناسب واسط کاربر

پیش‌بارگیری ناموفق نشانگر قوی‌ای برای رویداد میان‌گیری آتی برای کاربر است. فراخوانی onError به شما امکان می‌دهد به‌صورت واکنشی پاسخ دهید. به‌جای اینکه فقط خطا را ثبت کنید، می‌توانید واسط کاربر را تطبیق دهید. برای مثال، اگر ویدیو بعدی پیش‌بارگیری نشود، برنامه شما می‌تواند پخش خودکار را برای تند کشیدن بعدی غیرفعال کند و برای شروع بازپخش، کاربر باید تک‌ضرب بزند.

علاوه‌براین، با بررسی نوع PreloadException می‌توانید استراتژی امتحان مجدد هوشمندانه‌تری تعریف کنید. برنامه می‌تواند انتخاب کند که منبع ناموفق را فوراً براساس پیام خطا یا کد وضعیت HTTP از مدیر بردارد. مورد باید به‌طور مناسب از جریان میانای کاربر برداشته شود تا مشکلات بار کردن به تجربه کاربری سرایت نکند. همچنین می‌توانید داده‌های دقیق‌تری از PreloadException مانند HttpDataSourceException دریافت کنید تا خطاها را بیشتر بررسی کنید. درباره عیب‌یابی ExoPlayer بیشتر بخوانید.

سیستم رفاقتی: چرا هم‌رسانی مؤلفه‌ها با ExoPlayer ضروری است؟

‫DefaultPreloadManager و ExoPlayer برای کار کردن با یکدیگر طراحی شده‌اند. برای اطمینان از پایداری و کارایی، آن‌ها باید چندین مؤلفه اصلی را هم‌رسانی کنند. اگر آن‌ها با مؤلفه‌های جداگانه و ناهماهنگ کار کنند، می‌تواند بر ایمنی رشته و قابلیت استفاده از قطعه‌های پیش‌بارشده در پخش‌کننده تأثیر بگذارد، زیرا باید مطمئن شویم که قطعه‌های پیش‌بارشده در پخش‌کننده صحیح پخش می‌شوند. همچنین، مؤلفه‌های جداگانه ممکن است برای منابع محدود مانند پهنای باند شبکه و حافظه رقابت کنند که می‌تواند منجر به کاهش عملکرد شود. بخش مهمی از چرخه حیات، مدیریت دفع مناسب است، ترتیب توصیه‌شده برای دفع این است که ابتدا PreloadManager و سپس ExoPlayer آزاد شود.

‫DefaultPreloadManager.Builder برای تسهیل این هم‌رسانی طراحی شده است و دارای APIهایی برای نمونه‌سازی PreloadManager و نمونه پخش‌کننده پیوندشده است. بیایید ببینیم چرا مؤلفه‌هایی مانند BandwidthMeter،‏ LoadControl،‏ TrackSelector، و Looper باید هم‌رسانی شوند. نمایش تصویری نحوه تعامل این مؤلفه‌ها با «بازپخش ExoPlayer» را بررسی کنید.

preloadManager2.png

جلوگیری از تداخل پهنای باند با BandwidthMeter مشترک

BandwidthMeter تخمینی از پهنای باند شبکه دردسترس را براساس نرخ‌های انتقال قبلی ارائه می‌دهد. اگر PreloadManager و پخش‌کننده از نمونه‌های جداگانه استفاده کنند، از فعالیت شبکه یکدیگر بی‌اطلاع خواهند بود که می‌تواند منجر به سناریوهای خرابی شود. برای مثال، سناریویی را درنظر بگیرید که در آن کاربر درحال تماشای ویدیو است، اتصال شبکه او ضعیف می‌شود، و MediaSource پیش‌بارگیری هم‌زمان بارگیری تهاجمی را برای ویدیو آینده آغاز می‌کند. فعالیت MediaSource پیش‌بارگیری‌شده پهنای باند موردنیاز پخش‌کننده فعال را مصرف می‌کند و باعث توقف ویدیو فعلی می‌شود. توقف درطول بازپخش، یک شکست قابل‌توجه در تجربه کاربری است.

با هم‌رسانی یک BandwidthMeter، TrackSelector می‌تواند قطعه‌های با بالاترین کیفیت را با توجه به شرایط فعلی شبکه و وضعیت میان‌گیر، درطول پیش‌بارگیری یا بازپخش انتخاب کند. سپس می‌تواند تصمیمات هوشمندانه‌ای برای محافظت از جلسه بازپخش فعال بگیرد و تجربه روانی را تضمین کند.

preloadManagerBuilder.setBandwidthMeter(customBandwidthMeter)

اطمینان از سازگاری با مؤلفه‌های LoadControl،‏ TrackSelector، و Renderer هم‌رسانی‌شده ExoPlayer

  • LoadControl: این مؤلفه خط‌مشی میان‌گیری را تعیین می‌کند، مثلاً اینکه قبل‌از شروع بازپخش چه مقدار داده میان‌گیری شود و چه زمانی بار کردن داده‌های بیشتر شروع یا متوقف شود. هم‌رسانی LoadControl تضمین می‌کند که مصرف حافظه پخش‌کننده و PreloadManager توسط یک استراتژی میان‌گیری هماهنگ و واحد در هر دو رسانه پیش‌بارشده و درحال پخش فعال هدایت می‌شود و از رقابت بر سر منابع جلوگیری می‌کند. برای اطمینان از ثبات، باید اندازه میان‌گیر را به‌طور هوشمندانه و هماهنگ با تعداد مواردی که پیش‌بارگیری می‌کنید و مدت‌زمان آن‌ها اختصاص دهید. در مواقع تداخل، پخش‌کننده بازپخش موردی را که درحال‌حاضر روی صفحه نمایش داده می‌شود در اولویت قرار می‌دهد. با LoadControl مشترک، مدیر پیش‌بارگیری تا زمانی که بایت‌های میان‌گیر هدف اختصاص‌داده‌شده برای پیش‌بارگیری به حد بالا نرسیده باشد، به پیش‌بارگیری ادامه می‌دهد و منتظر نمی‌ماند تا بارگیری برای بازپخش انجام شود.

توجه: هم‌رسانی LoadControl در جدیدترین نسخه Media3 (1.8) تضمین می‌کند که Allocator آن می‌تواند به‌درستی با PreloadManager و پخش‌کننده هم‌رسانی شود. استفاده از LoadControl برای کنترل مؤثر پیش‌بارگیری ویژگی‌ای است که در نسخه آتی Media3 1.9 دردسترس خواهد بود.

preloadManagerBuilder.setLoadControl(customLoadControl)

  • TrackSelector: این مؤلفه مسئول انتخاب قطعه‌هایی است که باید بارگیری و پخش شوند (برای مثال، ویدیو با وضوح خاص، صدا به زبان خاص). هم‌رسانی تضمین می‌کند که قطعه‌های انتخاب‌شده درطول پیش‌بارگیری همان قطعه‌هایی هستند که پخش‌کننده استفاده خواهد کرد. این کار از سناریو اتلاف‌کننده‌ای جلوگیری می‌کند که در آن قطعه ویدیو 480p پیش‌بارگیری می‌شود، اما پخش‌کننده بلافاصله آن را دور می‌اندازد و قطعه 720p را هنگام بازپخش واکشی می‌کند.< br /> مدیر پیش‌بارگیری نباید همان نمونه TrackSelector را با پخش‌کننده هم‌رسانی کند. درعوض، باید از نمونه TrackSelector متفاوت اما با همان پیاده‌سازی استفاده کنند. به همین دلیل است که TrackSelectorFactory را به‌جای TrackSelector در DefaultPreloadManager.Builder تنظیم می‌کنیم.

preloadManagerBuilder.setTrackSelectorFactory(customTrackSelectorFactory)

  • پردازنده: این مؤلفه مسئول درک قابلیت‌های پخش‌کننده بدون ایجاد پردازنده‌های کامل است. این طرح را بررسی می‌کند تا ببیند پخش‌کننده نهایی از کدام قالب‌های ویدیو، صدا، و نوشتار پشتیبانی خواهد کرد. این کار به آن اجازه می‌دهد تا به‌طور هوشمندانه فقط قطعه رسانه‌ای سازگار را انتخاب و بارگیری کند و از هدر رفتن پهنای باند برای محتوایی که پخش‌کننده واقعاً نمی‌تواند پخش کند جلوگیری می‌کند.

preloadManagerBuilder.setRenderersFactory(customRenderersFactory)

درباره مؤلفه‌های Exoplayer بیشتر بخوانید.

قانون طلایی: یک تکرارکننده بازپخش مشترک برای کنترل همه آن‌ها

رشته‌ای که می‌توان ازطریق آن به نمونه ExoPlayer دسترسی پیدا کرد را می‌توان با ارسال Looper هنگام ایجاد پخش‌کننده به‌طور صریح مشخص کرد. بااستفاده از Player.getApplicationLooper می‌توان Looper رشته‌ای را که باید از آن به پخش‌کننده دسترسی پیدا کرد پُرسمان کرد. با حفظ یک Looper مشترک بین پخش‌کننده و PreloadManager، تضمین می‌شود که تمام عملیات روی این اشیاء رسانه‌ای مشترک در صف پیام یک رشته واحد سریال‌سازی می‌شوند. این کار می‌تواند اشکالات همزمانی را کاهش دهد.

همه تعاملات بین PreloadManager و پخش‌کننده با منابع رسانه‌ای که باید بار شوند یا پیش‌بار شوند باید در همان رشته بازپخش اتفاق بیفتد. هم‌رسانی Looper برای ایمنی رشته ضروری است و بنابراین باید PlaybackLooper را بین PreloadManager و پخش‌کننده هم‌رسانی کنیم.

PreloadManager یک شیء MediaSource باوضعیت را در پس‌زمینه آماده می‌کند. وقتی کد واسط کاربر شما player.setMediaSource(mediaSource) را فراخوانی می‌کند، شما درحال انجام واگذاری این شیء پیچیده و دارای وضعیت از MediaSource پیش‌بارگیری به پخش‌کننده هستید. در این سناریو، کل PreloadMediaSource از مدیر به پخش‌کننده منتقل می‌شود. همه این تعامل‌ها و انتقال‌ها باید در همان PlaybackLooper انجام شود.

اگر PreloadManager و ExoPlayer روی رشته‌های مختلفی کار می‌کردند، ممکن بود شرایط رقابتی رخ دهد. رشته PreloadManager می‌تواند وضعیت داخلی MediaSource را (مثلاً با نوشتن داده‌های جدید در میان‌گیر) دقیقاً در همان لحظه‌ای که رشته پخش‌کننده درحال تلاش برای خواندن از آن است تغییر دهد. این امر منجر به رفتار غیرقابل‌پیش‌بینی، IllegalStateException می‌شود که اشکال‌زدایی آن دشوار است.

preloadManagerBuilder.setPreloadLooper(playbackLooper)

بیایید ببینیم چگونه می‌توانید همه مؤلفه‌های بالا را بین ExoPlayer و DefaultPreloadManager در خود راه‌اندازی هم‌رسانی کنید.

val preloadManagerBuilder =
DefaultPreloadManager.Builder(context, targetPreloadStatusControl)

// Optional - Share components between ExoPlayer and DefaultPreloadManager
preloadManagerBuilder
     .setBandwidthMeter(customBandwidthMeter)
     .setLoadControl(customLoadControl)
     .setMediaSourceFactory(customMediaSourceFactory)
     .setTrackSelectorFactory(customTrackSelectorFactory)
     .setRenderersFactory(customRenderersFactory)
     .setPreloadLooper(playbackLooper)

val preloadManager = val preloadManagerBuilder.build()

نکته: اگر از مؤلفه‌های «پیش‌فرض» در ExoPlayer مانند DefaultLoadControl و غیره استفاده می‌کنید، نیازی نیست آن‌ها را به‌طور صریح با DefaultPreloadManager هم‌رسانی کنید. وقتی نمونه ExoPlayer خود را ازطریق buildExoPlayer از DefaultPreloadManager.Builder می‌سازید، این مؤلفه‌ها به‌طور خودکار با یکدیگر ارجاع داده می‌شوند، اگر از پیاده‌سازی‌های پیش‌فرض با پیکربندی‌های پیش‌فرض استفاده کنید. اما اگر از مؤلفه‌های سفارشی یا پیکربندی‌های سفارشی استفاده می‌کنید، باید به‌طور صریح ازطریق میاناهای برنامه‌سازی کاربردی بالا به DefaultPreloadManager اطلاع دهید.

پیش‌بارگذاری آماده تولید: الگوی پنجره لغزان

در فید پویا، کاربر می‌تواند در مقدار تقریباً نامحدودی از محتوا پیمایش کند. اگر به‌طور مداوم ویدیوها را بدون استراتژی حذف مربوطه به DefaultPreloadManager اضافه کنید، ناگزیر باعث OutOfMemoryError خواهید شد. هر MediaSource پیش‌بارشده به یک SampleQueue متصل است که میان‌گیرهای حافظه را اختصاص می‌دهد. با انباشته شدن این موارد، ممکن است فضای پشته برنامه تمام شود. راه حل الگوریتمی است که ممکن است ازقبل با آن آشنا باشید، به نام پنجره لغزنده. الگوی پنجره لغزنده مجموعه کوچک و قابل‌مدیریتی از موارد را در حافظه نگه می‌دارد که ازنظر منطقی در مجاورت موقعیت فعلی کاربر در فید قرار دارند. با پیمایش کاربر، این «پنجره» از موارد مدیریت‌شده با او حرکت می‌کند، موارد جدیدی را که در دید قرار می‌گیرند اضافه می‌کند، و همچنین مواردی را که اکنون دور شده‌اند حذف می‌کند.

slidingwindow.png

پیاده‌سازی الگوی پنجره لغزشی

باید بدانید که PreloadManager روش setWindowSize()‎ داخلی ارائه نمی‌دهد. پنجره لغزان الگوی طراحی‌ای است که شما، به‌عنوان توسعه‌دهنده، مسئول پیاده‌سازی آن بااستفاده از روش‌های add()‎ و remove()‎ هستید. منطق برنامه شما باید رویدادهای میانای کاربر، مانند پیمایش یا تغییر صفحه، را به این فراخوانی‌های میانای برنامه‌سازی کاربردی متصل کند. اگر می‌خواهید مرجع کدی برای این موضوع داشته باشید، این الگوی پنجره لغزنده را در نمونه socialite پیاده‌سازی کرده‌ایم که شامل PreloadManagerWrapper نیز می‌شود که پنجره لغزنده را تقلید می‌کند.

فراموش نکنید که preloadManager.remove(mediaItem) را در پیاده‌سازی خود اضافه کنید، زمانی که دیگر احتمال ندارد مورد به‌زودی در نمای کاربر ظاهر شود. عدم حذف مواردی که دیگر نزدیک کاربر نیستند، علت اصلی مشکلات حافظه در پیاده‌سازی‌های پیش‌بارگذاری است. فراخوانی()remove تضمین می‌کند که منابع آزاد می‌شوند و این به شما کمک می‌کند استفاده از حافظه برنامه‌تان را محدود و پایدار نگه دارید.

تنظیم دقیق راهبرد پیش‌بارگیری دسته‌بندی‌شده با TargetPreloadStatusControl

اکنون که مشخص کردیم چه چیزی را پیش‌بار کنیم (موارد موجود در پنجره)، می‌توانیم استراتژی مشخصی را برای مقدار پیش‌بار کردن هر مورد اعمال کنیم. قبلاً دیدیم که چگونه می‌توان با راه‌اندازی TargetPreloadStatusControl در بخش ۱ به این سطح از جزئیات دست یافت.

برای یادآوری، موردی در موقعیت ۱-/+ می‌تواند احتمال پخش بالاتری نسبت به موردی در موقعیت ۴-/+ داشته باشد. می‌توانید منابع بیشتری (شبکه، واحد پردازش مرکزی، حافظه) را به مواردی اختصاص دهید که احتمالاً کاربر در مرحله بعد مشاهده خواهد کرد. این کار یک استراتژی «پیش‌بارگیری» براساس مجاورت ایجاد می‌کند که کلید ایجاد تعادل بین بازپخش فوری و استفاده کارآمد از منابع است.

می‌توانید از داده‌های تجزیه‌وتحلیل ازطریق PreloadManagerListener، همان‌طور که در بخش‌های قبلی بحث شد، برای تصمیم‌گیری درباره استراتژی مدت پیش‌بارگیری استفاده کنید.

نتیجه‌گیری و مراحل بعدی

اکنون به دانش پیشرفته برای ساختن فیدهای رسانه‌ای سریع، پایدار، و با مصرف بهینه منابع بااستفاده از DefaultPreloadManager در Media3 مجهز شده‌اید.

بیایید نکات کلیدی را مرور کنیم:

  • از PreloadManagerListener برای جمع‌آوری اطلاعات آماری تحلیلی و پیاده‌سازی مدیریت خطای قوی استفاده کنید.
  • همیشه از یک DefaultPreloadManager.Builder برای ایجاد نمونه‌های مدیر و پخش‌کننده استفاده کنید تا مطمئن شوید مؤلفه‌های مهم هم‌رسانی می‌شوند.
  • الگوی پنجره لغزنده را با مدیریت فعال فراخوانی‌های add()‎ و remove()‎ پیاده‌سازی کنید تا از OutOfMemoryError جلوگیری شود.
  • از TargetPreloadStatusControl برای ایجاد یک استراتژی پیش‌بارگذاری هوشمند و لایه‌ای استفاده کنید که عملکرد و مصرف منابع را متعادل می‌کند.

مورد بعدی در بخش ۳: ذخیره کردن در حافظه نهان با رسانه پیش‌بارگیری‌شده

پیش‌بارگیری داده‌ها در حافظه مزیت عملکردی فوری دارد، اما می‌تواند با معاوضه‌هایی همراه باشد. وقتی برنامه بسته شود یا رسانه پیش‌بارشده از مدیر برداشته شود، داده‌ها ازبین می‌روند. برای دستیابی به سطح پایدارتری از بهینه‌سازی، می‌توانیم پیش‌بارگیری را با حافظه نهان دیسک ترکیب کنیم. این ویژگی درحال توسعه فعال است و به‌زودی در چند ماه آینده ارائه خواهد شد.

آیا بازخوردی برای هم‌رسانی دارید؟ مشتاقیم از شما بشنویم.

منتظر بمانید و بازپخش ویدیو را سریع‌تر کنید! 🚀

نویسنده:
ادامه خواندن