ExoPlayer Library 概述
ExoPlayer是運行在YouTube app Android版本上的視頻播放器
ExoPlayer是構(gòu)建在Android低水平媒體API之上的一個應(yīng)用層媒體播放器署照。和Android內(nèi)置的媒體播放器相比霹菊,ExoPlayer有許多優(yōu)點。ExoPlayer支持內(nèi)置的媒體播放器支持的所有格式外加自適應(yīng)格式DASH和SmoothStreaming慕爬。ExoPlayer可以被高度定制和擴展以適應(yīng)不同的使用場景畅铭。
ExoPlayer庫的核心是ExoPlayer接口淡溯。ExoPlayer公開了傳統(tǒng)的高水平媒體播放器的功能,例如媒體緩沖拒逮,播放罐氨,暫停和快進功能。ExoPlayer實現(xiàn)旨在對正在播放的媒體類型滩援,存儲方式和位置以及渲染方式做出一些假設(shè)(因此幾乎沒有限制)栅隐。ExoPlayer沒有直接實現(xiàn)媒體文件的加載和渲染,而是把這些工作委托給了在創(chuàng)建播放器或者播放器準備好播放的時候注入的組件玩徊。所有ExoPlayer實現(xiàn)的通用組件是:
-
MediaSource
:媒體資源租悄,用于定義要播放的媒體,加載媒體恩袱,以及從哪里加載媒體煤墙。簡單的說辨嗽,MediaSource
就是代表我們要播放的媒體文件拥刻,可以是本地資源,可以是網(wǎng)絡(luò)資源鸯屿。MediaSource
在播放開始的時候,通過ExoPlayer.prepare
方法注入把敢。 -
Renderer
:渲染器寄摆,用于渲染媒體文件。當創(chuàng)建播放器的時候修赞,Renderers
被注入婶恼。 -
TrackSelector
:軌道選擇器,用于選擇MediaSource
提供的軌道(tracks)柏副,供每個可用的渲染器使用熙尉。 -
LoadControl
:用于控制MediaSource
何時緩沖更多的媒體資源以及緩沖多少媒體資源。LoadControl
在創(chuàng)建播放器的時候被注入搓扯。
ExoPlayer庫提供了在普通使用場景下上述組件的默認實現(xiàn)。ExoPlayer可以使用這些默認的組件包归,也可以使用自定義組件锨推。例如可以注入一個自定義的LoadControl
用來改變播放器的緩存策略,或者可以注入一個自定義渲染器以使用Android本身不支持的視頻解碼器公壤。
優(yōu)點和缺點
優(yōu)點
- 支持HTTP上的動態(tài)自適應(yīng)流DASH和SmoothStreaming换可。更多詳情請參看 Supported formats。
- 支持高級的HLS特點,例如正確的處理
#EXT-X-DISCONTINUITY
標簽厦幅。 - 能夠無縫的合并沾鳄,串聯(lián),循環(huán)播放媒體文件确憨。
- 能夠被高度擴展和定制译荞,以適用不同的場景。
缺點
- 在某些設(shè)備上播放音頻休弃,ExoPlayer可能會比MediaPlayer消耗更多的電量吞歼。
使用
- 添加依賴
implementation 'com.google.android.exoplayer:exoplayer:2.X.X'
為了省事,我們依賴了整個ExoPlayer庫塔猾。你也可以只依賴你真正需要的庫篙骡。例如果你要播放DASH類型的媒體資源,你可以只依賴Core
,DASH
,UI
這三個庫丈甸。
implementation 'com.google.android.exoplayer:exoplayer-core:2.X.X'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.X.X'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X'
整個ExoPlayer庫包括5個子庫糯俗,依賴了整個ExoPlayer庫和依賴5個子庫是等效的。
-
exoplayer-core
:核心功能 (必要) -
exoplayer-dash
:支持DASH內(nèi)容 -
exoplayer-hls
:支持HLS內(nèi)容 -
exoplayer-smoothstreaming
:支持SmoothStreaming內(nèi)容 -
exoplayer-ui
:用于ExoPlayer的UI組件和相關(guān)的資源睦擂。
- 在布局文件中加入PlayerView
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
private PlayerView playerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
[...]
playerView = findViewById(R.id.video_view);
}
- 創(chuàng)建一個SimpleExoPlayer實例得湘,SimpleExoPlayer是ExoPlayer接口的一個默認的通用實現(xiàn)。
private ExoPlayer player;
private boolean playWhenReady;
private int currentWindow;
private long playbackPosition;
private void initializePlayer() {
player = ExoPlayerFactory.newSimpleInstance(
new DefaultRenderersFactory(this),
new DefaultTrackSelector(), new DefaultLoadControl());
playerView.setPlayer(player);
player.setPlayWhenReady(playWhenReady);
player.seekTo(currentWindow, playbackPosition);
}
在上面的代碼中顿仇,我們傳入了默認的渲染工廠(DefaultRenderersFactory)忽刽,默認的軌道選擇器(DefaultTrackSelector)和默認的加載控制器(DefaultLoadControl)天揖,然后把返回的播放器實例賦值給成員變量player。
- 創(chuàng)建一個MediaSource跪帝。
private void initializePlayer() {
[...]
//創(chuàng)建一個mp4媒體文件
Uri uri = Uri.parse(getString(R.string.media_url_mp4));
MediaSource mediaSource = buildMediaSource(uri);
player.prepare(mediaSource, true, false);
}
private MediaSource buildMediaSource(Uri uri) {
return new ExtractorMediaSource.Factory(
new DefaultHttpDataSourceFactory("exoplayer-codelab")).
createMediaSource(uri);
}
這時候就可以播放我們的媒體文件了今膊。上個圖。
完整的initializePlayer方法
private void initializePlayer() {
if (player==null){
player = ExoPlayerFactory.newSimpleInstance(
new DefaultRenderersFactory(this),
new DefaultTrackSelector(), new DefaultLoadControl());
playerView.setPlayer(player);
player.setPlayWhenReady(playWhenReady);
player.seekTo(currentWindow, playbackPosition);
}
Uri uri = Uri.parse(getString(R.string.media_url_mp4));
MediaSource mediaSource = buildMediaSource(uri);
player.prepare(mediaSource, false, true);
}
如果我們要播放一個音頻文件呢伞剑?我們只要在創(chuàng)建MediaSource的時候傳入一個音頻文件的路徑就可以了斑唬,其他的都交給PlayerView即可,真是很爽黎泣。
Uri uri = Uri.parse(getString(R.string.media_url_mp3));
另外必須注意恕刘,我們要在合適的時機釋放資源
private void releasePlayer() {
if (player != null) {
playbackPosition = player.getCurrentPosition();
currentWindow = player.getCurrentWindowIndex();
playWhenReady = player.getPlayWhenReady();
player.release();
player = null;
}
}
組合媒體資源
ExoPlayer庫提供了ConcatenatingMediaSource和DynamicConcatenatingMediaSource可以用來無縫的合并播放多個媒體資源。
使用ConcatenatingMediaSource
下面的方法構(gòu)建了一個音頻文件和視頻文件組合的媒體文件
private MediaSource buildMediaSource(Uri uri) {
DefaultHttpDataSourceFactory dataSourceFactory =
new DefaultHttpDataSourceFactory("user-agent");
ExtractorMediaSource videoSource =
new ExtractorMediaSource.Factory(dataSourceFactory).
createMediaSource(uri);
Uri audioUri = Uri.parse(getString(R.string.media_url_mp3));
ExtractorMediaSource audioSource =
new ExtractorMediaSource.Factory(dataSourceFactory).
createMediaSource(audioUri);
return new ConcatenatingMediaSource(audioSource, videoSource);
}
你可以點擊上一個和下一個按鈕來播放上一個或者下一個媒體文件抒倚。
自適應(yīng)流
ExtractorMediaSource適合常規(guī)的媒體文件(mp4褐着,webm,mkv等等)托呕。ExoPlayer也提供了自適應(yīng)流媒體文件的實現(xiàn)含蓉,像DASH(DashMediaSource), SmoothStreaming (SsMediaSource)和HLS(HlsMediaSource)。
簡而言之项郊,自適應(yīng)播放將視頻或者音頻文件切割成給定持續(xù)時間的多個塊馅扣。這些塊有不同的質(zhì)量(尺寸或者比特率)。視頻播放器可以根據(jù)當前可用的網(wǎng)絡(luò)帶寬選擇不同質(zhì)量的塊着降。例如開始播放的時候選擇低質(zhì)量的塊差油,可以更快的渲染第一幀,然后帶寬足夠的情況下任洞,第二塊可以選擇更高的質(zhì)量蓄喇。
自適應(yīng)軌道選擇
注意:英文是Adaptive track selection,不知道怎么翻譯才好交掏,我的理解是就是選擇上面所說的塊公罕,如果有大佬知道,還望不吝賜教耀销。
自適應(yīng)流的核心就是選擇最合適當前播放環(huán)境的軌道楼眷。讓我們啟用自適應(yīng)軌道選擇。
自適應(yīng)播放根據(jù)測量的下載速度來估計網(wǎng)絡(luò)帶寬熊尉。我們來定義一個DefaultBandwidthMeter常量罐柳。
private static final DefaultBandwidthMeter BANDWIDTH_METER =
new DefaultBandwidthMeter();
private void initializePlayer() {
if (player == null) {
// 用來創(chuàng)建AdaptiveVideoTrackSelection的工廠
TrackSelection.Factory adaptiveTrackSelectionFactory =
new AdaptiveTrackSelection.Factory(BANDWIDTH_METER);
player = ExoPlayerFactory.newSimpleInstance(
new DefaultRenderersFactory(this),
new DefaultTrackSelector(adaptiveTrackSelectionFactory),
new DefaultLoadControl());
[...]
}
DefaultTrackSelector用來選擇軌道,我們把AdaptiveTrackSelection.Factory傳入DefaultTrackSelector的構(gòu)造函數(shù)狰住,這樣DefaultTrackSelector就可以選擇自適應(yīng)的軌道了张吉。
創(chuàng)建一個自適應(yīng)的媒體資源
DASH是一個廣泛應(yīng)用的自適應(yīng)流格式。使用ExoPlayer播放DASH格式的媒體催植,需要創(chuàng)建一個自適應(yīng)媒體資源肮蛹。
private MediaSource buildMediaSource(Uri uri) {
DataSource.Factory manifestDataSourceFactory =
new DefaultHttpDataSourceFactory("ua");
DashChunkSource.Factory dashChunkSourceFactory =
new DefaultDashChunkSource.Factory(
new DefaultHttpDataSourceFactory("ua", BANDWIDTH_METER));
return new DashMediaSource.Factory(dashChunkSourceFactory,
manifestDataSourceFactory).createMediaSource(uri);
}
傳入一個DASH格式的uri
Uri uri = Uri.parse(getString(R.string.media_url_dash));
這樣就可以愉快的播放DASH格式的媒體文件了勺择。
監(jiān)聽ExoPlayer的事件
private ComponentListener componentListener;
private class ComponentListener extends Player.DefaultEventListener {
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
String stateString;
// actually playing media
if (playWhenReady && playbackState == Player.STATE_READY) {
Log.d(TAG, "onPlayerStateChanged: actually playing media");
}
switch (playbackState) {
case Player.STATE_IDLE:
stateString = "ExoPlayer.STATE_IDLE -";
break;
case Player.STATE_BUFFERING:
stateString = "ExoPlayer.STATE_BUFFERING -";
break;
case Player.STATE_READY:
stateString = "ExoPlayer.STATE_READY -";
break;
case Player.STATE_ENDED:
stateString = "ExoPlayer.STATE_ENDED -";
break;
default:
stateString = "UNKNOWN_STATE -";
break;
}
Log.d(TAG, "changed state to " + stateString + " playWhenReady: " + playWhenReady);
}
}
新建一個類ComponentListener繼承Player.DefaultEventListener然后覆蓋自己感興趣事件方法。
然后注冊監(jiān)聽
private void initializePlayer() {
if (player == null) {
[...]
player = ExoPlayerFactory.newSimpleInstance(
new DefaultRenderersFactory(this),
new DefaultTrackSelector(adaptiveTrackSelectionFactory),
new DefaultLoadControl());
//注冊監(jiān)聽
player.addListener(componentListener);
[...]
}
要記得在釋放資源的時候伦忠,也移除掉監(jiān)聽器省核。
private void releasePlayer() {
if (player != null) {
[...]
player.removeListener(componentListener);
player.release();
player = null;
}
}
使用VideoRendererEventListener and AudioRendererEventListener
我們可以自定義MyVideoRendererEventListener,MyAudioRendererEventListener分別實現(xiàn)上面兩個接口昆码。
private MyVideoRendererEventListener videoRendererEventListener;
private MyAudioRendererEventListener audioRendererEventListener;
private void initializePlayer() {
[...]
player.addListener(componentListener);
player.addVideoDebugListener(videoRendererEventListener);
player.addAudioDebugListener(audioRendererEventListener);
[...]
}
釋放資源的時候也一樣气忠,移除相應(yīng)的監(jiān)聽器。
自定義用戶界面
如果我們不設(shè)置的話赋咽,ExoPlayer 默認使用的播放控制界面是PlayerControlView
如果我們完全不想使用這個控制界面旧噪,可以在布局文件里面修改
<com.google.android.exoplayer2.ui.PlayerView
[...]
app:use_controller="false"/>
這樣控制界面就不顯示了。
自定義PlayerControlView的行為
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:show_timeout="10000"
app:fastforward_increment="30000"
app:rewind_increment="30000"/>
上面的例子中脓匿,快進和快退都改成了30秒淘钟。控制界面自動消失時間是10秒陪毡。
自定義PlayerControlView界面的外觀
- 你可以自定義控制界面米母,然后在布局文件里更改屬性
controller_layout_id
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:controller_layout_id="@layout/custom_playback_control"/>
PlayerControlView通過id來識別它使用的所有UI元素。當你自定義布局文件的時候缤骨,必須保持標準元素的id,例如@id/exo_play 或 @id/exo_pause尺借,以便讓PlayerControlView有機會找到它們绊起。
默認的PlayerControlView的控制界面是R.layout.exo_playback_control_view.xml
。你也可以直接從ExoPlayer庫中復(fù)制到app的res目錄下面燎斩,然后做相應(yīng)的更改即可虱歪。
參考鏈接: