
We only had some documentation for a custom shuffle mode, but none for generic repeat or shuffle modes. #minor-release Issue: google/ExoPlayer#9611 PiperOrigin-RevId: 409089623
208 lines
7.6 KiB
Markdown
208 lines
7.6 KiB
Markdown
---
|
||
title: Playlists
|
||
---
|
||
|
||
The playlist API is defined by the `Player` interface, which is implemented by
|
||
all `ExoPlayer` implementations. It enables sequential playback of multiple
|
||
media items. The following example shows how to start playback of a playlist
|
||
containing two videos:
|
||
|
||
~~~
|
||
// Build the media items.
|
||
MediaItem firstItem = MediaItem.fromUri(firstVideoUri);
|
||
MediaItem secondItem = MediaItem.fromUri(secondVideoUri);
|
||
// Add the media items to be played.
|
||
player.addMediaItem(firstItem);
|
||
player.addMediaItem(secondItem);
|
||
// Prepare the player.
|
||
player.prepare();
|
||
// Start the playback.
|
||
player.play();
|
||
~~~
|
||
{: .language-java}
|
||
|
||
Transitions between items in a playlist are seamless. There's no requirement
|
||
that they're of the same format (e.g., it’s fine for a playlist to contain both
|
||
H264 and VP9 videos). They may even be of different types (e.g., it’s fine for a
|
||
playlist to contain both videos and audio only streams). It's allowed to use the
|
||
same `MediaItem` multiple times within a playlist.
|
||
|
||
## Modifying the playlist
|
||
|
||
It's possible to dynamically modify a playlist by adding, moving and removing
|
||
media items. This can be done both before and during playback by calling the
|
||
corresponding playlist API methods:
|
||
|
||
~~~
|
||
// Adds a media item at position 1 in the playlist.
|
||
player.addMediaItem(/* index= */ 1, MediaItem.fromUri(thirdUri));
|
||
// Moves the third media item from position 2 to the start of the playlist.
|
||
player.moveMediaItem(/* currentIndex= */ 2, /* newIndex= */ 0);
|
||
// Removes the first item from the playlist.
|
||
player.removeMediaItem(/* index= */ 0);
|
||
~~~
|
||
{: .language-java}
|
||
|
||
Replacing and clearing the entire playlist are also supported:
|
||
|
||
~~~
|
||
// Replaces the playlist with a new one.
|
||
List<MediaItem> newItems = ImmutableList.of(
|
||
MediaItem.fromUri(fourthUri),
|
||
MediaItem.fromUri(fifthUri));
|
||
player.setMediaItems(newItems, /* resetPosition= */ true);
|
||
// Clears the playlist. If prepared, the player transitions to the ended state.
|
||
player.clearMediaItems();
|
||
~~~
|
||
{: .language-java}
|
||
|
||
The player automatically handles modifications during playback in the correct
|
||
way. For example if the currently playing media item is moved, playback is not
|
||
interrupted and its new successor will be played upon completion. If the
|
||
currently playing `MediaItem` is removed, the player will automatically move to
|
||
playing the first remaining successor, or transition to the ended state if no
|
||
such successor exists.
|
||
|
||
## Querying the playlist
|
||
|
||
The playlist can be queried using `Player.getMediaItemCount` and
|
||
`Player.getMediaItemAt`. The currently playing media item can be queried
|
||
by calling `Player.getCurrentMediaItem`. There are also other convenience
|
||
methods like `Player.hasNextMediaItem` or `Player.getNextMediaItemIndex` to
|
||
simplify navigation in the playlist.
|
||
|
||
## Repeat modes
|
||
|
||
The player supports 3 repeat modes that can be set at any time with
|
||
`Player.setRepeatMode`:
|
||
|
||
* `Player.REPEAT_MODE_OFF`: The playlist isn't repeated and the player will
|
||
transition to `Player.STATE_ENDED` once the last item in the playlist has
|
||
been played.
|
||
* `Player.REPEAT_MODE_ONE`: The current item is repeated in an endless loop.
|
||
Methods like `Player.seekToNextMediaItem` will ignore this and seek to the
|
||
next item in the list, which will then be repeated in an endless loop.
|
||
* `Player.REPEAT_MODE_ALL`: The entire playlist is repeated in an endless loop.
|
||
|
||
## Shuffle mode
|
||
|
||
Shuffle mode can be enabled or disabled at any time with
|
||
`Player.setShuffleModeEnabled`. When in shuffle mode, the player will play the
|
||
playlist in a precomputed, randomized order. All items will be played once and
|
||
the shuffle mode can also be combined with `Player.REPEAT_MODE_ALL` to repeat
|
||
the same randomized order in an endless loop. When shuffle mode is turned off,
|
||
playback continues from the current item at its original position in the
|
||
playlist.
|
||
|
||
Note that the indices as returned by methods like
|
||
`Player.getCurrentMediaItemIndex` always refer to the original, unshuffled
|
||
order. Similarly, `Player.seekToNextMediaItem` will not play the item at
|
||
`player.getCurrentMediaItemIndex() + 1`, but the next item according to the
|
||
shuffle order. Inserting new items in the playlist or removing items will keep
|
||
the existing shuffled order unchanged as far as possible.
|
||
|
||
### Setting a custom shuffle order
|
||
|
||
By default the player supports shuffling by using the `DefaultShuffleOrder`.
|
||
This can be customized by providing a custom shuffle order implementation, or by
|
||
setting a custom order in the `DefaultShuffleOrder` constructor:
|
||
|
||
~~~
|
||
// Set a custom shuffle order for the 5 items currently in the playlist:
|
||
exoPlayer.setShuffleOrder(
|
||
new DefaultShuffleOrder(new int[] {3, 1, 0, 4, 2}, randomSeed));
|
||
// Enable shuffle mode.
|
||
exoPlayer.setShuffleModeEnabled(/* shuffleModeEnabled= */ true);
|
||
~~~
|
||
{: .language-java}
|
||
|
||
## Identifying playlist items
|
||
|
||
To identify playlist items, `MediaItem.mediaId` can be set when building the
|
||
item:
|
||
|
||
~~~
|
||
// Build a media item with a media ID.
|
||
MediaItem mediaItem =
|
||
new MediaItem.Builder().setUri(uri).setMediaId(mediaId).build();
|
||
~~~
|
||
{: .language-java}
|
||
|
||
If an app does not explicitly define a media ID for a media item, the string
|
||
representation of the URI is used.
|
||
|
||
## Associating app data with playlist items
|
||
|
||
In addition to an ID, each media item can also be configured with a custom tag,
|
||
which can be any app provided object. One use of custom tags is to attach
|
||
metadata to each media item:
|
||
|
||
~~~
|
||
// Build a media item with a custom tag.
|
||
MediaItem mediaItem =
|
||
new MediaItem.Builder().setUri(uri).setTag(metadata).build();
|
||
~~~
|
||
{: .language-java}
|
||
|
||
|
||
## Detecting when playback transitions to another media item
|
||
|
||
When playback transitions to another media item, or starts repeating the same
|
||
media item, `Listener.onMediaItemTransition(MediaItem,
|
||
@MediaItemTransitionReason)` is called. This callback receives the new media
|
||
item, along with a `@MediaItemTransitionReason` indicating why the transition
|
||
occurred. A common use case for `onMediaItemTransition` is to update the
|
||
application's UI for the new media item:
|
||
|
||
~~~
|
||
@Override
|
||
public void onMediaItemTransition(
|
||
@Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {
|
||
updateUiForPlayingMediaItem(mediaItem);
|
||
}
|
||
~~~
|
||
{: .language-java}
|
||
|
||
If the metadata required to update the UI is attached to each media item using
|
||
custom tags, then an implementation might look like:
|
||
|
||
~~~
|
||
@Override
|
||
public void onMediaItemTransition(
|
||
@Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {
|
||
@Nullable CustomMetadata metadata = null;
|
||
if (mediaItem != null && mediaItem.localConfiguration != null) {
|
||
metadata = (CustomMetadata) mediaItem.localConfiguration.tag;
|
||
}
|
||
updateUiForPlayingMediaItem(metadata);
|
||
}
|
||
~~~
|
||
{: .language-java}
|
||
|
||
## Detecting when the playlist changes
|
||
|
||
When a media item is added, removed or moved,
|
||
`Listener.onTimelineChanged(Timeline, @TimelineChangeReason)` is called
|
||
immediately with `TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED`. This callback is
|
||
called even when the player has not yet been prepared.
|
||
|
||
~~~
|
||
@Override
|
||
public void onTimelineChanged(
|
||
Timeline timeline, @TimelineChangeReason int reason) {
|
||
if (reason == TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED) {
|
||
// Update the UI according to the modified playlist (add, move or remove).
|
||
updateUiForPlaylist(timeline);
|
||
}
|
||
}
|
||
~~~
|
||
{: .language-java}
|
||
|
||
When information such as the duration of a media item in the playlist becomes
|
||
available, the `Timeline` will be updated and `onTimelineChanged` will be called
|
||
with `TIMELINE_CHANGE_REASON_SOURCE_UPDATE`. Other reasons that can cause a
|
||
timeline update include:
|
||
|
||
* A manifest becoming available after preparing an adaptive media item.
|
||
* A manifest being updated periodically during playback of a live stream.
|