天天看點

GsyVideoPlayer視訊分析(二)------------清單視訊播放視訊

思考以下幾個問題:

1.視訊視窗的大小和位置是如何比對ListView的item大小和位置的?

2.視訊播放畫面是如何顯示出來的?

3.視訊播放的聲音如何顯示出來的?

通過代碼分析;

 @Override

  public View getView(final int position, View convertView, ViewGroup parent) {

      ....

      ....

     listVideoUtil.addVideoPlayer(position, holder.imageView, TAG, holder.videoContainer, holder.playerBtn);

     holder.playerBtn.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                notifyDataSetChanged();

                //listVideoUtil.setLoop(true);

                listVideoUtil.setPlayPositionAndTag(position, TAG);

//                final String url = "https://tv.miguvideo.com/?from=singlemessage&isappinstalled=0#video/live/761358370/room201706301600304971_R1";

                //listVideoUtil.setCachePath(new File(FileUtils.getPath()));

                final String url = "http://baobab.wdjcdn.com/14564977406580.mp4";

                listVideoUtil.startPlay(url);

            }

        });

       ........

      .......

}

listview的adapter的getview方法每次都會走一次listVideoUtil.addVideoPlayer(position, holder.imageView, TAG, holder.videoContainer, holder.playerBtn);

    public void addVideoPlayer(final int position, View imgView, String tag,

                               ViewGroup container, View playBtn) {

        container.removeAllViews();

        if (isCurrentViewPlaying(position, tag)) {

            if (!isFull) {

                ViewGroup viewGroup = (ViewGroup) gsyVideoPlayer.getParent();

                if (viewGroup != null)

                    viewGroup.removeAllViews();

                container.addView(gsyVideoPlayer);

                playBtn.setVisibility(View.INVISIBLE);

            }

        } else {

            playBtn.setVisibility(View.VISIBLE);

            container.removeAllViews();   //增加封面

            container.addView(imgView);

        }

    }

 代碼可以看出addVideoPlayer方法的意思是,如果目前的position等于listVideoUtil中擷取到的position,videoContainer中就顯示gsyVideoPlayer,否則顯示視訊封面;

 點選播放按鈕的作用:

1.向listVideoUtil中設定position,并通過notifyDataSetChanged方法使重新走getview(),重新走getview方法時,會重新進入addVideoPlayer方法判斷;

   至此第一個疑問已經解決:視訊顯示在哪個item就是這個position決定的;視訊視窗的大小由videoContainer的大小決定;

2.執行代碼listVideoUtil.startPlay(url);

下面開始分析startPlay(url)中做了哪些操作?

    public void startPlay(String url) {

        if (isSmall()) {

            smallVideoToNormal();//如果是小視窗,就轉為正常視窗播放,這個問題後面再說

        }

        this.url = url;

        gsyVideoPlayer.release();

        gsyVideoPlayer.setLooping(isLoop);//視訊是否循環播放

        gsyVideoPlayer.setSpeed(speed);//視訊播放速度

        gsyVideoPlayer.setNeedShowWifiTip(needShowWifiTip);//非wifi環境下,顯示流量提醒

        gsyVideoPlayer.setNeedLockFull(needLockFull);//是否需要全屏鎖屏

        gsyVideoPlayer.setUp(url, true, cachePath, mapHeadData, objects);//設定邊緩存邊播放

        //增加title

        gsyVideoPlayer.getTitleTextView().setVisibility(View.GONE);

        //設定傳回鍵

        gsyVideoPlayer.getBackButton().setVisibility(View.GONE);

        //設定全屏按鍵功能

        gsyVideoPlayer.getFullscreenButton().setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                resolveFullBtn();

            }

        });

        gsyVideoPlayer.startPlayLogic();

    }

