Streaming media dengan ExoPlayer

1. Sebelum memulai

526b239733391e74.png

Screenshot: Aplikasi Android YouTube

ExoPlayer adalah pemutar media tingkat aplikasi yang dibuild di atas API media tingkat rendah di Android. ExoPlayer memiliki sejumlah keunggulan dibandingkan MediaPlayer bawaan di Android. Pemutar ini mendukung banyak format media yang sama seperti MediaPlayer, ditambah format adaptif, seperti DASH dan SmoothStreaming. ExoPlayer sangat mudah disesuaikan dan diperluas, sehingga mampu berfungsi dalam banyak kasus penggunaan tingkat lanjut. Pemutar ini adalah project open source yang digunakan oleh aplikasi Google, termasuk YouTube dan Google Play Movies & TV.

Prasyarat

  • Pengetahuan pengembangan Android dan Android Studio tingkat menengah

Yang akan Anda lakukan

  • Membuat instance SimpleExoPlayer, yang menyiapkan dan memutar media dari berbagai sumber.
  • Mengintegrasikan ExoPlayer dengan siklus proses aktivitas aplikasi untuk mendukung layanan latar belakang, latar depan, dan pemutaran kembali dalam lingkungan tunggal atau multi-jendela.
  • Menggunakan MediaItem untuk membuat playlist.
  • Memutar streaming video adaptif, yang menyesuaikan kualitas media dengan bandwidth yang tersedia.
  • Mendaftarkan pemroses peristiwa untuk memantau status pemutaran dan menunjukkan cara pemroses dapat digunakan untuk mengukur kualitas pemutaran.
  • Menggunakan komponen UI ExoPlayer standar, kemudian menyesuaikannya dengan gaya aplikasi Anda.

Yang akan Anda butuhkan

  • Versi stabil terbaru Android Studio.
  • Perangkat Android dengan JellyBean (4.1) atau yang lebih tinggi, idealnya dengan Nougat (7.1) atau yang lebih tinggi karena mendukung multi-jendela.

2. Memulai persiapan

Mendapatkan kode

Untuk memulai, download project Android Studio:

Atau, Anda dapat membuat clone repositori GitHub:

git clone https://github.com/googlecodelabs/exoplayer-intro.git

Struktur direktori

Membuat clone atau unzip memberi Anda folder root (exoplayer-intro), yang berisi project gradle tunggal dengan beberapa modul; satu modul aplikasi dan satu modul untuk setiap langkah codelab ini, bersama dengan semua materi yang Anda butuhkan.

Mengimpor project

  1. Mulai Android Studio.
  2. Pilih File > New > Import Project*.*
  3. Pilih file build.gradle root.

111b190903697765.png

Screenshot: Struktur project saat mengimpor

Setelah build selesai, Anda akan melihat enam modul: modul app (tipe application) dan lima modul dengan nama exoplayer-codelab-N (dengan N adalah 00 hingga 04,, masing-masing tipe library). Modul app sebenarnya kosong, hanya memiliki satu manifes. Semua materi dari modul yang ditentukan saat ini exoplayer-codelab-N digabungkan ketika aplikasi di-build menggunakan dependensi gradle di app/build.gradle.

app/build.gradle

dependencies {
   implementation project(":exoplayer-codelab-00")
}

Aktivitas pemutar media Anda disimpan di modul exoplayer-codelab-N. Alasan untuk menyimpannya dalam modul library terpisah adalah agar Anda dapat membagikannya di antara APK dengan target platform yang berbeda, seperti seluler dan Android TV. Ini juga memungkinkan Anda untuk memanfaatkan fitur-fitur, seperti Pengiriman Dinamis, yang memungkinkan fitur pemutaran media Anda diinstal hanya ketika pengguna membutuhkannya.

  1. Deploy dan jalankan aplikasi untuk memeriksa semuanya berjalan lancar. Aplikasi harus mengisi layar dengan latar belakang hitam.

2dae13fed92e6c8c.png

Screenshot: Aplikasi kosong berjalan

3. Streaming!

Menambahkan dependensi ExoPlayer

