Using Exoplayer to play Video and Audio in Android like a Pro

Before starting, hold on a while and think of some app that includes Audio or Video in any form. Did you get the name of an app? In fact, think of apps that don’t have audio or video present in any form. Did you find one? This shows that how important is media to apps.

Playing music and video in your android app is a must to have a task in our app because music and video are the most effective way of fast communication between the user of the app and the provider or the developer of the app. Media can be played from the Internet or from your local device. Even, you can download and store the media in your local device or phone. So, every developer wants to add some kind of media to their app.

There are various ways of adding audio and video in an Android app. Some of these are MediaPlayer API and ExoPlayer. So, in this blog, we will learn how to use ExoPlayer in our Android app. So, the timeline of this blog is:

  • Recap MediaPlayer
  • What is ExoPlayer?
  • MediaPlayer vs Exoplayer
  • How to use ExoPlayer in Activity/Fragment?
  • ExoPlayer example
  • Conclusion

So, let’s get started.

MediaPlayer

To integrate audio, video, and images into your apps easily, the Android multimedia framework provides you the support of MediaPlayer. The MediaPlayer class is used to play media in android devices. It supports almost all types of media present i.e. it supports all types of audio and video format in android. Using MediaPlayer in our app is very easy. But the main drawback of the MediaPlayer is that it supports very little customization i.e. if you want to customize your MediaPlayer as per your own needs, then you will find it difficult to do so. So, if you want to customize your media player then you should use ExoPlayer.

What is ExoPlayer?

Is ExoPlayer new to you? Wait, have you watched any Youtube video in the last week? Yes? So, you have already used ExoPlayer. The Youtube app and many more Google’s video streaming apps use the ExoPlayer to stream and play videos.

ExoPlayer is a media player library that provides a way to play media in your android app. It’s an alternative of the Android,s MediaPlayer library used to play videos and audios. It supports dynamic adaptive streaming over HTTP(DASH). It includes smooth streaming and encryption of the played video. But the best advantage of using ExoPlayer is its customization property. You can customize your video player according to your needs. For example, if you want to add just the play button, then add that feature only. You can add the fast forward and recap button also. So, there are many features in the ExoPlayer that you can use as per your need.

ExoPlayer is reliable because it is directly handled by Google’s Android team. So, you will find each and every support for the ExoPlayer on the Developers website of Google.

MediaPlayer vs ExoPlayer

  • MediaPlayer is supported form the Android API level 1 to the current API level of Android. But ExoPlayer has a prerequisite of Android API level 16 i.e. the Android Jelly Bean. But this is not a matter to worry about. Almost 100 percent (99.2%, to be more precise) of Android devices present on the Play Store have API level 16 or more, so you can say that almost all Android devices present on Play Store, supports ExoPlayer.
  • The video played in the MediaPlayer does not support the smooth streaming. Also, there was no support for adaptive playbacks like DASH and HSL. Caching was another problem i.e. loading of the video in advance was not present in MediaPlayer.
  • MediaPlayer is a black box. You can’t customize the MediaPlayer according to you. So, you don’t know the background working of MediaPlayer. But the ExoPlayer is customizable i.e. add the features as per your need by using ExoPlayer.

To have a deep understanding of where the MediaPlayer and ExoPlayer works, see the below image:

Here you can find that the MediaPlayer is implemented inside the Operating System of Android and due to this you will find it difficult to customize the MediaPlayer according to your needs. But in the case of ExoPlayer, the implementation is done inside our App. So, you can easily customize the ExoPlayer in your app.

How to use ExoPlayer in Activity/Fragment?

ExoPlayer provides a lot of functions that will help us to use the ExoPlayer in our app. If you want to use ExoPlayer in your app then you should add two methods. One for initializing the player and other for releasing the player. In my case, I have used the initializePlayer() and releaseplayer(). Let's understand the same with the help of an example:

  • initializePlayer(): As the name suggests, this function is used to initialize the ExoPlayer in the player view. So by using this function, you can set your player to play the media when your player will be ready in the current window. If the player is already created then you can prepare the media source from any URL.
