MVP视频播放器详解(三)

3. MVP视频播放器详解

3.1 VipVideoPlayer.java

      主要有三个作用:

      1.初始化VipVideoView,对VipVideoView设置,如设置videoView的controller,loader,buffer更新的监听等等。

      这个在3.2节会详细分析。

      2.开放了视频的接口,如获取视频时长,设置视频播放路径,暂停和恢复等等。

      虽然核心的类是VipVideoView,但是用户添加的只是VipVideoPlayer类,所以对外的接口都只能由它提供,VideoView也只是作为VipVideoPlayer中的一个对象,在不同的接口中提供对应的方法。

      暂停和恢复:

    private boolean mHasPause = false;	
    private int mVideoPosition = 0;
	
	public void onPause() {
	  mHasPause = true;
          mVideoPosition = (int)mVideoView.getCurrentPosition();		
          mVideoView.pause();
	}

	public void onResume() {
	  if (mHasPause) {
		mVideoView.seekTo(mVideoPosition);
		mVideoView.start();			
	  }
	}    
      获取视频总长和获取视频当前位置:

    public long getDuration(){
    	return mVideoView.getDuration();
    }
    
    public long getCurrentPosition(){
    	return mVideoView.getCurrentPosition();
    }
       设置视频的Url:includeAdvert是表示是否包含广告,暂时不使用,默认false。

    public void setVideoURI(Uri uri, boolean includeAdvert) {
        setVideoURI(uri, null, includeAdvert);
    }

    public void setVideoURI(Uri uri, Map<String, String> headers, boolean includeAdvert) {
    	mVideoView.setVideoURI(uri, headers, includeAdvert);
    }

      3.处理滑动调节音量和播放进度的事件。

   @Override
   public boolean onTouchEvent(MotionEvent event) {
    	int uriType = mVideoView.getUri_type();
	if (VipVideoView.URI_TYPE_AD == uriType) {
	   return false;
	}
	switch (event.getAction()) {
	case MotionEvent.ACTION_DOWN:
           mLastX = event.getX();
           mLastY = event.getY();
	   mCurrentState = STATE_IDLE; 
	   times = 0;
		    	
	   if ((mReplay.getVisibility() == GONE)) { 
		if(VipVideoView.URI_TYPE_APP == uriType && mVideoView.isInPlaybackState()){
			if (mMediaControllerBottom != null) {
			    toggleMediaControlsVisiblity();
			}
                }
	   }				
           break;
       case MotionEvent.ACTION_MOVE:
           if (videoPlayerHandleMotion(event.getX() - mLastX, event.getY() - mLastY)) {
                mLastX = event.getX();
                mLastY = event.getY();	                
           }               
	   break;	
      case MotionEvent.ACTION_UP:
	   if (mCurrentState == STATE_PREPARED) {
		videoPlayerHandleSeekTo();
	   }
	   mCurrentState = STATE_COMPLETED;				
	   break;
      }
		
      return true;
    }
    private boolean videoPlayerHandleMotion(float dx, float dy) {
	//if (mVideoView.isPlaying()) {
		if (Math.abs(dx) > VIDEO_MOVE_X) {
			if (mCurrentState == STATE_IDLE) {
				mVideoView.pause();
				mCurrentState = STATE_PREPARED;					
			} else if (mCurrentState == STATE_PREPARED) {
				videoPlayerSeekTo((int)dx);
			}
			return true;
		} else if (Math.abs(dy) > VIDEO_MOVE_Y && mCurrentState != STATE_PREPARED) {
			mCurrentState = STATE_VOLUME;
			if (dy > 0) {
			   mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_LOWER,    
                           AudioManager.FX_FOCUS_NAVIGATION_UP);    
			} else {
			   mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_RAISE,    
                           AudioManager.FX_FOCUS_NAVIGATION_UP); 
			}
			return true;
		} 
	//}
	return false;
    }

    private int times = 0;
    private void videoPlayerSeekTo(int x) {
	times = x > 0 ? times + 1 : times - 1;	
	mMediaControllerTop.setVisibility(View.VISIBLE);
	mMediaControllerBottom.setVisibility(View.VISIBLE);
	mMediaControllerBottom.setProgressTime(times * VIDEO_MOVE_TIME);
     }
     private void videoPlayerHandleSeekTo(){
        mVideoPosition = (int)mVideoView.getCurrentPosition();	
        if (times != 0) {
           mVideoView.seekTo(mVideoPosition + times * VIDEO_MOVE_TIME);
        }
	mVideoView.start();
      }

        所有的触摸事件都是由onTouchEvent发出,URI_TYPE_AD表示广告类型,暂时没有使用。当我们手指按下的时候,触发ACTION DOWN,记录当前的位置和状态,初始化times,times表示移动的时间单位,times*VIDEO_MOVE_TIME表示移动times秒,手指滑动的时候,出发ACTION MOVE,这时候将移动的位置差交给videoPlayerHandleMotion处理,根据上下或者左右移动的位置差判断需要触发事件的性质,如果是上下滑动,直接在MOVE里调节音量,如果是左右滑动,再交个ACTION UP处理。


3.2 VipVideoView.java

       视频播放器的核心类,视频播放,消息的相应,外部接口的实现都是由这个类提供。、

       首先,了解视频是如何显示出来的。从类的定义可以看出:

