Berita Produk

Meningkatkan kualitas pemutaran media: Memperkenalkan pra-pemuatan dengan Media3 - Bagian 1

Waktu baca: 8 menit
Mayuri Khinvasara Khabya
Developer Relations Engineer

Dalam aplikasi yang berfokus pada media saat ini, memberikan pengalaman pemutaran yang lancar dan tanpa gangguan adalah kunci untuk pengalaman pengguna yang menyenangkan. Pengguna berharap video mereka dimulai secara instan dan diputar dengan lancar tanpa jeda.

Tantangan utamanya adalah latensi. Biasanya, pemutar video hanya memulai pekerjaannya—menghubungkan, mendownload, mengurai, melakukan buffering—setelah pengguna memilih item untuk diputar. Pendekatan reaktif ini lambat untuk konteks video pendek saat ini. Solusinya adalah bersikap proaktif. Kita perlu mengantisipasi apa yang akan ditonton pengguna selanjutnya dan menyiapkan kontennya terlebih dahulu. Inilah inti dari pra-pemuatan.

Manfaat utama pemuatan awal meliputi:

  • 🚀 Mulai Pemutaran Lebih Cepat: Video sudah siap diputar, sehingga transisi antar-item lebih cepat dan mulai lebih instan.
  • 📉 Pengurangan Penyangga: Dengan memuat data secara proaktif, pemutaran jauh lebih kecil kemungkinannya terhenti, misalnya karena gangguan jaringan.
  • ✨ Pengalaman Pengguna yang lebih lancar: Kombinasi antara mulai yang lebih cepat dan buffering yang lebih sedikit menciptakan interaksi yang lebih lancar dan mulus untuk dinikmati pengguna.

Dalam seri tiga bagian ini, kita akan memperkenalkan dan mempelajari utilitas canggih Media3 untuk memuat (pra-muat) komponen.

  • Di Bagian 1, kita akan membahas dasar-dasarnya: memahami berbagai strategi pemuatan awal yang tersedia di Media3, mengaktifkan PreloadConfiguration dan menyiapkan DefaultPreloadManager, serta mengaktifkan aplikasi Anda untuk memuat item awal. Di akhir blog ini, Anda akan dapat memuat dan memutar item media dengan peringkat dan durasi yang telah dikonfigurasi.
  • Di Bagian 2, kita akan membahas topik DefaultPreloadManager yang lebih lanjut: menggunakan pemroses untuk analisis, mempelajari praktik terbaik siap produksi seperti pola jendela geser dan komponen bersama kustom DefaultPreloadManager dan ExoPlayer.
  • Di Bagian 3, kita akan mempelajari lebih dalam penyiapan cache disk dengan DefaultPreloadManager.

Pramuat adalah solusinya. 🦸‍♀️

Ide inti di balik pemuatan awal sangat sederhana: muat konten media sebelum Anda membutuhkannya. Saat pengguna menggeser ke video berikutnya, segmen pertama video sudah didownload dan tersedia, siap untuk diputar segera.

Anggap saja seperti restoran. Dapur yang sibuk tidak menunggu pesanan untuk mulai memotong bawang. 🧅 Mereka melakukan persiapan terlebih dahulu. Pramuat adalah persiapan untuk pemutar video Anda.

Jika diaktifkan, pemuatan awal dapat membantu meminimalkan latensi bergabung saat pengguna melompat ke item berikutnya sebelum buffer pemutaran mencapai item berikutnya. Periode pertama jendela berikutnya disiapkan dan sampel video, audio, dan teks di-buffer. Periode yang telah dimuat sebelumnya kemudian dimasukkan ke dalam antrean pemutar dengan sampel yang di-buffer segera tersedia dan siap dimasukkan ke codec untuk rendering.

Di Media3, ada dua API utama untuk memuat sebelumnya, yang masing-masing cocok untuk kasus penggunaan yang berbeda. Memilih API yang tepat adalah langkah pertama.

1. Memuat item playlist terlebih dahulu dengan PreloadConfiguration

Ini adalah pendekatan sederhana, yang berguna untuk media linear dan berurutan seperti playlist yang urutan pemutarannya dapat diprediksi (seperti serangkaian episode). Anda memberikan daftar lengkap item media kepada pemain menggunakan API playlist ExoPlayer dan menetapkan PreloadConfiguration untuk pemutar, lalu pemutar akan otomatis memuat item berikutnya dalam urutan seperti yang dikonfigurasi. API ini mencoba mengoptimalkan latensi bergabung saat pengguna melompat ke item berikutnya sebelum buffer pemutaran sudah tumpang-tindih ke item berikutnya.

