作为一个练手项目,基于IjkMediaPlayer的仿照腾讯视频风格的播放器,支持全屏缩放,对其中核心的代码做记录。
播放器总体架构如图所示:

player是继承frameLayout,自己内部拥有IjkMediaPlayer的实例,以及做渲染显示用的TextureView,同时player只有一个mContainer子控件,mContainer同样也是一个frameLayout容器,容纳TextureView和Controller;Controller也是继承FrameLayout,并作为容器容纳各个控制控件的容器,内部通过接口和player相关联,控制player,同时也根据player的状态改变自己的界面。实现全屏,就是把mContainer先从player中移除掉,然后把mContainer添加到Activity的android.R.id.content根布局下面,然后隐藏导航栏,这样就实现全屏了。
下面是NiceVideoPlayer的主要代码:
1、初始化Container

 private void init(){
        mContainer = new FrameLayout(mContext);
        mContainer.setBackgroundColor(Color.BLACK);
        LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        this.addView(mContainer,params);
    }

2、添加并关联播放控制器控件

public void setController(BaseVideoPlayerController controller){
        mContainer.removeView(mController);
        mController = controller;
        mController.reset();
        controller.setNiceVideoPlayer(this);
        LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        mContainer.addView(mController,params);
    }

3、用户点击播放,需要开始准备播放,获取声音焦点

 private void initAudioManager(){
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        mAudioManager.requestAudioFocus(null,AudioManager.STREAM_MUSIC,AudioManager.AUDIOFOCUS_GAIN);
    }

4、初始化MediaPlayer,这里分IjkMediaPlayer和SDK自带的MediaPlayer

 private void initMediaPlayer(){
        if(mMediaPlayer == null){
            if(mPlayerType == TYPE_IJK){
                mMediaPlayer = new IjkMediaPlayer();
            }else if(mPlayerType == TYPE_NATIVE){
                mMediaPlayer = new AndroidMediaPlayer();
            }
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        }
    }

5、初始化TextureView,并把TextureView添加到mContainer中,注意是添加到最底层

private void initTextureView(){
        if(mTextureView == null){
            mTextureView = new NiceTextureView(mContext);
            mTextureView.setSurfaceTextureListener(this);  //这里设置监听,在textureview准备好之后进行下一步
        }
    }
 private void addTextureView(){
        mContainer.removeView(mTextureView);
        LayoutParams params = new LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT,
                Gravity.CENTER);
        mContainer.addView(mTextureView,0,params);
    }

6、TextureView准备好之后,处理监听回调:

@Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
        if(mSurfaceTexture == null){
            Log.i(TAG,"onSurfaceTextureAvailable");
            mSurfaceTexture = surfaceTexture;
            openMediaPlayer();
        }else{
            mTextureView.setSurfaceTexture(mSurfaceTexture);
        }

    }

7、打开播放器,设置播放所必须的监听器,并且关联textureView的Surface,同时设置播放资源:

private void openMediaPlayer(){
        //屏幕长量
        mContainer.setKeepScreenOn(true);
        //设置监听
        mMediaPlayer.setOnPreparedListener(mOnPreparedListener);
        mMediaPlayer.setOnInfoListener(mOnInfoListener);
        mMediaPlayer.setOnErrorListener(mOnErrorListener);
        mMediaPlayer.setOnCompletionListener(mOnCompletionListener);
        mMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);
        mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);

        //设置资源
        try {
            mMediaPlayer.setDataSource(mContext,Uri.parse(mUrl));
            if(mSurface == null){
                mSurface = new Surface(mSurfaceTexture);
            }
            mMediaPlayer.setSurface(mSurface);
            mMediaPlayer.prepareAsync();
            mCurrentState = STATE_PREPARING;
            mController.onPlayStateChanged(mCurrentState);
            Log.i(TAG,"播放器准备中");
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG,"打开播放器错误",e);
        }
    }

8、 IMediaPlayer.OnPreparedListener播放器异步准备监听

private IMediaPlayer.OnPreparedListener mOnPreparedListener = new IMediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(IMediaPlayer iMediaPlayer) {
            mCurrentState = STATE_PREPARED;
            mController.onPlayStateChanged(mCurrentState);
            iMediaPlayer.start(); //开始播放

        }
    };

9、IMediaPlayer.OnCompletionListener播放完毕的监听

 private IMediaPlayer.OnCompletionListener mOnCompletionListener = new IMediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(IMediaPlayer iMediaPlayer) {
            mCurrentState = STATE_COMPLETED;
            mController.onPlayStateChanged(mCurrentState);
            //关闭屏幕长亮
            mContainer.setKeepScreenOn(false);
        }
    };

10、这里主要监听播放器开始在TextureView中渲染的信息

 private IMediaPlayer.OnInfoListener mOnInfoListener = new IMediaPlayer.OnInfoListener() {
        @Override
        public boolean onInfo(IMediaPlayer iMediaPlayer, int what, int extra) {
            if(what == IMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START){
                //播放器开始渲染画面
                mCurrentState = STATE_PLAYING;
                mController.onPlayStateChanged(mCurrentState);
            }
            return true;
        }
    };

11、 IMediaPlayer.OnBufferingUpdateListener 缓存进度改变的信息

 private IMediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new IMediaPlayer.OnBufferingUpdateListener() {
        @Override
        public void onBufferingUpdate(IMediaPlayer iMediaPlayer, int i) {
            mBufferPercent = i; //此数据可以用来更新缓存进度条
        }
    };

12、进入全屏的方法:

public void enterFullScreen() {
        if(mCurrentMode  == MODE_FULL_SCREEN) return;

        //隐藏状态栏
        NiceUtil.hideActionBar(mContext);
        //横屏
        NiceUtil.getAppCompatActivity(mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        ViewGroup viewGroup = NiceUtil.scanForActivity(mContext).findViewById(android.R.id.content);


        if(mCurrentMode == MODE_TINY_WINDOW){

        }else{
            this.removeView(mContainer);
        }


        LayoutParams layoutParams = new LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        viewGroup.addView(mContainer,layoutParams);
        mCurrentMode = MODE_FULL_SCREEN;
        mController.onPlayModeChanged(mCurrentMode);

    }

13、退出全屏的操作:

public boolean exitFullScreen() {
        if(mCurrentMode != MODE_FULL_SCREEN) return false;
        ViewGroup viewGroup = NiceUtil.getAppCompatActivity(mContext).findViewById(android.R.id.content);
        viewGroup.removeView(mContainer);
        LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        this.addView(mContainer,params);

        //显示状态栏
        NiceUtil.ShowActionBar(mContext);
        //竖屏
        NiceUtil.getAppCompatActivity(mContext).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        mCurrentMode = MODE_NORMAL;
        mController.onPlayModeChanged(mCurrentMode);
        return true;
    }

14、重新onBackPress()方法,如果在全屏的时候,退出全屏

public boolean onBackPress(){
        if(mCurrentMode == MODE_FULL_SCREEN){
            return exitFullScreen();
        }
        if(mCurrentMode == MODE_TINY_WINDOW){
            return exitTinyWindow();
        }
        return false;
    }
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