2012-03-05
Tags: android
在Android上使用MediaPlayer播放影片有些要注意的事項,不然在播放時會出現一些預想不到的狀況。在播放當時如果想動態調整影片畫面大小,其實還蠻簡單的;但是不同的實作會導致結果差異很大,錯誤的實作會讓執行時的效能很差。
以下是這部份實作的程式碼與心得
相關程式碼如下
ChgMediaPlayerLayoutActivity.java
public class ChgMediaPlayerLayoutActivity extends Activity {chg_mediaplayer_layout.xml
private static final String LOG_TAG = ChgMediaPlayerLayoutActivity.class.getSimpleName();
private static final String VIDEO_FILE_PATH = "rtsp://....../video.3gp";
private MediaPlayer mediaPlayer;
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private Button playButton;
private Button pauseButton;
private Button stopButton;
private Button smallScreenButton;
private Button fullScreenButton;
private Button hiddenScreenButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chg_mediaplayer_layout);
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
playButton = (Button)findViewById(R.id.playButton);
pauseButton = (Button)findViewById(R.id.pauseButton);
stopButton = (Button)findViewById(R.id.stopButton);
smallScreenButton = (Button)findViewById(R.id.smallScreenButton);
fullScreenButton = (Button)findViewById(R.id.fullScreenButton);
hiddenScreenButton = (Button)findViewById(R.id.hiddenScreenButton);
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.e(LOG_TAG, "ErrorCode : " + what);
return false;
}
});
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
try {
Log.d(LOG_TAG, "onCompletion");
mediaPlayer.seekTo(0);
}
catch (Exception e) {
Log.e(LOG_TAG, e.getMessage(),e);
}
}
});
surfaceHolder = surfaceView.getHolder();
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
Log.d(LOG_TAG, "surfaceCreated");
mediaPlayer.reset();
//mediaPlayer.setDisplay(...)必需在callback的surfaceCreated(...) method裡叫用,否則會有聲音無影像
//也就是說mediaPlayer.setDisplay(...)必需在整個畫面render完成後才能叫用,否則會出現異常
mediaPlayer.setDisplay(surfaceHolder);
mediaPlayer.setDataSource(VIDEO_FILE_PATH);
mediaPlayer.prepare();
}
catch (Exception e) {
Log.e(LOG_TAG, e.getMessage(),e);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format,int width, int height) {
Log.d(LOG_TAG, "surfaceChanged");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(LOG_TAG, "surfaceDestroyed");
}
});
playButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(!mediaPlayer.isPlaying()){
mediaPlayer.start();
}
}
});
pauseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
}
}
});
stopButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
mediaPlayer.seekTo(0);
}
}
catch (Exception e) {
Log.e(LOG_TAG, e.getMessage(),e);
}
}
});
smallScreenButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LayoutParams layoutParams = surfaceView.getLayoutParams();
layoutParams.width = 300;
layoutParams.height = 200;
surfaceView.setLayoutParams(layoutParams);
}
});
fullScreenButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LayoutParams layoutParams = surfaceView.getLayoutParams();
layoutParams.width = LayoutParams.FILL_PARENT;
layoutParams.height = LayoutParams.FILL_PARENT;
surfaceView.setLayoutParams(layoutParams);
}
});
hiddenScreenButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LayoutParams layoutParams = surfaceView.getLayoutParams();
layoutParams.width = 0;
layoutParams.height = 0;
surfaceView.setLayoutParams(layoutParams);
}
});
}
@Override
protected void onStop() {
super.onStop();
if(mediaPlayer != null){
mediaPlayer.release();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" android:baselineAligned="false">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center_horizontal|center_vertical"
android:orientation="vertical" >
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/playButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/play" />
<Button
android:id="@+id/pauseButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/pause" />
<Button
android:id="@+id/stopButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/stop" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/smallScreenButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/smallScreen" />
<Button
android:id="@+id/fullScreenButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/fullScreen" />
<Button
android:id="@+id/hiddenScreenButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hiddenScreen" />
</LinearLayout>
</LinearLayout>
不要呼叫SurfaceView.setVisibility(View.INVISIBLE)
,這會導致SurfaceView從UI的ViewTree裡被移除;當你要再次顯示SurfaceView時,除了要重新產生新的SurfaceView之外,還要再跟MediaPlayer做一次完整的binding動作,執行的整度與效能都很糟。當Activity處於UI可見的狀態下,SurfaceView要想盡辦法讓它reuse,不要讓它進行重新生成instance的動作,這樣MediaPlayer的播放畫面在進行縮放大小時才會快速。