直播

ExoPlayer 可直接播放大多數自適應直播串流,無須任何特殊設定。詳情請參閱「支援的格式」頁面。

自適應直播會定期更新可用媒體的時段,以配合目前的即時時間。也就是說,播放位置通常會位於這個時間範圍內,且大多接近串流產生時的即時時間。目前即時位置與播放位置之間的差異稱為「即時偏移」

偵測及監控即時播放

每當即時視窗更新時,已註冊的 Player.Listener 執行個體就會收到 onTimelineChanged 事件。您可以查詢各種 PlayerTimeline.Window 方法,擷取目前直播的詳細資料,如下表和下圖所示。

即時視窗

  • Player.isCurrentWindowLive 表示目前播放的媒體項目是否為直播。即使直播已結束,這個值仍為 true。
  • Player.isCurrentWindowDynamic 表示目前播放的媒體項目是否仍在更新。通常尚未結束的直播影片會顯示這類資訊。請注意,在某些情況下,非直播也會出現這個旗標。
  • Player.getCurrentLiveOffset 會傳回目前即時時間與播放位置之間的偏移 (如有)。
  • Player.getDuration 會傳回目前直播視窗的長度。
  • Player.getCurrentPosition 會傳回相對於直播視窗開頭的播放位置。
  • Player.getCurrentMediaItem 會傳回目前的媒體項目,其中 MediaItem.liveConfiguration 包含應用程式提供的目標直播位移和直播位移調整參數的覆寫。
  • Player.getCurrentTimeline 會以 Timeline 形式傳回目前的媒體結構。您可以使用 Player.getCurrentMediaItemIndexTimeline.getWindow,從 Timeline 擷取目前的 Timeline.Window。在 Window中:
    • Window.liveConfiguration 包含目標即時位移和即時位移調整參數。這些值是以媒體中的資訊為準,以及在 MediaItem.liveConfiguration 中設定的任何應用程式提供的覆寫。
    • Window.windowStartTimeMs 是 Unix Epoch 之後的時間,即即時視窗的開始時間。
    • Window.getCurrentUnixTimeMs 是指自 Unix Epoch 紀元時間起算的目前即時時間。伺服器和用戶端之間已知的時鐘差異可能會修正這個值。
    • Window.getDefaultPositionMs 是直播視窗中的位置,播放器預設會從這個位置開始播放。

在直播中搜尋

你可以使用 Player.seekTo 搜尋直播視窗中的任何位置。傳遞的搜尋位置是相對於直播視窗的開頭。舉例來說,seekTo(0) 會跳至直播視窗的開頭。搜尋後,播放器會盡量維持與搜尋位置相同的即時偏移。

直播視窗也有預設位置,播放作業應從該位置開始。這個位置通常靠近直播邊緣。您可以呼叫 Player.seekToDefaultPosition,搜尋預設位置。

直播播放 UI

ExoPlayer 的預設 UI 元件會顯示直播視窗的長度,以及目前在視窗中的播放位置。這表示每次更新直播視窗時,位置都會向後跳。如需其他行為,例如顯示 Unix 時間或目前的即時偏移量,您可以分叉 PlayerControlView 並修改,以符合您的需求。

設定即時播放參數

ExoPlayer 會使用某些參數,控制播放位置與直播邊緣的偏移量,以及可用於調整此偏移量的播放速度範圍。

ExoPlayer 會從三個位置取得這些參數的值,優先順序由高到低 (系統會使用找到的第一個值):

  • 傳遞至 MediaItem.Builder.setLiveConfiguration 的每個 MediaItem 值。
  • DefaultMediaSourceFactory 上設定全域預設值。
  • 直接從媒體讀取的值。

Kotlin

// Global settings.
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000))
    .build()

// Per MediaItem settings.
val mediaItem =
  MediaItem.Builder()
    .setUri(mediaUri)
    .setLiveConfiguration(
      MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build()
    )
    .build()
player.setMediaItem(mediaItem)

Java

// Global settings.
ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000))
        .build();

// Per MediaItem settings.
MediaItem mediaItem =
    new MediaItem.Builder()
        .setUri(mediaUri)
        .setLiveConfiguration(
            new MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build())
        .build();
player.setMediaItem(mediaItem);

