1. 程式人生 > >Android-VideoView視訊播放(視訊第一幀,迴圈播放,隱藏播放條、橫屏生命週期、快取問題)

Android-VideoView視訊播放(視訊第一幀,迴圈播放,隱藏播放條、橫屏生命週期、快取問題)

大綱

* VideoView播放網路視訊、播放本地視訊

* 實現邊播邊快取

* 獲取視訊的第一幀圖片作為封面

* 實現類似朋友圈小視訊迴圈播放

* 隱藏播放條

* 互動,點選螢幕退出播放

* 解決橫屏生命週期重新載入的問題

* 載入時進度條的顯示與消失監聽 progressbar

videoview原型:
videoView extends SurfaceView implements MediaController.MediaPlayerControl
其中SurfaceView 為顯示提供支援,MediaPlayerControl則為媒體控制提供了支援。
如果要構建更為複雜和有特色個性的視訊View,需要繼承SurfaceView 和實現MediaPlayerControl介面。

VideoView播放網路視訊、播放本地視訊

用videoview播放視訊是非常簡單的一件事,下面我放全程式碼,實現以下:
首先佈局檔案video_play_layout.xml中

    <VideoView
        android:id="@+id/videoPlayView"
        android:layout_width="match_parent"
        android:layout_height="240dp"
        android:layout_centerVertical="true" />

VideoPlayActivity.java中

public class VideoPlayActivity extends AppCompatActivity  {
    private VideoView videoView;
    private MediaController mc;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.video_play_layout);
        videoView = (VideoView) findViewById(R.id.videoPlayView);
        //設定視訊控制器,元件可以控制視訊的播放,暫停,快進,元件,不需要你實現
        mc = new MediaController(this);
        videoView.setMediaController(mc);
        String netPlayUrl="http://baobab.wdjcdn.com/145076769089714.mp4";
        Uri uri = Uri.parse(netPlayUrl);
        videoView.setVideoURI(uri);//設定視訊的播放地址,網路地址。播放網路視訊
        //播放本地視訊
        videoView.setVideoPath(Environment.getExternalStorageDirectory().getPath()+"video.mp4");
        videoView.requestFocus();//讓VideiView獲取焦點  
        videoView.start();//開始播放
     }
}

呼叫VideoView的如下兩個方法來載入指定的視訊
setVidePath(String path):載入路徑檔案代表的視訊
setVideoURI(Uri uri):載入uri所對應的視訊
⚠️:載入網路視訊的時候記得要在AndroidManifest.xml中加入許可權:
<uses-permission android:name="android.permission.INTERNET" />

實現邊播邊快取

視訊快取策略:
先載入,快取到本地再播放,這適用於微信朋友圈這種小視訊、若視訊過大,肯定不能用這種。
邊播放邊快取:對餘大多數,還是使用邊播邊快取的。

邊播邊快取策略:

  • 第一種方法:開兩個執行緒,一個去正常讓videoview請求播放,另一個執行緒去下載檔案,等到再次點選播放,此時如果視訊以及有快取,就播放本地快取。(但是這種相當於兩份網路請求,時間會變慢)
  • 第二種方法:通過代理的策略實現一箇中間層將我們的網路請求轉移到本地實現的代理伺服器上,這樣我們真正請求的資料就會被代理拿到,這樣代理一邊向本地寫入資料,一邊根據我們需要的資料看是讀網路資料還是讀本地快取資料再提供給我們,真正做到了資料的複用。
    對於videoview,github開源上有一個寫好的庫實現邊播邊快取
    地址:
    AndroidVideoCache
    AndroidVideoCache-視訊邊播放邊快取的代理策略

具體使用,是分簡單,

compile 'com.danikula:videocache:2.7.0' //在app/build.gradle中加入依賴,引第三方

CustomViewApp.java

public class CustomViewApp extends Application {
    //全域性初始化一個本地代理伺服器
    private HttpProxyCacheServer proxy;

    public static HttpProxyCacheServer getProxy(Context context) {
        CustomViewApp app = (CustomViewApp) context.getApplicationContext();
        return app.proxy == null ? (app.proxy = app.newProxy()) : app.proxy;
    }

