Android VideoView学习

VideoView是继承自SurfaceView的,其实真正用于播放视频的是MediaPlayer+SurfaceView,VideoView只是将这两者封装起来,便于开发者使用。

1、VideoView的三个构造方法,都调用了initVideView()方法。在initVideoView()中做了两个重要的事:

(1)给与SurfaceView相关的SurfaceHolder注册监听器

  getHolder().addCallback(mSHCallback);

 注:SurfaceView 提供了一个surface,这个surface采用一个单独的线程在屏幕进行渲染,与surface交互使用SurfaceHodler接口,在SurfaceView中通过      getHolder()可以获得SurfaceHolder接口。

SurfaceHolder中注册的回调可以监听到surface的创建和销毁,通过surfaceCreated(SurfaceHolder holder)和surfaceDestroyed(SurfaceHolder holder)。

surface是在SurfaceView的window可见时创建出来的,是在window 隐藏的时候销毁的。

接着就会执行mSHCallback中响应的回调函数,具体看下面代码中注释:

    SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback()
    {
        //This is called immediately after any structural changes (format or size) have been made to the surface.
        public void surfaceChanged(SurfaceHolder holder, int format,int w, int h)
        {
            mSurfaceWidth = w;
            mSurfaceHeight = h;
            boolean isValidState =  (mTargetState == STATE_PLAYING);
            boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h);
            if (mMediaPlayer != null && isValidState && hasValidSize) {
                if (mSeekWhenPrepared != 0) {
                    seekTo(mSeekWhenPrepared);
                }
                start();
            }
        }
	//surface第一次被创建的时候,立即调用
        public void surfaceCreated(SurfaceHolder holder)
        {
            mSurfaceHolder = holder;
            openVideo();
        }
	//This is called immediately before a surface is being destroyed.
        public void surfaceDestroyed(SurfaceHolder holder)
        {
            // after we return from this we can't use the surface any more
            mSurfaceHolder = null;
            if (mMediaController != null) mMediaController.hide();
            release(true);
        }
    };

(2)初始化在整个播放中都会用到而且很重要的两个变量

          mCurrentState = STATE_IDLE;

          mTargetState  = STATE_IDLE;

2、调用构造函数,生成VideoView对象之后,我们会调用setVideoPath或者setVideoURI传入播放地址。

在setVideoURI(Uri uri, Map<String, String> headers)中会调用openVideo()方法,这是一个很重要的方法。不过只有在setVideoURI和surfaceCreated都执行过后才会真正执行openVideo中的东西,具体看源码。到底是setVideoURI先执行还是surfaceCreated,得看具体的实现流程。

也就是说,只有surface和URI后准备好之后,才会继续。

openVideo中到底做了什么工作呢?

其实看了源码后,也很好理解。初始化了一个MediaPlayer播放器,给这个播放器设置各种监听器,最重要的是调用了MediaPlayer的setDataSource方法传入上面取到的URI,调用mMediaPlayer.setDisplay(mSurfaceHolder);将画面输出到surface中,接着调用mMediaPlayer.prepareAsync();准备数据去,最后mCurrentState = STATE_PREPARING;。

3、接着程序的流程就转到了,数据装备好之后了。在mPreparedListener监听器中。

首先状态进行改变:mCurrentState = STATE_PREPARED;

 

 

时刻牢记MediaPlayer的播放流程很重要:

要想播放,最终必须调用MediaPlayer的start方法,无论你之前执行了什么,这是最高原则!

当你发现不能播的时候,你首先想想是不是没有调用start方法。

setDataSource:传入一个播放地址

prepareAsync:异步准备数据去。此时的状态是Preparing状态,在这个状态调用seekTo是没有作用的,不过我们需要把需要seek的位置保留下来(mSeekWhenPrepared),当准备完成时,即在onPrepared回调方法中,状态变为Prepared,这时才真正的去执行seekTo。这时流程转到了和Seek相关的逻辑上,当seek完成时,即在onSeekComplete回调方法中,状态依旧是Prepared。这个时候你要想播放,还得调用start方法。