Pemuatan awal hanya dimulai saat tidak ada media yang dimuat untuk pemutaran yang sedang berlangsung, sehingga mencegahnya bersaing untuk bandwidth dengan pemutaran utama.

Jika Anda masih tidak yakin apakah Anda memerlukan pemuatan awal, API ini adalah opsi yang mudah untuk mencobanya.

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

Dengan PreloadConfiguration di atas, pemutar mencoba memuat lima detik media untuk item berikutnya dalam playlist.

Setelah diaktifkan, pemuatan awal playlist dapat dinonaktifkan lagi dengan menggunakan PreloadConfiguration.DEFAULT untuk menonaktifkan pemuatan awal playlist:

  player.preloadConfiguration = PreloadConfiguration.DEFAULT

2. Memuat daftar dinamis terlebih dahulu dengan PreloadManager

Untuk UI dinamis seperti feed vertikal atau carousel, tempat item "berikutnya" ditentukan oleh interaksi pengguna, PreloadManager API cocok digunakan. Komponen ini adalah komponen mandiri baru yang canggih dalam library Media3 ExoPlayer yang dirancang khusus untuk melakukan pra-pemuatan secara proaktif. Library ini mengelola kumpulan MediaSource potensial, memprioritaskannya berdasarkan kedekatan dengan posisi pengguna saat ini, dan menawarkan kontrol terperinci atas konten yang akan di-preload, yang cocok untuk skenario kompleks seperti feed dinamis video bentuk pendek.

Menyiapkan PreloadManager

DefaultPreloadManager adalah implementasi kanonis untuk PreloadManager.

Builder DefaultPreloadManager dapat membuat DefaultPreloadManager dan instance ExoPlayer yang akan memutar konten yang telah di-preload. Untuk membuat DefaultPreloadManager, Anda harus meneruskan TargetPreloadStatusControl, yang dapat dikueri oleh pengelola pramuat untuk mengetahui jumlah yang akan dimuat untuk suatu item. Kami akan menjelaskan dan menentukan contoh TargetPreloadStatusControl di bagian di bawah.

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

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

Penggunaan builder yang sama untuk ExoPlayer dan DefaultPreloadManager diperlukan, yang memastikan bahwa komponen di balik keduanya dibagikan dengan benar.

Selesai. Sekarang Anda memiliki pengelola yang siap menerima petunjuk.

Mengonfigurasi Durasi dan Peringkat dengan TargetPreloadStatusControl

Bagaimana jika Anda ingin memuat sebelumnya, misalnya, 10 detik video? Anda dapat memberikan posisi item media di carousel, dan DefaultPreloadManager memprioritaskan pemuatan item berdasarkan seberapa dekat item tersebut dengan item yang sedang diputar pengguna.

Jika Anda ingin mengontrol durasi item yang akan dimuat sebelumnya, Anda dapat menyatakannya dengan DefaultPreloadManager.PreloadStatus yang Anda kembalikan.

Misalnya,

  • Item 'A' adalah prioritas tertinggi, muat video 5 detik.
  • Item 'B' adalah prioritas sedang, tetapi saat Anda mencapainya, muat video 3 detik.
  • Item 'C' memiliki prioritas lebih rendah, hanya melacak pemuatan.
  • Item 'D' bahkan kurang menjadi prioritas, cukup persiapkan.
  • Item lain berada jauh, Jangan memuat data apa pun terlebih dahulu.

Kontrol terperinci ini dapat membantu Anda mengoptimalkan penggunaan resource yang direkomendasikan untuk pemutaran yang lancar.

  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
            }
}

Tips: PreloadManager dapat menjaga agar item sebelumnya dan berikutnya tetap dimuat sebelumnya, sedangkan PreloadConfiguration hanya akan melihat ke depan ke item berikutnya.

Mengelola Item Pemuatan Awal

Setelah membuat pengelola, Anda dapat mulai memberi tahu tugas yang harus dikerjakan. Saat pengguna men-scroll feed, Anda akan mengidentifikasi video mendatang dan menambahkannya ke pengelola. Interaksi dengan PreloadManager adalah percakapan berbasis status antara UI Anda dan mesin pra-pemuatan.

