Android Developers Blog
The latest Android and Google Play news for app and game developers.
🔍
Platform Android Studio Google Play Jetpack Kotlin Docs News

23 March 2023

Media3 is ready to play!


Link copied to clipboard

Posted by Nevin Mital - Developer Relations Engineer, Android Media

Today, we’re pleased to announce the full release of the Jetpack Media3 library. After sharing a first look at the library at Android Developer Summit 2021, we published several alpha and beta releases over the past several months to ensure a high-quality set of APIs that we now encourage everyone to adopt.

Media3 is the new home for APIs that enable you to create rich audio and video experiences. If you’ve used libraries like ExoPlayer, MediaCompat, or Media2, you’ll find Media3 to be familiar. However, instead of using these separate libraries, Media3 provides a unified API for playback use-cases and also expands to cover new use-cases like video editing and transcoding. The APIs are simple to use yet powerful, customizable to meet your needs, and reliable and optimized so you can build for the diverse Android device ecosystem.

In this blog post, we’ll focus on the playback APIs in Media3, so please stay tuned for an upcoming post where we’ll dive deeper into the video editing and transcoding APIs. As a brief introduction, the following table describes key components for playback in Media3:

Player

An interface that defines traditional high-level functionality for an audio or video player, such as playback controls.

ExoPlayer

The default implementation of the Player interface in Media3.

MediaSession

An API that advertises media playback to and receives playback command requests from external clients.

MediaSessionService

A service that holds a MediaSession to enable background playback.

MediaLibraryService

A service that additionally allows you to expose a content library to external clients.

MediaController

An API that is generally used by external clients to retrieve playback information and send playback command requests to your media app. Complementary to a MediaSession. Examples of external clients include the notification and lock screen media controls on mobile and large screen devices, Android Auto, WearOS, and Google Assistant.

MediaBrowser

An API that additionally enables external clients to navigate your media app’s content library. Complementary to a MediaLibraryService.

Our developer documentation has more details on these components. Let’s take a closer look into what this new library offers and how you can start using it.

Keeping it simple

By consolidating the APIs for the playback developer journey into a single library, Media3 is able to introduce a Player interface that is used by several components, such as MediaSession and MediaController. This interface outlines traditional high-level functionality for audio and video playback, such as playback controls and the ability to query properties of the currently playing media.

Having a common interface for all “player-like” components means that creating new instances of these objects is straightforward:

val player = ExoPlayer.builder(context).build() val session = MediaSession.Builder(context, player).build() val controller = MediaController.Builder(context, session.token).build()

Media3's MediaSession and MediaController will automatically reflect the state of the components they're connected to. As a result, you can also simplify your app’s architecture by removing connectors like ExoPlayer’s MediaSessionConnector and more easily follow the flow of logic through your app. Calling play() on the MediaController will forward the action to the MediaSession, which will then forward it to the player.

Similarly, Media3 aims to make background playback cases easier to handle. The PlayerNotificationManager from ExoPlayer is no longer needed, as Media3’s MediaSessionService and MediaLibraryService automatically handle publishing a media notification as needed. The library handles configuring, starting, and stopping a foreground service for you as needed, but please also note some known issues summarized in this comment.

ExoPlayer is deprecated, long live ExoPlayer!

ExoPlayer has a new home and is the default implementation of the aforementioned Player interface in Media3. The standalone ExoPlayer project, with package name com.google.android.exoplayer2, will soon be discontinued, and future updates will be published in Media3. For the next few months, we’ll continue publishing equivalent releases of both Media3 and ExoPlayer to help you make the transition to Media3. For example, this means that ExoPlayer 2.18.5 and ExoPlayer in Media3 1.0.0 are identical aside from their package names. However, this is only temporary and we will deprecate the standalone ExoPlayer later this year, so we highly recommend migrating to Media3 as soon as possible. The “Migrating to Media3” section below describes the process in more detail, which includes a script that handles most of the work for you.

Note that Media3 is developed with the same philosophy as ExoPlayer (and in fact, is developed by the same team!). In other words, Media3 retains ExoPlayer’s customizable components, open source development on GitHub, receptivity to pull requests, and public issue tracker, to name a few similarities.

Migrating to Media3

As mentioned previously, the standalone ExoPlayer project, with package name com.google.android.exoplayer2, will soon be discontinued, so to continue receiving updates, you will need to migrate to Media3 ExoPlayer. Other Media APIs that should be migrated to Media3 include, but are not limited to, MediaSessionConnectorMediaBrowserServiceCompat, and MediaBrowserCompat.

We’ve prepared two key resources to help you achieve this migration as smoothly as possible:

  1. migration guide to walk you through the process step-by-step
  2. migration script to convert your standalone ExoPlayer project packages to the corresponding new modules and packages under Media3

The good news is that if you’re currently using ExoPlayer, there’s no need for any code changes and no need to re-integrate or re-write any customizations. The standalone ExoPlayer and Media3 ExoPlayer are identical aside from the package name, and the conversion can be done automatically with the aforementioned migration script. Just make sure you’ve updated your project to use the latest version of ExoPlayer before getting started. For full details and steps, please refer to the migration guide.

Furthermore, since Media3 is fully backwards-compatible with prior media APIs such as MediaControllerCompat and MediaMetadataCompat, your existing integrations will continue to work as before even after the migration. Note that new features such as per-controller customization of commands are only available for clients using Media3. That is to say, for example, all legacy controllers, such as MediaControllerCompat, will receive the same set of available commands. You can identify a legacy controller by checking if getControllerVersion() returns 0 in the MediaSession.ControllerInfo.

The power of Media3, in the palm of your hand

Media3 offers several options for you to adjust its behavior to better fit your needs. The next few sections describe some such mechanisms.

Play it your own way

Although ExoPlayer is the recommended Player implementation to use for audio and video streaming apps, Media3 also introduces the SimpleBasePlayer to minimize the number of methods you need to implement to integrate with a custom player. Start by implementing the getState method. This is where you can declare the Command set supported by your player and configure metadata such as the currently playing media item index and the current timestamp.

class CustomPlayer : SimpleBasePlayer(looper) { override fun getState(): State { // Set available Commands // Configure playWhenReady, mediaItemIndex, currentPosition, etc. } // Implement methods required by available Commands }

The SimpleBasePlayer class will enforce valid player state and handle informing listeners of state changes. Additionally, any methods related to a Command you don’t declare as available are ignored, so beyond getState, you only need to implement the methods that will actually be used.

Better control over your commands

The MediaSession and MediaController APIs have also been updated to give you more control. With Media3, you can advertise your app’s playback capabilities on a per-controller basis. Modify the commands available to a client app in the onConnect method of your MediaSession.Callback. For example, to prevent a client app with package name com.example.myClient from having access to the “seek to next media item” Player.Command:

var sessionCallback = object : MediaSession.Callback { override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): MediaSession.ConnectionResult { val connectionResult = super.onConnect(session, controller) if (controller.packageName == "com.example.myClient") { val availablePlayerCommands = connectionResult.availablePlayerCommands.buildUpon() .remove(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) // Disallow myClient from being able to skip to the next media item .build() return MediaSession.ConnectionResult.accept( connectionResult.availableSessionCommands, availablePlayerCommands ) } return connectionResult // Other clients retain normal command access } } var mediaSession = MediaSession.Builder(context, player) .setCallback(sessionCallback) // Remember to set the callback on your MediaSession! .build()

Creating custom commands

Of course, as with the previous media APIs, you can add custom commands tailored to your app. To implement a custom command, create a new SessionCommand. Similar to as shown above, you can give controllers access to this custom command by including it in the list of available session commands. You can handle custom command behavior in the onCustomCommand method of the same Callback:

override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle ): ListenableFuture<SessionResult> { if (customCommand.customAction == MY_CUSTOM_COMMAND) { // Do custom action return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS)) } // Return error for invalid custom command return Futures.immediateFuture(SessionResult(SessionResult.RESULT_ERROR_BAD_VALUE)) }

You can also ask client apps to display your custom command by including it in a setCustomLayout call in the onPostConnect method of the MediaSession.Callback.

Next steps

We’d love for you to start using Media3 in your app! 

To start exploring the library, feel free to check out the demo app to see an example of audio and video playback, including how to integrate with a media session. Stay tuned to our developer guides for more detailed guidance on the different components in Media3 landing soon. Our sample app, the Universal Android Music Player, and our testing tool, the Media Controller Test app, will also be updated to Media3 on their main branches in the coming weeks.

If you run into any issues, have any feature requests, or would like to share any other sort of feedback, please let us know using the Media3 issue tracker on GitHub. We look forward to hearing from you!