01.视频播放器框架介绍

视频播放器介绍文档

目录介绍

  • 01.该视频播放器介绍
  • 02.视频播放器功能
  • 03.视频播放器架构说明
  • 04.视频播放器如何使用
  • 05.播放器详细Api文档
  • 06.播放器封装思路
  • 07.播放器示例展示图
  • 08.添加自定义视图
  • 09.视频播放器优化处理
  • 10.播放器问题记录说明
  • 11.性能优化和库大小
  • 12.视频缓存原理介绍
  • 13.查看视频播放器日志
  • 14.该库异常code说明
  • 15.该库系列wiki文档
  • 16.版本更新文档记录

00.视频播放器通用框架

  • 基础封装视频播放器player,可以在ExoPlayer、MediaPlayer,声网RTC视频播放器内核,原生MediaPlayer可以自由切换
  • 对于视图状态切换和后期维护拓展,避免功能和业务出现耦合。比如需要支持播放器UI高度定制,而不是该lib库中UI代码
  • 针对视频播放,音频播放,播放回放,以及视频直播的功能。使用简单,代码拓展性强,封装性好,主要是和业务彻底解耦,暴露接口监听给开发者处理业务具体逻辑
  • 该播放器整体架构:播放器内核(自由切换) + 视频播放器 + 边播边缓存 + 高度定制播放器UI视图层

01.该视频播放器介绍

1.1 该库说明

播放器功能 MediaPlayer ExoPlayer IjkPlayer RTC TXPlayer
UI/Player/业务解耦 支持 支持 支持
切换视频播放模式 支持 支持 支持
视频无缝切换 支持 支持 支持
调节播放进度 支持 支持 支持
网络环境监听 支持 支持 支持
滑动改变亮度/声音 支持 支持 支持
设置视频播放比例 支持 支持 支持
自由切换视频内核 支持 支持 支持
记录播放位置 支持 支持 支持
清晰度模式切换 支持 支持 支持
重力感应自动进入 支持 支持 支持
锁定屏幕功能 支持 支持 支持
倍速播放 不支持 支持 支持
视频小窗口播放 支持 支持 支持
列表小窗口播放 支持 支持 支持
边播边缓存 支持 支持 支持
同时播放多个视频 支持 支持 支持
仿快手预加载 支持 支持 支持
基于内核无UI 支持 支持 支持
添加弹幕 支持 支持 支持
全屏显示电量 支持 支持 支持

1.2 该库功能说明

类型 功能说明
项目结构 VideoCache缓存lib,VideoKernel视频内核lib,VideoPlayer视频UIlib
内核 MediaPlayer、ExoPlayer、IjkPlayer,后期接入Rtc和TXPlayer
协议/格式 http/https、concat、rtsp、hls、rtmp、file、m3u8、mkv、webm、mp3、mp4等
画面 调整显示比例:默认、16:9、4:3、填充;播放时旋转画面角度(0,90,180,270);镜像旋转
布局 内核和UI分离,和市面GitHub上大多数播放器不一样,方便定制,通过addView添加
播放 正常播放,小窗播放,列表播放,仿抖音播放
自定义 可以自定义添加视频UI层,可以说UI和Player高度分离,支持自定义渲染层SurfaceView

