Eventi giocatore

Ascoltare eventi di riproduzione

Gli eventi, come le modifiche allo stato e gli errori di riproduzione, vengono segnalati come registrati Player.Listener istanze. Per registrare un listener in modo che riceva tali eventi:

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 ha metodi predefiniti vuoti, quindi devi solo implementare i metodi che ti interessano. Vedi Javadoc per una descrizione completa i metodi e quando vengono chiamati. Alcuni dei metodi più importanti descritti più dettagliatamente di seguito.

I listener possono scegliere se implementare i callback di singoli eventi o un callback onEvents generico che viene chiamato dopo che si sono verificati uno o più eventi in sinergia. Per una spiegazione, consulta Individual callbacks vs onEvents da preferire per diversi casi d'uso.

Modifiche allo stato di riproduzione

Le modifiche allo stato del player possono essere ricevute implementando onPlaybackStateChanged(@State int state) in un account registrato Player.Listener. Il player può essere in uno dei quattro stati di riproduzione seguenti:

  • Player.STATE_IDLE: si tratta dello stato iniziale, lo stato in cui il player è stata interrotta e la riproduzione non è riuscita. Il giocatore avrà solo risorse limitate in questo stato.
  • Player.STATE_BUFFERING: il giocatore non è in grado di giocare immediatamente dal suo posizione corrente. Ciò accade principalmente perché è necessario caricare più dati.
  • Player.STATE_READY: il giocatore può giocare immediatamente dalla sua posizione attuale posizione.
  • Player.STATE_ENDED: il player ha terminato la riproduzione di tutti i contenuti multimediali.

Oltre a questi stati, il giocatore ha una bandiera playWhenReady per indicare l'intenzione dell'utente di giocare. Le modifiche a questo flag possono essere ricevute implementando onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason).

Un player è in riproduzione (ovvero, la sua posizione sta avanzando e i contenuti multimediali vengono presentati all'utente) quando tutte e tre le seguenti condizioni sono soddisfatte:

  • Il player è nello stato Player.STATE_READY
  • playWhenReady è true
  • La riproduzione non è soppressa per un motivo restituito da Player.getPlaybackSuppressionReason

Anziché dover controllare queste proprietà singolarmente, Player.isPlaying di Google Cloud. Le modifiche a questo stato possono essere ricevute implementando 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.
        }
      }
    });

Errori di riproduzione

Gli errori che causano la mancata riuscita della riproduzione possono essere ricevuti tramite l'implementazione onPlayerError(PlaybackException error) in un account registrato Player.Listener. In caso di errore, questo metodo viene chiamato subito prima della transizione dello stato di riproduzione a Player.STATE_IDLE. È possibile ritentare le riproduzioni non riuscite o interrotte chiamando ExoPlayer.prepare.

Tieni presente che alcune implementazioni di Player passano istanze delle sottoclassi di PlaybackException per fornire ulteriori informazioni sull'errore. Per ad esempio ExoPlayer passa ExoPlaybackException, che ha type, rendererIndex e altri campi specifici di ExoPlayer.

L'esempio seguente mostra come rilevare quando una riproduzione non è riuscita a causa di un Problema di rete 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.
          }
        }
      }
    });

Transizioni playlist