ExoPlayer adalah project open source yang dihosting di GitHub. Setiap rilis didistribusikan melalui Google Maven, yang merupakan salah satu repositori paket default yang digunakan oleh Android Studio dan Gradle. Setiap rilis diidentifikasi secara unik oleh string dengan format berikut:

com.google.android.exoplayer:exoplayer:X.X.X

Anda dapat menambahkan ExoPlayer ke project Anda hanya dengan mengimpor class dan komponen UI-nya. Ukurannya cukup kecil, memiliki footprint yang diperkecil sekitar 70 sampai 300 kB, tergantung pada fitur yang disertakan dan format yang didukung. Library ExoPlayer dibagi menjadi modul-modul untuk memungkinkan developer mengimpor fungsionalitas yang mereka butuhkan saja. Untuk informasi lebih lanjut tentang struktur modular ExoPlayer, lihat Menambahkan modul ExoPlayer.

  1. Buka file build.gradle dari modul player-lib.
  2. Tambahkan baris berikut ke bagian dependencies dan sinkronkan project.

exoplayer-codelab-00/build.gradle

dependencies {
   [...]

implementation 'com.google.android.exoplayer:exoplayer-core:2.12.0'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.12.0'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.12.0'

}

Tambahkan PlayerView element

  1. Buka file resource tata letak activity_player.xml dari modul exoplayer-codelab-00.
  2. Tempatkan kursor di dalam elemen FrameLayout.
  3. Mulailah mengetik <PlayerView dan biarkan Android Studio melengkapi otomatis elemen PlayerView.
  4. Gunakan match_parent untuk width dan height.
  5. Deklarasikan ID sebagai video_view.

activity_player.xml

<com.google.android.exoplayer2.ui.PlayerView
   android:id="@+id/video_view"
   android:layout_width="match_parent"
   android:layout_height="match_parent"/>

Untuk ke depannya, Anda merujuk ke elemen UI ini sebagai tampilan video.

  1. Di PlayerActivity, Anda kini dapat memperoleh referensi ke hierarki tampilan yang dibuat dari file XML yang baru saja Anda edit.

PlayerActivity.kt

    private val viewBinding by lazy(LazyThreadSafetyMode.NONE) {
        ActivityPlayerBinding.inflate(layoutInflater)
    }
  1. Setel root pohon tampilan Anda sebagai tampilan konten Aktivitas Anda. Periksa juga apakah properti videoView terlihat di referensi viewBinding Anda, dan tipenya adalah PlayerView.
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(viewBinding.root)
    }

Membuat ExoPlayer

Untuk memutar media streaming, Anda memerlukan objek ExoPlayer. Cara paling sederhana untuk membuatnya adalah dengan menggunakan class SimpleExoPlayer.Builder. Seperti namanya, class ini menggunakan pola builder untuk mem-build instance SimpleExoPlayer.

SimpleExoPlayer adalah implementasi yang nyaman dan serba guna dari antarmuka ExoPlayer.

Tambahkan metode pribadi initializePlayer untuk membuat SimpleExoPlayer Anda.

PlayerActivity.kt

private var player: SimpleExoPlayer? = null
[...]
   private fun initializePlayer() {
        player = SimpleExoPlayer.Builder(this)
            .build()
            .also { exoPlayer ->
                viewBinding.videoView.player = exoPlayer
            }
    }

Buat SimpleExoPlayer.Builder menggunakan konteks Anda, lalu panggil build untuk membuat objek SimpleExoPlayer Anda. Kemudian, tugaskan ke player, yang perlu Anda deklarasikan sebagai kolom anggota. Anda kemudian menggunakan properti yang dapat diubah viewBinding.videoView.player untuk melakukan binding player ke tampilan yang sesuai.

Membuat item media

Sekarang player Anda membutuhkan konten untuk diputar. Untuk itu, Anda perlu membuat MediaItem. Ada berbagai jenis MediaItem, tetapi mulailah dengan membuat satu untuk file MP3 di internet.

Cara paling sederhana untuk membuat MediaItem adalah dengan menggunakan MediaItem.fromUri, yang menerima URI file media. Tambahkan MediaItem ke player menggunakan player.setMediaItem.

  1. Tambahkan kode berikut ke initializePlayer di dalam block also:

PlayerActivity.kt

