Интерфейс проигрывателя

Проигрыватель – это компонент приложения, который позволяет воспроизводить медиаконтент. Интерфейс Media3 Player задает структуру для функций, которые обычно выполняет проигрыватель. К ним относятся:

  • управлять воспроизведением, например запускать, приостанавливать и перематывать контент;
  • запрашивать свойства воспроизводимого контента, например позицию воспроизведения;
  • управление плейлистом или очередью медиаконтента;
  • настраивать параметры воспроизведения, например перемешивание, повтор, скорость и громкость;
  • Отрисовка видео на экране

В Media3 также реализован интерфейс Player, который называется ExoPlayer.

Единый интерфейс между компонентами

Интерфейс Player реализуют несколько компонентов Media3, например:

Компонент Описание и примечания о поведении
ExoPlayer API медиапроигрывателя и реализация интерфейса Player по умолчанию.
MediaController Взаимодействует с MediaSession для отправки команд воспроизведения. Если ваши элементы Player и MediaSession находятся в компоненте Service, отдельном от Activity или Fragment, где размещен интерфейс проигрывателя, вы можете назначить элемент MediaController проигрывателем для компонента интерфейса, например PlayerView или Player Composable. Вызовы методов воспроизведения и плейлистов отправляются на устройство "Player" через MediaSession.
MediaBrowser Помимо функций, предлагаемых MediaController, взаимодействует с MediaLibrarySession для просмотра доступного медиаконтента.
SimpleBasePlayer Реализация Player, которая сокращает количество методов, которые нужно реализовать, до минимума. Полезно, если вы используете собственный проигрыватель, который хотите подключить к MediaSession.
ForwardingSimpleBasePlayer Подкласс SimpleBasePlayer, предназначенный для пересылки операций воспроизведения в другой объект Player, но при этом позволяющий настраивать поведение так же, как и SimpleBasePlayer. Используйте этот класс, чтобы запретить или изменить определенные операции воспроизведения.
RemoteCastPlayer Реализация Player для управления воспроизведением в удаленном приложении-получателе Cast.
CastPlayer Реализация Player для управления воспроизведением контента Cast как на локальном, так и на удаленном устройстве.

Хотя MediaSession не реализует интерфейс Player, при его создании требуется Player. Его цель – предоставить доступ к Player из других процессов или потоков.

Архитектура воспроизведения Media3

Если у вас есть доступ к Player, вы можете вызывать его методы напрямую, чтобы отправлять команды воспроизведения. Вы можете транслировать воспроизведение и предоставлять внешним источникам возможность управлять им, реализовав MediaSession. В этих внешних источниках реализован MediaController, который позволяет подключаться к сеансу мультимедиа и отправлять запросы на воспроизведение.

Если вы хотите, чтобы медиаконтент воспроизводился в фоновом режиме, вам нужно разместить сеанс и проигрыватель в MediaSessionService или MediaLibraryService, которые работают как активная служба. В этом случае вы сможете отделить проигрыватель от Activity в приложении, содержащего интерфейс для управления воспроизведением. В этом случае вам может понадобиться медиаконтроллер.

Диаграмма, на которой показано, как компоненты воспроизведения Media3 вписываются в архитектуру мультимедийного приложения.
Рисунок 1. Интерфейс Player играет ключевую роль в архитектуре Media3.

Состояние проигрывателя

Состояние медиаплеера, реализующего интерфейс Player, состоит в основном из четырех категорий информации:

  1. Статус воспроизведения
  2. Плейлист мультимедийных объектов
    • Последовательность из MediaItem экземпляров для воспроизведения.
    • Восстановление с помощью getCurrentTimeline()
    • Объекты Player могут предоставлять методы для работы с плейлистами, например добавления или удаления MediaItem, а также удобные методы, такие как getCurrentMediaItem().
  3. Свойства воспроизведения и паузы, например:
    • playWhenReady: Указывает, хочет ли пользователь, чтобы мультимедиа воспроизводилось, когда это возможно, или оставалось на паузе.
    • Причина блокировки воспроизведения – если применимо, причина блокировки воспроизведения, даже если playWhenReady имеет значение true.
    • isPlaying: Указывает, воспроизводится ли видео в данный момент. Значение true будет только в том случае, если состояние воспроизведения – STATE_READY, значение playWhenReady – true и воспроизведение не подавлено.
  4. Позиция воспроизведения, в том числе:

