视频播放器介绍文档
目录介绍
- 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视频层级示例图
6.2 视频播放器流程图
- 待完善
6.3 视频播放器lib库
6.4 视频内核lib库介绍
6.5视频播放器UI库介绍
07.播放器示例展示图
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中特别常见,因此该库一定程度是借鉴腾讯播放器……