private fun initializePlayer() {
    [...]
        .also { exoPlayer ->
            [...]
            val mediaItem = MediaItem.fromUri(getString(R.string.media_url_mp3))
            exoPlayer.setMediaItem(mediaItem)
        }
}

Perlu diperhatikan bahwa R.string.media_url_mp3 ditentukan sebagai https://storage.googleapis.com/exoplayer-test-media-0/play.mp3 di strings.xml.

Berbagi siklus proses Aktivitas dengan baik

player kita dapat menghabiskan banyak resource termasuk memori, CPU, koneksi jaringan, dan codec hardware. Ketersediaan sebagian besar resource ini tidak banyak, terutama untuk codec hardware yang mungkin hanya ada satu. Anda harus melepaskan resource tersebut untuk digunakan aplikasi lain saat tidak menggunakannya, seperti saat aplikasi Anda ditempatkan di latar belakang.

Dengan kata lain, siklus proses pemutar Anda harus terikat dengan siklus proses aplikasi Anda. Untuk menerapkan ini, Anda perlu mengganti empat metode PlayerActivity: onStart, onResume, onPause, dan onStop.

  1. Dengan PlayerActivity yang terbuka, klik Code menu > Override methods....
  2. Pilih onStart, onResume, onPause, dan onStop.
  3. Lakukan inisialisasi pemutar di callback onStart atau onResume, tergantung pada level API.

PlayerActivity.kt

public override fun onStart() {
 super.onStart()
 if (Util.SDK_INT >= 24) {
   initializePlayer()
 }
}

public override fun onResume() {
 super.onResume()
 hideSystemUi()
 if ((Util.SDK_INT < 24 || player == null)) {
   initializePlayer()
 }
}

API Android level 24 dan yang lebih tinggi mendukung multi-jendela. Karena aplikasi Anda dapat terlihat, tetapi tidak aktif dalam mode jendela terpisah, Anda perlu melakukan inisialisasi pada pemutar di onStart. API Android level 24 dan yang lebih rendah mengharuskan Anda menunggu selama mungkin sampai Anda mengambil resource, jadi tunggu sampai onResume sebelum melakukan inisialisasi pada pemutar.

  1. Tambahkan metode hideSystemUi.

PlayerActivity.kt

@SuppressLint("InlinedApi")
private fun hideSystemUi() {
 viewBinding.videoView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE
     or View.SYSTEM_UI_FLAG_FULLSCREEN
     or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
     or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
     or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
     or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
}

hideSystemUi adalah metode helper yang dipanggil di onResume, yang memungkinkan Anda untuk memiliki pengalaman layar penuh.

  1. Lepaskan resource dengan releasePlayer (yang akan segera Anda buat) di onPause dan onStop.

PlayerActivity.kt

public override fun onPause() {
 super.onPause()
 if (Util.SDK_INT < 24) {
   releasePlayer()
 }
}

public override fun onStop() {
 super.onStop()
 if (Util.SDK_INT >= 24) {
   releasePlayer()
 }
}

Dengan API Level 24 dan yang lebih rendah, tidak ada jaminan onStop akan dipanggil, jadi Anda harus melepaskan pemutar secepat mungkin di onPause. Dengan API Level 24 dan yang lebih tinggi (yang membawa mode multi-jendela dan jendela-terpisah), onStop pasti dipanggil. Dalam keadaan dijeda, aktivitas Anda masih terlihat, jadi Anda perlu menunggu untuk melepaskan pemutar sampai onStop.

Sekarang Anda perlu membuat metode releasePlayer, yang membebaskan resource pemutar dan menghancurkannya.

  1. Tambahkan kode berikut ke aktivitas:

PlayerActivity.kt

private var playWhenReady = true
private var currentWindow = 0
private var playbackPosition = 0L
[...]

private fun releasePlayer() {
    player?.run {
        playbackPosition = this.currentPosition
        currentWindow = this.currentWindowIndex
        playWhenReady = this.playWhenReady
        release()
    }
    player = null
}

Sebelum Anda melepaskan dan menghancurkan pemutar, simpan informasi berikut:

  • Putar/jeda status menggunakan playWhenReady.
  • Posisi pemutaran saat ini menggunakan currentPosition.
  • Indeks jendela saat ini menggunakancurrentWindowIndex. Untuk informasi lebih lanjut tentang jendela, lihat Timeline.

