재생목록

재생목록 API는 모든 ExoPlayer 구현에서 구현되는 Player 인터페이스로 정의됩니다. 재생목록을 사용하면 여러 미디어 항목을 순차적으로 재생할 수 있습니다. 다음 예에서는 두 개의 동영상이 포함된 재생목록의 재생을 시작하는 방법을 보여줍니다.

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()

자바

// 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))

자바

// 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()

자바

// 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.hasNextMediaItem 또는 Player.getNextMediaItemIndex와 같은 다른 편의 메서드도 있습니다.

반복 모드

플레이어는 언제든지 Player.setRepeatMode로 설정할 수 있는 3가지 반복 모드를 지원합니다.

  • 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.seekToNextMediaItemplayer.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

자바

// 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()

자바

// 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()

자바

// 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)
}

자바

@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)
}

자바

@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);
}

재생목록이 변경되는 시점 감지

미디어 항목이 추가, 삭제 또는 이동되면 Listener.onTimelineChanged(Timeline, @TimelineChangeReason)TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED와 함께 즉시 호출됩니다. 이 콜백은 플레이어가 아직 준비되지 않은 경우에도 호출됩니다.

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)
  }
}

자바

@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가 호출됩니다. 타임라인 업데이트를 유발할 수 있는 기타 이유는 다음과 같습니다.

  • 적응형 미디어 항목을 준비한 후 매니페스트를 사용할 수 있게 됩니다.
  • 라이브 스트림 재생 중에 주기적으로 업데이트되는 매니페스트