自訂

ExoPlayer 程式庫的核心是 Player 介面。Player 會公開傳統的高階媒體播放器功能,例如緩衝媒體、播放、暫停和跳轉功能。預設的實作方式 ExoPlayer 的設計用意是避免對播放的媒體類型、儲存方式、位置,以及算繪方式做出一些假設 (因此施加了一些限制)。ExoPlayer 實作會將這項工作委派給在建立播放器時插入的元件,或將新媒體來源傳遞至播放器時插入的元件,而不會直接實作媒體的載入和算繪作業。所有 ExoPlayer 實作項目通用的元件如下:

  • MediaSource 執行個體,用於定義要播放的媒體、載入媒體,以及可從哪個已載入的媒體讀取。MediaSource 例項是由玩家中的 MediaSource.FactoryMediaItem 建立。也可以使用以媒體來源為基礎的播放清單 API,直接將這些音訊傳遞給播放器。
  • MediaItem 轉換為 MediaSourceMediaSource.Factory 例項。系統會在建立玩家時插入 MediaSource.Factory
  • 轉譯媒體的個別元件的 Renderer 執行個體。系統會在建立玩家時插入這些項目。
  • TrackSelector 會選取 MediaSource 提供的曲目,供各個可用的 Renderer 使用。系統會在建立玩家時插入 TrackSelector
  • LoadControl,控制 MediaSource 緩衝處理更多媒體的時機,以及緩衝的媒體量。系統會在建立玩家時插入 LoadControl
  • LivePlaybackSpeedControl,用於在即時播放期間控製播放速度,讓玩家保持設定的即時偏移值。系統會在建立玩家時插入 LivePlaybackSpeedControl

這個程式庫有插入實作遊戲元件的元件的概念。某些元件的預設實作會委派工作以進一步插入的元件。如此一來,許多子元件就能個別替換為以自訂方式設定的實作項目。

播放器自訂功能

以下將介紹透過插入元件來自訂播放器的一些常見範例。

設定網路堆疊

以下頁面說明如何自訂 ExoPlayer 使用的網路堆疊

快取從網路載入的資料

請參閱有關即時快取下載媒體的指南。

自訂伺服器互動

部分應用程式可能想要攔截 HTTP 要求和回應。您可能需要插入自訂要求標頭、讀取伺服器的回應標頭,以及修改要求的 URI 等等。例如,應用程式可以在要求媒體區隔時,插入權杖做為標頭,藉此驗證自身身分。

以下範例說明如何在 DefaultMediaSourceFactory 中插入自訂 DataSource.Factory 來實作這些行為:

Kotlin

val dataSourceFactory =
  DataSource.Factory {
    val dataSource = httpDataSourceFactory.createDataSource()
    // Set a custom authentication request header.
    dataSource.setRequestProperty("Header", "Value")
    dataSource
  }
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)
    )
    .build()

Java

DataSource.Factory dataSourceFactory =
    () -> {
      HttpDataSource dataSource = httpDataSourceFactory.createDataSource();
      // Set a custom authentication request header.
      dataSource.setRequestProperty("Header", "Value");
      return dataSource;
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory))
        .build();

在上方的程式碼片段中,插入的 HttpDataSource 會包含每個 HTTP 要求中的 "Header: Value" 標頭。每次與 HTTP 來源互動時,系統都會固定這個行為。

如需更精細的方法,可以使用 ResolvingDataSource 插入及時行為。下列程式碼片段說明如何在與 HTTP 來源互動之前插入要求標頭:

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time request headers.
    dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri))
  }

Java

    DataSource.Factory dataSourceFactory =
        new ResolvingDataSource.Factory(
            httpDataSourceFactory,
            // Provide just-in-time request headers.
            dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));

您也可以使用 ResolvingDataSource 及時修改 URI,如以下程式碼片段所示:

Kotlin

