События игрока

Прослушивание событий воспроизведения

События, такие как изменения состояния и ошибки воспроизведения, передаются зарегистрированным экземплярам Player.Listener . Чтобы зарегистрировать прослушиватель для получения таких событий:

Котлин

// Add a listener to receive events from the player.
player.addListener(listener)

Ява

// Add a listener to receive events from the player.
player.addListener(listener);

Player.Listener имеет пустые методы по умолчанию, поэтому вам нужно реализовать только те методы, которые вас интересуют. Полное описание методов и случаев их вызова см. в Javadoc . Некоторые из наиболее важных методов описаны более подробно ниже.

У прослушивателей есть выбор между реализацией обратных вызовов отдельных событий или общим обратным вызовом onEvents , который вызывается после того, как одно или несколько событий происходят вместе. См. Individual callbacks vs onEvents , чтобы узнать, какие из них следует предпочесть для разных случаев использования.

Изменения состояния воспроизведения

Изменения в состоянии игрока можно получить, реализовав onPlaybackStateChanged(@State int state) в зарегистрированном Player.Listener . Плеер может находиться в одном из четырех состояний воспроизведения:

  • Player.STATE_IDLE : это исходное состояние, состояние, когда проигрыватель остановлен и когда воспроизведение не удалось. В этом состоянии у игрока будут только ограниченные ресурсы.
  • Player.STATE_BUFFERING : игрок не может сразу играть со своей текущей позиции. Чаще всего это происходит потому, что необходимо загрузить больше данных.
  • Player.STATE_READY : игрок может немедленно начать игру со своей текущей позиции.
  • Player.STATE_ENDED : проигрыватель завершил воспроизведение всех медиафайлов.

В дополнение к этим состояниям у игрока есть флаг playWhenReady , указывающий на намерение пользователя играть. Изменения этого флага можно получить, реализовав onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason) .

Игрок играет (то есть его позиция продвигается и медиафайлы предоставляются пользователю), когда выполняются все три из следующих условий:

  • Игрок находится в состоянии Player.STATE_READY .
  • playWhenReady имеет true
  • Воспроизведение не подавляется по причине, возвращаемой Player.getPlaybackSuppressionReason

Вместо того, чтобы проверять эти свойства по отдельности, можно вызвать Player.isPlaying . Изменения этого состояния можно получить, реализовав onIsPlayingChanged(boolean isPlaying) :

Котлин

player.addListener(
  object : Player.Listener {
    override fun onIsPlayingChanged(isPlaying: Boolean) {
      if (isPlaying) {
        // Active playback.
      } else {
        // Not playing because playback is paused, ended, suppressed, or the player
        // is buffering, stopped or failed. Check player.playWhenReady,
        // player.playbackState, player.playbackSuppressionReason and
        // player.playerError for details.
      }
    }
  }
)

Ява

player.addListener(
    new Player.Listener() {
      @Override
      public void onIsPlayingChanged(boolean isPlaying) {
        if (isPlaying) {
          // Active playback.
        } else {
          // Not playing because playback is paused, ended, suppressed, or the player
          // is buffering, stopped or failed. Check player.getPlayWhenReady,
          // player.getPlaybackState, player.getPlaybackSuppressionReason and
          // player.getPlaybackError for details.
        }
      }
    });

Ошибки воспроизведения

Ошибки, приводящие к сбою воспроизведения, можно получить, реализовав onPlayerError(PlaybackException error) в зарегистрированном Player.Listener . При возникновении сбоя этот метод будет вызываться непосредственно перед переходом состояния воспроизведения в Player.STATE_IDLE . Неудачное или остановленное воспроизведение можно повторить, вызвав ExoPlayer.prepare .

Обратите внимание, что некоторые реализации Player передают экземпляры подклассов PlaybackException для предоставления дополнительной информации об ошибке. Например, ExoPlayer передает ExoPlaybackException , который имеет type , rendererIndex и другие поля, специфичные для ExoPlayer.

В следующем примере показано, как определить, что воспроизведение не удалось из-за проблемы с сетью HTTP:

Котлин

player.addListener(
  object : Player.Listener {
    override fun onPlayerError(error: PlaybackException) {
      val cause = error.cause
      if (cause is HttpDataSourceException) {
        // An HTTP error occurred.
        val httpError = cause
        // It's possible to find out more about the error both by casting and by querying
        // the cause.
        if (httpError is InvalidResponseCodeException) {
          // Cast to InvalidResponseCodeException and retrieve the response code, message
          // and headers.
        } else {
          // Try calling httpError.getCause() to retrieve the underlying cause, although
          // note that it may be null.
        }
      }
    }
  }
)

Ява

player.addListener(
    new Player.Listener() {
      @Override
      public void onPlayerError(PlaybackException error) {
        @Nullable Throwable cause = error.getCause();
        if (cause instanceof HttpDataSourceException) {
          // An HTTP error occurred.
          HttpDataSourceException httpError = (HttpDataSourceException) cause;
          // It's possible to find out more about the error both by casting and by querying
          // the cause.
          if (httpError instanceof HttpDataSource.InvalidResponseCodeException) {
            // Cast to InvalidResponseCodeException and retrieve the response code, message
            // and headers.
          } else {
            // Try calling httpError.getCause() to retrieve the underlying cause, although
            // note that it may be null.
          }
        }
      }
    });

Переходы плейлистов

