短视频宝贝=慢?阿里巴巴工程师这样秒开短视频

前言

随着短视频兴起,各大APP中短视频随处可见,feeds流、详情页等等。怎样让用户有一个好的视频观看体验显得越来越重要了。大部分feeds里面滑动观看视频的时候,有明显的等待感,体验不是很好。针对这个问题我们展开了一波优化,目标是:视频播放秒开,视频播放体验良好。无图无真相,上个对比图,左边是优化之前的,右边是优化之后的:

compare.gif

问题分析

视频格式的选择

在正式分析问题之前有必要说明下:我们现在首页的视频,都是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 unit,CU)、预测单位(predict unit,PU) 和转换单位(transform unit,TU )。总的来说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再加上几十帧的数据。

做了一个简单的计算:闲鱼短视频一般最长是30s,feeds里面的分辨率是320p,码率是1141kb/s,ftyp+moov这个视频的数据量在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?source=dra)
*  [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

 

原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/520200.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI又被彩虹吹?!新浪财经:应届博士算法毕业,80万年薪被疯抢

在2019人工智能计算大会上发布了《2019—2020中国人工智能计算力发展评估报告》。根据报告,预计2023年,中国人工智能基础架构市场将超过80亿美元。人工智能市场布局火力全开巨头哭诉:全球只有30万人才截至2017年,我国人工智能市场…

Haproxy 管控台介绍

Queue 队列 简称全称说明Curcurrent queued requests当前的队列请求数量Maxmax queued requests最大的队列请求数量Limit队列限制数量 Session rate (每秒的连接回话)列表 简称全称说明scurcurrent sessions每秒的当前回话的限制数量smaxmax sessions每秒的新的最大的回话量s…

阿里云时空数据库引擎HBase Ganos上线,场景、功能、优势全解析

随着全球卫星导航定位系统、传感网、移动互联网、IoT等技术的快速发展,越来越多的终端设备连接至网络,由此产生了大规模的时空位置信息,如车辆轨迹、个人轨迹、群体活动、可穿戴设备时空位置等。这些数据具有动态变化(数据写入频繁…

云栖专辑|阿里开发者们的第二个感悟:PG大V德哥的使命感与开放心态

2015年12月20日,云栖社区上线。2018年12月20日,云栖社区3岁。 阿里巴巴常说“晴天修屋顶”。 在我们看来,寒冬中,最值得投资的是学习,是增厚的知识储备。 所以社区特别制作了这个专辑——分享给开发者们20个弥足珍贵的…

【经典必看】14个实用的数据库设计技巧

戳蓝字“CSDN云计算”关注我们哦!作者 | 程序IT圈责编 | 阿秃1. 原始单据与实体之间的关系可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对应且只对应一个实体。在特殊情况下,它们可能是一…

VS Code 全局配置

文章目录1. settings.json2. 在项目根目录添加.eslintrc.js3. 在项目根目录添加.prettierrc.json1. settings.json ctrlshirtp 搜索settings.json替换为下面内容即可 {// 主题颜色 浅色主题"workbench.colorTheme": "Monokai","workbench.iconTheme…

云栖专辑 | 阿里开发者们的第3个感悟:从身边开源开始学习,用过才能更好理解代码

2015年12月20日,云栖社区上线。2018年12月20日,云栖社区3岁。 阿里巴巴常说“晴天修屋顶”。 在我们看来,寒冬中,最值得投资的是学习,是增厚的知识储备。 所以社区特别制作了这个专辑——分享给开发者们20个弥足珍贵的…

个人帐目管理系统java_Java 项目 个人帐目管理系统

目录第一部分项目描述 31.1项目目的 3第二部分需求和开发环境 32.1使用技术和开发环境 32.2项目需求 32.3详细功能 32.4 E-R图 32.5数据库的设计 32.5.1数据表的设计 32.5.2数据库约束的设计 42.5.3数据库序列的设计 42.5.4数据库索引的设计 42.5.5数据库视图的设计 52.5.6数据…

Visual Studio Code

文章目录一、基础配置1. 中文界面2. 高效插件3. vue代码片段4. 全局配置5. VSCode 常用快捷键6. VSCode高级操作技巧一、基础配置 1. 中文界面 中文界面 2. 高效插件 VSCode 高效必备开发插件 3. vue代码片段 Visual Studio Code Vue代码片段 4. 全局配置 Visual Studi…

KubeCon 2018 参会记录 —— FluentBit Deep Dive

在最近的上海和北美KubeCon大会上,来自于Treasure Data的Eduardo Silva(Fluentd Maintainer)带来了最期待的关于容器日志采集工具FluentBit的最新进展以及深入解析的分享;我们知道Fluentd是在2016年底正式加入CNCF,成为…

全球首个!阿里云开源批流一体机器学习平台Alink……

11月28日,阿里云正式开源机器学习平台 Alink,这也是全球首个批流一体的算法平台,旨在降低算法开发门槛,帮助开发者掌握机器学习的生命全周期。 Flink Forward 2019在京举办,吸引众多开发者参与标题Alink基于实时计算引…

2018年的AI/ML惊喜及预测19年的走势(一)

考虑到技术变革的速度,我认为让专业IT人士分享他们对2018年最大惊喜及2019年预测的看法会很有趣。以下是他们对人工智能(AI),机器学习( ML)和其他数据科学迭代的看法: CLARA分析公司首席执行官…

Java访问静态常量_Java如何在Spring EL中访问静态方法或常量?

在这个例子中,您将学习如何使用Spring Expression Language访问类范围的方法或常量。要访问类范围的方法或常量T(),例如,您将需要使用Spring EL的运算符T(java.lang.Math)。该运算符将使我们能够访问给定类上的静态方法和常量。作为示例&…

聚焦产业·城市、擎领数字未来:IMPACT2019紫光云峰会在津成功举办

近日,紫光云技术有限公司在天津举行主题为“产业城市 擎领未来”的IMPACT2019紫光云峰会,深度阐释打造产业数字引擎的理念和实践,并为unI X云创中心揭牌,发布紫光云芯片产业数字引擎。 天津市人民政府副秘书长杨明远为大会致辞会上…

IntelliJ IDEA 2020 创建xml文件

1、file—setting,左上角输入template, 2、在左侧栏找到File And Code Templates 3、中间选中Files 4、点击号,添加模板 5、输入模板名字:Name:mybatis-cfg.xml (name可以自定义) 6、后缀名extension&#…

刚刚,蚂蚁金服荣膺“中国金融大数据领军企业”称号

小蚂蚁说: 2018中国软件和信息服务领域十大领军企业、人物及产业园区评选活动是业界最权威和最受关注的评选之一。12月20日,在北京举行的“2018中国软件大会”正式宣布蚂蚁金服成为“2018中国大数据金融领军企业”。 2018年12月20日,在北京举…

学java要算法吗_学习java不可不知的几种算法

1、冒泡排序算法&#xff1a;编程语言算法中比较经典的算法。每个程序员都必须了解和会运用的。AAA软件教育程序算法基础通过多次比较(相邻两个数)和交换来实现排序&#xff1a;public class bubble {public static void bubbleSort(int[] a) {int temp;for (int i 1; i < …

2018年的AI/ML惊喜及预测19年的走势(二)

年度回顾&#xff1a;2018年的AI/ML惊喜及预测19年的走势&#xff08;一&#xff09; Unravel Data首席执行官Kunal Agarwal 人工智能和机器学习的日益重视将会推动TensorFlow和H2O实现技术突破成为可能。此外&#xff0c;Spark和Kafka将继续呈现引人注目的受欢迎程度。 随着…

IntelliJ IDEA 2020 数据库连接Oracle和Mysql

Mysql数据库连接 填写下面需求要的信息url需要处理 jdbc:mysql://127.0.0.1:3306/xxxdatabase?autoReconnecttrue&useUnicodetrue&characterEncodingutf8&zeroDateTimeBehaviorCONVERT_TO_NULL&useSSLfalse&serverTimezoneCTT&nullCatalogMeansCurr…