前言
做短视频也有一年半了,谈不上很专业,但是也算踩了不少坑,不管是总结还是什么,就当这一年多以来的经验记录吧。
效果
先上个录屏看看效果,图1,抖音的录屏,图2,自己完成的第一版效果(不得不说,细节上还是跟抖音有差距)
图1(抖音) 图2(优化前效果)
看上面的效果,可以看出抖音和我们的产品有比较大的差距,抖音在滑动时基本无缝播放了,而图2这边在滑动之后会明显的有停顿感,大概150ms左右的区别,而产品想要的可能就是把这150ms给干掉,基于此,只能继续做优化了,想要极致的体验就得一点点细节进行分析了。
先贴一篇参考文章# 揭秘盒马鲜生 Android 短视频秒播优化方案
参考上面的文章,基本实现思路跟我自己的整体上差不多,关键的地方在于盒马使用了多播放器+播放器首帧,而我这边为了防止性能问题,用的单播放器+封面隐藏,这里就是关键的点了。
有了上面的这些引子,再来讲述下面的内容相信大家就有点兴趣了.
列表方案
现有的视频列表的实现方式有如下几种,如果有更好的欢迎留言
- recyclerView+PagerSnapHelper
- 优点:低端机上性能较好,可以控制滑动速率和动画
- 缺点:生命周期需要自己控制,手指按住屏幕的情况可以一直往下滑动,容易出现bug
- viewpager2+fragment
- 优点:API齐全,上手速度快
- 缺点:低端机上反复加载和释放fragment会有卡顿,性能低
- Viewpager2+ RecyclerView.Adapter
- 优点:相比方案2能好,相比方案1无法控制滑动速率和动画
- 缺点:生命周期由自己控制
4 自定义viewpager
- 据说淘宝是这个方案,未具体研究
- TikTok是自定义的viewpager,在滑动控制上可以达到完美的效果
附带一篇文章讲解多种方案的实现以及差别 仿抖音上下滑动方案
对低端机性能和播放秒开进行综合评估之后,最终采用了Vwpager2+RecyclerView.Adapter+多播放器的方案,目前市面上已知的同类型短视频app,除了tiktok在性能和秒开都做的较好之外,其他的如抖音,虾皮,快手等都存在一定的问题,要么就是秒开效果不好,要么就是低端机滑动时明显卡顿,下面贴2个帧率图对比下效果,手机配置为4G RAM,64G ROM,8核(这个配置在非洲还是中高端了)
虾皮 TikTok
各类问题解决
过程讲解起来比较麻烦,也要画较多的图和排版,为了方便,采取自问自答的形式来讲述吧
1. 如何做到视频秒开处理
-
老方案:
采用一张封面覆盖在surfaceView上面,视频首帧加载的时候,隐藏封面,显示播放,这种方案就是我上面优化前的效果图,一定会有那么一点停顿,无法做到百分之百的无缝播放,特别是首帧和第二帧动作差别较大的时候,效果上十分明显
-
优化方案 exoPlayer+多播放器+提前prepare
说下核心思路吧,首先是多播放器,目前我采用的是3个,也就是当前屏幕,当前屏幕上的,当前屏幕下的,用以控制上下滑动视频,为了节省内存开销,使用到了recyclerView的holder管理机制,自动销毁和复用,在adapter的onBindViewHolder()方法去创建播放器,在adapter的onViewRecycled()方法中释放和销毁播放器,这里存在一个反复创建和销毁的过程,所以内存开销必然会比较大,在低端机上可能会导致卡顿,这个是我目前的方案。
重点1: 滑动过程中播放,想要做到抖音等app的无缝秒开的效果,必须要在滑动中就开始播放,否则滑动完成之后,就会存在上面说的100-150ms的停顿,代码如下,在ViewPager2的监听方法里面播放视频:
override fun onPageScrollStateChanged(state: Int) {
if (state == ViewPager2.SCROLL_STATE_IDLE) {
if (currentSelectedPosition != 0) {
playPosition(currentSelectedPosition)
}
}
}
重点2: 在adapter的onBindViewHolder创建播放器的时候需要prepare资源,在滑动完成,代码如下:
override fun onBindViewHolder(
context: Context,
holder: MultiBaseAdapter.BindingViewHolder,
layerBinding: VideoLayerVideoBinding?,
position: Int,
multiItem: MultiItem, payload: Payload?,
) {
playerManager?.prepare(videoModel!!)
}
2.视频如何自适应屏幕
要么以宽度来适配,要么以高度来适配,视频宽高比和屏幕宽高比相除,取一个铺满全屏的比例将视频进行缩放或者裁减即可。
3.秒开是否需要对数据源做处理
这个相当于是补充项吧,要做到秒开,数据源有如下处理:
1>视频源尽量小,让服务端把数据转码成h264、h265(这个有专利费问题),列表中所有的视频要统一编码格式,因为播放器在处理不同编码的时候内部要进行编码转换,会消耗性能和时间,相同格式的视频可以让解码器复用.
2>预加载,在播放视频的时候,可以根据当前网速对后面要播放的视频提前预加载一部分,比如3-5s
3>下发不同分辨率的视频,让服务端下发不同分辨率的视频,不同分辨率的视频,码率和大小都不同,在弱网情况下,可以选低分辨率的视频进行预加载和播放,这样不管是下载和解码速度都会快很多。
4>转码的时候视频的moov头要在前面,这样就不需要下载完整的视频文件才能播放,只有部分视频文件也可以播放,对于边播边下载来说也可以较好的实现效果。
4.surfaceView在RecyclerView中导致滑动时,遮住了上层的点赞控件.