[java]
package cn.c;
import java.io.File;
import java.io.IOException;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnSeekCompleteListener;
import android.media.MediaPlayer.OnVideoSizeChangedListener;
import android.os.Bundle;
import android.os.Environment;
import android.view.Display;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.LinearLayout;
/**
* 利用MidiaPlayer播放視頻
* 涉及到SurfaceView和SurfaceHolder
* MediaPlayer主要用於播放音頻,它沒有提供輸出圖像的輸出界面
* 此時我們可用SurfaceView控件將它與MediaPlayer結合起來
* 就能達到視頻的輸出.如果說SurfaceView是用來繪制(顯示)視頻的
* 那麼SurfaceHolder就是用來控制SurfaceView的
* 流程:
* 1 設置MediaPlayer將要播放的視頻
* mMediaPlayer.setDataSource(path);
* 2 設置MediaPlayer的視頻輸出界面
* mMediaPlayer.setDisplay(mSurfaceHolder);
* 3 MediaPlayer進行解碼工作
* mMediaPlayer.prepareAsync();
* 4 MediaPlayer開始播放
* mMediaPlayer.start();
*/
/**
* 參考資料:http://www.embedu.org/Column/Column503.htm
* 利用MidiaPlayer播放視頻大致的流程:
* 創建播放器對象-->設置需要播放的視頻的來源-->
* 對視頻進行解碼等准備工作-->開始播放-->(暫停/重啟)停止播放
* 對應這些流程,在MediaPlayer中都定義了相關方法;
* 而且,在調用這些方法後,MediaPlayer可以進入不同的狀態.
* 這些方法和狀態都很好理解,讀者可以參照上圖來了解.
* 另外,因為在設置播放的視頻的視頻數據源之後,它需要對其進行解碼(調用prepare()方法)
* 這是一個比較耗時的操作,為了避免應用程序阻止,
* 可以使用它的prepareAsync()方法配合OnPreparedListener監聽器來讓其異步操作.
* 注意,只有MediaPlayer的准備工作做完後,才能開始播放
* MediaPlayer本身不能顯示視頻內容,它只能控制視頻的播放
* 如果要使用MediaPlayer來播放視頻,還需要配合SurfaceView來顯示視頻
* 且將MediaPlayer中解碼的視頻數據在SurfaceView中播放,需要調用MediaPlayer中的setDisplay()
* 它接受一個SurfaceHolder對象,可將我們用於
* 播放視頻的SurfaceView對應的SurfaceHolder對象作為參數
* 這樣,MediaPlayer和SurfaceView就建立起了關聯.
*/
public class MainActivity extends Activity implements
OnCompletionListener,OnErrorListener,OnInfoListener,OnPreparedListener,
OnSeekCompleteListener,OnVideoSizeChangedListener,SurfaceHolder.Callback{
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private Display mDisplay;
private MediaPlayer mMediaPlayer;
int videoWidth=0;
int videoHeight=0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mDisplay=getWindowManager().getDefaultDisplay();
mSurfaceView=(SurfaceView) findViewById(R.id.surfaceView);
mSurfaceHolder=mSurfaceView.getHolder();
//SurfaceHolder是用於管理SurfaceView對象
//那它是怎麼做到管理的呢?即用mSurfaceHolder
//添加回調即addCallback.
//該類實現了SurfaceHolder.Callback接口,所以addCallback(this)
//在SurfaceHolder.Callback接口中一共有三個方法來實現對於
//SurfaceView對象的管理,即:
//surfaceCreated()
//surfaceChanged()
//surfaceDestroyed()
//概況:SurfaceHolder主要作用是監控底層的情況
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mMediaPlayer=new MediaPlayer();
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnErrorListener(this);
mMediaPlayer.setOnInfoListener(this);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnSeekCompleteListener(this);
mMediaPlayer.setOnVideoSizeChangedListener(this);
String path=Environment.getExternalStorageDirectory().getPath()
+File.separator+"Test_Movie.m4v";
try {
//設置MediaPlayer將要播放的視頻
mMediaPlayer.setDataSource(path);
} catch (IllegalArgumentException e) {
e.printStackTrace();
finish();
} catch (IllegalStateException e) {
e.printStackTrace();
finish();
} catch (IOException e) {
e.printStackTrace();
finish();
}
}
//來自於SurfaceHolder.Callback接口
//創建SurfaceView完成時,會調用該方法
public void surfaceCreated(SurfaceHolder holder) {
//設置MediaPlayer的視頻輸出界面
//只顯示音頻而不顯示視頻時可不調用這個方法
mMediaPlayer.setDisplay(mSurfaceHolder);
try {
//准備播放,調用mMediaPlayer.prepareAsync();
//方法後會執行覆寫後的
//public void onPrepared(MediaPlayer mp)
//注意:
//也可調用prepare()解碼,但為一個同步的操作
mMediaPlayer.prepareAsync();
} catch (Exception e) {
e.printStackTrace();
finish();
}
}
//來自於SurfaceHolder.Callback接口
//當SurfaceView的寬度,高度或其他參數發生變化時,會調用該方法
public void surfaceChanged(SurfaceHolder holder,
int format, int width,int height) {
}
//來自於SurfaceHolder.Callback接口
//當SurfaceView摧毀時,會調用該方法
public void surfaceDestroyed(SurfaceHolder holder) {
}
//來自於MediaPlayer.OnVideoSizeChangedListener接口
//當視頻的寬度或高度發生變化時調用該方法
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
}
//來自於MediaPlayer.OnSeekCompleteListener接口
public void onSeekComplete(MediaPlayer mp) {
}
//來自於MediaPlayer.OnPreparedListener接口
//一旦調用該方法,MediaPlayer就進入了"准備就緒"
//狀態,准備開始播放.
//此處可用於動態設置SurfaceView的寬和高!!!
public void onPrepared(MediaPlayer mediaPlayer) {
videoWidth=mediaPlayer.getVideoWidth();
videoHeight=mediaPlayer.getVideoHeight();
if (videoWidth > mDisplay.getWidth()|| videoHeight > mDisplay.getHeight()) {
float heightRatio = (float) videoHeight/ (float) mDisplay.getHeight();
float widthRatio = (float) videoWidth/ (float) mDisplay.getWidth();
if (heightRatio > 1 || widthRatio > 1) {
if (heightRatio > widthRatio) {
videoHeight = (int) Math.ceil((float) videoHeight/ (float) heightRatio);
videoWidth = (int) Math.ceil((float) videoWidth/ (float) heightRatio);
} else {
videoHeight = (int) Math.ceil((float) videoHeight/ (float) widthRatio);
videoWidth = (int) Math.ceil((float) videoWidth/ (float) widthRatio);
}
}
}
//設置SurfaceView的寬和高
mSurfaceView.setLayoutParams
(new LinearLayout.LayoutParams(videoWidth,videoHeight));
//MediaPlayer開始播放
mediaPlayer.start();
}
//來自於MediaPlayer.OnInfoListener接口
//當出現關於播放媒體的特定信息或者需要發出警告的時候
//將調用該方法
//比如開始緩沖、緩沖結束、下載速度變化(該行待驗證)
//小結:這些Info都是以MediaPlayer.MEDIA_INFO_開頭的
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (what==MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING) {
//音頻和視頻數據不正確地交錯時將出現該提示信息.在一個
//正確交錯的媒體文件中,音頻和視頻樣本將依序排列,從而
//使得播放可以有效和平穩地進行
}
if (what==MediaPlayer.MEDIA_INFO_NOT_SEEKABLE) {
//當媒體不能正確定位時將出現該提示信息.
//此時意味著它可能是一個在線流
}
if (what==MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING) {
//當設備無法播放視頻時將出現該提示信息
//比如視頻太復雜或者碼率過高
}
if (what==MediaPlayer.MEDIA_INFO_METADATA_UPDATE) {
//當新的元數據可用時將出現該提示信息
}
if (what==MediaPlayer.MEDIA_INFO_UNKNOWN) {
//其余不可知提示信息
}
return false;
}
//來自於MediaPlayer.OnErrorListener接口
//MediaPlayer發生錯誤時會調用該方法
//只有如下這三種錯誤.
//小結:這些錯誤都是以MediaPlayer.MEDIA_ERROR.開頭的
public boolean onError(MediaPlayer mp, int what, int extra) {
if (what==MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
System.out.println("第一種錯誤");
}
if (what==MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
System.out.println("第二種錯誤");
}
if (what==MediaPlayer.MEDIA_ERROR_UNKNOWN) {
System.out.println("第三種錯誤");
}
return false;
}
//來自於MediaPlayer.OnCompletionListener接口
//當MediaPlayer播放完文件時,會調用該方法.
//此時可以進行一些其他的操作比如:播放下一個視頻
public void onCompletion(MediaPlayer mp) {
finish();
}
}