iOS音视频(直播 音频 视频)

Feed流短视频秒开背后的那些事儿

2018-07-25  本文已影响76人  夢之希翼

1.背景:

  浏览器iOS客户端的Feed流短视频播放过程中,不少用户反馈视频加载过程太久,导致会没兴趣继续等待下去,从而流失了这部分用户及无法提高用户的人均播放次数。

2.解决方案:

2.1.替换MPMoviePlayer为AVFoudation(秒开率从16.7%到35%)

根据统计数据,2016年3月时的短视频的秒开率在16.7%左右。我们对浏览器视频加载的过程进行了分析。

1、浏览器依然使用苹果播放器老的API,MPMoviePlayer,没有使用最新的AVFoudation播放器组件。根据WWDC的介绍,AVFoudation组件在性能、灵活性、多源设置、多线程、多实例比MPMoviePlayer优秀非常多。AVFoudation基于Core Audio和Core Video实现,在音视频的性能上做了更多的优化及开放更多的API给使用者使用,例如视频源的关键帧数据获取和视频编辑、录制等。更利于后续短视频的预加载和UGC方面的使用。

在我们测试性能过程中,对两个播放组件的进行了对比。T1的秒开率的对比图如下:(绿色为AVFoudation,蓝色为MPMoviePlayer)

然而我们一直没替换的原因是AVFoudation比MPMoviePlayer的播放出错率高很多,如下图所示(蓝色MPMoviePlayer,绿色AVFoudation)

这个播放出错率的问题一直制约了我们切换系统内核,经过我们多次走查代码,走查统计项,走查用户反馈,我们找到了三个核心的解决方案。

2.1.1.增加重试机制:

背景:MP内核内部封装有相关的重试机制,而更底层的AV内核不会有相关机制,导致了无网络或者慢网络下,MP内核能通过重试机制一定程度的恢复正常播放。AV内核则会直接出现出错界面。

方案:在主动或者被动获取到错误信息后,在WIFI的网络状态下,进行重试。重试默认值为6次,每次间隔为*2的递增,分别是1秒、2秒、4秒、8秒、16秒、32秒。其中重试机制功能启动和重试次数都有下发CD开关控制。

效果:1.无网络切换有网络下,以前是在无网络下直接出现出错界面。现在是切换过程中重试则可以正常播放。

2.慢网络切换正常网络下,重试后可以更快的正常播放。减少因为loading导致的用户流失率。

3.正常网络切换无网络,以前是直接出错,现在是重试后,只要能恢复正常网络,依然能正常播放。

2.1.2.优化播放第二个视频的出错率

背景:在IOS9以下的所有版本,播放第一个视频后,不退出当前播放器,立刻播放第二个视频,就会播放出错。

原因:AVPlayerItem主要储存AVURLAssert相关信息,用于作为AVPlayer的元数据使用。当第一个视频播放,AVPlayer使用第一个AVPlayerItem是完全正常的。当AVPlayer存活的情况下,播放第二个视频使用系统API:replaceCurrentItemWithPlayerItem的过程中,第一个item和第二个item的播放格式不同,导致Compositor不一致。AVPlayer内部会直接在KVO的status回调中返回fail状态,直接抛出-11800的错误码。

方案:取消以前代码中replaceCurrentItemWithPlayerItem的使用,在每个视频播放前先析构之前的AVPlayer,重新创建AVPlayer进行设置里面的媒体元数据,确保元数据在同步和异步过程中使用正常。

风险: 有可能对视频T1快开比有一定影响,待灰度进行相关的性能分析得出相关的影响程度。

效果:在IOS9以下的所有版本,播放任何视频,无论在连续播放多个视频都能不会出错。

2.1.3.优化用户点击重试按钮后的逻辑

背景:以上代码是播放出错后点击出错页面重试按钮的处理代码,如果某个视频强制VPS,但是用户是在无网络或者慢网络下,VPS返回失败,则无法设置contentURL。此时如果用户网络恢复了,点击重试按钮后,会一直播放一个错误的kMoviePlayerNoneVideoURL。这样无论用户点击了多少次重试按钮,视频都会一直播放失败。也影响了现在视频的播放出错率。

