ExoPlayer簡單使用

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消耗更多的電量吞歼。

使用

  1. 添加依賴
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)的資源睦擂。
  1. 在布局文件中加入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);
}
  1. 創(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。

  1. 創(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);
}

這時候就可以播放我們的媒體文件了今膊。上個圖。


11537585473_.pic_thumb.jpg

完整的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);
    }
6b5e3f6d1570358a.png

你可以點擊上一個和下一個按鈕來播放上一個或者下一個媒體文件抒倚。

自適應(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界面的外觀

  1. 你可以自定義控制界面米母,然后在布局文件里更改屬性 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)的更改即可虱歪。

參考鏈接:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市栅表,隨后出現(xiàn)的幾起案子笋鄙,更是在濱河造成了極大的恐慌,老刑警劉巖怪瓶,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萧落,死亡現(xiàn)場離奇詭異,居然都是意外死亡洗贰,警方通過查閱死者的電腦和手機找岖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敛滋,“玉大人许布,你說我怎么就攤上這事∫锘危” “怎么了蜜唾?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵杂曲,是天一觀的道長。 經(jīng)常有香客問我袁余,道長擎勘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任泌霍,我火速辦了婚禮货抄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘朱转。我一直安慰自己蟹地,他們只是感情好,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布藤为。 她就那樣靜靜地躺著怪与,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缅疟。 梳的紋絲不亂的頭發(fā)上分别,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天,我揣著相機與錄音存淫,去河邊找鬼耘斩。 笑死,一個胖子當著我的面吹牛桅咆,可吹牛的內(nèi)容都是我干的括授。 我是一名探鬼主播,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼岩饼,長吁一口氣:“原來是場噩夢啊……” “哼荚虚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起籍茧,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤版述,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后寞冯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渴析,經(jīng)...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年吮龄,在試婚紗的時候發(fā)現(xiàn)自己被綠了檬某。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡螟蝙,死狀恐怖恢恼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情胰默,我是刑警寧澤场斑,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布漓踢,位于F島的核電站,受9級特大地震影響漏隐,放射性物質(zhì)發(fā)生泄漏喧半。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一青责、第九天 我趴在偏房一處隱蔽的房頂上張望挺据。 院中可真熱鬧,春花似錦脖隶、人聲如沸扁耐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婉称。三九已至,卻和暖如春构蹬,著一層夾襖步出監(jiān)牢的瞬間王暗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工庄敛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留俗壹,地道東北人。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓藻烤,卻偏偏與公主長得像绷雏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子隐绵,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內(nèi)容