Tag Archives: jetpack media3

Media3 is ready to play!

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!

Introducing Jetpack Media3

Posted by Don Turner, Developer Relations Engineer

Blue background with a dark blue tablet illustration. The Android Jetpack logo is flying across the screen

Introducing Jetpack Media3

Today, we're launching the first alpha of Jetpack Media3. It's a collection of support libraries for media playback, including ExoPlayer. This article will explain why we created Media3, what it contains, and how it can simplify your app architecture.


Why another media API?

We have several existing media APIs: Jetpack Media also known as MediaCompat, Jetpack Media2, and ExoPlayer. These libraries were developed with different goals, and have several areas of overlapping functionality.

For example, ExoPlayer and Media2 both contain UI components, and MediaCompat and Media2 contain classes for handling media sessions.

It can be challenging to decide which library to use for a given use case, and objects from different libraries are often not compatible, requiring adapters or connecting code. Media3 removes these challenges by providing a single set of libraries which work well together.

To create Media3 we:

  • Identified the common areas of functionality in our existing media libraries, including UI, playback and media session handling.
  • Refined and merged the best parts.
  • Created a common Player interface for all "player-like" objects (more on this later).

What's in the box

Media3 contains many libraries. The ones most relevant for simple media playback are shown below.


Library name

Purpose

Useful classes for playback

media3-exoplayer

Objects for playing video and audio, provided by ExoPlayer. 

SimpleExoPlayer for simple playback use cases

media3-ui

Views for displaying media playback controls, content, and metadata. 

StyledPlayerView displays audio and video content from a Player

media3-session

Objects for creating and interacting with a media session.

MediaSession for advertising what you're playing

MediaLibraryService for advertising your content library



A common Player

Our existing media APIs have a lot of objects which accept playback commands, like "play," "pause," and "skip". Identifying these "player-like" objects and ensuring that they implement a common Player interface was one of the biggest undertakings in the development of Media3.

We've updated, enhanced, and streamlined the Player interface from ExoPlayer to act as the common Player interface for Media3.

Classes such as MediaController and MediaSession that previously contained references to other "player-like" objects have been updated to reference the new player.

This is useful when communicating with UI components. Both ExoPlayer and MediaController now implement Player, so either one of them can be used to communicate with StyledPlayerView or other UI components.

Diagram showing how MediaController and ExoPlayer implement the Player interface and can be used to communicate with UI components, like StyledPlayerView

Diagram showing how MediaController and ExoPlayer implement the Player interface and can be used to communicate with UI components, like StyledPlayerView


Simplified architecture

Using this Player interface avoids the need for connecting components, allowing for less code and a simpler app architecture.

In particular, this makes working with media sessions easier. Instead of using the MediaSessionConnector extension, or writing your own "player to media session" connector, you can create a MediaSession using a Player, like this:

player = ExoPlayer.Builder(context).build()
session = MediaSession.Builder(context, player).build()

Now your media session will automatically reflect the state of your player, and any commands sent to your media session will be automatically forwarded to your player. All that in just two lines of code!

Providing a content library

If your app needs to expose its content library to other apps, like Android Auto, use MediaLibraryService, rather than a MediaBrowserService from MediaCompat.

You'll then create a MediaLibrarySession and implement a MediaLibrarySessionCallback whose methods will be called by the browsing app to obtain your content tree.

Diagram showing how MediaLibraryService can be used to expose a content library

Diagram showing how MediaLibraryService can be used to expose a content library


Easier updates

One of the key benefits of using Jetpack libraries is API stability. If you use symbols that are part of the stable API, you generally don't need to update your code to use a new release of that library within the same major version.

In Media3, some of the most commonly used objects are marked as stable, including the Player API and media session classes.

Most of ExoPlayer's API surface is marked as unstable.

Diagram showing stable and unstable areas of the Media3 API

Diagram showing stable and unstable areas of the Media3 API


To use an unstable method or class you'll need to add the OptIn annotation before using it.

@androidx.annotation.OptIn(UnstableApi::class)
private fun initializeExoPlayer() {
  // ...
}

If your project uses a lot of unstable methods it may be more convenient to add this suppression to your project-wide lint.xml.

<issue id="UnsafeOptInUsageError">
  <ignore
      regexp='\(markerClass = androidx\.media3\.UnstableApi\.class\)'/>
</issue>

Just because part of an API is marked as unstable doesn't mean that the API is unreliable or that you shouldn't use it - it's just a way of informing you that it might change in the future.


Getting started

Media3 is released today in alpha and we'd love you to try it out.

One of the best ways to do this is to check out the demo app, which shows how to play video and audio, and integrate with a media session.

You can add the Media3 dependencies to your app by adding the following artifacts to your build.gradle:

implementation 'androidx.media3:media3-ui:1.0.0-alpha01'
implementation 'androidx.media3:media3-exoplayer:1.0.0-alpha01'
implementation 'androidx.media3:media3-session:1.0.0-alpha01'

If you have feedback or run into problems, please file an issue. We'd really love to hear from you.

For more information check out the “What's next for AndroidX Media and ExoPlayer” talk from Android Dev Summit 2021 and the Media3 release notes.