Прослушивание событий воспроизведения
События, такие как изменения состояния и ошибки воспроизведения, передаются зарегистрированным экземплярам 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
:
-
onPositionDiscontinuity
сreason=DISCONTINUITY_REASON_SEEK
. Это прямой результат вызоваPlayer.seekTo
. Обратный вызов имеет поляPositionInfo
для позиции до и после поиска. -
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();