程序员人生 网站导航

[置顶] 【腾讯bugly干货分享】QQ空间直播秒开优化实践

栏目:综合技术时间:2016-08-01 17:04:29

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1204&extra=page%3D1

2016年应当是直播元年,直播利用百团大战,QQ 空间也在6.5版本上线了直播功能,从无到有、快速搭建了直播间。“先扛住再优化”,第1个版本和竞品相比,我们进入直播间的速度比较慢。根据外网统计在6.5版本的用户端看到画面需要4.4s,因此在6.5发布以后,着手启动了优化工作,目标:观看直播需要到达秒进体验(1s内看到画面)。

先上1张直播间的截图:

1、优化效果

1)实验室数据(小米5 WIFI)30次平均进入时间475ms

2)外网运营数据 (6.5版本 对照 6.5.3版本)

从外网运营数据看,观看成功率提升到99.41%;观看延时提升到平均2.5s,加快43.5%。用户进入直播间的时间区间在(0,1] (观看端0⑴秒内进入成功)的占比提升到19.52%,提升191%。

3)与竞品“映客”对照 (左边空间 VS 右边映客 小米5 WIFI)

https://v.qq.com/iframe/preview.html?vid=x0306f83wfv&amp

4)总结

1、优化可以拔高速度上限,能使用户进入直播间的耗时上限提高到 500ms 之内,从(0,1]的占比区间提升,对大量用户的提升还是比较明显的。

2、直播是强依赖网络状态的产品。如果主播的网络条件很差,上行丢包严重时,观众卡在这个时间点进入,由于没有上行是拉取不到首帧数据的,这类情况会致使统计数据被拉高。这也是整体平均时间未到 1s 之内的缘由。

2、QQ 空间直播的架构

在前期技术选型上,综合斟酌开发周期,稳定性和质量监控体系,我们选用腾讯云的现有视频互动直播解决方案,以下是整体的架构图。

1、 直播房间使用 roomid 做唯1 key。逻辑上分为两层。音视频房间,主要负责和腾讯云的流媒体服务器通讯音视频数据和音视频房间状态的保护;消息房间,主要负责和空间的服务器进行交互,包括赞,评,打赏等业务逻辑和消息房间的状态保护。消息房间通过注册接口来响应音视频房间的状态。这样设计好处就是,消息房间和音视频房间是解耦的,各自单独运行都是允许的;

2、 观众端可以通过云 sdk、RTMP 或 HLS 协议3种方式收到主播的推流;观看场景涵盖 H5,native 多平台。

3、 直播浮层设计为独立进程,主要是斟酌到独立进程 crash 不影响主进程的稳定性;缺点是和主进程的通讯复杂,进程启动有部份耗时;

3、耗时分析

我们将观看直播耗时的各阶段拆细分析:

1、 全部观看直播的流程是串行的,致使整体耗时是每一个步骤的耗时累加。

2、 拉取房间信息,拉取直播参数配置,拉取接口机 IP 是3个网络要求,耗时存在不稳定性,1般是 300ms,网络情况不好就会到 1000ms+;

3、 直播进程的生命周期是跟随 avtivity 的生命周期,activity 烧毁后,进程也随之烧毁,再打开需要耗时重新创建进程。

4、互动直播 SDK 的上下文是依赖直播进程,新进入也需要重新初始化。

5、 拉取首帧数据是单步骤耗时最久,急需解决。

4、确立方案,各个击破

根据直播的具体业务来分析,我们确立了以下几个解决的纬度。

速度优化1般有以下几个方向来解决问题:

1、 预加载。

2、 缓存。

3、 串行变成并行,减少串行耗时。

4、 对单步骤中的耗时逻辑梳理优化。

根据这些方向,我们做的工作:

1、 预加载进程。

2、 互动直播 SDK 上下文全局单例,并且预先初始化。

3、 并行预拉取接口机 IP,房间信息,预进入互动直播 sdk 房间。

4、 接口机缓存首帧数据,减少 GOP 分片时间,修改播放器逻辑,解析到 I 帧就开始播放。

1)新方案的整体流程图:

该方案在加速的基础上,还有其他的优点:

1、对现有的代码改动最小,保证版本的稳定性,除新增的预拉取逻辑,在原有流程上只需要将之前的异步逻辑改成拉取缓存的逻辑。

2、原有逻辑成为备份逻辑,流程茁壮型得到增强,预拉取失败还有原有逻辑作为备份“重试”,进房间成功率提高。

2)预拉取流程,详细介绍