方案:对重试按钮点击的响应代码进行处理,不在使用contentURL作为播放标准来直接播放,而是使用播放器内movieInfo的元媒体数据进行相关的重试VPS或者换源。

  这三个方案执行后,AVFoudation的播放出错率从21.568%降低到5.498%,降幅高达76.1%。比MPMoviePlayer的6.908%还要低1.5%的比例。

  从而我们优化后把AVFoudation的所有性能指标都优于MPMoviePlayer,切换了这个内核。也为后面的秒开率优化奠定了基础,秒开率优化需要AVFoudation的高拓展性和易用性。

2.2.VPS预加载(秒开率从35%到45%)

  VPS视频服务的定义:基于目前视频的播放URL是动态变化的,后台服务器下发到客户端的URL都是页面URL,业界的做法是通过这个页面URL打开一个Webview去爬取里面的播放URL。VPS视频服务就是简化了这个步骤,通过VPS服务器,把页面URL解析成播放URL给客户端。

VPS的预加载意义在于能提前把每个Feed流内的短视频在网络允许的情况下更早的获取到实际的播放URL,从而减少中间链路的请求时间和相应时间。同时VPS预加载也同时处理的播放URL失效的情况和重爬机制,避免视频源地址失效后到时用户播放失败。

2.3.Metadata预加载(秒开率从45%到75%)

  Metadata的定义:视频的元数据,主要包含了这个视频的基础数据,例如creationdata、duration、height、width、framerate等。

列举现在主流的两种播放格式MP4和FLV的Metadata数据结构:

MP4:

FLV:

    元数据是视频播放前首先需要下载的,故我们对元数据进行预加载,可以让视频能更早的进入prepare阶段,减少播放器下载元数据的时间。加速了播放器去加载第一帧元数据的时间。

2.4.可播放标准的修改(秒开率从75%到85%)

2.4.1.播放流程优化

  实验原因:目前依赖于系统的LikeyToKeepUp通知回调来判断T3达到时间,有可能会延误,例如已经播放了十几秒通知才回调。故希望通过干预的方式,提前判断出播放器流畅播放的前提条件。

  视频流畅播放条件:

  /* 标记为可流畅播放,目前以下几种情形都认定为LikelyToKeepUp:

  *  1)AVPlayerItem.isPlaybackLikelyToKeepUp == YES;

  *  2)收到bufferFull事件;

  *  3)playableDuration >= 2.f;

  *  4) currentLoadedPercent占比 >= 20%;

  *  5) 最后一秒永远无法达到LikeyToKeepUp的状态

  *  6) IOS10中timeControlStatus == AVPlayerTimeControlStatusPlaying

  */

2.4.2.预加载TimeOut时间优化

  实验目的:预加载超时时间从6秒提升到10秒,目的提高预缓存命中率,降低失败率和超时率,从而提高秒开率。

  实验结果:  经过327W的VV实验,预加载命中率从83.813%涨到83.924%,无效率从72.043%降低到71.713%,失败率从7.097%降低到5.636%,超时率从2.766%降低到1.322%。

  综合以上实验结果,预加载命中率提升很小,所以秒开率也得不到很大的提高。但是这个优化对超时率和失败率及无效率有了明显的提高,故已经在上周线上进行调整。

2.4.3.优化缓存内核的loadedtime和currenttime(基于个人实验室进行)

  实验目的:提高秒开率。

  实验结论:loadedtime和currenttime每次调用耗时是0.3毫秒到0.7毫秒间,而每次播放总共调用是4到10次,预计只能提高1.2毫秒到7毫秒间,发现对秒开率提升甚微,但是调整代码影响面比较大,故目前维持现状。

2.4.4.预加载参数调优

  实验目的:1、提高正在工作的预加载working个数,从而让用户能更快的获得媒体数据,提高秒开率。 2、提高已缓存的缓存池数量,从而提高预缓存命中率,降低无用率。 3、提高准备缓存的个数,从而提高预缓存命中率。

  实验结论:

  1、提高正在工作的预加载working个数,虽然秒开率有所提升,但对第一个视频的网络资源和目前机器的内存资源有较大的影响,会造成较大的卡顿现象。故目前维持了现状。

  2、提高已缓存的缓存池数量,经过验证从默认值8个,调整到12个或者16个,秒开率变化很小。故结论是预加载命中率瓶颈不在此。(ABTest测试量是300万VV)

  3、提高准备缓存的个数,经过验证从默认值4个,调整到6个或者8个,秒开率依然变化很小。故结论是预加载命中率瓶颈不在此。(ABTest测试量是300万VV)