    private HttpProxyCacheServer newProxy() {
        return new HttpProxyCacheServer(this);
    }

}
VideoPlayActivity.java中:
    private VideoView videoView;
    private MediaController mc;
    private String proxyUrl;
    private HttpProxyCacheServer proxy;
   @Override
    protected void onCreate(Bundle savedInstanceState) { 
       super.onCreate(savedInstanceState);
        setContentView(R.layout.video_play_layout);
        videoView = (VideoView) findViewById(R.id.videoPlayView);
        //設定視訊控制器,元件可以控制視訊的播放,暫停,快進,元件,不需要你實現
        mc = new MediaController(this);
        videoView.setMediaController(mc);
        String netPlayUrl="http://baobab.wdjcdn.com/145076769089714.mp4";
        proxy = CustomViewApp.getProxy(getApplicationContext());
        proxyUrl = proxy.getProxyUrl(netPlayUrl);
        videoView.setVideoPath(proxyUrl);//播放的是代理伺服器返回的url,已經進行了封裝處理
        videoView.requestFocus();//讓VideiView獲取焦點  
        videoView.start();//開始播放
}
....

AndroidManifest.xml中,因為application改變了,所以application標籤中,
android:name=".CustomViewApp" ⚠️

此時播放的時候,可以看到快取的進度條,明顯的邊播邊載入,在載入完完整的一次後,再次開啟就不會載入了,而是播放快取的視訊。具體原理想要深層探究可以看我上面的文章連結

獲取視訊的第一幀圖片作為封面

類似朋友圈,播放前我們是需要一個封面圖加一個按鈕,單擊按鈕跳轉到我們剛才寫的activity中,

* a .服務端提供url,直接提供一個方法獲得url,並使用glide庫將url轉換成圖片

Bitmap bitmap = null;
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
           try {

    //根據網路視訊的url獲取第一幀--親測可用。但是這個方法獲取本地視訊的第一幀,不可用,還沒找到方法解決。
            if (Build.VERSION.SDK_INT >= 14) {
                retriever.setDataSource(videoUrl, new HashMap<String, String>());
            } else {
                retriever.setDataSource(videoUrl);
            }
            //獲得第一幀圖片
            bitmap = retriever.getFrameAtTime();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } finally {
            retriever.release();
        }
        return bitmap;

實現類似朋友圈小視訊迴圈播放

videoview正常播放完畢會退出,如果做到迴圈播放,像朋友圈小視訊一樣?也是非常簡單的。

       //迴圈播放
        videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mediaPlayer) {
                proxyUrl = proxy.getProxyUrl(playUrl);
                videoView.setVideoPath(proxyUrl);
                videoView.start();
            }
        });

隱藏播放條

去掉 private MediaController mc; 遮蔽關於它的操作

互動,點選螢幕退出播放

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction()== MotionEvent.ACTION_UP){
            finish();
        }
        return true;
    }

解決橫屏生命週期重新載入, 視訊從頭播放的問題

在預設情況下當螢幕從豎屏變到橫屏時會觸發onConfigurationChanged事件,畫面會重新載入

解決:

  • a. 在manifest中設定該Activity的configChanges為
    android:configChanges=“screenSize|keyboardHidden|orientation”
  • b.過載onConfigurationChanged事件

解釋:

  • a.不設定Activity的android:configChanges時,切屏會重新呼叫各個生命週期,切橫屏時會執行一次,切豎屏時會執行兩次
  • b.設定Activity的android:configChanges=“orientation"時,切屏還是會重新呼叫各個生命週期,切橫、豎屏時只會執行一次
  • c.設定Activity的android:configChanges=“orientation|keyboardHidden"時,切屏不會重新呼叫各個生命週期,只會執行onConfigurationChanged方法
  • d.是由於google在android3.2中添加了screensize改變的通知,在轉屏的時候,不僅是orientation發生了改變,screensize同樣也發生了改變,所以要設定screenSize引數:
      只豎屏顯示的話(android:screenOrientation="portrait")
      只橫屏顯示的話(android:screenOrientation="landscape")

載入時進度條的顯示與消失 progressbar

視訊最初載入的時候,如果什麼處理都不做,網速又卡一點的話,會有幾秒的黑屏,所以我們總要在載入的時候做一點處理,增加使用者體驗吧:可以直接放一個progressbar,或是生動一點,我是自定義了一個view,一個封面圖加一個progressbar,載入的時候顯示,載入完最初的一段就可以取消了。

1

     videoView.setOnInfoListener(new MediaPlayer.OnInfoListener() {
            @Override
            public boolean onInfo(MediaPlayer mp, int what, int extra) {
               if (what==MediaPlayer.MEDIA_INFO_BUFFERING_START){
                   bufferingCoverView.setVisibility(View.VISIBLE);
               }
               else {
                   bufferingCoverView.setVisibility(View.GONE);
               }
               return true;
            }
        });
        //在視訊預處理完成後呼叫
        videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                bufferingCoverView.setVisibility(View.GONE);
            }
        });




來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。

遇到問題,記錄正確解決問題博文,轉給更多需要的人