博罗中山网站建设,贵州二建报名入口官网,大宗交易平台官网,谷歌推广公司前言
随着短视频兴起#xff0c;各大APP中短视频随处可见#xff0c;feeds流、详情页等等。怎样让用户有一个好的视频观看体验显得越来越重要了。大部分feeds里面滑动观看视频的时候#xff0c;有明显的等待感#xff0c;体验不是很好。针对这个问题我们展开了一波优化各大APP中短视频随处可见feeds流、详情页等等。怎样让用户有一个好的视频观看体验显得越来越重要了。大部分feeds里面滑动观看视频的时候有明显的等待感体验不是很好。针对这个问题我们展开了一波优化目标是视频播放秒开视频播放体验良好。无图无真相上个对比图左边是优化之前的右边是优化之后的 问题分析
视频格式的选择
在正式分析问题之前有必要说明下我们现在首页的视频都是320p H.264编码的mp4视频。 H.264 H.265 H.264也称作MPEG-4AVC(Advanced Video Codec高级视频编码)是一种视频压缩标准同时也是一种被广泛使用的高精度视频的录制、压缩和发布格式。H.264因其是蓝光光盘的一种编解码标准而著名所有蓝光播放器都必须能解码H.264。H.264相较于以前的编码标准有着一些新特性如多参考帧的运动补偿、变块尺寸运动补偿、帧内预测编码等通过利用这些新特性H.264比其他编码标准有着更高的视频质量和更低的码率.H.265/HEVC的编码架构大致上和H.264/AVC的架构相似也主要包含帧内预测(intra prediction)、帧间预测(inter prediction)、转换 (transform)、量化 (quantization)、去区块滤波器(deblocking filter)、熵编码(entropy coding)等模块。但在HEVC编码架构中整体被分为了三个基本单位分别是编码单位(coding unitCU)、预测单位(predict unitPU) 和转换单位(transform unitTU )。总的来说H.265压缩效率更高传输码率更低视频画质更优。看起来使用H.265似乎是很明智的选择
但我们这里选择的是H.264。原因是H.264支持的机型范围更为广泛。 PS闲鱼H.265视频在宝贝详情页会在近期上线敬请关注体验!TS FLV MP4
TS是日本高清摄像机拍摄下进行的封装格式全称为MPEG2-TS。TS即Transport Stream的缩写。MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。下述命令可以把mp4转换成ts格式从结果来看ts文件(4.3MB)比mp4文件(3.9MB)大10%左右。
ffmpeg -i input.mp4 -c copy output.ts
FLV是FLASH VIDEO的简称FLV流媒体格式是随着Flash MX的推出发展而来的视频格式。由于它形成的文件极小、加载速度极快使得网络观看视频文件成为可能它的出现有效地解决了视频文件导入Flash后使导出的SWF文件体积庞大不能在网络上很好的使用等问题。FLV只支持一个音频流、一个视频流不能在一个文件里包含多路音频流。音频采样率不支持48k视频编码不支持H.265。相同编码格式下文件大小和mp4几乎没有区别。 ffmpeg -i input.mp4 -c copy output.flv
MP4是为大家所熟知的一种视频封装格式MP4或称MPEG-4第14部分是一种标准的数字多媒体容器格式。MPEG-4第14部分的扩充名为.mp4以存储数字音频及数字视频为主但也可以存储字幕和静止图像。因其可容纳支持比特流的视频流MP4可以在网络传输时使用流式传输。其兼容性很好几乎所有的移动设备都支持而且还能在浏览器、桌面系统进行播放。综合上面几个封装格式的特点我们的最终选择是MP4.
播放流程
一个视频在客户端的播放流程是怎么样的播放首开慢耗时在什么地方耗时点是否能够快速低成本的解决了解视频的播放流程有助于找到问题的突破口。视频从加载到播放可以分为三个阶段
读取IO“获取” 内容 - 从 “本地” or “服务器” 上获取解析Parser“理解” 内容 - 参考 “格式协议” 来 “理解” 内容渲染Render“展示” 内容 - 通过扬声器/屏幕来 “展示” 内容可以看出内容获取从“服务器”改为“本地”这样会节省很大一部分时间而且成本很低是一个很好的切入点。事实也是如此我们的优化正是围绕此点展开。
PS: 我们使用的网络库播放器都是集团内部的本身做了很多优化。本文不涉及网络协议播放器方面的优化讨论。
技术方案
鉴于上面的分析我们要做的工作是把mp4文件提前缓存一部分到feeds滑动要播放的时候播放本地的mp4文件。由于用户可能继续观看视频所以本地的数据播放完后需要从网络下载数据进行播放。这里需要解决两个问题
应该提前下载多少数据缓存数据播放完成后该怎么切换到网络数据
MOOV BOX的位置
对于第一个问题我们不得不分析一下mp4的文件结构看看我们应该下载多少数据量合适。MP4是由很多Box 组成的Box里面可以嵌套Box 这里不详细介绍MP4的格式信息。但是可以看出moov box对播放很关键它提供的信息如宽高、时长、码率、编码格式、帧列表、关键帧列表等等。播放器没有获取到moov box是没办法进行播放的。所以下载的数据应该要包含moov box再加上几十帧的数据。
做了一个简单的计算闲鱼短视频一般最长是30sfeeds里面的分辨率是320p码率是1141kb/sftypmoov这个视频的数据量在31kb左右(打开文件可以看出mdat是从31754byte的位置开始的)所以头部信息10帧的数据大约是(31kb 1141kb/3)/8 51KB
Proxy
第二个问题缓存数据播放完成后该怎么切换到网络数据呢在本地数据播放完成之后设置一个网络地址给播放器告诉播放器下载的offset是多少然后继续从网络下载数据播放。这样看起来可行但是需要播放器提供支持本地数据播放完成的回调设置网络url并支持offset。另外服务端需要支持range参数而且切换到网络播放的时候需要新建立网络连接很可能会造成卡顿。
最终我们选择了proxy的方式把proxy作为中间人负责预加载数据、给播放器提供数据切换逻辑在proxy里面来完成。未加入proxy之前流程是这样的 加入了proxy之后流程是这样的
这样做的好处很明显我们可以在proxy里面做很多事情例如本地文件缓存数据和网络数据的切换工作。甚至是和CDN使用其它的协议进行通信。我们这里假定预加载工作已经完成看看播放器是怎么和proxy进行交互的。播放的时候会用Proxy提供的一个localhost的url进行播放这样代理服务器会收到网络请求把本地预加载的数据返回给播放器。播放器完全感知不到proxy模块、预加载模块的存在。播放器、预加载模块都是Proxy的client调用逻辑都是一样。图示说明如下
下面逐步解释一下数据的加载过程
Client发起http请求获取数据箭头1所示文件缓存如果存在所请求的数据则直接返回数据箭头2所示若本地文件缓存数据不够则发起网络请求向CDN请求数据箭头3所示获取网络数据写入文件缓存箭头4所示返回请求的数据给Client箭头2所示
实现模块
预加载模块
确定了技术方案后预加载模块还是有很多工作要做的。在列表网络数据解析完成后会触发视频预加载首先会根据url生成md5值然后去查看这个md5值对应的任务是否存在如果存在则不会重复提交。生成任务后会提交到线程池在后台线程进行处理。网络从Wifi切换到3G的时候会把任务取消防止消耗用户的数据流量。
预加载任务在线程池执行的时候其流程是这样的首先会获取一个本地代理的url。然后发起http请求。Proxy会收到http请求进行处理开始做真正的数据预加载工作。预加载模块读取到指定的数据量后终止。到此预加载的任务就已完成。流程图如下所示 在用户快速滑动的时候怎么能保证视频还能继续秒开呢预加载模块对于每一个任务都会维护一个状态机在Fling的时候会把划过的任务暂停下把最新要显示的任务优先级提高让其优先执行。
Proxy模块
Proxy内部有个local的httpServer负责拦截播放器和预加载模块的http请求。client在请求时会带入CDN的url在本地缓存数据没有的时候会去CDN获取新鲜数据。因为有多个地方向Proxy请求数据所以用线程池来处理多个client的连接很有必要这样多个client可以并行不会因为前面有client在请求而阻塞。文件缓存使用LruDiskCache在超过指定文件大小后老的缓存文件会删除这是一个在使用文件缓存时很容易忽视的问题。由于我们的场景视频是连续播放的不存在seek的情况所以文件缓存相对比较简单不用考虑文件分段的情况。Proxy内部对于同一个url会映射到一个client如果预加载和播放同时进行数据只会有一份不会去重复下载数据。再来一个Proxy内部构造示意图 遇到的问题
在测试中发现有的视频还是会播放很慢仔细查看本地的确缓存了期望的数据大小但是播放的时候还是有较长的等待时间这种视频有个特点moov box在尾部。对于moov在尾部的视频是整个文件都下载完成后才进行播放的原因是moov box里面存了很多关键信息前面分析mp4格式的时候有提到。对于这个问题有两个解法
解法一
服务端在进行转码的时候保证moov的头部在前面发现moov位置不正确的视频服务端进行订正。
PS:查看moov在文件中的位置可以用hex文本编辑器打开按字符搜索moov所在的位置即可MAC上面还可以使用MediaParser , 另外还可以用ffmpeg命令生成moov在头部或者尾部的mp4文件。
例如从1.mp4 copy一个文件使其moov头在尾部
ffmpeg -i 1.mp4 -c copy -f mp4 output.mp4 从1.mp4 copy一个文件使其moov头在头部ffmpeg -i 1.mp4 -c copy -f mp4 -movflags faststart output2.mp4
解法二
不用修改moov box的位置而是在播放端进行处理播放端需要检测流信息如果moov前面没有就去请求文件的尾部信息。具体就是发起 HTTP MP4 请求读取响应 body 的开头如果发现 moov 在开头就接着往下读mdat。如果发现开头没有先读到 mdat马上 RESET 这个连接然后通过 Range 头读取文件末尾数据因为前面一个 HTTP 请求已经获取到了 Content-Length 知道了 MP4 文件的整个大小通过 Range 头读取部分文件尾部数据也是可以的。示意图如下 这个方案的缺点是对于moov box在尾部的视频会多两次http connection。
结语
本文介绍了常见的视频编码格式视频封装格式介绍了moov头信息对于视频播放的影响。随着对于播放流程的分析我们找到了问题的切入点。简单说就是围绕着数据预加载展开把网络请求数据的工作提前完成播放的时候直接从缓存读取而且后续的视频回看都是从缓存读取不仅解决了视频初始化播放慢的问题还解决了播放缓存问题可以说是一箭双雕。Proxy是这个方案的核心思想本地localhost的url是一个关键纽带视频预加载模块和播放器模块解耦彻底换了播放器照样可以使用。到此为止视频feeds秒开优化就已完成。上线后的数据来看视频打开速度在800ms左右。
回过头来或许我们还可以更进一步可以对预加载收到的数据进行验证确保缓存了准确的信息而不是固定的数值。还可以进行更加深度的优化让用户观看视频的体验更加顺滑。
参考文献
* [AndroidVideoCache](https://github.com/danikula/AndroidVideoCache)
* [视频的封装格式和编码格式](https://www.jianshu.com/p/8034fa1ed682)
* [播放器技术分享1架构设计](http://blog.51cto.com/ticktick/2324928?sourcedra)
* [MP4文件格式的解析以及MP4文件的分割算法](https://cloud.tencent.com/developer/article/1120604)
* [从天猫某活动视频3次请求说起](https://juejin.im/post/5c0e0f75e51d45410c5e1aea)
* [视音频编解码学习工程FLV封装格式分析器]https://blog.csdn.net/leixiaohua1020/article/details/17934487
* https://www.adobe.com/content/dam/acom/en/devnet/flv/video_file_format_spec_v10_1.pdf
* https://baike.baidu.com/item/flv
* https://standards.iso.org/ittf/PubliclyAvailableStandards/index.html 原文链接 本文为云栖社区原创内容未经允许不得转载。