private fun initializePlayer() {
    if (player == null) {
        player = ExoPlayerFactory.newSimpleInstance(
        DefaultRenderersFactory(context),
        DefaultTrackSelector(),
        DefaultLoadControl())
        playerView?.setPlayer(player)
        player.setPlayWhenReady(playWhenReady)
        player.seekTo(currentWindow, playbackPosition)
    }
    val mediaSource = buildMediaSource(Uri.parse(getString(R.string.media_url_mp4)))
    player.prepare(mediaSource, true, false)
}
  • releasePlayer(): This method is used to get the Playback position, find the states of play and pause used in the ExoPlayer. This is also used to used to release the player and set the player to null.
private fun releasePlayer() {
    if (player != null) {
        playbackPosition = player.getCurrentPosition()
        currentWindow = player.getCurrentWindowIndex()
        playWhenReady = player.getPlayWhenReady()
        player.release()
        player = null
    }
}

So in order to use the ExoPlayer in your Activity or Fragment use the following steps:

  • Use the above initializePlayer() function to initialize your player in your app. You can use this function at onStart() and onResume() of your app. So, whenever you start or resume your app, then you ExoPlayer will be found and the media will be played.
  • If you want to destroy your activity or fragment then use the releasePlayer() function to release the player before destroying the activity or fragment. If you will not do this step, then you video player will be present in the app background and this is not good to have a condition. So, use this releasePlayer() function to release the player from your app before destroying the activity. Use releasePlayer() at onPause() and onStop() for releasing the player.

Playback States

In any player used for media playing, there are 4 states present in that player. These states are : Idle, Buffering, Ready and Ended state. A simple view of these states are:

Picture courtesy: Exoplayer

  1. Idle (Player.STATE_IDLE): when the player has nothing to play, the player will be in the Idle state.
  2. Buffering (Player.STATE.BUFFERING): when the player tries to load the data or the media then the player will be in the buffering state.
  3. Ready (Player.STATE_READY): when the player is ready to play the video then the player will be in the ready state.
  4. Ended (Player.STATE_END): when the player has played the media then the player will come into the ended state. This state must be added if you are destroying an activity or a fragment.

A player can go to the end state from the idle state if you don’t want to play the video or you want to destroy the activity. If you don’t want the player to go to the end state i.e. you want to play the media then from the idle state the player will prepare for media playing by going into the buffering state. Form the buffering state, the player can move to the ready state and play the video. In the end, the player will come to the end state after the destruction of the activity or fragment.

You can set the status of the player by making a function named onPlayerStateChanged(). Following is the code for the onPlayerStateChanged() function:

private val status:String
fun onPlayerStateChanged(playWhenReady:Boolean, playbackState:Int) {
    when (playbackState) {
        Player.STATE_BUFFERING -> status = PlaybackStatus.LOADING
        Player.STATE_ENDED -> status = PlaybackStatus.STOPPED
        Player.STATE_IDLE -> status = PlaybackStatus.IDLE
        Player.STATE_READY -> status = if (playWhenReady) PlaybackStatus.PLAYING else PlaybackStatus.PAUSED
        else -> status = PlaybackStatus.IDLE
    }
}

So, now we are done with the theory part of the ExoPlayer. Now, let’s move on to the implementation of the ExoPlayer in our Android.

Let’s get started with the ExoPlayer example:

Now, let’s have an example to add the ExoPlayer in our app. In this app, there will be one media player with text view in the app. So, let’s get started:

Step 1: Add the dependency of ExoPlayer in your app

The very first step is to add the dependency to our app. To add the dependency to our app, go to the app-level build.gradle file in your app and add the below line:

implementation 'com.google.android.exoplayer:exoplayer-core:2.7.3'

After adding the dependency in the build.gradle file, sync your project and Android Studio will add the ExoPlayer dependency to your app.

Step 2: Add the ExoPlayer in your activity

After adding the dependencies of ExoPlayer in your app, now add ExoPlayer to your app by adding the below code to your android activity_main.xml file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        
        <com.google.android.exoplayer2.ui.SimpleExoPlayerView
            android:id="@+id/simpleExoPlayerView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
        </com.google.android.exoplayer2.ui.SimpleExoPlayerView>
        
        <ProgressBar
            android:id="@+id/progressBar"
            android:visibility="invisible"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
    </FrameLayout>
    
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        
        <TextView
            android:text="@string/lipsum"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"/>
    
    </ScrollView>

