ExoPlayer 程式庫的核心是 Player
介面。Player
會公開傳統的高階媒體播放器功能,例如緩衝媒體、播放、暫停和跳轉功能。預設的實作方式 ExoPlayer
的設計用意是避免對播放的媒體類型、儲存方式、位置,以及算繪方式做出一些假設 (因此施加了一些限制)。ExoPlayer
實作會將這項工作委派給在建立播放器時插入的元件,或將新媒體來源傳遞至播放器時插入的元件,而不會直接實作媒體的載入和算繪作業。所有 ExoPlayer
實作項目通用的元件如下:
MediaSource
執行個體,用於定義要播放的媒體、載入媒體,以及可從哪個已載入的媒體讀取。MediaSource
例項是由玩家中的MediaSource.Factory
從MediaItem
建立。也可以使用以媒體來源為基礎的播放清單 API,直接將這些音訊傳遞給播放器。- 將
MediaItem
轉換為MediaSource
的MediaSource.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
引數包含載入失敗的詳細資訊,可根據錯誤類型或失敗的要求自訂邏輯。
自訂擷取器標記
擷取器旗標可用來自訂如何從漸進式媒體中擷取個別格式。您可以在提供給 DefaultMediaSourceFactory
的 DefaultExtractorsFactory
上設定。下列範例會傳送標記,啟用以索引方式尋找 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
傳遞至 MediaCodecVideoRenderer
和 MediaCodecAudioRenderer
建構函式。
透過 ForwardingPlayer
攔截方法呼叫
您可以自訂 Player
例項的部分行為,方法是將其納入 ForwardingPlayer
的子類別,並覆寫方法,藉此執行下列任一操作:
- 先存取參數,再將其傳送至委派
Player
。 - 請先存取委派
Player
的傳回值,然後再傳回。 - 完整重新實作方法。
覆寫 ForwardingPlayer
方法時,請務必確保實作方式一致且符合 Player
介面,特別是在處理預期有相同或相關行為的方法時。例如:
- 如要覆寫所有「play」作業,您必須同時覆寫
ForwardingPlayer.play
和ForwardingPlayer.setPlayWhenReady
,因為呼叫端預期在playWhenReady = true
時,這些方法的行為會完全相同。 - 如要變更跳轉遞增數,您需要同時覆寫
ForwardingPlayer.seekForward
,才能以自訂累加執行搜尋,且需要覆寫ForwardingPlayer.getSeekForwardIncrement
,才能將正確的自訂增量回報給呼叫端。 - 如要控制玩家執行個體要宣傳的
Player.Commands
,必須同時覆寫Player.getAvailableCommands()
和Player.isCommandAvailable()
,並監聽Player.Listener.onAvailableCommandsChanged()
回呼,以在基礎播放器發生變更時收到通知。
自訂 MediaSource
上述範例插入自訂元件,以便在所有傳遞至播放器的 MediaItem
物件播放期間使用。如果需要精細自訂,也可將自訂元件插入個別的 MediaSource
執行個體,再將其直接傳遞給玩家。以下範例說明如何自訂 ProgressiveMediaSource
,以使用自訂 DataSource.Factory
、ExtractorsFactory
和 LoadErrorHandlingPolicy
:
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
將設定變更傳送至元件。傳送訊息要在播放執行緒上傳遞,可確保訊息會按照在播放器上執行的任何其他作業的執行順序執行。