【Android】 從頭搭建視頻播放器(2)——SystemMediaPlayerImpl
轉載請註明出處:http://blog.csdn.net/arnozhang12/article/details/48734139
1、MediaPlayer 簡介
首先,我們基於系統的 android.media.MediaPlayer
實現我們的 BaseMediaPlayer
。當然,我們也可以使用其他第三方的 Player 來實現,具體就在實際使用中按需求來做。
那麼,我們需要先對 MediaPlayer 做一些必要的瞭解。通過網上的資料搜索,我認爲這篇文章對 MediaPlayer
的各種狀態遷移和使用規範做了較好的闡述:
我從這篇文章裏面借了一張圖如下:
通過對上圖的理解,結合播放器的功能特性,抽離出了主要的狀態遷移如下:
我們着重需要關注的方法列表如下:
方法 | 特性 |
---|---|
new MediaPlayer()創建一個 MediaPlayer,處於 Idle 狀態 |
|
reset() | 重置 MediaPlayer,使其切換到 Idle 狀態 |
setDataSource(context, uri) | 設置視頻數據源,切換到 Initialized 狀態 |
prepare() | 準備數據,同步方法,完成後將切換到 Prepared 狀態 |
prepareAsync() | 異步準備數據,處於 Preparing 狀態,準備完畢後通過回調 OnPreparedListener.onPrepared 方法,處於 Prepared 狀態 |
start() | 開始/恢復播放,切換到 Started 狀態 |
seekTo(millSeconds) | 跳轉進度 |
pause() | 暫停播放,切換到 Paused 狀態 |
stop() | 停止播放,切換到 Stopped 狀態 |
release() | 釋放播放器資源,切換到終止 End 狀態,這時候不允許對 MediaPlayer 做任何操作了 |
setDisplay(surfaceHolder) | 設置畫面展示 SurfaceHolder |
2、關注的回調
MediaPlayer 有衆多回調 Listener,我們需要着重關注:
回調 | 特性 |
---|---|
MediaPlayer.OnPreparedListener | 視頻通過 prepareAsync 異步準備好之後回調 |
MediaPlayer.OnCompletionListener | 視頻播放完成後的回調 |
MediaPlayer.OnVideoSizeChangedListener | 視頻畫面大小更改的回調,我們一般通過這個回調更改 SurfaceView 的大小及位置,以達到等比例縮放播放的效果 |
MediaPlayer.OnBufferingUpdateListener | 視頻緩衝進度更改回調,一般我們通過這個回調更改 SeekBar 的 SecondaryProgress |
MediaPlayer.OnErrorListener | 播放器內部發生錯誤回調,一般是錯誤的調用或者其他錯誤引起 |
3、使用 MediaPlayer 必需的步驟
接下來我們基於系統的 MediaPlayer 實現 SystemMediaPlayerImpl
,它繼承自 BaseMediaPlayer。一般使用 MediaPlayer 有如下步驟:
步驟 | 內容 |
---|---|
初始化 | new MediaPlayer |
設置一系列 Listener | OnPreparedListener 等等 |
設置數據源 | setDataSource. 可以是本地文件,也可以是網絡數據 Url |
處理 OnPreparedListener.onPrepared 回調 | 設置畫面展示 SurfaceHolder,並調用 start 開始播放。如果需要從視頻的某個位置播放,可在這裏調用 seekTo(millSeconds) 直接跳轉 |
處理 OnVideoSizeChangedListener.onVideoSizeChanged 回調 | 在此通過獲取到的視頻畫面大小調整 SurfaceView 的位置及大小,達到等比例縮放的效果 |
處理 OnBufferingUpdateListener.onBufferingUpdate 回調 | 再次可以更新界面上的進度條的緩衝進度 |
處理 OnCompletionListener.onCompletion 回調 | 在此處理播放完成通知,可以按需求來做按章節連續播放或者退出頁面等等上層業務邏輯 |
處理 OnErrorListener.onError 回調 | 處理錯誤,進行 UI 的展示更新等等 |
4、SystemMediaPlayerImpl
我們根據上面的步驟,結合 上一篇文章 中介紹的 BaseMediaPlayer,實現出 SystemMediaPlayerImpl 如下:
/**
* SystemMediaPlayerImpl.java
*
* @author arnozhang
* @email [email protected]
* @date 2015.9.25
*/
public class SystemMediaPlayerImpl extends BaseMediaPlayer {
private MediaPlayer mMediaPlayer;
public SystemMediaPlayerImpl(Context context, SurfaceView surfaceView) {
super(context, surfaceView);
init();
}
private void init() {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setOnPreparedListener(onPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);
mMediaPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListener);
mMediaPlayer.setOnErrorListener(mOnErrorListener);
mMediaPlayer.setOnCompletionListener(mOnCompletionListener);
}
@Override
public void play(VideoInfo videoInfo) {
if (mMediaPlayer == null) {
return;
}
super.play(videoInfo);
// Clear old display and Reset.
mMediaPlayer.setDisplay(null);
mMediaPlayer.reset();
try {
mMediaPlayer.setDataSource(mContext, Uri.parse(videoInfo.getCurrentStreamUrl()));
} catch (IOException e) {
e.printStackTrace();
}
// Prepare For Play.
mMediaPlayer.prepareAsync();
notifyStartPlay();
notifyLoading();
}
@Override
public void pause() {
if (mMediaPlayer == null || !mMediaPlayerIsPrepared) {
return;
}
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
notifyPaused();
}
}
@Override
public void resume() {
if (mMediaPlayer == null || !mMediaPlayerIsPrepared) {
return;
}
mMediaPlayer.start();
notifyResumed();
}
@Override
public void stop() {
if (mMediaPlayer == null) {
return;
}
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
notifyStopped();
}
@Override
public void seekTo(int millSeconds) {
if (mMediaPlayer != null) {
mMediaPlayer.seekTo(millSeconds);
}
}
@Override
public void setVolume(float volume) {
if (mMediaPlayer != null) {
mMediaPlayer.setVolume(volume, volume);
}
}
@Override
public int getDuration() {
return mMediaPlayer != null ? mMediaPlayer.getDuration() : 0;
}
@Override
public int getCurrentPosition() {
return mMediaPlayer != null ? mMediaPlayer.getCurrentPosition() : 0;
}
@Override
public void doDestroy() {
clearLoadingFailed();
mMediaPlayer.release();
mMediaPlayer = null;
}
@Override
public boolean isPlaying() {
return mMediaPlayer != null && mMediaPlayer.isPlaying();
}
@Override
protected void playWithDisplayHolder(SurfaceHolder holder) {
mMediaPlayer.setDisplay(holder);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.start();
mMediaPlayer.seekTo(0);
trySeekToStartPosition();
}
private void clearLoadingFailed() {
ThreadManager.getInstance().getUIHandler().removeCallbacks(mLoadingFailedRunnable);
}
MediaPlayer.OnPreparedListener onPreparedListener = new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer player) {
mAutoPlayWhenHolderCreated = false;
mMediaPlayerIsPrepared = true;
mIsLoading = false;
clearLoadingFailed();
notifyFinishLoading();
// Start Play.
SurfaceHolder holder = mSurfaceView.getHolder();
if (holder == null) {
mAutoPlayWhenHolderCreated = true;
} else {
playWithDisplayHolder(holder);
}
}
};
private void trySeekToStartPosition() {
int seekToPosition = mVideoInfo.getCurrentPosition();
if (mMediaPlayer != null && mMediaPlayerIsPrepared && mVideoSizeInitialized && seekToPosition > 0) {
mMediaPlayer.seekTo(seekToPosition);
}
}
MediaPlayer.OnCompletionListener mOnCompletionListener =
new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer player) {
player.seekTo(0);
notifyPlayComplete();
}
};
MediaPlayer.OnVideoSizeChangedListener mOnVideoSizeChangedListener =
new MediaPlayer.OnVideoSizeChangedListener() {
public void onVideoSizeChanged(MediaPlayer player, int width, int height) {
mVideoWidth = player.getVideoWidth();
mVideoHeight = player.getVideoHeight();
updateSurfaceSize();
mVideoSizeInitialized = true;
trySeekToStartPosition();
}
};
MediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener =
new MediaPlayer.OnBufferingUpdateListener() {
public void onBufferingUpdate(MediaPlayer player, int percent) {
mBufferPercent = percent;
}
};
MediaPlayer.OnErrorListener mOnErrorListener = new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer player, int what, int extra) {
String errorWhat;
switch (what) {
case 1:
errorWhat = "MEDIA_ERROR_UNKNOWN";
break;
case 100:
errorWhat = "MEDIA_ERROR_SERVER_DIED";
break;
default:
errorWhat = "!";
}
String errorExtra;
switch (extra) {
case -1010:
errorExtra = "MEDIA_ERROR_UNSUPPORTED";
break;
case -1007:
errorExtra = "MEDIA_ERROR_MALFORMED";
break;
case -1004:
errorExtra = "MEDIA_ERROR_IO";
break;
case -110:
errorExtra = "MEDIA_ERROR_TIMED_OUT";
break;
default:
errorExtra = "!";
}
String msg = String.format("what:%#x,%s extra:%#x,%s", what, errorWhat, extra, errorExtra);
notifyError(what, msg);
return true;
}
};
}
下一篇我們將介紹手勢檢測與控制。