Membuat aplikasi media player dasar menggunakan Media3 ExoPlayer

Jetpack Media3 menentukan antarmuka Player yang menguraikan fungsi dasar untuk pemutaran file video dan audio. ExoPlayer adalah implementasi default antarmuka ini di Media3. Kami merekomendasikan penggunaan ExoPlayer karena menyediakan serangkaian fitur komprehensif yang mencakup sebagian besar kasus penggunaan pemutaran dan dapat disesuaikan untuk menangani kasus penggunaan tambahan yang mungkin Anda miliki. ExoPlayer juga mengabstraksi fragmentasi perangkat dan OS sehingga kode Anda berfungsi secara konsisten di seluruh ekosistem Android. ExoPlayer mencakup:

Halaman ini memandu Anda melalui beberapa langkah penting dalam membangun aplikasi pemutaran, dan untuk mengetahui detail lebih lanjut, Anda dapat melihat panduan lengkap kami tentang Media3 ExoPlayer.

Memulai

Untuk memulai, tambahkan dependensi pada modul ExoPlayer, UI, dan Umum Jetpack Media3:

implementation "androidx.media3:media3-exoplayer:1.3.1"
implementation "androidx.media3:media3-ui:1.3.1"
implementation "androidx.media3:media3-common:1.3.1"

Bergantung pada kasus penggunaan, Anda mungkin juga memerlukan modul tambahan dari Media3, seperti exoplayer-dash untuk memutar streaming dalam format DASH.

Pastikan untuk mengganti 1.3.1 dengan versi library pilihan Anda. Anda dapat membaca catatan rilis untuk melihat versi terbaru.

Membuat pemutar media

Dengan Media3, Anda dapat menggunakan implementasi antarmuka Player yang disertakan, ExoPlayer, atau membuat implementasi kustom Anda sendiri.

Membuat ExoPlayer

Cara paling sederhana untuk membuat instance ExoPlayer adalah sebagai berikut:

Kotlin

val player = ExoPlayer.Builder(context).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();

Anda dapat membuat pemutar media dalam metode siklus proses onCreate() dari Activity, Fragment, atau Service tempat pemutar media berada.

Builder mencakup berbagai opsi penyesuaian yang mungkin Anda minati, seperti:

Media3 menyediakan komponen UI PlayerView yang dapat Anda sertakan dalam file tata letak aplikasi. Komponen ini mengenkapsulasi PlayerControlView untuk kontrol pemutaran, SubtitleView untuk menampilkan subtitel, dan Surface untuk merender video.

Menyiapkan pemain

Tambahkan item media ke playlist untuk pemutaran dengan metode seperti setMediaItem() dan addMediaItem(). Kemudian, panggil prepare() untuk mulai memuat media dan mendapatkan resource yang diperlukan.

Anda tidak boleh melakukan langkah-langkah ini sebelum aplikasi berada di latar depan. Jika pemain berada dalam Activity atau Fragment, ini berarti menyiapkan pemain dalam metode siklus proses onStart() pada API level 24 dan yang lebih tinggi atau metode siklus proses onResume() pada API level 23 dan yang lebih lama. Untuk pemain yang berada di Service, Anda dapat menyiapkannya di onCreate().

Mengontrol pemutar

Setelah pemutar disiapkan, Anda dapat mengontrol pemutaran dengan memanggil metode pada pemutar seperti:

Komponen UI seperti PlayerView atau PlayerControlView akan diupdate sebagaimana mestinya saat terikat ke pemutar.

Melepaskan pemutar

Pemutaran dapat memerlukan resource dengan persediaan terbatas, seperti dekoder video, jadi penting untuk memanggil release() di pemutar Anda untuk mengosongkan resource saat pemutar tidak lagi diperlukan.

Jika pemain berada di Activity atau Fragment, lepaskan pemutar dalam metode siklus proses onStop() pada API level 24 dan yang lebih tinggi, atau metode onPause() pada API level 23 dan yang lebih lama. Untuk pemain yang ada di Service, Anda dapat merilisnya di onDestroy().

Mengelola pemutaran dengan sesi media