Tindakan ini memungkinkan Anda untuk melanjutkan pemutaran dari tempat terakhir yang ditinggalkan pengguna. Yang perlu Anda lakukan adalah memberikan informasi status ini saat Anda melakukan inisialisasi pada pemutar.

Persiapan akhir

Yang perlu Anda lakukan sekarang adalah memberikan informasi status yang Anda simpan di releasePlayer ke pemutar Anda selama inisialisasi.

  1. Tambahkan kode berikut ke initializePlayer:

PlayerActivity.kt

private fun initializePlayer() {
    [...]
    exoPlayer.playWhenReady = playWhenReady
    exoPlayer.seekTo(currentWindow, playbackPosition)
    exoPlayer.prepare()
}

Berikut ini yang terjadi:

  • playWhenReady memberi tahu pemutar apakah akan memulai pemutaran segera setelah semua resource untuk pemutaran didapatkan. Karena playWhenReady awalnya adalah true, pemutaran dimulai secara otomatis saat pertama kali aplikasi berjalan.
  • seekTo memberi tahu pemutar untuk mencari posisi tertentu dalam jendela tertentu. currentWindow dan playbackPosition diinisialisasi ke nol sehingga pemutaran dimulai dari awal saat pertama kali aplikasi berjalan.
  • prepare memberi tahu pemutar untuk mendapatkan semua resource yang diperlukan untuk pemutaran.

Memutar audio

Akhirnya, tugas Anda selesai! Mulai aplikasi untuk memutar file MP3 dan lihat sematan karya seninya.

d92917867ee23ef8.png

Screenshot: Aplikasi memainkan satu lagu.

Menguji siklus proses aktivitas

Uji apakah aplikasi berfungsi di berbagai status siklus proses aktivitas.

  1. Mulai aplikasi lain dan tempatkan aplikasi Anda di latar depan lagi. Apakah aplikasi melanjutkan pada posisi yang benar?
  2. Jeda aplikasi, pindahkan ke latar belakang, lalu ke latar depan lagi. Apakah aplikasi tetap dalam keadaan dijeda ketika ditempatkan di latar belakang dalam keadaan dijeda?
  3. Putar aplikasi. Bagaimana perilakunya jika Anda mengubah orientasi dari potret ke lanskap dan sebaliknya?

Memutar video

Jika Anda ingin memutar video, caranya semudah mengubah URI item media menjadi file MP4.

  1. Ubah URI di initializePlayer ke R.string.media_url_mp4.
  2. Mulai kembali aplikasi dan uji perilakunya setelah ditempatkan di latar belakang dengan pemutaran video juga.

PlayerActivity.kt

private fun initializePlayer() {
  [...]
     val mediaItem = MediaItem.fromUri(getString(R.string.media_url_mp4));
  [...]
}

PlayerView melakukan semuanya. Video dirender ke layar penuh, bukan karya seni.

425c6c65f78e8d46.png

Screenshot: Aplikasi sedang memutar video.

Anda hebat! Anda baru saja membuat aplikasi untuk streaming media dengan layar penuh di Android, lengkap dengan pengelolaan siklus proses, status tersimpan, serta kontrol UI!

4. Membuat playlist

Aplikasi Anda saat ini memutar satu file media, tetapi bagaimana jika Anda ingin memutar lebih dari satu file media, satu demi satu? Untuk itu, Anda memerlukan playlist.

Playlist dapat dibuat dengan menambahkan lebih banyak MediaItem ke player Anda menggunakan addMediaItem. Hal ini memungkinkan pemutaran yang lancar, dan buffering ditangani di latar belakang sehingga pengguna tidak melihat indikator buffering dengan lingkaran berputar ketika mengubah item media.

  1. Tambahkan kode berikut ke initializePlayer:

PlayerActivity.kt

private void initializePlayer() {
  [...]
  exoPlayer.addMediaItem(mediaItem) // Existing code

  val secondMediaItem = MediaItem.fromUri(getString(R.string.media_url_mp3));
  exoPlayer.addMediaItem(secondMediaItem);
  [...]
}