2.5.增加预加载触发场景(秒开率从85%到88%)

  视频内嵌正文页增加预加载。

  经过对前十名域名的秒开率进行了调查,发现了一个域名秒开率在20%以下。

       经过多番调查,发现是自媒体视频内嵌正文页,这种情况是没有走预加载流程的,故秒开率非常低。我对VV在10000以上域名是正文页的数量统计,预计是3.3%的占比。这些域名基本秒开率都低于20%,如果通过增加预加载的方式,按75%的占比来算。预估能提高2.5%的秒开率。这个将在下个版本进行优化,同时验证是否对信息流正文页秒开率有所影响。

2.6.视频源预加载(秒开率从88%到92%)

对视频源前面320KB数据进行预加载,可以让大部分视频能瞬速达到T3的流畅播放标准。我们遇到的问题主要是AVPlayer的网络托管。按之前MPPlayer的机制是无法完成的,MPPlayer更像一个黑盒,不具备流数据的开放。庆幸的是AVPlayer有开通AVAssetResourceLoader的机制,让这一切成为了可能。以下为相关的类图。

网络托管的流程图:

代理对象处理流程

a.当视频播放器向代理请求dataRequest时,判断代理是否已经向服务器发起了请求,如果没有,则发起下载整个视频文件的请求

b.如果代理已经和服务器建立链接,则判断当前的dataRequest请求的offset是否大于当前已经缓存的文件的offset,如果大于则取消当前与服务器的请求,并从offset开始到文件尾向服务器发起请求(此时应该是由于播放器向后拖拽,并且超过了已缓存的数据时才会出现)

c.如果当前的dataRequest请求的offset小于已经缓存的文件的offset,同时大于代理向服务器请求的range的offset,说明有一部分已经缓存的数据可以传给播放器,则将这部分数据返回给播放器(此时应该是由于播放器向前拖拽,请求的数据已经缓存过才会出现)

d.如果当前的dataRequest请求的offset小于代理向服务器请求的range的offset,则取消当前与服务器的请求,并从offset开始到文件尾向服务器发起请求(此时应该是由于播放器向前拖拽,并且超过了已缓存的数据时才会出现)

只要代理重新向服务器发起请求,就会导致缓存的数据不连续,则加载结束后不用将缓存的数据放入本地cache

e.如果代理和服务器的链接超时,重试一次,如果还是错误则通知播放器网络错误

如果服务器返回其他错误,则代理通知播放器网络错误

这里面我们遇到的一个比较的坑是MP4结构的moov预加载,先介绍下MP4的结构。

  .MPEG4文件主要是通过atom(box)这种结构单元来构成的(图2),

  .每个atom(box)包含header 和 data  2部分

  .header包含了当前atom的size(长度)和type(名字)

  .data是atom的实际数据,data中可以包含子atom

  .当data包含子atom的时候,那么该atom就是container atom了

  .在MPEG4中,主要包含:ftyp , moov , mdat 等container atom单元,其中最重要的是moov和mdat2个atom(图3)

  .moov包含了该mpeg4文件媒体信息,例如duration ,video track , auto track , video sample 等等一系列信息

  .mdat则是包含了实际的视频数据

MP4的预加载处理流程是ftyp => moov => mdat固定流程进行拆包获取stream info ,然后基于stream info 信息,decode frame ,decode audio 进行播放,直至播放完毕。而在于我们预加载moov结构时,moov结构可能在文件的尾部,而且导致预加载失败。故我们在读流过程中发现前面320KB没moov字段则会从尾部开始预加载moov结构数据,确保预加载成功。

参考文档:

https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/00_Introduction.html

https://developer.apple.com/library/content/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-SW1

https://developer.apple.com/documentation/avfoundation/avassetresourceloader

上一篇下一篇

猜你喜欢

热点阅读