从“预拉取接口机 IP”这个点来详细介绍如何做预拉取,缓存管理和时序处理:

1、 由于直播进程和主进程是内存隔离。Feeds 转动停止(开始预拉取)是在主进程触发。拉取的 wns 要求需要在直播进程。通过 AIDL 跨进程去调用。

2、 接口机 IP 的要求为异步,需要缓存要求的状态。要求缓存接口机 IP 数据时,预拉取的状态为成功,直接使用缓存数据。

预拉取的状态为要求中,等待本次预拉取的结果。

预拉取的状态为失败,走之前流程,重新要求接口机 IP。

3、 接口机 IP 需要有时效性的,每次滑动停止都预加载 IP,会造成了要求浪费;并且腾讯云的接口机 IP 有就近接入的特性。为保证负载稳定,如果1直使用缓存的接口机 IP 可能会致使某台机器负载过量。需要加入时效性的控制。

3)秒开关键

仔细的同学肯定发现还有1个最大的耗时点没有解决——拉取首帧数据过慢。这个步骤耗时下降才是秒开的关键。

首帧数据的展现进程,实际上是1个下载,解码,渲染的进程。

这里简单插述1下视频编解码进程中的1种约定:GOP( Group of Pictures )

为了便于视频内容的存储和传输,通常需要减少视频内容的体积,也就是需要将原始的内容元素(图象和音频)经过紧缩,紧缩算法也简称编码格式。例如视频里边的原始图象数据会采取 H.264 编码格式进行紧缩,音频采样数据会采取 AAC 编码格式进行紧缩。 视频内容经过编码紧缩后,确切有益于存储和传输; 不过当要观看播放时,相应地也需要解码进程。因此编码和解码之间,明显需要约定1种编码器和解码器都可以理解的约定。就视频图象编码和解码而言,这类约定很简单: 编码器将多张图象进行编码后生产成1段1段的 GOP ( Group of Pictures ) , 解码器在播放时则是读取1段1段的 GOP 进行解码后读取画面再渲染显示。 GOP ( Group of Pictures ) 是1组连续的画面,由1张 I 帧和数张 B / P 帧组成,是视频图象编码器和解码器存取的基本单位,它的排列顺序将会1直重复到影象结束。

在云 SDK 中,将帧类型扩大到5种:

  • I 帧不需要参考帧。
  • P 帧只参考上1帧。
  • P_WITHSP 帧可参考上1帧、I 帧、GF 帧、SP 帧,自己不可以被参考。
  • SP 帧可参考 I 帧、GF 帧、SP 帧。
  • GF 帧可参考 I 帧、GF 帧 。

    1) 标准的 H264 编码的参照关系,每个 GOP 的第1针是 I 帧,P 帧顺次参考上1帧,抗丢包性不强,如果中间有 I 帧或 P 帧丢失,则该 GOP 内后续 P 帧就会解码失败。

(1.标准 GOP 组织图)

2) 在实时直播的场景,为保证流畅性,重写编码器逻辑,首个 GOP 包开头为I帧,后面 GOP 包开头为 GF 帧,这是利用 GF 帧的传递参考关系是跨 GOP,每一个 GF 帧参考上1个 GOP 的 I 帧或 GF 帧。GF 帧体积对照 I 帧要小,后续 GOP 的下载解码更快速。

(2.SDK 的 GOP 组织图)

3) 对 GOP 内部的帧组织,也使用 P_WITHSP 帧来代替 P 帧,主要是由于 P_WITHSP 帧(粉红色表示)的解析可参考上1帧、I 帧、GF 帧、SP 帧,自己不可以被参考。就算上1帧 P_WITHSP 未解码出来,后1帧 P_WITHSP 的解码也不受影响,增强了抗丢包性

(3.SDK 的 GOP 帧内部参考关系)

那具体到业务上,通过 wireshark 抓包我们发现。进程主要耗时在首个 GOP 包下行比较慢,需要等待 I 帧(FT 是 0 代表 I 帧)下载终了才开始解码,如果 I 帧不完全没法解码,则需要等待第2个 GOP 包,等待时间加长。

那通过这个现象,为了让全部进程加快,和 SDK 的同事在1.8.1版本1起做了以下工作:

1、 减小首个 GOP 包的分片大小:将 GOP 的分片由 5s 改成 3s,并且首个 GOP 包只缓存必要的 I 帧,减少首个 GOP 包的体积;(PS:GOP 包的长度和主播端编码性能也是强相干,GOP 分片太小,编码性能不高,分片时长的肯定需要综合斟酌)