Periksa perilaku kontrol pemutar. Anda dapat menggunakan 1f79fee4d082870f.png dan 39627002c03ce320.png untuk melihat urutan item media.

7b5c034dafabe1bd.png

Screenshot: Kontrol pemutaran menampilkan tombol sebelumnya dan berikutnya

Itu sangat mudah! Untuk informasi lebih lanjut, lihat dokumentasi developer di Item Media dan Playlist, dan artikel ini tentang API Playlist.

5. Streaming adaptif

Streaming adaptif adalah teknik streaming media dengan memvariasikan kualitas streaming berdasarkan bandwidth jaringan yang tersedia. Hal ini memungkinkan pengguna untuk menikmati media dengan kualitas terbaik yang dimungkinkan bandwidth mereka.

Biasanya, konten media yang sama dibagi menjadi beberapa trek dengan kualitas berbeda (kecepatan bit dan resolusi). Pemutar memilih trek berdasarkan bandwidth jaringan yang tersedia.

Setiap trek dibagi menjadi beberapa bagian dengan durasi tertentu, biasanya antara 2 sampai 10 detik. Ini memungkinkan pemutar untuk beralih dengan cepat di antara trek ketika bandwidth yang tersedia berubah. Pemutar bertanggung jawab untuk menggabungkan bagian-bagian ini agar pemutaran berjalan lancar.

Pemilihan trek adaptif

Inti dari streaming adaptif adalah memilih trek yang paling sesuai untuk lingkungan saat ini. Update aplikasi Anda untuk memutar media streaming adaptif dengan menggunakan pemilihan trek adaptif.

  1. Update initializePlayer dengan kode berikut:

PlayerActivity.kt

private fun initializePlayer() {
   val trackSelector = DefaultTrackSelector(this).apply {
        setParameters(buildUponParameters().setMaxVideoSizeSd())
    }
   player = SimpleExoPlayer.Builder(this)
        .setTrackSelector(trackSelector)
        .build()
  [...]
}

Pertama, buat DefaultTrackSelector, yang bertanggung jawab untuk memilih trek di item media. Lalu, beri tahu trackSelector Anda untuk hanya memilih trek definisi standar atau lebih rendah. Itu adalah cara yang baik untuk menghemat data pengguna Anda dengan mengorbankan kualitas. Terakhir, teruskan trackSelector Anda ke builder agar dapat digunakan ketika mem-build instance SimpleExoPlayer.

Mem-build Item Media yang adaptif

DASH adalah format streaming adaptif yang banyak digunakan. Untuk melakukan streaming konten DASH, Anda perlu membuat MediaItem seperti sebelumnya. Namun, kali ini kita harus menggunakan MediaItem.Builder, bukan fromUri.

Itu karena fromUri menggunakan ekstensi file untuk menentukan format media yang mendasarinya tetapi URI DASH kita tidak memiliki ekstensi file sehingga kita harus menyediakan jenis MIME dari APPLICATION_MPD ketika menyusun MediaItem.

  1. Update initializePlayer sebagai berikut:

PlayerActivity.kt

private void initializePlayer() {
  [...]

  // Replace this line
  val mediaItem = MediaItem.fromUri(getString(R.string.media_url_mp4));

  // With this
   val mediaItem = MediaItem.Builder()
        .setUri(getString(R.string.media_url_dash))
        .setMimeType(MimeTypes.APPLICATION_MPD)
        .build()

  // Also remove the following lines
  val secondMediaItem = MediaItem.fromUri(getString(R.string.media_url_mp3))
    exoPlayer.addMediaItem(secondMediaItem)
}
  1. Mulai ulang aplikasi dan lihat streaming video adaptif dengan DASH yang sedang beraksi. Dengan ExoPlayer, semuanya mudah!

Format streaming adaptif lainnya

HLS (MimeTypes.APPLICATION_M3U8) dan SmoothStreaming (MimeTypes.APPLICATION_SS) adalah format streaming adaptif lain yang biasa digunakan, dan keduanya didukung oleh ExoPlayer. Untuk informasi lebih lanjut tentang menyusun sumber media adaptif lainnya, lihat aplikasi demo ExoPlayer.

6. Memproses peristiwa

