Événements du joueur

Écouter les événements de lecture

Les événements, tels que les changements d'état et les erreurs de lecture, sont signalés aux instances Player.Listener enregistrées. Pour enregistrer un écouteur afin de recevoir ces événements :

Kotlin

// 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 comporte des méthodes par défaut vides. Vous n'avez donc qu'à implémenter les méthodes qui vous intéressent. Consultez la Javadoc pour obtenir une description complète des méthodes et du moment où elles sont appelées. Certaines des méthodes les plus importantes sont décrites plus en détail ci-dessous.

Les écouteurs peuvent choisir d'implémenter des rappels d'événements individuels ou un rappel onEvents générique qui est appelé après qu'un ou plusieurs événements se sont produits ensemble. Pour savoir lequel privilégier selon les cas d'utilisation, consultez Individual callbacks vs onEvents.

Changements d'état de la lecture

Vous pouvez recevoir les changements d'état du lecteur en implémentant onPlaybackStateChanged(@State int state) dans un Player.Listener enregistré. Le lecteur peut être dans l'un des quatre états de lecture suivants :

  • Player.STATE_IDLE : il s'agit de l'état initial, de l'état lorsque le lecteur est arrêté et de l'état lorsque la lecture a échoué. Dans cet état, le lecteur ne dispose que de ressources limitées.
  • Player.STATE_BUFFERING : le lecteur ne peut pas démarrer immédiatement la lecture depuis sa position actuelle. Cela se produit généralement parce que davantage de données doivent être chargées.
  • Player.STATE_READY : le lecteur peut démarrer immédiatement la lecture depuis sa position actuelle.
  • Player.STATE_ENDED : le lecteur a terminé de lire tous les contenus multimédias.

En plus de ces états, le lecteur dispose d'un indicateur playWhenReady pour indiquer l'intention de l'utilisateur de lire le contenu. Vous pouvez recevoir les modifications apportées à ce signal en implémentant onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason).

Un lecteur est en cours de lecture (c'est-à-dire que sa position avance et que du contenu multimédia est présenté à l'utilisateur) lorsque les trois conditions suivantes sont remplies :

  • Le lecteur est à l'état Player.STATE_READY.
  • playWhenReady est true
  • La lecture n'est pas annulée pour une raison renvoyée par Player.getPlaybackSuppressionReason.

Plutôt que de devoir vérifier ces propriétés individuellement, vous pouvez appeler Player.isPlaying. Vous pouvez recevoir les modifications apportées à cet état en implémentant onIsPlayingChanged(boolean isPlaying) :

Kotlin

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.
        }
      }
    });

Erreurs de lecture

Les erreurs qui entraînent l'échec de la lecture peuvent être reçues en implémentant onPlayerError(PlaybackException error) dans un Player.Listener enregistré. En cas d'échec, cette méthode est appelée immédiatement avant que l'état de lecture ne passe à Player.STATE_IDLE. Les lectures ayant échoué ou ayant été arrêtées peuvent être relancées en appelant ExoPlayer.prepare.

Notez que certaines implémentations de Player transmettent des instances de sous-classes de PlaybackException pour fournir des informations supplémentaires sur l'échec. Par exemple, ExoPlayer transmet ExoPlaybackException, qui comporte type, rendererIndex et d'autres champs spécifiques à ExoPlayer.

L'exemple suivant montre comment détecter l'échec d'une lecture en raison d'un problème de réseau HTTP :

Kotlin

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.
          }
        }
      }
    });

Transitions de playlists

Chaque fois que le lecteur passe à un nouvel élément multimédia de la playlist, onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) est appelé sur les objets Player.Listener enregistrés. La raison indique s'il s'agissait d'une transition automatique, d'une recherche (par exemple, après avoir appelé player.next()), d'une répétition du même élément ou d'un changement de playlist (par exemple, si l'élément en cours de lecture est supprimé).

Métadonnées

Les métadonnées renvoyées par player.getCurrentMediaMetadata() peuvent changer pour de nombreuses raisons : transitions de playlist, mises à jour des métadonnées in-stream ou mise à jour du MediaItem actuel en cours de lecture.

Si vous êtes intéressé par les modifications apportées aux métadonnées, par exemple pour mettre à jour une UI qui affiche le titre actuel, vous pouvez écouter onMediaMetadataChanged.

Recherche…

L'appel des méthodes Player.seekTo entraîne une série de rappels aux instances Player.Listener enregistrées :

  1. onPositionDiscontinuity avec reason=DISCONTINUITY_REASON_SEEK. Il s'agit du résultat direct de l'appel de Player.seekTo. Le rappel comporte des champs PositionInfo pour la position avant et après la recherche.
  2. onPlaybackStateChanged avec tout changement d'état immédiat lié à la recherche. Notez qu'il est possible qu'aucun changement ne soit effectué.

Rappels individuels et onEvents

Les écouteurs peuvent choisir d'implémenter des rappels individuels tels que onIsPlayingChanged(boolean isPlaying) et le rappel générique onEvents(Player player, Events events). Le rappel générique permet d'accéder à l'objet Player et spécifie l'ensemble des events qui se sont produits ensemble. Ce rappel est toujours appelé après les rappels qui correspondent aux événements individuels.

Kotlin

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);
  }
}