Di Android, sesi media menyediakan cara yang distandardisasi untuk berinteraksi dengan pemutar media melintasi batas proses. Menghubungkan sesi media ke pemutar memungkinkan Anda mengiklankan pemutaran media secara eksternal dan menerima perintah pemutaran dari sumber eksternal, misalnya untuk berintegrasi dengan kontrol media sistem di perangkat seluler dan layar besar.

Untuk menggunakan sesi media, tambahkan dependensi pada modul Sesi Media3:

implementation "androidx.media3:media3-session:1.3.1"

Membuat sesi media

Anda dapat membuat MediaSession setelah melakukan inisialisasi pada pemutar, seperti berikut:

Kotlin

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

Media3 otomatis menyinkronkan status Player dengan status MediaSession. Ini berfungsi dengan implementasi Player apa pun, termasuk ExoPlayer, CastPlayer, atau implementasi kustom.

Memberikan kontrol kepada klien lain

Aplikasi klien dapat menerapkan pengontrol media untuk mengontrol pemutaran sesi media Anda. Untuk menerima permintaan ini, tetapkan objek callback saat mem-build MediaSession Anda.

Saat pengontrol akan terhubung ke sesi media Anda, metode onConnect() akan dipanggil. Anda dapat menggunakan ControllerInfo yang disediakan untuk memutuskan apakah akan menerima atau menolak permintaan tersebut. Lihat contohnya di aplikasi demo Sesi Media3.

Setelah terhubung, pengontrol dapat mengirimkan perintah pemutaran ke sesi. Selanjutnya, sesi mendelegasikan perintah tersebut ke pemutar. Perintah pemutaran dan playlist yang ditentukan dalam antarmuka Player ditangani secara otomatis oleh sesi.

Metode callback lainnya memungkinkan Anda menangani, misalnya, permintaan untuk perintah pemutaran kustom dan mengubah playlist. Callback ini juga menyertakan objek ControllerInfo sehingga Anda dapat menentukan kontrol akses berdasarkan permintaan per permintaan.

Memutar media di latar belakang

Untuk terus memutar media saat aplikasi tidak berada di latar depan, misalnya untuk memutar musik, buku audio, atau podcast meskipun pengguna tidak membuka aplikasi Anda, Player dan MediaSession harus dienkapsulasi dalam layanan latar depan. Media3 menyediakan antarmuka MediaSessionService untuk tujuan ini.

Mengimplementasikan MediaSessionService

Buat class yang memperluas MediaSessionService dan buat instance MediaSession dalam metode siklus proses onCreate().

Kotlin

class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    // Create your Player and MediaSession in the onCreate lifecycle event
    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
    }

    // Remember to release the player and media session in onDestroy
    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        super.onDestroy()
    }
}

Java

public class PlaybackService extends MediaSessionService {
    private MediaSession mediaSession = null;

    @Override
    public void onCreate() {
        super.onCreate();
        ExoPlayer player = new ExoPlayer.Builder(this).build();
        mediaSession = new MediaSession.Builder(this, player).build();
    }

    @Override
    public void onDestroy() {
        mediaSession.getPlayer().release();
        mediaSession.release();
        mediaSession = null;
        super.onDestroy();
    }
}

Dalam manifes, class Service dengan filter intent MediaSessionService dan minta izin FOREGROUND_SERVICE untuk menjalankan layanan latar depan:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Terakhir, di class yang Anda buat, ganti metode onGetSession() untuk mengontrol akses klien ke sesi media Anda. Tampilkan MediaSession untuk menerima permintaan koneksi, atau tampilkan null untuk menolak permintaan.

Kotlin

// This example always accepts the connection request
override fun onGetSession(
    controllerInfo: MediaSession.ControllerInfo
): MediaSession? = mediaSession

Java

@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
  // This example always accepts the connection request
  return mediaSession;
}

Menghubungkan ke UI Anda

Setelah sesi media Anda berada di Service yang terpisah dari Activity atau Fragment tempat UI pemutar berada, Anda dapat menggunakan MediaController untuk menautkannya bersama-sama. Dalam metode onStart() dari Activity atau Fragment dengan UI Anda, buat SessionToken untuk MediaSession, lalu gunakan SessionToken untuk mem-build MediaController. Proses build MediaController terjadi secara asinkron.

Kotlin