就是一些簡單設定項,其中gsyVideoPlayer.setUp(url, true, cachePath, mapHeadData, objects);會把url改為全局的mUrl;

    @Override

    public void startPlayLogic() {

        if (mStandardVideoAllCallBack != null) {

            Debuger.printfLog("onClickStartThumb");

            mStandardVideoAllCallBack.onClickStartThumb(mUrl, mObjects);

        }

        prepareVideo();

        startDismissControlViewTimer();

    }

    protected void prepareVideo() {

        if (GSYVideoManager.instance().listener() != null) {

            GSYVideoManager.instance().listener().onCompletion();

        }

        GSYVideoManager.instance().setListener(this);

        GSYVideoManager.instance().setPlayTag(mPlayTag);

        GSYVideoManager.instance().setPlayPosition(mPlayPosition);

        addTextureView();//動态添加TextureView

        mAudioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);//暫時擷取音頻焦點

        ((Activity) getContext()).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//保持螢幕常亮不滅屏

        GSYVideoManager.instance().prepare(mUrl, mMapHeadData, mLooping, mSpeed);

        setStateAndUi(CURRENT_STATE_PREPAREING);

    }

    科普1,音頻焦點分類:

    AudioManager.AUDIOFOCUS_REQUEST_GRANTED------------永久擷取音頻焦點

    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT-------------暫時擷取音頻焦點,比如音樂背景播放,目前視訊播放會搶奪音頻焦點,視訊播放完成,音樂自動播放;

   AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK--------提示類型擷取音頻焦點,比如聽音樂的時候來短信,音樂聲音降低

    protected void addTextureView() {

        if (mTextureViewContainer.getChildCount() > 0) {

            mTextureViewContainer.removeAllViews();

        }

        mTextureView = null;

        mTextureView = new GSYTextureView(getContext());

        mTextureView.setSurfaceTextureListener(this);

        mTextureView.setRotation(mRotate);

        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);

        layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);

        mTextureViewContainer.addView(mTextureView, layoutParams);

    }

    構造了一個TextureView,并添加到gsyVideoPlayer布局中。

    TextureView設定了SurfaceTextureListener;

     科普2:SurfaceTextureListener回調方法:

 1.onSurfaceTextureAvailable(SurfaceTexture arg0, int arg1, int arg2)-------------TextureView可用時調用

 2.onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)

 3.onSurfaceTextureDestroyed(SurfaceTexture surface)------------TextureView銷毀時調用

 4.onSurfaceTextureUpdated(SurfaceTexture surface)

   回到代碼:

    @Override

    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {

        mSurface = new Surface(surface);

        GSYVideoManager.instance().setDisplay(mSurface);

        //顯示暫停切換顯示的圖檔

        showPauseCover();

    }

    意思是:在TextureView可以使用時,将surface交給GSYVideoManager,用于顯示視訊的畫面,至此第二個問題已經解決,當然此時還沒有開始播放視訊, 真正播放視訊是在

GSYVideoManager.instance().prepare(mUrl, mMapHeadData, mLooping, mSpeed)中,此處的mUrl就是上面儲存的url;

public void prepare(final String url, final Map<String, String> mapHeadData, boolean loop, float speed) {
    if (TextUtils.isEmpty(url)) return;
    Message msg = new Message();
    msg.what = HANDLER_PREPARE;
    GSYModel fb = new GSYModel(url, mapHeadData, loop, speed);
    msg.obj = fb;
    mMediaHandler.sendMessage(msg);
}      
public class MediaHandler extends Handler {
    public MediaHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case HANDLER_PREPARE:
                initVideo(msg);
                break;      
private void initVideo(Message msg) {
    try {
        currentVideoWidth = 0;
        currentVideoHeight = 0;
        mediaPlayer.release();

        if (videoType == GSYVideoType.IJKPLAYER) {
            initIJKPlayer(msg);
        } else if (videoType == GSYVideoType.IJKEXOPLAYER) {
            initEXOPlayer(msg);
        }
        setNeedMute(needMute);
        mediaPlayer.setOnCompletionListener(GSYVideoManager.this);
        mediaPlayer.setOnBufferingUpdateListener(GSYVideoManager.this);
        mediaPlayer.setScreenOnWhilePlaying(true);
        mediaPlayer.setOnPreparedListener(GSYVideoManager.this);
        mediaPlayer.setOnSeekCompleteListener(GSYVideoManager.this);
        mediaPlayer.setOnErrorListener(GSYVideoManager.this);
        mediaPlayer.setOnInfoListener(GSYVideoManager.this);
        mediaPlayer.setOnVideoSizeChangedListener(GSYVideoManager.this);
        mediaPlayer.prepareAsync();

    } catch (Exception e) {
        e.printStackTrace();
    }
}      

根據videoType的類型,建立不同的播放器,initIJKPlayer(msg)或者initEXOPlayer(msg);

不管哪種播放器,msg中有視訊播放的url,用于播放器播放視訊使用;

最後調用mediaPlayer.prepareAsync()播放視訊;