Événements du joueur

Écouter des é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 de tels événements, procédez comme suit:

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 ne devez donc implémenter que celles qui vous intéressent. Consultez la page 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 ont le choix entre 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 produisent ensemble. Consultez la section Individual callbacks vs onEvents pour en savoir plus sur les méthodes à privilégier pour différents cas d'utilisation.

Changements de l'état de la lecture

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

  • Player.STATE_IDLE: état initial, état d'arrêt du lecteur et d'échec de la lecture. Dans cet état, le lecteur ne dispose que de ressources limitées.
  • Player.STATE_BUFFERING: le lecteur ne peut pas lancer immédiatement la lecture à partir de sa position actuelle. Cela se produit généralement lorsque davantage de données doivent être chargées.
  • Player.STATE_READY: le lecteur peut lancer la lecture immédiatement à partir de sa position actuelle.
  • Player.STATE_ENDED: le lecteur a fini 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 jouer. Vous pouvez recevoir les modifications apportées à cette option en implémentant onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason).

Le lecteur est en cours de lecture (c'est-à-dire que sa position progresse et que le 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 interrompue pour un motif renvoyé par Player.getPlaybackSuppressionReason.

Au lieu de devoir vérifier ces propriétés individuellement, Player.isPlaying peut être appelé. 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. Vous pouvez relancer la lecture en cas d'échec ou d'arrêt en appelant ExoPlayer.prepare.

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

L'exemple suivant montre comment détecter un échec de 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 des playlists

Chaque fois que le lecteur passe à un nouvel élément multimédia dans la playlist, onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) est appelé sur les objets Player.Listener enregistrés. Le motif indique s'il s'agit 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'une modification de la playlist (par exemple, si l'élément en cours de lecture est supprimé).

Metadata

Les métadonnées renvoyées par player.getCurrentMediaMetadata() peuvent être modifiées pour de nombreuses raisons: les transitions de playlists, les mises à jour des métadonnées InStream ou la mise à jour de l'MediaItem actuelle en cours de lecture.

Si vous êtes intéressé par les modifications de 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 sur les 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'un tel changement n'est peut-être pas le cas.

Rappels individuels et rappel onEvents

Les écouteurs peuvent choisir d'implémenter des rappels individuels tels que onIsPlayingChanged(boolean isPlaying) ou le rappel générique onEvents(Player player, Events events). Le rappel générique fournit un accès à l'objet Player et spécifie l'ensemble des events qui ont eu lieu ensemble. Ce rappel est toujours appelé après les rappels correspondant 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 convient de privilégier les événements individuels dans les cas suivants:

  • L'écouteur 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 via les paramètres de rappel ou déclenche un autre élément 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 est transmis à un système d'analyse qui doit connaître tous les événements individuels et tous les changements d'état.

Il est préférable d'utiliser le paramètre onEvents(Player player, Events events) générique dans les cas suivants:

  • L'écouteur souhaite déclencher la même logique pour plusieurs événements. Par exemple, vous pouvez mettre à jour une UI pour onPlaybackStateChanged et onPlayWhenReadyChanged.
  • L'écouteur doit accéder à l'objet Player pour déclencher d'autres événements, par exemple en effectuant une recherche après une transition d'élément multimédia.
  • L'écouteur a l'intention d'utiliser plusieurs valeurs d'état transmises ensemble via des rappels distincts ou en combinaison avec des méthodes getter Player. Par exemple, l'utilisation de Player.getCurrentWindowIndex() avec le Timeline fourni dans onTimelineChanged n'est sécurisée qu'à partir du rappel onEvents.
  • L'écouteur vérifie si les événements se sont produits ensemble de manière logique. Par exemple, passer de 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 onEvents générique, par exemple pour enregistrer les motifs de modification d'un élément multimédia avec onMediaItemTransition, mais agir seulement 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 pouvant être utiles à des fins d'analyse et de journalisation. Pour en savoir plus, consultez la page des données analytiques.

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 avec une seule ligne:

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

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

Événements déclenchés à des positions de lecture spécifiées

Certains cas d'utilisation nécessitent le déclenchement d'événements à des positions de lecture spécifiées. Cette opération est possible à l'aide de PlayerMessage. Un PlayerMessage peut être créé à l'aide de ExoPlayer.createMessage. La position de lecture à laquelle l'exécuter 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 permet de 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 l'PlayerMessage configurée, vous pouvez la planifier à 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();