好幾天沒寫博客了,處理了一點個人私事加上平時加班,基本上時間不充裕,上篇文章講了一下用Mediaplayer來播放音樂,這次就講講使用Mediaplayer來和SurfaceView配合播放一個視頻流媒體。MediaPlayer不僅可以播放視頻,還可以與SurfaceView的配合,SurfaceView主要用於顯示MediaPlayer播放的視頻流媒體的畫面渲染,兩者可以一起協同播放視頻。
基礎維護
先來看下要實現的界面:
如果你看過上篇文章,就發現其實很簡單的就是多了一個進度條,還有一個就是SurfaceView,就是下面那塊黑色區域;
布局文件代碼:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.googlevideo.MainActivity" > <EditText android:id="@+id/edit_musicPath" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="輸入MV的路徑" /> <SeekBar android:id="@+id/seekBar_video" android:layout_width="match_parent" android:layout_height="wrap_content" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:id="@+id/btn_Play" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="playEvent" android:text="播放" /> <Button android:id="@+id/btn_Pause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="pauseEvent" android:text="暫停" /> <Button android:id="@+id/btn_Stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="stopEvent" android:text="停止" /> <Button android:id="@+id/btn_Replay" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="replayEvent" android:text="重播" /> </LinearLayout> <SurfaceView android:id="@+id/surface_video" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
Demo實現
實現Demo之前應該講講視頻播放的原理,先確定視頻的格式,這個和解碼相關,不同的格式視頻編碼不同,然后通過編碼格式進行解碼,最后得到一幀一幀的圖像,並把這些圖像快速的顯示在界面上,即為播放一段視頻。SurfaceView在Android中就是完成這個功能的。SurfaceView是配合MediaPlayer使用的,MediaPlayer也提供了相應的方法設置SurfaceView顯示圖片,只需要為MediaPlayer指定SurfaceView顯示圖像即可。它的完整簽名:void setDisplay(SurfaceHolder sh)。它需要傳遞一個SurfaceHolder對象,SurfaceHolder可以理解為SurfaceView裝載需要顯示的一幀幀圖像的容器,它可以通過SurfaceHolder.getHolder()方法獲得。使用MediaPlayer配合SurfaceView播放視頻的步驟與播放使用MediaPlayer播放MP3大體一致,只需要額外設置顯示的SurfaceView即可。
先准備一段能播放的視頻:
播放視頻效果圖:
代碼如下:
editText = (EditText) findViewById(R.id.edit_musicPath); pathString = editText.getText().toString().trim(); File file = new File(pathString); if (file.exists()) { try { mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(pathString); //通過SurfaceView獲取的Holder mediaPlayer.setDisplay(holder); mediaPlayer.prepare(); mediaPlayer.start(); btn_PlayButton.setEnabled(false); //設置Bar的最大值 int max=mediaPlayer.getDuration(); seekBarVideo.setMax(max); //定時器更新進度條 timer=new Timer(); timeTask=new TimerTask() { @Override public void run() { // TODO Auto-generated method stub seekBarVideo.setProgress(mediaPlayer.getCurrentPosition()); } }; timer.schedule(timeTask, 0, 500); //視頻播放完之后重新設置顯示 mediaPlayer.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { // TODO Auto-generated method stub btn_PlayButton.setEnabled(true); } }); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { Toast.makeText(this, "Sorry,你輸入的路徑有問題,請仔細檢查", Toast.LENGTH_SHORT) .show(); }
SeekBar的使用:
//設置Bar的最大值 int max=mediaPlayer.getDuration(); seekBarVideo.setMax(max); //定時器更新進度條 timer=new Timer(); timeTask=new TimerTask() { @Override public void run() { // TODO Auto-generated method stub seekBarVideo.setProgress(mediaPlayer.getCurrentPosition()); } }; timer.schedule(timeTask, 0, 500);
其中holder是SurfaceHolder,在onCreate中獲取:
surfaceView = (SurfaceView) findViewById(R.id.surface_video); holder = surfaceView.getHolder(); //兼容4.0以后的手機版本,本身是不維護的 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
如果正在播放視頻,最小化的時候是會有聲音的,需要在回調函數中處理一下:
holder.addCallback(new Callback() { @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub Log.i(tag, "銷毀了Holder"); if (mediaPlayer!=null&&mediaPlayer.isPlaying()) { currentPosition=mediaPlayer.getCurrentPosition(); mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer=null; timer.cancel(); timeTask.cancel(); timer=null; timeTask=null; } } @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub Log.i(tag,"創建了Holder"); if (currentPosition>0) { try { mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDataSource(pathString); //通過SurfaceView獲取的Holder mediaPlayer.setDisplay(holder); mediaPlayer.prepare(); mediaPlayer.start(); mediaPlayer.seekTo(currentPosition); btn_PlayButton.setEnabled(false); //視頻播放完之后重新設置顯示 mediaPlayer.setOnCompletionListener(new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { // TODO Auto-generated method stub btn_PlayButton.setEnabled(true); } }); int max=mediaPlayer.getDuration(); seekBarVideo.setMax(max); //定時器更新進度條 timer=new Timer(); timeTask=new TimerTask() { @Override public void run() { // TODO Auto-generated method stub seekBarVideo.setProgress(mediaPlayer.getCurrentPosition()); } }; timer.schedule(timeTask, 0, 500); } catch (Exception e) { // TODO: handle exception } } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub Log.i(tag,"改變了Holder"); } });
進度條是SeekBar,如果需要快進或者后退的時候是需要將焦點賦值給mediaPlayer的:
seekBarVideo=(SeekBar) findViewById(R.id.seekBar_video); seekBarVideo.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub int position=seekBar.getProgress(); if (mediaPlayer!=null&&mediaPlayer.isPlaying()) { mediaPlayer.seekTo(position); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // TODO Auto-generated method stub } });
暫停事件:
public void pauseEvent(View view) { if (btn_PauseButton.getText().equals("繼續")) { mediaPlayer.start(); btn_PauseButton.setText("暫停"); return; } if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); btn_PauseButton.setText("繼續"); } }
停止事件:
public void stopEvent(View view) { if (mediaPlayer != null && mediaPlayer.isPlaying()) { btn_PlayButton.setEnabled(true); mediaPlayer.stop(); // 釋放mediaplayer否則的話會占用內存 mediaPlayer.release(); mediaPlayer = null; } btn_PauseButton.setText("暫停"); btn_PlayButton.setEnabled(true); }
重播事件:
public void replayEvent(View view) { surfaceView.setVisibility(View.VISIBLE); if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.seekTo(0); } else { playEvent(view); } // 重播的時候應該設置播放的狀態 btn_PlayButton.setEnabled(true); }