Pada langkah sebelumnya, Anda telah mempelajari cara melakukan streaming media progresif dan adaptif. ExoPlayer melakukan banyak pekerjaan untuk Anda di belakang layar, termasuk yang berikut ini:

  • Mengalokasikan memori
  • Mendownload file container
  • Mengekstrak metadata dari container
  • Mendekode data
  • Melakukan rendering video, audio, dan teks ke layar dan pengeras suara

Terkadang, mengetahui apa yang dilakukan ExoPlayer saat runtime sangat berguna untuk memahami dan meningkatkan pengalaman pemutaran bagi pengguna Anda.

Misalnya, Anda mungkin ingin menunjukkan perubahan status pemutaran di antarmuka pengguna dengan melakukan hal berikut:

  • Menampilkan indikator pemuatan dengan lingkaran berputar ketika pemutar masuk ke status buffering
  • Menampilkan overlay dengan opsi "tonton berikutnya" ketika trek telah berakhir

ExoPlayer menawarkan beberapa antarmuka pemroses yang menyediakan callback untuk peristiwa yang berguna. Anda menggunakan pemroses untuk menyimpan log status pemutar.

Memproses

  1. Buat TAG yang konstan di luar class PlayerActivity, yang akan Anda gunakan untuk logging nanti.

PlayerActivity.kt

private const val TAG = "PlayerActivity"
  1. Implementasikan antarmuka Player.EventListener dalam fungsi factory di luar class PlayerActivity. Ini digunakan untuk memberi tahu Anda tentang peristiwa pemutar yang penting, termasuk error dan perubahan status pemutaran.
  2. Ganti onPlaybackStateChanged dengan menambahkan kode berikut:

PlayerActivity.kt

private fun playbackStateListener() = object : Player.EventListener {
    override fun onPlaybackStateChanged(playbackState: Int) {
        val stateString: String = when (playbackState) {
            ExoPlayer.STATE_IDLE -> "ExoPlayer.STATE_IDLE      -"
            ExoPlayer.STATE_BUFFERING -> "ExoPlayer.STATE_BUFFERING -"
            ExoPlayer.STATE_READY -> "ExoPlayer.STATE_READY     -"
            ExoPlayer.STATE_ENDED -> "ExoPlayer.STATE_ENDED     -"
            else -> "UNKNOWN_STATE             -"
        }
        Log.d(TAG, "changed state to $stateString")
    }
}
  1. Deklarasikan jenis anggota pribadi Player.EventListener dalam PlayerActivity.

PlayerActivity.kt

class PlayerActivity : AppCompatActivity() {
    [...]

    private val playbackStateListener: Player.EventListener = playbackStateListener()
}

onPlaybackStateChanged dipanggil ketika status pemutaran berubah. Status baru diberikan oleh parameter playbackState.

Pemutar dapat berada di salah satu dari empat status berikut:

Status

Deskripsi

ExoPlayer.STATE_IDLE

Pemutar telah dibuatkan instance, tetapi belum siap.

ExoPlayer.STATE_BUFFERING

Pemutar tidak dapat melakukan pemutaran dari posisi saat ini karena tidak cukup data untuk buffering

ExoPlayer.STATE_READY

Pemutar dapat langsung melakukan pemutaran dari posisi saat ini. Ini berarti pemutar akan mulai memutar media secara otomatis jika properti playWhenReady pemutar adalah true. Jika false, pemutar akan dijeda.

ExoPlayer.STATE_ENDED

Pemutar telah selesai memutar media.

Mendaftarkan pemroses Anda

Agar callback Anda dipanggil, Anda harus mendaftarkan playbackStateListener dengan pemutar. Lakukan ini di initializePlayer.

  1. Daftarkan pemroses sebelum pemutaran disiapkan.

PlayerActivity.kt

private void initializePlayer() {
    [...]
    exoPlayer.seekTo(currentWindow, playbackPosition)
    exoPlayer.addListener(playbackStateListener)
    [...]
}

Sekali lagi, Anda perlu merapikan untuk menghindari referensi menggantung dari pemutar yang dapat menyebabkan kebocoran memori.

  1. Hapus pemroses di releasePlayer:

PlayerActivity.kt