02.视频播放器功能

  • A基础功能
    • A.1.1 能够自定义视频加载loading类型,设置视频标题,设置视频底部图片,设置播放时长等基础功能
    • A.1.2 可以切换播放器的视频播放状态,播放错误,播放未开始,播放开始,播放准备中,正在播放,暂停播放,正在缓冲等等状态
    • A.1.3 可以自由设置播放器的播放模式,比如,正常播放,全屏播放,和小屏幕播放。其中全屏播放支持旋转屏幕。
    • A.1.4 可以支持多种视频播放类型,比如,原生封装视频播放器,还有基于ijkPlayer封装的播放器。
    • A.1.5 可以设置是否隐藏播放音量,播放进度,播放亮度等,可以通过拖动seekBar改变视频进度。还支持设置n秒后不操作则隐藏头部和顶部布局功能
    • A.1.6 可以设置竖屏模式下全屏模式和横屏模式下的全屏模式,方便多种使用场景
    • A.1.7 top和bottom面版消失和显示:点击视频画面会显示、隐藏操作面板;显示后不操作会5秒后自动消失【也可以设置n秒消失时间】
  • B高级功能
    • B.1.1 支持一遍播放一遍缓冲的功能,其中缓冲包括两部分,第一种是播放过程中缓冲,第二种是暂停过程中缓冲
    • B.1.2 基于ijkPlayer,ExoPlayer,Rtc,原生MediaPlayer等的封装播放器,支持多种格式视频播放
    • B.1.3 可以设置是否记录播放位置,设置播放速度,设置屏幕比例
    • B.1.4 支持滑动改变音量【屏幕右边】,改变屏幕亮度【屏幕左边】,屏幕底测左右滑动调节进度
    • B.1.5 支持list页面中视频播放,滚动后暂停播放,播放可以自由设置是否记录状态。并且还支持删除视频播放位置状态。
    • B.1.6 切换横竖屏:切换全屏时,隐藏状态栏,显示自定义top(显示电量);竖屏时恢复原有状态
    • B.1.7 支持切换视频清晰度模式
    • B.1.8 添加锁屏功能,竖屏不提供锁屏按钮,横屏全屏时显示,并且锁屏时,屏蔽手势处理
  • C拓展功能【这块根据实际情况选择是否需要使用,一般视频付费App会有这个工鞥】
    • C1产品需求:类似优酷,爱奇艺视频播放器部分逻辑。比如如果用户没有登录也没有看视频权限,则提示试看视频[自定义布局];如果用户没有登录但是有看视频权限,则正常观看;如果用户登录,但是没有充值会员,部分需要权限视频则进入试看模式,试看结束后弹出充值会员界面;如果用户余额不足,比如余额只有99元,但是视频观看要199元,则又有其他提示。
    • C2自身需求:比如封装好了视频播放库,那么点击视频上登录按钮则跳到登录页面;点击充值会员页面也跳到充值页面。这个通过定义接口,可以让使用者通过方法调用,灵活处理点击事件。
    • C.1.1 可以设置试看模式,设置试看时长。试看结束后就提示登录或者充值……
    • C.1.2 对于设置视频的宽高,建议设置成4:3或者16:9或者常用比例,如果不是常用比例,则可能会有黑边。其中黑边的背景可以设置
    • C.1.3 可以设置播放有权限的视频时的各种文字描述,而没有把它写在封装库中,使用者自己设定
    • C.1.4 锁定屏幕功能,这个参考大部分播放器,只有在全屏模式下才会有

