在 Android 視頻播放器 (一):使用VideoView播放視頻 我們講了一下如何使用VideoView播放視頻,了解了基本的播放器的一些知識和內容。也知道VideoView內部封裝的就是MediaPlayer,本文就介紹如何使用MediaPlayer播放視頻。
一、簡介
MediaPlayer是Android中的一個多媒體播放類,其提供的API能滿足簡單的大部分音視頻的播放需求。
下面簡單介紹一下MediaPlayer:
- MediaPlayer是使用Surface進行視頻的展示的。
- MediaPlayer只支持mp4、avi、3gp格式的視頻,支持格式相對單一。
- MediaPlayer可以播放網絡視頻,支持的網絡視頻的協議為:Http協議和RTSP協議兩種。
二、MediaPlayer 使用方法
MediaPlayer 的提供的方法如下:
- void setDataSource(String path) :通過一個具體的路徑來設置MediaPlayer的數據源,path可以是本地的一個路徑,也可以是一個網絡路徑
- void setDataSource(Context context, Uri uri): 通過給定的Uri來設置MediaPlayer的數據源,這里的Uri可以是網絡路徑或是一個ContentProvider的Uri。
- void setDataSource(MediaDataSource dataSource) :通過提供的MediaDataSource來設置數據源
- void setDataSource(FileDescriptor fd): 通過文件描述符FileDescriptor來設置數據源
- int getCurrentPosition() :獲取當前播放的位置
- int getAudioSessionId() :返回音頻的session ID
- int getDuration() :得到文件的時間
- TrackInfo[] getTrackInfo() :返回一個track信息的數組
- boolean isLooping (): 是否循環播放
- boolean isPlaying(): 是否正在播放
- void pause () :暫停
- void start () :開始
- void stop () : 停止
- void prepare(): 同步的方式裝載流媒體文件。
- void prepareAsync(): 異步的方式裝載流媒體文件。
- void reset(): 重置MediaPlayer至未初始化狀態。
- void release (): 回收流媒體資源。
- void seekTo(int msec): 指定播放的位置(以毫秒為單位的時間)
- void setAudioStreamType(int streamtype) :指定流媒體類型
- void setLooping(boolean looping) :設置是否單曲循環
- void setNextMediaPlayer(MediaPlayer next) : 當前這個MediaPlayer播放完畢后,MediaPlayer next開始播放
- void setWakeMode(Context context, int mode):設置CPU喚醒的狀態。
- setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) :網絡流媒體的緩沖變化時回調
- setOnCompletionListener(MediaPlayer.OnCompletionListener listener) :網絡流媒體播放結束時回調
- setOnErrorListener(MediaPlayer.OnErrorListener listener) :發生錯誤時回調
- setOnPreparedListener(MediaPlayer.OnPreparedListener listener):當裝載流媒體完畢的時候回調。
Android通過MediaPlayer控制播放器的狀態的方式來控制媒體文件的播放,其中:
- prepare()和prepareAsync() 提供了同步和異步兩種方式設置播放器進入prepare狀態,需要注意的是,如果MediaPlayer實例是由create方法創建的,那么第一次啟動播放前不需要再調用prepare()了,因為create方法里已經調用過了。
- start()是真正啟動文件播放的方法,
- pause()和stop()比較簡單,起到暫停和停止播放的作用,
- seekTo()是定位方法,可以讓播放器從指定的位置開始播放,需要注意的是該方法是個異步方法,也就是說該方法返回時並不意味着定位完成,尤其是播放的網絡文件,真正定位完成時會觸發OnSeekComplete.onSeekComplete(),如果需要是可以調用setOnSeekCompleteListener(OnSeekCompleteListener)設置監聽器來處理的。
- release()可以釋放播放器占用的資源,一旦確定不再使用播放器時應當盡早調用它釋放資源。
- reset()可以使播放器從Error狀態中恢復過來,重新會到Idle狀態。
使用MediaPlayer播放視頻的步驟如下:
- 創建MediaPlayer對象,並讓它加載指定的視頻文件;
- 在界面布局文件中定義SurfaceView控件,或在程序中創建SurfaceView控件,並為SurfaceView的SurfaceHolder添加Callback監聽器;
- 調用MediaPlayer對象的setDisney(SurfaceHolder sh)方法將所播放的視頻圖像輸出到指定的SurfaceView控制;
- 調用MediaPlayer的start()、stop()、pause()方法來控制視頻播放。
下圖是一個MediaPlayer對象被支持的播放控制操作驅動的聲明周期和狀態。其中,橢圓代表MediaPlayer可能駐留的狀態,弧線表示驅動MediaPlayer在各個狀態之間遷移的播放控制操作。這里有兩種類型的弧線。由單箭頭開始的弧線代表同步方法調用,而以雙箭頭開頭的弧線代表異步方法調用。
三、使用MediaPlayer實現視頻播放
下面我們將展示如何使用MediaPlayer播放香港衛視的視頻流:
1. 聲明權限
需要在AndroidManifest.xml添加權限。
<uses-permission android:name="android.permission.INTERNET" />
2. 編寫SurfaceView布局
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <SurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="195dp" /> </LinearLayout> </android.support.constraint.ConstraintLayout>
3. 編寫播放視頻代碼
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
SurfaceView surfaceView;
SurfaceHolder surfaceHolder;
MediaPlayer mediaPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = findViewById(R.id.surface_view);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource("http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8");
} catch (IOException e) {
e.printStackTrace();
}
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Surface surface = holder.getSurface();
mediaPlayer.setSurface(surface);
mediaPlayer.prepareAsync();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
四、MediaPlayer使用注意事項
- 在使用start()播放流媒體之前,需要裝載流媒體資源。這里最好使用prepareAsync()用異步的方式裝載流媒體資源。因為流媒體資源的裝載是會消耗系統資源的,在一些硬件不理想的設備上,如果使用prepare()同步的方式裝載資源,可能會造成UI界面的卡頓,這是非常影響用於體驗的。因為推薦使用異步裝載的方式,為了避免還沒有裝載完成就調用start()而報錯的問題,需要綁定MediaPlayer.setOnPreparedListener()事件,它將在異步裝載完成之后回調。異步裝載還有一個好處就是避免裝載超時引發ANR((Application Not Responding)錯誤。
mediaPlayer = new MediaPlayer(); mediaPlayer.setDataSource(path); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 通過異步的方式裝載媒體資源 mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { // 裝載完畢回調 mediaPlayer.start(); } });
- 使用完MediaPlayer需要回收資源。MediaPlayer是很消耗系統資源的,所以在使用完MediaPlayer,不要等待系統自動回收,最好是主動回收資源
if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; }