播放器介面

播放器是應用程式的元件,可協助播放媒體項目。Media3 Player 介面會設定播放器通常處理的功能大綱。這包括:

  • 影響播放控制項,例如播放、暫停和跳轉
  • 查詢目前播放媒體的屬性,例如播放位置
  • 管理媒體項目播放清單/佇列
  • 設定播放屬性,例如隨機播放、重複播放、速度和音量
  • 將影片轉譯到螢幕

Media3 也提供 Player 介面的實作,稱為 ExoPlayer。

元件之間的通用介面

Media3 中有幾個元件會實作 Player 介面,例如:

元件 說明和行為注意事項
ExoPlayer 媒體播放器 API,以及 Player 介面的預設實作。
MediaController 與 MediaSession 互動,傳送播放指令。如果 Player 和 MediaSession 位於與播放器 UI 所在的 Activity 或 Fragment 分開的 Service 中,您可以將 MediaController 指派為 UI 元件 (例如 PlayerView 或 Player 可組合項) 的播放器。系統會透過 MediaSession 將播放和播放清單方法呼叫傳送至 Player。
MediaBrowser 除了 MediaController 提供的功能外,還可與 MediaLibrarySession 互動,瀏覽可用的媒體內容。
SimpleBasePlayer Player 實作,可將要實作的方法數量降至最低。如要將自訂播放器連結至 MediaSession,這個方法就很實用。
ForwardingSimpleBasePlayer SimpleBasePlayer 子類別,用於將播放作業轉送至另一個 Player,同時允許與 SimpleBasePlayer 相同的一致行為自訂。使用這個類別可抑制或修改特定播放作業。
RemoteCastPlayer Player 實作,用於控制遠端 Cast 接收端應用程式的播放。
CastPlayer Player 實作,用於控制本機和遠端 Cast 播放。

雖然 MediaSession 不會實作 Player 介面,但在建立時需要 Player。其目的是提供從其他程序或執行緒存取 Player 的權限。

Media3 播放架構

如果您有 Player 的存取權,應直接呼叫其方法來發出播放指令。您可以導入 MediaSession,通告播放功能並授予外部來源播放控制項。這些外部來源會實作 MediaController,有助於連線至媒體工作階段,並發出播放指令要求。

在背景播放媒體時,您必須將媒體工作階段和播放器放在 MediaSessionService 或 MediaLibraryService 中,並做為前景服務執行。這樣一來,您就可以將播放器與應用程式中包含播放控制項 UI 的 Activity 分開。這可能需要使用媒體控制器。

圖表:Media3 播放元件如何融入媒體應用程式架構。
圖 1:Player 介面在 Media3 架構中扮演重要角色。

播放器狀態

實作 Player 介面的媒體播放器狀態主要包含 4 類資訊:

  1. 播放狀態
  2. 媒體項目播放清單
  3. 播放/暫停屬性,例如:
    • playWhenReady:指出使用者是否希望媒體在可能的情況下播放,或保持暫停狀態
    • Playback suppression reason:如果適用,即使 playWhenReady 為 true,也會指出播放受抑制的原因
    • isPlaying: 表示播放器目前是否正在播放,只有在播放狀態為 STATE_READY、playWhenReady 為 true,且播放未受到抑制時,才會是 true
  4. 播放位置,包括:

此外,Player 介面可讓您存取可用的曲目、媒體中繼資料、播放速度、音量和其他播放輔助屬性。

監聽變更

使用 Player.Listener 監聽 Player 中的變更。如要進一步瞭解如何建立及使用事件監聽器,請參閱 ExoPlayer 說明文件的「播放器事件」一節。

請注意,事件監聽器介面不含任何回呼,無法追蹤正常播放進度。如要持續監控播放進度 (例如設定進度列 UI),請以適當的時間間隔查詢目前位置。

Kotlin

fun checkPlaybackPosition(delayMs: Long): Boolean =
  handler.postDelayed(
    {
      val currentPosition = player.currentPosition
      // Update UI based on currentPosition
      checkPlaybackPosition(delayMs)
    },
    delayMs,
  )

Java

boolean checkPlaybackPosition(long delayMs) {
  return handler.postDelayed(
      () -> {
        long currentPosition = player.getCurrentPosition();
        // Update UI based on currentPosition
        checkPlaybackPosition(delayMs);
      },
      delayMs);
}

控制播放

Player 介面提供多種方法,可操控狀態及控制播放:

自訂 Player 實作

如要建立自訂播放器,可以擴充 Media3 內含的 SimpleBasePlayer。這個類別提供 Player 介面的基本實作,可盡量減少您需要實作的方法數量。

首先,覆寫 getState() 方法。呼叫這個方法時,應填入目前的播放器狀態,包括:

  • 可用的指令集
  • 播放屬性,例如播放器是否應在播放狀態為 STATE_READY 時開始播放、目前播放媒體項目的索引,以及目前項目中的播放位置

Kotlin

class CustomPlayer(looper: Looper) : SimpleBasePlayer(looper) {
  override fun getState(): State {
    return State.Builder()
      .setAvailableCommands(Commands.EMPTY) // Set which playback commands the player can handle
      // Configure additional playback properties
      .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
      .setCurrentMediaItemIndex(0)
      .setContentPositionMs(0)
      .build()
  }
}

Java

private static final class CustomPlayer extends SimpleBasePlayer {
  public CustomPlayer(Looper looper) {
    super(looper);
  }

  @Override
  protected State getState() {
    return new State.Builder()
        .setAvailableCommands(Commands.EMPTY) // Set which playback commands the player can handle
        // Configure additional playback properties
        .setPlayWhenReady(true, PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST)
        .setCurrentMediaItemIndex(0)
        .setContentPositionMs(0)
        .build();
  }
}

SimpleBasePlayer 會強制要求 State 必須使用有效的狀態值組合建立。此外,它也會處理事件監聽器,並通知事件監聽器狀態變更。如需手動觸發狀態更新,請呼叫 invalidateState()。

除了 getState() 方法外,您只需要實作播放器宣告可用的指令所使用的方法。找出與您要實作的功能相對應的可覆寫處理常式方法。舉例來說,覆寫 handleSeek() 方法,以支援 COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM 和 COMMAND_SEEK_TO_NEXT_MEDIA_ITEM 等作業。

修改 Player 導入方式

您可以使用 ForwardingSimpleBasePlayer 修改現有 Player 的狀態和行為,而不必建立完全自訂的 Player。詳情請參閱「自訂」頁面指南。