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色彩也能够。