播放列表 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
的一个常见用例是更新应用的新媒体内容的界面:
Kotlin
override fun onMediaItemTransition( mediaItem: MediaItem?, @MediaItemTransitionReason reason: Int, ) { updateUiForPlayingMediaItem(mediaItem) }
Java
@Override public void onMediaItemTransition( @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) { updateUiForPlayingMediaItem(mediaItem); }
如果使用自定义标记将更新界面所需的元数据附加到每项媒体内容,则实现可能如下所示:
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
。可能导致时间表更新的其他原因包括:
- 准备自适应媒体内容后,清单会变为可用状态。
- 在直播播放期间定期更新的清单。