Il est préférable d'utiliser des événements individuels dans les cas suivants :

  • L'auditeur s'intéresse aux raisons des changements. Par exemple, les raisons fournies pour onPlayWhenReadyChanged ou onMediaItemTransition.
  • L'écouteur n'agit que sur les nouvelles valeurs fournies par les paramètres de rappel ou déclenche autre chose qui ne dépend pas des paramètres de rappel.
  • L'implémentation de l'écouteur préfère une indication claire et lisible de ce qui a déclenché l'événement dans le nom de la méthode.
  • L'écouteur envoie des rapports à un système d'analyse qui doit connaître tous les événements individuels et les changements d'état.

Le générique onEvents(Player player, Events events) doit être privilégié dans les cas suivants :

  • L'écouteur souhaite déclencher la même logique pour plusieurs événements. Par exemple, la mise à jour d'une UI pour onPlaybackStateChanged et onPlayWhenReadyChanged.
  • L'écouteur doit accéder à l'objet Player pour déclencher d'autres événements, par exemple une recherche après une transition d'élément multimédia.
  • L'écouteur a l'intention d'utiliser plusieurs valeurs d'état signalées par le biais de rappels distincts ensemble ou en combinaison avec les méthodes getter Player. Par exemple, l'utilisation de Player.getCurrentWindowIndex() avec le Timeline fourni dans onTimelineChanged n'est sécurisée que depuis le rappel onEvents.
  • L'écouteur souhaite savoir si les événements se sont produits logiquement ensemble. Par exemple, onPlaybackStateChanged à STATE_BUFFERING en raison d'une transition d'élément multimédia.

Dans certains cas, les écouteurs peuvent avoir besoin de combiner les rappels individuels avec le rappel générique onEvents, par exemple pour enregistrer les raisons du changement d'élément multimédia avec onMediaItemTransition, mais n'agir qu'une fois que tous les changements d'état peuvent être utilisés ensemble dans onEvents.

AnalyticsListener utilisé(s)

Lorsque vous utilisez ExoPlayer, un AnalyticsListener peut être enregistré auprès du lecteur en appelant addAnalyticsListener. Les implémentations AnalyticsListener peuvent écouter des événements détaillés qui peuvent être utiles à des fins d'analyse et de journalisation. Pour en savoir plus, consultez la page Analytics.

EventLogger utilisé(s)

EventLogger est un AnalyticsListener fourni directement par la bibliothèque à des fins de journalisation. Ajoutez EventLogger à un ExoPlayer pour activer la journalisation supplémentaire utile en une seule ligne :

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

Pour en savoir plus, consultez la page Journalisation du débogage.

Déclencher des événements à des positions de lecture spécifiques

Certains cas d'utilisation nécessitent de déclencher des événements à des positions de lecture spécifiques. Cette fonctionnalité est compatible avec PlayerMessage. Un PlayerMessage peut être créé à l'aide de ExoPlayer.createMessage. La position de lecture à laquelle il doit être exécuté peut être définie à l'aide de PlayerMessage.setPosition. Les messages sont exécutés sur le thread de lecture par défaut, mais cela peut être personnalisé à l'aide de PlayerMessage.setLooper. PlayerMessage.setDeleteAfterDelivery peut être utilisé pour contrôler si le message sera exécuté chaque fois que la position de lecture spécifiée est atteinte (cela peut se produire plusieurs fois en raison des modes de recherche et de répétition) ou uniquement la première fois. Une fois la PlayerMessage configurée, elle peut être planifiée à l'aide de PlayerMessage.send.

Kotlin

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();