Android Exoplayer allows you to play or stream audio and video files efficiently. Unlike Android’s built-in MediaPlayer, ExoPlayer offers more control and customization over playback, buffering strategies and playlist management. It support various multimedia formats like MP4, MP3, DASH, HLS, SmoothStreaming. It started as standalone project by Google and later migrated into Jetpack Media3 APIs.
In this article we'll see a basic example on how the player can be integrated in your app.
Once the dependencies are added, the player components are ready to be used in the app. Below are the keypointers on how the player can be used.
You can also further customize the media item by using MediaSource.Factory(). Below a dash media source is created using DashMediaSource
You can do the same for HLS videos using HlsMediaSource. For this you need to add exoplyer's hls module first.
Here is an example of preparing a playlist.
I hope we have covered the fundamentals of Exoplayer. The complete code can be found here. Let me know your queries in the comments section below.
Cheers!
Happy Coding 🤗
In this article we'll see a basic example on how the player can be integrated in your app.
1. Getting started
To get started, add the exoplayer dependencies to your project. Find the latest version of exoplayer from here
build.gradle
dependencies {
....
// exoplayer
implementation("androidx.media3:media3-exoplayer:1.4.1")
implementation("androidx.media3:media3-ui:1.4.1")
implementation("androidx.media3:media3-exoplayer-dash:1.4.1")
}
- Add the player to your xml layout using PlayerView component.
- In your activity create the player instance using ExoPlayer.Builder() and the attach the player to the PlayerView component that is added in xml
- Prepare the media(s) that needs to be played using MediaItem.Builder() method.
- Add the media items to the player and call prepare() to play the media.
2. Sample URLs
Below are the few sample URLs that you can use to experiment with ExoPlayer.
val dashUrl = "https://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7.8506521BFC350652163895D4C26DEE124209AA9E&key=ik0"
val mp4Url =
"https://firebasestorage.googleapis.com/v0/b/project-8525323942962534560.appspot.com/o/samples%2Fsample2.mp4?alt=media&token=2f09078b-6c87-4159-9054-73c9b88d665b"
val mp3Url = "https://firebasestorage.googleapis.com/v0/b/project-8525323942962534560.appspot.com/o/samples%2Fnightfall-future-bass-music-228100.mp3?alt=media&token=32821471-654b-4a9e-9790-1e9c7d1cc584"
val mediaUrl1 =
"https://firebasestorage.googleapis.com/v0/b/project-8525323942962534560.appspot.com/o/samples%2Fnightfall-future-bass-music-228100.mp3?alt=media&token=32821471-654b-4a9e-9790-1e9c7d1cc584"
val mediaUrl2 =
"https://firebasestorage.googleapis.com/v0/b/project-8525323942962534560.appspot.com/o/samples%2Fsample2.mp4?alt=media&token=2f09078b-6c87-4159-9054-73c9b88d665b"
val mediaUrl3 =
"https://firebasestorage.googleapis.com/v0/b/project-8525323942962534560.appspot.com/o/samples%2Fin-slow-motion-inspiring-ambient-lounge-219592.mp3?alt=media&token=8c4e73cb-97c6-4163-9cfe-0728dbecf076"
val mediaUrl4 =
"https://firebasestorage.googleapis.com/v0/b/project-8525323942962534560.appspot.com/o/samples%2Fnight-detective-226857.mp3?alt=media&token=4f6ade23-0aaf-4afc-acb9-645540f2fe87"
3. Basic example - Playing Mp3, Mp4, Dash & Playlist
Now let's see this in action by creating simple player that can play few media types like mp3, mp4, dash or a playlist.- Create a new activity named PlayerActivity and add the PlayerView component to its xml layout
activity_player.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/black"> <androidx.media3.ui.PlayerView android:id="@+id/player_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
- Open the activity file and do the below changes.
- First the player instance is built using ExoPlayer.Builder() and attached to xml player component
- The media item is perpared using MediaItem.Builder() and set to player using setMediaItems().
- By calling prepare(), player will load the media and ready to play. Here by setting playWhenReady=true, the player will autoplay the media once it's ready
- And most importantly the player holds the video decoders and other resources that are limited on the device. These should be released when no longer needed by the app to be able to used by other apps. This can be done by calling release() method. Usually we do this in onPause(), onStop() methods.
PlayerActivity.ktpackage info.androidhive.exoplayer import android.os.Bundle import android.widget.Toast import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.media3.common.MediaItem import androidx.media3.common.MimeTypes import androidx.media3.common.Player import androidx.media3.exoplayer.ExoPlayer import info.androidhive.exoplayer.databinding.ActivityPlayerBinding class PlayerActivity : AppCompatActivity() { private val binding by lazy(LazyThreadSafetyMode.NONE) { ActivityPlayerBinding.inflate(layoutInflater) } private var player: Player? = null private var playWhenReady = true private var mediaItemIndex = 0 private var playbackPosition = 0L private val mp4Url = "https://firebasestorage.googleapis.com/v0/b/project-8525323942962534560.appspot.com/o/samples%2Fsample2.mp4?alt=media&token=2f09078b-6c87-4159-9054-73c9b88d665b" private val playbackStateListener: Player.Listener = object : Player.Listener { override fun onPlaybackStateChanged(playbackState: Int) { super.onPlaybackStateChanged(playbackState) when (playbackState) { ExoPlayer.STATE_IDLE -> {} ExoPlayer.STATE_BUFFERING -> {} ExoPlayer.STATE_READY -> {} ExoPlayer.STATE_ENDED -> {} } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContentView(binding.root) } private fun initializePlayer() { player = ExoPlayer.Builder(this).build().also { exoPlayer -> binding.playerView.player = exoPlayer exoPlayer.trackSelectionParameters = exoPlayer.trackSelectionParameters.buildUpon().setMaxVideoSizeSd().build() val mediaItem = MediaItem.Builder().setUri(mp4Url).build() exoPlayer.setMediaItems(listOf(mediaItem), mediaItemIndex, playbackPosition) exoPlayer.playWhenReady = playWhenReady exoPlayer.addListener(playbackStateListener) exoPlayer.prepare() } } private fun releasePlayer() { player?.let { player -> playbackPosition = player.currentPosition mediaItemIndex = player.currentMediaItemIndex playWhenReady = player.playWhenReady player.removeListener(playbackStateListener) player.release() } player = null } public override fun onStart() { super.onStart() initializePlayer() } public override fun onResume() { super.onResume() if (player == null) { initializePlayer() } } public override fun onPause() { super.onPause() releasePlayer() } public override fun onStop() { super.onStop() releasePlayer() } }
- Open AndroidManifest.xml and add INTERNET permission as we are playing the media from a remote URL. Add configChanges flags to avoid activity restarts when the device orientation changes.
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" /> <activity android:name=".PlaylistActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
4. Playing HLS, DASH videos
Usually player can play these videos if url ends with the extension like .mp4 or .m3u8. If not, we need to manually set the MIME type as shown below.
// Dash video
MediaItem.Builder().setUri(mediaUrl).setMimeType(MimeTypes.APPLICATION_MPD)
// HLS video
MediaItem.Builder().setUri(mediaUrl).setMimeType(MimeTypes.APPLICATION_M3U8)
val dataSourceFactory: DataSource.Factory = DefaultHttpDataSource.Factory()
// Prepare dash media source
val mediaSource: MediaSource =
DashMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mediaUrl))
exoPlayer.setMediaSource(mediaSource)
exoPlayer.prepare()
implementation("androidx.media3:media3-exoplayer-hls:1.4.1")
val dataSourceFactory: DataSource.Factory = DefaultHttpDataSource.Factory()
// Prepare dash media source
val mediaSource: MediaSource =
HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mediaUrl))
exoPlayer.setMediaSource(mediaSource)
exoPlayer.prepare()
5. Playing a playlist
The player is capable of playing a playlist as well. You can pass the list of media URLs to player and the player plays the next item once the current item ends. Tapping the next and previous controls on the player, plays the next or previous media item.Here is an example of preparing a playlist.
PlaylistActivity.kt
package info.androidhive.exoplayer
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer
import info.androidhive.exoplayer.databinding.ActivityPlaylistBinding
class PlaylistActivity : AppCompatActivity() {
private val binding by lazy(LazyThreadSafetyMode.NONE) {
ActivityPlaylistBinding.inflate(layoutInflater)
}
private val mediaUrl1 =
"https://firebasestorage.googleapis.com/v0/b/project-8525323942962534560.appspot.com/o/samples%2Fnightfall-future-bass-music-228100.mp3?alt=media&token=32821471-654b-4a9e-9790-1e9c7d1cc584"
private val mediaUrl2 =
"https://firebasestorage.googleapis.com/v0/b/project-8525323942962534560.appspot.com/o/samples%2Fsample2.mp4?alt=media&token=2f09078b-6c87-4159-9054-73c9b88d665b"
private val mediaUrl3 =
"https://firebasestorage.googleapis.com/v0/b/project-8525323942962534560.appspot.com/o/samples%2Fin-slow-motion-inspiring-ambient-lounge-219592.mp3?alt=media&token=8c4e73cb-97c6-4163-9cfe-0728dbecf076"
private val mediaUrl4 =
"https://firebasestorage.googleapis.com/v0/b/project-8525323942962534560.appspot.com/o/samples%2Fnight-detective-226857.mp3?alt=media&token=4f6ade23-0aaf-4afc-acb9-645540f2fe87"
private var player: Player? = null
private var playWhenReady = true
private var mediaItemIndex = 0
private var playbackPosition = 0L
private val mediaItems: MutableList<MediaItem> = mutableListOf()
private val playbackStateListener: Player.Listener = object : Player.Listener {
override fun onPlaybackStateChanged(playbackState: Int) {
super.onPlaybackStateChanged(playbackState)
when (playbackState) {
ExoPlayer.STATE_IDLE -> {}
ExoPlayer.STATE_BUFFERING -> {}
ExoPlayer.STATE_READY -> {}
ExoPlayer.STATE_ENDED -> {}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
}
private fun initializePlayer() {
// ExoPlayer implements the Player interface
player = ExoPlayer.Builder(this).build().also { exoPlayer ->
binding.playerView.player = exoPlayer
// Update the track selection parameters to only pick standard definition tracks
exoPlayer.trackSelectionParameters =
exoPlayer.trackSelectionParameters.buildUpon().setMaxVideoSizeSd().build()
mediaItems.clear()
mediaItems.addAll(preparePlayList())
exoPlayer.setMediaItems(mediaItems, mediaItemIndex, playbackPosition)
exoPlayer.playWhenReady = playWhenReady
exoPlayer.addListener(playbackStateListener)
exoPlayer.prepare()
}
}
private fun preparePlayList(): List<MediaItem> {
return listOf(
MediaItem.fromUri(mediaUrl1),
MediaItem.fromUri(mediaUrl2),
MediaItem.fromUri(mediaUrl3),
MediaItem.fromUri(mediaUrl4),
)
}
private fun releasePlayer() {
player?.let { player ->
playbackPosition = player.currentPosition
mediaItemIndex = player.currentMediaItemIndex
playWhenReady = player.playWhenReady
player.removeListener(playbackStateListener)
player.release()
}
player = null
}
public override fun onStart() {
super.onStart()
initializePlayer()
}
public override fun onResume() {
super.onResume()
if (player == null) {
initializePlayer()
}
}
public override fun onPause() {
super.onPause()
releasePlayer()
}
public override fun onStop() {
super.onStop()
releasePlayer()
}
}
Cheers!
Happy Coding 🤗
Great article! The step-by-step guide on using ExoPlayer was easy to follow and super helpful. I loved the examples for different media formats and how to set up playlists. Thanks for making media playback so simple!
ReplyDeleteYou are welcome Anil :)
Delete