override fun onStart() {
  val sessionToken = SessionToken(this, ComponentName(this, PlaybackService::class.java))
  val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
  controllerFuture.addListener(
    {
        // Call controllerFuture.get() to retrieve the MediaController.
        // MediaController implements the Player interface, so it can be
        // attached to the PlayerView UI component.
        playerView.setPlayer(controllerFuture.get())
      },
    MoreExecutors.directExecutor()
  )
}

Java

@Override
public void onStart() {
  SessionToken sessionToken =
    new SessionToken(this, new ComponentName(this, PlaybackService.class));
  ListenableFuture<MediaController> controllerFuture =
    new MediaController.Builder(this, sessionToken).buildAsync();
  controllerFuture.addListener(() -> {
    // Call controllerFuture.get() to retrieve the MediaController.
    // MediaController implements the Player interface, so it can be
    // attached to the PlayerView UI component.
    playerView.setPlayer(controllerFuture.get());
  }, MoreExecutors.directExecutor())
}

MediaController mengimplementasikan antarmuka Player, sehingga Anda dapat menggunakan metode yang sama seperti play() dan pause() untuk mengontrol pemutaran. Serupa dengan komponen lainnya, jangan lupa untuk merilis MediaController saat tidak lagi diperlukan, seperti metode siklus proses onStop() dari Activity, dengan memanggil MediaController.releaseFuture().

Memublikasikan notifikasi

Layanan latar depan diwajibkan untuk memublikasikan notifikasi saat aktif. MediaSessionService akan otomatis membuat notifikasi MediaStyle untuk Anda dalam bentuk MediaNotification. Untuk memberikan notifikasi kustom, buat MediaNotification.Provider dengan DefaultMediaNotificationProvider.Builder atau dengan membuat implementasi kustom antarmuka penyedia. Tambahkan penyedia Anda ke MediaSession dengan setMediaNotificationProvider.

Mengiklankan koleksi konten Anda

MediaLibraryService dibuat di MediaSessionService dengan memungkinkan aplikasi klien untuk menjelajahi konten media yang disediakan oleh aplikasi Anda. Aplikasi klien mengimplementasikan MediaBrowser untuk berinteraksi dengan MediaLibraryService Anda.

Mengimplementasikan MediaLibraryService mirip dengan menerapkan MediaSessionService, kecuali bahwa di onGetSession() Anda harus menampilkan MediaLibrarySession, bukan MediaSession. Dibandingkan dengan MediaSession.Callback, MediaLibrarySession.Callback memiliki metode tambahan yang memungkinkan klien browser mengakses konten yang ditawarkan oleh layanan library Anda.

Serupa dengan MediaSessionService, deklarasikan MediaLibraryService dalam manifes Anda dan minta izin FOREGROUND_SERVICE untuk menjalankan layanan latar depan:

<service
    android:name=".PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
        <action android:name="android.media.browse.MediaBrowserService"/>
    </intent-filter>
</service>

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Contoh di atas menyertakan filter intent untuk MediaLibraryService dan, untuk kompatibilitas mundur, MediaBrowserService lama. Filter intent tambahan memungkinkan aplikasi klien yang menggunakan MediaBrowserCompat API untuk mengenali Service Anda.

MediaLibrarySession memungkinkan Anda menyalurkan library konten dalam struktur hierarki, dengan MediaItem root tunggal. Setiap MediaItem dalam hierarki dapat memiliki berapa pun jumlah node MediaItem turunan. Anda dapat menyalurkan root yang berbeda, atau hierarki yang berbeda, berdasarkan permintaan aplikasi klien. Misalnya, hierarki yang Anda kembalikan ke klien yang mencari daftar item media yang direkomendasikan mungkin hanya berisi MediaItem root dan satu level node MediaItem turunan, sedangkan hierarki yang Anda kembalikan ke aplikasi klien yang berbeda dapat merepresentasikan library konten yang lebih lengkap.

Membuat MediaLibrarySession

MediaLibrarySession memperluas MediaSession API untuk menambahkan API penjelajahan konten. Dibandingkan dengan callback MediaSession, callback MediaLibrarySession menambahkan metode seperti:

Metode callback yang relevan akan menyertakan objek LibraryParams dengan sinyal tambahan tentang jenis hierarki konten yang diminati aplikasi klien.