Плейлисты

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.

Режимы повтора

Проигрыватель поддерживает три режима повтора, которые можно задать в любое время с помощью кнопки 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();

Если приложение не задает идентификатор медиаконтента для медиаобъекта, используется строковое представление URI.

Как связать данные приложения с элементами плейлиста

Помимо идентификатора, для каждого мультимедийного объекта можно задать пользовательский тег, который может представлять собой любой объект, предоставленный приложением. С помощью специальных тегов можно добавлять метаданные к каждому медиаобъекту:

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

Как узнать, когда изменился плейлист

Когда медиаобъект добавляется, удаляется или перемещается, метод 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)
  }
}

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 обновляется и вызывается onTimelineChanged с TIMELINE_CHANGE_REASON_SOURCE_UPDATE. Другие причины, по которым может обновляться хронология:

  • Манифест становится доступен после подготовки адаптивного медиаконтента.
  • Манифест, который периодически обновляется во время воспроизведения трансляции.