Кроме того, интерфейс Player позволяет получить доступ к доступным дорожкам, метаданным медиаконтента, скорости воспроизведения, громкости и другим вспомогательным свойствам воспроизведения.

Отслеживание изменений

Используйте Player.Listener, чтобы отслеживать изменения в Player. Подробнее о том, как создать и использовать прослушиватель, можно узнать в документации ExoPlayer о событиях проигрывателя.

Обратите внимание, что интерфейс прослушивателя не включает обратные вызовы для отслеживания обычного воспроизведения. Чтобы постоянно отслеживать ход воспроизведения, например для настройки интерфейса индикатора выполнения, необходимо запрашивать текущую позицию через определенные интервалы.

Kotlin

fun checkPlaybackPosition(delayMs: Long): Boolean =
  handler.postDelayed(
    {
      val currentPosition = player.currentPosition
      // Update UI based on currentPosition
      checkPlaybackPosition(delayMs)
    },
    delayMs,
  )

Java

boolean checkPlaybackPosition(long delayMs) {
  return handler.postDelayed(
      () -> {
        long currentPosition = player.getCurrentPosition();
        // Update UI based on currentPosition
        checkPlaybackPosition(delayMs);
      },
      delayMs);
}

Управление воспроизведением

Интерфейс Player позволяет управлять воспроизведением разными способами:

Реализации с собственным кодом Player

Чтобы создать собственный проигрыватель, можно расширить класс SimpleBasePlayer, входящий в состав Media3. Этот класс предоставляет базовую реализацию интерфейса Player, чтобы свести к минимуму количество методов, которые вам нужно реализовать.

Начните с переопределения метода getState(). Этот метод должен заполнять текущее состояние проигрывателя при вызове, включая:

  • Набор доступных команд
  • Свойства воспроизведения, например нужно ли начинать воспроизведение, когда статус воспроизведения STATE_READY, индекс текущего медиаконтента и позиция воспроизведения в текущем контенте.

Kotlin

class CustomPlayer(looper: Looper) : SimpleBasePlayer(looper) {
  override fun getState(): State {
    return State.Builder()
      .setAvailableCommands(Commands.EMPTY) // Set which playback commands the player can handle
      // Configure additional playback properties
      .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
      .setCurrentMediaItemIndex(0)
      .setContentPositionMs(0)
      .build()
  }
}

Java

private static final class CustomPlayer extends SimpleBasePlayer {
  public CustomPlayer(Looper looper) {
    super(looper);
  }

  @Override
  protected State getState() {
    return new State.Builder()
        .setAvailableCommands(Commands.EMPTY) // Set which playback commands the player can handle
        // Configure additional playback properties
        .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
        .setCurrentMediaItemIndex(0)
        .setContentPositionMs(0)
        .build();
  }
}

SimpleBasePlayer гарантирует, что State будет создан с допустимой комбинацией значений статуса. Он также будет обрабатывать слушателей и уведомлять их об изменениях состояния. Если вам нужно вручную запустить обновление состояния, вызовите функцию invalidateState().

Помимо метода getState(), вам нужно реализовать только те методы, которые используются для команд, объявленных доступными в вашем проигрывателе. Найдите метод обработчика, который можно переопределить и который соответствует нужной вам функции. Например, переопределите метод handleSeek(), чтобы поддерживать такие операции, как COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM и COMMAND_SEEK_TO_NEXT_MEDIA_ITEM.

Как изменить реализацию Player

Вместо того чтобы создавать полностью специальную директиву Player, вы можете использовать ForwardingSimpleBasePlayer, чтобы изменить статус и поведение существующей директивы Player. Подробнее о настройке канала рассказывается в руководстве.