美文网首页iOS开发心得android 集结号android菜鸟低调修行纪念册
Android 实现视屏播放器、边播边缓存功能、外加铲屎(IJK

Android 实现视屏播放器、边播边缓存功能、外加铲屎(IJK

作者: 恋猫月亮 | 来源:发表于2016-11-18 17:59 被阅读58212次

    Android 实现视屏播放器与边播边缓存功能外加蹲坑铲屎(IJKPlayer)

    hello,大家好,我就是那个会掀桌子的话唠,刚刚结束两篇关于音频播放与录制的文章,旧坑未埋就挖新坑,还望多多关照。最近累趴了,周末果断休假。

    </p>

    快看,用力戳它:https://github.com/CarGuo/GSYVideoPlayer 。项目是翻改至<a href="https://github.com/lipangit/JieCaoVideoPlayer">JieCaoVideoPlayer</a>,本文特长,看官请耐心,妹子会有的。

    效果


    </p>

    开源播放器选择

    </p>

    Android上最为人熟知的MediaPlayer,对,就是这货,在上两篇音频文章中频频露脸的家伙,这次又有它的身影,然而还是这次不讲他,就连他的封装类VideoView也不讲<( ̄︶ ̄)>,呸呸呸,又扯了一堆没用的。

    • <a href="https://github.com/Bilibili/ijkplayer">ijkplayer</a>,这次要推荐的是它,鼎鼎大名的BILIBILI开源的播放器。基于FFMPEG,支持Android与IOS,还封装了谷歌亲儿子MediaPlayer与干儿子<a href="https://github.com/google/ExoPlayer">EXOPlayer</a>(为什么要用EXO),支持直播流,Star-9000多与fork-3000的视频播放器你支持安利。(issues 600多算活跃吗┑( ̄Д  ̄)┍)

    集成工作还是有定的工作量的,它的DEMO肯定满足不了欲求不满的设计狮和产品汪的,这里我们不跑分,不打广告,不讲原理,只求站在巨人的肩膀上学(cao)习(xi),快速集成。

    • 定义一个单例的视频内核播放管理器。
    • 自定义一个满足你上下其手的TextureView
    • 定义一个UI层级逻辑播放器
    • 重力旋转的相关逻辑处理
    • 列表逻辑的相关处理
    • 列表到全屏相关的逻辑处理
    • 视频缓存逻辑

    1、播放管理器:GSYVideoManager

    单例,没得商量,它需要负责真正的播放请求与显示逻辑,集成了IjkMediaPlayer,BILIBLI的开源小组还是很有心的,它的封装和接口使用基本和MediaPlayer没有什么区别,只需要用起来就好了。‘

    这里我们要实现IjkMediaPlayer的播放接口,监听IjkMediaPlayer的相关状态回调然后封发到各个逻辑播放器中。从下方代码可以看到,真的和MediaPlayer好像。

    mediaPlayer = new IjkMediaPlayer();
    //音频类型
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    //数据源
    mediaPlayer.setDataSource(((GSYModel) msg.obj).getUrl(), ((GSYModel) msg.obj).getMapHeadData());
    //播放完成
    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();
    

    监听的回调接口里,大部分大家都耳目能详吧,没听过也没关系,都写上就对了,但是最主要需要关注的两个,一个是通过setOnVideoSizeChangedListener拿到视频宽和高,这是我们后续正常显示视频的依靠之一。

    另外一个就是setOnInfoListener,这里我们主要是获取到视频相关的元信息里视频旋转角度!还记得那时候对视频播放不熟悉,和产品还有QA力争“这个视频本来就是转了90度的,我就不改,你咬我吗···”这样的黑历史。Σ( ° △ °|||)

    特别是Android拍摄的竖屏视频,旋转不是视频本身的图像,而是增加了旋转信息,而这个时候你需要做的就是识别它,然后转了它丫的。另外,因为Android本身的MediaPlaer和VideoView自身就处理好所以不需要你旋转。((ノO益O)ノ彡┻━┻亲生的啊)

    这里的接口主要是把当前播放的视频状态和信息到返回到逻辑播放器中。

    @Override
    public void onInfo(int what, int extra) {
        if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {
            BACKUP_PLAYING_BUFFERING_STATE = mCurrentState;
            setStateAndUi(CURRENT_STATE_PLAYING_BUFFERING_START);
        } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {
            if (BACKUP_PLAYING_BUFFERING_STATE != -1) {
                setStateAndUi(BACKUP_PLAYING_BUFFERING_STATE);
                BACKUP_PLAYING_BUFFERING_STATE = -1;
            }
        } else if (what == IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED) {
            //这里返回了视频旋转的角度,根据角度旋转视频到正确的画面
            mRotate = extra;
            if (mTextureView != null)
                mTextureView.setRotation(mRotate);
        }
    }
    
    

    2、自定义TextureView:GSYTextureView

    为什么不用SurfaceView?因为TextureView很可爱啊。这里我们主要针对视频的大小和旋转角度设置TextureView的大小,详细就不多说了(不是懒),挑其中一类讲讲,因为主要也是这个。

    • 例如根据视频的长宽比和屏幕的长宽比判断,如果视频宽与屏幕宽之比小于高之比,那么就需要按理比压缩宽度,然后高度适应屏幕。
    • 例如根据旋转信息,判断TextureView界面的比例是横的还是竖的,如果View是竖的,而视频也是竖的,那么因为旋转了90度,那么让视频的高显示为屏幕的宽度,从新计算旋转后的宽度。

    觉得看起来有点绕口?没关系,用着用着就习惯了····

    width = widthSpecSize;
    height = heightSpecSize;
    ···
    if (videoWidth * height < width * videoHeight) {
        width = height * videoWidth / videoHeight;
    } else if (videoWidth * height > width * videoHeight) {
        height = width * videoHeight / videoWidth;
    }
    ···
    if (getRotation() != 0 && getRotation() % 90 == 0) {
        if (widthS < heightS) {
            if (width > height) {
                width = (int) (width * (float) widthS / height);
                height = widthS;
            } else {
                height = (int) (height * (float) width / widthS);
                width = widthS;
            }
        } else {
            if (width > height) {
                height = (int) (height * (float) width / widthS);
                width = widthS;
            } else {
                width = (int) (width * (float) widthS / height);
                height = widthS;
            }
        }
    }
    
    

    3、UI层级逻辑播放器 GSYVideoPlayer

    所有的UI逻辑基本都可以写到这里,目前继承了 FrameLayout,View.OnClickListener, View.OnTouchListener, SeekBar.OnSeekBarChangeListener, TextureView.SurfaceTextureListener和GSYMediaPlayerListener。
     
     逻辑播放器实现的内容太多了,这里主要说几个地方,好吧,我承认我懒╮(╯_╰)╭ ,但是写太多了也没人看啊,所以这里主要是说一些关键的点,有需要留言再开个坑聊一聊,反正有DEMO。

    • 记录界面的播放状态,把播放管理器GSYVideoManager的状态记录下来,如果有别的逻辑播放器点击播放了,就把原本的逻辑播放器状态清空,所有逻辑播放器的整个界面的UI都是根据这个State来决定的。

    在逻辑播放器中统一分发各种状态,把被播放的manager状态同步到这里,之后你想要在哪个逻辑播放器里播放只需要对应的设置状态后把manager的监听同步过来。

    switch (mCurrentState) {
        //正常初始化状态
        case CURRENT_STATE_NORMAL:
            if (isCurrentMediaListener()) {
                cancelProgressTimer();
                GSYVideoManager.instance().releaseMediaPlayer();
            }
            break;
    
        //loading中
        case CURRENT_STATE_PREPAREING:
            resetProgressAndTime();
            break;
        //播放中
        case CURRENT_STATE_PLAYING:
            startProgressTimer();
            break;
        //暂停
        case CURRENT_STATE_PAUSE:
            startProgressTimer();
            break;
        //错误-需要判断是否切换了逻辑播放器
        case CURRENT_STATE_ERROR:
            if (isCurrentMediaListener()) {
                GSYVideoManager.instance().releaseMediaPlayer();
            }
            break;
        //结束
        case CURRENT_STATE_AUTO_COMPLETE:
            cancelProgressTimer();
            mProgressBar.setProgress(100);
            mCurrentTimeTextView.setText(mTotalTimeTextView.getText());
            break;
    }
    
    • 增加界面的onTouch事件,根据ViewgetId判断触摸的是进度条还是界面,如果是界面判断是左右滑动就显示DialogseekTo,如果是上下就根据屏幕的左边还是右边来选择是调节音量还是亮度
    ···
    case MotionEvent.ACTION_MOVE:
        float deltaX = x - mDownX;
        float deltaY = y - mDownY;
        float absDeltaX = Math.abs(deltaX);
        float absDeltaY = Math.abs(deltaY);
        //是全屏还是设置了可以触摸
        if (mIfCurrentIsFullscreen || mIsTouchWiget) {
            //之前是否已经符合了触摸逻辑条件
            if (!mChangePosition && !mChangeVolume && !mBrightness) {
                //如果手指动了超过一定距离就可以判断是滑动,防止点击的误判的
                if (absDeltaX > mThreshold || absDeltaY > mThreshold) {
                    cancelProgressTimer();
                    //如果是左右的就是进度
                    if (absDeltaX >= mThreshold) {
                        mChangePosition = true;
                        mDownPosition = getCurrentPositionWhenPlaying();
                        if (mVideoAllCallBack != null && isCurrentMediaListener()) {
                            mVideoAllCallBack.onTouchScreenSeekPosition(mUrl, mObjects);
                        }
                    } else {
    
                    //如果是上下的判断是左边还是右边
                        if (mFirstTouch) {
                            mBrightness = mDownX < mScreenWidth * 0.5f;
                            mFirstTouch = false;
                        }
                        if (!mBrightness) {
                            mChangeVolume = true;
                            mGestureDownVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
                            if (mVideoAllCallBack != null && isCurrentMediaListener()) {
                                mVideoAllCallBack.onTouchScreenSeekVolume(mUrl, mObjects);
                            }
                        }
                    }
                }
            }
        }
        ···
        //根据flag执行逻辑
    
    • 要监听TextureView.setSurfaceTextureListener来通知画面的创建和销毁,比如回到后台,onPause等。

    这里有一个是TextureView的动态添加,动态添加的好处是你可以在不停止视频的情况下载不同的逻辑播放器中切换视频播放,比如列表全屏。

    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);
    }
    
    ···
    
    //把Surface丢给视频播放管理
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        mSurface = new Surface(surface);
        GSYVideoManager.instance().setDisplay(mSurface);
    }
    
    //告诉视频播放渲染画面销毁了
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        GSYVideoManager.instance().setDisplay(null);
        surface.release();
        return true;
    }
    
    
    • 每次播放都要把Manager的player的监听移到当前播放的逻辑播放器,这样才能够正确的监听视频的播放状态。
    //这里其实就有播放管理器的监听分发保存的逻辑需要注意
    GSYVideoManager.instance().setLastListener(this);
    GSYVideoManager.instance().setListener(gsyVideoPlayer);
    
    

    3、列表全屏逻辑 :Window层级的全屏、单例逻辑播放器的全屏ListVideoUtil。

    </p>
    效果GIF(比较大):

    1)、Window层级的

    传闻每一个Activity都有一个com.android.internal.R.id.content,它默默的包含了各种你塞进去的物体,而且是一个FrameLayout,谷歌有太多它的传说了,我们用它是就是。

    既然是FrameLayout,那么我们往他里面塞东西就好了,这里我们可以在GSYVideoPlayer里面写一个方法,在点击全屏按钮的时候:

    • 隐藏状态栏,清除当前TextureView。
    • 然后新创建一个GSYVideoPlayer2,只有把这个G2添加到window下FrameLayout
    • 设置它的播放状态和当前列表这个逻辑播放器一致。
    • 最后把G2告知Manager承接画面,这样是就实现了无缝的列表到全屏啦,返回只需要倒着做就好了。

    在切换的时候可以做一些位移动画,让播放器的全屏更加友好,下面长代码来袭((/- -)/。深夜码字不易,不知道为什么每次这个时候老婆的意见很大啊。

    Constructor<GSYBaseVideoPlayer> constructor = (Constructor<GSYBaseVideoPlayer>) GSYBaseVideoPlayer.this.getClass().getConstructor(Context.class);
    final GSYBaseVideoPlayer gsyVideoPlayer = constructor.newInstance(getContext());
    //记录新创建的这个video的id,在返回的时候通过它销毁
    gsyVideoPlayer.setId(FULLSCREEN_ID);
    WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    final int w = wm.getDefaultDisplay().getWidth();
    final int h = wm.getDefaultDisplay().getHeight();
    //设置黑色背景,自动充满全屏
    FrameLayout.LayoutParams lpParent = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    FrameLayout frameLayout = new FrameLayout(context);
    frameLayout.setBackgroundColor(Color.BLACK);
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        //如果5.0的话,先让播放器出现的位置和列表中一直,再样式一会执行到屏幕中间的过度动画效果
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(getWidth(), getHeight());
        lp.setMargins(mListItemRect[0], mListItemRect[1], 0, 0);
        frameLayout.addView(gsyVideoPlayer, lp);
        vp.addView(frameLayout, lpParent);
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                TransitionManager.beginDelayedTransition(vp);
                resolveFullVideoShow(context, gsyVideoPlayer, h, w);
            }
        }, 300);
    } else {
        //5.0一下直接显示
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(getWidth(), getHeight());
        frameLayout.addView(gsyVideoPlayer, lp);
        vp.addView(frameLayout, lpParent);
        resolveFullVideoShow(context, gsyVideoPlayer, h, w);
    }
    //设置全屏逻辑播放器的状态,动态及添加播放view
    gsyVideoPlayer.setUp(mUrl, mCache, mObjects);
    gsyVideoPlayer.setStateAndUi(mCurrentState);
    gsyVideoPlayer.addTextureView();
    //添加监听
    GSYVideoManager.instance().setLastListener(this);
    GSYVideoManager.instance().setListener(gsyVideoPlayer);
    
    
    2)、ListVideoUtil的单例模式

    这里利用另外一种实现思路,列表的逻辑播放器只用一个,因为普通的list在滑动的时候会有复用和销毁,这会导致视频被释放而停止了,如果你是和今日黄(tou)条一样的视频列表播放效果,滑出屏幕就停止那无所谓。

    如果你需要无论怎么滑动,视频都在原来的位置播放的话,那么ListVideoUtil适合你,,内部它已经带了全屏,防错位,旋转的各种逻辑,直接上代码,有兴趣的看DEMO。

    
    listVideoUtil = new ListVideoUtil(this);
    //设置列表最外层的布局用于全屏,空FrameLayout
    listVideoUtil.setFullViewContainer(videoFullContainer);
    //全屏隐藏状态栏,如果有的话
    listVideoUtil.setHideStatusBar(true);
    
    ···
    //在列表中吧列表位置,封面,哪个列表的TAG,列表视频的承载ViewGroup,播放按键传入到Utils中
    listVideoUtil.addVideoPlayer(position, imageView, TAG, holder.videoContainer, holder.playerBtn);
    holder.playerBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //每次播放都要更新列表让其他的item恢复状态
            notifyDataSetChanged();
            //设置播放的tag和位置,防止错位
            listVideoUtil.setPlayPositionAndTag(position, TAG);
            //开始播放
            final String url = "http://baobab.wdjcdn.com/14564977406580.mp4";
            listVideoUtil.startPlay(url);
        }
    });
    
    

    4、OrientationUtils 重力旋转的工具类

    OrientationUtils使用的是OrientationEventListener,通过手机的角度判断需要旋转到哪个位置。为什么用它?因为谷歌到的时候刚好看到,缘分啊懂吗。

    这里需要个关注的是手动点击和自动旋转之间的冲突,主要看代码吧,老婆开始催我了 (ノಠ益ಠ)ノ彡┻━┻。

    //判断系统是否开了旋转,是的,这货不需要系统旋转是否开启
    boolean autoRotateOn = (android.provider.Settings.System.getInt(activity.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 1);
            if (!autoRotateOn) {
                if (mIsLand == 0) {
                    return;
                }
            }
            // 设置竖屏
            if (((rotation >= 0) && (rotation <= 30)) || (rotation >= 330)) {
                //是否点击导致的
                if (mClick) {
                    if (mIsLand > 0 && !mClickLand) {
                        return;
                    } else {
                        //清除状态
                        mClickPort = true;
                        mClick = false;
                        mIsLand = 0;
                    }
                } else {
                    //自动旋转
                    if (mIsLand > 0) {
                        screenType = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                        gsyVideoPlayer.getFullscreenButton().setImageResource(R.drawable.video_enlarge);
                        mIsLand = 0;
                        mClick = false;
                    }
                }
            }
            // 设置横屏
            else if (((rotation >= 230) && (rotation <= 310))) {
                if (mClick) {
                    if (!(mIsLand == 1) && !mClickPort) {
                        return;
                    } else {
                        mClickLand = true;
                        mClick = false;
                        mIsLand = 1;
                    }
                } else {
                    if (!(mIsLand == 1)) {
                        screenType = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                        gsyVideoPlayer.getFullscreenButton().setImageResource(R.drawable.video_shrink);
                        mIsLand = 1;
                        mClick = false;
                    }
                }
            }
            // 设置反向横屏
            else if (rotation > 30 && rotation < 95) {
                if (mClick) {
                    if (!(mIsLand == 2) && !mClickPort) {
                        return;
                    } else {
                        mClickLand = true;
                        mClick = false;
                        mIsLand = 2;
                    }
                } else if (!(mIsLand == 2)) {
                    screenType = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
                    activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                    gsyVideoPlayer.getFullscreenButton().setImageResource(R.drawable.video_shrink);
                    mIsLand = 2;
                    mClick = false;
                }
            }
        }
    };
    orientationEventListener.enable();
    

    6、边播边缓存

    好吧,老婆睡了,我偷偷起来了(。・・)ノ
     这个需求曾经让我彻夜难眠,因为IJKPlayer不支持,好吧,没见过哪个播放器支持的,和产品争(tuo)论(yan)需(shi)求(jian)之后,最终还是github大法好:<a href="https://github.com/danikula/AndroidVideoCache">AndroidVideoCache</a>。

    接入简单,使用简单,你可以趾高气扬的和产品说,这个so easy了。

    HttpProxyCacheServer proxy = getProxy();
    //注意不能传入本地路径,本地的你还传进来干嘛。
    String proxyUrl = proxy.getProxyUrl(VIDEO_URL);
    videoView.setVideoPath(proxyUrl);
    
    

    该项目的原理其实就是将url链接转化为本地链接 h t t p://127.0.0.1:LocalPort/url,然后它开一个服务器一边下载缓存视频,一边把缓存的数据正常返回给你的播放器,如果已经缓存过的这里会返回一个本地文件路径。Σ( ° △ °|||)︴曾经的我真的是too young too smiple。

    5、一些坑和说明

    • 1、IJKPLAY的后台播放和回到前台恢复画面的速度之快是其他播放器(我坐井观天)无法比拟的,真的好快,而且适合你,因为你什么都不用做。

    • 2、IJKPLAY有一个问题,我也提过ISSUSE了 <a href="https://github.com/Bilibili/ijkplayer/issues/2104">#2104</a>,不过目前还未解决,就是某些短小的视频会无法seekTo,说是FFMEPG的问题,然后就太监了。

    • 3、IJKPLAY库里还封装了exoplayer谷歌干儿子,用法也基本一致,这个播放器自己内部判断旋转,不会有上面的seekto问题,可是后台或者onPause之后的画面恢复速度堪忧啊,各位遇到过吗?

    • 4、千万别开硬解码,不然会这样。 ( ‵o′)凸

    • 5、拖动进度条,需要在停止拖动的时候,判断视频是不是已经播放完了被释放了。

    • 6、如果横屏全屏的话,恢复到正常画面是最好有一个延时,这样画面才不会出现背景抖动的问题,还有最关键的,Maifest文件。

    //不要忘记配置activity,所有背景的activity
    
    android:configChanges="orientation|keyboardHidden|screenSize"
    
    
    • 7、普通列表中播放视频在快速移动可能出现的错位问题
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        int lastVisibleItem = firstVisibleItem + visibleItemCount;
        //大于0说明有播放
        if (GSYVideoManager.instance().getPlayPosition() >= 0) {
            //当前播放的位置
            int position = GSYVideoManager.instance().getPlayPosition();
            //对应的播放列表TAG
            if (GSYVideoManager.instance().getPlayTag().equals(ListNormalAdapter.TAG)
                    && (position < firstVisibleItem || position > lastVisibleItem)) {
                //如果滑出去了上面和下面就是否,和今日头条一样
                GSYVideoPlayer.releaseAllVideos();
                listNormalAdapter.notifyDataSetChanged();
            }
        }
    }
    
    

    到底了呢(o)/。

    下面的的看到了吗 ?<( ̄︶ ̄)>

    点我点我上60级:https://github.com/CarGuo/GSYVideoPlayer

    </p>
    能看到这里都是真爱啊,我最后问两句,你们会觉得文章太长阅读起来比较费劲吗?

    相关文章: Android 列表视频的全屏、自动小窗口优化实践
     
    友情链接:

    • <a href="https://github.com/CarGuo/GSYVideoPlayer">GSYVideoPlayer</a>

    • <a href="https://github.com/Bilibili/ijkplayer">ijkplayer</a>

    • <a href="https://github.com/danikula/AndroidVideoCache">AndroidVideoCache</a>

    • <a href="https://github.com/lipangit/JieCaoVideoPlayer">JieCaoVideoPlayer</a>

    相关文章

      网友评论

      • 96dc7dc74904:大佬,有没有实现加载时,显示加载速度,或者记载加载网络??
      • 小蘑菇的驼羊:大佬,StandardGSYVideoPlayer有没有监听播放完毕的接口?
      • asla:楼主,问一个小白问题,用ijkplayer通过rtsp拉流,可是已经设置了ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "rtsp_transport", "tcp");拉倒的视频还是一直有灰屏,是因为什么?因为我才开始用ijkplayer,不是很了解,但感觉这样设置了应该没有问题。我用你的app拉流拉倒的是没有问题的,可是在我的APP里拉流就出了问题,能否帮忙分析下?
      • FTE:感谢楼主的分享
      • 409d496d7545:大神我想问下你这个播放器说的是支持视频转GIF,我没有找到这个dome,可否给我说下大概的方法。
      • BigCoder:恋猫大神,GSYPlayer支持视频缓存,我想问问要是存储空间满了,有做处理吗?做的什么样的处理?
        BigCoder:APP中我将视频缓存到本地,以便下次播放同一个视频时不用去联网重新播放,我在想如果手机空间满了,会不会出错?
      • ysm_2015:多谢大神的指导!!!让我成功的完成了毕业以来的第一个项目!
      • 孤独的根号零:请教一个问题,gsy播放视屏时全屏的状态下,按虚拟返回键直接退出Activity可能有哪些原因造成的
      • dcd721857c58:为是GSY?
      • yann02:大佬,视频播放完成后,拖动进度条没有响应,我想拖动后能从那个地方开始播放怎么办啊?
      • zero0123:GSYVideoPlayer 有没有OnSeekBarChangeListener 这样的监听 需要设置视频观看3分钟之后付费播放在哪里设置呢
        GoAllOutInWork:哥们 你这个需求完成了吗?可以指导一下吗?
      • ysm_2015:楼主 你这个网络视频只能在wifi状态下播放 流量情况下没有办法播放啊
      • 木子v大可:这个框架可以不可以先缓存完毕之后再执行播放了?
      • kingple:现在倍速范围是0.5-2 作者又没有开放ijk编译配置,作者能不能扩大倍速比范围到0.1-10或者开放ijk编译配置我这边自己编译ijk
      • 杨杰C:大佬,我使用输入url的方式播放rtsp流一直没翻译,是要用其他方式播放吗?
      • 灼眼行者:您好,这个封装真的特别好,非常感谢,想请教一个问题,就是播放视屏一开始就需要设置成全屏,怎么设置呢?
      • 954583961ecc:GSYVideoManager.instance().getMediaPlayer().seekTo(GSYVideoManager.instance().getMediaPlayer().getCurrentPosition() + 20);我是用在机顶盒播放的,为什么移动过去缓冲的时候又回复到原来的位置了,是我使用的不对么
        一米阳光_060e:我现在也在用机顶盒写视频播放,需求要无缝播放,用的原生的Mediaplaer+SurfaceView,手机上可以正常切换,机顶盒中间就会有黑屏,什么原因呢?
      • 西门吹雪少帅:学习了,可以留下通讯方式么,或者建个群。
      • RamboPan:作为一只菜鸟,想弱弱的问问,如果需要加签名怎么改。:pensive:
        因为我们公司的平板都是需要添加签名才能安装的,然后我发现如果添加了签名文件,就报错了。
        [
        Error:(52, 0) No signature of method:
        org.gradle.api.internal.plugins.DefaultExtraPropertiesExtension.dataDependencies() is applicable for argument types:
        (com.neenbedankt.gradle.androidapt.AndroidAptExtension_Decorated) values:
        [com.neenbedankt.gradle.androidapt.AndroidAptExtension_Decorated@118e6cb7]
        <a href="openFile:/Users/rambopan/AndroidStudioProjects/GSYVideoPlayer2/app/build.gradle">Open File</a>
        ]
        发现你好像是做了一个配置文件,所有module来引用,感觉嵌套有点多,不知道怎么操作了。:sob:
      • 25c3b30b0df0:请问怎么设置缓存的maxCacheSize()
      • 朱亚朋:大神,我从GitHub上下载了demo 在列表里面播放的时候没有声音,画面正常,全屏了以后才能正常有声音。是本身就这样还是代码有问题?
        朱亚朋:@恋猫月亮 哦已经解决了 谢谢大神
        恋猫月亮:@朱亚朋 代码里设置了列表静音
      • 97a2688fa508:你好,请问这个有没有指定时间播放的方法的。能不能获取到视频的总时间和播放的当前时间点。????
        97a2688fa508:我找到相应得方法了。视频总时间进度 mGsy.getDuration(); 当前播放进度 mGsy.getCurrentPositionWhenPlaying(); 指定跳转时间进度 mGsy.setSeekOnStart(60123); 注:60秒123(时间格式必须是 至少4位数) 感谢博主!!!!
      • fozei:非常的赞!!!
      • 许沫梓:博主你好,我想问一下,那个获取视频当前播放帧怎么获取
        恋猫月亮:@许沫梓 最简单的推荐用gilde,不过也可以用其他方式,参考demo中就有
      • 25c3b30b0df0:如果程序长时间处于后台,被销毁,再点击程序,出现崩溃现象,而且是必现
        恋猫月亮:@twofly 添加状态保存和恢复接口,或者判断被回收了重新打开
        25c3b30b0df0:@恋猫月亮 那我改怎么解决这个问题,麻烦指导下
        恋猫月亮:@twofly demo中没有做状态恢复
      • GoAllOutInWork:你好,请问能直接设置横屏显示吗?设置让Activity强制横屏 所以希望进来的时候直接全屏播放,可以实现吗?
        恋猫月亮:@GoAllOutInWork 可以,demo就有
      • 汪珺:大神,ScrollView嵌套使用全屏会崩溃呀
        汪珺:@恋猫月亮 出错堆栈
        1 java.lang.NoClassDefFoundError:Failed resolution of: Landroid/support/v7/internal/view/ContextThemeWrapper;
        2 com.shuyu.gsyvideoplayer.utils.CommonUtil.getAppCompActivity(CommonUtil.java:181)
        3 ......
        4 Caused by:
        5 java.lang.ClassNotFoundException:Didn't find class "android.support.v7.internal.view.ContextThemeWrapper" on path: DexPathList[[zip file "/data/app/on.gcbdemo.com.gcbdemo-1/base.apk"],nativeLibraryDirectories=[/data/app/on.gcbdemo.com.gcbdemo-1/lib/arm, /data/app/on.gcbdemo.com.gcbdemo-1/base.apk!/lib/armeabi-v7a, /vendor/lib, /system/lib]]
        6 dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
        7 java.lang.ClassLoader.loadClass(ClassLoader.java:511)
        8 java.lang.ClassLoader.loadClass(ClassLoader.java:469)
        9 com.shuyu.gsyvideoplayer.utils.CommonUtil.getAppCompActivity(CommonUtil.java:181)
        10 com.shuyu.gsyvideoplayer.utils.CommonUtil.hideSupportActionBar(CommonUtil.java:122)
        11 com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer.startWindowFullscreen(GSYBaseVideoPlayer.java:471)
        12 com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer.startWindowFullscreen(StandardGSYVideoPlayer.java:324)
        13 on.gcbdemo.com.gcbdemo.ui.DetailPlayer$3.onClick(DetailPlayer.java:152)
        14 android.view.View.performClick(View.java:5273)
        15 android.view.View$PerformClick.run(View.java:21315)
        16 android.os.Handler.handleCallback(Handler.java:743)
        17 android.os.Handler.dispatchMessage(Handler.java:95)
        18 android.os.Looper.loop(Looper.java:150)
        19 android.app.ActivityThread.main(ActivityThread.java:5665)
        20 java.lang.reflect.Method.invoke(Native Method)
        恋猫月亮:@汪珺 log是??
      • 那一刻彷徨:为啥有些 mp4格式的 会出现 只有声音没有画面
        恋猫月亮:@那一刻彷徨 看了看看项目的github首页
      • 7ecf07e3455b:拍摄 那个DEMO 能不能实现一个 类似WebDetailActivity 的 功能?或者能不能 讲讲 思路 , 我的需求 是 将横屏 拍摄的内容 竖着 置顶显示出来, 当我旋转preview的时候 发现要么内容是压扁的 要么 就是只能播放一个摄像头竖的分辨率下的内容
        7ecf07e3455b:@恋猫月亮 我是拍摄的时候~
        我试试 重新计算宽高~
        恋猫月亮:@阿里赤壁 你是想实现实时拍摄的时候,横屏拍摄的内容着显示是吧?还是拍摄完?拍摄完的你可以在拍摄的时候增加可以rotation属性,如果是实时,那么你可以旋转渲染层,然后对渲染控件重新计算宽高
      • 7ecf07e3455b:拍摄那个 能做成 GSYVideoPlayer 中WebDetailActivity的效果吗 ?就是将横屏的内容 竖着播放
      • 11985abefa30:你好 我想在视频左下角添加一个播放按钮控制视频的播放及暂停逻辑怎么处理呢?
        还有就是那个视频加载的动画效果是在哪里更改?
      • lebonbill:大哥,AndroidVideoCache不支持流媒体缓存,有没有其他好的方案,用ijk边播放边录制
      • 浩仔_Boy:放入直播链接,顶部有个绿色条,楼主,这个绿条怎么去掉呀?谢谢!
      • 2012lc:大神,与下拉刷新的recyclerview配合使用的时候,全屏播放会错位,这是怎么回事哎~
        2012lc:@恋猫月亮 恩,已经解决了,谢谢
        恋猫月亮: @2012lc 设置position和tag,参加demo
      • bdf0379f96ed:GSYVideoType.enableMediaCodec();//开启硬解码功能,只要开启了硬解码功能就各种BUG,包括变成右下角的小窗口视频不显示,开启全屏时和缩小全屏时也是视频不显示,或是显示延迟严重
      • 明风桦:怎么静音呢,就是一播放就是静音模式,只有点击进去才是有声音的
      • 浩仔_Boy:在全屏视频状态下,可不可以,添加上滑播放下一个视频,下滑播放上一个视频呢,类似抖音。
      • LeoZzz:大神 问你个问题 为什么播放视频的时候 出现黑屏 只有声音。。。 我是 iOS..........!!!!! 请问 你有没有遇到过 这个问题,我按照官网的 支持更多编码格式了。。 黑屏视频格式 MP4
        编码 是h264
      • 慕容墨风:不费劲,还觉得不够详细:joy:
      • 360a88b0e4cf:大神,这个不支持mkv格式的视频吗。
        Link Or mCache Error
        恋猫月亮: @朱玉麟 mkv的没打包进入
      • 波波一起飞:看了还是不会用!
      • 3ffe662ae777:播放大的视频文件(.mov格式)会卡啊, wmv格式播放不了,能否查看下什么原因啊
      • GoAllOutInWork:你好,请问一下我使用的是列表布局,为什么展开全屏的时候会先竖屏再横屏呢,能避免这个吗?使用的是ListVideoUtil 谢谢了
      • Mrbrent:请问 怎么播放m3u8格式的视频
      • 如曲终破尘:你好,请问这个支持m3u8流缓存吗?
      • 飞奔的蜗牛rancho:楼主的so有没有加自己定制的接口啊,你上面的实现是不是只改了java层的?还是也定制了c层的呢?
      • chuwe1:我视频播不出来?
      • 10e8a366f9e5:请问一下,getCurrentPositionWhenPlaying可以获取播放进度,另外这个getPlayPosition是什么?
        还有,能不能直接设置进度,setPlayPosition这个方法是什么作用
      • 飞奔的蜗牛rancho:楼主的so是原生的还是加了定制啊
        恋猫月亮: @hxk 你可以用原生的ijk,也可以用普通定制so,和特色定制so,文章有些看了,你还是看github
      • 35b6ce7a9e86:大神,为什么我设置封面不成功呀,照着demo设置都不成功
      • 56edd9a12ee5:你好,通过阅读源码,发现在每次在执行
        setStateAndUi(Pause)
        时,都会去执行一个
        updatePauseCover()
        这个是会保存一个视频暂停时,保存视频当前画面的方法;我猜目的是为了防止 App 退到后台再回来时的黑屏现象,但是我注释掉这个方法,发现从后台返回,画面还是存在的,是 IJKPlayer 底层自动处理恢复的?

        我尝试仿照着 initExoPlayer() 方法,写了一个使用系统原生 mediaPlayer 的,发现从后台返回,画面也是存在
        56edd9a12ee5:@恋猫月亮
        Render 层 ? 项目里没有看到有 Render
        恋猫月亮: @WalkingRoy 你看render层
      • 小欧lq:请问下可以用来直播吗
        恋猫月亮: @小欧lq 可以
      • double_so3:2.0.5 版本 Demo 切横屏 再切回来 竖屏没有声音了只有画面
        恋猫月亮: @double_so3 哦哦,因为demo设置了列表小屏幕静音
        double_so3:@恋猫月亮 List Video 和 Recycler 的都是
        恋猫月亮: @double_so3 哪个页面?
      • 0e7f5dc37bb5:RTSP流怎么播30秒就会断掉?
      • b5df1d12c3ae:想问一下,怎么让播放器支持wmv格式的视频?
      • 472573231d3f:请教大神,怎么支持字幕播放啊?
      • 472573231d3f:能支持字幕吗?
      • c2ef23c92e5e:点击全屏的时候怎么没有效果,手机没有全屏锁定
        c2ef23c92e5e:不好意思,在Demo里面看到了,我还有一个问题, 增加封面,Demo里面使用的是mipmap里面的图片,holder.gsyVideoPlayer.setThumbImageView(holder.imageView);如果有很多视频播放,增加封面应该怎么做?
      • 8e188a9e779f:大神,怎么获取正在播放的时间啊!
        恋猫月亮: @BraveHeart_c5c7 这个没有
        8e188a9e779f:@恋猫月亮 找了一下,没有找到实时获取播放时间的回调方法
        恋猫月亮: @BraveHeart_c5c7 看wiki的api
      • 802593319287:这玩意怎么操作啊,请教一下
      • c74e2af42ebc:大神 支持h.264解码吗
        凤鸣游子:@恋猫月亮 Mediaplayer默认也支持播放的缓存,就是可以支持拖动进度条到已经缓存好的位置,但是播放完之后重新播放又需要网络再次访问资源一遍。你所说的边播边缓存不是指这个把,是把视频文件下载下来吗?下次就不需要网络吗,我也在想怎么实现。
        恋猫月亮: @Yanni2008 支持
      • b8c3d59007fd:不支持 H.263格式的么?
        恋猫月亮: @獨家丶記憶 支持
      • ca606fd10ab7:我依赖的compile 'com.shuyu:GSYVideoPlayer:2.0.4' 这个全部的库,全屏之后只是页面横屏了 播放器并没有布满全屏,而且全屏后只有声音没有图像是怎么回事,清单文件中加了configChanges了,是还需要设置什么吗
      • b2b1b282d90c:楼主有弄过FFmpeg的视频合并吗?
      • 0b4e7a50ddc6:准备在2D播放器中加一个后续的图像处理,可否开发?找不到您的联系方式,只能在此留言了。
      • MechelleWang:楼主 如果我想增加视频播放上一首/下一首,逻辑加到哪儿?GSYVideoControlView?
        MechelleWang:@恋猫月亮 好的!楼主还有个问题,只有samplelistener实现了videovallcallback?有些方法比如onClickStartIcon我并没有找到重写的位置,但是在GSYVideoView中调用了该方法
        恋猫月亮: @MechelleWang 继承标准播放器,然后返回你自己的布局,然后增加你自己的逻辑!
      • 有兴不虚昧:要获取直播流的缩略图怎么做
      • 6783fc7b20d4:楼主,你的项目不支持rtmp直播流吗?播放rtmp直播流会缓存一下播放,一直这一重复,github上介绍AndroidVideoCache不支持任何流技术是不是这个的问题
      • AntLuo:JitPack引入方法 compile 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-x64:v2.0.2'这个gsyVideoPlayer-x64是怎么确认的
      • ca606fd10ab7:大神,我想自定义UI以及相应功能该在哪实现啊,还有手势功能的弹窗ui能改吗
        恋猫月亮: @微笑着_5c25 可以,具体参考standrdgsyplayer
      • ca606fd10ab7:有没有技术交流群啊 给个号加一下今天讨论讨论
        ca606fd10ab7:@恋猫月亮 :unamused: 好吧
        恋猫月亮: @微笑着_5c25 github首页有,但是群里平常吹水的多…
      • MrTrying:有个问题,断开网络之后,还能继续播放一段时间,但是缓存播放完以后有最后回调了onAutoCompletion。这种情况是不是应该算error而不能算completion?
        https://github.com/CarGuo/GSYVideoPlayer/issues/348
      • 媚竹风:请问大神,一个需求是,通过APP下载的视频,只能通过APP播放,这个怎么处理,是不是把视频文件加密,播放得时候在解密
      • 5fc6ec6eb9ee:支持带cookie播放吗?
        恋猫月亮: @BingdianCN 支持,可以带header
      • fbdcef9a8b33:JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal start byte 0x8c
        这个咋回事,直接用的StandardGSYVideoPlayer,直接setup+startPlayLogic别的啥也没干
        恋猫月亮: @安珀 demo中?
        fbdcef9a8b33:我看ijk里面有人提过这个
      • skyarraon:文章不长,有错别字。。
        恋猫月亮: @头顶的蓝天 是…总是会打错别字😂
      • skyarraon:老婆内心世界:老子在床上等你半天了,你他妈在那边敲代码。。。

        :sweat_smile:
      • 谢尔顿:大神,我发现了一个问题,我在用你的那个recycler2的时候,当出现小窗口,往下拉,再点击其他item的时候,播放的还是小窗口的视频地址。我已经解决解决了。
        GoAllOutInWork:@恋猫月亮 楼主为什么使用 Recylerview2的listVideoUtil这个类 进入全屏的时候会先竖屏再横屏呢,能不能直接到横屏?
        恋猫月亮: @GoAllOutInWork 新版本已经解决
        GoAllOutInWork:你好 请问一下你是怎么解决的
      • Karolinezhang:大神,自己拿到socket的流可以播放吗,怎么播放啊
      • 73ece15c815f:大神,请问一下,,你这个currentPositionWhenPlaying,返回的播放进度结果是什么单位啊,有点看不懂。。
        73ece15c815f:@恋猫月亮 谢谢 脑袋短路
        恋猫月亮: @小呢个李 毫秒
      • 刘泽旻:老哥 问一下加速后 1.5倍就会变音 音调会变高 怎么处理 哔哩哔哩android确不会变音
      • aosting:学习了
        恋猫月亮: @aosting 欢迎到github使用并发现问题
      • 3f270e490cb7:ijkplayer对rtmp的支持不太好,如果直播的话,可以参见 https://github.com/daniulive/SmarterStreaming
        不够,点播的话确实足够强大了
        恋猫月亮: @曾经在奋斗 你的意思是,ijk直播不行,点播强大?
      • a7f899c4ef21:在平板上面开启缓存之后,第一次进入视频播放页面,会等待加载几分钟时间,然后换手机上播放加载就很快,这个是什么原因呢?
      • 73ece15c815f:大神,用这个ListGSYVideoPlayer, 如何拿到每一个视频播放完成的回调啊!
        73ece15c815f:@恋猫月亮 好的,谢谢哈。搞定了!
        恋猫月亮: @小呢个李 继承,重写下接口不就拿到了么
      • liubaoyua:这是我能找到的完善度最高的一个视频库,感谢 up 主
        恋猫月亮: @liubaoyua 文章有些看了,欢迎视察demo
      • 73ece15c815f:博主,我遇到一个问题,我界面底部有一个播放列表,点击列表控制视频的播放。。比如我当前播放的是列表中位置1给的视频地址,全屏和退出全屏播放都没有问题,但是如果我全屏后退出全屏点击播放列表中位置2,但视频播放的仍然是位置1的视频。。再次点击位置2才能生效。。。 另外,还有一个问题,播放完了,,想要重新从视频某一段开始看,不成功,状态一直强行回到播放完了的状态。
        73ece15c815f:@恋猫月亮 我调用了一下releaseAllVideos这个方法 第一个问题就解决了
        73ece15c815f:@恋猫月亮 好的,谢谢了哈!
        恋猫月亮: @小呢个李 第一个问题不是很清楚,一般不是项目的问题。第二个是,播放器播放完了就释放了,无法拖动。
      • c2eb054eb90f:请问大神如何监听正在加载中和加载百分比进度?
      • fe36f7a9e41a:打开硬解码后,横竖屏切换会黑屏几秒钟,这个怎么解决
      • 9f4e3998518c:@恋猫月亮 在下小白 求助下大神为什么添加依赖成功后 在布局文件中使用video组件是会报错 是不是出了添加依赖还要加什么东西
        9f4e3998518c:@恋猫月亮 回复神速呀 开心
        9f4e3998518c:@恋猫月亮 java.lang.UnsatisfiedLinkError: no ijkffmpeg in java.library.path   at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1864)   at java.lang.Runtime.loadLibrary0(Runtime.java:870)   at java.lang.System.loadLibrary(System.java:1122)   at tv.danmaku.ijk.media.player.IjkMediaPlayer$1.loadLibrary(IjkMediaPlayer.java:176)   at tv.danmaku.ijk.media.player.IjkMediaPlayer.loadLibrariesOnce(IjkMediaPlayer.java:187)   at tv.danmaku.ijk.media.player.IjkMediaPlayer.initPlayer(IjkMediaPlayer.java:228)   at tv.danmaku.ijk.media.player.IjkMediaPlayer.<init>(IjkMediaPlayer.java:224)   at tv.danmaku.ijk.media.player.IjkMediaPlayer.<init>(IjkMediaPlayer.java:215)   at com.shuyu.gsyvideoplayer.GSYVideoManager.<init>(GSYVideoManager.java:240)   at com.shuyu.gsyvideoplayer.GSYVideoManager.instance(GSYVideoManager.java:101)   at com.shuyu.gsyvideoplayer.GSYImageCover.onMeasure_Original(GSYImageCover.java:38)   at com.shuyu.gsyvideoplayer.GSYImageCover.onMeasure(GSYImageCover.java:-1)   at android.view.View.measure(View.java:19731)
        恋猫月亮: @待卍解 具体log是?
      • 472b37a92034:你好这个能自己定义ui吗?我想添加一些分享按钮啥的要怎么做啊
        恋猫月亮: @君易天下 可以的,你可以参考demo,布局只要基础控件id一样,其他的你要加什么控件都可以,继承播放器,返回你自己的layoutId,然后对你增加的控件做对应的逻辑。
      • leonseven:你好,视屏封面可以用视屏的开始的画面设置吗?有具体的方法吗?
        恋猫月亮: @leonseven 这个需要你外部去获取
      • d2777d0ffe6f:不错不错
      • 三棵猴面包树:h.265编码的能播放吗?




        9d445bc2486d:楼主您好,我的项目是一个activity下有一个大的fragment,然后这个大的Fragment下有一个小的Fragment,然后视频是在小的fragment中播放,但是全屏之后点击返回的时候,整个界面就全部白了,什么都没有了,但是声音还在,这个问题该怎么解决呢
        三棵猴面包树:@恋猫月亮 谢谢 正在学习中
        恋猫月亮: @三棵猴面包树 可以
      • wenzhihao123:可以 小伙子。。:+1:
      • 总是说下次:遇到这么多bug ,还这么happy的人也是少见
        恋猫月亮: @总是说下次 😂😂😂😂
      • 8fd28ad15588:楼主 解析本地xml播放 如何根据传过来的指令随机的播放xml的播单里的视频 怎样预留这个接口
        恋猫月亮: @Qgold12138 这个,用rxjava,在点击的时候,处理解析和播放是最好的
      • 8fd28ad15588:ijkplayer可以支持播放图片吗 求解求解
        恋猫月亮: @Qgold12138 只要协议支持就可以
        8fd28ad15588:@恋猫月亮 就是自己给图片设置总时长,然后到时间就结束
        恋猫月亮: @Qgold12138 图片??
      • b7b04d1b9435:好强大,无意间进入如此强大的地方

      本文标题:Android 实现视屏播放器、边播边缓存功能、外加铲屎(IJK

      本文链接:https://www.haomeiwen.com/subject/lmcspttx.html