播放器事件

監聽播放事件

事件 (例如狀態變更和播放錯誤) 會回報為已註冊的事件 Player.Listener 執行個體。如要註冊接聽程式來接收這類 事件:

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 含有空白的預設方法,因此您只需要實作 要查看的方法如需完整的 方法和呼叫的時機以下列舉幾個最重要的方法 。

事件監聽器可以選擇實作個別事件回呼,也可以使用的是 系統在一或多個事件發生後呼叫的一般 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):

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

播放錯誤

實作可接收播放失敗的錯誤 已完成註冊的 onPlayerError(PlaybackException error) Player.Listener。如果作業失敗,系統會呼叫此方法 播放狀態轉變成 Player.STATE_IDLE 之前。 您可以呼叫 ExoPlayer.prepare 來重試播放失敗或停止播放的情況。

請注意,部分 Player 實作會傳遞 PlaybackException,提供失敗原因的額外資訊。適用對象 例如,ExoPlayer 傳遞 ExoPlaybackException,其中包含 typerendererIndex 和其他 ExoPlayer 專用欄位。

以下範例說明如何偵測影片是否因 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.
          }
        }
      }
    });

播放清單轉場效果

每當播放器變更播放清單中的新媒體項目時 onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) 已在登錄上呼叫 Player.Listener 物件。原因會指出是否為自動 轉場效果、跳轉 (例如在呼叫 player.next() 後),重複 或是由播放清單變更造成的 (例如,如果目前 播放項目時)。

中繼資料

由於許多原因,從 player.getCurrentMediaMetadata() 傳回的中繼資料可能有所變動 造成播放清單轉換、串流內中繼資料更新或更新 目前的 MediaItem在播放中。

對中繼資料的異動 例如更新 目前可以播放onMediaMetadataChanged

正在指定播放時間點

呼叫 Player.seekTo 方法會導致一系列回呼已註冊 Player.Listener 執行個體:

  1. reason=DISCONTINUITY_REASON_SEEK會員價 onPositionDiscontinuity。這是 呼叫 Player.seekTo 的直接結果。回呼有 PositionInfo ] 欄位來代表跳轉前後的位置。
  2. onPlaybackStateChanged,且與跳轉相關的任何立即狀態變更。 請注意,這類變更不一定會發生。

個別回呼與 onEvents

事件監聽器可以選擇實作個別回呼,例如 onIsPlayingChanged(boolean isPlaying)和一般 onEvents(Player player, Events events) 回呼。一般回呼會提供 存取 Player 物件,並指定發生的 events 組合 。系統一律會在對應的回呼函式後呼叫此回呼 個別事件

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

建議在下列情況下優先使用個別事件:

  • 事件監聽器想瞭解變更的原因。舉例來說, 原因為 onPlayWhenReadyChangedonMediaItemTransition
  • 事件監聽器只會對透過回呼參數提供的新值採取行動 也可以觸發不依賴回呼參數的其他內容。
  • 事件監聽器實作偏好清楚易讀的 觸發了方法名稱中的事件。
  • 事件監聽器向分析系統回報,該系統中需瞭解 個別事件和狀態變更

一般的 onEvents(Player player, Events events) 應優先採用 下列情況:

  • 事件監聽器想為多個事件觸發相同邏輯。適用對象 例如,更新 onPlaybackStateChangedonPlayWhenReadyChanged
  • 事件監聽器需要存取 Player 物件,才能觸發後續事件。 例如在媒體項目轉換後跳轉
  • 事件監聽器想要使用回報的多個狀態值 搭配使用個別回呼,或與 Player getter 搭配使用 方法。例如,使用 Player.getCurrentWindowIndex() 搭配 onTimelineChanged 中提供的 Timeline 只能在 onEvents 回呼。
  • 事件監聽器想瞭解事件是否有邏輯發生。適用對象 例如 onPlaybackStateChangedSTATE_BUFFERING,因為某個媒體項目 轉換。

在某些情況下,事件監聽器可能需要將個別回呼與 一般 onEvents 回呼,例如記錄媒體項目變更原因 具備 onMediaItemTransition,但只能在可使用所有狀態變更時執行 透過 onEvents 整合。

使用AnalyticsListener

使用 ExoPlayer 時,可以向玩家註冊 AnalyticsListener 呼叫 addAnalyticsListenerAnalyticsListener 項導入後可 監聽適合用於數據分析和記錄的詳細事件 用途。詳情請參閱數據分析頁面

使用EventLogger

EventLogger 是由以下程式庫直接提供的 AnalyticsListener: 以便進行記錄。在 ExoPlayer 中新增 EventLogger,以啟用實用的功能 即可:

Kotlin

player.addAnalyticsListener(EventLogger())

Java

player.addAnalyticsListener(new EventLogger());

詳情請參閱偵錯記錄頁面

在指定的播放位置觸發事件

部分用途需要在指定的播放位置觸發事件。這是 支援使用 PlayerMessage。可以用 PlayerMessage 建立 ExoPlayer.createMessage。應執行播放器的播放位置 您可使用 PlayerMessage.setPosition 進行設定。訊息會在 預設播放執行緒,但如要自訂播放方式,請使用 PlayerMessage.setLooperPlayerMessage.setDeleteAfterDelivery可使用 控制訊息是否會在指定的每次指定時執行 發生播放位置時 (可能會因為跳轉而多次變更) 或重複模式) 或第一次設定。PlayerMessage 可以使用 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();