Android應用開發之視頻播放器

資源:

ImageButton所用圖片4張

Strings:
<string name="app_name">MyVideoPlayer</string>
    <string name="video_text">視頻文件</string>
    <string name="notfoundsdcard_error">找不到Sd卡</string>
    <string name="notfoundfile_error">找不到視頻文件</string>

佈局


<?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:background="#FFFFCC"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/video_text" />

    <EditText
        android:id="@+id/videoFileEt"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="cat.3gp" />

    <TableLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:stretchColumns="*" >

        <TableRow >

            <ImageButton
                android:id="@+id/playBtn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/play" />

            <ImageButton
                android:id="@+id/pauseBtn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/pause" />

            <ImageButton
                android:id="@+id/resetBtn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/reset" />

            <ImageButton
                android:id="@+id/stopBtn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/stop" />
        </TableRow>
    </TableLayout>

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="fill_parent"
        android:layout_height="240dip" />

</LinearLayout>

Activity

package cn.class3g.videoplayer;

import java.io.File;
import java.io.IOException;

import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.Toast;

public class VideoPlayerActivity extends Activity implements OnClickListener {

	private static final String TAG = "VideoPlayerActivity";
	private ImageButton playBtn, pauseBtn, resetBtn, stopBtn;
	private EditText fileEt;
	private SurfaceView videoSv;

	private MediaPlayer mediaPlayer;

	private SurfaceHolder holder;
	private int position = 0;
	File videoFile = null;
	

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		findViews();

		mediaPlayer = new MediaPlayer();
	}

	private void findViews() {
		fileEt = (EditText) this.findViewById(R.id.videoFileEt);
		playBtn = (ImageButton) this.findViewById(R.id.playBtn);
		pauseBtn = (ImageButton) this.findViewById(R.id.pauseBtn);
		resetBtn = (ImageButton) this.findViewById(R.id.resetBtn);
		stopBtn = (ImageButton) this.findViewById(R.id.stopBtn);

		playBtn.setOnClickListener(this);
		pauseBtn.setOnClickListener(this);
		resetBtn.setOnClickListener(this);
		stopBtn.setOnClickListener(this);

		videoSv = (SurfaceView) this.findViewById(R.id.surfaceView);
		holder = videoSv.getHolder();

		holder.setFixedSize(176, 144); // 設置分辨率
		/* 下面設置Surface不維護自己的緩衝區,而是等待屏幕的渲染引擎將內容推送到用戶面前 */
		holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		//holder.addCallback(new SurfaceCallback());
	}

	public void onClick(View v) {

		if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) {
			String fileName = fileEt.getText().toString().trim();

			videoFile = new File(
					Environment.getExternalStorageDirectory(), fileName);
			if (videoFile.exists()) {
				try {
					switch (v.getId()) {
					case R.id.playBtn:
						playVideo(videoFile);
						break;
					case R.id.pauseBtn:
//						if(mediaPlayer.isPlaying()){
//							mediaPlayer.pause();
//						}else{
//							mediaPlayer.start();
//						}
						if(mediaPlayer.isPlaying()){
							position = mediaPlayer.getCurrentPosition();
							mediaPlayer.pause();
						}
						
						break;
					case R.id.resetBtn:
						if(mediaPlayer.isPlaying()){
							mediaPlayer.seekTo(0);
						}else{
							playVideo(videoFile);
						}
						break;
					case R.id.stopBtn:
						if(mediaPlayer.isPlaying()){
							mediaPlayer.stop();
						}
						break;
					}
				} catch (Exception e) {
					Log.e(TAG, e.toString());
				}

			} else {
				showToast(R.string.notfoundfile_error);
			}
		} else {
			showToast(R.string.notfoundsdcard_error);
		}
	}

	private void playVideo(File videoFile) throws IOException {
		mediaPlayer.reset();
		mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
		mediaPlayer.setDataSource(videoFile.getAbsolutePath());
		mediaPlayer.setDisplay(holder);
		mediaPlayer.prepare();
		mediaPlayer.start();
	}

	private void showToast(int resId) {
		Toast.makeText(this, resId, 1).show();
	}
}

解決來電顯示問題

protected void onDestroy() {
		
		if(mediaPlayer != null){
			if(mediaPlayer.isPlaying()){
				mediaPlayer.stop();
			}
			mediaPlayer.release();
		}
		super.onDestroy();
	}

	protected void onPause() {
		if(mediaPlayer != null && !mediaPlayer.isPlaying()){
			position = mediaPlayer.getCurrentPosition();
			mediaPlayer.stop();
		}
		super.onPause();
	}

	protected void onResume() {
		if(position >0 && videoFile != null){
			try {
				playVideo(videoFile);
				Log.i(TAG,"position="+ position);
				mediaPlayer.seekTo(position);
				position = 0;
			} catch (IOException e) {
				Log.e(TAG,e.toString());
			}
			
		}
		
		super.onResume();
	}

問題:

       來電後返回後,只有聲音,沒有畫面

原因:

       當前activity對象被遮擋後,SurfaceView對象被銷燬,而activity重新顯示時他的重建晚於onResume()方法的執行,所以此時只有聲音而沒有圖象

 

 

解決SurfaceView的重建問題


取消onPause和onResume,由下面方法代替

private final class SurfaceCallback implements Callback{

		public void surfaceCreated(SurfaceHolder holder) {
			if(position >0 && videoFile != null){
				try {
					playVideo(videoFile);
					Log.i(TAG,"position="+ position);
					mediaPlayer.seekTo(position);
					position = 0;
				} catch (IOException e) {
					Log.e(TAG,e.toString());
				}
			}
			Log.i(TAG, "surfaceCreated");
		}

		public void surfaceChanged(SurfaceHolder holder, int format, int width,
				int height) {
			// TODO Auto-generated method stub
			
		}

		public void surfaceDestroyed(SurfaceHolder holder) {
			if(mediaPlayer != null && mediaPlayer.isPlaying()){
				position = mediaPlayer.getCurrentPosition();
				mediaPlayer.stop();
			}
			Log.i(TAG, "surfaceDestroyed");
		}
		
	}

爲SurfaceView對象添加Callback對象

holder.addCallback(new SurfaceCallback());

問題:

       應用程序如處於後臺,且系統需要內存時會把應用殺死,此時回到播放器界面,需要重新調用onCreate()方法,所以無法繼續之前的播放。

 

解決內存不足時應用被殺死問題:

protected void onRestoreInstanceState(Bundle savedInstanceState) {
		position = savedInstanceState.getInt("position");
		String fn = savedInstanceState.getString("fileName");
		if(fn!=null){
			videoFile = new File(Environment.getExternalStorageDirectory(),fn);
		}
			
		super.onRestoreInstanceState(savedInstanceState);
	}

	protected void onSaveInstanceState(Bundle outState) {
		outState.putInt("position", position);
		outState.putString("fileName", videoFile.getName());
		super.onSaveInstanceState(outState);

注意:

1.        在2.1和2.2版本的模擬器中視頻播放有bug,真機沒事兒。

2.        有些視頻文件,不能正常的調整播放位置(估計跟格式轉換時索引丟失有關)。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章