播放列表 API 由 Player
接口定义,所有 ExoPlayer
实现都实现了该接口。播放列表支持按顺序播放多个媒体项。以下示例展示了如何开始播放包含两个视频的播放列表
Kotlin
// Build the media items. val firstItem = MediaItem.fromUri(firstVideoUri) val 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()
Java
// 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();
播放列表中项目之间的过渡是无缝的。它们不要求是相同的格式(例如,播放列表可以同时包含 H264 和 VP9 视频)。它们甚至可以是不同类型(即,播放列表可以包含视频、图像和纯音频流)。您可以在播放列表中多次使用同一个 MediaItem
。
修改播放列表
您可以通过添加、移动、移除或替换媒体项来动态修改播放列表。这可以在播放之前和播放期间通过调用相应的播放列表 API 方法来完成
Kotlin
// 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) // Replace the second item in the playlist. player.replaceMediaItem(/* index= */ 1, MediaItem.fromUri(newUri))
Java
// 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); // Replace the second item in the playlist. player.replaceMediaItem(/* index= */ 1, MediaItem.fromUri(newUri));
也支持替换和清空整个播放列表
Kotlin
// Replaces the playlist with a new one. val newItems: List<MediaItem> = listOf(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()
Java
// Replaces the playlist with a new one. ImmutableList<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();
播放器会自动以正确的方式处理播放期间的修改
- 如果当前播放的
MediaItem
被移动,播放不会中断,并且其新的后续项将在播放完成后播放。 - 如果当前播放的
MediaItem
被移除,播放器将自动播放第一个剩余的后续项,如果不存在这样的后续项,则会过渡到结束状态。 - 如果当前播放的
MediaItem
被替换,则在与播放相关的MediaItem
属性没有更改的情况下,播放不会中断。例如,在大多数情况下,更新MediaItem.MediaMetadata
字段不会影响播放。
查询播放列表
可以使用 Player.getMediaItemCount
和 Player.getMediaItemAt
查询播放列表。可以通过调用 Player.getCurrentMediaItem
查询当前播放的媒体项。还有其他便捷方法,例如 Player.hasNextMediaItem
或 Player.getNextMediaItemIndex
,以简化播放列表中的导航。
重复模式
播放器支持 3 种重复模式,可以随时使用 Player.setRepeatMode
进行设置
Player.REPEAT_MODE_OFF
:播放列表不会重复,一旦播放列表中的最后一个项目播放完毕,播放器将过渡到Player.STATE_ENDED
状态。Player.REPEAT_MODE_ONE
:当前项目无限循环播放。像Player.seekToNextMediaItem
这样的方法将忽略此设置,并跳转到列表中的下一个项目,然后该项目将无限循环播放。Player.REPEAT_MODE_ALL
:整个播放列表无限循环播放。
随机播放模式
随机播放模式可以随时通过 Player.setShuffleModeEnabled
启用或禁用。在随机播放模式下,播放器将以预先计算的随机顺序播放播放列表。所有项目都将播放一次,随机播放模式也可以与 Player.REPEAT_MODE_ALL
结合使用,以无限循环重复相同的随机顺序。当随机播放模式关闭时,播放将从当前项目在其播放列表中的原始位置继续。
请注意,像 Player.getCurrentMediaItemIndex
这样的方法返回的索引始终是指原始的、未随机播放的顺序。类似地,Player.seekToNextMediaItem
不会播放位于 player.getCurrentMediaItemIndex() + 1
的项目,而是根据随机播放顺序播放下一个项目。在播放列表中插入新项目或移除项目将尽可能保持现有随机播放顺序不变。
设置自定义随机播放顺序
默认情况下,播放器通过使用 DefaultShuffleOrder
支持随机播放。这可以通过提供自定义随机播放顺序实现,或在 DefaultShuffleOrder
构造函数中设置自定义顺序来定制。
Kotlin
// Set a custom shuffle order for the 5 items currently in the playlist: exoPlayer.setShuffleOrder(DefaultShuffleOrder(intArrayOf(3, 1, 0, 4, 2), randomSeed)) // Enable shuffle mode. exoPlayer.shuffleModeEnabled = true
Java
// 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);
识别播放列表项
要识别播放列表项,可以在构建项目时设置 MediaItem.mediaId
Kotlin
// Build a media item with a media ID. val mediaItem = MediaItem.Builder().setUri(uri).setMediaId(mediaId).build()
Java
// Build a media item with a media ID. MediaItem mediaItem = new MediaItem.Builder().setUri(uri).setMediaId(mediaId).build();
如果应用未明确为媒体项定义媒体 ID,则使用 URI 的字符串表示形式。
将应用数据与播放列表项关联
除了 ID 之外,每个媒体项还可以配置一个自定义标签,可以是任何应用提供的对象。自定义标签的一个用途是将元数据附加到每个媒体项
Kotlin
// Build a media item with a custom tag. val mediaItem = MediaItem.Builder().setUri(uri).setTag(metadata).build()
Java
// Build a media item with a custom tag. MediaItem mediaItem = new MediaItem.Builder().setUri(uri).setTag(metadata).build();
检测播放何时过渡到另一个媒体项
当播放过渡到另一个媒体项或开始重复播放同一媒体项时,将调用 Listener.onMediaItemTransition(MediaItem, @MediaItemTransitionReason)
。此回调会接收新的媒体项,以及指示过渡原因的 @MediaItemTransitionReason
。onMediaItemTransition
的常见用例是为新的媒体项更新应用的 UI
Kotlin
override fun onMediaItemTransition( mediaItem: MediaItem?, @MediaItemTransitionReason reason: Int, ) { updateUiForPlayingMediaItem(mediaItem) }
Java
@Override public void onMediaItemTransition( @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) { updateUiForPlayingMediaItem(mediaItem); }
如果更新 UI 所需的元数据已使用自定义标签附加到每个媒体项,则实现可能如下所示
Kotlin
override fun onMediaItemTransition( mediaItem: MediaItem?, @MediaItemTransitionReason reason: Int, ) { var metadata: CustomMetadata? = null mediaItem?.localConfiguration?.let { localConfiguration -> metadata = localConfiguration.tag as? CustomMetadata } updateUiForPlayingMediaItem(metadata) }
Java
@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); }
检测播放列表何时更改
当添加、移除或移动媒体项时,会立即使用 TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED
调用 Listener.onTimelineChanged(Timeline, @TimelineChangeReason)
。即使播放器尚未准备好,也会调用此回调。
Kotlin
override fun onTimelineChanged(timeline: Timeline, @TimelineChangeReason reason: Int) { if (reason == Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED) { // Update the UI according to the modified playlist (add, move or remove). updateUiForPlaylist(timeline) } }
Java
@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); } }
当播放列表中媒体项的持续时间等信息可用时,Timeline
将更新,并使用 TIMELINE_CHANGE_REASON_SOURCE_UPDATE
调用 onTimelineChanged
。可能导致时间轴更新的其他原因包括
- 准备自适应媒体项后清单变得可用。
- 直播流播放期间清单定期更新。