03.视频播放器架构说明

  • 视频常见的布局视图
    • 视频底图(用于显示初始化视频时的封面图),视频状态视图【加载loading,播放异常,加载视频失败,播放完成等】
    • 改变亮度和声音【改变声音视图,改变亮度视图】,改变视频快进和快退,左右滑动快进和快退视图(手势滑动的快进快退提示框)
    • 顶部控制区视图(包含返回健,title等),底部控制区视图(包含进度条,播放暂停,时间,切换全屏等)
    • 锁屏布局视图(全屏时展示,其他隐藏),底部播放进度条视图(很多播放器都有这个),清晰度列表视图(切换清晰度弹窗)
  • 后期可能涉及的布局视图
    • 手势指导页面(有些播放器有新手指导功能),离线下载的界面(该界面中包含下载列表, 列表的item编辑(全选, 删除))
    • 用户从wifi切换到4g网络,提示网络切换弹窗界面(当网络由wifi变为4g的时候会显示)
    • 图片广告视图(带有倒计时消失),开始视频广告视图,非会员试看视图
    • 弹幕视图(这个很重要),水印显示视图,倍速播放界面(用于控制倍速),底部视频列表缩略图视图
    • 投屏视频视图界面,视频直播间刷礼物界面,老师开课界面,展示更多视图(下载,分享,切换音频等)
  • 视频播放器的痛点
    • 播放器内核难以切换
      • 不同的视频播放器内核,由于api不一样,所以难以切换操作。要是想兼容内核切换,就必须自己制定一个视频接口+实现类的播放器
    • 播放器内核和UI层耦合
      • 也就是说视频player和ui操作柔和到了一起,尤其是两者之间的交互。比如播放中需要更新UI进度条,播放异常需要显示异常UI,都比较难处理播放器状态变化更新UI操作
    • UI难以自定义或者修改麻烦
      • 比如常见的视频播放器,会把视频各种视图写到xml中,这种方式在后期代码会很大,而且改动一个小的布局,则会影响大。这样到后期往往只敢加代码,而不敢删除代码……
      • 有时候难以适应新的场景,比如添加一个播放广告,老师开课,或者视频引导业务需求,则需要到播放器中写一堆业务代码。迭代到后期,违背了开闭原则,视频播放器需要做到和业务分离
    • 视频播放器结构不清晰
      • 这个是指该视频播放器能否看了文档后快速上手,知道封装的大概流程。方便后期他人修改和维护,因此需要将视频播放器功能分离。比如切换内核+视频播放器(player+controller+view)
  • 需要达到的目的和效果
    • 基础封装视频播放器player,可以在ExoPlayer、MediaPlayer,声网RTC视频播放器内核,原生MediaPlayer可以自由切换
    • 对于视图状态切换和后期维护拓展,避免功能和业务出现耦合。比如需要支持播放器UI高度定制,而不是该lib库中UI代码
    • 针对视频播放,视频投屏,音频播放,播放回放,以及视频直播的功能
  • 通用视频框架特点
    • 一定要解耦合
      • 播放器内核与播放器解耦: 支持更多的播放场景、以及新的播放业务快速接入,并且不影响其他播放业务,比如后期添加阿里云播放器内核,或者腾讯播放器内核
      • 播放器player与视频UI解耦:支持添加自定义视频视图,比如支持添加自定义广告,新手引导,或者视频播放异常等视图,这个需要较强的拓展性
    • 适合多种业务场景
      • 比如适合播放单个视频,多个视频,以及列表视频,或者类似抖音那种一个页面一个视频,还有小窗口播放视频。也就是适合大多数业务场景
  • 视频分层
    • 播放器内核
      • 可以切换ExoPlayer、MediaPlayer,IjkPlayer,声网视频播放器,这里使用工厂模式Factory + AbstractVideoPlayer + 各个实现AbstractVideoPlayer抽象类的播放器类
      • 定义抽象的播放器,主要包含视频初始化,设置,状态设置,以及播放监听。由于每个内核播放器api可能不一样,所以这里需要实现AbstractVideoPlayer抽象类的播放器类,方便后期统一调用
      • 为了方便创建不同内核player,所以需要创建一个PlayerFactory,定义一个createPlayer创建播放器的抽象方法,然后各个内核都实现它,各自创建自己的播放器
    • VideoPlayer播放器
      • 可以自由切换视频内核,Player+Controller。player负责播放的逻辑,Controller负责视图相关的逻辑,两者之间用接口进行通信
      • 针对Controller,需要定义一个接口,主要负责视图UI处理逻辑,支持添加各种自定义视图View【统一实现自定义接口Control】,每个view尽量保证功能单一性,最后通过addView形式添加进来
      • 针对Player,需要定义一个接口,主要负责视频播放处理逻辑,比如视频播放,暂停,设置播放进度,设置视频链接,切换播放模式等操作。需要注意把Controller设置到Player里面,两者之间通过接口交互
    • UI控制器视图
      • 定义一个BaseVideoController类,这个主要是集成各种事件的处理逻辑,比如播放器状态改变,控制视图隐藏和显示,播放进度改变,锁定状态改变,设备方向监听等等操作
      • 定义一个view的接口InterControlView,在这里类里定义绑定视图,视图隐藏和显示,播放状态,播放模式,播放进度,锁屏等操作。这个每个实现类则都可以拿到这些属性呢
      • 在BaseVideoController中使用LinkedHashMap保存每个自定义view视图,添加则put进来后然后通过addView将视图添加到该控制器中,这样非常方便添加自定义视图
      • 播放器切换状态需要改变Controller视图,比如视频异常则需要显示异常视图view,则它们之间的交互是通过ControlWrapper(同时实现Controller接口和Player接口)实现

04.视频播放器如何使用

4.1 关于gradle引用说明

  • 如下所示
    //视频UI层,必须要有
    implementation 'cn.yc:VideoPlayer:3.0.1'
    //视频缓存,如果不需要则可以不依赖
    implementation 'cn.yc:VideoCache:3.0.0'
    //视频内核层,必须有
    implementation 'cn.yc:VideoKernel:3.0.1'
    

