Inti library ExoPlayer adalah antarmuka Player
. Player
menampilkan fungsi pemutar media tingkat tinggi tradisional seperti kemampuan untuk
melakukan buffering media, memutar, menjeda, dan mencari. Penerapan default ExoPlayer
dirancang untuk membuat beberapa asumsi tentang (sehingga menerapkan beberapa batasan pada)
jenis media yang diputar, cara dan tempat media disimpan, serta cara
dirender. Daripada menerapkan pemuatan dan rendering media secara langsung,
implementasi ExoPlayer
mendelegasikan pekerjaan ini ke komponen yang dimasukkan
saat pemutar dibuat atau saat sumber media baru diteruskan ke pemutar.
Komponen yang umum untuk semua implementasi ExoPlayer
adalah:
- Instance
MediaSource
yang menentukan media yang akan diputar, memuat media, dan dari mana media yang dimuat dapat dibaca. InstanceMediaSource
dibuat dariMediaItem
olehMediaSource.Factory
di dalam pemutar. Gambar tersebut juga dapat diteruskan langsung ke pemutar menggunakan API playlist berbasis sumber media. - Instance
MediaSource.Factory
yang mengonversiMediaItem
menjadiMediaSource
.MediaSource.Factory
dimasukkan saat pemutar dibuat. - Instance
Renderer
yang merender setiap komponen media. Elemen ini dimasukkan saat pemutar dibuat. TrackSelector
yang memilih jalur yang disediakan olehMediaSource
untuk digunakan oleh setiapRenderer
yang tersedia.TrackSelector
dimasukkan saat pemutar dibuat.LoadControl
yang mengontrol kapanMediaSource
melakukan buffering lebih banyak media, dan jumlah media yang di-buffer.LoadControl
dimasukkan saat pemain dibuat.LivePlaybackSpeedControl
yang mengontrol kecepatan pemutaran selama pemutaran live agar pemutar tetap dekat dengan offset live yang dikonfigurasi.LivePlaybackSpeedControl
akan dimasukkan saat pemutar dibuat.
Konsep memasukkan komponen yang mengimplementasikan bagian fungsi pemain ada di seluruh library. Implementasi default beberapa komponen mendelegasikan pekerjaan untuk komponen yang dimasukkan lebih lanjut. Hal ini memungkinkan banyak sub-komponen diganti satu per satu dengan implementasi yang dikonfigurasi dengan cara kustom.
Penyesuaian pemutar
Beberapa contoh umum penyesuaian pemutar dengan memasukkan komponen dijelaskan di bawah.
Mengonfigurasi stack jaringan
Kita memiliki halaman tentang menyesuaikan stack jaringan yang digunakan oleh ExoPlayer.
Menyimpan data dalam cache yang dimuat dari jaringan
Lihat panduan untuk caching cepat sementara dan mendownload media.
Menyesuaikan interaksi server
Beberapa aplikasi mungkin ingin menangkap permintaan dan respons HTTP. Anda dapat memasukkan header permintaan kustom, membaca header respons server, mengubah URI permintaan, dll. Misalnya, aplikasi Anda dapat mengautentikasi dirinya sendiri dengan memasukkan token sebagai header saat meminta segmen media.
Contoh berikut menunjukkan cara menerapkan perilaku ini dengan
memasukkan DataSource.Factory
kustom ke dalam DefaultMediaSourceFactory
:
Kotlin
val dataSourceFactory = DataSource.Factory { val dataSource = httpDataSourceFactory.createDataSource() // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value") dataSource } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory) ) .build()
Java
DataSource.Factory dataSourceFactory = () -> { HttpDataSource dataSource = httpDataSourceFactory.createDataSource(); // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value"); return dataSource; }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)) .build();
Dalam cuplikan kode di atas, HttpDataSource
yang dimasukkan akan menyertakan header "Header: Value"
dalam setiap permintaan HTTP. Perilaku ini diperbaiki untuk setiap
interaksi dengan sumber HTTP.
Untuk pendekatan yang lebih terperinci, Anda dapat memasukkan perilaku tepat waktu menggunakan ResolvingDataSource
. Cuplikan kode berikut menunjukkan cara memasukkan
header permintaan tepat sebelum berinteraksi dengan sumber HTTP:
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time request headers. dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time request headers. dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));
Anda juga dapat menggunakan ResolvingDataSource
untuk melakukan
modifikasi tepat waktu URI, seperti yang ditunjukkan dalam cuplikan berikut:
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time URI resolution logic. dataSpec.withUri(resolveUri(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time URI resolution logic. dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));
Menyesuaikan penanganan error
Mengimplementasikan LoadErrorHandlingPolicy
kustom memungkinkan aplikasi menyesuaikan
cara ExoPlayer bereaksi terhadap error pemuatan. Misalnya, aplikasi mungkin ingin gagal dengan cepat,
bukan mencoba ulang berkali-kali, atau ingin menyesuaikan logika back-off yang
mengontrol durasi waktu tunggu pemain di antara setiap percobaan ulang. Cuplikan berikut
menunjukkan cara menerapkan logika back-off kustom:
Kotlin
val loadErrorHandlingPolicy: LoadErrorHandlingPolicy = object : DefaultLoadErrorHandlingPolicy() { override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long { // Implement custom back-off logic here. return 0 } } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy) ) .build()
Java
LoadErrorHandlingPolicy loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy() { @Override public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) { // Implement custom back-off logic here. return 0; } }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context) .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)) .build();
Argumen LoadErrorInfo
berisi informasi lebih lanjut tentang pemuatan yang gagal untuk
menyesuaikan logika berdasarkan jenis error atau permintaan yang gagal.
Menyesuaikan tanda ekstraktor
Tanda ekstraktor dapat digunakan untuk menyesuaikan cara setiap format diekstrak
dari media progresif. Class ini dapat ditetapkan pada DefaultExtractorsFactory
yang
diberikan ke DefaultMediaSourceFactory
. Contoh berikut meneruskan flag
yang memungkinkan pencarian streaming MP3 berbasis indeks.
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING) val player = ExoPlayer.Builder(context) .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory)) .build()
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING); ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory)) .build();
Mengaktifkan pencarian kecepatan bit yang konstan
Untuk streaming MP3, ADTS, dan AMR, Anda dapat mengaktifkan pencarian perkiraan menggunakan
asumsi kecepatan bit konstan dengan flag FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
.
Flag ini dapat disetel untuk ekstraktor individual menggunakan masing-masing
metode DefaultExtractorsFactory.setXyzExtractorFlags
seperti yang dijelaskan di atas. Untuk
mengaktifkan pencarian kecepatan bit yang konstan bagi semua ekstraktor yang mendukungnya, gunakan
DefaultExtractorsFactory.setConstantBitrateSeekingEnabled
.
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
ExtractorsFactory
kemudian dapat dimasukkan melalui DefaultMediaSourceFactory
seperti
yang dijelaskan untuk menyesuaikan tanda ekstraktor di atas.
Mengaktifkan antrean buffer asinkron
Antrean buffer asinkron adalah peningkatan dalam pipeline rendering
ExoPlayer, yang mengoperasikan instance MediaCodec
dalam mode asinkron dan
menggunakan thread tambahan untuk menjadwalkan decoding dan rendering data. Mengaktifkannya
dapat mengurangi penurunan frame dan underrun audio.
Antrean buffer asinkron diaktifkan secara default di perangkat yang menjalankan Android 12 (level API 31) dan yang lebih baru, serta dapat diaktifkan secara manual mulai dari Android 6.0 (level API 23). Sebaiknya aktifkan fitur untuk perangkat tertentu tempat Anda mengamati penurunan frame atau underrun audio, terutama saat memutar konten yang dilindungi DRM atau dengan kecepatan frame tinggi.
Dalam kasus yang paling sederhana, Anda perlu memasukkan DefaultRenderersFactory
ke
pemain seperti berikut:
Kotlin
val renderersFactory = DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing() val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()
Java
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing(); ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();
Jika Anda membuat instance perender secara langsung, teruskan AsynchronousMediaCodecAdapter.Factory
ke konstruktor MediaCodecVideoRenderer
dan MediaCodecAudioRenderer
.
Mengintersep panggilan metode dengan ForwardingPlayer
Anda dapat menyesuaikan beberapa perilaku instance Player
dengan menggabungkannya ke dalam
subclass ForwardingPlayer
dan mengganti metode untuk melakukan salah satu
hal berikut:
- Akses parameter sebelum meneruskannya ke
Player
delegasi. - Akses nilai return dari
Player
delegasi sebelum menampilkannya. - Implementasikan kembali metode ini sepenuhnya.
Saat mengganti metode ForwardingPlayer
, penting untuk memastikan
implementasi tetap konsisten sendiri dan mematuhi antarmuka
Player
, terutama saat menangani metode yang dimaksudkan untuk memiliki
perilaku yang identik atau terkait. Contoh:
- Jika ingin mengganti setiap operasi 'play', Anda harus mengganti
ForwardingPlayer.play
danForwardingPlayer.setPlayWhenReady
karena pemanggil akan mengharapkan perilaku metode ini identik saatplayWhenReady = true
. - Jika ingin mengubah kenaikan pencari, Anda harus mengganti
ForwardingPlayer.seekForward
untuk melakukan pencarian dengan inkremental yang disesuaikan, danForwardingPlayer.getSeekForwardIncrement
untuk melaporkan penambahan yang disesuaikan yang benar kembali ke pemanggil. - Jika ingin mengontrol
Player.Commands
yang diiklankan oleh instance pemain, Anda harus menggantiPlayer.getAvailableCommands()
danPlayer.isCommandAvailable()
, serta memproses callbackPlayer.Listener.onAvailableCommandsChanged()
untuk mendapatkan notifikasi tentang perubahan yang berasal dari pemain yang mendasarinya.
Penyesuaian MediaSource
Contoh di atas memasukkan komponen yang disesuaikan untuk digunakan selama pemutaran semua
objek MediaItem
yang diteruskan ke pemutar. Jika penyesuaian
halus diperlukan, Anda juga dapat memasukkan komponen yang disesuaikan ke dalam
instance MediaSource
individual, yang dapat diteruskan langsung ke pemutar. Contoh
di bawah menunjukkan cara menyesuaikan ProgressiveMediaSource
untuk menggunakan
DataSource.Factory
, ExtractorsFactory
, dan LoadErrorHandlingPolicy
kustom:
Kotlin
val mediaSource = ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri))
Java
ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri));
Membuat komponen kustom
Library ini menyediakan implementasi default komponen yang tercantum di bagian atas
halaman ini untuk kasus penggunaan umum. ExoPlayer
dapat menggunakan komponen ini, tetapi
juga dapat dibuat untuk menggunakan implementasi kustom jika perilaku non-standar
diperlukan. Beberapa kasus penggunaan untuk penerapan kustom adalah:
Renderer
– Anda mungkin ingin mengimplementasikanRenderer
kustom untuk menangani jenis media yang tidak didukung oleh implementasi default yang disediakan oleh library.TrackSelector
– MengimplementasikanTrackSelector
kustom memungkinkan developer aplikasi mengubah cara jalur yang diekspos olehMediaSource
dipilih untuk digunakan oleh setiapRenderer
yang tersedia.LoadControl
– MenerapkanLoadControl
kustom memungkinkan developer aplikasi mengubah kebijakan buffering pemain.Extractor
– Jika Anda perlu mendukung format penampung yang saat ini tidak didukung oleh library, pertimbangkan untuk menerapkan classExtractor
kustom.MediaSource
– Menerapkan classMediaSource
kustom mungkin sesuai jika Anda ingin mendapatkan sampel media untuk diumpankan ke perender dengan cara kustom, atau jika ingin menerapkan perilaku komposisiMediaSource
kustom.MediaSource.Factory
– MengimplementasikanMediaSource.Factory
kustom memungkinkan aplikasi menyesuaikan cara pembuatanMediaSource
dariMediaItem
.DataSource
– Paket upstream ExoPlayer sudah berisi sejumlah implementasiDataSource
untuk berbagai kasus penggunaan. Anda dapat mengimplementasikan classDataSource
milik sendiri untuk memuat data dengan cara lain, seperti melalui protokol kustom, menggunakan stack HTTP kustom, atau dari cache persisten kustom.
Saat membuat komponen kustom, sebaiknya lakukan hal-hal berikut:
- Jika komponen kustom perlu melaporkan peristiwa kembali ke aplikasi, sebaiknya
Anda melakukannya menggunakan model yang sama seperti komponen ExoPlayer yang ada, misalnya
menggunakan class
EventDispatcher
atau meneruskanHandler
bersama dengan pemroses ke konstruktor komponen. - Kami merekomendasikan agar komponen kustom menggunakan model yang sama seperti komponen ExoPlayer
yang ada untuk memungkinkan konfigurasi ulang oleh aplikasi selama pemutaran. Untuk melakukannya,
komponen kustom harus mengimplementasikan
PlayerMessage.Target
dan menerima perubahan konfigurasi dalam metodehandleMessage
. Kode aplikasi harus meneruskan perubahan konfigurasi dengan memanggil metodecreateMessage
ExoPlayer, mengonfigurasi pesan, dan mengirimkannya ke komponen menggunakanPlayerMessage.send
. Mengirim pesan yang akan dikirim di thread pemutaran memastikan pesan tersebut dijalankan secara berurutan dengan operasi lain yang dilakukan pada pemutar.