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