4.2 在xml中添加布局

  • 注意,在实际开发中,由于Android手机碎片化比较严重,分辨率太多了,建议灵活设置布局的宽高比为4:3或者16:9或者你认为合适的,可以用代码设置。
  • 如果宽高比变形,则会有黑边
    <org.yczbj.ycvideoplayerlib.player.VideoPlayer
        android:id="@+id/video_player"
        android:layout_width="match_parent"
        android:layout_height="240dp"/>
    

4.3 最简单的视频播放器参数设定

  • 如下所示
    //创建基础视频播放器,一般播放器的功能
    BasisVideoController controller = new BasisVideoController(this);
    //设置控制器
    mVideoPlayer.setVideoController(controller);
    //设置视频播放链接地址
    mVideoPlayer.setUrl(url);
    //开始播放
    mVideoPlayer.start();
    

4.4 注意问题

  • 如果是全屏播放,则需要在清单文件中设置当前activity的属性值
    • android:configChanges 保证了在全屏的时候横竖屏切换不会执行Activity的相关生命周期,打断视频的播放
    • android:screenOrientation 固定了屏幕的初始方向
    • 这两个变量控制全屏后和退出全屏的屏幕方向
          <activity android:name=".VideoActivity"
              android:configChanges="orientation|keyboardHidden|screenSize"
              android:screenOrientation="portrait"/>
      
  • 如何一进入页面就开始播放视频,稍微延时一下即可
    • 代码如下所示,注意避免直接start(),因为有可能视频还没有初始化完成……
      mVideoPlayer.postDelayed(new Runnable() {
          @Override
          public void run() {
              mVideoPlayer.start();
          }
      },300);
      

05.播放器详细Api文档

  • 01.最简单的播放
  • 02.如何切换视频内核
  • 03.切换视频模式
  • 04.切换视频清晰度
  • 05.视频播放监听
  • 06.列表中播放处理
  • 07.悬浮窗口播放
  • 08.其他重要功能Api
  • 09.播放多个视频
  • 10.VideoPlayer相关Api
  • 11.Controller相关Api
  • 12.仿快手播放视频
  • 具体看这篇文档:视频播放器Api说明

06.播放器封装思路

6.1视频层级示例图

image

6.2 视频播放器流程图

  • 待完善

6.3 视频播放器lib库

image

6.4 视频内核lib库介绍

image

image

6.5视频播放器UI库介绍

image

07.播放器示例展示图

image

image

image

image

image

image

image

image

image

image

08.添加自定义视图

  • 比如,现在有个业务需求,需要在视频播放器刚开始添加一个广告视图,等待广告倒计时120秒后,直接进入播放视频逻辑。相信这个业务场景很常见,大家都碰到过,使用该播放器就特别简单,代码如下所示:
  • 首先创建一个自定义view,需要实现InterControlView接口,重写该接口中所有抽象方法,这里省略了很多代码,具体看demo。
    public class AdControlView extends FrameLayout implements InterControlView, View.OnClickListener {
    
        private ControlWrapper mControlWrapper;
        public AdControlView(@NonNull Context context) {
            super(context);
            init(context);
        }
    
        private void init(Context context){
            LayoutInflater.from(getContext()).inflate(R.layout.layout_ad_control_view, this, true);
        }
       
        /**
         * 播放状态
         * -1               播放错误
         * 0                播放未开始
         * 1                播放准备中
         * 2                播放准备就绪
         * 3                正在播放
         * 4                暂停播放
         * 5                正在缓冲(播放器正在播放时,缓冲区数据不足,进行缓冲,缓冲区数据足够后恢复播放)
         * 6                暂停缓冲(播放器正在播放时,缓冲区数据不足,进行缓冲,此时暂停播放器,继续缓冲,缓冲区数据足够后恢复暂停
         * 7                播放完成
         * 8                开始播放中止
         * @param playState                     播放状态,主要是指播放器的各种状态
         */
        @Override
        public void onPlayStateChanged(int playState) {
            switch (playState) {
                case ConstantKeys.CurrentState.STATE_PLAYING:
                    mControlWrapper.startProgress();
                    mPlayButton.setSelected(true);
                    break;
                case ConstantKeys.CurrentState.STATE_PAUSED:
                    mPlayButton.setSelected(false);
                    break;
            }
        }
    
        /**
         * 播放模式
         * 普通模式,小窗口模式,正常模式三种其中一种
         * MODE_NORMAL              普通模式
         * MODE_FULL_SCREEN         全屏模式
         * MODE_TINY_WINDOW         小屏模式
         * @param playerState                   播放模式
         */
        @Override
        public void onPlayerStateChanged(int playerState) {
            switch (playerState) {
                case ConstantKeys.PlayMode.MODE_NORMAL:
                    mBack.setVisibility(GONE);
                    mFullScreen.setSelected(false);
                    break;
                case ConstantKeys.PlayMode.MODE_FULL_SCREEN:
                    mBack.setVisibility(VISIBLE);
                    mFullScreen.setSelected(true);
                    break;
            }
            //暂未实现全面屏适配逻辑,需要你自己补全
        }
    }
    
  • 然后该怎么使用这个自定义view呢?很简单,在之前基础上,通过控制器对象add进来即可,代码如下所示
    controller = new BasisVideoController(this);
    AdControlView adControlView = new AdControlView(this);
    adControlView.setListener(new AdControlView.AdControlListener() {
        @Override
        public void onAdClick() {
            BaseToast.showRoundRectToast( "广告点击跳转");
        }
    
        @Override
        public void onSkipAd() {
            playVideo();
        }
    });
    controller.addControlComponent(adControlView);
    //设置控制器
    mVideoPlayer.setController(controller);
    mVideoPlayer.setUrl(proxyUrl);
    mVideoPlayer.start();
    

09.视频播放器优化处理

9.1 如何兼容不同内核播放器

  • 提问:针对不同内核播放器,比如谷歌的ExoPlayer,B站的IjkPlayer,还有原生的MediaPlayer,有些api不一样,那使用的时候如何统一api呢?
    • 比如说,ijk和exo的视频播放listener监听api就完全不同,这个时候需要做兼容处理
    • 定义接口,然后各个不同内核播放器实现接口,重写抽象方法。调用的时候,获取接口对象调用api,这样就可以统一Api
  • 定义一个接口,这个接口有什么呢?这个接口定义通用视频播放器方法,比如常见的有:视频初始化,设置url,加载,以及播放状态,简单来说可以分为三个部分。
    • 第一部分:视频初始化实例对象方法,主要包括:initPlayer初始化视频,setDataSource设置视频播放器地址,setSurface设置视频播放器渲染view,prepareAsync开始准备播放操作
    • 第二部分:视频播放器状态方法,主要包括:播放,暂停,恢复,重制,设置进度,释放资源,获取进度,设置速度,设置音量
    • 第三部分:player绑定view后,需要监听播放状态,比如播放异常,播放完成,播放准备,播放size变化,还有播放准备
  • 首先定义一个工厂抽象类,然后不同的内核播放器分别创建其具体的工厂实现具体类
    • PlayerFactory:抽象工厂,担任这个角色的是工厂方法模式的核心,任何在模式中创建对象的工厂类必须实现这个接口
    • ExoPlayerFactory:具体工厂,具体工厂角色含有与业务密切相关的逻辑,并且受到使用者的调用以创建具体产品对象。
  • 如何使用,分为三步,具体操作如下所示
    • 1.先调用具体工厂对象中的方法createPlayer方法;2.根据传入产品类型参数获得具体的产品对象;3.返回产品对象并使用。
    • 简而言之,创建对象的时候只需要传递类型type,而不需要对应的工厂,即可创建具体的产品对象
  • 这种创建对象最大优点
    • 工厂方法用来创建所需要的产品,同时隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
    • 加入新的产品时,比如后期新加一个阿里播放器内核,这个时候就只需要添加一个具体工厂和具体产品就可以。系统的可扩展性也就变得非常好,完全符合“开闭原则”