public class VipVideoView extends SurfaceView 
	implements MediaPlayerControl {

      VipVideoView其实就是一个SurfaceView,视频的内容就是通过SurfaceView中的Surface绘制出来的,我们需要到访问Surface,可以通过SurfaceView的一个叫做SurfaceHolder的接口现实。在这之前,我们还需要定义一个MediaPlayer,它就是Vitamio提供的多媒体框架,对MediaPlayer进行设置,看这段:

mMediaPlayer.setDisplay(mSurfaceHolder);
这样MediaPlayer就可以通过mSurfaceHolder接口获取到Surface并显示视频内容了。(我们这里只讨论视频的如何显示)

      其次,消息的相应。我们创建了一个MediaPlayer对象,应该对它进行一些设置。

if (mMediaPlayer == null) {
            	mMediaPlayer = new MediaPlayer(mContext);
            }
            mMediaPlayer.setOnPreparedListener(mPreparedListener);
//当视频准备完成时,这个动作发生在mMediaPlayer.prepareAsync();之后
            mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
//当视频尺寸发生改变时
            mMediaPlayer.setOnCompletionListener(mCompletionListener);
//当视频播放完成时
            mMediaPlayer.setOnErrorListener(mErrorListener);
//当视频播放发生错误时
            mMediaPlayer.setOnInfoListener(mOnInfoListener);
//视频播放的时候会发送一些特定的消息,比如开始缓冲,缓冲结束等等
            mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
//当视频缓冲发生变化时,这里可以获取到缓冲的百分比
            mMediaPlayer.setDisplay(mSurfaceHolder);
//视频需要显示在Surface上
      		mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);
//直接拖动视频,seek结束时
      		mMediaPlayer.setOnTimedTextListener(mTimedTextListener);
//视频播放时间更新
            if (uri_type == URI_TYPE_AD) {
//URI_TYPE_AD 表示广告类型,在广告播放完成之后会将类型改为URI_TYPE_APP,然后继续播放,此功能已经不使用
                mMediaPlayer.setDataSource(mContext, getADUrl(), mHeaders);
            } else if (uri_type == URI_TYPE_APP){
                mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
//视频播放的数据源
            }
  
            //mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
      		mMediaPlayer.setVideoChroma(mVideoChroma == MediaPlayer.VIDEOCHROMA_RGB565 ? MediaPlayer.VIDEOCHROMA_RGB565 : MediaPlayer.VIDEOCHROMA_RGBA);
            
            mMediaPlayer.setScreenOnWhilePlaying(true);
//视频播放过程中,要保持屏幕常亮
            if (!mIsComplete) {
//mIsComplete 和 needPrepare 联合使用,是为了解决:(视频播放完成,退出视频播放器再进来的时候,还会加载视频)的问题
            	mMediaPlayer.prepareAsync();
//准备视频
            }

         setXXXListener设置了视频播放的监听器,我们一一分析一下各个监听器的工作内容:

        (下面说到的controller,loader,replay,就是覆盖在播放器上面的控制器,加载进度,重播,它们都是一个个布局类的试图)

         mPreparedListener :这个监听器能监听到事件的前提是mMediaPlayer.preparedAsync(),字面上就可以看出它是一个异步线程,当视频准备好时,mPreparedListener接受到消息,执行onPrepared(), onPrepared()主要是执行真正的start()播放动作,并且显示controller,seekTo到某个需要的位置,例如接着上一次播放结束的位置开始播放。

        mSizeChangedListener:主要是获取视频的size

        mCompletionListener:播放完成之后将controller隐藏,将replay显示出来

        mErrorListener:当发生错误的处理

        mOnInfoListener:处理了两个状态,

                                          开始缓冲:如果正在播放,暂停视频,将loader显示

                                           缓冲结束:如果刚刚暂停了,继续播放,隐藏loader,显示controller

         mBufferingUpdateListener:这里可以获取到缓冲的百分比,然后提供接口将数值传递到loader显示出来

         mSeekCompleteListener:拖动进度条结束之后的一个外部接口

        mTimedTextListener:暂时没有用到

        播放的流程差不多就是这样: -->prepareAsync-->start-->开始缓冲-->正在缓冲百分比-->缓冲结束-->播放-->播放结束-->显示重播

        关于重播,seekTo(0)从头开始,注意,如果我们退出过播放器界面,应该再一次prepareAsync,如果没有退出过,直接start就可以了,这里通过mIsComplete&needPrepare两个参数控制。

        参数mCurrentState&mTargetState,表示播放的当前状态和下一个状态,例如执行了mMediaPlayer.prepareAsync();之后,

        mCurrentState = STATE_PREPARING;   //当前状态就是正在准备
        mTargetState = STATE_PLAYING;  //下一个状态应该是开始播放

       我们也是根据每个播放状态的阶段,对controller,loader,replay等view进行适时的控制。


       上面只是播放的流程,那到底入口在哪里呢?

    public void setVideoURI(Uri uri, Map<String, String> headers, boolean includeAdvert) {
        mUri = uri;
        mHeaders = headers;
        mSeekWhenPrepared = 0;
        uri_type = includeAdvert ? URI_TYPE_AD : URI_TYPE_APP;
        openVideo();
        requestLayout();
        invalidate();
    }
          设置视频的数据源后,openVideo就会立即创建MediaPlayer对象,然后进行上面的播放流程。




       

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值