美文网首页
dkPlayer java结构分析

dkPlayer java结构分析

作者: Lei_9c47 | 来源:发表于2019-02-22 11:15 被阅读0次

dkPlayer java结构分析

项目中使用的播放器:github:https://github.com/dueeeke/dkplayer

首先附上结构流程图:

image
  1. 定义抽象类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);
    }

相关文章

网友评论

      本文标题:dkPlayer java结构分析

      本文链接:https://www.haomeiwen.com/subject/smisyqtx.html