dkPlayer java结构分析
2019-02-22 本文已影响0人
Lei_9c47
dkPlayer java结构分析
项目中使用的播放器:github:https://github.com/dueeeke/dkplayer
首先附上结构流程图:
image- 定义抽象类AbstractPlayer为播放器的基本行为做抽象
public abstract class AbstractPlayer {
protected PlayerEventListener mPlayerEventListener;
/**
* 初始化播放器实例
*/
public abstract void initPlayer();
/**
* 设置播放地址
* @param path 播放地址
* @param headers 播放地址请求头
*/
public abstract void setDataSource(String path, Map<String, String> headers);
/**
* 用于播放raw和asset里面的视频文件
*/
public abstract void setDataSource(AssetFileDescriptor fd);
/**
* 播放
*/
public abstract void start();
/**
* 暂停
*/
public abstract void pause();
/**
* 停止
*/
public abstract void stop();
/**
* 准备开始播放(异步)
*/
public abstract void prepareAsync();
/**
* 重置播放器
*/
public abstract void reset();
/**
* 是否正在播放
*/
public abstract boolean isPlaying();
/**
* 调整进度
*/
public abstract void seekTo(long time);
/**
* 释放播放器
*/
public abstract void release();
/**
* 获取当前播放的位置
*/
public abstract long getCurrentPosition();
/**
* 获取视频总时长
*/
public abstract long getDuration();
/**
* 获取缓冲百分比
*/
public abstract int getBufferedPercentage();
/**
* 设置渲染视频的View,主要用于TextureView
*/
public abstract void setSurface(Surface surface);
/**
* 设置渲染视频的View,主要用于SurfaceView
*/
public abstract void setDisplay(SurfaceHolder holder);
/**
* 设置音量
*/
public abstract void setVolume(float v1, float v2);
/**
* 设置是否循环播放
*/
public abstract void setLooping(boolean isLooping);
/**
* 设置硬解码
*/
public abstract void setEnableMediaCodec(boolean isEnable);
/**
* 设置其他播放配置
*/
public abstract void setOptions();
/**
* 设置播放速度
*/
public abstract void setSpeed(float speed);
/**
* 获取当前缓冲的网速
*/
public abstract long getTcpSpeed();
/**
* 绑定VideoView
*/
public void bindVideoView(PlayerEventListener playerEventListener) {
this.mPlayerEventListener = playerEventListener;
}
}
抽象类与接口小知识为后续做铺垫
众所周知普通类如果实现一个接口,那么普通类需要重写接口中的所有方法
但是抽象类实现接口,可以完全覆盖/重写 接口中的方法,也可只重写接口中的某几个方法
子类再继承抽象类时,子类重写的方法即为抽象类中未重写接口中的方法。
2.定义子类继承abstractPlayer,子类中持有IjkMediaPlayer(真正的媒体播放器),在abstractPlayer的抽象方法中一一将播放器的对应功能实现
public class IjkPlayer extends AbstractPlayer {
protected IjkMediaPlayer mMediaPlayer;
......
@Override
public void initPlayer() {
mMediaPlayer = new IjkMediaPlayer();
setOptions();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setOnErrorListener(onErrorListener);
mMediaPlayer.setOnCompletionListener(onCompletionListener);
mMediaPlayer.setOnInfoListener(onInfoListener);
mMediaPlayer.setOnBufferingUpdateListener(onBufferingUpdateListener);
mMediaPlayer.setOnPreparedListener(onPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener);
mMediaPlayer.setOnNativeInvokeListener(new IjkMediaPlayer.OnNativeInvokeListener() {
@Override
public boolean onNativeInvoke(int i, Bundle bundle) {
return true;
}
});
}
@Override
public void setOptions() {
}
@Override
public void setDataSource(String path, Map<String, String> headers) {
try {
Uri uri = Uri.parse(path);
if(uri.getScheme().equals(ContentResolver.SCHEME_ANDROID_RESOURCE)){
RawDataSourceProvider rawDataSourceProvider = RawDataSourceProvider.create(mAppContext, uri);
mMediaPlayer.setDataSource(rawDataSourceProvider);
} else {
mMediaPlayer.setDataSource(mAppContext, uri, headers);
}
} catch (Exception e) {
mPlayerEventListener.onError();
}
}
@Override
public void setDataSource(AssetFileDescriptor fd) {
try {
mMediaPlayer.setDataSource(fd.getFileDescriptor());
} catch (Exception e) {
mPlayerEventListener.onError();
}
}
@Override
public void pause() {
try {
mMediaPlayer.pause();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
//以下省略...
}
3.播放器的具体抽象实现了,现在我们需要将上层的控制view做一个封装,这里定义了BaseIjkVideoView,它主要持有了AbstractPlayer和更上层操作视频播放seek等功能的BaseVideoController,并且自身实现了MediaPlayerControl以便于让后续继承了BaseIjkVideoView的子类去实现具体接口
public abstract class BaseIjkVideoView extends FrameLayout implements MediaPlayerControl, PlayerEventListener {
protected AbstractPlayer mMediaPlayer;//播放器
@Nullable
protected BaseVideoController mVideoController;//控制器
...
/**
* 初始化播放器
*/
protected void initPlayer() {
if (mMediaPlayer == null) {
//没有传入播放器实现,默认使用IjkPlayer
if (mPlayerConfig.mAbstractPlayer != null) {
mMediaPlayer = mPlayerConfig.mAbstractPlayer;
} else {
mMediaPlayer = new IjkPlayer(getContext());
}
//传入PlayerEventListener
mMediaPlayer.bindVideoView(this);
mMediaPlayer.initPlayer();
mMediaPlayer.setEnableMediaCodec(mPlayerConfig.enableMediaCodec);
mMediaPlayer.setLooping(mPlayerConfig.isLooping);
}
}
}
同时该接口还会传递给BaseVideoController,让BaseVideoController内部通过该接口操控播放器部分显示页面点击重试后的播放器动作。
/**
* 控制器基类
* Created by Devlin_n on 2017/4/12.
*/
public abstract class BaseVideoController extends FrameLayout {
//默认实现了播放错误是的业务逻辑
public void setPlayState(int playState) {
currentPlayState = playState;
hideStatusView();
switch (playState) {
case IjkVideoView.STATE_ERROR:
mStatusView.setMessage(getResources().getString(R.string.dkplayer_error_message));
mStatusView.setButtonTextAndAction(getResources().getString(R.string.dkplayer_retry), new OnClickListener() {
@Override
public void onClick(View v) {
hideStatusView();
mediaPlayer.retry();
}
});
this.addView(mStatusView, 0);
break;
}
}
...
//给了个handler循环实现进度条的递进
protected Runnable mShowProgress = new Runnable() {
@Override
public void run() {
int pos = setProgress();
if (mediaPlayer.isPlaying()) {
postDelayed(mShowProgress, 1000 - (pos % 1000));
}
}
};
...
}
另外它还实现了PlayerEventListener,通过mMediaPlayer.bindVideoView(this)绑定
目的是让继承了AbstractPlayer的子类做一层回调,好让后续BaseIjkVideoView的子类实现各种播放状态的变化
public interface PlayerEventListener {
void onError();
void onCompletion();
void onInfo(int what, int extra);
void onPrepared();
void onVideoSizeChanged(int width, int height);
}
//ijkplayer内部回调
private IMediaPlayer.OnPreparedListener onPreparedListener = new IMediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(IMediaPlayer iMediaPlayer) {
mPlayerEventListener.onPrepared();
}
};
再来看一下播放器如何全屏操作,IjkVideoView中实现两个方法
/**
* 进入全屏
*/
@Override
public void startFullScreen() {
if (mVideoController == null) return;
Activity activity = WindowUtil.scanForActivity(mVideoController.getContext());
if (activity == null) return;
if (isFullScreen) return;
WindowUtil.hideSystemBar(mVideoController.getContext());
//先移除IjkVideoView中的总父容器playerContainer
this.removeView(playerContainer);
ViewGroup contentView = activity
.findViewById(android.R.id.content);
LayoutParams params = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
//直接添加到系统的contentView设置MATCH_PARENT
contentView.addView(playerContainer, params);
orientationEventListener.enable();
isFullScreen = true;
setPlayerState(PLAYER_FULL_SCREEN);
}
/**
* 退出全屏
*/
@Override
public void stopFullScreen() {
if (mVideoController == null) return;
Activity activity = WindowUtil.scanForActivity(mVideoController.getContext());
if (activity == null) return;
if (!isFullScreen) return;
if (!mPlayerConfig.mAutoRotate) orientationEventListener.disable();
WindowUtil.showSystemBar(mVideoController.getContext());
ViewGroup contentView = activity
.findViewById(android.R.id.content);
//移除contentView中布局
contentView.removeView(playerContainer);
LayoutParams params = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
//重新添加会ijkVideoView中
this.addView(playerContainer, params);
this.requestFocus();
isFullScreen = false;
setPlayerState(PLAYER_NORMAL);
}