[java] package cn.c; import java.io.File; 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.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.widget.LinearLayout; import android.widget.MediaController; import android.widget.MediaController.MediaPlayerControl; /** * 參考資料: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就建立起了關聯. * * MediaPlayer播放視頻步驟 * 1 設定MediaPlayer將要播放的視頻 * mMediaPlayer.setDataSource(path); * 2 設定MediaPlayer的視頻輸出介面 * mMediaPlayer.setDisplay(mSurfaceHolder); * 3 MediaPlayer解碼 * mMediaPlayer.prepareAsync()且結合 * public void onPrepared(MediaPlayer mediaPlayer) * 4 MediaPlayer開始播放 * mediaPlayer.start(); * * */ public class MainActivity extends Activity implements OnCompletionListener,OnErrorListener,OnInfoListener,OnPreparedListener, OnSeekCompleteListener,OnVideoSizeChangedListener,SurfaceHolder.Callback,MediaPlayerControl{ private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private MediaController mMediaController; 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 (Exception e) { e.printStackTrace(); finish(); } mMediaController=new MediaController(MainActivity.this); } @Override public boolean onTouchEvent(MotionEvent event) { if (mMediaController.isShowing()) { mMediaController.hide(); } else { mMediaController.show(); } return false; } //來自於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準備好之後才可以對 //MediaController進行設定和使用 mMediaController.setMediaPlayer(this); mMediaController.setAnchorView(findViewById(R.id.mainView)); mMediaController.setEnabled(true); mMediaController.show(); //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(); } //以下方法均來自MediaPlayerControl介面 public void start() { mMediaPlayer.start(); } public void pause() { if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); } } public int getDuration() { return mMediaPlayer.getDuration(); } public int getCurrentPosition() { return mMediaPlayer.getCurrentPosition(); } public void seekTo(int pos) { mMediaPlayer.seekTo(pos); } public boolean isPlaying() { return mMediaPlayer.isPlaying(); } public int getBufferPercentage() { // TODO Auto-generated method stub return 0; } public boolean canPause() { return true; } public boolean canSeekBackward() { return true; } public boolean canSeekForward() { return true; } }