اخبار محصول

ارتقا دادن بازپخش رسانه: ژرف‌کاوی در 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، «انتخابگر قطعه» می‌تواند قطعه‌های با بالاترین کیفیت را با درنظر گرفتن شرایط شبکه فعلی و وضعیت میان‌گیر درطول پیش‌بارگیری یا بازپخش انتخاب کند. سپس می‌تواند تصمیمات هوشمندانه‌ای برای محافظت از جلسه بازپخش فعال بگیرد و تجربه روان را تضمین کند.

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() هستید. منطق برنامه شما باید رویدادهای رابط کاربری، مانند پیمایش یا تغییر صفحه، را به این تماس‌های API متصل کند. اگر مرجع کد برای این می‌خواهید، این الگوی پنجره لغزنده را در نمونه socialite پیاده‌سازی کرده‌ایم که شامل PreloadManagerWrapper نیز می‌شود که پنجره لغزنده را تقلید می‌کند.

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

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

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

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

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

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

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

بیایید نکات کلیدی را خلاصه کنیم:

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

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

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

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

با ما همراه باشید و سرعت بازپخش ویدیو را افزایش دهید! 🚀

نوشته:
ادامه خواندن