
最近開發(fā)的項(xiàng)目涉及音頻、視頻播放,搜索了解到 ExoPlayer 2.x可以很好的滿足需求,簡單翻譯了一下幫助文檔。
開始的時(shí)候看到 ExoPlayer 的 demo 覺得太復(fù)雜了,為了圖省事使用的他的封裝版 ExoMedia ,后來發(fā)現(xiàn)這個(gè)庫目前還不支持字幕,還是得用回 ExoPlayer。翻譯文檔的過程中,也逐漸明白了 ExoPlayer 2.x 的 使用。
原文鏈接:Developer guide
在Android設(shè)備上播放音頻和視頻已經(jīng)很流行了,Android框架提供了用于播放媒體用最少的代碼的快速解決方案MediaPlayer。也提供了low-level 的 api 用于構(gòu)建定制媒體播放器的解決方案,例如MediaCodec, AudioTrack和MediaDrm。
ExoPlayer是建立在 Android low-level api之上的 app級(jí)開源播放器。開源項(xiàng)目包含 ExoPlayer庫和demo。
- ExoPlayer庫 - 類庫源代碼
- Demo - 演示類庫的使用
本文介紹了 ExoPlayer 庫及其用途,使用 ExoPlayer 的利弊 ,具體的代碼示例演示了如何使用,展示了如何使用ExoPlayer 播放 DASH,SmoothStreaming 和 HLS 自適應(yīng)流,如 MP4,M4A,F(xiàn)MP4,支持 WebM,MKV,MP3,OGG,WAV,MPEG-TS,MPEG-PS,F(xiàn)LV 和 ADTS( AAC)。還討論 ExoPlayer 的事件、消息、定制和 對(duì) DRM 的支持。
優(yōu)點(diǎn)和缺點(diǎn)
相比 Android 內(nèi)置的 MediaPlayer,ExoPlayer 具有許多優(yōu)于優(yōu)勢(shì):
- 支持 Dynamic Adaptive Streaming over HTTP (DASH) 和SmoothStreaming,更多支持請(qǐng)參閱支持的格式)詳細(xì)信息頁面。
- 支持高級(jí) HLS 功能,如正確處理 #EXT-X-DISCONTINUITY的標(biāo)簽。
- 能夠無縫融合,串聯(lián)和循環(huán)媒體資源。
- 支持定制和擴(kuò)展,ExoPlayer是考慮到這一點(diǎn)而設(shè)計(jì)的,并允許許多部件與定制實(shí)現(xiàn)替換。
- 更新起來更方便
- 設(shè)備通用性更強(qiáng)
- 支持在Android 4.3(API級(jí)別18)和更高的Widevine通用加密。
需要注意的是,也有一些缺點(diǎn),這一點(diǎn)很重要:
- ExoPlayer的標(biāo)準(zhǔn)音頻和視頻部件依賴于Android的 MediaCodecAPI,MediaCodecAPI 在搭載Android 4.1(API級(jí)別16)發(fā)布。因此,他們不會(huì)在較早版本的 Android 的工作。Widevine 的通用加密可以在 Android 4.3(API級(jí)別18)和更高。
Library overview
ExoPlayer庫的核心是ExoPlayer接口。ExoPlayer 接口暴露了傳統(tǒng)的 high-level 播放器中的功能,如資源緩沖,播放,暫停和拖拽等。接口的實(shí)現(xiàn)類對(duì)媒體的播放類型、存儲(chǔ)位置和渲染方式做出假設(shè),而不是籠統(tǒng)的加載和渲染。Exoplayer 把播放類型、存儲(chǔ)位置和渲染方式等任務(wù)委派給不同的部件,然后在創(chuàng)建播放器或后臺(tái)播放的時(shí)候把這些部件注入。這些部件包括:
- MediaSource - 負(fù)責(zé)裝載 media,裝載到MediaSource 的 media 可以被讀取,MediaSource 在調(diào)用 ExoPlayer.prepare 方法時(shí)被注入。
- Render S - 用于渲染 media 的部件,在創(chuàng)建播放器時(shí)被注入
- TrackSelector - 從MediaSource 中選出 media 提供給可用的 Render S 來渲染,在創(chuàng)建播放器時(shí)被注入。
- LoadControl - 控制 MediaSource 緩存更多的 media,有多少 media 被緩沖。在創(chuàng)建播放器時(shí)被注入。
類庫提供這些部件在通常情況下的默認(rèn)實(shí)現(xiàn),下面有詳細(xì)描述。一個(gè) ExoPlayer 可以利用這些部件。如果標(biāo)準(zhǔn)實(shí)現(xiàn)不能滿足需求,也可以使用自定義實(shí)現(xiàn)。例如,自定義LoadControl 可以改變播放器的緩沖策略,或自定義Renderer 可實(shí)現(xiàn) Android 本身不支持的視頻編解碼器。
注入的概念貫穿整個(gè)類庫的實(shí)現(xiàn),默認(rèn)的部件實(shí)現(xiàn)類何以被進(jìn)一步的委托注入,這讓許多子部件被單獨(dú)更換成自定義實(shí)現(xiàn)。比如,通過提供一個(gè)定制的工廠有可能從一個(gè)非標(biāo)準(zhǔn)的源或通過不同的網(wǎng)絡(luò)協(xié)議棧加載數(shù)據(jù)。而默認(rèn)的MediaSource 實(shí)現(xiàn)需要一個(gè)或多個(gè) DataSource 工廠的構(gòu)造函數(shù)被注入。
Getting started
開始使用 ExoPlayer 需要執(zhí)行以下步驟:
- 添加依賴
- 創(chuàng)建一個(gè) SimpleExoPlayer 實(shí)例
- 將 player 和 view (用于視頻輸出和用戶輸入)關(guān)聯(lián)
- 調(diào)用 prepare 方法,注入MediaSource,準(zhǔn)備播放
- 播放結(jié)束釋放 player
這些步驟在下面更詳細(xì)地所述。對(duì)于一個(gè)完整的示例,請(qǐng)參閱 PlayerActivity在ExoPlayer演示程序。
Add ExoPlayer as a dependency
確保支持 Jcenter 倉庫
repositories {
jcenter()
}
添加依賴 - 版本
compile 'com.google.android.exoplayer:exoplayer:r2.X.X'
Creating the playerr
您可以通過 ExoPlayerFactory 工廠方法創(chuàng)建一個(gè) ExoPlayer 實(shí)例。 工廠提供了一系列的方法創(chuàng)建不同程度定制的 ExoPlayer 實(shí)例。對(duì)于絕大多數(shù)使用情況下,默認(rèn) Renderer 庫提供的實(shí)現(xiàn)足夠用。這時(shí)應(yīng)該使用 ExoPlayerFactory.newSimpleInstance 方法。方法返回 SimpleExoPlayer,SimpleExoPlayer 擴(kuò)展了 ExoPlayer 并添加額外的 high-level 播放功能。下面演示了如何創(chuàng)建一個(gè) SimpleExoPlayer。
// 1. Create a default TrackSelector
Handler mainHandler = new Handler();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory =
new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector =
new DefaultTrackSelector(mainHandler, videoTrackSelectionFactory);
// 2. Create a default LoadControl
LoadControl loadControl = new DefaultLoadControl();
// 3. Create the player
SimpleExoPlayer player =
ExoPlayerFactory.newSimpleInstance(context, trackSelector, loadControl);
Attaching the player to a view
ExoPlayer 庫提供了 SimpleExoPlayerView ,它封裝了一個(gè) PlaybackControlView 和 Surface 用來渲染視頻。一個(gè) SimpleExoPlayerView 可以包含在布局 XML 文件中。下面演示了如何綁定 player
simpleExoPlayerView.setPlayer(player);
如果您需要比播放器控制和渲染視頻的 Surface 上更細(xì)粒度的控制,可以給播放器設(shè)置目標(biāo) SurfaceView, TextureView,SurfaceHolder 或 Surface 直接分別使用 SimpleExoPlayer 的 setVideoSurfaceView,setVideoTextureView,setVideoSurfaceHolder 和 setVideoSurface 方法。您可以使用PlaybackControlView 作為一個(gè)獨(dú)立的部件,或?qū)崿F(xiàn)自己的 playback 直接與 player 進(jìn)行交互。setTextOutput 和setId3Output可用于 playback 時(shí)接收字幕和 ID3 元數(shù)據(jù)輸出。
Preparing the player
在 ExoPlayer 每一個(gè) media 都由 MediaSource 代表。要播放 media 必須先創(chuàng)建一個(gè)相應(yīng)的 MediaSource,然后把這個(gè)對(duì)象傳遞給 ExoPlayer.prepare。ExoPlayer 庫提供 MediaSource 的DASH實(shí)現(xiàn)(實(shí)DashMediaSource),SmoothStreaming 實(shí)現(xiàn)( SsMediaSource),HLS 實(shí)現(xiàn)(HlsMediaSource)和常規(guī) media files 實(shí)現(xiàn)(ExtractorMediaSource)。這些實(shí)現(xiàn)在后面詳細(xì)介紹。下面的代碼演示如何使用 MediaSource prapare 一個(gè)適合播放 MP4 文件的 player。
// Measures bandwidth during playback. Can be null if not required.
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(this,
Util.getUserAgent(this, "yourApplicationName"), bandwidthMeter);
// Produces Extractor instances for parsing the media data.
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
// This is the MediaSource representing the media to be played.
MediaSource videoSource = new ExtractorMediaSource(mp4VideoUri,
dataSourceFactory, extractorsFactory, null, null);
// Prepare the player with the source.
player.prepare(videoSource);
一旦 player 已經(jīng)被 prepared,playback 可以通過調(diào)用播放器上的方法進(jìn)行控制。例如 setPlayWhenReady 可用于啟動(dòng)和暫停播放,和各種 seekTo 方法可以用于改變進(jìn)度。如果 player 被綁定到 SimpleExoPlayerView 或 PlaybackControlView ,那么用戶與這些部件的交互將會(huì)導(dǎo)致 player 相應(yīng)的方法被調(diào)用。
Releasing the player
不再使用的時(shí)候?qū)?player 釋放掉是非常重要的,以便騰出有限的資源,如為其他應(yīng)用程序使用視頻解碼器。這可以通過調(diào)用 ExoPlayer.release 來完成。
MediaSource
在 ExoPlayer 每一個(gè) media 都由 MediaSource 代表。要播放 media 必須先創(chuàng)建一個(gè)相應(yīng)的 MediaSource,然后把這個(gè)對(duì)象傳遞給 ExoPlayer.prepare。ExoPlayer 庫提供 MediaSource 的DASH實(shí)現(xiàn)(實(shí)DashMediaSource),SmoothStreaming 實(shí)現(xiàn)( SsMediaSource),HLS 實(shí)現(xiàn)(HlsMediaSource)和常規(guī) media files 實(shí)現(xiàn)(ExtractorMediaSource)。Demo app de PlayerActivity,分別演示了如何實(shí)例化以上四種 MediaSource。
除了上述的 MediaSource 的實(shí)現(xiàn)方式,ExoPlayer 庫還提供MergingMediaSource,LoopingMediaSource 和 ConcatenatingMediaSource。這些 MediaSource 實(shí)現(xiàn)能夠通過組合能夠支持更復(fù)雜的 playback 功能。下面對(duì)一些常見的用例進(jìn)行說明。注意,盡管下面的實(shí)例使用的視頻 playback,它們同樣適用于僅有音頻 playback,事實(shí)上適用于所有支持的 media types 的 playback。
Side-loading a subtitle file
給定的視頻文件和一個(gè)單獨(dú)的字幕文件,MergingMediaSource 可以將它們合并到用于 playback 的單一來源。
MediaSource videoSource = new ExtractorMediaSource(videoUri, ...);
MediaSource subtitleSource = new SingleSampleMediaSource(subtitleUri, ...);
// Plays the video with the sideloaded subtitle.
MergingMediaSource mergedSource =
new MergingMediaSource(videoSource, subtitleSource);
Seamlessly looping a video
LoopingMediaSource 可以實(shí)現(xiàn)視頻的無縫循環(huán)。下面演示了如何實(shí)現(xiàn)無縫循環(huán)。也可以創(chuàng)建一個(gè)指定循環(huán)計(jì)數(shù)的 LoopingMediaSource。
MediaSource source = new ExtractorMediaSource(videoUri, ...);
// Loops the video indefinitely.
LoopingMediaSource loopingSource = new LoopingMediaSource(source);
**Seamlessly playing a sequence of videos **
ConcatenatingMediaSource 使得 playback 支持順序播放兩個(gè)或多個(gè)單獨(dú)的 MediaSource S,下面的示例順序播放兩段視頻。無縫切換資源,并且對(duì)被串聯(lián)資源的格式是否相同沒有要求。
MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video, then the second video.
ConcatenatingMediaSource concatenatedSource =
new ConcatenatingMediaSource(firstSource, secondSource);
**Advanced composition **
可以為了更不尋常的用例進(jìn)一步結(jié)合復(fù)合 MediaSourceS。給定兩個(gè)視頻 A 和 B,下面的例子演示了如何使用 LoopingMediaSource 和ConcatenatingMediaSource 按照( A,A,B)的序列進(jìn)行無線循環(huán)。
MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video twice.
LoopingMediaSource firstSourceTwice = new LoopingMediaSource(firstSource, 2);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
new ConcatenatingMediaSource(firstSourceTwice, secondSource);
// Loops the sequence indefinitely.
LoopingMediaSource compositeSource = new LoopingMediaSource(concatenatedSource);
下面的例子是等價(jià)的,這表明實(shí)現(xiàn)相同的結(jié)果方式不止一個(gè)。
MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
new ConcatenatingMediaSource(firstSource, firstSource, secondSource);
// Loops the sequence indefinitely.
LoopingMediaSource compositeSource = new LoopingMediaSource(concatenatedSource);
避免在一次 composition 中用同一個(gè) MediaSource 實(shí)例多次,除非文檔中明確允許。在上面的例子中兩次使用 firstSource 的就是這樣的情況,由于對(duì) Javadoc 中 ConcatenatingMediaSource 明確規(guī)定,重復(fù)項(xiàng)是允許的。在一般情況下,由組合物形成的對(duì)象的曲線應(yīng)該是一個(gè)樹。在 composition 中使用多個(gè)等同于 MediaSource 的實(shí)例是允許的。
**Player events **
playback 過程中,應(yīng)用程序可以監(jiān)聽由 ExoPlayer 生成的指示 player 全部狀態(tài)的事件??梢愿鶕?jù)這些事件來更新用戶界面。許多 ExoPlayer 組件還報(bào)告它們自己特定的 low-level 的事件,可以用于性能監(jiān)控。
High level events
ExoPlayer 使用 addListener 和 removeListener 添加和移除 ExoPlayer.EventListener 實(shí)例。被注冊(cè)的偵聽器可以發(fā)布 playback 的狀態(tài)變化,以及出錯(cuò)時(shí)發(fā)布錯(cuò)誤產(chǎn)生的原因。
開發(fā)者自定義 playback 應(yīng)該注冊(cè)一個(gè)監(jiān)聽器,并根據(jù) player 的狀態(tài)變化來更新控件。如果播放失敗,應(yīng)用程序還應(yīng)該向用戶顯示相應(yīng)的錯(cuò)誤。
使用 SimpleExoPlayer 時(shí),可以在 player 上設(shè)置額外的監(jiān)聽器。特別是 setVideoListener 允許應(yīng)用程序接收與視頻渲染有關(guān)的事件,該事件可以是用于調(diào)整用戶界面。setVideoDebugListener 和setAudioDebugListener 可以接收調(diào)試信息。
Low level events
除了 high-level 監(jiān)聽器,許多由 ExoPlayer 庫提供的組件,允許擁有自己的事件偵聽器。通常需要傳遞一個(gè) Handler 給部件,用來確定監(jiān)聽器方法被調(diào)用線程。在大多數(shù)情況下,Handler應(yīng)與應(yīng)用程序的主線程關(guān)聯(lián)。
**Sending messages to components **
有些 ExoPlayer 組件允許在播放過程中更改配置。按照慣例,您通過使用 sendMessages 或 blockingSendMessages 方法傳遞變化消息給ExoPlayer 組件。這種方式可以確保兩個(gè)線程安全并且使得配置更改和在 player 上的他操作執(zhí)行有序。
**Customization **
相比 Android 內(nèi)置的 MediaPlayer,ExoPlayer 的主要好處是支持定制和擴(kuò)展的 player 以更好地滿足開發(fā)者的實(shí)際用例。在考慮到這一點(diǎn)而設(shè)計(jì)ExoPlayer 庫時(shí),定義了大量接口和抽象基類幫助開發(fā)者輕松的替換類庫提供的默認(rèn)實(shí)現(xiàn),下面展示了一些用于構(gòu)建自定義組件的用例:
- Render - 實(shí)現(xiàn)自定義Renderer處理不是由庫提供的默認(rèn)實(shí)現(xiàn)所支持的媒體類型。
- Extractor - If you need to support a container format not currently supported by the library, consider implementing a custom Extractor class, which can then be used to together with ExtractorMediaSource to play media of that type.
- MediaSource - Implementing a custom MediaSource class may be appropriate if you wish to obtain media samples to feed to renderers in a custom way, or if you wish to implement custom MediaSource compositing behavior.
- TrackSelector - mplementing a custom TrackSelector allows an app developer to change the way in which tracks exposed by a MediaSource are selected for consumption by each of the available Renderers.
- DataSource - ExoPlayer’s upstream package already contains a number of DataSource implementations for different use cases. You may want to implement you own DataSource class to load data in another way, such as over a custom protocol, using a custom HTTP stack, or through a persistent cache.
Customization guidelines
- 如果自定義組件需要向應(yīng)用程序發(fā)布事件,我們建議您一定要使用現(xiàn)有的 ExoPlayer 組件模型,在構(gòu)造組件的時(shí)候同時(shí)傳遞監(jiān)聽器和 Handler
- 我們建議的自定義組件使用現(xiàn)有 ExoPlayer 組件相同的模型,允許在playback 的過程中重新配置。要做到這一點(diǎn),就需要實(shí)現(xiàn)ExoPlayerComponent 接口,并在 handleMessage 方法中處理接收到的配置改變信息。您的應(yīng)用程序應(yīng)該調(diào)用 ExoPlayer 的 sendMessages和blockingSendMessages 方法來傳遞改變的配置信息。
ExoPlayer Demo 中 PlayerActivity 演示了 DrmSessionManager 如何 在初始化 player 的時(shí)候被創(chuàng)建和注入。