val dataSourceFactory: DataSource.Factory =
  ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec ->
    // Provide just-in-time URI resolution logic.
    dataSpec.withUri(resolveUri(dataSpec.uri))
  }

Java

DataSource.Factory dataSourceFactory =
    new ResolvingDataSource.Factory(
        httpDataSourceFactory,
        // Provide just-in-time URI resolution logic.
        dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));

自訂錯誤處理機制

實作自訂 LoadErrorHandlingPolicy,可讓應用程式自訂 ExoPlayer 回應載入錯誤的方式。舉例來說,應用程式可能想要快速失敗,而非重試多次,或想自訂輪詢邏輯,以控制玩家每次重試的等待時間長度。下列程式碼片段說明如何實作自訂輪詢邏輯:

Kotlin

val loadErrorHandlingPolicy: LoadErrorHandlingPolicy =
  object : DefaultLoadErrorHandlingPolicy() {
    override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long {
      // Implement custom back-off logic here.
      return 0
    }
  }
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(
      DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)
    )
    .build()

Java

LoadErrorHandlingPolicy loadErrorHandlingPolicy =
    new DefaultLoadErrorHandlingPolicy() {
      @Override
      public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) {
        // Implement custom back-off logic here.
        return 0;
      }
    };

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context)
                .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy))
        .build();

LoadErrorInfo 引數包含載入失敗的詳細資訊,可根據錯誤類型或失敗的要求自訂邏輯。

自訂擷取器標記

擷取器旗標可用來自訂如何從漸進式媒體中擷取個別格式。您可以在提供給 DefaultMediaSourceFactoryDefaultExtractorsFactory 上設定。下列範例會傳送標記,啟用以索引方式尋找 MP3 串流。

Kotlin

val extractorsFactory =
  DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING)
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory))
    .build()

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING);

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory))
        .build();

啟用固定位元率跳轉

針對 MP3、ADTS 和 AMR 串流,您可以使用 FLAG_ENABLE_CONSTANT_BITRATE_SEEKING 旗標,使用固定位元率假設啟用近似跳轉功能。您可以使用上述的個別 DefaultExtractorsFactory.setXyzExtractorFlags 方法,為個別擷取工具設定這些標記。如要啟用固定位元率尋找所有支援該擷取器的擷取器,請使用 DefaultExtractorsFactory.setConstantBitrateSeekingEnabled

Kotlin

val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)

Java

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);

然後,即可按照上方自訂擷取器標記的說明,透過 DefaultMediaSourceFactory 插入 ExtractorsFactory

啟用非同步緩衝區佇列

非同步緩衝區佇列是 ExoPlayer 轉譯管道的改善功能,除了在非同步模式下運作 MediaCodec 執行個體,還會使用其他執行緒安排資料解碼和轉譯作業。啟用後可減少影格遺失和音訊不足的問題。

搭載 Android 12 (API 級別 31) 以上版本的裝置預設會啟用非同步緩衝區佇列,但您可以從 Android 6.0 (API 級別 23) 開始手動啟用。考慮在特定裝置上,遇到影格遺失或音訊不足的問題,尤其是播放受 DRM 保護或高影格速率內容時,更是如此。

在最簡單的情況下,您必須將 DefaultRenderersFactory 插入玩家,如下所示:

Kotlin

val renderersFactory = 
  DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing()
val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()

Java

DefaultRenderersFactory renderersFactory =
    new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing();
ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();

如果您要直接將轉譯器執行個體化,請將 AsynchronousMediaCodecAdapter.Factory 傳遞至 MediaCodecVideoRendererMediaCodecAudioRenderer 建構函式。

透過 ForwardingPlayer 攔截方法呼叫

您可以自訂 Player 例項的部分行為,方法是將其納入 ForwardingPlayer 的子類別,並覆寫方法,藉此執行下列任一操作:

  • 先存取參數,再將其傳送至委派 Player
  • 請先存取委派 Player 的傳回值,然後再傳回。
  • 完整重新實作方法。

