【Android基礎】多媒體編程部分總結,android多媒體
一、MediaPlayer的使用
1)如何獲得MediaPlayer執行個體:
可以使用直接new的方式:
MediaPlayer mp = new MediaPlayer();
也可以使用create的方式,如:
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);//這時就不用調用setDataSource了
2) 如何設定要播放的檔案:
MediaPlayer要播放的檔案主要包括3個來源:
a. 使用者在應用中事先內建的resource資源
例如:MediaPlayer.create(this, R.raw.test);
b. 儲存在SD卡或其他檔案路徑下的媒體檔案
例如:mp.setDataSource("/sdcard/test.mp3");
c. 網路上的媒體檔案
例如:mp.setDataSource("http://www.citynorth.cn/music/confucius.mp3");
MediaPlayer的setDataSource一共四個方法:
setDataSource (String path)
setDataSource (FileDescriptor fd)
setDataSource (Context context, Uri uri)
setDataSource (FileDescriptor fd, long offset, long length)
其中使用FileDescriptor時,需要將檔案放到與res檔案夾平級的assets檔案夾裡,然後使用:
AssetFileDescriptor fileDescriptor = getAssets().openFd("rain.mp3");
m_mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(), fileDescriptor.getLength());
來設定datasource
3)對播放器的主要控制方法:
Android通過控制播放器的狀態的方式來控制媒體檔案的播放,其中:
prepare()和prepareAsync() 提供了同步和非同步兩種方式設定播放器進入prepare狀態,需要注意的是,如果MediaPlayer執行個體是由create方法建立的,那麼第一次啟動播放前不需要再調用prepare()了,因為create方法裡已經調用過了。
start()是真正開機檔案播放的方法,
pause()和stop()比較簡單,起到暫停和停止播放的作用,
seekTo()是定位方法,可以讓播放器從指定的位置開始播放,需要注意的是該方法是個非同步方法呼叫,也就是說該方法返回時並不意味著定位完成,尤其是播放的網路檔案,真正定位完成時會觸發OnSeekComplete.onSeekComplete(),如果需要是可以調用setOnSeekCompleteListener(OnSeekCompleteListener)設定監聽器來處理的。
release()可以釋放播放器佔用的資源,一旦確定不再使用播放器時應當儘早調用它釋放資源。
reset()可以使播放器從Error狀態中恢複過來,重新會到Idle狀態。
4)設定播放器的監聽器:
MediaPlayer提供了一些設定不同監聽器的方法來更好地對播放器的工作狀態進行監聽,以期及時處理各種情況,
如: setOnCompletionListener(MediaPlayer.OnCompletionListener listener)、
setOnErrorListener(MediaPlayer.OnErrorListener listener)等,設定播放器時需要考慮到播放器可能出現的情況設定好監聽和處理邏輯,以保持播放器的健壯性。
二、Android中的soundpool小結
可以用soundpool,用soundpool可以播一些短的反應速度要求高的聲音,
比如遊戲中的爆破聲,而mediaplayer適合播放長點的。
1. SoundPool載入音樂檔案使用了獨立的線程,不會阻塞UI主線程的操作。但是這裡如果音效檔案過大沒有載入完成,我們調用play方法時可能產生嚴重的後果,這裡Android SDK提供了一個SoundPool.OnLoadCompleteListener類來協助我們瞭解媒體檔案是否載入完成,我們重載 onLoadComplete(SoundPool soundPool, int sampleId, int status) 方法即可獲得。
2. 從上面的onLoadComplete方法可以看出該類有很多參數,比如類似id,是的SoundPool在load時可以處理多個媒體一次初始化並放入記憶體中,這裡效率比MediaPlayer高了很多。
3. SoundPool類支援同時播放多個音效,這對於遊戲來說是十分必要的,而MediaPlayer類是同步執行的只能一個檔案一個檔案的播放。
使用方法:
1. 建立一個SoundPool
public SoundPool(int maxStream, int streamType, int srcQuality)
maxStream —— 同時播放的流的最大數量
streamType —— 流的類型,一般為STREAM_MUSIC(具體在AudioManager類中列出)
srcQuality —— 採樣率轉化品質,當前無效果,使用0作為預設值
eg.
SoundPool soundPool = new SoundPool(3, AudioManager.STREAM_MUSIC, 0);
建立了一個最多支援3個流同時播放的,類型標記為音樂的SoundPool。
2 一般把多個聲音放到HashMap中去,比如
soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);
soundPoolMap = new HashMap<Integer, Integer>();
soundPoolMap.put(1, soundPool.load(this, R.raw.dingdong, 1));
soundpool的載入:
int load(Context context, int resId, int priority) //從APK資源載入
int load(FileDescriptor fd, long offset, long length, int priority) //從FileDescriptor對象載入
int load(AssetFileDescriptor afd, int priority) //從Asset對象載入
int load(String path, int priority) //從完整檔案路徑名載入
最後一個參數為優先順序。
3 播放
play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate) ,其中leftVolume和rightVolume表示左右音量,priority表示優先順序,loop表示迴圈次數,rate表示速率,如
//速率最低0.5最高為2,1代表正常速度
sp.play(soundId, 1, 1, 0, 0, 1);
而停止則可以使用 pause(int streamID) 方法,這裡的streamID和soundID均在構造SoundPool類的第一個參數中指明了總數量,而id從0開始。
三、Android三種播放視頻的方式
1、使用其內建的播放器。指定Action為ACTION_VIEW,Data為Uri,Type為其MIME類型。
2、使用VideoView來播放。在布局檔案中使用VideoView結合MediaController來實現對其控制。
3、使用MediaPlayer類和SurfaceView來實現,這種方式很靈活。
1、調用其內建的播放器
<span style="font-size:18px;">Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v"); //調用系統內建的播放器 Intent intent = new Intent(Intent.ACTION_VIEW); Log.v("URI:::::::::", uri.toString()); intent.setDataAndType(uri, "video/mp4"); startActivity(intent);</span>
2、使用VideoView來實現:
<span style="font-size:18px;">Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v"); VideoView videoView = (VideoView)this.findViewById(R.id.video_view); videoView.setMediaController(new MediaController(this)); videoView.setVideoURI(uri); videoView.start(); videoView.requestFocus();</span>
3、使用MediaPlayer:
<span style="font-size:18px;">public class VideoSurfaceDemo extends Activity implements OnCompletionListener,OnErrorListener,OnInfoListener, OnPreparedListener, OnSeekCompleteListener,OnVideoSizeChangedListener,SurfaceHolder.Callback{ private Display currDisplay; private SurfaceView surfaceView; private SurfaceHolder holder; private MediaPlayer player; private int vWidth,vHeight; //private boolean readyToPlay = false; public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); this.setContentView(R.layout.video_surface); surfaceView = (SurfaceView)this.findViewById(R.id.video_surface); //給SurfaceView添加CallBack監聽 holder = surfaceView.getHolder(); holder.addCallback(this); //為了可以播放視頻或者使用Camera預覽,我們需要指定其Buffer類型 holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); //下面開始執行個體化MediaPlayer對象 player = new MediaPlayer(); player.setOnCompletionListener(this); player.setOnErrorListener(this); player.setOnInfoListener(this); player.setOnPreparedListener(this); player.setOnSeekCompleteListener(this); player.setOnVideoSizeChangedListener(this); Log.v("Begin:::", "surfaceDestroyed called"); //然後指定需要播放檔案的路徑,初始化MediaPlayer String dataPath = Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v"; try { player.setDataSource(dataPath); Log.v("Next:::", "surfaceDestroyed called"); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //然後,我們取得當前Display對象 currDisplay = this.getWindowManager().getDefaultDisplay(); } @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { // 當Surface尺寸等參數改變時觸發 Log.v("Surface Change:::", "surfaceChanged called"); } @Override public void surfaceCreated(SurfaceHolder holder) { // 當SurfaceView中的Surface被建立的時候被調用 //在這裡我們指定MediaPlayer在當前的Surface中進行播放 player.setDisplay(holder); //在指定了MediaPlayer播放的容器後,我們就可以使用prepare或者prepareAsync來準備播放了 player.prepareAsync(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.v("Surface Destory:::", "surfaceDestroyed called"); } @Override public void onVideoSizeChanged(MediaPlayer arg0, int arg1, int arg2) { // 當video大小改變時觸發 //這個方法在設定player的source後至少觸發一次 Log.v("Video Size Change", "onVideoSizeChanged called"); } @Override public void onSeekComplete(MediaPlayer arg0) { // seek操作完成時觸發 Log.v("Seek Completion", "onSeekComplete called"); } @Override public void onPrepared(MediaPlayer player) { // 當prepare完成後,該方法觸發,在這裡我們播放視頻 //首先取得video的寬和高 vWidth = player.getVideoWidth(); vHeight = player.getVideoHeight(); if(vWidth > currDisplay.getWidth() || vHeight > currDisplay.getHeight()){ //如果video的寬或者高超出了當前螢幕的大小,則要進行縮放 float wRatio = (float)vWidth/(float)currDisplay.getWidth(); float hRatio = (float)vHeight/(float)currDisplay.getHeight(); //選擇大的一個進行縮放 float ratio = Math.max(wRatio, hRatio); vWidth = (int)Math.ceil((float)vWidth/ratio); vHeight = (int)Math.ceil((float)vHeight/ratio); //設定surfaceView的布局參數 surfaceView.setLayoutParams(new LinearLayout.LayoutParams(vWidth, vHeight)); //然後開始播放視頻 player.start(); } } @Override public boolean onInfo(MediaPlayer player, int whatInfo, int extra) { // 當一些特定資訊出現或者警告時觸發 switch(whatInfo){ case MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING: break; case MediaPlayer.MEDIA_INFO_METADATA_UPDATE: break; case MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING: break; case MediaPlayer.MEDIA_INFO_NOT_SEEKABLE: break; } return false; } @Override public boolean onError(MediaPlayer player, int whatError, int extra) { Log.v("Play Error:::", "onError called"); switch (whatError) { case MediaPlayer.MEDIA_ERROR_SERVER_DIED: Log.v("Play Error:::", "MEDIA_ERROR_SERVER_DIED"); break; case MediaPlayer.MEDIA_ERROR_UNKNOWN: Log.v("Play Error:::", "MEDIA_ERROR_UNKNOWN"); break; default: break; } return false; } @Override public void onCompletion(MediaPlayer player) { // 當MediaPlayer播放完成後觸發 Log.v("Play Over:::", "onComletion called"); this.finish(); } }</span>