爱奇艺奇秀直播的秒播体验优化实践

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在视频直播中,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"首帧渲染速度","attrs":{}},{"type":"text","text":"会直接影响用户体验。想象一下,你兴致勃勃进入了一个爱豆的直播间,进入直播间后迟迟不见直播画面,而是长时间停留在直播间背景图上,这是大多数用户都无法接受的体验。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了提高用户在","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"爱奇艺直播和小视频平台奇秀app iOS端","attrs":{}},{"type":"text","text":"的观看体验,爱奇艺去年中旬对","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"奇秀秒播","attrs":{}},{"type":"text","text":"进行了整体的优化,主要涉及","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"业务逻辑以及视觉上","attrs":{}},{"type":"text","text":"的优化。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":16}},{"type":"strong","attrs":{}}],"text":"01 问题出在哪儿?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从进入直播间到第一帧画面渲染。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"播放流程一般涉及如下几个方面:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bb/bb2fb45bef6b1ade0e0a22a72fadf07f.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分析耗时操作,首先要找到耗时较多的逻辑。这里尝试了两种方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"打印日志。","attrs":{}},{"type":"text","text":"在进入方法前、方法执行结束后,打印日志,输出方法的耗时。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"利用埋点投递。","attrs":{}},{"type":"text","text":"在秒播路径中设置若干关键节点,每个节点触发时,将耗时数据投递到神策。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于打印日志,在实际操作的过程中,发现有比较明显的弊端,比如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)测试次数一般不会很多,所以","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"不具有代表性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)进入","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"不同的直播间、在不同的网络状况下","attrs":{}},{"type":"text","text":",都可能会影响到","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"测试数据。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为了保证分析数据的准确,最终采用","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"埋点投递配合TimeProfile的方式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先将秒播的过程分成","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"几个重要的阶段","attrs":{}},{"type":"text","text":":进入直播间、房间数据请求返回、准备播放、第一帧渲染成功。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过埋点,统计出不同阶段的耗时情况。这里以优化前的5.5.0版本举例,如图所示:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bf/bffe8918c3339d771144e0cd0cd18789.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从图中可以看到,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"房间数据请求解析阶段以及播放阶段耗时较长。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"缩小了问题的范围,我们使用","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"XCode自带的调试工具Time Profile进行具体的耗时分析。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"TimeProfile的原理是通过按照固定的时间间隔来跟踪每一个线程的堆栈信息,通过统计比较不同时间间隔之间的堆栈状态,来推算某个方法执行了多久,并获得一个近似值。如图:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a2/a212bbeaf4136912f8e54442cfbf583a.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对多次测试数据进行对比发现,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"具体的耗时逻辑主要有两处:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)播放器SetFrame操作会卡主主线程较长时间。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)处理房间接口返回的数据消耗的较多的时间。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":16}},{"type":"strong","attrs":{}}],"text":"02 如何解决?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,最亟待解决的问题是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SetFrame导致卡顿的问题","attrs":{}},{"type":"text","text":",通过与播放内核同学的沟通,猜测","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"可能与苹果系统的转场动画有关(疑似资源争抢,播放器短时间内获取不到上下文)","attrs":{}},{"type":"text","text":",导致播放器在系统进行动画的过程中去争抢相关资源,最终导致第一帧渲染失败。所以这里引入状态机来解决这个问题:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/55/552775350c8a412994e22c467c0c1a96.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有的播放操作都等到系统告知我们UI布局结束后再去操作,这样调整后,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"秒播回到正常的区间(大概56%左右),且未再复现卡顿问题。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其次,对于处理接口返回的数据,进行的一系列初始化操作耗时较多的问题。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"具体分为2点:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)初始化各个功能模块会导致耗时较多,比如聊天区历史消息功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)由于iOS要求UI相关的操作需要在主线程进行,所以接口请求返回后,存在多次异步到主线程的逻辑,非常消耗时间。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"针对功能模块初始化耗时问题,我们在直播间抽象了一个","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"功能管理模块,一方面是控制模块的加载时机,另一方面可以将功能模块与直播间解耦。","attrs":{}},{"type":"text","text":"如图:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fb/fb9386b8d2801ca02092b451489455e4.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"针对","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"重复异步到主线程","attrs":{}},{"type":"text","text":"的问题,我们在底层对","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"所有的网络请求","attrs":{}},{"type":"text","text":"都进行一次异步到主线程的操作,这样就不用在业务层由开发同学再进行异步主线程的操作了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"这个问题改动之后,秒播提升较为明显,大概稳定在78%左右。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b9/b923b02252abb7af1d77adba631be4e5.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":16}},{"type":"strong","attrs":{}}],"text":"03 一些非技术相关的调整","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面主要讨论的是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"从外部进入直播间的秒播优化","attrs":{}},{"type":"text","text":",我们还有一种比较重要的切换直播间的方式——上下滑切换。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于上下滑技术上的相关优化,我们放在下一个章节说明。我们讨论一种通过","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"一些视觉上的处理","attrs":{}},{"type":"text","text":",让上下滑操作中的秒播","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"看起来\"更快\"一些。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"相较于之前先加载直播间UI的方式,新的方式延迟直播间相关UI的创建(拿到房间数据后再创建),由于观众首先看到的是直播画面,所以会给人一种播放的\"更快\"的错觉。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/5c/5ca73be81ffb41d36dde573c041ca980.gif","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":16}},{"type":"strong","attrs":{}}],"text":"04 继续改进的方向","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"再看看技术方面。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前针对上下滑也做了一些优化,主要是在滑动操作中,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"当手指离开屏幕时判断出动画结束时是否会滑入到下一个直播间,从而提前加载播放数据。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"相对于之前等待完全滑入到下一个直播间后,再加载播放数据,有了一定的提升。但实际上还存在着","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"一些缺点","attrs":{}},{"type":"text","text":",比如:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)已经判断出要滑动到下一个直播间,但在滑动动画结束之前,用户突然触摸屏幕导致滑动中止。这时如果用户放弃滑入到下个直播间,而是返回刚刚的直播间,那么就要重新加载播放数据,体验不是很好。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)在用户开始滑动到手指离开屏幕,这段时间其实什么都做不了,浪费掉了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于滑动切换直播间这个操作,目前更好的实现方式是使用","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"双播放器,","attrs":{}},{"type":"text","text":"这样在开始滑动时,就可以直接加载下个直播间的数据,从而避免了对于一些特殊情况的复杂逻辑处理并且","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"最大限度地提前了加载的时间。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但由于我们的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"播放内核较大,目前采用双播放器会导致直播间架构复杂度上升,边际成本较高","attrs":{}},{"type":"text","text":",所以没有再进一步尝试双播放器的方式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":16}},{"type":"strong","attrs":{}}],"text":"05 结语","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"通过对逻辑、视觉效果的优化,最终将秒播率提升至78%左右。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了通过一些新的机制(","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"比如上面提到的延迟加载、模块管理","attrs":{}},{"type":"text","text":")来保证后续版本迭代时不会对秒播率造成影响,还会尝试双播放器来进一步提升秒播率,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"为用户带来更好的播放体验。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章