1. Menambahkan Item Media

Saat mengisi feed, Anda harus memberi tahu pengelola media yang perlu dilacak. Jika baru memulai, Anda dapat menambahkan seluruh daftar yang ingin di-preload. Selanjutnya, Anda dapat terus menambahkan satu item ke daftar sesuai kebutuhan. Anda memiliki kontrol penuh atas item yang ada dalam daftar pra-pemuatan, yang berarti Anda juga harus mengelola item yang ditambahkan dan dihapus dari pengelola.

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

Pengelola sekarang akan mulai mengambil data untuk MediaItem ini di latar belakang.

Setelah menambahkan, beri tahu pengelola untuk mengevaluasi ulang daftar barunya (mengisyaratkan bahwa ada sesuatu yang telah berubah seperti menambahkan/ menghapus item, atau pengguna beralih untuk memutar item baru).

  preloadManager.invalidate()

2. Mengambil dan Memutar Item

Berikut logika pemutaran utama. Saat pengguna memutuskan untuk memutar video tersebut, Anda tidak perlu membuat MediaSource baru. Sebagai gantinya, Anda meminta PreloadManager untuk memberikan yang sudah disiapkannya. Anda dapat mengambil MediaSource dari Pengelola Pemuatan Awal menggunakan MediaItem.

Jika item yang diambil dari PreloadManager adalah null, berarti mediaItem belum di-preload atau ditambahkan ke PreloadManager, jadi Anda memilih untuk menyetel mediaItem secara langsung.

  // 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()

Dengan menyiapkan MediaSource yang diambil dari PreloadManager, Anda dapat bertransisi dengan lancar dari pra-pemuatan ke pemutaran, menggunakan data yang sudah ada dalam memori. Hal inilah yang membuat waktu mulai lebih cepat.

3. Menjaga indeks saat ini tetap sinkron dengan UI

Karena feed / daftar kita bisa bersifat dinamis, penting untuk memberi tahu PreloadManager tentang indeks pemutaran saat ini agar PreloadManager selalu dapat memprioritaskan item yang paling dekat dengan indeks saat ini untuk pra-pemuatan.

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

4. Menghapus Item

Agar pengelola tetap efisien, Anda harus menghapus item yang tidak perlu lagi dilacak, seperti item yang jauh dari posisi pengguna saat ini.

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

Jika perlu menghapus semua item sekaligus, Anda dapat memanggil preloadManager.reset().

5. Melepaskan Pengelola

Jika tidak lagi memerlukan PreloadManager (misalnya, saat UI Anda dihancurkan), Anda harus melepaskannya untuk membebaskan resource-nya. Tempat yang tepat untuk melakukannya adalah saat Anda sudah merilis resource Player. Sebaiknya lepaskan pengelola sebelum pemain karena pemain dapat terus bermain jika Anda tidak memerlukan lagi pra-pemuatan.

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

Waktu demo

Lihat langsung cara kerjanya 👍

Dalam demo di bawah , kita dapat melihat dampak PreloadManager di sisi kanan yang memiliki waktu pemuatan lebih cepat, sedangkan sisi kiri menunjukkan pengalaman yang ada. Anda juga dapat melihat contoh kode untuk demo. (Bonus: Alat ini juga menampilkan latensi startup untuk setiap video)

Demo-PreloadManager_2.webp

Apa Selanjutnya?

Selesai sudah Bagian 1. Sekarang Anda memiliki alat untuk membangun sistem pra-pemuatan dinamis. Anda dapat menggunakan PreloadConfiguration untuk memuat item berikutnya dari playlist di ExoPlayer atau menyiapkan DefaultPreloadManager, menambahkan dan menghapus item dengan cepat, mengonfigurasi status pramuat target, dan mengambil konten yang dimuat sebelumnya dengan benar untuk pemutaran.

Di Bagian 2, kita akan membahas DefaultPreloadManager lebih dalam. Kita akan mempelajari cara memproses peristiwa pra-pemuatan, membahas praktik terbaik seperti penggunaan jendela geser untuk menghindari masalah memori, dan mengintip komponen bersama kustom ExoPlayer dan DefaultPreloadManager.

Apakah ada masukan yang ingin Anda sampaikan? Kami ingin mendengar pendapat Anda.

Nantikan info selanjutnya, dan buat aplikasi Anda berjalan lebih cepat. 🚀

Ditulis oleh:

Lanjutkan membaca