9.2 播放器UI抽取封装优化

  • 发展中遇到的问题
    • 播放器可支持多种场景下的播放,多个产品会用到同一个播放器,这样就会带来一个问题,一个播放业务播放器状态发生变化,其他播放业务必须同步更新播放状态,各个播放业务之间互相交叉,随着播放业务的增多,开发和维护成本会急剧增加, 导致后续开发不可持续。
  • UI难以自定义或者修改麻烦
    • 比如常见的视频播放器,会把视频各种视图写到xml中,这种方式在后期代码会很大,而且改动一个小的布局,则会影响大。这样到后期往往只敢加代码,而不敢删除代码……
    • 有时候难以适应新的场景,比如添加一个播放广告,老师开课,或者视频引导业务需求,则需要到播放器中写一堆业务代码。迭代到后期,违背了开闭原则,视频播放器需要做到和业务分离
  • 视频播放器结构需要清晰
    • 也就是说视频player和ui操作柔和到了一起,尤其是两者之间的交互。比如播放中需要更新UI进度条,播放异常需要显示异常UI,都比较难处理播放器状态变化更新UI操作
    • 这个是指该视频播放器能否看了文档后快速上手,知道封装的大概流程。方便后期他人修改和维护,因此需要将视频播放器功能分离。比如切换内核+视频播放器(player+controller+view)
    • 一定要解耦合,播放器player与视频UI解耦:支持添加自定义视频视图,比如支持添加自定义广告,新手引导,或者视频播放异常等视图,这个需要较强的拓展性
  • 适合多种业务场景
    • 比如适合播放单个视频,多个视频,以及列表视频,或者类似抖音那种一个页面一个视频,还有小窗口播放视频。也就是适合大多数业务场景
  • 方便播放业务发生变化
    • 播放状态变化是导致不同播放业务场景之间交叉同步,解除播放业务对播放器的直接操控,采用接口监听进行解耦。比如:player+controller+interface
  • 关于视频播放器
    • 定义一个视频播放器InterVideoPlayer接口,操作视频播放,暂停,缓冲,进度设置,设置播放模式等多种操作。
    • 然后写一个播放器接口的具体实现类,在这个里面拿到内核播放器player,然后做相关的实现操作。
  • 关于视频视图View
    • 定义一个视图InterVideoController接口,主要负责视图显示/隐藏,播放进度,锁屏,状态栏等操作。
    • 然后写一个播放器视图接口的具体实现类,在这里里面inflate视图操作,然后接口方法实现,为了方便后期开发者自定义view,因此需要addView操作,将添加进来的视图用map集合装起来。
  • 播放器player和controller交互
    • 在player中创建BaseVideoController对象,这个时候需要把controller添加到播放器中,这个时候有两个要点特别重要,需要把播放器状态监听,和播放模式监听传递给控制器
    • setPlayState设置视频播放器播放逻辑状态,主要是播放缓冲,加载,播放中,暂停,错误,完成,异常,播放进度等多个状态,方便控制器做UI更新操作
    • setPlayerState设置视频播放切换模式状态,主要是普通模式,小窗口模式,正常模式三种其中一种,方便控制器做UI更新
  • 播放器player和view交互
    • 这块非常关键,举个例子,视频播放失败需要显示控制层的异常视图View;播放视频初始化需要显示loading,然后更新UI播放进度条等。都是播放器和视图层交互
    • 可以定义一个类,同时实现InterVideoPlayer接口和InterVideoController接口,这个时候会重新这两个接口所有的方法。此类的目的是为了在InterControlView接口实现类中既能调用VideoPlayer的api又能调用BaseVideoController的api
  • 如何添加自定义播放器视图
    • 添加了自定义播放器视图,比如添加视频广告,可以选择跳过,选择播放暂停。那这个视图view,肯定是需要操作player或者获取player的状态的。这个时候就需要暴露监听视频播放的状态接口监听
    • 首先定义一个InterControlView接口,也就是说所有自定义视频视图view需要实现这个接口,该接口中的核心方法有:绑定视图到播放器,视图显示隐藏变化监听,播放状态监听,播放模式监听,进度监听,锁屏监听等
    • 在BaseVideoController中的状态监听中,通过InterControlView接口对象就可以把播放器的状态传递到子类中

9.4 代码方面优化措施

  • 如果是在Activity中的话,建议设置下面这段代码
    @Override
    protected void onResume() {
        super.onResume();
        if (mVideoPlayer != null) {
            //从后台切换到前台,当视频暂停时或者缓冲暂停时,调用该方法重新开启视频播放
            mVideoPlayer.resume();
        }
    }
    
    
    @Override
    protected void onPause() {
        super.onPause();
        if (mVideoPlayer != null) {
            //从前台切到后台,当视频正在播放或者正在缓冲时,调用该方法暂停视频
            mVideoPlayer.pause();
        }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mVideoPlayer != null) {
            //销毁页面,释放,内部的播放器被释放掉,同时如果在全屏、小窗口模式下都会退出
            mVideoPlayer.release();
        }
    }
    
    @Override
    public void onBackPressed() {
        //处理返回键逻辑;如果是全屏,则退出全屏;如果是小窗口,则退出小窗口
        if (mVideoPlayer == null || !mVideoPlayer.onBackPressed()) {
            super.onBackPressed();
        }
    }
    