可用的設定值如下:

  • targetOffsetMs:目標即時偏移。播放器會盡可能在播放期間接近這個即時偏移。
  • minOffsetMs:允許的最低即時偏移量。即使將偏移量調整為目前的網路狀況,播放器也不會在播放期間嘗試低於這個偏移量。
  • maxOffsetMs:允許的直播時差上限。即使將偏移量調整為目前的網路狀況,播放器也不會在播放期間嘗試取得高於這個偏移量的資料。
  • minPlaybackSpeed:播放器嘗試達到目標即時偏移時,可使用的最低播放速度。
  • maxPlaybackSpeed:播放器可用於追趕進度的最高播放速度,以達到目標即時偏移。

調整播放速度

播放低延遲直播時,ExoPlayer 會稍微變更播放速度,藉此調整直播偏移。播放器會嘗試比對媒體或應用程式提供的目標即時偏移,但也會嘗試因應網路狀況變化。舉例來說,如果播放期間發生重新緩衝,播放器會稍微減緩播放速度,以遠離即時邊緣。如果網路恢復穩定,足以支援更接近直播邊緣的播放,播放器就會加快播放速度,回到目標直播時間差。

如不希望自動調整播放速度,可以將 minPlaybackSpeedmaxPlaybackSpeed 屬性設為 1.0f,即可停用這項功能。同樣地,只要將這些值明確設為 1.0f 以外的值,即可為非低延遲直播啟用這項功能。如要進一步瞭解如何設定這些屬性,請參閱上方的設定部分

自訂播放速度調整演算法

如果啟用速度調整功能,LivePlaybackSpeedControl 會定義要進行哪些調整。您可以實作自訂 LivePlaybackSpeedControl,或自訂預設實作 (即 DefaultLivePlaybackSpeedControl)。在這兩種情況下,您都可以在建構播放器時設定執行個體:

Kotlin

val player =
  ExoPlayer.Builder(context)
    .setLivePlaybackSpeedControl(
      DefaultLivePlaybackSpeedControl.Builder().setFallbackMaxPlaybackSpeed(1.04f).build()
    )
    .build()

Java

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setLivePlaybackSpeedControl(
            new DefaultLivePlaybackSpeedControl.Builder()
                .setFallbackMaxPlaybackSpeed(1.04f)
                .build())
        .build();

DefaultLivePlaybackSpeedControl 的相關自訂參數如下:

  • fallbackMinPlaybackSpeedfallbackMaxPlaybackSpeed:如果媒體和應用程式提供的 MediaItem 都未定義限制,則可調整的最小和最大播放速度。
  • proportionalControlFactor:控制速度調整的平滑度。值越高,調整速度就越快,但越有可能聽得出來。值越小,速度轉換越平順,但速度會較慢。
  • targetLiveOffsetIncrementOnRebufferMs:每當發生重新緩衝時,這個值就會加到目標即時偏移中,以便更謹慎地繼續播放。如要停用這項功能,請將值設為 0。
  • minPossibleLiveOffsetSmoothingFactor:指數平滑因子,用於根據目前緩衝的媒體追蹤可能的最低即時偏移。值越接近 1,表示估算結果越保守,可能需要較長時間才能因應網路狀況改善而調整;值越小,表示估算結果越快調整,但發生重新緩衝的風險也越高。

BehindLiveWindowException 和 ERROR_CODE_BEHIND_LIVE_WINDOW

如果播放器暫停或緩衝的時間夠長,播放位置可能會落後直播視窗。如果發生這種情況,播放作業就會失敗,且系統會透過 Player.Listener.onPlayerError 回報錯誤代碼為 ERROR_CODE_BEHIND_LIVE_WINDOW 的例外狀況。應用程式程式碼可能會想處理這類錯誤,方法是在預設位置繼續播放。試用版應用程式的 PlayerActivity 示範了這個方法。

Kotlin

override fun onPlayerError(error: PlaybackException) {
  if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) {
    // Re-initialize player at the live edge.
    player.seekToDefaultPosition()
    player.prepare()
  } else {
    // Handle other errors
  }
}

Java

@Override
public void onPlayerError(PlaybackException error) {
  if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) {
    // Re-initialize player at the live edge.
    player.seekToDefaultPosition();
    player.prepare();
  } else {
    // Handle other errors
  }
}