Всякий раз, когда проигрыватель переходит к новому элементу мультимедиа в списке воспроизведения onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) вызывается для зарегистрированных объектов Player.Listener . Причина указывает, был ли это автоматический переход, поиск (например, после вызова player.next() ), повторение одного и того же элемента или вызвано изменением списка воспроизведения (например, если текущий воспроизводимый элемент удален).

Метаданные

Метаданные, возвращаемые из player.getCurrentMediaMetadata() могут меняться по многим причинам: переходы списков воспроизведения, обновления метаданных в потоке или обновление текущего MediaItem в середине воспроизведения.

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

Ищу

Вызов методов Player.seekTo приводит к серии обратных вызовов зарегистрированным экземплярам Player.Listener :

  1. onPositionDiscontinuity с reason=DISCONTINUITY_REASON_SEEK . Это прямой результат вызова Player.seekTo . Обратный вызов имеет поля PositionInfo для позиции до и после поиска.
  2. onPlaybackStateChanged с любым немедленным изменением состояния, связанным с поиском. Обратите внимание, что такого изменения может не быть.

Отдельные обратные вызовы и onEvents

Слушатели могут выбирать между реализацией отдельных обратных вызовов, таких как onIsPlayingChanged(boolean isPlaying) и общего обратного вызова onEvents(Player player, Events events) . Общий обратный вызов обеспечивает доступ к объекту Player и определяет набор events , которые произошли вместе. Этот обратный вызов всегда вызывается после обратных вызовов, соответствующих отдельным событиям.

Котлин

override fun onEvents(player: Player, events: Player.Events) {
  if (
    events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) ||
      events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)
  ) {
    uiModule.updateUi(player)
  }
}

Ява

@Override
public void onEvents(Player player, Events events) {
  if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)
      || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
    uiModule.updateUi(player);
  }
}

Индивидуальные мероприятия следует отдавать предпочтение в следующих случаях:

  • Слушателя интересуют причины изменений. Например, причины, указанные в onPlayWhenReadyChanged или onMediaItemTransition .
  • Слушатель действует только на новые значения, предоставленные через параметры обратного вызова, или запускает что-то еще, что не зависит от параметров обратного вызова.
  • Реализация прослушивателя предпочитает четкое и читаемое указание того, что вызвало событие, в имени метода.
  • Слушатель сообщает системе аналитики, которой необходимо знать обо всех отдельных событиях и изменениях состояния.

Общий onEvents(Player player, Events events) следует отдавать предпочтение в следующих случаях:

  • Слушатель хочет активировать одну и ту же логику для нескольких событий. Например, обновление пользовательского интерфейса для onPlaybackStateChanged и onPlayWhenReadyChanged .
  • Слушателю необходим доступ к объекту Player , чтобы инициировать дальнейшие события, например поиск перехода элемента мультимедиа.
  • Прослушиватель намеревается использовать несколько значений состояния, о которых сообщается через отдельные обратные вызовы, вместе или в сочетании с методами получения Player . Например, использование Player.getCurrentWindowIndex() с Timeline , предоставленной в onTimelineChanged , безопасно только внутри обратного вызова onEvents .
  • Слушателя интересует, логически ли события произошли вместе. Например, onPlaybackStateChanged на STATE_BUFFERING из-за перехода элемента мультимедиа.

В некоторых случаях прослушивателям может потребоваться объединить отдельные обратные вызовы с общим обратным вызовом onEvents , например, для записи причин изменения элемента мультимедиа с помощью onMediaItemTransition , но действовать только после того, как все изменения состояния можно будет использовать вместе в onEvents .

Использование AnalyticsListener

При использовании ExoPlayer AnalyticsListener можно зарегистрировать в проигрывателе, вызвав addAnalyticsListener . Реализации AnalyticsListener могут прослушивать подробные события, которые могут быть полезны для целей аналитики и ведения журнала. Более подробную информацию можно найти на странице аналитики .

Использование EventLogger

EventLogger — это AnalyticsListener , предоставляемый непосредственно библиотекой для целей ведения журнала. Добавьте EventLogger в ExoPlayer , чтобы включить полезную дополнительную регистрацию в одну строку:

Котлин

player.addAnalyticsListener(EventLogger())

Ява

player.addAnalyticsListener(new EventLogger());

Дополнительные сведения см. на странице журнала отладки .

Запуск событий в указанных позициях воспроизведения

В некоторых случаях использования требуется запуск событий в определенных позициях воспроизведения. Это поддерживается с помощью PlayerMessage . PlayerMessage можно создать с помощью ExoPlayer.createMessage . Позицию воспроизведения, в которой оно должно выполняться, можно установить с помощью PlayerMessage.setPosition . По умолчанию сообщения выполняются в потоке воспроизведения, но это можно настроить с помощью PlayerMessage.setLooper . PlayerMessage.setDeleteAfterDelivery можно использовать для управления тем, будет ли сообщение выполняться каждый раз, когда встречается указанная позиция воспроизведения (это может происходить несколько раз из-за режимов поиска и повторения) или только в первый раз. После настройки PlayerMessage его можно запланировать с помощью PlayerMessage.send .

Котлин

player
  .createMessage { messageType: Int, payload: Any? -> }
  .setLooper(Looper.getMainLooper())
  .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120000)
  .setPayload(customPayloadData)
  .setDeleteAfterDelivery(false)
  .send()

Ява

player
    .createMessage(
        (messageType, payload) -> {
          // Do something at the specified playback position.
        })
    .setLooper(Looper.getMainLooper())
    .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120_000)
    .setPayload(customPayloadData)
    .setDeleteAfterDelivery(false)
    .send();