转载请注明出处:http://www.jianshu.com/p/fc07935a0b04
项目代码地址:https://github.com/Android-Jungle/android-jungle-mediaplayer
1、MediaPlayer 简介
首先,我们基于系统的 android.media.MediaPlayer
实现我们的 BaseMediaPlayer
。当然,我们也可以使用其他第三方的 Player 来实现,具体就在实际使用中按需求来做。
那么,我们需要先对 MediaPlayer 做一些必要的了解。通过网上的资料搜索,我认为这篇文章对 MediaPlayer
的各种状态迁移和使用规范做了较好的阐述:
[译]class android.media.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;
}
};
}
下一篇我们将介绍手势检测与控制。