A player is the component of your app that facilitates playback of media items.
The Media3 Player
interface
sets up an outline for functionality generally handled by a player. This
includes:
- Affecting playback controls, such as playing, pausing, and seeking
- Querying properties of the currently playing media, such as the playback position
- Managing a playlist/queue of media items
- Configuring playback properties, such as shuffling, repeating, speed, and volume
- Rendering video to the screen
Media3 also provides an implementation of the Player
interface, called
ExoPlayer
.
A common interface between components
Several components in Media3 implement the Player interface, for example:
Component | Description & behavior notes |
---|---|
ExoPlayer |
A media player API, and the default implementation of the Player interface. |
MediaController |
Interacts with a MediaSession to send playback commands. If
your Player and MediaSession are in a
Service separate from the Activity or
Fragment where your player's UI lives, you can assign your
MediaController as the player for your
PlayerView UI. Playback and playlist method calls are sent
to your Player through your MediaSession .
|
MediaBrowser |
In addition to the functionality offered by a
MediaController , interacts with a
MediaLibrarySession to browse available media content.
|
ForwardingPlayer |
A Player implementation that forwards method calls to
another Player . Use this class to suppress or modify
specific operations by overriding the respective methods.
|
SimpleBasePlayer |
A Player implementation that reduces the number of methods
to implement to a minimum. Helpful when using a custom player that you
want to connect to a MediaSession .
|
CastPlayer |
A Player implementation that communicates with a Cast
receiver app. Behavior depends on the underlying Cast session.
|
Although a MediaSession
doesn't implement the Player
interface, it requires
a Player
when creating one. Its purpose is to provide access to the Player
from other processes or threads.
Media3 playback architecture
If you have access to a Player
, you should call its methods directly to issue
playback commands. You can advertise your playback and grant external sources
playback control by implementing a MediaSession
. These external sources
implement a MediaController
, which facilitates connecting to a media session
and issuing playback command requests.
When playing media in the background, you need to house your media session and
player within a MediaSessionService
or MediaLibraryService
that runs as a
foreground service. If you do so, you can separate your player from the Activity
in your app that contains the UI for playback control. This may necessitate that
you use a media controller.
Player state
The state of a media player implementing the Player
interface consists
primarily of 4 categories of information:
- Playback state
- Retrieve with
getPlaybackState()
. - The state value defined by the interface are
STATE_IDLE
,STATE_BUFFERING
,STATE_READY
, andSTATE_ENDED
.
- Retrieve with
- Playlist of media items
- A sequence of
MediaItem
instances for playback. - Retrieve with
getCurrentTimeline()
Player
instances can provide playlist operation methods like adding or removing aMediaItem
and convenience methods likegetCurrentMediaItem()
.
- A sequence of
- Play/pause properties, such as:
playWhenReady
: An indication of whether the user wants media to play when possible or remain paused- Playback suppression reason:
An indication of why playback is suppressed, if applicable, even if
playWhenReady
istrue
isPlaying
: An indication of whether the player is currently playing, which will only betrue
if the playback state isSTATE_READY
,playWhenReady
istrue
, and playback is not suppressed
- Playback position, including:
- Current media item index:
The index of the current
MediaItem
in the playlist. isPlayingAd
: An indication of whether an inserted ad is playing.- Current playback position:
The current playback position within the current
MediaItem
or inserted ad.
- Current media item index:
The index of the current
In addition, the Player
interface allows access to the
available tracks,
media metadata,
playback speed,
volume and other
auxiliary properties of the playback.
Listen for changes
Use a Player.Listener
to listen for changes in a Player
. See the ExoPlayer documentation on
Player events for
details on how to create and use a listener.
Note that the listener interface doesn't include any callbacks to track normal playback progression. To continuously monitor playback progress, such as to set up a progress bar UI, you should query the current position at proper intervals.
Kotlin
val handler = Handler(Looper.getMainLooper()) fun checkPlaybackPosition(delayMs: Long): Boolean = handler.postDelayed( { val currentPosition = player.currentPosition // Update UI based on currentPosition checkPlaybackPosition(delayMs) }, delayMs)
Java
Handler handler = new Handler(Looper.getMainLooper()); boolean checkPlaybackPosition(long delayMs) { return handler.postDelayed(() -> { long currentPosition = player.getCurrentPosition(); // Update UI based on currentPosition checkPlaybackPosition(delayMs); }, delayMs); }
Control playback
The Player
interface offers many ways to manipulate the state and control
playback:
- Basic playback controls
like
play()
,pause()
,prepare()
andstop()
. - Playlist operations like
addMediaItem()
orremoveMediaItem()
. - Seeking to change the current item or position.
- Set repeat modes and the shuffle mode.
- Update track selection preferences.
- Set the playback speed.
Custom Player
implementations
To create a custom player, you can extend the
SimpleBasePlayer
included in Media3. This class provides a base implementation of the Player
interface to reduce the number of methods you need to implement to a minimum.
Start by overriding the getState()
method. This method should populate the
current player state when called, including:
- The set of available commands
- Playback properties, such as whether the player should start playing when the
playback state is
STATE_READY
, the index of the currently playing media item, and the playback position within the current item
Kotlin
class CustomPlayer : SimpleBasePlayer(looper) { override fun getState(): State { return State.Builder() .setAvailableCommands(...) // Set which playback commands the player can handle // Configure additional playback properties .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST) .setCurrentMediaItemIndex(0) .setContentPositionMs(0) .build() } }
Java
public class CustomPlayer extends SimpleBasePlayer { public CustomPlayer(Looper looper) { super(looper); } @Override protected State getState() { return new State.Builder() .setAvailableCommands(...) // Set which playback commands the player can handle // Configure additional playback properties .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST) .setCurrentMediaItemIndex(0) .setContentPositionMs(0) .build(); } }
SimpleBasePlayer
will enforce that the State
is created with a valid
combination of state values. It will also handle listeners and informing
listeners of state changes. If you need to manually trigger a state update,
call invalidateState()
.
Beyond the getState()
method, you only need to implement methods that are used
for commands your player declares to be available. Find the overridable handler
method that corresponds to the functionality you want to implement. For example,
override the handleSeek()
method to support operations like COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM
and COMMAND_SEEK_TO_NEXT_MEDIA_ITEM
.