播放清單

播放清單 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.getMediaItemCountPlayer.getMediaItemAt 查詢播放清單。呼叫 Player.getCurrentMediaItem 即可查詢目前播放的媒體項目。此外,還有 Player.hasNextMediaItemPlayer.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)。這個回呼會接收新的媒體項目,以及指出轉場原因的 @MediaItemTransitionReasononMediaItemTransition 的常見用途是更新應用程式的 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。其他可能導致時間軸更新的原因包括:

  • 準備好自動調整媒體項目後,即可使用資訊清單。
  • 在直播播放期間定期更新的資訊清單。