Прослушивание событий воспроизведения
События, такие как изменения состояния и ошибки воспроизведения, передаются зарегистрированным экземплярам Player.Listener . Чтобы зарегистрировать слушатель для получения таких событий:
Котлин
// Add a listener to receive events from the player. player.addListener(listener)
Java
// 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 -
playWhenReadyistrue - Воспроизведение не подавляется по причине, возвращаемой функцией
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. } } } )
Java
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. } } } } )
Java
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 :
-
onPositionDiscontinuitywithreason=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) } }
Java
@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())
Java
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()
Java
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();