天天看點

Exoplayer的簡單實作-surfaceView

最近一直在弄這個Exoplayer,官方的實在是太全面了、在GitHub庫中下來的demo在我這跑不起來、實在是讓我好憂傷。官方demo的PlayerActivity寫的很全面,但原諒我有些分不清它們的具體功能,是以我删除掉好多、八百多行的代碼最後隻剩下接近三百多行、、、

首先、來一段官方的描述:

ExoPlayer是Android應用程式級别的媒體播放器。

它提供了播放音頻和視訊本地和在網際網路上Android的MediaPlayer的API的替代品。

ExoPlayer不支援目前Android的MediaPlayer的API,包括儀表闆和SmoothStreaming自适應播放支援的功能。

不同的是MediaPlayer的API,ExoPlayer容易定制和擴充,并且可以通過Play商店應用程式更新進行更新。

文檔

該開發人員指南提供了豐富的資訊,以幫助您開始。

本類引用記錄了ExoPlayer庫類。

項目首頁-項目首頁

GitHub的項目 -包含源代碼,還有一個示範應用程式。

使用ExoPlayer

通過jCenter

最簡單的方式開始使用ExoPlayer是通過在項目的build.gradle檔案:

編譯“com.google.android.exoplayer:exoplayer:rX.XX” 其中, rX.XX是您的首選版本。

有關最新版本,請參閱該項目的釋出 。 有關詳細資訊,請參閱該項目Bintray 。

由于library

ExoPlayer也可以從源使用Gradle建構的。 你可以把它作為像這樣一個相關項目:

// settings.gradle

include ‘:app’, ‘:..:ExoPlayer:library’

// app/build.gradle dependencies {

compile project(‘:..:ExoPlayer:library’) }

如果你想使用ExoPlayer作為 jar 運作:

./gradlew jarRelease 并複制library.jar到新項目的libs檔案夾。

我使用的是第一個方法,即在項目的build.gradle檔案中編譯“com.google.android.exoplayer:exoplayer:rX.XX”

Exoplayer的簡單實作-surfaceView

完成後在項目的External Libraries中:

Exoplayer的簡單實作-surfaceView

然後在官方的demo中複制了一下檔案:

Exoplayer的簡單實作-surfaceView

ExtractorSampleSource -對于格式,如FMP4,MP4,M4A,MKV,WebM功能,支援MP3,AAC,MPEG-TS,MPEG-PS,OGG,FLV和WAV。

HlsSampleSource - HLS 播放

它們的用途可以看 開發者指南、、、

在我的OnePlayerActivity實作了一個視訊的播放(包括全屏播放)

contentUri 是視訊的uri播放位址

contentType 是流媒體傳輸協定類型,可以支援DASH和HLS以及OTHER類型

//擷取視訊資料
    private void onShown() {
        contentUri = Uri.parse("http://devimages.apple.com/samplecode/adDemo/ad.m3u8");
        //contentUri = Uri.parse("http://html5demos.com/assets/dizzy.mp4");
        //inferContentType根據uri的字尾判斷
        contentType = inferContentType(contentUri);
        Log.e("TAG", "contentUri" + contentUri + "contentType" + contentType);
        if (player == null) {
            //if (!maybeRequestPermission()) { 檢查權限,6.0以上可以動态擷取權限
            preparePlayer(true);
            //}
        } else {
            player.setBackgrounded(false);
        }
    }
           

初始化準備player,addListener添加監聽,如播放的狀态onStateChanged,視訊大小變化onVideoSizeChanged, 出現錯誤onError,

private void preparePlayer(boolean playWhenReady) {
        if (player == null) {
            player = new DemoPlayer(getRendererBuilder());
            player.addListener(this);
            player.seekTo(playerPosition);//播放進度的設定 一開始為0
            playerNeedsPrepare = true; //是否立即播放
        }
        if (playerNeedsPrepare) {
            player.prepare();
            playerNeedsPrepare = false;
        }
        player.setSurface(surfaceView.getHolder().getSurface());
        player.setPlayWhenReady(playWhenReady);

    }
           

官方的PlayerActivity中使用的媒體控制器使用的是goole的MediaController,好用,但沒有全屏、、、

//初始化對象以及依附父布局root,View
 mediaController = new KeyCompatibleMediaController(this);
 mediaController.setAnchorView(root);
 ...
 //自定義KeyCompatibleMediaController 媒體控制類,繼承于MediaController 
 private static final class KeyCompatibleMediaController extends MediaController {
    private MediaController.MediaPlayerControl playerControl;
    public KeyCompatibleMediaController(Context context) {
      super(context);
    }
    @Override
    public void setMediaPlayer(MediaController.MediaPlayerControl playerControl) {
      super.setMediaPlayer(playerControl);
      this.playerControl = playerControl;
    }
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
      int keyCode = event.getKeyCode();
      //這是前進和後退按鈕、、、
      if (playerControl.canSeekForward() && (keyCode == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD
          || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT)) {
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
          playerControl.seekTo(playerControl.getCurrentPosition() + ); // milliseconds
          show();
        }
        return true;
      } else if (playerControl.canSeekBackward() && (keyCode == KeyEvent.KEYCODE_MEDIA_REWIND
          || keyCode == KeyEvent.KEYCODE_DPAD_LEFT)) {
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
          playerControl.seekTo(playerControl.getCurrentPosition() - ); // milliseconds
          show();
        }
        return true;
      }
      return super.dispatchKeyEvent(event);
    } 
...
//關聯player
mediaController.setMediaPlayer(player.getPlayerControl());
mediaController.setEnabled(true);
           

但在我的OnePlayerActivity中沒有使用這個,而是自己寫的,當然布局、圖檔什麼的要自己提供了

@Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.py://播放鍵
                if(player.getPlayWhenReady()){
                    py.setBackground(getResources().getDrawable(R.drawable.pause));
                    player.setPlayWhenReady(false);
                }else {
                    py.setBackground(getResources().getDrawable(R.drawable.player));
                    player.setPlayWhenReady(true);
                }
                break;
            case R.id.fs://全屏鍵
                if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {//橫屏
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    fs.setBackground(getResources().getDrawable(R.drawable.full));
                } else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){//豎屏
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    fs.setBackground(getResources().getDrawable(R.drawable.lessen));
                }
                break;
        }
    }
           

SeekBar實作監聽後,使用Player的seekTo來控制視訊的播放進度、

用seekBar.setProgress(msg.arg1);來設定seekbar的進度,

在視訊狀态監聽中的開始播放時可以擷取到視訊的長度,Player.getDuration();(也可以擷取到視訊的寬高等)

seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {//放開
                     player.seekTo(seekBar.getProgress());
            }
        });
           

使用SurfaceView在進入這個activity時會出現一會兒的黑屏,這是因為SurfaceView因為不同于一般的view,它有自己良好的緩沖以及資料存取機制,系統對他有特殊處理。當surfaceview第一次在目前activity上添加的時候,系統會給WindowManager重新排布局,relayout,這樣就會黑一下,這個隻會出現在第一次,以後再添加surfaceview時就不會黑屏了。

生命周期及調用流程:

Exoplayer的簡單實作-surfaceView

這裡需要注意的一點是在調用Exoplayer的playWhenReady接口時需要等到surfaceView的surfaceCreated後才能調用, 否則會失敗.

最後,感謝您的閱讀、、、

MyExoplayer:http://download.csdn.net/detail/zp0203/9600650 隻有一個視訊播放的,listView 中播放的還未實作、、、、