10.播放器问题记录说明

11.性能优化和库大小

12.视频缓存原理介绍

  • 网络上比较好的项目:https://github.com/danikula/AndroidVideoCache
    • 网络用的HttpURLConnection,文件缓存处理,文件最大限度策略,回调监听处理,断点续传,代理服务等。
  • 但是存在一些问题,比如如下所示
    • 文件的缓存超过限制后没有按照lru算法删除,
    • 处理返回给播放器的http响应头消息,响应头消息的获取处理改为head请求(需服务器支持)
    • 替换网络库为okHttp(因为大部分的项目都是以okHttp为网络请求库的),但是这个改动性比较大
  • 然后看一下怎么使用,超级简单。传入视频url链接,返回一个代理链接,然后就可以呢
    HttpProxyCacheServer cacheServer = ProxyVideoCacheManager.getProxy(this);
    String proxyUrl = cacheServer.getProxyUrl(URL_AD);
    mVideoPlayer.setUrl(proxyUrl);
    
    
    public static HttpProxyCacheServer getProxy(Context context) {
        return sharedProxy == null ? (sharedProxy = newProxy(context)) : sharedProxy;
    }
    
    private static HttpProxyCacheServer newProxy(Context context) {
        return new HttpProxyCacheServer.Builder(context)
                .maxCacheSize(512 * 1024 * 1024)       // 512MB for cache
                //缓存路径,不设置默认在sd_card/Android/data/[app_package_name]/cache中
                //.cacheDirectory()
                .build();
    }
    
  • 大概的原理
    • 原始的方式是直接塞播放地址给播放器,它就可以直接播放。现在我们要在中间加一层本地代理,播放器播放的时候(获取数据)是通过我们的本地代理的地址来播放的,这样我们就可以很好的在中间层(本地代理层)做一些处理,比如:文件缓存,预缓存(秒开处理),监控等。
  • 原理详细一点来说
    • 1.采用了本地代理服务的方式,通过原始url给播放器返回一个本地代理的一个url ,代理URL类似:http://127.0.0.1:port/视频url;(port端口为系统随机分配的有效端口,真实url是为了真正的下载),然后播放器播放的时候请求到了你本地的代理上了。
    • 2.本地代理采用ServerSocket监听127.0.0.1的有效端口,这个时候手机就是一个服务器了,客户端就是socket,也就是播放器。
    • 3.读取客户端就是socket来读取数据(http协议请求)解析http协议。
    • 4.根据url检查视频文件是否存在,读取文件数据给播放器,也就是往socket里写入数据(socket通信)。同时如果没有下载完成会进行断点下载,当然弱网的话数据需要生产消费同步处理。
  • 如何实现预加载
    • 其实预加载的思路很简单,在进行一个播放视频后,再返回接下来需要预加载的视频url,启用线程去请求下载数据
    • 开启一个线程去请求并预加载一部分的数据,可能需要预加载的数据大于>1,利用队列先进入的先进行加载,因此可以采用LinkedHashMap保存正在预加载的task。
    • 在开始预加载的时候,判断该播放地址是否已经预加载,如果不是那么创建一个线程task,并且把它放到map集合中。然后执行预加载逻辑,也就是执行HttpURLConnection请求
    • 提供取消对应url加载的任务,因为有可能该url不需要再进行预加载了,比如参考抖音,当用户瞬间下滑几个视频,那么很多视频就需要跳过了不需要再进行预加载
  • 具体直接看项目代码:VideoCache缓冲模块

13.查看视频播放器日志

  • 统一管理视频播放器封装库日志,方便后期排查问题
    • 比如,视频内核,日志过滤则是:aaa
    • 比如,视频player,日志过滤则是:bbb
    • 比如,缓存模块,日志过滤则是:VideoCache

14.该库异常code说明

  • 针对视频封装库,统一处理抛出的异常,为了方便开发者快速知道异常的来由,则可以查询约定的code码。
    • 这个在sdk中特别常见,因此该库一定程度是借鉴腾讯播放器……

视频框架:https://github.com/yangchong211/YCVideoPlayer

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342