Building a video player app in Android (Part 3 / 5)
With ExoPlayer, Playlists, MediaSession, Audio Focus, PIP
The goal of this series of articles is to get you started with using ExoPlayer to build a simple, but feature rich, video player app (that supports playlists, MediaSession, Audio Focus, and Picture in Picture).
This is the third part of a 5 part series that includes:
- ExoPlayer introduction
- ExoPlayer playlists, UI customization, and events
- MediaSession Connector extension for ExoPlayer (this article)
- Supporting Audio Focus
- Supporting PIP
The previous article in the series covered the steps needed to create playlists by concatenating MediaSource
s, along details on customizing the ExoPlayer’s playback control UI. This article gets into the details of using the MediaSession connector extension to seamlessly use MediaSession capabilities in the app.
MediaSession Overview
MediaSession allows information in the player (which is running your media app) to be exposed to other parts of the Android OS.
- Things like whether the player is currently paused, or playing, or what media is loaded are all broadcast via the MediaSession to other parts of the OS, to other apps or system components. This is how what’s currently playing and it’s playback state is sent to a MediaStyle notification, or to a Bluetooth device that’s interested. Or even the Google Assistant.
- Other apps and the OS can also manipulate the player (running in your app) via
MediaController.TransportControls
that are bound to the MediaSession. This allows media buttons on Bluetooth headphones, the Google Assistant, and even other apps to be able to play, pause, or skip songs in a media app.
In summary, a MediaSession is used for two things: publishing metadata about the current media being played in your app, and allowing other components (the OS and other applications) to control playback (in your app). Metadata is used by Bluetooth devices, Android Auto and Wear, and even the Google Assistant. Playback control is used by the the same things as metadata, in addition to the the OS (so that button presses of wired headsets can be translated to commands that your app can use).
For more details on MediaSession here are some resources.
- Understanding MediaSession medium article
- Working with a MediaSession on d.android.com
- Android sample code for working with MediaSession
Using MediaSession Connector Extension
Make sure to add the gradle dependency to your build.gradle
file.
ext { ver = “2.7.0” }
dependencies {
implementation
“com.google.android.exoplayer:extension-mediasession:$ver”
}
Then you can create the session and attach it to the player (in your VideoActivity
) as shown below.
Note on Java varargs and Kotlin — ExoPlayer and the MediaSession connector extension are written in Java, and the setPlayer()
method accepts varargs as the 3rd parameter. When calling this method from Kotlin, if you don’t pass a 3rd argument, then everything works as you would expect when using Java. However, if you pass null as the 3rd argument then this will thrown a NullPointerException
. If you do have a list of arguments that you would like to pass, then you have to use the spread operator * in order to pass a list of arguments to the Java varargs parameter. Here’s an example of this.
val uriList = mutableListOf<MediaSource>()
val mediaSource = ConcatenatingMediaSource(*uriList.toTypedArray())
Doing this will enable the basic set of playback actions (ACTION_PLAY_PAUSE
, ACTION_PLAY
, ACTION_PAUSE
, ACTION_SEEK_TO
, ACTION_FAST_FORWARD
, ACTION_REWIND
, ACTION_STOP
). This means the connector will be able to handle these actions that are generated via MediaSession from media buttons, such as those in your Bluetooth headphones which allow pause, play, and fast forward, and rewind.
However, if you want to provide control over the playlist then you also have to tell the connector to handle the ACTION_SKIP_*
commands by adding a QueueNavigator
. Here’s an example of what this might look like.
Using this example you can also change the code that creates your ExoPlayer and it’s playlist, so that the playlist is loaded from this same ‘MediaCatalog
’ list object. The code to load the playlist might now look like this.
You can also tell the connector that you want to handle other MediaSession actions in addition to the ones described above. You can find more info about each of the following in this medium article.
MediaSessionConnector.PlaybackPreparer
— This allows you to handle actions likeACTION_PREPARE_FROM_SEARCH
andACTION_PREPARE_FROM_URI
which is required if you wanted to integrate with Android Auto or Google Assistant. For more information on allowing the Google Assistant to control your media app via MediaSession, please refer to this article on developers.android.com.MediaSessionConnector.PlaybackController
— This allows you to handle basic playback controller actions such asACTION_PLAY_PAUSE
andACTION_SEEK_TO
. This is optional, and if you don’t use this to intercept such calls, aDefaultPlaybackController
is used.MediaSessionConnector.QueueEditor
— This allows you to handleACTION_SET_RATING
and otherMediaSessionCompat.FLAG_HANDLES_QUEUE_COMMAND
commands.TimelineQueueEditor
is an implementation of this that works together with aDynamicConcatenatingMediaSource
.MediaSessionConnector.CustomActionProvider
— Handles any custom actions that you want to provide in your app such as providing a way to control the repeat mode in your app.
Source code on GitHub
By following these 5 articles, you should be able to create a video player app that is similar to this sample we have created (which you can get from Android Studio as well).
Resources for further learning
ExoPlayer
- IO17 ExoPlayer codelab
- IO14 ExoPlayer Introduction Video
- IO17 ExoPlayer Session Video
- Why ExoPlayer?
- Latest changes for ExoPlayer v2.6.1
MediaSession, Audio Focus
DASH, HLS
Picture in Picture