看一个简单的播放流程


//传入播放地址

setVideoURI   

//接着调用了seekto,不过此时的状态是   STATE_IDLE,因此先将需要seek的值保存在    mSeekWhenPrepared                                          
seekTo, mCurrentState : 0                                              
 

//接着SurfaceView可见了,surface被创建了出来,现在开始真正的去执行openVideo()      此时的状态是 STATE_PREPARING                                                                
surfaceCreated

//surfaceCreated 比如后调用一次surfaceChanged,此时1280*720这个值,是系统默认的或有什么策略吧                                                         
surfaceChanged, w : 1280 h : 720                                       
  

//接着影片装备好了 ,此时状态是    STATE_PREPARED        ,开始真正去seekto,然后去start,可以看源码流程,不过此时影片的尺寸(应该是分辨率)还没有获得

//不过不影响播放流程,以后获得也可以                         状态会进入            STATE_PLAYING           
onPrepared, mSeekWhenPrepared : 360000 mVideoWidth : 0 mVideoHeight : 0
seekTo, mCurrentState : 2     

//    进入这个回调,才说明得到了影片分辨率,然后根据这个分辨率去设定surface的分辨率尺寸。                                     
onVideoSizeChanged, mVideoWidth : 1920 mVideoHeight : 1072             
surfaceChanged, w : 1920 h : 1072                                      



注意:

(1)surfaceHolder.setFixedSize(w, h);是设置分辨率,视频窗口的大小是由surfaceView的大小决定的。只要设置surfaceView的layout就行了。(这句话不一定是对的)到现在我都没搞明白setFixedSize设置的是什么东西,到底有什么作用?

(2)播放影片,大小显示问题:

SurfaceView的onMesuare方法中的长宽是SurfaceView的大小,也就是SurfaceView在屏幕中显示的大小。SurfaceView中有一个Surface,这个Surface就是专门用来显示影片的。这个Surface有两个“大小”,一个大小是和SurfaceView的大小一样,就是在屏幕中显示的大小,另一个大小是这surface的分辨率,这个分辨率最终要和影片的分辨率一样,是通过setFixedSize来设置的。要想用户看到画面大小的变化,还是需要调整第一个大小,也就是SurfaceView的大小,也就是说调整SurfaceView的显示的大小,调整SurfaceView的大小,是通过surfaceView的setLayoutParams来调整的。setLayoutParams是告诉父控件,自己想在父控件中的宽高,这只是一种意愿,而真正surfaceView的大小还是在onMesure中决定的,不过,在onMeasure中,这种“意愿”反应在getDefaultSize(w, widthMeasureSpec)和getDefaultSize(h, heightMeasureSpec)中。

不过还有一点需要说明,在VideoView(也就是surfaceView)的onMesure中,有一个计算surfaceView的方法,它不是直接使用setLayoutParams传入的高宽(这个高宽可以是具体的值,比如屏幕的高宽,也可以是系统提供的属性值MATCH_PARENT,有时影片尺寸没有达到我们想要的效果,可能问题就是出在这里),而是让传入的高宽和影片的大小(也就是分辨率)进行运算,然后计算出一个高宽,这样做,是为了根据影片的比例(分辨率)很好的显示影片,而不会让影片看着走样。不过我们有时确实需要,让影片走样(比如全屏),那么,我们就要修改onMesure代码了。

总的来说,就是一句话:“用户看到影片的大小,是由SurfaceView的大小决定的!”

注意:上面的描述不一定对,but,对你的开发一定有帮助

 

20140123

调用VideoView的start方法,有两个作用:(1)由于状态还不对,调用start只是为了让mTargetState = STATE_PLAYING;为以后再次调用start能进行播放。(2)一切准备就绪真正的播放。

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值