Android音视频系列IT之家

Android视频播放器封装

2019-04-19  本文已影响352人  撸代码的皇太极

推荐一个视频播放器封装库的演示项目:iMusic视频播放器

之前开发公司项目时,在部分项目中有用到著名的开源项目:节操视频播放器(后更名为饺子播放器)。然而在后期的项目维护时,索性从头开始,独立封装视频播放器框架,封装基础功能,方便后期迭代和维护。现已将其整理开源iMusic
此播放器支持的特性包括但不限于:体积小、极简集成、完全自定义交互UI、支持常规的横竖屏切换、可拖拽的小窗口切换、可拖拽的全局悬浮窗口播放、界面跳转无缝衔接播放、全屏播放下手势识别调节等功能。网络数据交互采用MVP思想搭建框架。
我们先来看下iMusic项目效果图:

体验完整功能请前往iMusic下载体验
视频播放器框架结构一览:
播放器框架图示
图中的TrackListener为播放器本身。如图所示,播放器框架设计之初被封装设计成为了一个可自定义UI、单例、代理模式的交互播放器。本篇将分三大类叙述视频播放器的封装实现过程。

一、解码渲染

解码器这里选择基于系统的MediaPlayer实现,如果你想更换解码器,请下载源码自定更换。画面渲染使用TextrueView实现,至于为什么不使用SurfaceView,自行Google一波。

1.TextureView创建和初始化
   //初始化一个TextureView并添加至ViewGroup或找到你的TextureView 组件
   mTextureView=new TextureView(getContext());
   //设置画布监听
   textureView.setSurfaceTextureListener(this);
   //添加至布局
   fragment.addView(textureView,new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT, Gravity.CENTER));
    /**
     * TextureView准备好了回调
     * @param surface 内部画布渲染surface
     * @param width TextureView布局宽
     * @param height TextureView布局高
     */
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        Logger.d(TAG,"onSurfaceTextureAvailable-->width:"+width+",height:"+height);
        //这里对画面改变、转场播放做了处理,声明一个mSurfaceTexture ,在TextureView发生变化时更新
        if (mSurfaceTexture == null) {
            mSurfaceTexture = surface;
            //prepare();
        } else {
            mTextureView.setSurfaceTexture(mSurfaceTexture);
        }
    }

    /**
     * TextureView宽高发生变化时回调
     * @param surface 内部surface
     * @param width 新的TextureView布局宽
     * @param height 新的TextureView布局高
     */
    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        Logger.d(TAG,"onSurfaceTextureSizeChanged-->width:"+width+",height:"+height);
    }

    /**
     * TextureView销毁时回调
     * @param surface 内部surface
     * @return Most applications should return true.
     */
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        Logger.d(TAG,"onSurfaceTextureDestroyed");
        return null==mSurfaceTexture;
    }

    /**
     * TextureView刷新时回调
     * @param surface 内部surface
     */
    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }
2.MediaPlayer初始化和准备播放
    mMediaPlayer = new MediaPlayer();
    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    //设置准备播放监听器,在onPrepared回调中开始播放
    mMediaPlayer.setOnPreparedListener(this);
    //...此处省去一系列监听设置
    //异步准备
    mMediaPlayer.prepareAsync();
    
    /**
     * 播放器准备好了
     * @param mp 解码器
     */
    @Override
    public void onPrepared(MediaPlayer mp) {
        Logger.d(TAG,"onPrepared");
        if(null!=mSurfaceTexture){
            if(null!=mSurface){
                mSurface.release();
                mSurface=null;
            }
            mSurface =new Surface(mSurfaceTexture);
            mp.setSurface(mSurface);
        }
        //开始播放
        mp.start();
    }

二、单例

要实现单例播放,就必须有个被设计为单例模式的第三方委托代理类持有MediaPlayer,此库就取名为 VideoPlayerManager,VideoPlayerManager持有MediaPlayer和TextureView。播放视频前需要先释放一波,然后更新监听器。

    /**
     * 开始播放的入口开始播放、准备入口
     */-
    public void startPlayVideo(){
        //还原可能正在进行的播放任务
        VideoPlayerManager.getInstance().onReset();
        //更新内部监听器
        VideoPlayerManager.getInstance().addOnPlayerEventListener(this);
        //这个用在列表销毁时判断播放器是否需要销毁
        setWorking(true);
        //准备画面渲染图层
        if(null!=mSurfaceView){
            addTextrueViewToView(BaseVideoPlayer.this);
            //开始准备播放
            VideoPlayerManager.getInstance().startVideoPlayer(mDataSource,getContext());
        }
    }
    //VideoPlayerManager.getInstance().onReset();
    /**
     * 放播放器
     */
    private void reset() {
        try {
            if(null!=mMediaPlayer){
                if(mMediaPlayer.isPlaying()){
                    mMediaPlayer.stop();
                }
                mMediaPlayer.reset();
                mMediaPlayer.release();
                mMediaPlayer=null;
            }
        }catch (RuntimeException e){
            e.printStackTrace();
        }
    }

三、自定义交互UI

播放器提供了三个交互类型的基类,分别是BaseVideoController(播放控制器)、BaseCoverController(封面控制器)、BaseGestureController(手势识别控制器),要实现自定义UI交互,需继承此三个类实现自己的业务交互。播放事件的UI刷新机制,是由BaseVideoPlayer统一调度,

1. 自定义交互控制器

交互控制器在本项目中被定义为:用户与播放器的UI交互控制器。如需自定义请继承BaseVideoController类并实现其抽象方法,调用播放器通道的setVideoController(V controller);绑定控制器。</br>
如在播放过程中开启小窗口、悬浮窗播放器时,可指定控制器小窗口、悬浮窗专用的交互控制器。悬浮窗口的关闭按钮不支持自定义。另外,控制器还提供了子线程中的播放进度方法,100毫秒执行一次,主线程的播放进度方法是1000毫秒执行一次,请注意你的UI刷新。

2. 自定义封面控制器

封面控制器是指视频在开始播放前的封面显示图层,如需自定义请继承BaseCoverController类,调用播放器通道的setVideoCoverController(C controller);绑定控制器。BaseCoverController中默认实现了点击开始播放能力。若需自定义点击自己的View开始播放,请实现点击事件后
调用BaseCoverController的mOnStartListener.onStartPlay();方法开始播放。

3. 自定义手势识别器

手势识别器是播放器在全屏状态下播放时,播放器内部检测用户手势滑动行为对播放器功能做出改变时的UI交互提示,如快进、快退、音量、亮度等调节后的UI显示交互,如需自定义
请继承BaseGestureController类,实现其抽象方法,调用调用播放器通道的setVideoGestureController(G controller);绑定控制器。自定义手势识别器还支持消费手势触摸事件,详见BaseGestureController抽象方法onTouchEnevt介绍。

全部功能请阅读视频播放器Wiki

上一篇 下一篇

猜你喜欢

热点阅读