Almost every android application requires playing music videos or mp3 files online or offline (a file exists in internal or maybe in external storage). Playing an audio-video file on your android must have a task to perform. Media can played in various ways like from the server or from your local app memory. So, In this article, we’ll learn how to play these type of media with ExoPlayer.
We will not gonna dive into the history of ExoPlayer or why it is best? Pros and cons of ExoPlayer with
MediaPlayer. You guys just have to know that this Player is mostly used in popular apps like Youtube, NetFlix, Amazon Prime, etc… Brief Into of ExpPlayerExoPlayer is an open-source library provided by Google for Android. It support features not supported by Android MediaPlayer for plyaing online and offline audio video file. It is also possible to play media with a lot of customization from low-level media APIs. The downside of it is very complicated and in most cases it is unnecessary.
Here’s a list of supproted android version for ExoPlayer are.
To show you how to implement the ExoPlayer we’re gonna play the audio and video file stored in local (
internal storage) and play the media uploaded on server with simple Uri url.The ExoPlayer library is split into modules to allow developer to import only a subset of functionality provided by full library. The available library modules are listed below. Adding a dependency to the full ExoPlayer library is equivalent to adding dependencies on all of the library modules individually.
Available modules of ExoPlayer library.
exoplayer-core | Core functionality (required) |
---|---|
exoplayer-dash | Support for DASH content. |
extension-cronet | Module depend on external libraries |
exoplayer-ui | UI components and resources for use with ExoPlayer |
There are other modues available for ExoPlayer you can check them out here.
Now add the following exoplayer dependecies into
build.gradle
file.
implementation 'com.google.android.exoplayer:exoplayer-core:2.18.1' implementation 'com.google.android.exoplayer:exoplayer-dash:2.18.1' implementation 'com.google.android.exoplayer:extension-cronet:2.18.1' implementation 'com.google.android.exoplayer:exoplayer-ui:2.18.1'
Change the 2.18.1
with the latest version of library.
Turn on Java8 support into build.gradle
into android {}
section.
compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }
Note: Make sure to add the Google and Jcenter repositories into settings.gralde
file like this.
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.PREFER_PROJECT) repositories { google() //noinspection JcenterRepositoryObsolete jcenter() maven { url 'https://jitpack.io' } maven { url "https://maven.google.com" } mavenCentral() } }
Sync the project after adding the dependency.
Then add internet permission in your AndroidManifest.xml
file to get video url, read/write storage are optional for caching, play media in devices or something like that.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" />
Also add the following line into AndroidManifest.xml
in application
tag.
android:requestLegacyExternalStorage="true"
Here’s the scenario, lets say you need to downaload some media file from server and save it to private storage of application. So, the user can play it when his/her device is not connected to internet. By default, saving and loading files from the internal storage are private to the application and other applications will not have access to these files.
First we gonna play a mp3 file. Suppose we have this file name some-random-audio.mp3
stored into internal stroage.
Add a property called exoPlayer
for the player
private lateinit var exoPlayer: ExoPlayer
Next, add the initializePlayer()
method where you’re going to create a new instance of ExoPlayer and assign it to the exoPlayer
member variable.
val renderersFactory = buildRenderersFactory(applicationContext, true) // 1 val trackSelector = DefaultTrackSelector(applicationContext) // 2 exoPlayer = ExoPlayer.Builder(applicationContext, renderersFactory) // 3 .setTrackSelector(trackSelector) .build().apply { trackSelectionParameters = DefaultTrackSelector.Parameters.Builder(applicationContext).build() // 4 addListener(exoPlayerListener) // 5 playWhenReady = false // 6 } private fun buildRenderersFactory( context: Context, preferExtensionRenderer: Boolean ): RenderersFactory { val extensionRendererMode = if (preferExtensionRenderer) DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER else DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON return DefaultRenderersFactory(context.applicationContext) .setExtensionRendererMode(extensionRendererMode) .setEnableDecoderFallback(true) }
Going through the above step-by-step:
exoPlayer
after creation.exoPlayerListener
in a couple of minutes.playWhenReady
is false then the ExoPlayer will not automatically starts the media after setting the media Uri. It will wait until user set playWhenReady
value to true.Don’t worry about the specifics of these default classes, using the default classes works perfectly in most use cases.
Awesome, we’ve created an instance of the ExoPlayer!
. Now we want our player to have the ability to play media. This is how you play the media with ExoPlayer:
val mediaItem = MediaItem.fromUri("data/user/application-name/some-random-audio.mp3") // 1 exoPlayer.setMediaItem(mediaItem) // 2 exoPlayer.prepare() // 3 exoPlayer.playWhenReady = true // 4
Here’s the summary of the above code:
fromUri
method takes a Uri of the media that you want to play. In this case you’ll play the media from a local internal storage (private file only accessible to your app).mediaItem
to exoPlayer
.prepare()
method on the ExoPlayer instance. This method prepares the player to play the provided media source.playWhenReady
value to true
.You have now initialized the player, prepared it, and you have set the mediaItem
. Well hope so you’ll listen the media file stream 🥳 .
Just one last thing is to see the implementation of exoPlayerListener
.
private val exoPlayerListener = object : Player.Listener { // 1 override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) { // 2 if(playWhenReady) progressBar.gone() } override fun onPlaybackStateChanged(playbackState: Int) { when (playbackState) { Player.STATE_BUFFERING -> { // 3 progressBar.visible() } Player.STATE_READY -> { // 4 progressBar.gone() } Player.STATE_ENDED -> { // 5 exoPlayer.seekTo(0) exoPlayer.playWhenReady = false } } } override fun onPlayerError(error: PlaybackException) {} // 6
Here’s what we’re doing with the above code:
playWhenReady
is true
.playbackState
to show the progress.playWhenReady
value to false
.Next we need to create a PlayerView where we can show our video preview. If you have used Android’s MediaPlayer API you would display videos in a SurfaceView. The ExoPlayer library provides it’s own high level view (StyledPlayerView) for media playback. It displays video, subtitles and album art, and also displays playback controls.
To add it, open the Fragment or Activity related xml layout file from res/layout and add the following:
<com.google.android.exoplayer2.ui.StyledPlayerView android:id="@+id/playerView" android:layout_width="match_parent" android:layout_height="200dp" android:clickable="true" android:focusable="true" app:auto_show="false" app:resize_mode="fill" app:surface_type="texture_view" />
Attach the StyledPlayerView to the view is very straightforward. We just set need to set the exoPlayer
instance on the player view that you added to the xml by calling the setPlayer(…) method.
val playerView = findViewById<StyledPlayerView>(R.id.playerView) playerView.player = exoPlayer val mediaItem = MediaItem.fromUri("data/user/application-name/some-random-audio.mp4") exoPlayer.setMediaItem(mediaItem) exoPlayer.prepare() exoPlayer.playWhenReady = true
After executing the above code you’ll see your player view will showing the video.
Playing an audio file with ExoPlayer is not different than playing mp3 file with local storage. We just need to add some more default classes when initializing the exoPlayer
instance 😆.
val renderersFactory = buildRenderersFactory(applicationContext, true) val mediaSourceFactory = DefaultMediaSourceFactory(getDataSourceFactory(applicationContext), DefaultExtractorsFactory()) // 1 val trackSelector = DefaultTrackSelector(applicationContext) exoPlayer = ExoPlayer.Builder(applicationContext, renderersFactory) .setTrackSelector(trackSelector) .setMediaSourceFactory(mediaSourceFactory) // 2 .build().apply { addAnalyticsListener(EventLogger()) trackSelectionParameters = DefaultTrackSelector.Parameters.getDefaults(applicationContext) addListener(exoPlayerListener) playWhenReady = false } private fun buildRenderersFactory( context: Context, preferExtensionRenderer: Boolean ): RenderersFactory { val extensionRendererMode = if (preferExtensionRenderer) DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER else DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON return DefaultRenderersFactory(context.applicationContext) .setExtensionRendererMode(extensionRendererMode) .setEnableDecoderFallback(true) } private fun getDataSourceFactory(context: Context): DataSource.Factory = // 3 DefaultDataSource.Factory(context, getHttpDataSourceFactory(context)) private fun getHttpDataSourceFactory(context: Context): HttpDataSource.Factory { val cronetEngine: CronetEngine? = CronetUtil.buildCronetEngine(context) var httpDataSourceFactory: HttpDataSource.Factory? = null if (cronetEngine != null) httpDataSourceFactory = CronetDataSource.Factory(cronetEngine, Executors.newSingleThreadExecutor()) if (httpDataSourceFactory == null) { // We don't want to use Cronet, or we failed to instantiate a CronetEngine. val cookieManager = CookieManager() cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER) CookieHandler.setDefault(cookieManager) httpDataSourceFactory = DefaultHttpDataSource.Factory() } return httpDataSourceFactory }
Most of code for initialzing the ExoPlayer is same as before. Other than that here’s the explanation of the remaing code:
getDataSourceFactory
method is a component from which streams of data can be read. You have to set the ExtractorsFactory, which just returns the array of default extractors.mediaSourceFactory
to instance to exoPlayer
.dataSource
is normally an HttpDataSource, and is responsible for fetching data over HTTP and HTTPS, as well as any other URI schemes.Next create the MediaItem with the online remote server audio file and prepare the exoPlayer
.
val mediaItem = MediaItem.fromUri("https://www.some-random-website-url/random-audio.mp3") exoPlayer.setMediaItem(mediaItem) exoPlayer.prepare() exoPlayer.playWhenReady = true
Last step is easy as sweet; we just need to change the the mediaItem
url and set the assign the exoPlayer
to the StyledPlayerView.
val playerView = findViewById<StyledPlayerView>(R.id.playerView) playerView.player = exoPlayer val mediaItem = MediaItem.fromUri("data/user/application-name/some-random-audio.mp4") exoPlayer.setMediaItem(mediaItem) exoPlayer.prepare() exoPlayer.playWhenReady = trueBonus
I was working on some application where I have this requirement to apply some curves on the StyledPlayerView. So, just thought why not share this bit of info with you guys too.
Before adding the following code our player view will look this: No curves:
Now for the curve just add the following code:
val playerView = findViewById<StyledPlayerView>(R.id.playerView) outlineProvider = object : ViewOutlineProvider() { override fun getOutline(view: View, outline: Outline) { outline.setRoundRect(0, 0, view.width, view.height, 20f) } } clipToOutline = true
Here’s the final output will look like.
So, we have covered a simple and minimlistic information about ExoPlayer of how to play online and offline(local internal storage) audio and video file in this blog. But this is not all about ExoPlayer, there are a lot of other features of ExoPlayer. Actually, if you can’t do something with the MediaPlayer, then there is a high chance that you will find that feature in the ExoPlayer library.
However, be careful of over-engineering! You must be thinking now: “ExoPlayer is awesome, I’ll use it all the time!” Before you do that, ask yourself this: “Do I really need an it?” If the requirements are simple just to a simple sound go for MediaPlayer.
I hope you enjoyed this tutorial and learned something from it. If you have any question or comments, or you want to share your experience with ExoPlayer, please join in the discussion section below.
Thanks for being here and keep reading…
Quick Links
Legal Stuff
Social Media