2、 首个 GOP 包需要走网络下载,一样网络条件下这部份路径越短下载越快。GOP 包之前是存在流控服务器上,GOP 包要到达客户端连接的接口机,还需要链接传输的耗时。新的版本直接在接口机上缓存当前直播中房间的 GOP 数据,保证在客户端连上接口机以后,就能够直接从本机缓存中推流首帧数据,省掉之前的链接传输耗时。

3、 大部份播放器都是拿到1个完全的 GOP 后才能解码播放, 改写播放器逻辑让播放器拿到第1个关键帧(I帧)后就给予显示。不需要等待全部的 GOP 下载终了才开始解码。

以上3点做好了以后,效果明显,全部的拉取首帧的时间由之前的 2140ms 降到平均 300ms,固然完成这些工作其实不是上面叙述的3点那末简单,中间进程我们也发现1些辣手问题,并推动解决:

如主播上行网络丢包致使的 GOP 乱序、多台接口机之间缓存的管理、GOP 分片时长的肯定。

4)延续优化

我们1直没有放弃“更快更爽”的体验寻求,在后续的迭代中也延续优化直播的体验:

1、接口机 IP 竞速。

2、合并要求。

3、多码率。

5、遇到的问题

我们的优化手段是将串行的异步要求改成并行;但是将串行改成并行后,几个异步要求同时开始,如何保证各个异步回调的时序运行正常,这是1定要解决的问题,也是大家在做优化进程中比较有代表性的问题。

处理这类异步回调时序问题类似于 Promise 模式。我这里在具体业务上使用 LiveVideoPreLoadManager 来统1处理,类图以下:

  • LiveVideoPreLoadManager:负责对外暴露启动预加载方法和拉取结果数据对象的方法。其主要方法及职责以下。

Compute:注册监听器,获得结果的数据对象,使用监听器实例来响应对数据对象的处理。
preLoad:启动异步任务的履行。

  • CacheManager:缓存异步任务处理结果和状态,检查是不是过期。负责检测异步任务是不是处理终了、返回和存储异步任务处理结果。其主要方法及职责以下。

getResult:获得缓存异步任务的履行结果。
setResult:设置缓存异步任务的履行结果及当前的履行状态:开始,进程中,结束。
isDone:检测异步任务是不是履行终了。

  • Result:负责表示异步任务处理结果。具体类型由相应的业务决定。

  • Task:负责真正履行异步任务。其主要方法及职责以下

run:履行异步任务所代表的进程。

获得异步任务处理结果的序列图以下。

采取这类模式,当异步任务同时开始,如拉取房间信息,接口机 IP,房间信息,它们都被封装在 LiveVideoPreLoadManager 的 Task 要求实例中,而主流程则不必关心这些细节,只需要将之前的要求方式变成 LiveVideoPreLoadManage.compute,并注册对应的异步回调接口。Compute 内部会通过 CacheManager 的 getResult 方法检查异步任务处理结果状态,如果异步任务已履行终了,则该调用会直接返回,类似与同步操作(步骤5,6,7),那末 LiveVideoPreLoadManager 对外暴露的 compute 方法是个同步方法;若异步任务还未履行终了,则会阻塞1直等待异步任务履行终了,再调用 compute 注册的回调来响应结果,此时 compute 方法是个异步方法(步骤5,4)。也就是说,不管compute方法是1个同步方法还是异步方法,对客户真个编写方式都是1样的。

采取这类 Promise 模式,即对原有流程改动最小,也增强了原有流程的茁壮型,在预拉取失败的时候,那末原有流程的串行逻辑作为兜底保护。从统计数据也能够看到,在优化版本以后,版本的观看端进入房间成功率也有提升。

6、总结

全部的秒开优化版本时间非常紧张,中间肯定还有别的优化空间,统计数据上来看,整体用户的进入时间还是在 2.5s+,新的迭代版本还在延续优化,大家如果对秒进有甚么好的想法和建议,欢迎交换。也欢迎大家下载新版 QQ 空间独立版体验 Qzone 的直播功能,分享生活,留住感动!

更多精彩内容欢迎关注bugly的微信公众账号:
bugly

腾讯 Bugly是1款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上利用崩溃的情况和解决方案。智能合并功能帮助开发同学把每天上报的数千条 Crash 根据根因合并分类,逐日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报可以在发布后快速的了解利用的质量情况,适配最新的 iOS, Android 官方操作系统,鹅厂的工程师都在使用,快来加入我们吧!

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