其他
年底了,一起来撸个视频播放器吧!
https://blog.csdn.net/m0_37700275
基础封装视频播放器player,可以在ExoPlayer、MediaPlayer,声网RTC视频播放器内核,原生MediaPlayer可以自由切换 对于视图状态切换和后期维护拓展,避免功能和业务出现耦合。比如需要支持播放器UI高度定制,而不是该lib库中UI代码 针对视频播放,音频播放,播放回放,以及视频直播的功能。使用简单,代码拓展性强,封装性好,主要是和业务彻底解耦,暴露接口监听给开发者处理业务具体逻辑 该播放器整体架构:播放器内核(自由切换) + 视频播放器 + 边播边缓存 + 高度定制播放器UI视图层
https://github.com/yangchong211/YCVideoPlayer
https://juejin.im/post/6883457444752654343
基础内核播放库:提供基础的播放功能,可以自由切换内核,也方便拓展添加其他sdk内核播放器 统一播放器:屏蔽底层内核播放器播放差异,根据协议为上层提供统一的播放能力接口,供上层调用 播放视图层:负责播放器视图层的UI控制和调度,彻底解除播放业务与播放器的耦合 播放场景业务:负责向用户展示音视频播放能力和交互的业务 播放关联业务: 为播放器提供增值或支撑的业务,比如视频埋点统计,后期添加投屏,后期添加下载功能 demo:提供各种播放场景案例代码,基本上有大多数常用播放器的使用场景,建议直接看demo拿来即用
播放器内核拓展难
播放器内核难以切换
一定要解耦合
传入不同类型方便创建不同内核
具体设计方案
播放器内核
第一部分:视频初始化实例对象方法,主要包括:initPlayer初始化视频,setDataSource设置视频播放器地址,setSurface设置视频播放器渲染view,prepareAsync开始准备播放操作 第二部分:视频播放器状态方法,主要包括:播放,暂停,恢复,重制,设置进度,释放资源,获取进度,设置速度,设置音量 第三部分:player绑定view后,需要监听播放状态,比如播放异常,播放完成,播放准备,播放size变化,还有播放准备
发展中遇到的问题
不太好适合多种业务场景
VideoPlayer播放器
UI控制器视图
添加了基础播放功能的几个播放视图。有播放完成,播放异常,播放加载,顶部标题栏,底部控制条栏,锁屏,以及手势滑动栏。如何控制它们的显示隐藏切换呢? 在addView这些视图时,大多数的view都是默认GONE隐藏的。比如当视频初始化时,先缓冲则显示缓冲view而隐藏其他视图,接着播放则显示顶部/底部视图而隐藏其他视图
举个例子,播放的时候,点击一下视频,会显示顶部title视图和底部控制条视图,那么这样会同时显示两个视图。 点击顶部title视图的返回键可以关闭播放器,点击底部控制条视图的播放暂停可以控制播放条件。这个时候底部控制条视图FrameLayout的ChildView在整个视频的底部,顶部title视图FrameLayout的ChildView在整个视频的顶部,这样可以达到上下层都可以相应事件。
正常播放时,设置检查系统是否开启自动旋转,打开监听;全屏模式播放视频的时候,强制监听设备方向;在小窗口模式播放视频的时候,取消重力感应监听 注意一点。关于是否开启自动旋转的重力感应监听,可以给外部开发者暴露一个方法设置的开关。让用户选择是否开启该功能
写一个类,然后继承OrientationEventListener类,注意视频播放器重力感应监听不要那么频繁。表示500毫秒才检测一次…… mOrientationHelper.enable();表示检查系统是否开启自动旋转。mOrientationHelper.disable();表示取消监听 具体可以看这篇博客:06.播放器UI抽取封装
BasisVideoController controller = new BasisVideoController(this);
//设置控制器
mVideoPlayer.setVideoController(controller);
//设置视频播放链接地址
mVideoPlayer.setUrl(url);
//开始播放
mVideoPlayer.start();
controller.addControlComponent(adControlView);
直接创建两个VideoPlayer,实现代码和播放单个视频一样,只是需要注意:不要开启音频焦点监听。 如果是开启的音频焦点改变监听,那么播放该视频的时候,就会停止其他音视频的播放操作。类似,你听音乐,这个时候去看视频,那么音乐就暂停呢
第一种:点击item播放,当item滑动到不可见时暂停播放;点击其他可见item播放视频,则会暂停其他正在播放的视频,也就是说一次只能播放一个视频 第二种:滑动item,用户不用点击,让其自动进行播放,这种业务场景在玩手机碰到过。大概思路时,进入列表自动播放第一个,然后在RecyclerView滑动监听的方法中,判断如果页面滑动停止了,则遍历RecyclerView子控件找到第一个完全可见的item,然后拿到该item的索引即可播放该位置的视频
第一种操作使用ViewPager,是垂直方向可以滚动的VerticalViewPager + PagerAdapter,这种方式在item创建上可以设置预加载加载布局视图 第二种操作使用RecyclerView,是用ScrollPageHelper + RecyclerView,这种方式也可以实现一个页面一个item,一次滑动一个
第一种:每个item放一个VideoPlayer,但是要注意需要用一个单例VideoPlayerManager来保证只有一个VideoPlayer对象,这样就可以保证一次播放一个视频。当ViewHolder中的视图被回收时需要销毁视频资源 第二种:只创建一个VideoPlayer,那个播放就添加到具体的item布局中。比如播放第一个视频就把player对象添加到视图中,点击播放第三个时需要把player从它的父布局中移除后然后再添加到该item的布局中,这样就可以实现
在列表中播放,可以监听RecyclerView中的item生命周期,有一个AttachedToWindow是绑定item视图,还有一个DetachedFromWindow方法是item离开窗口时调用,在这个里面可以做视频销毁的逻辑。
在该控制器中,已经做了相关的初始化操作,比如设置视频可以拖动,根据屏幕方向自动进入/退出全屏,设置滑动调节亮度,音量,进度,默认开启等操作。 快速添加基础视频播放器的模块,包括视频播放完成view,播放异常view,播放top视图view,播放底部控制蓝view,手势滑动视图view等。同时在每一个视图view中可以拿到视频播放器的状态,便于设置UI的操作。
现在有个业务需求,需要在视频播放器刚开始添加一个广告视图,等待广告倒计时120秒后,直接进入播放视频逻辑。相信这个业务场景很常见,大家都碰到过,使用该播放器就特别简单。 首先创建一个自定义view,需要实现InterControlView接口,重写该接口中所有抽象方法,这里省略了很多代码,具体看demo。最后在调用controller.addControlComponent(adControlView);添加到基础视频播放器,这种方式满足大多数的场景……
在封装BaseVideoController控制器的时候,考虑到后期的拓展性,把视频各个视频都是以addView的形式添加进来,使用LinkedHashMap存储这样可以保证顺序。 需要注意的是在这个Controller中,需要把播放器的播放状态,播放模式,播放进度,锁屏等操作给绑定到开发者自定义实现的播放器视图View中。
比如给视频设置封面图片,这个时候总不能在播放器内部引入一个Glide,然后加载图片,这样和业务耦合呢。可以把这个设置封面view暴露给开发者,然后设置,这样更好一些。 比如外部开发者想要知道视频播放器的状态,做一些业务上操作,这个时候完全可以通过接口的形式暴露出来,该播放器把视频的播放模式监听,播放状态监听,还有各种视频操作都暴露了方法出来,方便开发者调用。
https://github.com/danikula/AndroidVideoCache
文件的缓存超过限制后没有按照lru算法删除, 处理返回给播放器的http响应头消息,响应头消息的获取处理改为head请求(需服务器支持) 替换网络库为okHttp(因为大部分的项目都是以okHttp为网络请求库的),但是这个改动性比较大
String proxyVideoUrl = server.getProxyUrl(URL_AD);
其实预加载的思路很简单,在进行一个播放视频后,再返回接下来需要预加载的视频url,启用线程去请求下载数据 开启一个线程去请求并预加载一部分的数据,可能需要预加载的数据大于>1,利用队列先进入的先进行加载,因此可以采用LinkedHashMap保存正在预加载的task。 在开始预加载的时候,判断该播放地址是否已经预加载,如果不是那么创建一个线程task,并且把它放到map集合中。然后执行预加载逻辑,也就是执行HttpURLConnection请求 提供取消对应url加载的任务,因为有可能该url不需要再进行预加载了,比如参考抖音,当用户瞬间下滑几个视频,那么很多视频就需要跳过了不需要再进行预加载。这个后期在做
public void onCompletion() {
VideoPlayerConfig config = VideoViewManager.getConfig();
if (config!=null && config.mBuriedPointEvent!=null){
//视频播放完成
config.mBuriedPointEvent.playerCompletion(mUrl);
}
}
画中画方案:虽然Android8.0及其以上版本已提供了画中画方案,但是Android8.0以下版本仍然保有大量用户,其缺点就是无法满足Android8.0以下用户需; 采用系统浮层:采用系统浮层需要系统浮层权限,Android厂商对系统浮层的授权越来越严格,导致用户授权过程的体验比较差;需要权限,可能有些手机不太好适配; 在每个展示页面单独添加播放器浮窗:优点是不受Android系统版本限制,并且用户无需系统浮层权限授权,适合所有手机用户,体验较好