02. 视频播放器整体构造
目录介绍
- 01. 视频常见的布局视图
- 02. 前期可能波及的视图
- 03. 须要达到的目标和成果
- 04. 视频视图层级示意图
- 05. 整体架构思路剖析流程
- 06. 如何创立不同播放器
- 07. 如何敌对解决播放器 UI
- 08. 交互交给内部开发者
- 09. 对于优先级视图展现
- 10. 代码我的项目 lib 代码介绍
00. 视频播放器通用框架
- 根底封装视频播放器 player,能够在 ExoPlayer、MediaPlayer,声网 RTC 视频播放器内核,原生 MediaPlayer 能够自在切换
- 对于视图状态切换和前期保护拓展,防止性能和业务呈现耦合。比方须要反对播放器 UI 高度定制,而不是该 lib 库中 UI 代码
- 针对视频播放,音频播放,播放回放,以及视频直播的性能。应用简略,代码拓展性强,封装性好,次要是和业务彻底解耦,裸露接口监听给开发者解决业务具体逻辑
- 该播放器整体架构:播放器内核(自在切换) + 视频播放器 + 边播边缓存 + 高度定制播放器 UI 视图层
- 我的项目地址:https://github.com/yangchong2…
- 对于视频播放器整体性能介绍文档:https://juejin.im/post/688345…
01. 视频常见的布局视图
- 视频底图(用于显示初始化视频时的封面图),视频状态视图【加载 loading,播放异样,加载视频失败,播放实现等】
- 扭转亮度和声音【扭转声音视图,扭转亮度视图】,扭转视频快进和快退,左右滑动快进和快退视图(手势滑动的快进快退提示框)
- 顶部控制区视图(蕴含返回健,title 等),底部控制区视图(蕴含进度条,播放暂停,工夫,切换全屏等)
- 锁屏布局视图(全屏时展现,其余暗藏),底部播放进度条视图(很多播放器都有这个),清晰度列表视图(切换清晰度弹窗)
- 底部播放进度条视图(很多播放器都有这个),当 bottom 视图显示时底部进度条暗藏,反之则显示
02. 前期可能波及的视图
- 手势领导页面(有些播放器有老手领导性能),离线下载的界面(该界面中蕴含下载列表, 列表的 item 编辑(全选, 删除))
- 用户从 wifi 切换到 4g 网络,提醒网络切换弹窗界面(当网络由 wifi 变为 4g 的时候会显示)
- 图片广告视图(带有倒计时隐没),开始视频广告视图,非会员试看视图
- 弹幕视图(这个很重要),水印显示视图,倍速播放界面(用于管制倍速),底部视频列表缩略图视图
- 投屏视频视图界面,视频直播间刷礼物界面,老师开课界面,展现更多视图(下载,分享,切换音频等)
03. 须要达到的目标和成果
- 根底封装视频播放器 player,能够在 ExoPlayer、MediaPlayer,声网 RTC 视频播放器内核,原生 MediaPlayer 能够自在切换
- 对于视图状态切换和前期保护拓展,防止性能和业务呈现耦合。比方须要反对播放器 UI 高度定制,而不是该 lib 库中 UI 代码
- 针对视频播放,音频播放,播放回放,以及视频直播的性能。应用简略,代码拓展性强,封装性好,次要是和业务彻底解耦,裸露接口监听给开发者解决业务具体逻辑
04. 视频视图层级示意图
05. 整体架构思路剖析流程
-
播放器内核
- 能够切换 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 接口)实现
06. 如何创立不同播放器
-
指标要求
- 根底播放器封装了蕴含 ExoPlayer、MediaPlayer,ijkPlayer,声网视频播放器等
-
能够自在切换初始化任何一种视频播放器,比方通过结构传入类型参数来创立不同的视频播放器
PlayerFactory playerFactory = IjkPlayerFactory.create(); IjkVideoPlayer ijkVideoPlayer = (IjkVideoPlayer) playerFactory.createPlayer(this); PlayerFactory playerFactory = ExoPlayerFactory.create(); ExoMediaPlayer exoMediaPlayer = (ExoMediaPlayer) playerFactory.createPlayer(this); PlayerFactory playerFactory = MediaPlayerFactory.create(); AndroidMediaPlayer androidMediaPlayer = (AndroidMediaPlayer) playerFactory.createPlayer(this); `
– 应用那种模式创立播放器
-
工厂模式
- 暗藏内核播放器创立具体细节,开发者只须要关怀所需产品对应的工厂,毋庸关怀创立细节即可创立播放器。合乎开闭准则
-
适配器模式
- 这个也是预先补救模式,然而在该库中,没有尝试这种形式。https://www.runoob.com/design…
-
如何做到内核无缝切换?
- 具体的代码案例,以及具体做法,在下一篇博客中会介绍到。或者间接看代码:视频播放器
-
播放器内核的架构图如下所示
07. 如何敌对解决播放器 UI
-
倒退中遇到的问题
- 播放器可反对多种场景下的播放,多个产品会用到同一个播放器,这样就会带来一个问题,一个播放业务播放器状态发生变化,其余播放业务必须同步更新播放状态,各个播放业务之间相互穿插,随着播放业务的增多,开发和保护老本会急剧减少, 导致后续开发不可继续。
-
播放器内核和 UI 层耦合
- 也就是说视频 player 和 ui 操作柔和到了一起,尤其是两者之间的交互。比方播放中须要更新 UI 进度条,播放异样须要显示异样 UI,都比拟难解决播放器状态变动更新 UI 操作
-
UI 难以自定义或者批改麻烦
- 比方常见的视频播放器,会把视频各种视图写到 xml 中,这种形式在前期代码会很大,而且改变一个小的布局,则会影响大。这样到前期往往只敢加代码,而不敢删除代码……
- 有时候难以适应新的场景,比方增加一个播放广告,老师开课,或者视频疏导业务需要,则须要到播放器中写一堆业务代码。迭代到前期,违反了开闭准则,视频播放器须要做到和业务拆散
-
视频播放器构造须要清晰
- 这个是指该视频播放器是否看了文档后疾速上手,晓得封装的大略流程。不便前期别人批改和保护,因而须要将视频播放器性能拆散。比方切换内核 + 视频播放器(player+controller+view)
-
肯定要解耦合
- 播放器 player 与视频 UI 解耦:反对增加自定义视频视图,比方反对增加自定义广告,老手疏导,或者视频播放异样等视图,这个须要较强的拓展性
-
适宜多种业务场景
- 比方适宜播放单个视频,多个视频,以及列表视频,或者相似抖音那种一个页面一个视频,还有小窗口播放视频。也就是适宜大多数业务场景
-
具体操作
- 播放状态变动是导致不同播放业务场景之间穿插同步,解除播放业务对播放器的间接操控,采纳接口监听进行解耦。比方:player+controller+interface
- 具体的代码案例,以及具体做法,在下一篇博客中会介绍到。或者间接看代码:视频播放器
08. 交互交给内部开发者
- 在播放器中,很重要一个就是须要把播放器 player 的播放模式 (小屏幕,失常,全屏模式),以及播放状态(播放,暂停,异样,实现,加载,缓冲等多种状态) 裸露给管制层 view,不便做 UI 更新。
-
比方内部开发者想加一个广告视图,这个时候必定须要给它播放器的状态
- 增加了自定义播放器视图,比方增加视频广告,能够抉择跳过,抉择播放暂停。那这个视图 view,必定是须要操作 player 或者获取 player 的状态的。这个时候就须要裸露监听视频播放的状态接口监听
- 首先定义一个 InterControlView 接口,也就是说所有自定义视频视图 view 须要实现这个接口,该接口中的外围办法有:绑定视图到播放器,视图显示暗藏变动监听,播放状态监听,播放模式监听,进度监听,锁屏监听等
- 在 BaseVideoController 中的状态监听中,通过 InterControlView 接口对象就能够把播放器的状态传递到子类中
-
举一个代码的例子
- 比方,当初有个业务需要,须要在视频播放器刚开始增加一个广告视图,期待广告倒计时 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. 对于优先级视图展现
-
视频播放器为了拓展性,须要裸露 view 接口供内部开发者自定义视频播放器视图,通过 addView 的模式增加到播放器的控制器中。
- 这就波及 view 视图的层级性。管制 view 视图的显示和暗藏是特地重要的,这个时候在自定义 view 中就须要拿到播放器的状态
-
举一个简略的例子,根底视频播放器
- 增加了根底播放性能的几个播放视图。有播放实现,播放异样,播放加载,顶部标题栏,底部管制条栏,锁屏,以及手势滑动栏。如何管制它们的显示暗藏切换呢?
- 在 addView 这些视图时,大多数的 view 都是默认 GONE 暗藏的。比方当视频初始化时,先缓冲则显示缓冲 view 而暗藏其余视图,接着播放则显示顶部 / 底部视图而暗藏其余视图
-
比方有时候须要显示两种不同的自定义视图如何解决
- 举个例子,播放的时候,点击一下视频,会显示顶部 title 视图和底部管制条视图,那么这样会同时显示两个视图。
- 点击顶部 title 视图的返回键能够敞开播放器,点击底部管制条视图的播放暂停能够管制播放条件。这个时候底部管制条视图 FrameLayout 的 ChildView 在整个视频的底部,顶部 title 视图 FrameLayout 的 ChildView 在整个视频的顶部,这样能够达到上上层都能够相应事件。
-
那么 FrameLayout 层层重叠,如何让上层不响应事件
- 在最上方显示的层加上:android:clickable=”true” 能够防止点击下层触发底层。或者间接给管制设置一个 background 色彩也能够。