覆寫 ForwardingPlayer 方法時,請務必確保實作方式一致且符合 Player 介面,特別是在處理預期有相同或相關行為的方法時。例如:

  • 如要覆寫所有「play」作業,您必須同時覆寫 ForwardingPlayer.playForwardingPlayer.setPlayWhenReady,因為呼叫端預期在 playWhenReady = true 時,這些方法的行為會完全相同。
  • 如要變更跳轉遞增數,您需要同時覆寫 ForwardingPlayer.seekForward,才能以自訂累加執行搜尋,且需要覆寫 ForwardingPlayer.getSeekForwardIncrement,才能將正確的自訂增量回報給呼叫端。
  • 如要控制玩家執行個體要宣傳的 Player.Commands,必須同時覆寫 Player.getAvailableCommands()Player.isCommandAvailable(),並監聽 Player.Listener.onAvailableCommandsChanged() 回呼,以在基礎播放器發生變更時收到通知。

自訂 MediaSource

上述範例插入自訂元件,以便在所有傳遞至播放器的 MediaItem 物件播放期間使用。如果需要精細自訂,也可將自訂元件插入個別的 MediaSource 執行個體,再將其直接傳遞給玩家。以下範例說明如何自訂 ProgressiveMediaSource,以使用自訂 DataSource.FactoryExtractorsFactoryLoadErrorHandlingPolicy

Kotlin

val mediaSource =
  ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
    .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
    .createMediaSource(MediaItem.fromUri(streamUri))

Java

ProgressiveMediaSource mediaSource =
    new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory)
        .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy)
        .createMediaSource(MediaItem.fromUri(streamUri));

建立自訂元件

針對常見用途,這個程式庫會提供本頁頂端所列元件的預設實作方式。ExoPlayer 可以使用這些元件,但如果需要非標準行為,也可以建構用於自訂的實作項目。自訂導入的部分用途如下:

  • Renderer:建議您實作自訂的 Renderer,以處理程式庫提供的預設實作不支援的媒體類型。
  • TrackSelector - 實作自訂 TrackSelector 可讓應用程式開發人員變更由 MediaSource 公開的追蹤方式,讓每個可用的 Renderer 使用。
  • LoadControl:實作自訂 LoadControl 可讓應用程式開發人員變更玩家的緩衝處理政策。
  • Extractor:如需支援程式庫目前不支援的容器格式,請考慮實作自訂的 Extractor 類別。
  • MediaSource:如果您想透過自訂方式取得媒體範例提供給轉譯器,或是想實作自訂 MediaSource 組合行為,不妨實作自訂的 MediaSource 類別。
  • MediaSource.Factory:實作自訂 MediaSource.Factory 可讓應用程式自訂從 MediaItem 建立 MediaSource 的方式。
  • DataSource – ExoPlayer 的上游套件已包含適用於不同用途的多個 DataSource 實作項目。建議您實作自己的 DataSource 類別,透過自訂通訊協定、自訂 HTTP 堆疊或自訂永久快取等方式載入資料。

建立自訂元件時,建議您採取以下做法:

  • 如果自訂元件需要將事件回報給應用程式,建議您使用與現有 ExoPlayer 元件相同的模型來完成,例如使用 EventDispatcher 類別,或將 Handler 與事件監聽器一起傳遞至元件的建構函式。
  • 我們建議自訂元件使用與現有 ExoPlayer 元件相同的模型,以允許應用程式在播放期間重新設定。如要這麼做,自訂元件應實作 PlayerMessage.Target,並在 handleMessage 方法中接收設定變更。應用程式程式碼應透過呼叫 ExoPlayer 的 createMessage 方法、設定訊息,然後使用 PlayerMessage.send 將設定變更傳送至元件。傳送訊息要在播放執行緒上傳遞,可確保訊息會按照在播放器上執行的任何其他作業的執行順序執行。