private void releasePlayer() {
 player?.run {
   [...]
   removeListener(playbackStateListener)
   release()
 }
  player = null
}
  1. Buka logcat dan jalankan aplikasi.
  2. Gunakan kontrol UI untuk mencari, menjeda, dan melanjutkan pemutaran. Anda akan melihat perubahan status pemutaran di log.

Mengelola lebih jauh

ExoPlayer menawarkan sejumlah pemroses lain, yang berguna dalam memahami pengalaman pemutaran pengguna. Ada pemroses untuk audio dan video, juga AnalyticsListener, yang berisi callback dari semua pemroses. Berikut ini beberapa metode yang paling penting:

  • onRenderedFirstFrame dipanggil ketika frame pertama video dirender. Dengan ini, Anda dapat menghitung berapa lama pengguna harus menunggu untuk melihat konten yang berarti di layar.
  • onDroppedVideoFrames dipanggil ketika frame video drop. Frame yang drop menunjukkan bahwa pemutaran tersendat dan pengalaman pengguna cenderung buruk.
  • onAudioUnderrun dipanggil ketika underrun audio terjadi. Underrun menyebabkan gangguan yang terdengar dalam suara dan lebih kentara daripada frame video yang drop.

AnalyticsListener dapat ditambahkan ke player dengan addAnalyticsListener. Ada metode yang sesuai untuk pemroses audio dan video juga.

Pikirkan tentang peristiwa yang penting bagi aplikasi dan pengguna Anda. Untuk informasi lebih lanjut, lihat Pemroses untuk peristiwa pemutar. Pemroses peristiwa selesai!

7. Menyesuaikan antarmuka pengguna

Sejauh ini, Anda telah menggunakan PlayerControlView ExoPlayer untuk menampilkan pengontrol pemutaran kepada pengguna.

bcfe17eebcad9e13.png

Screenshot: Pengontrol pemutaran default

Bagaimana jika Anda ingin mengubah fungsionalitas atau tampilan dan nuansa kontrol ini? Untunglah, kontrol ini sangat mudah disesuaikan.

Penyesuaian sederhana pertama adalah tidak menggunakan pengontrol sama sekali. Ini dapat dilakukan dengan mudah menggunakan atribut use_controller pada elemen PlayerView di dalam activity_player.xml.

  1. Setel use_controller ke false dan kontrolnya tidak akan muncul lagi:

activity_player.xml

<com.google.android.exoplayer2.ui.PlayerView
   [...]
   app:use_controller="false"/>
  1. Tambahkan namespace berikut ke FrameLayout Anda:

activity_player.xml

<FrameLayout
  [...]
  xmlns:app="http://schemas.android.com/apk/res-auto">

Coba sekarang.

Menyesuaikan perilaku

PlayerControlView memiliki beberapa atribut yang memengaruhi perilakunya. Misalnya, Anda dapat menggunakan show_timeout untuk menyesuaikan penundaan dalam milidetik sebelum kontrol disembunyikan setelah pengguna terakhir kali berinteraksi dengannya. Untuk melakukannya:

  1. Hapus app:use_controller="false".
  2. Ubah tampilan pemutar untuk menggunakan show_timeout:

activity_player.xml

<com.google.android.exoplayer2.ui.PlayerView
   android:id="@+id/video_view"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   app:show_timeout="10000"/>

Atribut PlayerControlView juga dapat disetel secara terprogram.

Menyesuaikan tampilan

Itu awal yang baik. Tetapi, bagaimana jika Anda ingin PlayerControlView terlihat berbeda atau mengubah tombol mana yang akan ditampilkan? Implementasi PlayerControlView tidak mengasumsikan adanya tombol apa pun, jadi mudah untuk menghapusnya dan menambahkan yang baru.

Lihat bagaimana Anda dapat menyesuaikan PlayerControlView.

  1. Buat file tata letak custom_player_control_view.xml yang baru di folder player-lib/res/layout/.
  2. Dari menu konteks folder tata letak, pilih New - Layout resource file dan beri nama custom_player_control_view.xml.

ae1e3795726d4e4e.png

Screenshot: File tata letak untuk tampilan kontrol pemutar telah dibuat.

  1. Salin file tata letak asli dari sini ke custom_player_control_view.xml.
  2. Hapus elemen ImageButton dengan ID @id/exo_prev dan @id/exo_next.

