点击蓝字 关注我们
概述
如图1.1所示,优酷播放SDK封装了统一的接口层,对各平台提供统一的API接口以及丰富的播放服务,如点播、直播、短视频、软硬解、DRM加密、预加载、智能挡等。这些播放服务都由播放内核来支撑。优酷播放内核是优酷自主开发的具备完全自主知识产权的一套播放框架。它对上承接了优酷丰富灵活的业务逻辑,对下屏蔽了各端系统的差异,是一个高可靠、可扩展、跨平台的优秀播放框架。
图1.1 播放服务框架
内核架构设计
内核的主要功能就是接收上层下发的指令,将数据从网络获取到本地,解析,解码并渲染,同时上报播放过程中的关键信息。所以播放内核框架需要简单高效,不要有多余的步骤,尽量少用线程。
图2.1就是我们最新的播放内核功能框图。绿色部分是内核引擎,主要负责接收外部命令,协调各子模块进行相应处理,并将各个子模块的状态和埋点信息上报上去。主要的数据加工模块被抽象成AVSource,AVDecoder,AVRender这三个模块,分别负责数据的获取,数据的解码,数据的渲染。
图 2.1 播放内核框架
当前,业界主流的播放框架都是基于pipeline架构的(我们的老框架也是这个结构),我们把新老架构的核心处理模块抽象出来放一起比较,如图2.2所示。老架构是基于pipeline结构的,各个模块内部有驱动线程,不同的播放源有不同的source,下载、解析和缓存管理模块跟source强绑定在一起,解码实现跟decoder绑定在一起,每个解码器对应一个decoder,渲染实现跟consumer绑定在一起,还融合了后处理的功能,所有模块跟架构耦合严重。新架构逻辑比较简单,把数据获取、解码、渲染三个主要过程抽象成各自独立的模块,跟播放框架独立开来,播放框架主要负责驱动这三个模块,包括控制播放流畅,监控播放状态,上报播放信息等流程。相比较而言,新的内核框架具备如下几个优点:
1. 更高效
原本需要分别控制source/decoder/consumer三个模块,每个模块都包含有一个线程,现在只需要控制consumer一个模块就够了。数据流转由原本的流水线变成由consumer统一调度,既提升了作业效率,又解决了因为音频视频处理速度不同导致可能引起source模块阻塞的问题。
2. 模块解耦
原本数据获取,处理及渲染的逻辑跟框架是绑定在一起的,不同的场景需要对应不同的模块。比如点播/直播/短视频有不同的source,android/ios/软解有不同的decoder和consumer,使得针对某个平台的微小改动都需要针对整个内核重新发版,维护和变更变得困难。新架构将框架和逻辑区分开来,将数据处理的部分独立成AVSource、AVDecoder、AVRender三个模块,框架部分只负责数据加工的简单逻辑。模块解耦后,数据处理接口变得更加简单清晰,各子模块也独立出来,实现了自升级、自测试,方便版本迭代。
3. 可配置
播放内核支持了多方的业务,包括优酷,来疯,淘票票,支付宝,华为等,各个版本因feature list不一致,所以差异很大。老架构因为各模块耦合紧,也不具备按feature进行配置的能力,所以提供特定版本会有不少的工作量。新架构中,我们对播放内核支持的feature list进行了全面的梳理,结合各个子模块的特点,新增了能够组装个feature的配置能力,通过简单的配置文档就可以编译出我们需要的版本,大大提升了特定版本release的效率。
图2.2 新老框架对比
播放性能调优
除了主要的数据驱动流程,我们还设计了一个随时监控播放健康状况的监控模块。我们希望了解当前播放过程的所有状况,包括是否正常启播,启播是不是够快,播放是否有卡顿,音画是否同步,声音流是否异常,是否黑屏,是否花屏等。我们对异常状况进行了量化,定义出了几个重要的指标:成功率、秒开率、卡顿率、播放异常率,并为此做了专项的优化。
1. 成功率
我们针对各个模块规范了错误码段,比如下载错误1xxx,解码错误2xxx,并详细定义了各个异常出现的时候的错误类型,尽量做到了一个错误码对应一个错误原因,使得在出现问题的时候很容易确定大致的问题原因。对于无法明确原因的错误码,比如source open失败,无法确认是framework的问题,还是下载的问题,或者pcdn的问题,我们引入了“错误快照”功能,在出现错误的时候启动该功能,然后重试的时候自动保留一部分数据,使得我们很容易知道是否是数据本身出了问题,大大提升了排查问题的效率。
2. 卡顿率
为了合理的平衡卡顿的次数和卡顿时长,我们设计了一套可配置各个临界值的缓存机制,并结合线上拐点数据配置了合理的启播和loading缓存,以及最大缓存。除此之外,由于很多loading都出现在启播之后的一小段时间之内,所以针对启播后的场景,我们设计了启播buffer动态调整的机制,即在启播后评估网络速度和prepare的时间,如果数据获取很快,prepare耗时低,那么就判断启播后生产的数据能够跟得上消费的数据,所以设置的启播buffer很低,反之则设置一定量的启播buffer,来应对启播后的抖动。
3. 秒开率
为了快速渲染出第一帧画面,需要对启播链路进行分析,确定主要耗时模块。对内核来讲,主要的耗时模块有:数据prepare,解析codec参数,硬解模块创建,硬解模块初始化,渲染模块open,第一帧数据读取,第一帧数据解码,第一帧数据渲染等步骤。我们需要尽可能的缩短主要模块耗时,同时也要确立启播关键路径,将非关键路径上的模块并行处理。我们优化了prepare,open的耗时,提前下载首片数据,避免了m3u8文件的下载解析耗时,提前初始化了硬解码器和渲染器,避免了有数据之后还要等待初始化的耗时。除此之外,我们还引入了预加载,包括数据预加载,source预加载,多实例预加载。这些全流程的优化措施极大的提升了我们启播的速度。
4. 播放异常率
以上三项核心指标是大多数播放器都会有并极力优化的点,但是这几项指标好并不一定说明用户的播放体验就好,有些异常场景无法从这几个指标上反应出来,比如没有声音,播放卡顿,音画不同步,画面黑屏或者花屏等等。我们需要更完善的指标来监控这些异常的情况,为进一步优化提供依据。根据场景的情况及用户反馈,我们梳理了如下这些异常情况,并设定了异常的边界:
视频卡顿:每秒输出视频低于10帧。
音频卡顿:每秒输出音频sample数低于正常的80%。
有画无声:每次输出音频sample数低于正常的20%,切视频帧率大于10帧。
有声无画:每秒输出视频低于5帧,且音频sample数大于80%。
无声无画:每秒输出视频低于5帧,且音频sample数小于20%。
音画不同步:每秒8帧以上音视频pts diff差600ms以上。
黑屏:每隔50帧取当前画面进行算法检测。
绿屏:每隔50帧取当前画面进行算法检测。
花屏:每隔50帧取当前画面进行算法检测。
以上为各异常的详细定义,我们同时定义了如下的一些指标:
单项异常率 = 单项异常数 / vv_end数
总异常率 = 所有异常总和 / vv_end数
至此,我们有了新的技术指标来衡量播放质量,这其中,视频卡顿和音画不同步的异常最为突出,需要重点来优化。播放出现卡顿,多半是因为系统计算能力不足引起,为了节省系统算力,保障基础播放流畅体验,我们引入了算力智能决策中心。根据各模块运行时重要程度排序,优先保障基础播放模块,在基础播放流畅性遇到瓶颈时通过牺牲一部分功能来保障播放主流程不受影响。比如解码前后丢帧,后处理功能自动启停,弹幕量自适应控制等等。
播放音画不同步大多也都是因为解码不及时导致渲染帧数累加,视频越来越滞后引起,通过引入算力智能决策中心,音画不同步问题也大幅缩小。另外还有很多不同步分析下来是在连接蓝牙耳机场景下出现,通过对蓝牙耳机进行适配,蓝牙场景下的不同步问题也得到了解决。
小结
优酷业务繁多,平台各异,播放内核是一切播放业务的基石,所以我们的目标就是:提供稳定可靠,性能卓越,灵活可扩展,统一跨平台的播放内核。我们需要给出真正反应用户体验的性能参数,持续打磨播放性能,提升有用体验。
以下内容你可能感兴趣