</LinearLayout>

By adding the above code to your activity, you have added the ExoPlayer to your app. Now you just have to handle the various states of ExoPlayer to your app. You have to initialize the player and handle various states of ExoPlayer.

Step 3: Code for handing the ExoPlayer

At last, you have to add the below code to your MainActivity.kt . We have to handle each and every aspect of exoplayer. So, add the below code:

class MainActivity : AppCompatActivity(), Player.EventListener {
    override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters?) {
    }

    override fun onTracksChanged(trackGroups: TrackGroupArray?, trackSelections: TrackSelectionArray?) {
    }

    override fun onPlayerError(error: ExoPlaybackException?) {
    }

    override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
        if (playbackState == Player.STATE_BUFFERING)
            progressBar.visibility = View.VISIBLE
        else if (playbackState == Player.STATE_READY)
            progressBar.visibility = View.INVISIBLE
    }

    override fun onLoadingChanged(isLoading: Boolean) {
    }

    override fun onPositionDiscontinuity() {
    }

    override fun onRepeatModeChanged(repeatMode: Int) {
    }

    override fun onTimelineChanged(timeline: Timeline?, manifest: Any?) {
    }

    private lateinit var simpleExoplayer: SimpleExoPlayer
    private var playbackPosition = 0L
    private val dashUrl = "http://rdmedia.bbc.co.uk/dash/ondemand/bbb/2/client_manifest-separate_init.mpd"
    private val bandwidthMeter by lazy {
        DefaultBandwidthMeter()
    }
    private val adaptiveTrackSelectionFactory by lazy {
        AdaptiveTrackSelection.Factory(bandwidthMeter)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onStart() {
        super.onStart()

        initializeExoplayer()
    }

    override fun onStop() {

        releaseExoplayer()
        super.onStop()
    }

    private fun initializeExoplayer() {
        simpleExoplayer = ExoPlayerFactory.newSimpleInstance(
                DefaultRenderersFactory(this),
                DefaultTrackSelector(adaptiveTrackSelectionFactory),
                DefaultLoadControl()
        )

        prepareExoplayer()
        simpleExoPlayerView.player = simpleExoplayer
        simpleExoplayer.seekTo(playbackPosition)
        simpleExoplayer.playWhenReady = true
        simpleExoplayer.addListener(this)
    }

    private fun releaseExoplayer() {
        playbackPosition = simpleExoplayer.currentPosition
        simpleExoplayer.release()
    }

    private fun buildMediaSource(uri: Uri): MediaSource {
        val dataSourceFactory = DefaultHttpDataSourceFactory("ua", bandwidthMeter)
        val dashChunkSourceFactory = DefaultDashChunkSource.Factory(dataSourceFactory)
        return DashMediaSource(uri, dataSourceFactory, dashChunkSourceFactory, null, null)
    }

    private fun prepareExoplayer() {
        val uri = Uri.parse(dashUrl)
        val mediaSource = buildMediaSource(uri)
        simpleExoplayer.prepare(mediaSource)
    }
}

Finally, you can run the app on your device to see the output of the above program or code.

Step 4: Handling orientation change

Till now, we are done with the coding part of the ExoPlayer. But there is one drawback. Try to change the orientation of the device while playing the video. What you will find that after changing the orientation the video will start playing from the initial state. So, we must handle this situation because our video must play form the same place where it was in the other orientation.

So, to remove this problem, use the below code in your AndroiManifest.xml file:

<activity android:name=".MainActivity"
    android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode">
</activity>

Thats all. You are done with a functional ExoPlayer. Customize the player by adding some more features to your ExoPlayer app. You can go the Develpoer Website and learn more about the ExoPlayer.

Conclusion

So, we have covered a lot about ExoPlayer 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 ia high chance that you will find that feature in the ExoPlayer library.

Instead of so many features, before using ExoPlayer, you must think of the features that your app requires i.e. if you are making a simple music player app then go with the MediaPlayer.

So, explore more about ExoPlayer from here.

Keep Learning :)

Team MindOrks