Untuk menggunakan tata letak kustom, Anda perlu menyetel atribut app:controller_layout_id dari elemen PlayerView di file activity_player.xml.

  1. Gunakan ID tata letak file kustom Anda seperti pada cuplikan kode berikut:

activity_player.xml

<com.google.android.exoplayer2.ui.PlayerView
   android:id="@+id/video_view"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   app:controller_layout_id="@layout/custom_player_control_view"/>
  1. Mulai aplikasi kembali. Tampilan kontrol pemutar tidak lagi memiliki tombol sebelumnya dan berikutnya.

89e6535a22c8e321.png

Screenshot: Tampilan kontrol pemutar kustom tanpa tombol sebelumnya atau berikutnya

Anda dapat menerapkan perubahan apa pun yang Anda suka di file tata letak. Secara default, warna tema Android dipilih. Anda dapat menggantinya agar sesuai dengan desain aplikasi Anda.

  1. Tambahkan atribut android:tint ke setiap elemen ImageButton:

custom_player_control_view.xml

<ImageButton android:id="@id/exo_rew"
   android:tint="#FF00A6FF"
   style="@style/ExoMediaButton.Rewind"/>
  1. Tambahkan semua atribut android:textColor yang Anda temukan dalam file kustom Anda ke warna yang sama: #FF00A6FF.

custom_player_control_view.xml

<TextView android:id="@id/exo_position"
   [...]
   android:textColor="#FF00A6FF"/>
<TextView android:id="@id/exo_duration"
   [...]
   android:textColor="#FF00A6FF"/>
  1. Jalankan aplikasi. Sekarang Anda memiliki komponen UI yang indah dan berwarna!

e9835d65d6dd0634.png

Screenshot: Tombol berwarna dan tampilan teks

Mengganti gaya default

Anda baru saja membuat file tata letak kustom dan mereferensikannya menggunakan controller_layout_id di activity_player.xml.

Pendekatan lain adalah mengganti file tata letak default yang digunakan PlayerControlView. Kode sumber PlayerControlView memberitahu kita bahwa kode itu menggunakan R.layout.exo_player_control_view untuk tata letak. Jika Anda membuat file tata letak sendiri dengan nama file yang sama, PlayerControlView akan menggunakan file Anda sebagai gantinya.

  1. Hapus atribut controller_layout_id yang baru saja Anda tambahkan.
  2. Hapus file custom_player_control_view.xml.

PlayerView dalam activity_player.xml kini akan terlihat seperti ini:

activity_player.xml

<com.google.android.exoplayer2.ui.PlayerView
   android:id="@+id/video_view"
   android:layout_width="match_parent"
   android:layout_height="match_parent"/>
  1. Buat file bernama exo_player_control_view.xml dalam folder res/layout di modul library player-lib Anda.
  2. Masukkan kode berikut ke dalam exo_player_control_view.xml untuk menambahkan tombol putar, tombol jeda, dan ImageView dengan logo:

exo_player**_control_view.xml**

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:layout_gravity="bottom"
   android:layoutDirection="ltr"
   android:background="#CC000000"
   android:orientation="vertical">

 <LinearLayout
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:gravity="center"
   android:paddingTop="4dp"
   android:orientation="horizontal">

   <ImageButton android:id="@id/exo_play"
      style="@style/ExoMediaButton.Play"/>

   <ImageButton android:id="@id/exo_pause"
      style="@style/ExoMediaButton.Pause"/>

 </LinearLayout>

 <ImageView
     android:contentDescription="@string/logo"
     android:src="@drawable/google_logo"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"/>

</LinearLayout>

Ini menunjukkan cara Anda dapat menambahkan elemen Anda sendiri di sini dan mencampurnya dengan elemen kontrol standar. ExoPlayerView sekarang menggunakan kontrol kustom Anda dan semua logika untuk bersembunyi dan tampil saat interaksi dengan kontrol dipertahankan.

8 Selamat

Selamat! Anda telah belajar banyak tentang mengintegrasikan ExoPlayer dengan aplikasi Anda.

Pelajari lebih lanjut

Untuk mempelajari ExoPlayer lebih lanjut, lihat panduan developer dan kode sumber, serta berlangganan blog ExoPlayer.