最近項(xiàng)目需要用到 MediaPlayer + SurfaceView 來播放短視頻框杜,回憶了一下之前的做法寫了一下狭园,但是出現(xiàn)不少問題爱态,這邊干脆就看了一下 api 從頭學(xué)習(xí)了一下挑格。
MediaPlayer
參考:鏈接
這個類主要用于音視頻的播放师幕,而使用它只要了解這個類的幾個狀態(tài)就能很好地運(yùn)用。上面鏈接中有相應(yīng)的狀態(tài)圖抹凳,我就不再費(fèi)時畫了遏餐。但是這個圖是一定要看的,比我這長篇容易理解得多赢底。
一失都、快速使用:播放功能
我們用 MediaPlayer 如果只是圖個省事,可以通過下面的方式快速實(shí)現(xiàn)播放功能:
- 1.創(chuàng)建 MediaPlayer 對象
- 2.確認(rèn)要加載的音視頻流或文件:setDataSource()
- 3.加載音視頻流或文件:prepare()
- 4.播放:start()
這之后可以通過 pause()幸冻、stop()粹庞、release() 等方法進(jìn)行控制,但是如果需要切換播放資源嘁扼,則需先調(diào)用 reset() 方法使 MediaPlayer 對象回到 Idle 狀態(tài)信粮,才能再次調(diào)用 setDataSource() 切換資源。
二趁啸、快速使用:setDataSource的使用
如果是正在學(xué)習(xí) Android 的新人强缘,初次使用對于 setDataSource() 這個方法應(yīng)該也會有疑惑督惰,這里也講解一下使用方法:
Sets the data source as a content Uri.
@param context the Context to use when resolving the Uri
@param uri the Content URI of the data you want to play
public void setDataSource(Context context, Uri uri)
Sets the data source (file-path or http/rtsp URL) to use.
@param path the path of the file, or the http/rtsp URL of the stream you want to play
public void setDataSource(String path)
Sets the data source (FileDescriptor) to use. It is the caller’s responsibility
to close the file descriptor. It is safe to do so as soon as this call returns.
@param fd the FileDescriptor for the file you want to play
public void setDataSource(FileDescriptor fd)
Sets the data source (FileDescriptor) to use. The FileDescriptor must be
seekable (N.B. a LocalSocket is not seekable). It is the caller’s responsibility
to close the file descriptor. It is safe to do so as soon as this call returns.
@param fd the FileDescriptor for the file you want to play
@param offset the offset into the file where the data to be played starts, in bytes
@param length the length in bytes of the data to be played
public void setDataSource(FileDescriptor fd, long offset, long length)
上面是這個方法的多個不同參數(shù)的重載,現(xiàn)在對上面方法的使用進(jìn)行說明:
- setDataSource(Context context, Uri uri)
假設(shè) test.mp3 放在 res/raw/ 目錄下
mMediaPlayer= new MediaPlayer();
Uri setDataSourceuri = Uri.parse("android.resource://"+getPackageName()+"/"+R.raw.test);
mMediaPlayer.setDataSource(this, uri)
- public void setDataSource(String path)
這里可以直接填外部存儲路徑或者音視頻 Url
1.播放本地音視頻:
mMediaPlayer.setDataSource("/mnt/sdcard/test.mp3");
2.播放網(wǎng)絡(luò)音視頻:
Uri uri = Uri.parse("http://**");
mMediaPlayer.setDataSource(Context, uri);
- public void setDataSource(FileDescriptor fd, long offset, long length)
假設(shè) test.mp3 文件放在 assets 目錄下
AssetManager assetMg = this.getApplicationContext().getAssets();
AssetFileDescriptor fileDescriptor = assetMg.openFd("test.mp3");
mMediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(), fileDescriptor.getStartOffset(), fileDescriptor.getLength());
三旅掂、狀態(tài)講解
Idle狀態(tài):當(dāng)使用new()方法創(chuàng)建一個MediaPlayer對象或者調(diào)用了其reset()方法時赏胚,該MediaPlayer對象處于idle狀態(tài)。在處于Idle狀態(tài)時商虐,調(diào)用getCurrentPosition(),getDuration(),getVideoHeight(),getVideoWidth(),setAudioStreamType(int), setLooping(boolean), setVolume(float, float), pause(), start(), stop(), seekTo(int), prepare()或者 prepareAsync()方法都會出現(xiàn)相應(yīng)錯誤觉阅。這兩種方法的一個重要差別就是:當(dāng)一個MediaPlayer對象剛被構(gòu)建的時候,內(nèi)部的播放引擎和對象的狀態(tài)都沒有改變秘车,在這個時候調(diào)用以上的那些方法典勇,框架將無法回調(diào)客戶端程序注冊的OnErrorListener.onError()方法,所以不會進(jìn)入Error狀態(tài)叮趴;但若這個MediaPlayer對象調(diào)用了reset()方法之后割笙,再調(diào)用以上的那些方法,內(nèi)部的播放引擎就會回調(diào)客戶端程序注冊的OnErrorListener.onError()方法眯亦,這時MediaPlayer會進(jìn)入Error狀態(tài)伤溉。
提示:使用new操作符創(chuàng)建的MediaPlayer對象處于Idle狀態(tài),而那些通過重載的create()便利方法創(chuàng)建的MediaPlayer對象卻不是處于Idle狀態(tài)妻率。事實(shí)上乱顾,如果成功調(diào)用了重載的create()方法,那么這些對象已經(jīng)是Prepare狀態(tài)了宫静。End狀態(tài):通過release()方法可以進(jìn)入End狀態(tài)走净,只要MediaPlayer對象不再被使用,就應(yīng)當(dāng)盡快將其通過release()方法釋放掉囊嘉,以釋放相關(guān)的軟硬件組件資源温技,這其中有些資源是只有一份的(相當(dāng)于臨界資源)革为。如果MediaPlayer對象進(jìn)入了End狀態(tài)扭粱,則不會再進(jìn)入任何其他狀態(tài)了。
Initialized狀態(tài):這個狀態(tài)比較簡單震檩,MediaPlayer調(diào)用setDataSource()方法就進(jìn)入Initialized狀態(tài)琢蛤,表示此時要播放的文件已經(jīng)設(shè)置好了。
提示:若當(dāng)此MediaPlayer處于其它的狀態(tài)下抛虏,調(diào)用setDataSource()方法博其,會拋出IllegalStateException異常。Prepared狀態(tài):初始化完成之后還需要通過調(diào)用prepare()或prepareAsync()方法迂猴,這兩個方法一個是同步的一個是異步的慕淡,只有進(jìn)入Prepared狀態(tài),才表明MediaPlayer到目前為止都沒有錯誤沸毁,可以進(jìn)行文件播放峰髓。
提示:在不合適的狀態(tài)下調(diào)用prepare()和prepareAsync()方法會拋出IllegalStateException異常傻寂。當(dāng)MediaPlayer對象處于Prepared狀態(tài)的時候,可以調(diào)整音頻/視頻的屬性携兵,如音量疾掰,播放時是否一直亮屏,循環(huán)播放等徐紧。Preparing狀態(tài):這個狀態(tài)比較好理解静檬,主要是和prepareAsync()配合,如果異步準(zhǔn)備完成并级,會觸發(fā)OnPreparedListener.onPrepared()拂檩,進(jìn)而進(jìn)入Prepared狀態(tài)。
Started狀態(tài):顯然嘲碧,MediaPlayer一旦準(zhǔn)備好广恢,就可以調(diào)用start()方法,這樣MediaPlayer就處于Started狀態(tài)呀潭,這表明MediaPlayer正在播放文件過程中钉迷。可以使用isPlaying()測試MediaPlayer是否處于了Started狀態(tài)钠署。如果播放完畢糠聪,而又設(shè)置了循環(huán)播放,則MediaPlayer仍然會處于Started狀態(tài)谐鼎,類似的舰蟆,如果在該狀態(tài)下MediaPlayer調(diào)用了seekTo()或者start()方法均可以讓MediaPlayer停留在Started狀態(tài)。
Paused狀態(tài):Started狀態(tài)下MediaPlayer調(diào)用pause()方法可以暫停MediaPlayer狸棍,從而進(jìn)入Paused狀態(tài)身害,MediaPlayer暫停后再次調(diào)用start()則可以繼續(xù)MediaPlayer的播放,轉(zhuǎn)到Started狀態(tài)草戈,暫停狀態(tài)時可以調(diào)用seekTo()方法塌鸯,這是不會改變狀態(tài)的。
Stop狀態(tài):Started或者Paused狀態(tài)下均可調(diào)用stop()停止MediaPlayer唐片,而處于Stop狀態(tài)的MediaPlayer要想重新播放丙猬,需要通過prepareAsync()和prepare()回到先前的Prepared狀態(tài)重新開始才可以。
PlaybackCompleted狀態(tài):文件正常播放完畢费韭,而又沒有設(shè)置循環(huán)播放的話就進(jìn)入該狀態(tài)茧球,并會觸發(fā)OnCompletionListener的onCompletion()方法。此時可以調(diào)用start()方法重新從頭播放文件星持,也可以stop()停止MediaPlayer抢埋,或者也可以seekTo()來重新定位播放位置。
Error狀態(tài):在一般情況下,由于種種原因一些播放控制操作可能會失敗揪垄,如不支持的音頻/視頻格式鲤屡,缺少隔行掃描的音頻/視頻,分辨率太高福侈,流超時等原因酒来,等等會觸發(fā)會觸發(fā)OnErrorListener.onError()事件,此時MediaPlayer會進(jìn)入Error狀態(tài)肪凛,及時捕捉并妥善處理這些錯誤是很重要的堰汉,可以幫助我們及時釋放相關(guān)的軟硬件資源,也可以改善用戶體驗(yàn)伟墙。
本想自己看看順便翻譯一點(diǎn)翘鸭,結(jié)果撿了現(xiàn)成的翻譯,參考鏈接:
http://www.itnose.net/detail/6090452.html