好幾天沒寫博客了,處理了一點個人私事加上平時加班,基本上時間不充裕,上篇文章講了一下用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);
}