Ogni volta che il player passa a un nuovo elemento multimediale nella playlist. onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) è stata chiamata quando è stata effettuata la registrazione Player.Listener oggetti. Il motivo indica se si è trattato di un evento automatico transizione, una ricerca (ad esempio dopo aver chiamato player.next()), una ripetizione lo stesso elemento o causato da una modifica alla playlist (ad esempio, se al momento l'elemento in riproduzione viene rimosso).

Metadati

I metadati restituiti da player.getCurrentMediaMetadata() possono cambiare a causa di molti Motivi: transizioni di playlist, aggiornamenti dei metadati in-stream o aggiornamento MediaItem attuale durante la riproduzione.

Se ti interessa apportare modifiche ai metadati, ad esempio aggiornare una UI che mostri il titolo attuale, puoi ascoltare onMediaMetadataChanged.

Attivazione dello spostamento in corso

La chiamata di metodi Player.seekTo comporta una serie di callback ai metodi registrati Player.Listener istanze:

  1. onPositionDiscontinuity con reason=DISCONTINUITY_REASON_SEEK. Questo è il risultato diretto della chiamata a Player.seekTo. Il callback ha PositionInfo campi per la posizione prima e dopo la ricerca.
  2. onPlaybackStateChanged con qualsiasi modifica di stato immediata relativa alla ricerca. Tieni presente che questo cambiamento potrebbe non avvenire.

Callback individuali e onEvents

Gli ascoltatori possono scegliere se implementare singoli callback come onIsPlayingChanged(boolean isPlaying) e le opzioni generiche Chiamata onEvents(Player player, Events events). Il callback generico fornisce all'oggetto Player e specifica l'insieme di events che si è verificato in sinergia. Questo callback viene sempre chiamato dopo i callback che corrispondono i singoli eventi.

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

È preferibile optare per i singoli eventi nei seguenti casi:

  • L'ascoltatore è interessato ai motivi delle modifiche. Ad esempio, motivi forniti per onPlayWhenReadyChanged o onMediaItemTransition.
  • Il listener agisce solo sui nuovi valori forniti tramite i parametri di callback o attiva qualcos'altro che non dipende dai parametri di callback.
  • L'implementazione dell'ascoltatore preferisce un'indicazione chiara e leggibile di ciò che abbia attivato l'evento nel nome del metodo.
  • L'ascoltatore segnala a un sistema di analisi che deve conoscere tutti singoli eventi e modifiche di stato.

Il campo onEvents(Player player, Events events) generico deve essere preferito nel campo i seguenti casi:

  • Il listener vuole attivare la stessa logica per più eventi. Per ad esempio l'aggiornamento di una UI sia per onPlaybackStateChanged onPlayWhenReadyChanged.
  • Il listener deve accedere all'oggetto Player per attivare ulteriori eventi, ad esempio la ricerca dopo la transizione di un elemento multimediale.
  • Il listener intende utilizzare più valori di stato segnalati tramite callback separati insieme o in combinazione con il getter Player di machine learning. Ad esempio, l'utilizzo di Player.getCurrentWindowIndex() con Il metodo di pagamento Timeline fornito in onTimelineChanged è al sicuro soltanto all'interno di Chiamata di onEvents.
  • Il listener è interessato a sapere se gli eventi si sono verificati logicamente insieme. Per esempio, da onPlaybackStateChanged a STATE_BUFFERING a causa di un elemento multimediale una transizione.

In alcuni casi, gli ascoltatori potrebbero dover combinare i singoli callback con callback onEvents generico, ad esempio per registrare i motivi di modifica degli elementi multimediali con onMediaItemTransition, ma agisce solo una volta che è possibile utilizzare tutte le modifiche di stato insieme in onEvents.

In uso: AnalyticsListener

Quando utilizzi ExoPlayer, è possibile registrare AnalyticsListener con il player chiamando il numero addAnalyticsListener. AnalyticsListener implementazioni sono in grado per ascoltare eventi dettagliati che possono essere utili per l'analisi e il logging scopi. Per ulteriori dettagli, consulta la pagina Dati e analisi.

In uso: EventLogger

EventLogger è un AnalyticsListener fornito direttamente dalla libreria per per scopi di logging. Aggiungi EventLogger a un ExoPlayer per abilitare dati utili logging aggiuntivo con un'unica riga:

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

Per ulteriori dettagli, consulta la pagina di logging del debug.

Attivazione di eventi in posizioni di riproduzione specificate

Alcuni casi d'uso richiedono l'attivazione di eventi in posizioni di riproduzione specifiche. Questo è supportata tramite PlayerMessage. È possibile creare un PlayerMessage utilizzando ExoPlayer.createMessage. La posizione di riproduzione in cui deve essere eseguita può essere impostato utilizzando PlayerMessage.setPosition. I messaggi vengono eseguiti la riproduzione del thread per impostazione predefinita, ma puoi personalizzarla utilizzando PlayerMessage.setLooper. È possibile usare PlayerMessage.setDeleteAfterDelivery per controllare se il messaggio verrà eseguito ogni volta che rilevata nella posizione di riproduzione (può verificarsi più volte a causa della ricerca e ripetizione) o solo la prima volta. Una volta che PlayerMessage è configurato, può essere pianificato utilizzando 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();