java.lang.Object
↳androidx.media2.common.SessionPlayer
Subclasses:
RemoteSessionPlayer, MediaPlayer
Gradle dependencies
compile group: 'androidx.media2', name: 'media2-common', version: '1.3.0'
- groupId: androidx.media2
- artifactId: media2-common
- version: 1.3.0
Artifact androidx.media2:media2-common:1.3.0 it located at Google repository (https://maven.google.com/)
Overview
Base interface for all media players that want media session.
APIs that return should be the asynchronous calls and shouldn't block
the calling thread. This guarantees the APIs are safe to be called on the main thread.
Topics covered here are:
- Best practices
- Player states
- Invalid method calls
Best practices
Here are best practices when implementing/using SessionPlayer:
- When updating UI, you should respond to SessionPlayer.PlayerCallback invocations instead of
SessionPlayer.PlayerResult objects since the player can be controlled by others.
- When a SessionPlayer object is no longer being used, call SessionPlayer.close() as soon as
possible to release the resources used by the internal player engine associated with the
SessionPlayer. For example, if a player uses hardware decoder, other player instances may
fallback to software decoders or fail to play. You cannot use SessionPlayer instance after
you call SessionPlayer.close(). There is no way to reuse the instance.
- The current playback position can be retrieved with a call to SessionPlayer.getCurrentPosition(), which is helpful for applications such as a music player that need
to keep track of the playback progress.
- The playback position can be adjusted with a call to SessionPlayer.seekTo(long). Although the
asynchronous SessionPlayer.seekTo(long) call returns right away, the actual seek operation may take a
while to finish, especially for audio/video being streamed.
- You can call SessionPlayer.seekTo(long) from the SessionPlayer.PLAYER_STATE_PAUSED. In these cases,
if you are playing a video stream and the requested position is valid, one video frame may
be displayed.
Player states
The playback control of audio/video files is managed as a state machine. The SessionPlayer
defines four states:
- SessionPlayer.PLAYER_STATE_IDLE: Initial state after the instantiation.
While in this state, you should call SessionPlayer.setMediaItem(MediaItem) or SessionPlayer.setPlaylist(List, MediaMetadata). Check returned for potential
error.
Calling SessionPlayer.prepare() transfers this object to SessionPlayer.PLAYER_STATE_PAUSED.
- SessionPlayer.PLAYER_STATE_PAUSED: State when the audio/video playback is paused.
Call SessionPlayer.play() to resume or start playback from the position where it paused.
- SessionPlayer.PLAYER_STATE_PLAYING: State when the player plays the media item.
In this state, SessionPlayer.PlayerCallback.onBufferingStateChanged(SessionPlayer, MediaItem, int) will be called regularly to tell the buffering status.
Playback state would remain SessionPlayer.PLAYER_STATE_PLAYING when the currently playing
media item is changed.
When the playback reaches the end of stream, the behavior depends on repeat mode, set by
SessionPlayer.setRepeatMode(int). If the repeat mode was set to SessionPlayer.REPEAT_MODE_NONE, the
player will transfer to the SessionPlayer.PLAYER_STATE_PAUSED. Otherwise, the SessionPlayer
object remains in the SessionPlayer.PLAYER_STATE_PLAYING and playback will be ongoing.
- SessionPlayer.PLAYER_STATE_ERROR: State when the playback failed and player cannot be recovered
by itself.
In general, playback might fail due to various reasons such as unsupported audio/video
format, poorly interleaved audio/video, resolution too high, streaming timeout, and others.
In addition, due to programming errors, a playback control operation might be performed
from an invalid state. In these cases the player may
transition to this state.
Subclasses may have extra methods to reset the player state to
SessionPlayer.PLAYER_STATE_IDLE from
other states. Take a look at documentations of specific subclass that you're interested in.
Invalid method calls
The only method you safely call from the
SessionPlayer.PLAYER_STATE_ERROR is
SessionPlayer.close(). Any
other methods might return meaningless data.
Subclasses of the SessionPlayer may have extra methods that are safe to be called in the error
state and/or provide a method to recover from the error state. Take a look at documentations of
specific subclass that you're interested in.
Most methods can be called from any non-Error state. In case they're called in invalid state,
the implementation should ignore and would return SessionPlayer.PlayerResult with BaseResult.RESULT_ERROR_INVALID_STATE. The following table lists the methods that aren't
guaranteed to successfully running if they're called from the associated invalid states.
Method Name | Invalid States |
setAudioAttributes | {Paused, Playing} |
prepare | {Paused, Playing} |
play | {Idle} |
pause | {Idle} |
seekTo | {Idle} |
Summary
Fields |
---|
public static final int | BUFFERING_STATE_BUFFERING_AND_PLAYABLE Buffering state indicating the player is buffering but enough has been buffered
for this player to be able to play the content. |
public static final int | BUFFERING_STATE_BUFFERING_AND_STARVED Buffering state indicating the player is buffering, but the player is currently starved
for data, and cannot play. |
public static final int | BUFFERING_STATE_COMPLETE Buffering state indicating the player is done buffering, and the remainder of the content is
available for playback. |
public static final int | BUFFERING_STATE_UNKNOWN Buffering state is unknown. |
public static final int | INVALID_ITEM_INDEX Media item index is invalid. |
public static final int | PLAYER_STATE_ERROR State when the player is in error state and cannot be recovered self. |
public static final int | PLAYER_STATE_IDLE State when the player is idle, and needs configuration to start playback. |
public static final int | PLAYER_STATE_PAUSED State when the player's playback is paused |
public static final int | PLAYER_STATE_PLAYING State when the player's playback is ongoing |
public static final int | REPEAT_MODE_ALL Playing media list will be repeated. |
public static final int | REPEAT_MODE_GROUP Playback of the playing media group will be repeated. |
public static final int | REPEAT_MODE_NONE Playback will be stopped at the end of the playing media list. |
public static final int | REPEAT_MODE_ONE Playback of the current playing media item will be repeated. |
public static final int | SHUFFLE_MODE_ALL Media list will be played in shuffled order. |
public static final int | SHUFFLE_MODE_GROUP Media group will be played in shuffled order. |
public static final int | SHUFFLE_MODE_NONE Media list will be played in order. |
public static final long | UNKNOWN_TIME Value indicating the time is unknown |
Methods |
---|
public abstract <any> | addPlaylistItem(int index, MediaItem item)
Adds the media item to the playlist at the index. |
public void | close()
Removes all existing references to callbacks and executors. |
public <any> | deselectTrack(SessionPlayer.TrackInfo trackInfo)
Deselects the SessionPlayer.TrackInfo for the current media item. |
public abstract AudioAttributesCompat | getAudioAttributes()
Gets the AudioAttributesCompat that media player has. |
public abstract long | getBufferedPosition()
Gets the position for how much has been buffered, or SessionPlayer.UNKNOWN_TIME if unknown. |
public abstract int | getBufferingState()
Returns the current buffering state of the player. |
protected final java.util.List<Pair> | getCallbacks()
Gets the callbacks with executors for subclasses to notify player events. |
public abstract MediaItem | getCurrentMediaItem()
Gets the current media item, which is currently playing or would be played with later
SessionPlayer.play(). |
public abstract int | getCurrentMediaItemIndex()
Gets the index of current media item in playlist. |
public abstract long | getCurrentPosition()
Gets the current playback position. |
public abstract long | getDuration()
Gets the duration of the current media item, or SessionPlayer.UNKNOWN_TIME if unknown. |
public abstract int | getNextMediaItemIndex()
Gets the next item index in the playlist. |
public abstract float | getPlaybackSpeed()
Gets the actual playback speed to be used by the player when playing. |
public abstract int | getPlayerState()
Gets the current player state. |
public abstract java.util.List<MediaItem> | getPlaylist()
Gets the playlist. |
public abstract MediaMetadata | getPlaylistMetadata()
Gets the playlist metadata. |
public abstract int | getPreviousMediaItemIndex()
Gets the previous item index in the playlist. |
public abstract int | getRepeatMode()
Gets the repeat mode. |
public SessionPlayer.TrackInfo | getSelectedTrack(int trackType)
Gets currently selected track's SessionPlayer.TrackInfo for the given track type. |
public abstract int | getShuffleMode()
Gets the shuffle mode. |
public java.util.List<SessionPlayer.TrackInfo> | getTracks()
Gets the full list of selected and unselected tracks that the media contains. |
public VideoSize | getVideoSize()
Gets the size of the video. |
public <any> | movePlaylistItem(int fromIndex, int toIndex)
Moves the media item at fromIdx to toIdx in the playlist. |
public abstract <any> | pause()
Pauses playback. |
public abstract <any> | play()
Starts or resumes playback. |
public abstract <any> | prepare()
Prepares the media items for playback. |
public final void | registerPlayerCallback(java.util.concurrent.Executor executor, SessionPlayer.PlayerCallback callback)
Register SessionPlayer.PlayerCallback to listen changes. |
public abstract <any> | removePlaylistItem(int index)
Removes the media item from the playlist |
public abstract <any> | replacePlaylistItem(int index, MediaItem item)
Replaces the media item at index in the playlist. |
public abstract <any> | seekTo(long position)
Seeks to the specified position. |
public <any> | selectTrack(SessionPlayer.TrackInfo trackInfo)
Selects the SessionPlayer.TrackInfo for the current media item. |
public abstract <any> | setAudioAttributes(AudioAttributesCompat attributes)
Sets the AudioAttributesCompat to be used during the playback of the media. |
public abstract <any> | setMediaItem(MediaItem item)
Sets a MediaItem for playback. |
public abstract <any> | setPlaybackSpeed(float playbackSpeed)
Sets the playback speed. |
public abstract <any> | setPlaylist(java.util.List<MediaItem> list, MediaMetadata metadata)
Sets a list of MediaItem with metadata. |
public abstract <any> | setRepeatMode(int repeatMode)
Sets the repeat mode. |
public abstract <any> | setShuffleMode(int shuffleMode)
Sets the shuffle mode. |
public <any> | setSurface(Surface surface)
Sets the to be used as the sink for the video portion of the media. |
public abstract <any> | skipToNextPlaylistItem()
Skips to the next item in the playlist. |
public abstract <any> | skipToPlaylistItem(int index)
Skips to the item in the playlist at the index. |
public abstract <any> | skipToPreviousPlaylistItem()
Skips to the previous item in the playlist. |
public final void | unregisterPlayerCallback(SessionPlayer.PlayerCallback callback)
Unregister the previously registered SessionPlayer.PlayerCallback. |
public abstract <any> | updatePlaylistMetadata(MediaMetadata metadata)
Updates the playlist metadata while keeping the playlist as-is. |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Fields
public static final int
PLAYER_STATE_IDLEState when the player is idle, and needs configuration to start playback.
public static final int
PLAYER_STATE_PAUSEDState when the player's playback is paused
public static final int
PLAYER_STATE_PLAYINGState when the player's playback is ongoing
public static final int
PLAYER_STATE_ERRORState when the player is in error state and cannot be recovered self.
public static final int
BUFFERING_STATE_UNKNOWNBuffering state is unknown.
public static final int
BUFFERING_STATE_BUFFERING_AND_PLAYABLEBuffering state indicating the player is buffering but enough has been buffered
for this player to be able to play the content.
See SessionPlayer.getBufferedPosition() for how far is buffered already.
public static final int
BUFFERING_STATE_BUFFERING_AND_STARVEDBuffering state indicating the player is buffering, but the player is currently starved
for data, and cannot play.
public static final int
BUFFERING_STATE_COMPLETEBuffering state indicating the player is done buffering, and the remainder of the content is
available for playback.
public static final int
REPEAT_MODE_NONEPlayback will be stopped at the end of the playing media list.
public static final int
REPEAT_MODE_ONEPlayback of the current playing media item will be repeated.
public static final int
REPEAT_MODE_ALLPlaying media list will be repeated.
public static final int
REPEAT_MODE_GROUPPlayback of the playing media group will be repeated.
A group is a logical block of media items which is specified in the section 5.7 of the
Bluetooth AVRCP 1.6. An example of a group is the playlist.
public static final int
SHUFFLE_MODE_NONEMedia list will be played in order.
public static final int
SHUFFLE_MODE_ALLMedia list will be played in shuffled order.
public static final int
SHUFFLE_MODE_GROUPMedia group will be played in shuffled order.
A group is a logical block of media items which is specified in the section 5.7 of the
Bluetooth AVRCP 1.6. An example of a group is the playlist.
public static final long
UNKNOWN_TIMEValue indicating the time is unknown
public static final int
INVALID_ITEM_INDEXMedia item index is invalid. This value will be returned when the corresponding media item
does not exist.
Constructors
Methods
public abstract <any>
play()
Starts or resumes playback.
On success, this transfers the player state to SessionPlayer.PLAYER_STATE_PLAYING and
a SessionPlayer.PlayerResult should be returned with the current media item when the command
was completed. If it is called in SessionPlayer.PLAYER_STATE_IDLE or SessionPlayer.PLAYER_STATE_ERROR,
it should be ignored and a SessionPlayer.PlayerResult should be returned with
BaseResult.RESULT_ERROR_INVALID_STATE.
Returns:
a representing the pending completion of the command
public abstract <any>
pause()
Pauses playback.
On success, this transfers the player state to SessionPlayer.PLAYER_STATE_PAUSED and
a SessionPlayer.PlayerResult should be returned with the current media item when the command
was completed. If it is called in SessionPlayer.PLAYER_STATE_IDLE or SessionPlayer.PLAYER_STATE_ERROR,
it should be ignored and a SessionPlayer.PlayerResult should be returned with
BaseResult.RESULT_ERROR_INVALID_STATE.
Returns:
a representing the pending completion of the command
public abstract <any>
prepare()
Prepares the media items for playback. During this time, the player may allocate resources
required to play, such as audio and video decoders. Before calling this API, set media
item(s) through either SessionPlayer.setMediaItem(MediaItem) or SessionPlayer.setPlaylist(List, MediaMetadata).
On success, this transfers the player state from SessionPlayer.PLAYER_STATE_IDLE to
SessionPlayer.PLAYER_STATE_PAUSED and a SessionPlayer.PlayerResult should be returned with the prepared
media item when the command completed. If it's not called in SessionPlayer.PLAYER_STATE_IDLE,
it should be ignored and SessionPlayer.PlayerResult should be returned with
BaseResult.RESULT_ERROR_INVALID_STATE.
Returns:
a representing the pending completion of the command
public abstract <any>
seekTo(long position)
Seeks to the specified position.
The position is the relative position based on the MediaItem.getStartPosition(). So
calling SessionPlayer.seekTo(long) with 0 means the seek to the start position.
On success, a SessionPlayer.PlayerResult should be returned with the current media item when the
command completed. If it's called in SessionPlayer.PLAYER_STATE_IDLE, it is ignored and
a SessionPlayer.PlayerResult should be returned with
BaseResult.RESULT_ERROR_INVALID_STATE.
Parameters:
position: the new playback position in ms. The value should be in the range of start
and end positions defined in MediaItem.
Returns:
a representing the pending completion of the command
public abstract <any>
setPlaybackSpeed(float playbackSpeed)
Sets the playback speed. The default playback speed is 1.0f, and negative values
indicate reverse playback and 0.0f is not allowed.
The supported playback speed range depends on the underlying player implementation, so it is
recommended to query the actual speed of the player via SessionPlayer.getPlaybackSpeed() after the
operation completes. In particular, please note that player implementations may not support
reverse playback.
On success, a SessionPlayer.PlayerResult should be returned with the current media item when the
command completed.
Parameters:
playbackSpeed: the requested playback speed
Returns:
a representing the pending completion of the command
See also: SessionPlayer.getPlaybackSpeed(), SessionPlayer.PlayerCallback.onPlaybackSpeedChanged(SessionPlayer, float)
Sets the AudioAttributesCompat to be used during the playback of the media.
You must call this method in SessionPlayer.PLAYER_STATE_IDLE in order for the audio attributes to
become effective thereafter. Otherwise, the call would be ignored and SessionPlayer.PlayerResult
should be returned with BaseResult.RESULT_ERROR_INVALID_STATE.
On success, a SessionPlayer.PlayerResult should be returned with the current media item when the
command completed.
Parameters:
attributes: non-null AudioAttributes
.
public abstract int
getPlayerState()
Gets the current player state.
Returns:
the current player state
See also: SessionPlayer.PlayerCallback.onPlayerStateChanged(SessionPlayer, int), SessionPlayer.PLAYER_STATE_IDLE, SessionPlayer.PLAYER_STATE_PAUSED, SessionPlayer.PLAYER_STATE_PLAYING, SessionPlayer.PLAYER_STATE_ERROR
public abstract long
getCurrentPosition()
Gets the current playback position.
The position is the relative position based on the MediaItem.getStartPosition().
So the position 0 means the start position of the MediaItem.
Returns:
the current playback position in ms, or SessionPlayer.UNKNOWN_TIME if unknown
public abstract long
getDuration()
Gets the duration of the current media item, or SessionPlayer.UNKNOWN_TIME if unknown. If the
current MediaItem has either start or end position, then duration would be adjusted
accordingly instead of returning the whole size of the MediaItem.
Returns:
the duration in ms, or SessionPlayer.UNKNOWN_TIME if unknown
public abstract long
getBufferedPosition()
Gets the position for how much has been buffered, or SessionPlayer.UNKNOWN_TIME if unknown.
The position is the relative position based on the MediaItem.getStartPosition().
So the position 0 means the start position of the MediaItem.
Returns:
the buffered position in ms, or SessionPlayer.UNKNOWN_TIME if unknown
public abstract int
getBufferingState()
Returns the current buffering state of the player.
During the buffering, see SessionPlayer.getBufferedPosition() for the quantifying the amount
already buffered.
Returns:
the buffering state, or SessionPlayer.BUFFERING_STATE_UNKNOWN if unknown
See also: SessionPlayer.getBufferedPosition()
public abstract float
getPlaybackSpeed()
Gets the actual playback speed to be used by the player when playing. A value of 1.0f
is the default playback value, and a negative value indicates reverse playback.
Note that it may differ from the speed set in SessionPlayer.setPlaybackSpeed(float).
Returns:
the actual playback speed
Gets the size of the video.
Returns:
the size of the video. The width and height of size could be 0 if there is no video
or the size has not been determined yet.
See also: SessionPlayer.PlayerCallback.onVideoSizeChanged(SessionPlayer, VideoSize)
public <any>
setSurface(Surface surface)
Sets the to be used as the sink for the video portion of the media.
A null surface will reset any Surface and result in only the audio track being played.
On success, a SessionPlayer.PlayerResult is returned with
the current media item when the command completed.
Parameters:
surface: the to be used for the video portion of the media
Returns:
a which represents the pending completion of the command
public abstract <any>
setPlaylist(java.util.List<MediaItem> list,
MediaMetadata metadata)
Sets a list of MediaItem with metadata. Use this or SessionPlayer.setMediaItem(MediaItem) to specify
which items to play.
This can be called multiple times in any states other than SessionPlayer.PLAYER_STATE_ERROR. This
would override previous SessionPlayer.setMediaItem(MediaItem) or SessionPlayer.setPlaylist(List, MediaMetadata) calls.
Ensure uniqueness of each MediaItem in the playlist so the session can uniquely
identity individual items. All MediaItems shouldn't be null as well.
It's recommended to fill MediaMetadata in each MediaItem especially for the
duration information with the key MediaMetadata.METADATA_KEY_DURATION. Without the
duration information in the metadata, session will do extra work to get the duration and send
it to the controller.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) and SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem)
when it's completed. The current media item would be the first item in the playlist.
The implementation must close the in the FileMediaItem
when a media item in the playlist is a FileMediaItem.
On success, a SessionPlayer.PlayerResult should be returned with the first media item of the
playlist when the command completed.
Parameters:
list: a list of MediaItem objects to set as a play list
Returns:
a which represents the pending completion of the command
See also: SessionPlayer.setMediaItem(MediaItem), SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata), SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem)
Gets the AudioAttributesCompat that media player has.
public abstract <any>
setMediaItem(
MediaItem item)
Sets a MediaItem for playback. Use this or SessionPlayer.setPlaylist(List, MediaMetadata) to specify which
items to play. If you want to change current item in the playlist, use one of
SessionPlayer.skipToPlaylistItem(int), SessionPlayer.skipToNextPlaylistItem(), or
SessionPlayer.skipToPreviousPlaylistItem() instead of this method.
This can be called multiple times in any states other than SessionPlayer.PLAYER_STATE_ERROR. This
would override previous SessionPlayer.setMediaItem(MediaItem) or SessionPlayer.setPlaylist(List, MediaMetadata) calls.
It's recommended to fill MediaMetadata in MediaItem especially for the
duration information with the key MediaMetadata.METADATA_KEY_DURATION. Without the
duration information in the metadata, session will do extra work to get the duration and send
it to the controller.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) and SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem)
when it's completed. The current item would be the item given here.
The implementation must close the in the FileMediaItem
if the given media item is a FileMediaItem.
On success, a SessionPlayer.PlayerResult should be returned with item set.
Parameters:
item: the descriptor of media item you want to play
Returns:
a which represents the pending completion of the command
See also: SessionPlayer.setPlaylist(List, MediaMetadata), SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata), SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem)
public abstract <any>
addPlaylistItem(int index,
MediaItem item)
Adds the media item to the playlist at the index. Index equals to or greater than
the current playlist size (e.g. MAX_VALUE
) will add the item at the end of
the playlist.
If index is less than or equal to the current index of the playlist,
the current index of the playlist should be increased correspondingly.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) when it's
completed.
The implementation must close the in the FileMediaItem
if the given media item is a FileMediaItem.
On success, a SessionPlayer.PlayerResult should be returned with item added.
Parameters:
index: the index of the item you want to add in the playlist
item: the media item you want to add
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata)
public abstract <any>
removePlaylistItem(int index)
Removes the media item from the playlist
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) when it's
completed.
On success, a SessionPlayer.PlayerResult should be returned with item removed.
If the last item is removed, the player should be moved to SessionPlayer.PLAYER_STATE_IDLE.
Parameters:
index: the index of the item you want to remove in the playlist
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata)
public abstract <any>
replacePlaylistItem(int index,
MediaItem item)
Replaces the media item at index in the playlist. This can be also used to update metadata of
an item.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) when it's
completed.
The implementation must close the in the FileMediaItem
if the given media item is a FileMediaItem.
On success, a SessionPlayer.PlayerResult should be returned with item set.
Parameters:
index: the index of the item to replace in the playlist
item: the new item
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata)
public <any>
movePlaylistItem(int fromIndex, int toIndex)
Moves the media item at fromIdx to toIdx in the playlist.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) when it's
completed.
On success, a SessionPlayer.PlayerResult should be returned with item set.
Parameters:
fromIndex: the media item's initial index in the playlist
toIndex: the media item's target index in the playlist
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata)
public abstract <any>
skipToPreviousPlaylistItem()
Skips to the previous item in the playlist.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) when it's
completed.
On success, a SessionPlayer.PlayerResult should be returned with the current media item when the
command completed.
Returns:
a representing the pending completion of the command
See also: SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem)
public abstract <any>
skipToNextPlaylistItem()
Skips to the next item in the playlist.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) when it's
completed.
On success, a SessionPlayer.PlayerResult should be returned with the current media item when the
command completed.
Returns:
a representing the pending completion of the command
See also: SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem)
public abstract <any>
skipToPlaylistItem(int index)
Skips to the item in the playlist at the index.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) when it's
completed.
On success, a SessionPlayer.PlayerResult should be returned with the current media item when the
command completed.
Parameters:
index: the index of the item you want to play in the playlist
See also: SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem)
public abstract <any>
updatePlaylistMetadata(
MediaMetadata metadata)
Updates the playlist metadata while keeping the playlist as-is.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onPlaylistMetadataChanged(SessionPlayer, MediaMetadata) when it's
completed.
On success, a SessionPlayer.PlayerResult should be returned with the current media item when the
command completed.
Parameters:
metadata: metadata of the playlist
See also: SessionPlayer.PlayerCallback.onPlaylistMetadataChanged(SessionPlayer, MediaMetadata)
public abstract <any>
setRepeatMode(int repeatMode)
Sets the repeat mode.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onRepeatModeChanged(SessionPlayer, int) when it's completed.
On success, a SessionPlayer.PlayerResult should be returned with the current media item when the
command completed.
Parameters:
repeatMode: repeat mode
See also: SessionPlayer.REPEAT_MODE_NONE, SessionPlayer.REPEAT_MODE_ONE, SessionPlayer.REPEAT_MODE_ALL, SessionPlayer.REPEAT_MODE_GROUP, SessionPlayer.PlayerCallback.onRepeatModeChanged(SessionPlayer, int)
public abstract <any>
setShuffleMode(int shuffleMode)
Sets the shuffle mode.
The implementation must notify registered callbacks with
SessionPlayer.PlayerCallback.onShuffleModeChanged(SessionPlayer, int) when it's completed.
On success, a SessionPlayer.PlayerResult should be returned with the current media item when the
command completed.
Parameters:
shuffleMode: the shuffle mode
Returns:
a representing the pending completion of the command
See also: SessionPlayer.SHUFFLE_MODE_NONE, SessionPlayer.SHUFFLE_MODE_ALL, SessionPlayer.SHUFFLE_MODE_GROUP, SessionPlayer.PlayerCallback.onShuffleModeChanged(SessionPlayer, int)
public abstract java.util.List<MediaItem>
getPlaylist()
Gets the playlist. It can be null if the playlist hasn't been set or it's reset by
SessionPlayer.setMediaItem(MediaItem).
Returns:
playlist, or null
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata)
Gets the playlist metadata.
Returns:
metadata metadata of the playlist, or null if none is set
See also: SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata), SessionPlayer.PlayerCallback.onPlaylistMetadataChanged(SessionPlayer, MediaMetadata)
public abstract int
getRepeatMode()
Gets the repeat mode.
Returns:
repeat mode
See also: SessionPlayer.REPEAT_MODE_NONE, SessionPlayer.REPEAT_MODE_ONE, SessionPlayer.REPEAT_MODE_ALL, SessionPlayer.REPEAT_MODE_GROUP, SessionPlayer.PlayerCallback.onRepeatModeChanged(SessionPlayer, int)
public abstract int
getShuffleMode()
Gets the shuffle mode.
Returns:
the shuffle mode
See also: SessionPlayer.SHUFFLE_MODE_NONE, SessionPlayer.SHUFFLE_MODE_ALL, SessionPlayer.SHUFFLE_MODE_GROUP, SessionPlayer.PlayerCallback.onShuffleModeChanged(SessionPlayer, int)
public abstract
MediaItem getCurrentMediaItem()
Gets the current media item, which is currently playing or would be played with later
SessionPlayer.play(). This value may be updated when
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) or
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) is
called.
Returns:
the current media item. Can be null only when the player is in
SessionPlayer.PLAYER_STATE_IDLE and a media item or playlist hasn't been set.
See also: SessionPlayer.setMediaItem(MediaItem), SessionPlayer.setPlaylist(List, MediaMetadata)
public abstract int
getCurrentMediaItemIndex()
Gets the index of current media item in playlist. This value should be updated when
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) or
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) is called.
Returns:
the index of current media item. Can be SessionPlayer.INVALID_ITEM_INDEX when current
media item is null or not in the playlist, and when the playlist hasn't been set.
public abstract int
getPreviousMediaItemIndex()
Gets the previous item index in the playlist. This value should be updated when
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) or
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) is called.
Returns:
the index of previous media item. Can be SessionPlayer.INVALID_ITEM_INDEX only when
previous media item does not exist or playlist hasn't been set.
public abstract int
getNextMediaItemIndex()
Gets the next item index in the playlist. This value should be updated when
SessionPlayer.PlayerCallback.onCurrentMediaItemChanged(SessionPlayer, MediaItem) or
SessionPlayer.PlayerCallback.onPlaylistChanged(SessionPlayer, List, MediaMetadata) is called.
Returns:
the index of next media item. Can be SessionPlayer.INVALID_ITEM_INDEX only when next media
item does not exist or playlist hasn't been set.
Register SessionPlayer.PlayerCallback to listen changes.
Parameters:
executor: a callback Executor
callback: a PlayerCallback
Unregister the previously registered SessionPlayer.PlayerCallback.
Parameters:
callback: the callback to be removed
protected final java.util.List<Pair>
getCallbacks()
Gets the callbacks with executors for subclasses to notify player events.
Returns:
map of callbacks and its executors
public java.util.List<SessionPlayer.TrackInfo>
getTracks()
Gets the full list of selected and unselected tracks that the media contains. The order of
the list is irrelevant as different players expose tracks in different ways, but the tracks
will generally be ordered based on track type.
The types of tracks supported may vary based on player implementation.
Returns:
list of tracks. The total number of tracks is the size of the list. If empty,
the implementation should return an empty list instead of null.
See also: SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_VIDEO, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_AUDIO, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_METADATA
Selects the SessionPlayer.TrackInfo for the current media item.
Generally one track will be selected for each track type.
The types of tracks supported may vary based on player implementation.
Note: SessionPlayer.getTracks() returns the list of tracks that can be selected, but the
list may be invalidated when SessionPlayer.PlayerCallback.onTracksChanged(SessionPlayer, List)
is called.
Parameters:
trackInfo: track to be selected
Returns:
a representing the pending completion of the command
See also: SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_VIDEO, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_AUDIO, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_METADATA, SessionPlayer.PlayerCallback
Deselects the SessionPlayer.TrackInfo for the current media item.
Generally, a track should already be selected in order to be deselected, and audio and video
tracks should not be deselected.
The types of tracks supported may vary based on player implementation.
Note: SessionPlayer.getSelectedTrack(int) returns the currently selected track per track type that
can be deselected, but the list may be invalidated when
SessionPlayer.PlayerCallback.onTracksChanged(SessionPlayer, List) is called.
Parameters:
trackInfo: track to be deselected
Returns:
a which represents the pending completion of the command
See also: SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_VIDEO, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_AUDIO, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_METADATA, SessionPlayer.PlayerCallback
Gets currently selected track's SessionPlayer.TrackInfo for the given track type.
The returned value can be outdated after
SessionPlayer.PlayerCallback.onTracksChanged(SessionPlayer, List),
SessionPlayer.PlayerCallback,
or SessionPlayer.PlayerCallback is called.
Parameters:
trackType: type of selected track
Returns:
selected track info
See also: SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_VIDEO, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_AUDIO, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE, SessionPlayer.TrackInfo.MEDIA_TRACK_TYPE_METADATA
Removes all existing references to callbacks and executors.
Note: Sub classes of SessionPlayer that override this API should call this super
method.
Source
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.media2.common;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.util.Log;
import android.view.Surface;
import androidx.annotation.CallSuper;
import androidx.annotation.GuardedBy;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.concurrent.futures.ResolvableFuture;
import androidx.core.util.Pair;
import androidx.media.AudioAttributesCompat;
import androidx.versionedparcelable.CustomVersionedParcelable;
import androidx.versionedparcelable.NonParcelField;
import androidx.versionedparcelable.ParcelField;
import androidx.versionedparcelable.VersionedParcelize;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.Closeable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
/**
* Base interface for all media players that want media session.
*
* <p>APIs that return {@link ListenableFuture} should be the asynchronous calls and shouldn't block
* the calling thread. This guarantees the APIs are safe to be called on the main thread.
*
* <p>Topics covered here are:
*
* <ol>
* <li><a href="#BestPractices">Best practices</a>
* <li><a href="#PlayerStates">Player states</a>
* <li><a href="#InvalidStates">Invalid method calls</a>
* </ol>
*
* <h3 id="BestPractices">Best practices</h3>
*
* Here are best practices when implementing/using SessionPlayer:
*
* <ul>
* <li>When updating UI, you should respond to {@link PlayerCallback} invocations instead of
* {@link PlayerResult} objects since the player can be controlled by others.
* <li>When a SessionPlayer object is no longer being used, call {@link #close()} as soon as
* possible to release the resources used by the internal player engine associated with the
* SessionPlayer. For example, if a player uses hardware decoder, other player instances may
* fallback to software decoders or fail to play. You cannot use SessionPlayer instance after
* you call {@link #close()}. There is no way to reuse the instance.
* <li>The current playback position can be retrieved with a call to {@link
* #getCurrentPosition()}, which is helpful for applications such as a music player that need
* to keep track of the playback progress.
* <li>The playback position can be adjusted with a call to {@link #seekTo(long)}. Although the
* asynchronous {@link #seekTo} call returns right away, the actual seek operation may take a
* while to finish, especially for audio/video being streamed.
* <li>You can call {@link #seekTo(long)} from the {@link #PLAYER_STATE_PAUSED}. In these cases,
* if you are playing a video stream and the requested position is valid, one video frame may
* be displayed.
* </ul>
*
* <h3 id="PlayerStates">Player states</h3>
*
* The playback control of audio/video files is managed as a state machine. The SessionPlayer
* defines four states:
*
* <ol>
* <li>{@link #PLAYER_STATE_IDLE}: Initial state after the instantiation.
* <p>While in this state, you should call {@link #setMediaItem(MediaItem)} or {@link
* #setPlaylist(List, MediaMetadata)}. Check returned {@link ListenableFuture} for potential
* error.
* <p>Calling {@link #prepare()} transfers this object to {@link #PLAYER_STATE_PAUSED}.
* <li>{@link #PLAYER_STATE_PAUSED}: State when the audio/video playback is paused.
* <p>Call {@link #play()} to resume or start playback from the position where it paused.
* <li>{@link #PLAYER_STATE_PLAYING}: State when the player plays the media item.
* <p>In this state, {@link PlayerCallback#onBufferingStateChanged( SessionPlayer, MediaItem,
* int)} will be called regularly to tell the buffering status.
* <p>Playback state would remain {@link #PLAYER_STATE_PLAYING} when the currently playing
* media item is changed.
* <p>When the playback reaches the end of stream, the behavior depends on repeat mode, set by
* {@link #setRepeatMode(int)}. If the repeat mode was set to {@link #REPEAT_MODE_NONE}, the
* player will transfer to the {@link #PLAYER_STATE_PAUSED}. Otherwise, the SessionPlayer
* object remains in the {@link #PLAYER_STATE_PLAYING} and playback will be ongoing.
* <li>{@link #PLAYER_STATE_ERROR}: State when the playback failed and player cannot be recovered
* by itself.
* <p>In general, playback might fail due to various reasons such as unsupported audio/video
* format, poorly interleaved audio/video, resolution too high, streaming timeout, and others.
* In addition, due to programming errors, a playback control operation might be performed
* from an <a href="#InvalidStates">invalid state</a>. In these cases the player may
* transition to this state.
* </ol>
*
* Subclasses may have extra methods to reset the player state to {@link #PLAYER_STATE_IDLE} from
* other states. Take a look at documentations of specific subclass that you're interested in.
*
* <p>
*
* <h3 id="InvalidStates">Invalid method calls</h3>
*
* The only method you safely call from the {@link #PLAYER_STATE_ERROR} is {@link #close()}. Any
* other methods might return meaningless data.
*
* <p>Subclasses of the SessionPlayer may have extra methods that are safe to be called in the error
* state and/or provide a method to recover from the error state. Take a look at documentations of
* specific subclass that you're interested in.
*
* <p>Most methods can be called from any non-Error state. In case they're called in invalid state,
* the implementation should ignore and would return {@link PlayerResult} with {@link
* PlayerResult#RESULT_ERROR_INVALID_STATE}. The following table lists the methods that aren't
* guaranteed to successfully running if they're called from the associated invalid states.
*
* <p>
*
* <table>
* <tr><th>Method Name</th> <th>Invalid States</th></tr>
* <tr><td>setAudioAttributes</td> <td>{Paused, Playing}</td></tr>
* <tr><td>prepare</td> <td>{Paused, Playing}</td></tr>
* <tr><td>play</td> <td>{Idle}</td></tr>
* <tr><td>pause</td> <td>{Idle}</td></tr>
* <tr><td>seekTo</td> <td>{Idle}</td></tr>
* </table>
*
* @deprecated androidx.media2 is deprecated. Please migrate to <a
* href="https://developer.android.com/guide/topics/media/media3">androidx.media3</a>.
*/
// Previously MediaSessionCompat.Callback.
// Players can extend this directly (e.g. MediaPlayer) or create wrapper and control underlying
// player.
// Preferably it can be interface, but API guideline requires to use abstract class.
@Deprecated
public abstract class SessionPlayer implements Closeable {
private static final String TAG = "SessionPlayer";
/**
*/
@RestrictTo(LIBRARY_GROUP)
@IntDef({
PLAYER_STATE_IDLE,
PLAYER_STATE_PAUSED,
PLAYER_STATE_PLAYING,
PLAYER_STATE_ERROR})
@Retention(RetentionPolicy.SOURCE)
public @interface PlayerState {
}
/**
*/
@RestrictTo(LIBRARY_GROUP)
@IntDef({
BUFFERING_STATE_UNKNOWN,
BUFFERING_STATE_BUFFERING_AND_PLAYABLE,
BUFFERING_STATE_BUFFERING_AND_STARVED,
BUFFERING_STATE_COMPLETE})
@Retention(RetentionPolicy.SOURCE)
public @interface BuffState {
}
/**
* State when the player is idle, and needs configuration to start playback.
*/
public static final int PLAYER_STATE_IDLE = 0;
/**
* State when the player's playback is paused
*/
public static final int PLAYER_STATE_PAUSED = 1;
/**
* State when the player's playback is ongoing
*/
public static final int PLAYER_STATE_PLAYING = 2;
/**
* State when the player is in error state and cannot be recovered self.
*/
public static final int PLAYER_STATE_ERROR = 3;
/**
* Buffering state is unknown.
*/
public static final int BUFFERING_STATE_UNKNOWN = 0;
/**
* Buffering state indicating the player is buffering but enough has been buffered
* for this player to be able to play the content.
* See {@link #getBufferedPosition()} for how far is buffered already.
*/
public static final int BUFFERING_STATE_BUFFERING_AND_PLAYABLE = 1;
/**
* Buffering state indicating the player is buffering, but the player is currently starved
* for data, and cannot play.
*/
public static final int BUFFERING_STATE_BUFFERING_AND_STARVED = 2;
/**
* Buffering state indicating the player is done buffering, and the remainder of the content is
* available for playback.
*/
public static final int BUFFERING_STATE_COMPLETE = 3;
/**
*/
@RestrictTo(LIBRARY_GROUP)
@IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL,
REPEAT_MODE_GROUP})
@Retention(RetentionPolicy.SOURCE)
public @interface RepeatMode {
}
/**
* Playback will be stopped at the end of the playing media list.
*/
public static final int REPEAT_MODE_NONE = 0;
/**
* Playback of the current playing media item will be repeated.
*/
public static final int REPEAT_MODE_ONE = 1;
/**
* Playing media list will be repeated.
*/
public static final int REPEAT_MODE_ALL = 2;
/**
* Playback of the playing media group will be repeated.
* A group is a logical block of media items which is specified in the section 5.7 of the
* Bluetooth AVRCP 1.6. An example of a group is the playlist.
*/
public static final int REPEAT_MODE_GROUP = 3;
/**
*/
@RestrictTo(LIBRARY_GROUP)
@IntDef({SHUFFLE_MODE_NONE, SHUFFLE_MODE_ALL, SHUFFLE_MODE_GROUP})
@Retention(RetentionPolicy.SOURCE)
public @interface ShuffleMode {
}
/**
* Media list will be played in order.
*/
public static final int SHUFFLE_MODE_NONE = 0;
/**
* Media list will be played in shuffled order.
*/
public static final int SHUFFLE_MODE_ALL = 1;
/**
* Media group will be played in shuffled order.
* A group is a logical block of media items which is specified in the section 5.7 of the
* Bluetooth AVRCP 1.6. An example of a group is the playlist.
*/
public static final int SHUFFLE_MODE_GROUP = 2;
/**
* Value indicating the time is unknown
*/
public static final long UNKNOWN_TIME = Long.MIN_VALUE;
/**
* Media item index is invalid. This value will be returned when the corresponding media item
* does not exist.
*/
public static final int INVALID_ITEM_INDEX = -1;
private final Object mLock = new Object();
@GuardedBy("mLock")
private final List<Pair<PlayerCallback, Executor>> mCallbacks = new ArrayList<>();
/**
* Starts or resumes playback.
* <p>
* On success, this transfers the player state to {@link #PLAYER_STATE_PLAYING} and
* a {@link PlayerResult} should be returned with the current media item when the command
* was completed. If it is called in {@link #PLAYER_STATE_IDLE} or {@link #PLAYER_STATE_ERROR},
* it should be ignored and a {@link PlayerResult} should be returned with
* {@link PlayerResult#RESULT_ERROR_INVALID_STATE}.
*
* @return a {@link ListenableFuture} representing the pending completion of the command
*/
@NonNull
public abstract ListenableFuture<PlayerResult> play();
/**
* Pauses playback.
* <p>
* On success, this transfers the player state to {@link #PLAYER_STATE_PAUSED} and
* a {@link PlayerResult} should be returned with the current media item when the command
* was completed. If it is called in {@link #PLAYER_STATE_IDLE} or {@link #PLAYER_STATE_ERROR},
* it should be ignored and a {@link PlayerResult} should be returned with
* {@link PlayerResult#RESULT_ERROR_INVALID_STATE}.
*
* @return a {@link ListenableFuture} representing the pending completion of the command
*/
@NonNull
public abstract ListenableFuture<PlayerResult> pause();
/**
* Prepares the media items for playback. During this time, the player may allocate resources
* required to play, such as audio and video decoders. Before calling this API, set media
* item(s) through either {@link #setMediaItem} or {@link #setPlaylist}.
* <p>
* On success, this transfers the player state from {@link #PLAYER_STATE_IDLE} to
* {@link #PLAYER_STATE_PAUSED} and a {@link PlayerResult} should be returned with the prepared
* media item when the command completed. If it's not called in {@link #PLAYER_STATE_IDLE},
* it should be ignored and {@link PlayerResult} should be returned with
* {@link PlayerResult#RESULT_ERROR_INVALID_STATE}.
*
* @return a {@link ListenableFuture} representing the pending completion of the command
*/
@NonNull
public abstract ListenableFuture<PlayerResult> prepare();
/**
* Seeks to the specified position.
* <p>
* The position is the relative position based on the {@link MediaItem#getStartPosition()}. So
* calling {@link #seekTo(long)} with {@code 0} means the seek to the start position.
* <p>
* On success, a {@link PlayerResult} should be returned with the current media item when the
* command completed. If it's called in {@link #PLAYER_STATE_IDLE}, it is ignored and
* a {@link PlayerResult} should be returned with
* {@link PlayerResult#RESULT_ERROR_INVALID_STATE}.
*
* @return a {@link ListenableFuture} representing the pending completion of the command
* @param position the new playback position in ms. The value should be in the range of start
* and end positions defined in {@link MediaItem}.
*/
@NonNull
public abstract ListenableFuture<PlayerResult> seekTo(long position);
/**
* Sets the playback speed. The default playback speed is {@code 1.0f}, and negative values
* indicate reverse playback and {@code 0.0f} is not allowed.
* <p>
* The supported playback speed range depends on the underlying player implementation, so it is
* recommended to query the actual speed of the player via {@link #getPlaybackSpeed()} after the
* operation completes. In particular, please note that player implementations may not support
* reverse playback.
* <p>
* On success, a {@link PlayerResult} should be returned with the current media item when the
* command completed.
*
* @param playbackSpeed the requested playback speed
* @return a {@link ListenableFuture} representing the pending completion of the command
* @see #getPlaybackSpeed()
* @see PlayerCallback#onPlaybackSpeedChanged(SessionPlayer, float)
*/
@NonNull
public abstract ListenableFuture<PlayerResult> setPlaybackSpeed(float playbackSpeed);
/**
* Sets the {@link AudioAttributesCompat} to be used during the playback of the media.
* <p>
* You must call this method in {@link #PLAYER_STATE_IDLE} in order for the audio attributes to
* become effective thereafter. Otherwise, the call would be ignored and {@link PlayerResult}
* should be returned with {@link PlayerResult#RESULT_ERROR_INVALID_STATE}.
* <p>
* On success, a {@link PlayerResult} should be returned with the current media item when the
* command completed.
*
* @param attributes non-null <code>AudioAttributes</code>.
*/
@NonNull
public abstract ListenableFuture<PlayerResult> setAudioAttributes(
@NonNull AudioAttributesCompat attributes);
/**
* Gets the current player state.
*
* @return the current player state
* @see PlayerCallback#onPlayerStateChanged(SessionPlayer, int)
* @see #PLAYER_STATE_IDLE
* @see #PLAYER_STATE_PAUSED
* @see #PLAYER_STATE_PLAYING
* @see #PLAYER_STATE_ERROR
*/
@PlayerState
public abstract int getPlayerState();
/**
* Gets the current playback position.
* <p>
* The position is the relative position based on the {@link MediaItem#getStartPosition()}.
* So the position {@code 0} means the start position of the {@link MediaItem}.
*
* @return the current playback position in ms, or {@link #UNKNOWN_TIME} if unknown
*/
public abstract long getCurrentPosition();
/**
* Gets the duration of the current media item, or {@link #UNKNOWN_TIME} if unknown. If the
* current {@link MediaItem} has either start or end position, then duration would be adjusted
* accordingly instead of returning the whole size of the {@link MediaItem}.
*
* @return the duration in ms, or {@link #UNKNOWN_TIME} if unknown
*/
public abstract long getDuration();
/**
* Gets the position for how much has been buffered, or {@link #UNKNOWN_TIME} if unknown.
* <p>
* The position is the relative position based on the {@link MediaItem#getStartPosition()}.
* So the position {@code 0} means the start position of the {@link MediaItem}.
*
* @return the buffered position in ms, or {@link #UNKNOWN_TIME} if unknown
*/
public abstract long getBufferedPosition();
/**
* Returns the current buffering state of the player.
* <p>
* During the buffering, see {@link #getBufferedPosition()} for the quantifying the amount
* already buffered.
*
* @return the buffering state, or {@link #BUFFERING_STATE_UNKNOWN} if unknown
* @see #getBufferedPosition()
*/
@BuffState
public abstract int getBufferingState();
/**
* Gets the actual playback speed to be used by the player when playing. A value of {@code 1.0f}
* is the default playback value, and a negative value indicates reverse playback.
* <p>
* Note that it may differ from the speed set in {@link #setPlaybackSpeed(float)}.
*
* @return the actual playback speed
*/
public abstract float getPlaybackSpeed();
/**
* Gets the size of the video.
*
* @return the size of the video. The width and height of size could be 0 if there is no video
* or the size has not been determined yet.
* @see PlayerCallback#onVideoSizeChanged(SessionPlayer, VideoSize)
*/
@NonNull
public VideoSize getVideoSize() {
throw new UnsupportedOperationException("getVideoSize is not implemented");
}
/**
* Sets the {@link Surface} to be used as the sink for the video portion of the media.
* <p>
* A null surface will reset any Surface and result in only the audio track being played.
* <p>
* On success, a {@link SessionPlayer.PlayerResult} is returned with
* the current media item when the command completed.
*
* @param surface the {@link Surface} to be used for the video portion of the media
* @return a {@link ListenableFuture} which represents the pending completion of the command
*/
@NonNull
public ListenableFuture<PlayerResult> setSurface(@Nullable Surface surface) {
throw new UnsupportedOperationException("setSurface is not implemented");
}
/**
* Sets a list of {@link MediaItem} with metadata. Use this or {@link #setMediaItem} to specify
* which items to play.
* <p>
* This can be called multiple times in any states other than {@link #PLAYER_STATE_ERROR}. This
* would override previous {@link #setMediaItem} or {@link #setPlaylist} calls.
* <p>
* Ensure uniqueness of each {@link MediaItem} in the playlist so the session can uniquely
* identity individual items. All {@link MediaItem}s shouldn't be {@code null} as well.
* <p>
* It's recommended to fill {@link MediaMetadata} in each {@link MediaItem} especially for the
* duration information with the key {@link MediaMetadata#METADATA_KEY_DURATION}. Without the
* duration information in the metadata, session will do extra work to get the duration and send
* it to the controller.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistChanged} and {@link PlayerCallback#onCurrentMediaItemChanged}
* when it's completed. The current media item would be the first item in the playlist.
* <p>
* The implementation must close the {@link ParcelFileDescriptor} in the {@link FileMediaItem}
* when a media item in the playlist is a {@link FileMediaItem}.
* <p>
* On success, a {@link PlayerResult} should be returned with the first media item of the
* playlist when the command completed.
*
* @param list a list of {@link MediaItem} objects to set as a play list
* @throws IllegalArgumentException if the given list is {@code null} or empty, or has
* duplicated media items.
* @return a {@link ListenableFuture} which represents the pending completion of the command
* @see #setMediaItem
* @see PlayerCallback#onPlaylistChanged
* @see PlayerCallback#onCurrentMediaItemChanged
*/
@NonNull
public abstract ListenableFuture<PlayerResult> setPlaylist(
@NonNull List<MediaItem> list, @Nullable MediaMetadata metadata);
/**
* Gets the {@link AudioAttributesCompat} that media player has.
*/
@Nullable
public abstract AudioAttributesCompat getAudioAttributes();
/**
* Sets a {@link MediaItem} for playback. Use this or {@link #setPlaylist} to specify which
* items to play. If you want to change current item in the playlist, use one of
* {@link #skipToPlaylistItem}, {@link #skipToNextPlaylistItem}, or
* {@link #skipToPreviousPlaylistItem} instead of this method.
* <p>
* This can be called multiple times in any states other than {@link #PLAYER_STATE_ERROR}. This
* would override previous {@link #setMediaItem} or {@link #setPlaylist} calls.
* <p>
* It's recommended to fill {@link MediaMetadata} in {@link MediaItem} especially for the
* duration information with the key {@link MediaMetadata#METADATA_KEY_DURATION}. Without the
* duration information in the metadata, session will do extra work to get the duration and send
* it to the controller.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistChanged} and {@link PlayerCallback#onCurrentMediaItemChanged}
* when it's completed. The current item would be the item given here.
* <p>
* The implementation must close the {@link ParcelFileDescriptor} in the {@link FileMediaItem}
* if the given media item is a {@link FileMediaItem}.
* <p>
* On success, a {@link PlayerResult} should be returned with {@code item} set.
*
* @param item the descriptor of media item you want to play
* @return a {@link ListenableFuture} which represents the pending completion of the command
* @see #setPlaylist
* @see PlayerCallback#onPlaylistChanged
* @see PlayerCallback#onCurrentMediaItemChanged
* @throws IllegalArgumentException if the given item is {@code null}.
*/
@NonNull
public abstract ListenableFuture<PlayerResult> setMediaItem(
@NonNull MediaItem item);
/**
* Adds the media item to the playlist at the index. Index equals to or greater than
* the current playlist size (e.g. {@link Integer#MAX_VALUE}) will add the item at the end of
* the playlist.
* <p>
* If index is less than or equal to the current index of the playlist,
* the current index of the playlist should be increased correspondingly.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} when it's
* completed.
* <p>
* The implementation must close the {@link ParcelFileDescriptor} in the {@link FileMediaItem}
* if the given media item is a {@link FileMediaItem}.
* <p>
* On success, a {@link PlayerResult} should be returned with {@code item} added.
*
* @param index the index of the item you want to add in the playlist
* @param item the media item you want to add
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
*/
@NonNull
public abstract ListenableFuture<PlayerResult> addPlaylistItem(int index,
@NonNull MediaItem item);
/**
* Removes the media item from the playlist
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} when it's
* completed.
* <p>
* On success, a {@link PlayerResult} should be returned with {@code item} removed.
* <p>
* If the last item is removed, the player should be moved to {@link #PLAYER_STATE_IDLE}.
*
* @param index the index of the item you want to remove in the playlist
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
*/
@NonNull
public abstract ListenableFuture<PlayerResult> removePlaylistItem(
@IntRange(from = 0) int index);
/**
* Replaces the media item at index in the playlist. This can be also used to update metadata of
* an item.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} when it's
* completed.
* <p>
* The implementation must close the {@link ParcelFileDescriptor} in the {@link FileMediaItem}
* if the given media item is a {@link FileMediaItem}.
* <p>
* On success, a {@link PlayerResult} should be returned with {@code item} set.
*
* @param index the index of the item to replace in the playlist
* @param item the new item
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
*/
@NonNull
public abstract ListenableFuture<PlayerResult> replacePlaylistItem(int index,
@NonNull MediaItem item);
/**
* Moves the media item at {@code fromIdx} to {@code toIdx} in the playlist.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} when it's
* completed.
* <p>
* On success, a {@link PlayerResult} should be returned with {@code item} set.
*
* @param fromIndex the media item's initial index in the playlist
* @param toIndex the media item's target index in the playlist
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
*/
@NonNull
public ListenableFuture<PlayerResult> movePlaylistItem(
@IntRange(from = 0) int fromIndex, @IntRange(from = 0) int toIndex) {
throw new UnsupportedOperationException("movePlaylistItem is not implemented");
}
/**
* Skips to the previous item in the playlist.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} when it's
* completed.
* <p>
* On success, a {@link PlayerResult} should be returned with the current media item when the
* command completed.
*
* @return a {@link ListenableFuture} representing the pending completion of the command
* @see PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)
*/
@NonNull
public abstract ListenableFuture<PlayerResult> skipToPreviousPlaylistItem();
/**
* Skips to the next item in the playlist.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} when it's
* completed.
* <p>
* On success, a {@link PlayerResult} should be returned with the current media item when the
* command completed.
*
* @return a {@link ListenableFuture} representing the pending completion of the command
* @see PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)
*/
@NonNull
public abstract ListenableFuture<PlayerResult> skipToNextPlaylistItem();
/**
* Skips to the item in the playlist at the index.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} when it's
* completed.
* <p>
* On success, a {@link PlayerResult} should be returned with the current media item when the
* command completed.
*
* @param index the index of the item you want to play in the playlist
* @see PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)
*/
@NonNull
public abstract ListenableFuture<PlayerResult> skipToPlaylistItem(
@IntRange(from = 0) int index);
/**
* Updates the playlist metadata while keeping the playlist as-is.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onPlaylistMetadataChanged(SessionPlayer, MediaMetadata)} when it's
* completed.
* <p>
* On success, a {@link PlayerResult} should be returned with the current media item when the
* command completed.
*
* @param metadata metadata of the playlist
* @see PlayerCallback#onPlaylistMetadataChanged(SessionPlayer, MediaMetadata)
*/
@NonNull
public abstract ListenableFuture<PlayerResult> updatePlaylistMetadata(
@Nullable MediaMetadata metadata);
/**
* Sets the repeat mode.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onRepeatModeChanged(SessionPlayer, int)} when it's completed.
* <p>
* On success, a {@link PlayerResult} should be returned with the current media item when the
* command completed.
*
* @param repeatMode repeat mode
* @see #REPEAT_MODE_NONE
* @see #REPEAT_MODE_ONE
* @see #REPEAT_MODE_ALL
* @see #REPEAT_MODE_GROUP
* @see PlayerCallback#onRepeatModeChanged(SessionPlayer, int)
*/
@NonNull
public abstract ListenableFuture<PlayerResult> setRepeatMode(
@RepeatMode int repeatMode);
/**
* Sets the shuffle mode.
* <p>
* The implementation must notify registered callbacks with
* {@link PlayerCallback#onShuffleModeChanged(SessionPlayer, int)} when it's completed.
* <p>
* On success, a {@link PlayerResult} should be returned with the current media item when the
* command completed.
*
* @param shuffleMode the shuffle mode
* @return a {@link ListenableFuture} representing the pending completion of the command
* @see #SHUFFLE_MODE_NONE
* @see #SHUFFLE_MODE_ALL
* @see #SHUFFLE_MODE_GROUP
* @see PlayerCallback#onShuffleModeChanged(SessionPlayer, int)
*/
@NonNull
public abstract ListenableFuture<PlayerResult> setShuffleMode(
@ShuffleMode int shuffleMode);
/**
* Gets the playlist. It can be {@code null} if the playlist hasn't been set or it's reset by
* {@link #setMediaItem}.
*
* @return playlist, or {@code null}
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
*/
@Nullable
public abstract List<MediaItem> getPlaylist();
/**
* Gets the playlist metadata.
*
* @return metadata metadata of the playlist, or null if none is set
* @see PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)
* @see PlayerCallback#onPlaylistMetadataChanged(SessionPlayer, MediaMetadata)
*/
@Nullable
public abstract MediaMetadata getPlaylistMetadata();
/**
* Gets the repeat mode.
*
* @return repeat mode
* @see #REPEAT_MODE_NONE
* @see #REPEAT_MODE_ONE
* @see #REPEAT_MODE_ALL
* @see #REPEAT_MODE_GROUP
* @see PlayerCallback#onRepeatModeChanged(SessionPlayer, int)
*/
@RepeatMode
public abstract int getRepeatMode();
/**
* Gets the shuffle mode.
*
* @return the shuffle mode
* @see #SHUFFLE_MODE_NONE
* @see #SHUFFLE_MODE_ALL
* @see #SHUFFLE_MODE_GROUP
* @see PlayerCallback#onShuffleModeChanged(SessionPlayer, int)
*/
@ShuffleMode
public abstract int getShuffleMode();
/**
* Gets the current media item, which is currently playing or would be played with later
* {@link #play}. This value may be updated when
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} or
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} is
* called.
*
* @return the current media item. Can be {@code null} only when the player is in
* {@link #PLAYER_STATE_IDLE} and a media item or playlist hasn't been set.
* @see #setMediaItem
* @see #setPlaylist
*/
@Nullable
public abstract MediaItem getCurrentMediaItem();
/**
* Gets the index of current media item in playlist. This value should be updated when
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} or
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} is called.
*
* @return the index of current media item. Can be {@link #INVALID_ITEM_INDEX} when current
* media item is null or not in the playlist, and when the playlist hasn't been set.
*/
@IntRange(from = INVALID_ITEM_INDEX)
public abstract int getCurrentMediaItemIndex();
/**
* Gets the previous item index in the playlist. This value should be updated when
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} or
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} is called.
*
* @return the index of previous media item. Can be {@link #INVALID_ITEM_INDEX} only when
* previous media item does not exist or playlist hasn't been set.
*/
@IntRange(from = INVALID_ITEM_INDEX)
public abstract int getPreviousMediaItemIndex();
/**
* Gets the next item index in the playlist. This value should be updated when
* {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)} or
* {@link PlayerCallback#onPlaylistChanged(SessionPlayer, List, MediaMetadata)} is called.
*
* @return the index of next media item. Can be {@link #INVALID_ITEM_INDEX} only when next media
* item does not exist or playlist hasn't been set.
*/
@IntRange(from = INVALID_ITEM_INDEX)
public abstract int getNextMediaItemIndex();
// Listeners / Callback related
// Intentionally final not to allow developers to change the behavior
/**
* Register {@link PlayerCallback} to listen changes.
*
* @param executor a callback Executor
* @param callback a PlayerCallback
* @throws IllegalArgumentException if executor or callback is {@code null}.
*/
public final void registerPlayerCallback(
@NonNull /*@CallbackExecutor*/ Executor executor,
@NonNull PlayerCallback callback) {
if (executor == null) {
throw new NullPointerException("executor shouldn't be null");
}
if (callback == null) {
throw new NullPointerException("callback shouldn't be null");
}
synchronized (mLock) {
for (Pair<PlayerCallback, Executor> pair : mCallbacks) {
if (pair.first == callback && pair.second != null) {
Log.w(TAG, "callback is already added. Ignoring.");
return;
}
}
mCallbacks.add(new Pair<>(callback, executor));
}
}
/**
* Unregister the previously registered {@link PlayerCallback}.
*
* @param callback the callback to be removed
* @throws IllegalArgumentException if the callback is {@code null}.
*/
public final void unregisterPlayerCallback(@NonNull PlayerCallback callback) {
if (callback == null) {
throw new NullPointerException("callback shouldn't be null");
}
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
if (mCallbacks.get(i).first == callback) {
mCallbacks.remove(i);
}
}
}
}
/**
* Gets the callbacks with executors for subclasses to notify player events.
*
* @return map of callbacks and its executors
*/
@NonNull
protected final List<Pair<PlayerCallback, Executor>> getCallbacks() {
List<Pair<PlayerCallback, Executor>> list = new ArrayList<>();
synchronized (mLock) {
list.addAll(mCallbacks);
}
return list;
}
/**
* Gets the full list of selected and unselected tracks that the media contains. The order of
* the list is irrelevant as different players expose tracks in different ways, but the tracks
* will generally be ordered based on track type.
* <p>
* The types of tracks supported may vary based on player implementation.
*
* @return list of tracks. The total number of tracks is the size of the list. If empty,
* the implementation should return an empty list instead of {@code null}.
* @see TrackInfo#MEDIA_TRACK_TYPE_VIDEO
* @see TrackInfo#MEDIA_TRACK_TYPE_AUDIO
* @see TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE
* @see TrackInfo#MEDIA_TRACK_TYPE_METADATA
*/
@NonNull
public List<TrackInfo> getTracks() {
throw new UnsupportedOperationException("getTracks is not implemented");
}
/**
* Selects the {@link TrackInfo} for the current media item.
* <p>
* Generally one track will be selected for each track type.
* <p>
* The types of tracks supported may vary based on player implementation.
* <p>
* Note: {@link #getTracks()} returns the list of tracks that can be selected, but the
* list may be invalidated when {@link PlayerCallback#onTracksChanged(SessionPlayer, List)}
* is called.
*
* @param trackInfo track to be selected
* @return a {@link ListenableFuture} representing the pending completion of the command
* @see TrackInfo#MEDIA_TRACK_TYPE_VIDEO
* @see TrackInfo#MEDIA_TRACK_TYPE_AUDIO
* @see TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE
* @see TrackInfo#MEDIA_TRACK_TYPE_METADATA
* @see PlayerCallback#onTrackSelected(SessionPlayer, TrackInfo)
*/
@NonNull
public ListenableFuture<PlayerResult> selectTrack(@NonNull TrackInfo trackInfo) {
throw new UnsupportedOperationException("selectTrack is not implemented");
}
/**
* Deselects the {@link TrackInfo} for the current media item.
* <p>
* Generally, a track should already be selected in order to be deselected, and audio and video
* tracks should not be deselected.
* <p>
* The types of tracks supported may vary based on player implementation.
* <p>
* Note: {@link #getSelectedTrack(int)} returns the currently selected track per track type that
* can be deselected, but the list may be invalidated when
* {@link PlayerCallback#onTracksChanged(SessionPlayer, List)} is called.
*
* @param trackInfo track to be deselected
* @return a {@link ListenableFuture} which represents the pending completion of the command
* @see TrackInfo#MEDIA_TRACK_TYPE_VIDEO
* @see TrackInfo#MEDIA_TRACK_TYPE_AUDIO
* @see TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE
* @see TrackInfo#MEDIA_TRACK_TYPE_METADATA
* @see PlayerCallback#onTrackDeselected(SessionPlayer, TrackInfo)
*/
@NonNull
public ListenableFuture<PlayerResult> deselectTrack(@NonNull TrackInfo trackInfo) {
throw new UnsupportedOperationException("deselectTrack is not implemented");
}
/**
* Gets currently selected track's {@link TrackInfo} for the given track type.
* <p>
* The returned value can be outdated after
* {@link PlayerCallback#onTracksChanged(SessionPlayer, List)},
* {@link PlayerCallback#onTrackSelected(SessionPlayer, TrackInfo)},
* or {@link PlayerCallback#onTrackDeselected(SessionPlayer, TrackInfo)} is called.
*
* @param trackType type of selected track
* @return selected track info
* @see TrackInfo#MEDIA_TRACK_TYPE_VIDEO
* @see TrackInfo#MEDIA_TRACK_TYPE_AUDIO
* @see TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE
* @see TrackInfo#MEDIA_TRACK_TYPE_METADATA
*/
@Nullable
public TrackInfo getSelectedTrack(@TrackInfo.MediaTrackType int trackType) {
throw new UnsupportedOperationException(
"getSelectedTrack is not implemented");
}
/**
* Removes all existing references to callbacks and executors.
*
* Note: Sub classes of {@link SessionPlayer} that override this API should call this super
* method.
*/
@CallSuper
@Override
public void close() {
synchronized (mLock) {
mCallbacks.clear();
}
}
/**
* Class for the player to return each audio/video/subtitle track's metadata.
*
* <p>Note: TrackInfo holds a MediaFormat instance, but only the following key-values will be
* supported when sending it over different processes:
*
* <ul>
* <li>{@link MediaFormat#KEY_LANGUAGE}
* <li>{@link MediaFormat#KEY_MIME}
* <li>{@link MediaFormat#KEY_IS_FORCED_SUBTITLE}
* <li>{@link MediaFormat#KEY_IS_AUTOSELECT}
* <li>{@link MediaFormat#KEY_IS_DEFAULT}
* </ul>
*
* @see #getTracks
* @deprecated androidx.media2 is deprecated. Please migrate to <a
* href="https://developer.android.com/guide/topics/media/media3">androidx.media3</a>.
*/
@Deprecated
@VersionedParcelize(isCustom = true)
public static class TrackInfo extends CustomVersionedParcelable {
public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0;
public static final int MEDIA_TRACK_TYPE_VIDEO = 1;
public static final int MEDIA_TRACK_TYPE_AUDIO = 2;
public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4;
public static final int MEDIA_TRACK_TYPE_METADATA = 5;
private static final String KEY_IS_FORMAT_NULL =
"androidx.media2.common.SessionPlayer.TrackInfo.KEY_IS_FORMAT_NULL";
private static final String KEY_IS_SELECTABLE =
"androidx.media2.common.SessionPlayer.TrackInfo.KEY_IS_SELECTABLE";
/**
*/
@IntDef(flag = false, /*prefix = "MEDIA_TRACK_TYPE",*/ value = {
MEDIA_TRACK_TYPE_UNKNOWN,
MEDIA_TRACK_TYPE_VIDEO,
MEDIA_TRACK_TYPE_AUDIO,
MEDIA_TRACK_TYPE_SUBTITLE,
MEDIA_TRACK_TYPE_METADATA,
})
@Retention(RetentionPolicy.SOURCE)
@RestrictTo(LIBRARY_GROUP)
public @interface MediaTrackType {}
@ParcelField(1)
int mId;
// Removed @ParcelField(2)
@ParcelField(3)
int mTrackType;
// Parceled via mParcelableExtras.
@NonParcelField
@Nullable
MediaFormat mFormat;
// Parceled via mParcelableExtras.
@NonParcelField
boolean mIsSelectable;
// For extra information containing MediaFormat and isSelectable data. Should only be used
// by onPreParceling() and onPostParceling().
@ParcelField(4)
Bundle mParcelableExtras;
@NonParcelField
private final Object mLock = new Object();
// WARNING: Adding a new ParcelField may break old library users (b/152830728)
/**
* Used for VersionedParcelable
*/
TrackInfo() {
// no-op
}
/**
* Constructor to create a TrackInfo instance.
*
* Note: The default value for {@link #isSelectable()} is false.
*
* @param id id of track unique across {@link MediaItem}s
* @param type type of track. Can be video, audio or subtitle
* @param format format of track
*/
public TrackInfo(int id, int type, @Nullable MediaFormat format) {
this(id, type, format, /* isSelectable= */ false);
}
/**
* Constructor to create a TrackInfo instance.
*
* @param id id of track unique across {@link MediaItem}s
* @param type type of track. Can be video, audio or subtitle
* @param format format of track
* @param isSelectable whether track can be selected via
* {@link SessionPlayer#selectTrack(TrackInfo)}.
*/
public TrackInfo(int id, int type, @Nullable MediaFormat format, boolean isSelectable) {
mId = id;
mTrackType = type;
mFormat = format;
mIsSelectable = isSelectable;
}
/**
* Gets the track type.
* @return MediaTrackType which indicates if the track is video, audio or subtitle
*/
@MediaTrackType
public int getTrackType() {
return mTrackType;
}
/**
* Gets the language code of the track.
* @return {@link Locale} which includes the language information
*/
@NonNull
public Locale getLanguage() {
String language = mFormat != null ? mFormat.getString(MediaFormat.KEY_LANGUAGE) : null;
if (language == null) {
language = "und";
}
return new Locale(language);
}
/**
* Gets the {@link MediaFormat} of the track. If the format is
* unknown or could not be determined, null is returned.
*/
@Nullable
public MediaFormat getFormat() {
return mFormat;
}
/**
* Gets the id of the track.
* The id is used by {@link #selectTrack(TrackInfo)} and {@link #deselectTrack(TrackInfo)}
* to identify the track to be (de)selected.
* So, it's highly recommended to ensure that the id of each track is unique across
* {@link MediaItem}s to avoid potential mis-selection when a stale {@link TrackInfo} is
* used.
*
* @return id of the track
*/
public int getId() {
return mId;
}
/**
* Whether the current track can be selected via {@link #selectTrack(TrackInfo)} or not.
*
* @return true if the current track can be selected; false if otherwise.
*/
public boolean isSelectable() {
return mIsSelectable;
}
@Override
@NonNull
public String toString() {
StringBuilder out = new StringBuilder(128);
out.append(getClass().getName());
out.append('#').append(mId);
out.append('{');
switch (mTrackType) {
case MEDIA_TRACK_TYPE_VIDEO:
out.append("VIDEO");
break;
case MEDIA_TRACK_TYPE_AUDIO:
out.append("AUDIO");
break;
case MEDIA_TRACK_TYPE_SUBTITLE:
out.append("SUBTITLE");
break;
case MEDIA_TRACK_TYPE_METADATA:
out.append("METADATA");
break;
default:
out.append("UNKNOWN");
break;
}
out.append(", ").append(mFormat);
out.append(", isSelectable=").append(mIsSelectable);
out.append("}");
return out.toString();
}
@Override
public int hashCode() {
return mId;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof TrackInfo)) {
return false;
}
TrackInfo other = (TrackInfo) obj;
return mId == other.mId;
}
/**
* @param isStream
*/
@RestrictTo(LIBRARY)
@Override
public void onPreParceling(boolean isStream) {
synchronized (mLock) {
mParcelableExtras = new Bundle();
mParcelableExtras.putBoolean(KEY_IS_FORMAT_NULL, mFormat == null);
if (mFormat != null) {
putStringValueToBundle(MediaFormat.KEY_LANGUAGE, mFormat, mParcelableExtras);
putStringValueToBundle(MediaFormat.KEY_MIME, mFormat, mParcelableExtras);
putIntValueToBundle(MediaFormat.KEY_IS_FORCED_SUBTITLE, mFormat,
mParcelableExtras);
putIntValueToBundle(MediaFormat.KEY_IS_AUTOSELECT, mFormat, mParcelableExtras);
putIntValueToBundle(MediaFormat.KEY_IS_DEFAULT, mFormat, mParcelableExtras);
}
mParcelableExtras.putBoolean(KEY_IS_SELECTABLE, mIsSelectable);
}
}
/**
*/
@RestrictTo(LIBRARY)
@Override
public void onPostParceling() {
if (mParcelableExtras != null && !mParcelableExtras.getBoolean(KEY_IS_FORMAT_NULL)) {
mFormat = new MediaFormat();
setStringValueToMediaFormat(MediaFormat.KEY_LANGUAGE, mFormat, mParcelableExtras);
setStringValueToMediaFormat(MediaFormat.KEY_MIME, mFormat, mParcelableExtras);
setIntValueToMediaFormat(MediaFormat.KEY_IS_FORCED_SUBTITLE, mFormat,
mParcelableExtras);
setIntValueToMediaFormat(MediaFormat.KEY_IS_AUTOSELECT, mFormat, mParcelableExtras);
setIntValueToMediaFormat(MediaFormat.KEY_IS_DEFAULT, mFormat, mParcelableExtras);
}
if (mParcelableExtras == null || !mParcelableExtras.containsKey(KEY_IS_SELECTABLE)) {
mIsSelectable = mTrackType != MEDIA_TRACK_TYPE_VIDEO;
} else {
mIsSelectable = mParcelableExtras.getBoolean(KEY_IS_SELECTABLE);
}
}
private static void putIntValueToBundle(
String intValueKey, MediaFormat mediaFormat, Bundle bundle) {
if (mediaFormat.containsKey(intValueKey)) {
bundle.putInt(intValueKey, mediaFormat.getInteger(intValueKey));
}
}
private static void putStringValueToBundle(
String stringValueKey, MediaFormat mediaFormat, Bundle bundle) {
if (mediaFormat.containsKey(stringValueKey)) {
bundle.putString(stringValueKey, mediaFormat.getString(stringValueKey));
}
}
private static void setIntValueToMediaFormat(
String intValueKey, MediaFormat mediaFormat, Bundle bundle) {
if (bundle.containsKey(intValueKey)) {
mediaFormat.setInteger(intValueKey, bundle.getInt(intValueKey));
}
}
private static void setStringValueToMediaFormat(
String stringValueKey, MediaFormat mediaFormat, Bundle bundle) {
if (bundle.containsKey(stringValueKey)) {
mediaFormat.setString(stringValueKey, bundle.getString(stringValueKey));
}
}
}
/**
* A callback class to receive notifications for events on the session player. See {@link
* #registerPlayerCallback(Executor, PlayerCallback)} to register this callback.
*
* @deprecated androidx.media2 is deprecated. Please migrate to <a
* href="https://developer.android.com/guide/topics/media/media3">androidx.media3</a>.
*/
@Deprecated
public abstract static class PlayerCallback {
/**
* Called when the state of the player has changed.
*
* @param player the player whose state has changed
* @param playerState the new state of the player
* @see #getPlayerState()
*/
public void onPlayerStateChanged(@NonNull SessionPlayer player,
@PlayerState int playerState) {
}
/**
* Called when a buffering events for a media item happened.
*
* @param player the player that is buffering
* @param item the media item for which buffering is happening
* @param buffState the new buffering state
* @see #getBufferingState()
*/
public void onBufferingStateChanged(@NonNull SessionPlayer player,
@Nullable MediaItem item, @BuffState int buffState) {
}
/**
* Called when the playback speed has changed.
*
* @param player the player that has changed the playback speed
* @param playbackSpeed the new playback speed
* @see #getPlaybackSpeed()
*/
public void onPlaybackSpeedChanged(@NonNull SessionPlayer player,
float playbackSpeed) {
}
/**
* Called when {@link #seekTo(long)} is completed.
*
* @param player the player that has completed seeking
* @param position the previous seeking request
* @see #getCurrentPosition()
*/
public void onSeekCompleted(@NonNull SessionPlayer player, long position) {
}
/**
* Called when a playlist is changed. It's also called after {@link #setPlaylist} or
* {@link #setMediaItem}.
*
* @param player the player that has changed the playlist and playlist metadata
* @param list new playlist
* @param metadata new metadata
* @see #getPlaylist()
* @see #getPlaylistMetadata()
*/
public void onPlaylistChanged(@NonNull SessionPlayer player,
@Nullable List<MediaItem> list, @Nullable MediaMetadata metadata) {
}
/**
* Called when a playlist metadata is changed.
*
* @param player the player that has changed the playlist metadata
* @param metadata new metadata
* @see #getPlaylistMetadata()
*/
public void onPlaylistMetadataChanged(@NonNull SessionPlayer player,
@Nullable MediaMetadata metadata) {
}
/**
* Called when the shuffle mode is changed.
* <p>
* {@link SessionPlayer#getPreviousMediaItemIndex()} and
* {@link SessionPlayer#getNextMediaItemIndex()} values can be outdated when this callback
* is called if the current media item is the first or last item in the playlist.
*
* @param player playlist agent for this event
* @param shuffleMode shuffle mode
* @see #SHUFFLE_MODE_NONE
* @see #SHUFFLE_MODE_ALL
* @see #SHUFFLE_MODE_GROUP
* @see #getShuffleMode()
*/
public void onShuffleModeChanged(@NonNull SessionPlayer player,
@ShuffleMode int shuffleMode) {
}
/**
* Called when the repeat mode is changed.
* <p>
* {@link SessionPlayer#getPreviousMediaItemIndex()} and
* {@link SessionPlayer#getNextMediaItemIndex()} values can be outdated when this callback
* is called if the current media item is the first or last item in the playlist.
*
* @param player player for this event
* @param repeatMode repeat mode
* @see #REPEAT_MODE_NONE
* @see #REPEAT_MODE_ONE
* @see #REPEAT_MODE_ALL
* @see #REPEAT_MODE_GROUP
* @see #getRepeatMode()
*/
public void onRepeatModeChanged(@NonNull SessionPlayer player,
@RepeatMode int repeatMode) {
}
/**
* Called when the player's current media item has changed. Generally called after a new
* media item is set through {@link #setPlaylist} or {@link #setMediaItem}, or after
* skipping to a different item in a given playlist.
*
* @param player the player whose media item changed
* @param item the new current media item. This can be {@code null} when the state of
* the player becomes {@link #PLAYER_STATE_IDLE}.
* @see #getCurrentMediaItem()
*/
public void onCurrentMediaItemChanged(@NonNull SessionPlayer player,
@Nullable MediaItem item) {
}
/**
* Called when the player finished playing. Playback state would be also set
* {@link #PLAYER_STATE_PAUSED} with it.
* <p>
* This will be called only when the repeat mode is set to {@link #REPEAT_MODE_NONE}.
*
* @param player the player whose playback is completed
* @see #REPEAT_MODE_NONE
*/
public void onPlaybackCompleted(@NonNull SessionPlayer player) {
}
/**
* Called when the player's current audio attributes are changed.
*
* @param player the player whose audio attributes are changed
* @param attributes the new current audio attributes
* @see #getAudioAttributes()
*/
public void onAudioAttributesChanged(@NonNull SessionPlayer player,
@Nullable AudioAttributesCompat attributes) {
}
/**
* Called to indicate the video size
* <p>
* The video size (width and height) could be 0 if there was no video,
* no display surface was set, or the value was not determined yet.
* <p>
* This callback is generally called when player updates video size, but will also be
* called when {@link PlayerCallback#onCurrentMediaItemChanged(SessionPlayer, MediaItem)}
* is called.
*
* @param player the player associated with this callback
* @param size the size of the video
* @see #getVideoSize()
*/
public void onVideoSizeChanged(@NonNull SessionPlayer player, @NonNull VideoSize size) {
}
/**
* Called when the player's subtitle track has new subtitle data available.
* @param player the player that reports the new subtitle data
* @param item the MediaItem of this media item
* @param track the track that has the subtitle data
* @param data the subtitle data
*/
public void onSubtitleData(@NonNull SessionPlayer player, @NonNull MediaItem item,
@NonNull TrackInfo track, @NonNull SubtitleData data) {
}
/**
* Called when the tracks of the current media item is changed such as
* 1) when tracks of a media item become available,
* 2) when new tracks are found during playback, or
* 3) when the current media item is changed.
* <p>
* When it's called, you should invalidate previous track information and use the new
* tracks to call {@link #selectTrack(TrackInfo)} or
* {@link #deselectTrack(TrackInfo)}.
*
* @param player the player associated with this callback
* @param tracks the list of tracks. It can be empty
* @see #getTracks()
*/
public void onTracksChanged(@NonNull SessionPlayer player,
@NonNull List<TrackInfo> tracks) {
}
/**
* Called when a track is selected.
*
* @param player the player associated with this callback
* @param trackInfo the selected track
* @see #selectTrack(TrackInfo)
*/
public void onTrackSelected(@NonNull SessionPlayer player, @NonNull TrackInfo trackInfo) {
}
/**
* Called when a track is deselected.
* <p>
* This callback will generally be called only after calling
* {@link #deselectTrack(TrackInfo)}.
*
* @param player the player associated with this callback
* @param trackInfo the deselected track
* @see #deselectTrack(TrackInfo)
*/
public void onTrackDeselected(@NonNull SessionPlayer player, @NonNull TrackInfo trackInfo) {
}
}
/**
* Result class of the asynchronous APIs.
*
* <p>Subclass may extend this class for providing more result and/or custom result code. For
* the custom result code, follow the convention below to avoid potential code duplication.
*
* <p>
*
* <ul>
* <li>Predefined error code: Negative integers greater than -100. (i.e. -100 < code < 0)
* <li>Custom error code: Negative integers equal to or less than -1000. (i.e. code < -1000)
* <li>Predefined info code: Positive integers less than 100. (i.e. 0 < code < 100)
* <li>Custom Info code: Positive integers equal to or greater than 1000. (i.e. code > +1000)
* </ul>
*
* @deprecated androidx.media2 is deprecated. Please migrate to <a
* href="https://developer.android.com/guide/topics/media/media3">androidx.media3</a>.
*/
@Deprecated
@SuppressWarnings("HiddenSuperclass")
public static class PlayerResult implements BaseResult {
/**
*/
@IntDef(flag = false, /*prefix = "RESULT",*/ value = {
RESULT_SUCCESS,
RESULT_ERROR_UNKNOWN,
RESULT_ERROR_INVALID_STATE,
RESULT_ERROR_BAD_VALUE,
RESULT_ERROR_PERMISSION_DENIED,
RESULT_ERROR_IO,
RESULT_ERROR_NOT_SUPPORTED,
RESULT_INFO_SKIPPED})
@Retention(RetentionPolicy.SOURCE)
@RestrictTo(LIBRARY)
public @interface ResultCode {}
private final int mResultCode;
private final long mCompletionTime;
private final MediaItem mItem;
/**
* Constructor that uses the current system clock as the completion time.
*
* @param resultCode result code. Recommends to use the standard code defined here.
* @param item media item when the command completed
*/
// Note: resultCode is intentionally not annotated for subclass to return extra error codes.
public PlayerResult(int resultCode, @Nullable MediaItem item) {
this(resultCode, item, SystemClock.elapsedRealtime());
}
// Note: resultCode is intentionally not annotated for subclass to return extra error codes.
private PlayerResult(int resultCode, @Nullable MediaItem item, long completionTime) {
mResultCode = resultCode;
mItem = item;
mCompletionTime = completionTime;
}
/**
*/
@RestrictTo(LIBRARY_GROUP)
@NonNull
public static ListenableFuture<PlayerResult> createFuture(int resultCode) {
ResolvableFuture<PlayerResult> result = ResolvableFuture.create();
result.set(new PlayerResult(resultCode, null));
return result;
}
/**
* Gets the result code.
* <p>
* Subclass of the {@link SessionPlayer} may have defined customized extra code other than
* codes defined here. Check the documentation of the class that you're interested in.
*
* @return result code
* @see #RESULT_ERROR_UNKNOWN
* @see #RESULT_ERROR_INVALID_STATE
* @see #RESULT_ERROR_BAD_VALUE
* @see #RESULT_ERROR_PERMISSION_DENIED
* @see #RESULT_ERROR_IO
* @see #RESULT_ERROR_NOT_SUPPORTED
* @see #RESULT_INFO_SKIPPED
*/
@Override
@ResultCode
public int getResultCode() {
return mResultCode;
}
/**
* Gets the completion time of the command. Being more specific, it's the same as
* {@link android.os.SystemClock#elapsedRealtime()} when the command completed.
*
* @return completion time of the command
*/
@Override
public long getCompletionTime() {
return mCompletionTime;
}
/**
* Gets the {@link MediaItem} for which the command was executed. In other words, this is
* the item sent as an argument of the command if any, otherwise the current media item when
* the command completed.
*
* @return media item when the command completed. Can be {@code null} for an error, or
* the current media item was {@code null}
*/
@Override
@Nullable
public MediaItem getMediaItem() {
return mItem;
}
}
}